From aa4c6c0178c06af328ee51c7bed1cc844f49e800 Mon Sep 17 00:00:00 2001 From: Lander Gallastegi Date: Tue, 5 Nov 2024 22:39:57 +0100 Subject: [PATCH 001/549] shader_recompiler: patch fmask access instructions (#1439) * Fix multisample texture fetch * Patch some fmask reads * clang-format * Assert insteed of ignore, coordinate fixes * Patch ImageQueryDimensions --- .../backend/spirv/emit_spirv_image.cpp | 1 + src/shader_recompiler/info.h | 13 ++++ .../ir/passes/resource_tracking_pass.cpp | 62 +++++++++++++++++-- src/shader_recompiler/specialization.h | 18 ++++++ src/video_core/amdgpu/resource.h | 5 ++ 5 files changed, 94 insertions(+), 5 deletions(-) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index fc99b8925..40e5ea8b9 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp @@ -176,6 +176,7 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, const ImageOperands operands; operands.AddOffset(ctx, offset); operands.Add(spv::ImageOperandsMask::Lod, lod); + operands.Add(spv::ImageOperandsMask::Sample, ms); const Id texel = texture.is_storage ? ctx.OpImageRead(result_type, image, coords, operands.mask, operands.operands) diff --git a/src/shader_recompiler/info.h b/src/shader_recompiler/info.h index b69863f4f..91256085a 100644 --- a/src/shader_recompiler/info.h +++ b/src/shader_recompiler/info.h @@ -86,6 +86,14 @@ struct SamplerResource { }; using SamplerResourceList = boost::container::small_vector; +struct FMaskResource { + u32 sgpr_base; + u32 dword_offset; + + constexpr AmdGpu::Image GetSharp(const Info& info) const noexcept; +}; +using FMaskResourceList = boost::container::small_vector; + struct PushData { static constexpr u32 BufOffsetIndex = 2; static constexpr u32 UdRegsIndex = 4; @@ -178,6 +186,7 @@ struct Info { TextureBufferResourceList texture_buffers; ImageResourceList images; SamplerResourceList samplers; + FMaskResourceList fmasks; PersistentSrtInfo srt_info; std::vector flattened_ud_buf; @@ -282,6 +291,10 @@ constexpr AmdGpu::Sampler SamplerResource::GetSharp(const Info& info) const noex return inline_sampler ? inline_sampler : info.ReadUdSharp(sharp_idx); } +constexpr AmdGpu::Image FMaskResource::GetSharp(const Info& info) const noexcept { + return info.ReadUd(sgpr_base, dword_offset); +} + } // namespace Shader template <> diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index 6c8809cf0..9468f80a7 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -142,7 +142,7 @@ public: explicit Descriptors(Info& info_) : info{info_}, buffer_resources{info_.buffers}, texture_buffer_resources{info_.texture_buffers}, image_resources{info_.images}, - sampler_resources{info_.samplers} {} + sampler_resources{info_.samplers}, fmask_resources(info_.fmasks) {} u32 Add(const BufferResource& desc) { const u32 index{Add(buffer_resources, desc, [&desc](const auto& existing) { @@ -183,6 +183,14 @@ public: return index; } + u32 Add(const FMaskResource& desc) { + u32 index = Add(fmask_resources, desc, [&desc](const auto& existing) { + return desc.sgpr_base == existing.sgpr_base && + desc.dword_offset == existing.dword_offset; + }); + return index; + } + private: template static u32 Add(Descriptors& descriptors, const Descriptor& desc, Func&& pred) { @@ -199,6 +207,7 @@ private: TextureBufferResourceList& texture_buffer_resources; ImageResourceList& image_resources; SamplerResourceList& sampler_resources; + FMaskResourceList& fmask_resources; }; } // Anonymous namespace @@ -618,6 +627,41 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip } ASSERT(image.GetType() != AmdGpu::ImageType::Invalid); const bool is_storage = IsImageStorageInstruction(inst); + + // Patch image instruction if image is FMask. + if (image.IsFmask()) { + ASSERT_MSG(!is_storage, "FMask storage instructions are not supported"); + + IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; + switch (inst.GetOpcode()) { + case IR::Opcode::ImageFetch: + case IR::Opcode::ImageSampleRaw: { + IR::F32 fmaskx = ir.BitCast(ir.Imm32(0x76543210)); + IR::F32 fmasky = ir.BitCast(ir.Imm32(0xfedcba98)); + inst.ReplaceUsesWith(ir.CompositeConstruct(fmaskx, fmasky)); + return; + } + case IR::Opcode::ImageQueryLod: + inst.ReplaceUsesWith(ir.Imm32(1)); + return; + case IR::Opcode::ImageQueryDimensions: { + IR::Value dims = ir.CompositeConstruct(ir.Imm32(static_cast(image.width)), // x + ir.Imm32(static_cast(image.width)), // y + ir.Imm32(1), ir.Imm32(1)); // depth, mip + inst.ReplaceUsesWith(dims); + + // Track FMask resource to do specialization. + descriptors.Add(FMaskResource{ + .sgpr_base = tsharp.sgpr_base, + .dword_offset = tsharp.dword_offset, + }); + return; + } + default: + UNREACHABLE_MSG("Can't patch fmask instruction {}", inst.GetOpcode()); + } + } + const auto type = image.IsPartialCubemap() ? AmdGpu::ImageType::Color2DArray : image.GetType(); u32 image_binding = descriptors.Add(ImageResource{ .sharp_idx = tsharp, @@ -652,11 +696,14 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip return {body->Arg(0), body->Arg(1)}; case AmdGpu::ImageType::Color1DArray: // x, slice [[fallthrough]]; - case AmdGpu::ImageType::Color2D: // x, y - return {ir.CompositeConstruct(body->Arg(0), body->Arg(1)), body->Arg(2)}; - case AmdGpu::ImageType::Color2DArray: // x, y, slice + case AmdGpu::ImageType::Color2D: // x, y, [lod] [[fallthrough]]; - case AmdGpu::ImageType::Color2DMsaa: // x, y, frag + case AmdGpu::ImageType::Color2DMsaa: // x, y. (sample is passed on different argument) + return {ir.CompositeConstruct(body->Arg(0), body->Arg(1)), body->Arg(2)}; + case AmdGpu::ImageType::Color2DArray: // x, y, slice, [lod] + [[fallthrough]]; + case AmdGpu::ImageType::Color2DMsaaArray: // x, y, slice. (sample is passed on different + // argument) [[fallthrough]]; case AmdGpu::ImageType::Color3D: // x, y, z return {ir.CompositeConstruct(body->Arg(0), body->Arg(1), body->Arg(2)), body->Arg(3)}; @@ -672,7 +719,12 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip if (inst_info.has_lod) { ASSERT(inst.GetOpcode() == IR::Opcode::ImageFetch); + ASSERT(image.GetType() == AmdGpu::ImageType::Color2D || + image.GetType() == AmdGpu::ImageType::Color2DArray); inst.SetArg(3, arg); + } else if (image.GetType() == AmdGpu::ImageType::Color2DMsaa || + image.GetType() == AmdGpu::ImageType::Color2DMsaaArray) { + inst.SetArg(4, arg); } } diff --git a/src/shader_recompiler/specialization.h b/src/shader_recompiler/specialization.h index c25c611e4..225b164b5 100644 --- a/src/shader_recompiler/specialization.h +++ b/src/shader_recompiler/specialization.h @@ -32,6 +32,13 @@ struct ImageSpecialization { auto operator<=>(const ImageSpecialization&) const = default; }; +struct FMaskSpecialization { + u32 width; + u32 height; + + auto operator<=>(const FMaskSpecialization&) const = default; +}; + /** * Alongside runtime information, this structure also checks bound resources * for compatibility. Can be used as a key for storing shader permutations. @@ -47,6 +54,7 @@ struct StageSpecialization { boost::container::small_vector buffers; boost::container::small_vector tex_buffers; boost::container::small_vector images; + boost::container::small_vector fmasks; Backend::Bindings start{}; explicit StageSpecialization(const Shader::Info& info_, RuntimeInfo runtime_info_, @@ -71,6 +79,11 @@ struct StageSpecialization { : sharp.GetType(); spec.is_integer = AmdGpu::IsInteger(sharp.GetNumberFmt()); }); + ForEachSharp(binding, fmasks, info->fmasks, + [](auto& spec, const auto& desc, AmdGpu::Image sharp) { + spec.width = sharp.width; + spec.height = sharp.height; + }); } void ForEachSharp(u32& binding, auto& spec_list, auto& desc_list, auto&& func) { @@ -115,6 +128,11 @@ struct StageSpecialization { return false; } } + for (u32 i = 0; i < fmasks.size(); i++) { + if (other.bitset[binding++] && fmasks[i] != other.fmasks[i]) { + return false; + } + } return true; } }; diff --git a/src/video_core/amdgpu/resource.h b/src/video_core/amdgpu/resource.h index 83be0b0a4..81fe43f4e 100644 --- a/src/video_core/amdgpu/resource.h +++ b/src/video_core/amdgpu/resource.h @@ -295,6 +295,11 @@ struct Image { return GetTilingMode() != TilingMode::Display_Linear; } + bool IsFmask() const noexcept { + return GetDataFmt() >= DataFormat::FormatFmask8_1 && + GetDataFmt() <= DataFormat::FormatFmask64_8; + } + bool IsPartialCubemap() const { const auto viewed_slice = last_array - base_array + 1; return GetType() == ImageType::Cube && viewed_slice < 6; From 204bba9be8f9e839b362463c823c2a462d8ad8af Mon Sep 17 00:00:00 2001 From: psucien Date: Tue, 5 Nov 2024 22:59:45 +0100 Subject: [PATCH 002/549] hot-fix: pr merge conflict resolved --- src/shader_recompiler/info.h | 5 ++--- src/shader_recompiler/ir/passes/resource_tracking_pass.cpp | 6 ++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/shader_recompiler/info.h b/src/shader_recompiler/info.h index 91256085a..f9cbacaf2 100644 --- a/src/shader_recompiler/info.h +++ b/src/shader_recompiler/info.h @@ -87,8 +87,7 @@ struct SamplerResource { using SamplerResourceList = boost::container::small_vector; struct FMaskResource { - u32 sgpr_base; - u32 dword_offset; + u32 sharp_idx; constexpr AmdGpu::Image GetSharp(const Info& info) const noexcept; }; @@ -292,7 +291,7 @@ constexpr AmdGpu::Sampler SamplerResource::GetSharp(const Info& info) const noex } constexpr AmdGpu::Image FMaskResource::GetSharp(const Info& info) const noexcept { - return info.ReadUd(sgpr_base, dword_offset); + return info.ReadUdSharp(sharp_idx); } } // namespace Shader diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index 9468f80a7..586785e6a 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -185,8 +185,7 @@ public: u32 Add(const FMaskResource& desc) { u32 index = Add(fmask_resources, desc, [&desc](const auto& existing) { - return desc.sgpr_base == existing.sgpr_base && - desc.dword_offset == existing.dword_offset; + return desc.sharp_idx == existing.sharp_idx; }); return index; } @@ -652,8 +651,7 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip // Track FMask resource to do specialization. descriptors.Add(FMaskResource{ - .sgpr_base = tsharp.sgpr_base, - .dword_offset = tsharp.dword_offset, + .sharp_idx = tsharp, }); return; } From f98b9f7726057ad525bdab2e0f45298a052744a0 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Wed, 6 Nov 2024 07:20:58 +0200 Subject: [PATCH 003/549] hot-fix : removed libSceRudp.srpx from lle modules since it appears it create more issues than solves --- src/emulator.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/emulator.cpp b/src/emulator.cpp index ae3cb17dd..d9d32ec1d 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -274,7 +274,7 @@ void Emulator::Run(const std::filesystem::path& file) { } void Emulator::LoadSystemModules(const std::filesystem::path& file, std::string game_serial) { - constexpr std::array ModulesToLoad{ + constexpr std::array ModulesToLoad{ {{"libSceNgs2.sprx", &Libraries::Ngs2::RegisterlibSceNgs2}, {"libSceFiber.sprx", &Libraries::Fiber::RegisterlibSceFiber}, {"libSceUlt.sprx", nullptr}, @@ -285,8 +285,7 @@ void Emulator::LoadSystemModules(const std::filesystem::path& file, std::string {"libSceRtc.sprx", &Libraries::Rtc::RegisterlibSceRtc}, {"libSceJpegEnc.sprx", nullptr}, {"libSceRazorCpu.sprx", nullptr}, - {"libSceCesCs.sprx", nullptr}, - {"libSceRudp.sprx", nullptr}}}; + {"libSceCesCs.sprx", nullptr}}}; std::vector found_modules; const auto& sys_module_path = Common::FS::GetUserPath(Common::FS::PathType::SysModuleDir); From 46ac48c311900d435f0a3bb5a1e040132088139f Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Wed, 6 Nov 2024 23:39:43 +0300 Subject: [PATCH 004/549] AJM: Added some missing features (#1489) * AJM: Added support for different PCM formats * updated libatrac9 ref * remove log * Add support for non-interleaved flag * Added support for output sideband format query --- externals/LibAtrac9 | 2 +- src/core/libraries/ajm/ajm.cpp | 23 +++++++ src/core/libraries/ajm/ajm.h | 3 + src/core/libraries/ajm/ajm_at9.cpp | 82 ++++++++++++++++++++----- src/core/libraries/ajm/ajm_at9.h | 29 ++++++++- src/core/libraries/ajm/ajm_context.cpp | 1 - src/core/libraries/ajm/ajm_instance.cpp | 28 +++++++-- src/core/libraries/ajm/ajm_instance.h | 33 +++------- src/core/libraries/ajm/ajm_mp3.cpp | 80 +++++++++++++++++------- src/core/libraries/ajm/ajm_mp3.h | 26 +++++--- 10 files changed, 226 insertions(+), 81 deletions(-) diff --git a/externals/LibAtrac9 b/externals/LibAtrac9 index 82767fe38..3acdcdc78 160000 --- a/externals/LibAtrac9 +++ b/externals/LibAtrac9 @@ -1 +1 @@ -Subproject commit 82767fe38823c32536726ea798f392b0b49e66b9 +Subproject commit 3acdcdc78f129c2e6145331ff650fa76dd88d62c diff --git a/src/core/libraries/ajm/ajm.cpp b/src/core/libraries/ajm/ajm.cpp index 80681cbaf..2396669b6 100644 --- a/src/core/libraries/ajm/ajm.cpp +++ b/src/core/libraries/ajm/ajm.cpp @@ -13,8 +13,31 @@ namespace Libraries::Ajm { +constexpr int ORBIS_AJM_CHANNELMASK_MONO = 0x0004; +constexpr int ORBIS_AJM_CHANNELMASK_STEREO = 0x0003; +constexpr int ORBIS_AJM_CHANNELMASK_QUAD = 0x0033; +constexpr int ORBIS_AJM_CHANNELMASK_5POINT1 = 0x060F; +constexpr int ORBIS_AJM_CHANNELMASK_7POINT1 = 0x063F; + static std::unique_ptr context{}; +u32 GetChannelMask(u32 num_channels) { + switch (num_channels) { + case 1: + return ORBIS_AJM_CHANNELMASK_MONO; + case 2: + return ORBIS_AJM_CHANNELMASK_STEREO; + case 4: + return ORBIS_AJM_CHANNELMASK_QUAD; + case 6: + return ORBIS_AJM_CHANNELMASK_5POINT1; + case 8: + return ORBIS_AJM_CHANNELMASK_7POINT1; + default: + UNREACHABLE(); + } +} + int PS4_SYSV_ABI sceAjmBatchCancel(const u32 context_id, const u32 batch_id) { LOG_INFO(Lib_Ajm, "called context_id = {} batch_id = {}", context_id, batch_id); return context->BatchCancel(batch_id); diff --git a/src/core/libraries/ajm/ajm.h b/src/core/libraries/ajm/ajm.h index 10e23482e..1ac7c7629 100644 --- a/src/core/libraries/ajm/ajm.h +++ b/src/core/libraries/ajm/ajm.h @@ -137,9 +137,12 @@ union AjmInstanceFlags { u64 codec : 28; }; }; +static_assert(sizeof(AjmInstanceFlags) == 8); struct AjmDecMp3ParseFrame; +u32 GetChannelMask(u32 num_channels); + int PS4_SYSV_ABI sceAjmBatchCancel(const u32 context_id, const u32 batch_id); int PS4_SYSV_ABI sceAjmBatchErrorDump(); void* PS4_SYSV_ABI sceAjmBatchJobControlBufferRa(void* p_buffer, u32 instance_id, u64 flags, diff --git a/src/core/libraries/ajm/ajm_at9.cpp b/src/core/libraries/ajm/ajm_at9.cpp index 07647c521..7fff0ff0c 100644 --- a/src/core/libraries/ajm/ajm_at9.cpp +++ b/src/core/libraries/ajm/ajm_at9.cpp @@ -14,8 +14,8 @@ extern "C" { namespace Libraries::Ajm { -AjmAt9Decoder::AjmAt9Decoder() { - m_handle = Atrac9GetHandle(); +AjmAt9Decoder::AjmAt9Decoder(AjmFormatEncoding format, AjmAt9CodecFlags flags) + : m_format(format), m_flags(flags), m_handle(Atrac9GetHandle()) { ASSERT_MSG(m_handle, "Atrac9GetHandle failed"); AjmAt9Decoder::Reset(); } @@ -40,7 +40,20 @@ void AjmAt9Decoder::Initialize(const void* buffer, u32 buffer_size) { const auto params = reinterpret_cast(buffer); std::memcpy(m_config_data, params->config_data, ORBIS_AT9_CONFIG_DATA_SIZE); AjmAt9Decoder::Reset(); - m_pcm_buffer.resize(m_codec_info.frameSamples * m_codec_info.channels, 0); + m_pcm_buffer.resize(m_codec_info.frameSamples * m_codec_info.channels * GetPointCodeSize(), 0); +} + +u8 AjmAt9Decoder::GetPointCodeSize() { + switch (m_format) { + case AjmFormatEncoding::S16: + return sizeof(s16); + case AjmFormatEncoding::S32: + return sizeof(s32); + case AjmFormatEncoding::Float: + return sizeof(float); + default: + UNREACHABLE(); + } } void AjmAt9Decoder::GetInfo(void* out_info) { @@ -53,28 +66,56 @@ void AjmAt9Decoder::GetInfo(void* out_info) { std::tuple AjmAt9Decoder::ProcessData(std::span& in_buf, SparseOutputBuffer& output, AjmSidebandGaplessDecode& gapless, - u32 max_samples_per_channel) { + std::optional max_samples_per_channel) { + int ret = 0; int bytes_used = 0; - u32 ret = Atrac9Decode(m_handle, in_buf.data(), m_pcm_buffer.data(), &bytes_used); + switch (m_format) { + case AjmFormatEncoding::S16: + ret = Atrac9Decode(m_handle, in_buf.data(), reinterpret_cast(m_pcm_buffer.data()), + &bytes_used, True(m_flags & AjmAt9CodecFlags::NonInterleavedOutput)); + break; + case AjmFormatEncoding::S32: + ret = Atrac9DecodeS32(m_handle, in_buf.data(), reinterpret_cast(m_pcm_buffer.data()), + &bytes_used, True(m_flags & AjmAt9CodecFlags::NonInterleavedOutput)); + break; + case AjmFormatEncoding::Float: + ret = + Atrac9DecodeF32(m_handle, in_buf.data(), reinterpret_cast(m_pcm_buffer.data()), + &bytes_used, True(m_flags & AjmAt9CodecFlags::NonInterleavedOutput)); + break; + default: + UNREACHABLE(); + } ASSERT_MSG(ret == At9Status::ERR_SUCCESS, "Atrac9Decode failed ret = {:#x}", ret); in_buf = in_buf.subspan(bytes_used); m_superframe_bytes_remain -= bytes_used; - std::span pcm_data{m_pcm_buffer}; + u32 skipped_samples = 0; if (gapless.skipped_samples < gapless.skip_samples) { - const auto skipped_samples = std::min(u32(m_codec_info.frameSamples), - u32(gapless.skip_samples - gapless.skipped_samples)); + skipped_samples = std::min(u32(m_codec_info.frameSamples), + u32(gapless.skip_samples - gapless.skipped_samples)); gapless.skipped_samples += skipped_samples; - pcm_data = pcm_data.subspan(skipped_samples * m_codec_info.channels); } - const auto max_samples = max_samples_per_channel == std::numeric_limits::max() - ? max_samples_per_channel - : max_samples_per_channel * m_codec_info.channels; + const auto max_samples = max_samples_per_channel.has_value() + ? max_samples_per_channel.value() * m_codec_info.channels + : std::numeric_limits::max(); - const auto pcm_size = std::min(u32(pcm_data.size()), max_samples); - const auto written = output.Write(pcm_data.subspan(0, pcm_size)); + size_t samples_written = 0; + switch (m_format) { + case AjmFormatEncoding::S16: + samples_written = WriteOutputSamples(output, skipped_samples, max_samples); + break; + case AjmFormatEncoding::S32: + samples_written = WriteOutputSamples(output, skipped_samples, max_samples); + break; + case AjmFormatEncoding::Float: + samples_written = WriteOutputSamples(output, skipped_samples, max_samples); + break; + default: + UNREACHABLE(); + } m_num_frames += 1; if ((m_num_frames % m_codec_info.framesInSuperframe) == 0) { @@ -85,7 +126,18 @@ std::tuple AjmAt9Decoder::ProcessData(std::span& in_buf, SparseOut m_num_frames = 0; } - return {1, (written / m_codec_info.channels) / sizeof(s16)}; + return {1, samples_written / m_codec_info.channels}; +} + +AjmSidebandFormat AjmAt9Decoder::GetFormat() { + return AjmSidebandFormat{ + .num_channels = u32(m_codec_info.channels), + .channel_mask = GetChannelMask(u32(m_codec_info.channels)), + .sampl_freq = u32(m_codec_info.samplingRate), + .sample_encoding = m_format, + .bitrate = u32(m_codec_info.samplingRate * GetPointCodeSize() * 8), + .reserved = 0, + }; } } // namespace Libraries::Ajm diff --git a/src/core/libraries/ajm/ajm_at9.h b/src/core/libraries/ajm/ajm_at9.h index e89e69cec..e5f65db93 100644 --- a/src/core/libraries/ajm/ajm_at9.h +++ b/src/core/libraries/ajm/ajm_at9.h @@ -8,10 +8,18 @@ #include "libatrac9.h" +#include + namespace Libraries::Ajm { constexpr s32 ORBIS_AJM_DEC_AT9_MAX_CHANNELS = 8; +enum AjmAt9CodecFlags : u32 { + ParseRiffHeader = 1 << 0, + NonInterleavedOutput = 1 << 8, +}; +DECLARE_ENUM_FLAG_OPERATORS(AjmAt9CodecFlags) + struct AjmSidebandDecAt9CodecInfo { u32 super_frame_size; u32 frames_in_super_frame; @@ -20,22 +28,37 @@ struct AjmSidebandDecAt9CodecInfo { }; struct AjmAt9Decoder final : AjmCodec { - explicit AjmAt9Decoder(); + explicit AjmAt9Decoder(AjmFormatEncoding format, AjmAt9CodecFlags flags); ~AjmAt9Decoder() override; void Reset() override; void Initialize(const void* buffer, u32 buffer_size) override; void GetInfo(void* out_info) override; + AjmSidebandFormat GetFormat() override; std::tuple ProcessData(std::span& input, SparseOutputBuffer& output, - AjmSidebandGaplessDecode& gapless, u32 max_samples) override; + AjmSidebandGaplessDecode& gapless, + std::optional max_samples) override; private: + u8 GetPointCodeSize(); + + template + size_t WriteOutputSamples(SparseOutputBuffer& output, u32 skipped_samples, u32 max_samples) { + std::span pcm_data{reinterpret_cast(m_pcm_buffer.data()), + m_pcm_buffer.size() / sizeof(T)}; + pcm_data = pcm_data.subspan(skipped_samples * m_codec_info.channels); + const auto pcm_size = std::min(u32(pcm_data.size()), max_samples); + return output.Write(pcm_data.subspan(0, pcm_size)); + } + + const AjmFormatEncoding m_format; + const AjmAt9CodecFlags m_flags; void* m_handle{}; u8 m_config_data[ORBIS_AT9_CONFIG_DATA_SIZE]{}; u32 m_superframe_bytes_remain{}; u32 m_num_frames{}; Atrac9CodecInfo m_codec_info{}; - std::vector m_pcm_buffer; + std::vector m_pcm_buffer; }; } // namespace Libraries::Ajm diff --git a/src/core/libraries/ajm/ajm_context.cpp b/src/core/libraries/ajm/ajm_context.cpp index d831ab484..e30e1c478 100644 --- a/src/core/libraries/ajm/ajm_context.cpp +++ b/src/core/libraries/ajm/ajm_context.cpp @@ -149,7 +149,6 @@ s32 AjmContext::InstanceCreate(AjmCodecType codec_type, AjmInstanceFlags flags, if (!IsRegistered(codec_type)) { return ORBIS_AJM_ERROR_CODEC_NOT_REGISTERED; } - ASSERT_MSG(flags.format == 0, "Only signed 16-bit PCM output is supported currently!"); std::optional opt_index; { std::unique_lock lock(instances_mutex); diff --git a/src/core/libraries/ajm/ajm_instance.cpp b/src/core/libraries/ajm/ajm_instance.cpp index 259fbc268..85a6f54a1 100644 --- a/src/core/libraries/ajm/ajm_instance.cpp +++ b/src/core/libraries/ajm/ajm_instance.cpp @@ -9,14 +9,28 @@ namespace Libraries::Ajm { +constexpr int ORBIS_AJM_RESULT_NOT_INITIALIZED = 0x00000001; +constexpr int ORBIS_AJM_RESULT_INVALID_DATA = 0x00000002; +constexpr int ORBIS_AJM_RESULT_INVALID_PARAMETER = 0x00000004; +constexpr int ORBIS_AJM_RESULT_PARTIAL_INPUT = 0x00000008; +constexpr int ORBIS_AJM_RESULT_NOT_ENOUGH_ROOM = 0x00000010; +constexpr int ORBIS_AJM_RESULT_STREAM_CHANGE = 0x00000020; +constexpr int ORBIS_AJM_RESULT_TOO_MANY_CHANNELS = 0x00000040; +constexpr int ORBIS_AJM_RESULT_UNSUPPORTED_FLAG = 0x00000080; +constexpr int ORBIS_AJM_RESULT_SIDEBAND_TRUNCATED = 0x00000100; +constexpr int ORBIS_AJM_RESULT_PRIORITY_PASSED = 0x00000200; +constexpr int ORBIS_AJM_RESULT_CODEC_ERROR = 0x40000000; +constexpr int ORBIS_AJM_RESULT_FATAL = 0x80000000; + AjmInstance::AjmInstance(AjmCodecType codec_type, AjmInstanceFlags flags) : m_flags(flags) { switch (codec_type) { case AjmCodecType::At9Dec: { - m_codec = std::make_unique(); + m_codec = std::make_unique(AjmFormatEncoding(flags.format), + AjmAt9CodecFlags(flags.codec)); break; } case AjmCodecType::Mp3Dec: { - m_codec = std::make_unique(); + m_codec = std::make_unique(AjmFormatEncoding(flags.format)); break; } default: @@ -62,9 +76,10 @@ void AjmInstance::ExecuteJob(AjmJob& job) { auto in_size = in_buf.size(); auto out_size = out_buf.Size(); while (!in_buf.empty() && !out_buf.IsEmpty() && !IsGaplessEnd()) { - const u32 samples_remain = m_gapless.total_samples != 0 - ? m_gapless.total_samples - m_gapless_samples - : std::numeric_limits::max(); + const auto samples_remain = + m_gapless.total_samples != 0 + ? std::optional{m_gapless.total_samples - m_gapless_samples} + : std::optional{}; const auto [nframes, nsamples] = m_codec->ProcessData(in_buf, out_buf, m_gapless, samples_remain); frames_decoded += nframes; @@ -87,6 +102,9 @@ void AjmInstance::ExecuteJob(AjmJob& job) { m_gapless.skipped_samples = 0; m_codec->Reset(); } + if (job.output.p_format != nullptr) { + *job.output.p_format = m_codec->GetFormat(); + } if (job.output.p_gapless_decode != nullptr) { *job.output.p_gapless_decode = m_gapless; } diff --git a/src/core/libraries/ajm/ajm_instance.h b/src/core/libraries/ajm/ajm_instance.h index 7c00c43ad..d1d398ae8 100644 --- a/src/core/libraries/ajm/ajm_instance.h +++ b/src/core/libraries/ajm/ajm_instance.h @@ -14,19 +14,6 @@ namespace Libraries::Ajm { -constexpr int ORBIS_AJM_RESULT_NOT_INITIALIZED = 0x00000001; -constexpr int ORBIS_AJM_RESULT_INVALID_DATA = 0x00000002; -constexpr int ORBIS_AJM_RESULT_INVALID_PARAMETER = 0x00000004; -constexpr int ORBIS_AJM_RESULT_PARTIAL_INPUT = 0x00000008; -constexpr int ORBIS_AJM_RESULT_NOT_ENOUGH_ROOM = 0x00000010; -constexpr int ORBIS_AJM_RESULT_STREAM_CHANGE = 0x00000020; -constexpr int ORBIS_AJM_RESULT_TOO_MANY_CHANNELS = 0x00000040; -constexpr int ORBIS_AJM_RESULT_UNSUPPORTED_FLAG = 0x00000080; -constexpr int ORBIS_AJM_RESULT_SIDEBAND_TRUNCATED = 0x00000100; -constexpr int ORBIS_AJM_RESULT_PRIORITY_PASSED = 0x00000200; -constexpr int ORBIS_AJM_RESULT_CODEC_ERROR = 0x40000000; -constexpr int ORBIS_AJM_RESULT_FATAL = 0x80000000; - class SparseOutputBuffer { public: SparseOutputBuffer(std::span> chunks) @@ -34,18 +21,19 @@ public: template size_t Write(std::span pcm) { - size_t bytes_written = 0; + size_t samples_written = 0; while (!pcm.empty() && !IsEmpty()) { auto size = std::min(pcm.size() * sizeof(T), m_current->size()); std::memcpy(m_current->data(), pcm.data(), size); - bytes_written += size; - pcm = pcm.subspan(size / sizeof(T)); + const auto nsamples = size / sizeof(T); + samples_written += nsamples; + pcm = pcm.subspan(nsamples); *m_current = m_current->subspan(size); if (m_current->empty()) { ++m_current; } } - return bytes_written; + return samples_written; } bool IsEmpty() { @@ -65,11 +53,6 @@ private: std::span>::iterator m_current; }; -struct DecodeResult { - u32 bytes_consumed{}; - u32 bytes_written{}; -}; - class AjmCodec { public: virtual ~AjmCodec() = default; @@ -77,9 +60,10 @@ public: virtual void Initialize(const void* buffer, u32 buffer_size) = 0; virtual void Reset() = 0; virtual void GetInfo(void* out_info) = 0; + virtual AjmSidebandFormat GetFormat() = 0; virtual std::tuple ProcessData(std::span& input, SparseOutputBuffer& output, AjmSidebandGaplessDecode& gapless, - u32 max_samples) = 0; + std::optional max_samples_per_channel) = 0; }; class AjmInstance { @@ -100,9 +84,6 @@ private: u32 m_total_samples{}; std::unique_ptr m_codec; - - // AjmCodecType codec_type; - // u32 index{}; }; } // namespace Libraries::Ajm diff --git a/src/core/libraries/ajm/ajm_mp3.cpp b/src/core/libraries/ajm/ajm_mp3.cpp index 542b31bee..eb65fe2a8 100644 --- a/src/core/libraries/ajm/ajm_mp3.cpp +++ b/src/core/libraries/ajm/ajm_mp3.cpp @@ -30,9 +30,27 @@ static constexpr std::array UnkTable = {0x48, 0x90}; SwrContext* swr_context{}; -AVFrame* ConvertAudioFrame(AVFrame* frame) { +static AVSampleFormat AjmToAVSampleFormat(AjmFormatEncoding format) { + switch (format) { + case AjmFormatEncoding::S16: + return AV_SAMPLE_FMT_S16; + case AjmFormatEncoding::S32: + return AV_SAMPLE_FMT_S32; + case AjmFormatEncoding::Float: + return AV_SAMPLE_FMT_FLT; + default: + UNREACHABLE(); + } +} + +AVFrame* AjmMp3Decoder::ConvertAudioFrame(AVFrame* frame) { + AVSampleFormat format = AjmToAVSampleFormat(m_format); + if (frame->format == format) { + return frame; + } + auto pcm16_frame = av_frame_clone(frame); - pcm16_frame->format = AV_SAMPLE_FMT_S16; + pcm16_frame->format = format; if (swr_context) { swr_free(&swr_context); @@ -40,9 +58,9 @@ AVFrame* ConvertAudioFrame(AVFrame* frame) { } AVChannelLayout in_ch_layout = frame->ch_layout; AVChannelLayout out_ch_layout = pcm16_frame->ch_layout; - swr_alloc_set_opts2(&swr_context, &out_ch_layout, AV_SAMPLE_FMT_S16, frame->sample_rate, - &in_ch_layout, AVSampleFormat(frame->format), frame->sample_rate, 0, - nullptr); + swr_alloc_set_opts2(&swr_context, &out_ch_layout, AVSampleFormat(pcm16_frame->format), + frame->sample_rate, &in_ch_layout, AVSampleFormat(frame->format), + frame->sample_rate, 0, nullptr); swr_init(swr_context); const auto res = swr_convert_frame(swr_context, pcm16_frame, frame); if (res < 0) { @@ -53,11 +71,9 @@ AVFrame* ConvertAudioFrame(AVFrame* frame) { return pcm16_frame; } -AjmMp3Decoder::AjmMp3Decoder() { - m_codec = avcodec_find_decoder(AV_CODEC_ID_MP3); - ASSERT_MSG(m_codec, "MP3 m_codec not found"); - m_parser = av_parser_init(m_codec->id); - ASSERT_MSG(m_parser, "Parser not found"); +AjmMp3Decoder::AjmMp3Decoder(AjmFormatEncoding format) + : m_format(format), m_codec(avcodec_find_decoder(AV_CODEC_ID_MP3)), + m_parser(av_parser_init(m_codec->id)) { AjmMp3Decoder::Reset(); } @@ -81,7 +97,7 @@ void AjmMp3Decoder::GetInfo(void* out_info) { std::tuple AjmMp3Decoder::ProcessData(std::span& in_buf, SparseOutputBuffer& output, AjmSidebandGaplessDecode& gapless, - u32 max_samples) { + std::optional max_samples_per_channel) { AVPacket* pkt = av_packet_alloc(); int ret = av_parser_parse2(m_parser, m_codec_context, &pkt->data, &pkt->size, in_buf.data(), @@ -109,24 +125,37 @@ std::tuple AjmMp3Decoder::ProcessData(std::span& in_buf, SparseOut } else if (ret < 0) { UNREACHABLE_MSG("Error during decoding"); } - if (frame->format != AV_SAMPLE_FMT_S16) { - frame = ConvertAudioFrame(frame); - } + frame = ConvertAudioFrame(frame); frames_decoded += 1; - samples_decoded += frame->nb_samples; - const auto size = frame->ch_layout.nb_channels * frame->nb_samples * sizeof(u16); - std::span pcm_data(reinterpret_cast(frame->data[0]), size >> 1); + u32 skipped_samples = 0; if (gapless.skipped_samples < gapless.skip_samples) { - const auto skipped_samples = std::min( - u32(frame->nb_samples), u32(gapless.skip_samples - gapless.skipped_samples)); + skipped_samples = std::min(u32(frame->nb_samples), + u32(gapless.skip_samples - gapless.skipped_samples)); gapless.skipped_samples += skipped_samples; - pcm_data = pcm_data.subspan(skipped_samples * frame->ch_layout.nb_channels); - samples_decoded -= skipped_samples; } - const auto pcm_size = std::min(u32(pcm_data.size()), max_samples); - output.Write(pcm_data.subspan(0, pcm_size)); + const auto max_samples = + max_samples_per_channel.has_value() + ? max_samples_per_channel.value() * frame->ch_layout.nb_channels + : std::numeric_limits::max(); + + switch (m_format) { + case AjmFormatEncoding::S16: + samples_decoded += + WriteOutputSamples(frame, output, skipped_samples, max_samples); + break; + case AjmFormatEncoding::S32: + samples_decoded += + WriteOutputSamples(frame, output, skipped_samples, max_samples); + break; + case AjmFormatEncoding::Float: + samples_decoded += + WriteOutputSamples(frame, output, skipped_samples, max_samples); + break; + default: + UNREACHABLE(); + } av_frame_free(&frame); } @@ -163,4 +192,9 @@ int AjmMp3Decoder::ParseMp3Header(const u8* buf, u32 stream_size, int parse_ofl, return ORBIS_OK; } +AjmSidebandFormat AjmMp3Decoder::GetFormat() { + LOG_ERROR(Lib_Ajm, "Unimplemented"); + return AjmSidebandFormat{}; +}; + } // namespace Libraries::Ajm diff --git a/src/core/libraries/ajm/ajm_mp3.h b/src/core/libraries/ajm/ajm_mp3.h index d2acf7abc..0ae956d61 100644 --- a/src/core/libraries/ajm/ajm_mp3.h +++ b/src/core/libraries/ajm/ajm_mp3.h @@ -7,11 +7,7 @@ #include "core/libraries/ajm/ajm_instance.h" extern "C" { -struct AVCodec; -struct AVCodecContext; -struct AVCodecParserContext; -struct AVFrame; -struct AVPacket; +#include } namespace Libraries::Ajm { @@ -54,19 +50,35 @@ struct AjmSidebandDecMp3CodecInfo { class AjmMp3Decoder : public AjmCodec { public: - explicit AjmMp3Decoder(); + explicit AjmMp3Decoder(AjmFormatEncoding format); ~AjmMp3Decoder() override; void Reset() override; void Initialize(const void* buffer, u32 buffer_size) override {} void GetInfo(void* out_info) override; + AjmSidebandFormat GetFormat() override; std::tuple ProcessData(std::span& input, SparseOutputBuffer& output, - AjmSidebandGaplessDecode& gapless, u32 max_samples) override; + AjmSidebandGaplessDecode& gapless, + std::optional max_samples_per_channel) override; static int ParseMp3Header(const u8* buf, u32 stream_size, int parse_ofl, AjmDecMp3ParseFrame* frame); private: + template + size_t WriteOutputSamples(AVFrame* frame, SparseOutputBuffer& output, u32 skipped_samples, + u32 max_samples) { + const auto size = frame->ch_layout.nb_channels * frame->nb_samples * sizeof(T); + std::span pcm_data(reinterpret_cast(frame->data[0]), size >> 1); + pcm_data = pcm_data.subspan(skipped_samples * frame->ch_layout.nb_channels); + const auto pcm_size = std::min(u32(pcm_data.size()), max_samples); + const auto samples_written = output.Write(pcm_data.subspan(0, pcm_size)); + return samples_written / frame->ch_layout.nb_channels; + } + + AVFrame* ConvertAudioFrame(AVFrame* frame); + + const AjmFormatEncoding m_format; const AVCodec* m_codec = nullptr; AVCodecContext* m_codec_context = nullptr; AVCodecParserContext* m_parser = nullptr; From f45cad6bc970d97f5a2b00913f8014a5ef08aeb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quang=20Ng=C3=B4?= Date: Thu, 7 Nov 2024 03:44:22 +0700 Subject: [PATCH 005/549] Fix patches not save changes properly (#1493) Don't use ```QCheckBox::text()``` because it may return strings that contain "&" cause patch name comparison to fail. --- src/qt_gui/cheats_patches.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/qt_gui/cheats_patches.cpp b/src/qt_gui/cheats_patches.cpp index 6e2e4f208..a35136f12 100644 --- a/src/qt_gui/cheats_patches.cpp +++ b/src/qt_gui/cheats_patches.cpp @@ -434,7 +434,9 @@ QCheckBox* CheatsPatches::findCheckBoxByName(const QString& name) { QWidget* widget = item->widget(); QCheckBox* checkBox = qobject_cast(widget); if (checkBox) { - if (checkBox->text().toStdString().find(name.toStdString()) != std::string::npos) { + const auto patchName = checkBox->property("patchName"); + if (patchName.isValid() && patchName.toString().toStdString().find( + name.toStdString()) != std::string::npos) { return checkBox; } } @@ -1176,6 +1178,7 @@ void CheatsPatches::addPatchesToLayout(const QString& filePath) { if (!patchName.isEmpty() && !patchLines.isEmpty()) { QCheckBox* patchCheckBox = new QCheckBox(patchName); + patchCheckBox->setProperty("patchName", patchName); patchCheckBox->setChecked(isEnabled); patchesGroupBoxLayout->addWidget(patchCheckBox); @@ -1349,8 +1352,10 @@ bool CheatsPatches::eventFilter(QObject* obj, QEvent* event) { void CheatsPatches::onPatchCheckBoxHovered(QCheckBox* checkBox, bool hovered) { if (hovered) { - QString text = checkBox->text(); - updateNoteTextEdit(text); + const auto patchName = checkBox->property("patchName"); + if (patchName.isValid()) { + updateNoteTextEdit(patchName.toString()); + } } else { instructionsTextEdit->setText(defaultTextEdit); } From a547b2774fd3e2c34b2bab077a964cc0471ae927 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quang=20Ng=C3=B4?= Date: Thu, 7 Nov 2024 19:56:02 +0700 Subject: [PATCH 006/549] Allow disable the auto updater (#1490) --- .github/workflows/build.yml | 6 +++--- CMakeLists.txt | 17 +++++++++++++---- src/qt_gui/main_window.cpp | 10 ++++++++++ src/qt_gui/main_window.h | 2 ++ src/qt_gui/main_window_ui.h | 8 ++++++++ src/qt_gui/settings_dialog.cpp | 13 +++++++++++++ 6 files changed, 49 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 44aa3dd37..976314f90 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -143,7 +143,7 @@ jobs: arch: amd64 - name: Configure CMake - run: cmake --fresh -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_QT_GUI=ON -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + run: cmake --fresh -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel @@ -265,7 +265,7 @@ jobs: variant: sccache - name: Configure CMake - run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_OSX_ARCHITECTURES=x86_64 -DENABLE_QT_GUI=ON -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache + run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_OSX_ARCHITECTURES=x86_64 -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(sysctl -n hw.ncpu) @@ -368,7 +368,7 @@ jobs: key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }} - name: Configure CMake - run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DENABLE_QT_GUI=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel3 diff --git a/CMakeLists.txt b/CMakeLists.txt index d9479e851..036f74309 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,7 @@ endif() option(ENABLE_QT_GUI "Enable the Qt GUI. If not selected then the emulator uses a minimal SDL-based UI instead" OFF) option(ENABLE_DISCORD_RPC "Enable the Discord RPC integration" ON) +option(ENABLE_UPDATER "Enables the options to updater" ON) # First, determine whether to use CMAKE_OSX_ARCHITECTURES or CMAKE_SYSTEM_PROCESSOR. if (APPLE AND CMAKE_OSX_ARCHITECTURES) @@ -732,6 +733,12 @@ set(EMULATOR src/emulator.cpp if(ENABLE_QT_GUI) qt_add_resources(RESOURCE_FILES src/shadps4.qrc) +if (ENABLE_UPDATER) + set(UPDATER src/qt_gui/check_update.cpp + src/qt_gui/check_update.h + ) +endif() + set(QT_GUI src/qt_gui/about_dialog.cpp src/qt_gui/about_dialog.h src/qt_gui/about_dialog.ui @@ -739,8 +746,6 @@ set(QT_GUI src/qt_gui/about_dialog.cpp src/qt_gui/background_music_player.h src/qt_gui/cheats_patches.cpp src/qt_gui/cheats_patches.h - src/qt_gui/check_update.cpp - src/qt_gui/check_update.h src/qt_gui/main_window_ui.h src/qt_gui/main_window.cpp src/qt_gui/main_window.h @@ -771,6 +776,7 @@ set(QT_GUI src/qt_gui/about_dialog.cpp ${EMULATOR} ${RESOURCE_FILES} ${TRANSLATIONS} + ${UPDATER} ) endif() @@ -843,8 +849,11 @@ else() endif() if (ENABLE_QT_GUI) - target_link_libraries(shadps4 PRIVATE Qt6::Widgets Qt6::Concurrent Qt6::Network Qt6::Multimedia) - add_definitions(-DENABLE_QT_GUI) + target_link_libraries(shadps4 PRIVATE Qt6::Widgets Qt6::Concurrent Qt6::Network Qt6::Multimedia) + add_definitions(-DENABLE_QT_GUI) + if (ENABLE_UPDATER) + add_definitions(-DENABLE_UPDATER) + endif() endif() if (WIN32) diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 8e56a6e9d..959b055c8 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -7,7 +7,9 @@ #include "about_dialog.h" #include "cheats_patches.h" +#ifdef ENABLE_UPDATER #include "check_update.h" +#endif #include "common/io_file.h" #include "common/path_util.h" #include "common/scm_rev.h" @@ -59,8 +61,10 @@ bool MainWindow::Init() { this->show(); // load game list LoadGameLists(); +#ifdef ENABLE_UPDATER // Check for update CheckUpdateMain(true); +#endif auto end = std::chrono::steady_clock::now(); auto duration = std::chrono::duration_cast(end - start); @@ -183,6 +187,7 @@ void MainWindow::LoadGameLists() { } } +#ifdef ENABLE_UPDATER void MainWindow::CheckUpdateMain(bool checkSave) { if (checkSave) { if (!Config::autoUpdate()) { @@ -192,6 +197,7 @@ void MainWindow::CheckUpdateMain(bool checkSave) { auto checkUpdate = new CheckUpdate(false); checkUpdate->exec(); } +#endif void MainWindow::GetPhysicalDevices() { Vulkan::Instance instance(false, false); @@ -254,10 +260,12 @@ void MainWindow::CreateConnects() { settingsDialog->exec(); }); +#ifdef ENABLE_UPDATER connect(ui->updaterAct, &QAction::triggered, this, [this]() { auto checkUpdate = new CheckUpdate(true); checkUpdate->exec(); }); +#endif connect(ui->aboutAct, &QAction::triggered, this, [this]() { auto aboutDialog = new AboutDialog(this); @@ -933,7 +941,9 @@ void MainWindow::SetUiIcons(bool isWhite) { ui->bootInstallPkgAct->setIcon(RecolorIcon(ui->bootInstallPkgAct->icon(), isWhite)); ui->bootGameAct->setIcon(RecolorIcon(ui->bootGameAct->icon(), isWhite)); ui->exitAct->setIcon(RecolorIcon(ui->exitAct->icon(), isWhite)); +#ifdef ENABLE_UPDATER ui->updaterAct->setIcon(RecolorIcon(ui->updaterAct->icon(), isWhite)); +#endif ui->downloadCheatsPatchesAct->setIcon( RecolorIcon(ui->downloadCheatsPatchesAct->icon(), isWhite)); ui->dumpGameListAct->setIcon(RecolorIcon(ui->dumpGameListAct->icon(), isWhite)); diff --git a/src/qt_gui/main_window.h b/src/qt_gui/main_window.h index 6264978aa..7dc85f0a7 100644 --- a/src/qt_gui/main_window.h +++ b/src/qt_gui/main_window.h @@ -56,7 +56,9 @@ private: void CreateDockWindows(); void GetPhysicalDevices(); void LoadGameLists(); +#ifdef ENABLE_UPDATER void CheckUpdateMain(bool checkSave); +#endif void CreateConnects(); void SetLastUsedTheme(); void SetLastIconSizeBullet(); diff --git a/src/qt_gui/main_window_ui.h b/src/qt_gui/main_window_ui.h index 373b2924e..a51e37d1e 100644 --- a/src/qt_gui/main_window_ui.h +++ b/src/qt_gui/main_window_ui.h @@ -26,7 +26,9 @@ public: QAction* downloadCheatsPatchesAct; QAction* dumpGameListAct; QAction* pkgViewerAct; +#ifdef ENABLE_UPDATER QAction* updaterAct; +#endif QAction* aboutAct; QAction* configureAct; QAction* setThemeDark; @@ -130,9 +132,11 @@ public: pkgViewerAct = new QAction(MainWindow); pkgViewerAct->setObjectName("pkgViewer"); pkgViewerAct->setIcon(QIcon(":images/file_icon.png")); +#ifdef ENABLE_UPDATER updaterAct = new QAction(MainWindow); updaterAct->setObjectName("updaterAct"); updaterAct->setIcon(QIcon(":images/update_icon.png")); +#endif aboutAct = new QAction(MainWindow); aboutAct->setObjectName("aboutAct"); aboutAct->setIcon(QIcon(":images/about_icon.png")); @@ -291,7 +295,9 @@ public: menuUtils->addAction(downloadCheatsPatchesAct); menuUtils->addAction(dumpGameListAct); menuUtils->addAction(pkgViewerAct); +#ifdef ENABLE_UPDATER menuHelp->addAction(updaterAct); +#endif menuHelp->addAction(aboutAct); retranslateUi(MainWindow); @@ -306,8 +312,10 @@ public: bootInstallPkgAct->setText( QCoreApplication::translate("MainWindow", "Install Packages (PKG)", nullptr)); bootGameAct->setText(QCoreApplication::translate("MainWindow", "Boot Game", nullptr)); +#ifdef ENABLE_UPDATER updaterAct->setText( QCoreApplication::translate("MainWindow", "Check for Updates", nullptr)); +#endif aboutAct->setText(QCoreApplication::translate("MainWindow", "About shadPS4", nullptr)); configureAct->setText(QCoreApplication::translate("MainWindow", "Configure...", nullptr)); #if QT_CONFIG(tooltip) diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index 84dc5011e..da2e3bd11 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -6,7 +6,9 @@ #include #include +#ifdef ENABLE_UPDATER #include "check_update.h" +#endif #include "common/logging/backend.h" #include "common/logging/filter.h" #include "main_window.h" @@ -143,6 +145,7 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge connect(ui->logFilterLineEdit, &QLineEdit::textChanged, this, [](const QString& text) { Config::setLogFilter(text.toStdString()); }); +#ifdef ENABLE_UPDATER connect(ui->updateCheckBox, &QCheckBox::stateChanged, this, [](int state) { Config::setAutoUpdate(state == Qt::Checked); }); @@ -153,6 +156,10 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge auto checkUpdate = new CheckUpdate(true); checkUpdate->exec(); }); +#else + ui->updaterGroupBox->setVisible(false); + ui->GUIgroupBox->setMaximumSize(265, 16777215); +#endif connect(ui->playBGMCheckBox, &QCheckBox::stateChanged, this, [](int val) { Config::setPlayBGM(val); @@ -278,7 +285,9 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge ui->userName->installEventFilter(this); ui->logTypeGroupBox->installEventFilter(this); ui->logFilter->installEventFilter(this); +#ifdef ENABLE_UPDATER ui->updaterGroupBox->installEventFilter(this); +#endif ui->GUIgroupBox->installEventFilter(this); // Input @@ -340,6 +349,7 @@ void SettingsDialog::LoadValuesFromConfig() { ui->vkSyncValidationCheckBox->setChecked(Config::vkValidationSyncEnabled()); ui->rdocCheckBox->setChecked(Config::isRdocEnabled()); +#ifdef ENABLE_UPDATER ui->updateCheckBox->setChecked(Config::autoUpdate()); std::string updateChannel = Config::getUpdateChannel(); if (updateChannel != "Release" && updateChannel != "Nightly") { @@ -350,6 +360,7 @@ void SettingsDialog::LoadValuesFromConfig() { } } ui->updateComboBox->setCurrentText(QString::fromStdString(updateChannel)); +#endif for (const auto& dir : Config::getGameInstallDirs()) { QString path_string; @@ -451,8 +462,10 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) { text = tr("logTypeGroupBox"); } else if (elementName == "logFilter") { text = tr("logFilter"); +#ifdef ENABLE_UPDATER } else if (elementName == "updaterGroupBox") { text = tr("updaterGroupBox"); +#endif } else if (elementName == "GUIgroupBox") { text = tr("GUIgroupBox"); } From ed0a9c0e4ec5e4b47900e7d340a511ecefc868b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quang=20Ng=C3=B4?= Date: Thu, 7 Nov 2024 19:56:24 +0700 Subject: [PATCH 007/549] Fix emulator name in desktop entry for appimages (#1491) --- .github/linux-appimage-qt.sh | 2 +- .github/linux-appimage-sdl.sh | 2 +- .github/shadps4.desktop | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/linux-appimage-qt.sh b/.github/linux-appimage-qt.sh index 06d5cbc11..8a5f340f6 100755 --- a/.github/linux-appimage-qt.sh +++ b/.github/linux-appimage-qt.sh @@ -30,4 +30,4 @@ cp -a "$GITHUB_WORKSPACE/build/translations" AppDir/usr/bin ./linuxdeploy-x86_64.AppImage --appdir AppDir -d "$GITHUB_WORKSPACE"/.github/shadps4.desktop -e "$GITHUB_WORKSPACE"/build/shadps4 -i "$GITHUB_WORKSPACE"/.github/shadps4.png --plugin qt rm AppDir/usr/plugins/multimedia/libgstreamermediaplugin.so ./linuxdeploy-x86_64.AppImage --appdir AppDir --output appimage -mv Shadps4-x86_64.AppImage Shadps4-qt.AppImage +mv shadPS4-x86_64.AppImage Shadps4-qt.AppImage diff --git a/.github/linux-appimage-sdl.sh b/.github/linux-appimage-sdl.sh index cc0e9f5fa..cf0ce4de1 100755 --- a/.github/linux-appimage-sdl.sh +++ b/.github/linux-appimage-sdl.sh @@ -18,4 +18,4 @@ chmod a+x linuxdeploy-plugin-checkrt-x86_64.sh ./linuxdeploy-x86_64.AppImage --appdir AppDir ./linuxdeploy-plugin-checkrt-x86_64.sh --appdir AppDir ./linuxdeploy-x86_64.AppImage --appdir AppDir -d "$GITHUB_WORKSPACE"/.github/shadps4.desktop -e "$GITHUB_WORKSPACE"/build/shadps4 -i "$GITHUB_WORKSPACE"/.github/shadps4.png --output appimage -mv Shadps4-x86_64.AppImage Shadps4-sdl.AppImage +mv shadPS4-x86_64.AppImage Shadps4-sdl.AppImage diff --git a/.github/shadps4.desktop b/.github/shadps4.desktop index 095acb787..6dc663718 100644 --- a/.github/shadps4.desktop +++ b/.github/shadps4.desktop @@ -1,9 +1,9 @@ [Desktop Entry] -Name=Shadps4 +Name=shadPS4 Exec=shadps4 Terminal=false Type=Application Icon=shadps4 -Comment=shadps4 emulator +Comment=shadPS4 Emulator Categories=Game; StartupWMClass=shadps4; From 176d222519b99fbbceb95c91fda7075387b8ee3e Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Thu, 7 Nov 2024 04:57:31 -0800 Subject: [PATCH 008/549] vk_pipeline_cache: Skip pipelines with geometry shaders when unsupported. (#1486) --- src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index c368f2101..da098fa37 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -322,7 +322,7 @@ bool PipelineCache::RefreshGraphicsKey() { switch (regs.stage_enable.raw) { case Liverpool::ShaderStageEnable::VgtStages::EsGs: { if (!instance.IsGeometryStageSupported() || !IsGsFeaturesSupported()) { - break; + return false; } if (!TryBindStageRemap(Shader::Stage::Export, Shader::Stage::Vertex)) { return false; From 75d2181489d45dc72feba98c96ce9b2f065835a4 Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Fri, 8 Nov 2024 08:45:03 +0300 Subject: [PATCH 009/549] ajm mp3: fix resampling (#1500) --- src/core/libraries/ajm/ajm_mp3.cpp | 40 ++++++++++++++++-------------- src/core/libraries/ajm/ajm_mp3.h | 2 ++ 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/core/libraries/ajm/ajm_mp3.cpp b/src/core/libraries/ajm/ajm_mp3.cpp index eb65fe2a8..90196bb91 100644 --- a/src/core/libraries/ajm/ajm_mp3.cpp +++ b/src/core/libraries/ajm/ajm_mp3.cpp @@ -28,8 +28,6 @@ static constexpr std::array, 2> BitrateTable = {{ static constexpr std::array UnkTable = {0x48, 0x90}; -SwrContext* swr_context{}; - static AVSampleFormat AjmToAVSampleFormat(AjmFormatEncoding format) { switch (format) { case AjmFormatEncoding::S16: @@ -49,26 +47,28 @@ AVFrame* AjmMp3Decoder::ConvertAudioFrame(AVFrame* frame) { return frame; } - auto pcm16_frame = av_frame_clone(frame); - pcm16_frame->format = format; + AVFrame* new_frame = av_frame_alloc(); + new_frame->pts = frame->pts; + new_frame->pkt_dts = frame->pkt_dts < 0 ? 0 : frame->pkt_dts; + new_frame->format = format; + new_frame->ch_layout = frame->ch_layout; + new_frame->sample_rate = frame->sample_rate; - if (swr_context) { - swr_free(&swr_context); - swr_context = nullptr; - } AVChannelLayout in_ch_layout = frame->ch_layout; - AVChannelLayout out_ch_layout = pcm16_frame->ch_layout; - swr_alloc_set_opts2(&swr_context, &out_ch_layout, AVSampleFormat(pcm16_frame->format), + AVChannelLayout out_ch_layout = new_frame->ch_layout; + swr_alloc_set_opts2(&m_swr_context, &out_ch_layout, AVSampleFormat(new_frame->format), frame->sample_rate, &in_ch_layout, AVSampleFormat(frame->format), frame->sample_rate, 0, nullptr); - swr_init(swr_context); - const auto res = swr_convert_frame(swr_context, pcm16_frame, frame); + swr_init(m_swr_context); + const auto res = swr_convert_frame(m_swr_context, new_frame, frame); if (res < 0) { LOG_ERROR(Lib_AvPlayer, "Could not convert to S16: {}", av_err2str(res)); + av_frame_free(&new_frame); + av_frame_free(&frame); return nullptr; } av_frame_free(&frame); - return pcm16_frame; + return new_frame; } AjmMp3Decoder::AjmMp3Decoder(AjmFormatEncoding format) @@ -78,6 +78,7 @@ AjmMp3Decoder::AjmMp3Decoder(AjmFormatEncoding format) } AjmMp3Decoder::~AjmMp3Decoder() { + swr_free(&m_swr_context); avcodec_free_context(&m_codec_context); } @@ -108,6 +109,11 @@ std::tuple AjmMp3Decoder::ProcessData(std::span& in_buf, SparseOut u32 frames_decoded = 0; u32 samples_decoded = 0; + auto max_samples = + max_samples_per_channel.has_value() + ? max_samples_per_channel.value() * m_codec_context->ch_layout.nb_channels + : std::numeric_limits::max(); + if (pkt->size) { // Send the packet with the compressed data to the decoder pkt->pts = m_parser->pts; @@ -121,6 +127,7 @@ std::tuple AjmMp3Decoder::ProcessData(std::span& in_buf, SparseOut AVFrame* frame = av_frame_alloc(); ret = avcodec_receive_frame(m_codec_context, frame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + av_frame_free(&frame); break; } else if (ret < 0) { UNREACHABLE_MSG("Error during decoding"); @@ -135,11 +142,6 @@ std::tuple AjmMp3Decoder::ProcessData(std::span& in_buf, SparseOut gapless.skipped_samples += skipped_samples; } - const auto max_samples = - max_samples_per_channel.has_value() - ? max_samples_per_channel.value() * frame->ch_layout.nb_channels - : std::numeric_limits::max(); - switch (m_format) { case AjmFormatEncoding::S16: samples_decoded += @@ -157,6 +159,8 @@ std::tuple AjmMp3Decoder::ProcessData(std::span& in_buf, SparseOut UNREACHABLE(); } + max_samples -= samples_decoded; + av_frame_free(&frame); } } diff --git a/src/core/libraries/ajm/ajm_mp3.h b/src/core/libraries/ajm/ajm_mp3.h index 0ae956d61..b5da19e77 100644 --- a/src/core/libraries/ajm/ajm_mp3.h +++ b/src/core/libraries/ajm/ajm_mp3.h @@ -8,6 +8,7 @@ extern "C" { #include +struct SwrContext; } namespace Libraries::Ajm { @@ -82,6 +83,7 @@ private: const AVCodec* m_codec = nullptr; AVCodecContext* m_codec_context = nullptr; AVCodecParserContext* m_parser = nullptr; + SwrContext* m_swr_context = nullptr; }; } // namespace Libraries::Ajm From f7b458c85ab2f9e3063f961e60d776e5afd95413 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sat, 9 Nov 2024 14:20:54 -0800 Subject: [PATCH 010/549] ci: Change to macOS 15 (#1508) --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 976314f90..a3b3ee8ec 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -162,7 +162,7 @@ jobs: path: upload/ macos-sdl: - runs-on: macos-latest + runs-on: macos-15 needs: get-info steps: - uses: actions/checkout@v4 @@ -218,7 +218,7 @@ jobs: path: shadps4-macos-sdl.tar.gz macos-qt: - runs-on: macos-latest + runs-on: macos-15 needs: get-info steps: - uses: actions/checkout@v4 From 4fd7f6745932ded7b810560c93cdd9aa5fd8cf3b Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Sun, 10 Nov 2024 05:14:48 -0300 Subject: [PATCH 011/549] Open Folder - improvement (Save/Log) (#1513) * Open Folder - improvement * TR --- src/qt_gui/gui_context_menus.h | 36 ++++++++++++++++++++++++++++---- src/qt_gui/translations/ar.ts | 29 ++++++++++++++++++------- src/qt_gui/translations/da_DK.ts | 29 ++++++++++++++++++------- src/qt_gui/translations/de.ts | 29 ++++++++++++++++++------- src/qt_gui/translations/el.ts | 29 ++++++++++++++++++------- src/qt_gui/translations/en.ts | 29 ++++++++++++++++++------- src/qt_gui/translations/es_ES.ts | 29 ++++++++++++++++++------- src/qt_gui/translations/fa_IR.ts | 29 ++++++++++++++++++------- src/qt_gui/translations/fi.ts | 29 ++++++++++++++++++------- src/qt_gui/translations/fr.ts | 29 ++++++++++++++++++------- src/qt_gui/translations/hu_HU.ts | 29 ++++++++++++++++++------- src/qt_gui/translations/id.ts | 29 ++++++++++++++++++------- src/qt_gui/translations/it.ts | 29 ++++++++++++++++++------- src/qt_gui/translations/ja_JP.ts | 29 ++++++++++++++++++------- src/qt_gui/translations/ko_KR.ts | 29 ++++++++++++++++++------- src/qt_gui/translations/lt_LT.ts | 29 ++++++++++++++++++------- src/qt_gui/translations/nb.ts | 29 ++++++++++++++++++------- src/qt_gui/translations/nl.ts | 29 ++++++++++++++++++------- src/qt_gui/translations/pl_PL.ts | 29 ++++++++++++++++++------- src/qt_gui/translations/pt_BR.ts | 29 ++++++++++++++++++------- src/qt_gui/translations/ro_RO.ts | 29 ++++++++++++++++++------- src/qt_gui/translations/ru_RU.ts | 29 ++++++++++++++++++------- src/qt_gui/translations/sq.ts | 29 ++++++++++++++++++------- src/qt_gui/translations/tr_TR.ts | 29 ++++++++++++++++++------- src/qt_gui/translations/uk_UA.ts | 29 ++++++++++++++++++------- src/qt_gui/translations/vi_VN.ts | 29 ++++++++++++++++++------- src/qt_gui/translations/zh_CN.ts | 29 ++++++++++++++++++------- src/qt_gui/translations/zh_TW.ts | 29 ++++++++++++++++++------- 28 files changed, 626 insertions(+), 193 deletions(-) diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h index 8d7018522..823ad921c 100644 --- a/src/qt_gui/gui_context_menus.h +++ b/src/qt_gui/gui_context_menus.h @@ -44,20 +44,31 @@ public: // Setup menu. QMenu menu(widget); + + // "Open Folder..." submenu + QMenu* openFolderMenu = new QMenu(tr("Open Folder..."), widget); + QAction* openGameFolder = new QAction(tr("Open Game Folder"), widget); + QAction* openSaveDataFolder = new QAction(tr("Open Save Data Folder"), widget); + QAction* openLogFolder = new QAction(tr("Open Log Folder"), widget); + + openFolderMenu->addAction(openGameFolder); + openFolderMenu->addAction(openSaveDataFolder); + openFolderMenu->addAction(openLogFolder); + + menu.addMenu(openFolderMenu); + QAction createShortcut(tr("Create Shortcut"), widget); - QAction openFolder(tr("Open Game Folder"), widget); QAction openCheats(tr("Cheats / Patches"), widget); QAction openSfoViewer(tr("SFO Viewer"), widget); QAction openTrophyViewer(tr("Trophy Viewer"), widget); - menu.addAction(&openFolder); menu.addAction(&createShortcut); menu.addAction(&openCheats); menu.addAction(&openSfoViewer); menu.addAction(&openTrophyViewer); // "Copy" submenu. - QMenu* copyMenu = new QMenu(tr("Copy info"), widget); + QMenu* copyMenu = new QMenu(tr("Copy info..."), widget); QAction* copyName = new QAction(tr("Copy Name"), widget); QAction* copySerial = new QAction(tr("Copy Serial"), widget); QAction* copyNameAll = new QAction(tr("Copy All"), widget); @@ -86,12 +97,29 @@ public: return; } - if (selected == &openFolder) { + if (selected == openGameFolder) { QString folderPath; Common::FS::PathToQString(folderPath, m_games[itemID].path); QDesktopServices::openUrl(QUrl::fromLocalFile(folderPath)); } + if (selected == openSaveDataFolder) { + QString userPath; + Common::FS::PathToQString(userPath, + Common::FS::GetUserPath(Common::FS::PathType::UserDir)); + QString saveDataPath = + userPath + "/savedata/1/" + QString::fromStdString(m_games[itemID].serial); + QDir(saveDataPath).mkpath(saveDataPath); + QDesktopServices::openUrl(QUrl::fromLocalFile(saveDataPath)); + } + + if (selected == openLogFolder) { + QString userPath; + Common::FS::PathToQString(userPath, + Common::FS::GetUserPath(Common::FS::PathType::UserDir)); + QDesktopServices::openUrl(QUrl::fromLocalFile(userPath + "/log")); + } + if (selected == &openSfoViewer) { PSF psf; QString game_update_path; diff --git a/src/qt_gui/translations/ar.ts b/src/qt_gui/translations/ar.ts index 457d84ef8..25e215183 100644 --- a/src/qt_gui/translations/ar.ts +++ b/src/qt_gui/translations/ar.ts @@ -100,11 +100,6 @@ Create Shortcut إنشاء اختصار - - - Open Game Folder - فتح مجلد اللعبة - Cheats / Patches @@ -120,10 +115,30 @@ Trophy Viewer عارض الجوائز + + + Open Folder... + فتح المجلد... + + + + Open Game Folder + فتح مجلد اللعبة + + + + Open Save Data Folder + فتح مجلد بيانات الحفظ + + + + Open Log Folder + فتح مجلد السجل + - Copy info - نسخ المعلومات + Copy info... + ...نسخ المعلومات diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts index e14826725..14c42f1d9 100644 --- a/src/qt_gui/translations/da_DK.ts +++ b/src/qt_gui/translations/da_DK.ts @@ -100,11 +100,6 @@ Create Shortcut Create Shortcut - - - Open Game Folder - Open Game Folder - Cheats / Patches @@ -120,10 +115,30 @@ Trophy Viewer Trophy Viewer + + + Open Folder... + Åbn Mappe... + + + + Open Game Folder + Åbn Spilmappe + + + + Open Save Data Folder + Åbn Gem Data Mappe + + + + Open Log Folder + Åbn Log Mappe + - Copy info - Copy info + Copy info... + Copy info... diff --git a/src/qt_gui/translations/de.ts b/src/qt_gui/translations/de.ts index 77b6a01ac..64a6c6480 100644 --- a/src/qt_gui/translations/de.ts +++ b/src/qt_gui/translations/de.ts @@ -100,11 +100,6 @@ Create Shortcut Verknüpfung erstellen - - - Open Game Folder - Spieleordner öffnen - Cheats / Patches @@ -120,10 +115,30 @@ Trophy Viewer Trophäen anzeigen + + + Open Folder... + Ordner öffnen... + + + + Open Game Folder + Spielordner öffnen + + + + Open Save Data Folder + Speicherordner öffnen + + + + Open Log Folder + Protokollordner öffnen + - Copy info - Infos kopieren + Copy info... + Infos kopieren... diff --git a/src/qt_gui/translations/el.ts b/src/qt_gui/translations/el.ts index a738bf90a..e064f8c26 100644 --- a/src/qt_gui/translations/el.ts +++ b/src/qt_gui/translations/el.ts @@ -100,11 +100,6 @@ Create Shortcut Create Shortcut - - - Open Game Folder - Open Game Folder - Cheats / Patches @@ -120,10 +115,30 @@ Trophy Viewer Trophy Viewer + + + Open Folder... + Άνοιγμα Φακέλου... + + + + Open Game Folder + Άνοιγμα Φακέλου Παιχνιδιού + + + + Open Save Data Folder + Άνοιγμα Φακέλου Αποθηκευμένων Δεδομένων + + + + Open Log Folder + Άνοιγμα Φακέλου Καταγραφής + - Copy info - Copy info + Copy info... + Copy info... diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index 9f25fc836..9bf7c7188 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -100,11 +100,6 @@ Create Shortcut Create Shortcut - - - Open Game Folder - Open Game Folder - Cheats / Patches @@ -120,10 +115,30 @@ Trophy Viewer Trophy Viewer + + + Open Folder... + Open Folder... + + + + Open Game Folder + Open Game Folder + + + + Open Save Data Folder + Open Save Data Folder + + + + Open Log Folder + Open Log Folder + - Copy info - Copy info + Copy info... + Copy info... diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index b0a6e4335..5d637249e 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -100,11 +100,6 @@ Create Shortcut Crear acceso directo - - - Open Game Folder - Abrir carpeta del juego - Cheats / Patches @@ -120,10 +115,30 @@ Trophy Viewer Ver trofeos + + + Open Folder... + Abrir Carpeta... + + + + Open Game Folder + Abrir Carpeta del Juego + + + + Open Save Data Folder + Abrir Carpeta de Datos Guardados + + + + Open Log Folder + Abrir Carpeta de Registros + - Copy info - Copiar información + Copy info... + Copiar información... diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index 15f5d6193..55a2fdf53 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -100,11 +100,6 @@ Create Shortcut ساخت شورتکات - - - Open Game Folder - بازکردن محل نصب بازی - Cheats / Patches @@ -120,10 +115,30 @@ Trophy Viewer مشاهده تروفی ها + + + Open Folder... + باز کردن پوشه... + - Copy info - کپی کردن اطلاعات + Open Game Folder + باز کردن پوشه بازی + + + + Open Save Data Folder + پوشه ذخیره داده را باز کنید + + + + Open Log Folder + باز کردن پوشه لاگ + + + + Copy info... + ...کپی کردن اطلاعات diff --git a/src/qt_gui/translations/fi.ts b/src/qt_gui/translations/fi.ts index cb7426e01..4d160bf6b 100644 --- a/src/qt_gui/translations/fi.ts +++ b/src/qt_gui/translations/fi.ts @@ -100,11 +100,6 @@ Create Shortcut Create Shortcut - - - Open Game Folder - Open Game Folder - Cheats / Patches @@ -120,10 +115,30 @@ Trophy Viewer Trophy Viewer + + + Open Folder... + Avaa Kansio... + + + + Open Game Folder + Avaa Pelikansio + + + + Open Save Data Folder + Avaa Tallennustiedostokansio + + + + Open Log Folder + Avaa Lokikansio + - Copy info - Copy info + Copy info... + Copy info... diff --git a/src/qt_gui/translations/fr.ts b/src/qt_gui/translations/fr.ts index 4c2d5cbdd..39cd11bf6 100644 --- a/src/qt_gui/translations/fr.ts +++ b/src/qt_gui/translations/fr.ts @@ -100,11 +100,6 @@ Create Shortcut Créer un raccourci - - - Open Game Folder - Ouvrir le dossier du jeu - Cheats / Patches @@ -120,10 +115,30 @@ Trophy Viewer Visionneuse de trophées + + + Open Folder... + Ouvrir le Dossier... + + + + Open Game Folder + Ouvrir le Dossier du Jeu + + + + Open Save Data Folder + Ouvrir le Dossier des Données de Sauvegarde + + + + Open Log Folder + Ouvrir le Dossier des Logs + - Copy info - Copier infos + Copy info... + Copier infos... diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index 633ba9810..86279b2de 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -100,11 +100,6 @@ Create Shortcut Parancsikon Létrehozása - - - Open Game Folder - Játék Mappa Megnyitása - Cheats / Patches @@ -120,10 +115,30 @@ Trophy Viewer Trófeák Megtekintése + + + Open Folder... + Mappa megnyitása... + - Copy info - Információ Másolása + Open Game Folder + Játék Mappa Megnyitása + + + + Open Save Data Folder + Mentési adatok mappa megnyitása + + + + Open Log Folder + Napló mappa megnyitása + + + + Copy info... + Információ Másolása... diff --git a/src/qt_gui/translations/id.ts b/src/qt_gui/translations/id.ts index f841ad3a8..d616f1cf3 100644 --- a/src/qt_gui/translations/id.ts +++ b/src/qt_gui/translations/id.ts @@ -100,11 +100,6 @@ Create Shortcut Create Shortcut - - - Open Game Folder - Open Game Folder - Cheats / Patches @@ -120,10 +115,30 @@ Trophy Viewer Trophy Viewer + + + Open Folder... + Buka Folder... + + + + Open Game Folder + Buka Folder Game + + + + Open Save Data Folder + Buka Folder Data Simpanan + + + + Open Log Folder + Buka Folder Log + - Copy info - Copy info + Copy info... + Copy info... diff --git a/src/qt_gui/translations/it.ts b/src/qt_gui/translations/it.ts index b6eb13240..c59289314 100644 --- a/src/qt_gui/translations/it.ts +++ b/src/qt_gui/translations/it.ts @@ -100,11 +100,6 @@ Create Shortcut Crea scorciatoia - - - Open Game Folder - Apri cartella del gioco - Cheats / Patches @@ -120,10 +115,30 @@ Trophy Viewer Visualizzatore Trofei + + + Open Folder... + Apri Cartella... + + + + Open Game Folder + Apri Cartella del Gioco + + + + Open Save Data Folder + Apri Cartella dei Dati di Salvataggio + + + + Open Log Folder + Apri Cartella dei Log + - Copy info - Copia informazioni + Copy info... + Copia informazioni... diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index a79b34e2a..f4a4b15ad 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -100,11 +100,6 @@ Create Shortcut ショートカットを作成 - - - Open Game Folder - ゲームフォルダを開く - Cheats / Patches @@ -120,10 +115,30 @@ Trophy Viewer トロフィービューワー + + + Open Folder... + フォルダを開く... + + + + Open Game Folder + ゲームフォルダを開く + + + + Open Save Data Folder + セーブデータフォルダを開く + + + + Open Log Folder + ログフォルダを開く + - Copy info - 情報をコピー + Copy info... + 情報をコピー... diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts index 6ef89ea24..2fa3ee153 100644 --- a/src/qt_gui/translations/ko_KR.ts +++ b/src/qt_gui/translations/ko_KR.ts @@ -100,11 +100,6 @@ Create Shortcut Create Shortcut - - - Open Game Folder - Open Game Folder - Cheats / Patches @@ -120,10 +115,30 @@ Trophy Viewer Trophy Viewer + + + Open Folder... + Open Folder... + + + + Open Game Folder + Open Game Folder + + + + Open Save Data Folder + Open Save Data Folder + + + + Open Log Folder + Open Log Folder + - Copy info - Copy info + Copy info... + Copy info... diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts index d7fc6e844..16aaf5d86 100644 --- a/src/qt_gui/translations/lt_LT.ts +++ b/src/qt_gui/translations/lt_LT.ts @@ -100,11 +100,6 @@ Create Shortcut Create Shortcut - - - Open Game Folder - Open Game Folder - Apgaulės / Pleistrai @@ -120,10 +115,30 @@ Trophy Viewer Trophy Viewer + + + Open Folder... + Atidaryti Katalogą... + + + + Open Game Folder + Atidaryti Žaidimo Katalogą + + + + Open Save Data Folder + Atidaryti Išsaugotų Duomenų Katalogą + + + + Open Log Folder + Atidaryti Žurnalų Katalogą + - Copy info - Copy info + Copy info... + Copy info... diff --git a/src/qt_gui/translations/nb.ts b/src/qt_gui/translations/nb.ts index cdcf4d5fb..940c6c9b6 100644 --- a/src/qt_gui/translations/nb.ts +++ b/src/qt_gui/translations/nb.ts @@ -100,11 +100,6 @@ Create Shortcut Create Shortcut - - - Open Game Folder - Open Game Folder - Cheats / Patches @@ -120,10 +115,30 @@ Trophy Viewer Trophy Viewer + + + Open Folder... + Åpne Mappen... + + + + Open Game Folder + Åpne Spillmappe + + + + Open Save Data Folder + Åpne Lagrede Data-mappen + + + + Open Log Folder + Åpne Loggmappen + - Copy info - Copy info + Copy info... + Copy info... diff --git a/src/qt_gui/translations/nl.ts b/src/qt_gui/translations/nl.ts index 380d90705..b0cfaff5e 100644 --- a/src/qt_gui/translations/nl.ts +++ b/src/qt_gui/translations/nl.ts @@ -100,11 +100,6 @@ Create Shortcut Create Shortcut - - - Open Game Folder - Open Game Folder - Cheats / Patches @@ -120,10 +115,30 @@ Trophy Viewer Trophy Viewer + + + Open Folder... + Map openen... + + + + Open Game Folder + Open Spelmap + + + + Open Save Data Folder + Open Map voor Opslagdata + + + + Open Log Folder + Open Logmap + - Copy info - Copy info + Copy info... + Copy info... diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index 5d211734e..4d11c13f6 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -100,11 +100,6 @@ Create Shortcut Utwórz skrót - - - Open Game Folder - Otwórz katalog gry - Cheats / Patches @@ -120,10 +115,30 @@ Trophy Viewer Menedżer trofeów + + + Open Folder... + Otwórz Folder... + + + + Open Game Folder + Otwórz Katalog Gry + + + + Open Save Data Folder + Otwórz Folder Danych Zapisów + + + + Open Log Folder + Otwórz Folder Dziennika + - Copy info - Kopiuj informacje + Copy info... + Kopiuj informacje... diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index eb79fade4..f1d3631d8 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -100,11 +100,6 @@ Create Shortcut Criar Atalho - - - Open Game Folder - Abrir Pasta do Jogo - Cheats / Patches @@ -120,10 +115,30 @@ Trophy Viewer Visualizador de Troféu + + + Open Folder... + Abrir Pasta... + + + + Open Game Folder + Abrir Pasta do Jogo + + + + Open Save Data Folder + Abrir Pasta de Save + + + + Open Log Folder + Abrir Pasta de Log + - Copy info - Copiar informação + Copy info... + Copiar informação... diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index 603cd3a24..fff0bcddb 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -100,11 +100,6 @@ Create Shortcut Create Shortcut - - - Open Game Folder - Open Game Folder - Trapaças / Patches @@ -120,10 +115,30 @@ Trophy Viewer Trophy Viewer + + + Open Folder... + Deschide Folder... + + + + Open Game Folder + Deschide Folder Joc + + + + Open Save Data Folder + Deschide Folder Date Salvate + + + + Open Log Folder + Deschide Folder Jurnal + - Copy info - Copy info + Copy info... + Copy info... diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index 4c58786c4..052623235 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -100,11 +100,6 @@ Create Shortcut Создать ярлык - - - Open Game Folder - Открыть папку с игрой - Cheats / Patches @@ -120,10 +115,30 @@ Trophy Viewer Просмотр трофеев + + + Open Folder... + Открыть Папку... + + + + Open Game Folder + Открыть папку с игрой + + + + Open Save Data Folder + Открыть Папку Сохранений + + + + Open Log Folder + Открыть Папку Логов + - Copy info - Копировать информацию + Copy info... + Копировать информацию... diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index 00fd5cb48..5715371bf 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -100,11 +100,6 @@ Create Shortcut Krijo Shkurtore - - - Open Game Folder - Hap Dosjen e Lojës - Cheats / Patches @@ -120,10 +115,30 @@ Trophy Viewer Shikuesi i Trofeve + + + Open Folder... + Hapni Dosjen... + + + + Open Game Folder + Hapni Dosjen e Lojës + + + + Open Save Data Folder + Hapni Dosjen e të Dhënave të Ruajtura + + + + Open Log Folder + Hapni Dosjen e Regjistrimeve + - Copy info - Kopjo informacionin + Copy info... + Kopjo informacionin... diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index 6c4913603..335465778 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -100,11 +100,6 @@ Create Shortcut Kısayol Oluştur - - - Open Game Folder - Oyun Klasörünü Aç - Cheats / Patches @@ -120,10 +115,30 @@ Trophy Viewer Kupa Görüntüleyici + + + Open Folder... + Klasörü Aç... + + + + Open Game Folder + Oyun Klasörünü Aç + + + + Open Save Data Folder + Kaydetme Verileri Klasörünü Aç + + + + Open Log Folder + Log Klasörünü Aç + - Copy info - Bilgiyi Kopyala + Copy info... + Bilgiyi Kopyala... diff --git a/src/qt_gui/translations/uk_UA.ts b/src/qt_gui/translations/uk_UA.ts index 61c884986..31bfe9dba 100644 --- a/src/qt_gui/translations/uk_UA.ts +++ b/src/qt_gui/translations/uk_UA.ts @@ -100,11 +100,6 @@ Create Shortcut Створити Ярлик - - - Open Game Folder - Відкрити папку з грою - Cheats / Patches @@ -120,10 +115,30 @@ Trophy Viewer Перегляд трофеїв + + + Open Folder... + Відкрити Папку... + + + + Open Game Folder + Відкрити папку з грою + + + + Open Save Data Folder + Відкрити Папку Збережених Даних + + + + Open Log Folder + Відкрити Папку Логів + - Copy info - Копіювати інформацію + Copy info... + Копіювати інформацію... diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index 5fca6b6b5..223cb9ed0 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -100,11 +100,6 @@ Create Shortcut Create Shortcut - - - Open Game Folder - Open Game Folder - Cheats / Patches @@ -120,10 +115,30 @@ Trophy Viewer Trophy Viewer + + + Open Folder... + Mở Thư Mục... + + + + Open Game Folder + Mở Thư Mục Trò Chơi + + + + Open Save Data Folder + Mở Thư Mục Dữ Liệu Lưu + + + + Open Log Folder + Mở Thư Mục Nhật Ký + - Copy info - Copy info + Copy info... + Copy info... diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index bfcbbaa98..4fe1f7c42 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -100,11 +100,6 @@ Create Shortcut 创建快捷方式 - - - Open Game Folder - 打开游戏文件夹 - Cheats / Patches @@ -120,10 +115,30 @@ Trophy Viewer Trophy 查看器 + + + Open Folder... + 打开文件夹... + + + + Open Game Folder + 打开游戏文件夹 + + + + Open Save Data Folder + 打开保存数据文件夹 + + + + Open Log Folder + 打开日志文件夹 + - Copy info - 复制信息 + Copy info... + 复制信息... diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index 84b32b7a5..4db00775d 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -100,11 +100,6 @@ Create Shortcut Create Shortcut - - - Open Game Folder - Open Game Folder - Cheats / Patches @@ -120,10 +115,30 @@ Trophy Viewer Trophy Viewer + + + Open Folder... + 打開資料夾... + + + + Open Game Folder + 打開遊戲資料夾 + + + + Open Save Data Folder + 打開存檔資料夾 + + + + Open Log Folder + 打開日誌資料夾 + - Copy info - Copy info + Copy info... + Copy info... From 7ab851592bca44c27e5c6a8f86d50361199b7082 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Sun, 10 Nov 2024 11:33:08 +0200 Subject: [PATCH 012/549] Videocodec implementation (#1484) * dummy videocodec * filled videodec parameters * vdec1 implementation * clang format fix * fixed codecType * added crop offset info * align output * align all h/w * some touchups * small touch (last one) --- CMakeLists.txt | 4 + src/common/logging/filter.cpp | 1 + src/common/logging/types.h | 1 + src/core/libraries/error_codes.h | 27 ++- src/core/libraries/libs.cpp | 2 + src/core/libraries/videodec/videodec.cpp | 137 +++++++++++ src/core/libraries/videodec/videodec.h | 108 +++++++++ src/core/libraries/videodec/videodec_impl.cpp | 222 ++++++++++++++++++ src/core/libraries/videodec/videodec_impl.h | 38 +++ 9 files changed, 539 insertions(+), 1 deletion(-) create mode 100644 src/core/libraries/videodec/videodec.cpp create mode 100644 src/core/libraries/videodec/videodec.h create mode 100644 src/core/libraries/videodec/videodec_impl.cpp create mode 100644 src/core/libraries/videodec/videodec_impl.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 036f74309..439663352 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -366,6 +366,10 @@ set(VDEC_LIB src/core/libraries/videodec/videodec2_impl.cpp src/core/libraries/videodec/videodec2.cpp src/core/libraries/videodec/videodec2.h src/core/libraries/videodec/videodec2_avc.h + src/core/libraries/videodec/videodec.cpp + src/core/libraries/videodec/videodec.h + src/core/libraries/videodec/videodec_impl.cpp + src/core/libraries/videodec/videodec_impl.h ) set(NP_LIBS src/core/libraries/np_manager/np_manager.cpp diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index a76c472a7..5ca594bf7 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -120,6 +120,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Lib, SharePlay) \ SUB(Lib, Fiber) \ SUB(Lib, Vdec2) \ + SUB(Lib, Videodec) \ CLS(Frontend) \ CLS(Render) \ SUB(Render, Vulkan) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index fd7dcfd1a..2821729d4 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -87,6 +87,7 @@ enum class Class : u8 { Lib_SharePlay, ///< The LibSceSharePlay implemenation Lib_Fiber, ///< The LibSceFiber implementation. Lib_Vdec2, ///< The LibSceVideodec2 implementation. + Lib_Videodec, ///< The LibSceVideodec implementation. Frontend, ///< Emulator UI Render, ///< Video Core Render_Vulkan, ///< Vulkan backend diff --git a/src/core/libraries/error_codes.h b/src/core/libraries/error_codes.h index 66ad5f8b6..6bbdc3080 100644 --- a/src/core/libraries/error_codes.h +++ b/src/core/libraries/error_codes.h @@ -590,4 +590,29 @@ constexpr int ORBIS_VIDEODEC2_ERROR_NEW_SEQUENCE = 0x811D0300; constexpr int ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT = 0x811D0301; constexpr int ORBIS_VIDEODEC2_ERROR_OVERSIZE_DECODE = 0x811D0302; constexpr int ORBIS_VIDEODEC2_ERROR_INVALID_SEQUENCE = 0x811D0303; -constexpr int ORBIS_VIDEODEC2_ERROR_FATAL_STREAM = 0x811D0304; \ No newline at end of file +constexpr int ORBIS_VIDEODEC2_ERROR_FATAL_STREAM = 0x811D0304; + +// Videodec library + +constexpr int ORBIS_VIDEODEC_ERROR_API_FAIL = 0x80C10000; +constexpr int ORBIS_VIDEODEC_ERROR_CODEC_TYPE = 0x80C10001; +constexpr int ORBIS_VIDEODEC_ERROR_STRUCT_SIZE = 0x80C10002; +constexpr int ORBIS_VIDEODEC_ERROR_HANDLE = 0x80C10003; +constexpr int ORBIS_VIDEODEC_ERROR_CPU_MEMORY_SIZE = 0x80C10004; +constexpr int ORBIS_VIDEODEC_ERROR_CPU_MEMORY_POINTER = 0x80C10005; +constexpr int ORBIS_VIDEODEC_ERROR_CPU_GPU_MEMORY_SIZE = 0x80C10006; +constexpr int ORBIS_VIDEODEC_ERROR_CPU_GPU_MEMORY_POINTER = 0x80C10007; +constexpr int ORBIS_VIDEODEC_ERROR_SHADER_CONTEXT_POINTER = 0x80C10008; +constexpr int ORBIS_VIDEODEC_ERROR_AU_SIZE = 0x80C10009; +constexpr int ORBIS_VIDEODEC_ERROR_AU_POINTER = 0x80C1000A; +constexpr int ORBIS_VIDEODEC_ERROR_FRAME_BUFFER_SIZE = 0x80C1000B; +constexpr int ORBIS_VIDEODEC_ERROR_FRAME_BUFFER_POINTER = 0x80C1000C; +constexpr int ORBIS_VIDEODEC_ERROR_FRAME_BUFFER_ALIGNMENT = 0x80C1000D; +constexpr int ORBIS_VIDEODEC_ERROR_CONFIG_INFO = 0x80C1000E; +constexpr int ORBIS_VIDEODEC_ERROR_ARGUMENT_POINTER = 0x80C1000F; +constexpr int ORBIS_VIDEODEC_ERROR_NEW_SEQUENCE = 0x80C10010; +constexpr int ORBIS_VIDEODEC_ERROR_DECODE_AU = 0x80C10011; +constexpr int ORBIS_VIDEODEC_ERROR_MISMATCH_SPEC = 0x80C10012; +constexpr int ORBIS_VIDEODEC_ERROR_INVALID_SEQUENCE = 0x80C10013; +constexpr int ORBIS_VIDEODEC_ERROR_FATAL_STREAM = 0x80C10014; +constexpr int ORBIS_VIDEODEC_ERROR_FATAL_STATE = 0x80C10015; diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index b0365435b..23d751622 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -41,6 +41,7 @@ #include "core/libraries/system/systemservice.h" #include "core/libraries/system/userservice.h" #include "core/libraries/usbd/usbd.h" +#include "core/libraries/videodec/videodec.h" #include "core/libraries/videodec/videodec2.h" #include "core/libraries/videoout/video_out.h" @@ -87,6 +88,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::GameLiveStreaming::RegisterlibSceGameLiveStreaming(sym); Libraries::SharePlay::RegisterlibSceSharePlay(sym); Libraries::Remoteplay::RegisterlibSceRemoteplay(sym); + Libraries::Videodec::RegisterlibSceVideodec(sym); } } // namespace Libraries diff --git a/src/core/libraries/videodec/videodec.cpp b/src/core/libraries/videodec/videodec.cpp new file mode 100644 index 000000000..96ece3c5c --- /dev/null +++ b/src/core/libraries/videodec/videodec.cpp @@ -0,0 +1,137 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "videodec.h" + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" +#include "videodec_impl.h" + +namespace Libraries::Videodec { + +static constexpr u64 kMinimumMemorySize = 32_MB; ///> Fake minimum memory size for querying + +int PS4_SYSV_ABI sceVideodecCreateDecoder(const OrbisVideodecConfigInfo* pCfgInfoIn, + const OrbisVideodecResourceInfo* pRsrcInfoIn, + OrbisVideodecCtrl* pCtrlOut) { + LOG_INFO(Lib_Videodec, "called"); + + if (!pCfgInfoIn || !pRsrcInfoIn || !pCtrlOut) { + return ORBIS_VIDEODEC_ERROR_ARGUMENT_POINTER; + } + if (pCfgInfoIn->thisSize != sizeof(OrbisVideodecConfigInfo) || + pRsrcInfoIn->thisSize != sizeof(OrbisVideodecResourceInfo)) { + return ORBIS_VIDEODEC_ERROR_STRUCT_SIZE; + } + + VdecDecoder* decoder = new VdecDecoder(*pCfgInfoIn, *pRsrcInfoIn); + pCtrlOut->thisSize = sizeof(OrbisVideodecCtrl); + pCtrlOut->handle = decoder; + pCtrlOut->version = 1; //??? + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceVideodecDecode(OrbisVideodecCtrl* pCtrlIn, + const OrbisVideodecInputData* pInputDataIn, + OrbisVideodecFrameBuffer* pFrameBufferInOut, + OrbisVideodecPictureInfo* pPictureInfoOut) { + LOG_INFO(Lib_Videodec, "called"); + if (!pCtrlIn || !pInputDataIn || !pPictureInfoOut) { + return ORBIS_VIDEODEC_ERROR_ARGUMENT_POINTER; + } + if (pCtrlIn->thisSize != sizeof(OrbisVideodecCtrl) || + pFrameBufferInOut->thisSize != sizeof(OrbisVideodecFrameBuffer)) { + return ORBIS_VIDEODEC_ERROR_STRUCT_SIZE; + } + + VdecDecoder* decoder = (VdecDecoder*)pCtrlIn->handle; + if (!decoder) { + return ORBIS_VIDEODEC_ERROR_HANDLE; + } + return decoder->Decode(*pInputDataIn, *pFrameBufferInOut, *pPictureInfoOut); +} + +int PS4_SYSV_ABI sceVideodecDeleteDecoder(OrbisVideodecCtrl* pCtrlIn) { + LOG_INFO(Lib_Videodec, "(STUBBED) called"); + + VdecDecoder* decoder = (VdecDecoder*)pCtrlIn->handle; + if (!decoder) { + return ORBIS_VIDEODEC_ERROR_HANDLE; + } + delete decoder; + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceVideodecFlush(OrbisVideodecCtrl* pCtrlIn, + OrbisVideodecFrameBuffer* pFrameBufferInOut, + OrbisVideodecPictureInfo* pPictureInfoOut) { + LOG_INFO(Lib_Videodec, "called"); + + if (!pFrameBufferInOut || !pPictureInfoOut) { + return ORBIS_VIDEODEC_ERROR_ARGUMENT_POINTER; + } + if (pFrameBufferInOut->thisSize != sizeof(OrbisVideodecFrameBuffer) || + pPictureInfoOut->thisSize != sizeof(OrbisVideodecPictureInfo)) { + return ORBIS_VIDEODEC_ERROR_STRUCT_SIZE; + } + + VdecDecoder* decoder = (VdecDecoder*)pCtrlIn->handle; + if (!decoder) { + return ORBIS_VIDEODEC_ERROR_HANDLE; + } + return decoder->Flush(*pFrameBufferInOut, *pPictureInfoOut); +} + +int PS4_SYSV_ABI sceVideodecMapMemory() { + LOG_ERROR(Lib_Videodec, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceVideodecQueryResourceInfo(const OrbisVideodecConfigInfo* pCfgInfoIn, + OrbisVideodecResourceInfo* pRsrcInfoOut) { + LOG_INFO(Lib_Videodec, "called"); + + if (!pCfgInfoIn || !pRsrcInfoOut) { + return ORBIS_VIDEODEC_ERROR_ARGUMENT_POINTER; + } + if (pCfgInfoIn->thisSize != sizeof(OrbisVideodecConfigInfo) || + pRsrcInfoOut->thisSize != sizeof(OrbisVideodecResourceInfo)) { + return ORBIS_VIDEODEC_ERROR_STRUCT_SIZE; + } + + pRsrcInfoOut->thisSize = sizeof(OrbisVideodecResourceInfo); + pRsrcInfoOut->pCpuMemory = nullptr; + pRsrcInfoOut->pCpuGpuMemory = nullptr; + + pRsrcInfoOut->cpuGpuMemorySize = kMinimumMemorySize; + pRsrcInfoOut->cpuMemorySize = kMinimumMemorySize; + + pRsrcInfoOut->maxFrameBufferSize = kMinimumMemorySize; + pRsrcInfoOut->frameBufferAlignment = 0x100; + + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceVideodecReset(OrbisVideodecCtrl* pCtrlIn) { + LOG_INFO(Lib_Videodec, "(STUBBED) called"); + + VdecDecoder* decoder = (VdecDecoder*)pCtrlIn->handle; + decoder->Reset(); + return ORBIS_OK; +} + +void RegisterlibSceVideodec(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("qkgRiwHyheU", "libSceVideodec", 1, "libSceVideodec", 1, 1, + sceVideodecCreateDecoder); + LIB_FUNCTION("q0W5GJMovMs", "libSceVideodec", 1, "libSceVideodec", 1, 1, sceVideodecDecode); + LIB_FUNCTION("U0kpGF1cl90", "libSceVideodec", 1, "libSceVideodec", 1, 1, + sceVideodecDeleteDecoder); + LIB_FUNCTION("jeigLlKdp5I", "libSceVideodec", 1, "libSceVideodec", 1, 1, sceVideodecFlush); + LIB_FUNCTION("kg+lH0V61hM", "libSceVideodec", 1, "libSceVideodec", 1, 1, sceVideodecMapMemory); + LIB_FUNCTION("leCAscipfFY", "libSceVideodec", 1, "libSceVideodec", 1, 1, + sceVideodecQueryResourceInfo); + LIB_FUNCTION("f8AgDv-1X8A", "libSceVideodec", 1, "libSceVideodec", 1, 1, sceVideodecReset); +}; + +} // namespace Libraries::Videodec \ No newline at end of file diff --git a/src/core/libraries/videodec/videodec.h b/src/core/libraries/videodec/videodec.h new file mode 100644 index 000000000..45c5a6924 --- /dev/null +++ b/src/core/libraries/videodec/videodec.h @@ -0,0 +1,108 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::Videodec { + +struct OrbisVideodecConfigInfo { + u64 thisSize; + u32 codecType; + u32 profile; + u32 maxLevel; + s32 maxFrameWidth; + s32 maxFrameHeight; + s32 maxDpbFrameCount; + u64 videodecFlags; +}; + +struct OrbisVideodecResourceInfo { + u64 thisSize; + u64 cpuMemorySize; + void* pCpuMemory; + u64 cpuGpuMemorySize; + void* pCpuGpuMemory; + u64 maxFrameBufferSize; + u32 frameBufferAlignment; +}; + +struct OrbisVideodecCtrl { + u64 thisSize; + void* handle; + u64 version; +}; + +struct OrbisVideodecFrameBuffer { + u64 thisSize; + void* pFrameBuffer; + u64 frameBufferSize; +}; + +struct OrbisVideodecAvcInfo { + u32 numUnitsInTick; + u32 timeScale; + u8 fixedFrameRateFlag; + u8 aspectRatioIdc; + u16 sarWidth; + u16 sarHeight; + u8 colourPrimaries; + u8 transferCharacteristics; + u8 matrixCoefficients; + u8 videoFullRangeFlag; + u32 frameCropLeftOffset; + u32 frameCropRightOffset; + u32 frameCropTopOffset; + u32 frameCropBottomOffset; +}; + +union OrbisVideodecCodecInfo { + u8 reserved[64]; + OrbisVideodecAvcInfo avc; +}; + +struct OrbisVideodecPictureInfo { + u64 thisSize; + u32 isValid; + u32 codecType; + u32 frameWidth; + u32 framePitch; + u32 frameHeight; + u32 isErrorPic; + u64 ptsData; + u64 attachedData; + OrbisVideodecCodecInfo codec; +}; + +struct OrbisVideodecInputData { + u64 thisSize; + void* pAuData; + u64 auSize; + u64 ptsData; + u64 dtsData; + u64 attachedData; +}; + +int PS4_SYSV_ABI sceVideodecCreateDecoder(const OrbisVideodecConfigInfo* pCfgInfoIn, + const OrbisVideodecResourceInfo* pRsrcInfoIn, + OrbisVideodecCtrl* pCtrlOut); +int PS4_SYSV_ABI sceVideodecDecode(OrbisVideodecCtrl* pCtrlIn, + const OrbisVideodecInputData* pInputDataIn, + OrbisVideodecFrameBuffer* pFrameBufferInOut, + OrbisVideodecPictureInfo* pPictureInfoOut); +int PS4_SYSV_ABI sceVideodecDeleteDecoder(OrbisVideodecCtrl* pCtrlIn); +int PS4_SYSV_ABI sceVideodecFlush(OrbisVideodecCtrl* pCtrlIn, + OrbisVideodecFrameBuffer* pFrameBufferInOut, + OrbisVideodecPictureInfo* pPictureInfoOut); +int PS4_SYSV_ABI sceVideodecMapMemory(); +int PS4_SYSV_ABI sceVideodecQueryResourceInfo(const OrbisVideodecConfigInfo* pCfgInfoIn, + OrbisVideodecResourceInfo* pRsrcInfoOut); +int PS4_SYSV_ABI sceVideodecReset(OrbisVideodecCtrl* pCtrlIn); + +void RegisterlibSceVideodec(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::Videodec \ No newline at end of file diff --git a/src/core/libraries/videodec/videodec_impl.cpp b/src/core/libraries/videodec/videodec_impl.cpp new file mode 100644 index 000000000..a244c4a61 --- /dev/null +++ b/src/core/libraries/videodec/videodec_impl.cpp @@ -0,0 +1,222 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "videodec_impl.h" + +#include "common/alignment.h" +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" + +// The av_err2str macro in libavutil/error.h does not play nice with C++ +#ifdef av_err2str +#undef av_err2str +#include +av_always_inline std::string av_err2string(int errnum) { + char errbuf[AV_ERROR_MAX_STRING_SIZE]; + return av_make_error_string(errbuf, AV_ERROR_MAX_STRING_SIZE, errnum); +} +#define av_err2str(err) av_err2string(err).c_str() +#endif // av_err2str + +namespace Libraries::Videodec { + +static inline void CopyNV12Data(u8* dst, const AVFrame& src) { + u32 width = Common::AlignUp((u32)src.width, 16); + u32 height = Common::AlignUp((u32)src.height, 16); + std::memcpy(dst, src.data[0], src.width * src.height); + std::memcpy(dst + src.width * height, src.data[1], (src.width * src.height) / 2); +} + +VdecDecoder::VdecDecoder(const OrbisVideodecConfigInfo& pCfgInfoIn, + const OrbisVideodecResourceInfo& pRsrcInfoIn) { + + const AVCodec* codec = avcodec_find_decoder(AV_CODEC_ID_H264); + ASSERT(codec); + + mCodecContext = avcodec_alloc_context3(codec); + ASSERT(mCodecContext); + mCodecContext->width = pCfgInfoIn.maxFrameWidth; + mCodecContext->height = pCfgInfoIn.maxFrameHeight; + + avcodec_open2(mCodecContext, codec, nullptr); +} + +VdecDecoder::~VdecDecoder() { + avcodec_free_context(&mCodecContext); + sws_freeContext(mSwsContext); +} + +s32 VdecDecoder::Decode(const OrbisVideodecInputData& pInputDataIn, + OrbisVideodecFrameBuffer& pFrameBufferInOut, + OrbisVideodecPictureInfo& pPictureInfoOut) { + pPictureInfoOut.thisSize = sizeof(OrbisVideodecPictureInfo); + pPictureInfoOut.isValid = false; + pPictureInfoOut.isErrorPic = true; + + if (!pInputDataIn.pAuData) { + return ORBIS_VIDEODEC_ERROR_AU_POINTER; + } + if (pInputDataIn.auSize == 0) { + return ORBIS_VIDEODEC_ERROR_AU_SIZE; + } + + AVPacket* packet = av_packet_alloc(); + if (!packet) { + LOG_ERROR(Lib_Videodec, "Failed to allocate packet"); + return ORBIS_VIDEODEC_ERROR_API_FAIL; + } + + packet->data = (u8*)pInputDataIn.pAuData; + packet->size = pInputDataIn.auSize; + packet->pts = pInputDataIn.ptsData; + packet->dts = pInputDataIn.dtsData; + + int ret = avcodec_send_packet(mCodecContext, packet); + if (ret < 0) { + LOG_ERROR(Lib_Videodec, "Error sending packet to decoder: {}", ret); + av_packet_free(&packet); + return ORBIS_VIDEODEC_ERROR_API_FAIL; + } + + AVFrame* frame = av_frame_alloc(); + if (frame == nullptr) { + LOG_ERROR(Lib_Videodec, "Failed to allocate frame"); + av_packet_free(&packet); + return ORBIS_VIDEODEC_ERROR_API_FAIL; + } + int frameCount = 0; + while (true) { + ret = avcodec_receive_frame(mCodecContext, frame); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + break; + } else if (ret < 0) { + LOG_ERROR(Lib_Videodec, "Error receiving frame from decoder: {}", ret); + av_packet_free(&packet); + av_frame_free(&frame); + return ORBIS_VIDEODEC_ERROR_API_FAIL; + } + + if (frame->format != AV_PIX_FMT_NV12) { + AVFrame* nv12_frame = ConvertNV12Frame(*frame); + ASSERT(nv12_frame); + av_frame_free(&frame); + frame = nv12_frame; + } + + CopyNV12Data((u8*)pFrameBufferInOut.pFrameBuffer, *frame); + + pPictureInfoOut.codecType = 0; + pPictureInfoOut.frameWidth = Common::AlignUp((u32)frame->width, 16); + pPictureInfoOut.frameHeight = Common::AlignUp((u32)frame->height, 16); + pPictureInfoOut.framePitch = frame->linesize[0]; + + pPictureInfoOut.isValid = true; + pPictureInfoOut.isErrorPic = false; + frameCount++; + if (frameCount > 1) { + LOG_WARNING(Lib_Videodec, "We have more than 1 frame"); + } + } + + av_packet_free(&packet); + av_frame_free(&frame); + return ORBIS_OK; +} + +s32 VdecDecoder::Flush(OrbisVideodecFrameBuffer& pFrameBufferInOut, + OrbisVideodecPictureInfo& pPictureInfoOut) { + pPictureInfoOut.thisSize = sizeof(pPictureInfoOut); + pPictureInfoOut.isValid = false; + pPictureInfoOut.isErrorPic = true; + + AVFrame* frame = av_frame_alloc(); + if (!frame) { + LOG_ERROR(Lib_Videodec, "Failed to allocate frame"); + return ORBIS_VIDEODEC_ERROR_API_FAIL; + } + + int frameCount = 0; + while (true) { + int ret = avcodec_receive_frame(mCodecContext, frame); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + break; + } else if (ret < 0) { + LOG_ERROR(Lib_Videodec, "Error receiving frame from decoder: {}", ret); + av_frame_free(&frame); + return ORBIS_VIDEODEC_ERROR_API_FAIL; + } + + if (frame->format != AV_PIX_FMT_NV12) { + AVFrame* nv12_frame = ConvertNV12Frame(*frame); + ASSERT(nv12_frame); + av_frame_free(&frame); + frame = nv12_frame; + } + + CopyNV12Data((u8*)pFrameBufferInOut.pFrameBuffer, *frame); + + pPictureInfoOut.codecType = 0; + pPictureInfoOut.frameWidth = Common::AlignUp((u32)frame->width, 16); + pPictureInfoOut.frameHeight = Common::AlignUp((u32)frame->height, 16); + pPictureInfoOut.framePitch = frame->linesize[0]; + + pPictureInfoOut.isValid = true; + pPictureInfoOut.isErrorPic = false; + + u32 width = Common::AlignUp((u32)frame->width, 16); + u32 height = Common::AlignUp((u32)frame->height, 16); + pPictureInfoOut.codec.avc.frameCropLeftOffset = u32(frame->crop_left); + pPictureInfoOut.codec.avc.frameCropRightOffset = + u32(frame->crop_right + (width - frame->width)); + pPictureInfoOut.codec.avc.frameCropTopOffset = u32(frame->crop_top); + pPictureInfoOut.codec.avc.frameCropBottomOffset = + u32(frame->crop_bottom + (height - frame->height)); + // TODO maybe more avc? + + if (frameCount > 1) { + LOG_WARNING(Lib_Videodec, "We have more than 1 frame"); + } + } + + av_frame_free(&frame); + return ORBIS_OK; +} + +s32 VdecDecoder::Reset() { + avcodec_flush_buffers(mCodecContext); + return ORBIS_OK; +} + +AVFrame* VdecDecoder::ConvertNV12Frame(AVFrame& frame) { + AVFrame* nv12_frame = av_frame_alloc(); + nv12_frame->pts = frame.pts; + nv12_frame->pkt_dts = frame.pkt_dts < 0 ? 0 : frame.pkt_dts; + nv12_frame->format = AV_PIX_FMT_NV12; + nv12_frame->width = frame.width; + nv12_frame->height = frame.height; + nv12_frame->sample_aspect_ratio = frame.sample_aspect_ratio; + nv12_frame->crop_top = frame.crop_top; + nv12_frame->crop_bottom = frame.crop_bottom; + nv12_frame->crop_left = frame.crop_left; + nv12_frame->crop_right = frame.crop_right; + + av_frame_get_buffer(nv12_frame, 0); + + if (mSwsContext == nullptr) { + mSwsContext = sws_getContext(frame.width, frame.height, AVPixelFormat(frame.format), + nv12_frame->width, nv12_frame->height, AV_PIX_FMT_NV12, + SWS_FAST_BILINEAR, nullptr, nullptr, nullptr); + } + + const auto res = sws_scale(mSwsContext, frame.data, frame.linesize, 0, frame.height, + nv12_frame->data, nv12_frame->linesize); + if (res < 0) { + LOG_ERROR(Lib_Videodec, "Could not convert to NV12: {}", av_err2str(res)); + return nullptr; + } + + return nv12_frame; +} + +} // namespace Libraries::Videodec diff --git a/src/core/libraries/videodec/videodec_impl.h b/src/core/libraries/videodec/videodec_impl.h new file mode 100644 index 000000000..3d48db608 --- /dev/null +++ b/src/core/libraries/videodec/videodec_impl.h @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "videodec.h" + +extern "C" { +#include +#include +#include +} + +namespace Libraries::Videodec { + +class VdecDecoder { +public: + VdecDecoder(const OrbisVideodecConfigInfo& pCfgInfoIn, + const OrbisVideodecResourceInfo& pRsrcInfoIn); + ~VdecDecoder(); + s32 Decode(const OrbisVideodecInputData& pInputDataIn, + OrbisVideodecFrameBuffer& pFrameBufferInOut, + OrbisVideodecPictureInfo& pPictureInfoOut); + s32 Flush(OrbisVideodecFrameBuffer& pFrameBufferInOut, + OrbisVideodecPictureInfo& pPictureInfoOut); + s32 Reset(); + +private: + AVFrame* ConvertNV12Frame(AVFrame& frame); + +private: + AVCodecContext* mCodecContext = nullptr; + SwsContext* mSwsContext = nullptr; +}; + +} // namespace Libraries::Videodec \ No newline at end of file From b64dcd2f561d8251223a3154e6a5e9aed441061c Mon Sep 17 00:00:00 2001 From: Lander Gallastegi Date: Tue, 12 Nov 2024 08:26:48 +0100 Subject: [PATCH 013/549] Assert fix (#1521) --- .../ir/passes/resource_tracking_pass.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index 586785e6a..7d29c845d 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -690,9 +690,9 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip IR::Inst* body = inst.Arg(1).InstRecursive(); const auto [coords, arg] = [&] -> std::pair { switch (image.GetType()) { - case AmdGpu::ImageType::Color1D: // x + case AmdGpu::ImageType::Color1D: // x, [lod] return {body->Arg(0), body->Arg(1)}; - case AmdGpu::ImageType::Color1DArray: // x, slice + case AmdGpu::ImageType::Color1DArray: // x, slice, [lod] [[fallthrough]]; case AmdGpu::ImageType::Color2D: // x, y, [lod] [[fallthrough]]; @@ -703,9 +703,9 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip case AmdGpu::ImageType::Color2DMsaaArray: // x, y, slice. (sample is passed on different // argument) [[fallthrough]]; - case AmdGpu::ImageType::Color3D: // x, y, z + case AmdGpu::ImageType::Color3D: // x, y, z, [lod] return {ir.CompositeConstruct(body->Arg(0), body->Arg(1), body->Arg(2)), body->Arg(3)}; - case AmdGpu::ImageType::Cube: // x, y, face + case AmdGpu::ImageType::Cube: // x, y, face, [lod] return {PatchCubeCoord(ir, body->Arg(0), body->Arg(1), body->Arg(2), is_storage, inst_info.is_array), body->Arg(3)}; @@ -717,8 +717,8 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip if (inst_info.has_lod) { ASSERT(inst.GetOpcode() == IR::Opcode::ImageFetch); - ASSERT(image.GetType() == AmdGpu::ImageType::Color2D || - image.GetType() == AmdGpu::ImageType::Color2DArray); + ASSERT(image.GetType() != AmdGpu::ImageType::Color2DMsaa && + image.GetType() != AmdGpu::ImageType::Color2DMsaaArray); inst.SetArg(3, arg); } else if (image.GetType() == AmdGpu::ImageType::Color2DMsaa || image.GetType() == AmdGpu::ImageType::Color2DMsaaArray) { From f5618e03429759e086cd9c6a8a1cf3488a3e1faf Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Mon, 11 Nov 2024 23:27:30 -0800 Subject: [PATCH 014/549] imgui: Dispatch SDL text input requests to main thread on macOS. (#1519) --- src/imgui/renderer/imgui_impl_sdl3.cpp | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/imgui/renderer/imgui_impl_sdl3.cpp b/src/imgui/renderer/imgui_impl_sdl3.cpp index 230d396f0..60b440c24 100644 --- a/src/imgui/renderer/imgui_impl_sdl3.cpp +++ b/src/imgui/renderer/imgui_impl_sdl3.cpp @@ -11,6 +11,7 @@ #include #if defined(__APPLE__) #include +#include #endif #ifdef _WIN32 #ifndef WIN32_LEAN_AND_MEAN @@ -71,7 +72,14 @@ static void PlatformSetImeData(ImGuiContext*, ImGuiViewport* viewport, ImGuiPlat auto window_id = (SDL_WindowID)(intptr_t)viewport->PlatformHandle; SDL_Window* window = SDL_GetWindowFromID(window_id); if ((!data->WantVisible || bd->ime_window != window) && bd->ime_window != nullptr) { - SDL_StopTextInput(bd->ime_window); + auto stop_input = [&bd] { SDL_StopTextInput(bd->ime_window); }; +#ifdef __APPLE__ + dispatch_sync(dispatch_get_main_queue(), ^{ + stop_input(); + }); +#else + stop_input(); +#endif bd->ime_window = nullptr; } if (data->WantVisible) { @@ -80,8 +88,17 @@ static void PlatformSetImeData(ImGuiContext*, ImGuiViewport* viewport, ImGuiPlat r.y = (int)data->InputPos.y; r.w = 1; r.h = (int)data->InputLineHeight; - SDL_SetTextInputArea(window, &r, 0); - SDL_StartTextInput(window); + const auto start_input = [&window, &r] { + SDL_SetTextInputArea(window, &r, 0); + SDL_StartTextInput(window); + }; +#ifdef __APPLE__ + dispatch_sync(dispatch_get_main_queue(), ^{ + start_input(); + }); +#else + start_input(); +#endif bd->ime_window = window; } } From 8a15d68a8d7f79390213ddab454fc6a32ab8d824 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Tue, 12 Nov 2024 08:31:56 +0100 Subject: [PATCH 015/549] Update Hungarian translation (#1518) --- src/qt_gui/translations/hu_HU.ts | 120 +++++++++++++++---------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index 86279b2de..a43b8d371 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -62,7 +62,7 @@ Select which directory you want to install to. - Select which directory you want to install to. + Válassza ki a mappát a játékok telepítésére. @@ -90,7 +90,7 @@ The value for location to install games is not valid. - A játékok telepítéséhez megadott érték nem érvényes. + A játékok telepítéséhez megadott útvonal nem érvényes. @@ -108,7 +108,7 @@ SFO Viewer - SFO Néző + SFO Nézegető @@ -158,22 +158,22 @@ Delete... - Delete... + Törlés... Delete Game - Delete Game + Játék törlése Delete Update - Delete Update + Frissítések törlése Delete DLC - Delete DLC + DLC-k törlése @@ -203,27 +203,27 @@ Game - Game + Játék requiresEnableSeparateUpdateFolder_MSG - This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + Ehhez a funkcióhoz szükséges a 'Külön Frissítési Mappa Engedélyezése' opció, hogy működjön. Ha használni akarja, először engedélyezze azt. This game has no update to delete! - This game has no update to delete! + Ehhez a játékhoz nem tartozik törlendő frissítés! Update - Update + Frissítés This game has no DLC to delete! - This game has no DLC to delete! + Ehhez a játékhoz nem tartozik törlendő DLC! @@ -238,7 +238,7 @@ Are you sure you want to delete %1's %2 directory? - Are you sure you want to delete %1's %2 directory? + Biztosan törölni akarja a %1's %2 mappát? @@ -246,7 +246,7 @@ Open/Add Elf Folder - Efl Mappa Megnyitása/Hozzáadása + ELF Mappa Megnyitása/Hozzáadása @@ -256,7 +256,7 @@ Boot Game - Játék Bootolása + Játék Indítása @@ -291,12 +291,12 @@ Exit shadPS4 - Kilépés a shadPS4 + Kilépés a shadPS4-ből Exit the application. - Lépjen ki az programból. + Lépjen ki a programból. @@ -341,7 +341,7 @@ Elf Viewer - Elf Néző + Elf Nézegető @@ -356,12 +356,12 @@ Dump Game List - Játék Lista Dumpolása + Játéklista Dumpolása PKG Viewer - PKG Néző + PKG Nézegető @@ -376,17 +376,17 @@ View - Megnézés + Nézet Game List Icons - Játék Könyvtár Ikonok + Játékkönyvtár Ikonok Game List Mode - Játék Könyvtár Mód + Játékkönyvtár Nézet @@ -490,12 +490,12 @@ Enable Fullscreen - Teljesképernyő Engedélyezése + Teljes Képernyő Engedélyezése Enable Separate Update Folder - Enable Separate Update Folder + Külön Frissítési Mappa Engedélyezése @@ -505,12 +505,12 @@ Is PS4 Pro - PS4 Pro + PS4 Pro mód Enable Discord Rich Presence - Engedélyezze a Discord Rich Presence-t + A Discord Rich Presence engedélyezése @@ -560,7 +560,7 @@ Back Button Behavior - Vissza gomb viselkedése + Vissza gomb Viselkedése @@ -693,17 +693,17 @@ * Unsupported Vulkan Version - * Támogatott Vulkan verzió hiányzik + * Nem támogatott Vulkan verzió Download Cheats For All Installed Games - Letöltés csalások minden telepített játékhoz + Csalások letöltése minden telepített játékhoz Download Patches For All Games - Frissítések letöltése minden játékhoz + Javítások letöltése minden játékhoz @@ -713,17 +713,17 @@ You have downloaded cheats for all the games you have installed. - Csalásokat töltöttél le az összes telepített játékhoz. + Minden elérhető csalás letöltődött az összes telepített játékhoz. Patches Downloaded Successfully! - Frissítések sikeresen letöltve! + Javítások sikeresen letöltve! All Patches available for all games have been downloaded. - Az összes játékhoz elérhető frissítés letöltésre került. + Az összes játékhoz elérhető javítás letöltésre került. @@ -773,7 +773,7 @@ PKG Version %1 is older than installed version: - A %1-es PKG verzió régebbi, mint a telepített verzió: + A(z) %1-es PKG verzió régebbi, mint a telepített verzió: @@ -793,7 +793,7 @@ Would you like to install DLC: %1? - Szeretné telepíteni a DLC-t: %1? + Szeretné telepíteni a %1 DLC-t? @@ -846,7 +846,7 @@ defaultTextEdit_MSG - A csalások/patchek kísérleti jellegűek.\nHasználja őket óvatosan.\n\nTöltse le a csalásokat egyesével a repository kiválasztásával és a letöltés gombra kattintással.\nA Patches fül alatt egyszerre letöltheti az összes patchet, választhat, melyeket szeretné használni, és elmentheti a választását.\n\nMivel nem fejlesztjük a csalásokat/patch-eket,\nkérjük, jelentse a problémákat a csalás szerzőjének.\n\nKészített egy új csalást? Látogasson el ide:\nhttps://github.com/shadps4-emu/ps4_cheats + A csalások/javítások kísérleti jellegűek.\nHasználja őket óvatosan.\n\nTöltse le a csalásokat egyesével a tároló kiválasztásával és a letöltés gombra kattintással.\nA Javítások fül alatt egyszerre letöltheti az összes javítást, majd választhat, melyeket szeretné használni, és elmentheti a választását.\n\nMivel nem mi fejlesztjük a csalásokat/patch-eket,\nkérjük, jelentse a problémákat a csalás szerzőjének.\n\nKészített egy új csalást? Látogasson el ide:\nhttps://github.com/shadps4-emu/ps4_cheats @@ -1011,7 +1011,7 @@ CheatsNotFound_MSG - Nincs található csalás ezen a játékverzión ebben a kiválasztott tárolóban,próbálj meg egy másik tárolót vagy a játék egy másik verzióját. + Nincs található csalás ezen a játékverzión ebben a kiválasztott tárolóban, próbálj meg egy másik tárolót vagy a játék egy másik verzióját. @@ -1041,7 +1041,7 @@ DownloadComplete_MSG - Frissítések sikeresen letöltve! Minden elérhető frissítés letöltésre került, nem szükséges egyesével letölteni őket minden játékhoz, mint a csalások esetében. Ha a javítás nem jelenik meg, lehet, hogy nem létezik a játék adott sorozatszámához és verziójához. + Frissítések sikeresen letöltve! Minden elérhető frissítés letöltésre került, nem szükséges egyesével letölteni őket minden játékhoz, mint a csalások esetében. Ha a javítások nem jelennek meg, lehet, hogy nem léteznek a játék adott sorozatszámához és verziójához. @@ -1061,7 +1061,7 @@ The downloaded patch only works on version: %1 - A letöltött javítás csak a(z) %1 verzión működik + A letöltött javításhoz a(z) %1 verzió működik @@ -1096,7 +1096,7 @@ Directory does not exist: - A könyvtár nem létezik: + A mappa nem létezik: @@ -1159,22 +1159,22 @@ separateUpdatesCheckBox - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + Külön Frissítéi Mappa Engedélyezése:\nEngedélyezi a frissítések külön mappába helyezését, a könnyű kezelésük érdekében. showSplashCheckBox - Indító képernyő megjelenítése:\nMegjeleníti a játék indító képernyőjét (különleges képet) a játék elindításakor. + Indítóképernyő megjelenítése:\nMegjeleníti a játék indítóképernyőjét (különleges képet) a játék elindításakor. ps4proCheckBox - PS4 Pro:\nAz emulátort PS4 PRO-ként kezeli, ami engedélyezheti a speciális funkciókat olyan játékokban, amelyek támogatják. + PS4 Pro:\nAz emulátort PS4 PRO-ként kezeli, ami engedélyezhet speciális funkciókat olyan játékokban, amelyek támogatják azt. discordRPCCheckbox - Engedélyezze a Discord Rich Presence-t:\nMegjeleníti az emulator ikonját és a kapcsolódó információkat a Discord profilján. + A Discord Rich Presence engedélyezése:\nMegjeleníti az emulator ikonját és a kapcsolódó információkat a Discord profilján. @@ -1199,17 +1199,17 @@ GUIgroupBox - Játék címzene lejátszása:\nHa a játék támogatja, engedélyezze a speciális zene lejátszását, amikor a játékot kiválasztja a GUI-ban. + Játék címzene lejátszása:\nHa a játék támogatja, engedélyezze egy speciális zene lejátszását, amikor a játékot kiválasztja a GUI-ban. hideCursorGroupBox - Akurátor elrejtése:\nVálassza ki, mikor tűnjön el az egérkurzor:\nSoha: Az egér mindig látható.\nInaktív: Állítson be egy időt, amikor inaktív állapotban eltűnik.\nMindig: soha nem látja az egeret. + Kurzor elrejtése:\nVálassza ki, mikor tűnjön el az egérmutató:\nSoha: Az egér mindig látható.\nInaktív: Állítson be egy időt, amennyi idő mozdulatlanság után eltűnik.\nMindig: az egér mindig el lesz rejtve. idleTimeoutGroupBox - Állítson be egy időt, ameddig az egér inaktív állapot után eltűnik. + Állítson be egy időt, ami után egér inaktív állapotban eltűnik. @@ -1234,17 +1234,17 @@ Touchpad Left - Érintőpad Balra + Érintőpad Bal Touchpad Right - Érintőpad Jobbra + Érintőpad Jobb Touchpad Center - Érintőpad Középen + Érintőpad Közép @@ -1254,7 +1254,7 @@ graphicsAdapterGroupBox - Grafikus eszköz:\nTöbb GPU-s rendszereken válassza ki a GPU-t, amelyet az emulátor használ a legördülő listából,\nvagy válassza az "Auto Select" lehetőséget, hogy automatikusan meghatározza azt. + Grafikus eszköz:\nTöbb GPU-s rendszereken válassza ki, melyik GPU-t használja az emulátor a legördülő listából,\nvagy válassza az "Auto Select" lehetőséget, hogy automatikusan kiválassza azt. @@ -1264,7 +1264,7 @@ heightDivider - Vblank osztó:\nAz emulátor frissítési sebessége e számot megszorozva működik. Ennek megváltoztatása kedvezőtlen hatásokat okozhat, például növelheti a játék sebességét, vagy megszakíthat kritikus játékfunkciókat, amelyek nem számítanak arra, hogy ez megváltozik! + Vblank elosztó:\nAz emulátor frissítési sebessége e számot megszorozva működik. Ennek megváltoztatása kedvezőtlen hatásokat okozhat, például növelheti a játék sebességét, vagy megszakíthat kritikus játékfunkciókat, amelyek nem számítanak arra, hogy ez megváltozik! @@ -1279,7 +1279,7 @@ gameFoldersBox - Játék mappák:\nA mappák listája az telepített játékok ellenőrzésére. + Játék mappák:\nA mappák listája, ahol telepített játékok vannak. @@ -1420,7 +1420,7 @@ Latest Version - Legújabb verzió + Új verzió @@ -1430,7 +1430,7 @@ Show Changelog - Módosítások megjelenítése + Változások megjelenítése @@ -1445,17 +1445,17 @@ No - Nem + Mégse Hide Changelog - Módosítások elrejtése + Változások elrejtése Changes - Módosítások + Változások @@ -1488,4 +1488,4 @@ A frissítési szkript fájl létrehozása nem sikerült - \ No newline at end of file + From 85f45d2788bacd430343fbc27577b9da2d79b442 Mon Sep 17 00:00:00 2001 From: Florian Piesche Date: Tue, 12 Nov 2024 07:32:56 +0000 Subject: [PATCH 016/549] Add scalable vector redraw of shadps4 icon (#1501) * Add scalable vector redraw of shadps4 icon * Update icon name in desktop file * Fix svg --- .github/linux-appimage-qt.sh | 2 +- .github/linux-appimage-sdl.sh | 2 +- .github/shadps4.desktop | 2 +- CMakeLists.txt | 1 + REUSE.toml | 1 + src/images/net.shadps4.shadPS4.svg | 2 ++ 6 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 src/images/net.shadps4.shadPS4.svg diff --git a/.github/linux-appimage-qt.sh b/.github/linux-appimage-qt.sh index 8a5f340f6..d176b17dc 100755 --- a/.github/linux-appimage-qt.sh +++ b/.github/linux-appimage-qt.sh @@ -27,7 +27,7 @@ chmod a+x linuxdeploy-plugin-checkrt-x86_64.sh cp -a "$GITHUB_WORKSPACE/build/translations" AppDir/usr/bin -./linuxdeploy-x86_64.AppImage --appdir AppDir -d "$GITHUB_WORKSPACE"/.github/shadps4.desktop -e "$GITHUB_WORKSPACE"/build/shadps4 -i "$GITHUB_WORKSPACE"/.github/shadps4.png --plugin qt +./linuxdeploy-x86_64.AppImage --appdir AppDir -d "$GITHUB_WORKSPACE"/.github/shadps4.desktop -e "$GITHUB_WORKSPACE"/build/shadps4 -i "$GITHUB_WORKSPACE"/src/images/net.shadps4.shadPS4.svg --plugin qt rm AppDir/usr/plugins/multimedia/libgstreamermediaplugin.so ./linuxdeploy-x86_64.AppImage --appdir AppDir --output appimage mv shadPS4-x86_64.AppImage Shadps4-qt.AppImage diff --git a/.github/linux-appimage-sdl.sh b/.github/linux-appimage-sdl.sh index cf0ce4de1..d747abbe6 100755 --- a/.github/linux-appimage-sdl.sh +++ b/.github/linux-appimage-sdl.sh @@ -17,5 +17,5 @@ chmod a+x linuxdeploy-plugin-checkrt-x86_64.sh # Build AppImage ./linuxdeploy-x86_64.AppImage --appdir AppDir ./linuxdeploy-plugin-checkrt-x86_64.sh --appdir AppDir -./linuxdeploy-x86_64.AppImage --appdir AppDir -d "$GITHUB_WORKSPACE"/.github/shadps4.desktop -e "$GITHUB_WORKSPACE"/build/shadps4 -i "$GITHUB_WORKSPACE"/.github/shadps4.png --output appimage +./linuxdeploy-x86_64.AppImage --appdir AppDir -d "$GITHUB_WORKSPACE"/.github/shadps4.desktop -e "$GITHUB_WORKSPACE"/build/shadps4 -i "$GITHUB_WORKSPACE"/src/images/net.shadps4.shadPS4.svg --output appimage mv shadPS4-x86_64.AppImage Shadps4-sdl.AppImage diff --git a/.github/shadps4.desktop b/.github/shadps4.desktop index 6dc663718..b9070c4f4 100644 --- a/.github/shadps4.desktop +++ b/.github/shadps4.desktop @@ -3,7 +3,7 @@ Name=shadPS4 Exec=shadps4 Terminal=false Type=Application -Icon=shadps4 +Icon=net.shadps4.shadPS4 Comment=shadPS4 Emulator Categories=Game; StartupWMClass=shadps4; diff --git a/CMakeLists.txt b/CMakeLists.txt index 439663352..e0e877a52 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -945,4 +945,5 @@ install(TARGETS shadps4 BUNDLE DESTINATION .) if (ENABLE_QT_GUI AND CMAKE_SYSTEM_NAME STREQUAL "Linux") install(FILES ".github/shadps4.desktop" DESTINATION "share/applications") install(FILES ".github/shadps4.png" DESTINATION "share/icons/hicolor/512x512/apps") + install(FILES "src/images/net.shadps4.shadPS4.svg" DESTINATION "share/icons/hicolor/scalable/apps") endif() diff --git a/REUSE.toml b/REUSE.toml index e1a266030..b04969735 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -35,6 +35,7 @@ path = [ "src/images/stop_icon.png", "src/images/shadPS4.icns", "src/images/shadps4.ico", + "src/images/net.shadps4.shadPS4.svg", "src/images/themes_icon.png", "src/images/update_icon.png", "src/shadps4.qrc", diff --git a/src/images/net.shadps4.shadPS4.svg b/src/images/net.shadps4.shadPS4.svg new file mode 100644 index 000000000..2d954b12c --- /dev/null +++ b/src/images/net.shadps4.shadPS4.svg @@ -0,0 +1,2 @@ + + From 0c52d02043404b6fdb00ae01d5eca57ce61c0949 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Wed, 13 Nov 2024 09:04:13 -0800 Subject: [PATCH 017/549] videodec: Lower sceVideodecDecode log to trace. (#1527) --- src/core/libraries/videodec/videodec.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/libraries/videodec/videodec.cpp b/src/core/libraries/videodec/videodec.cpp index 96ece3c5c..8d6ea4988 100644 --- a/src/core/libraries/videodec/videodec.cpp +++ b/src/core/libraries/videodec/videodec.cpp @@ -36,7 +36,7 @@ int PS4_SYSV_ABI sceVideodecDecode(OrbisVideodecCtrl* pCtrlIn, const OrbisVideodecInputData* pInputDataIn, OrbisVideodecFrameBuffer* pFrameBufferInOut, OrbisVideodecPictureInfo* pPictureInfoOut) { - LOG_INFO(Lib_Videodec, "called"); + LOG_TRACE(Lib_Videodec, "called"); if (!pCtrlIn || !pInputDataIn || !pPictureInfoOut) { return ORBIS_VIDEODEC_ERROR_ARGUMENT_POINTER; } From 6ae92c0aac3eec89a26baaad48cb05d9efb9bd27 Mon Sep 17 00:00:00 2001 From: Florian Piesche Date: Thu, 14 Nov 2024 08:56:14 +0000 Subject: [PATCH 018/549] Add FreeDesktop metadata (#1495) --- .github/linux-appimage-qt.sh | 2 +- .github/linux-appimage-sdl.sh | 2 +- CMakeLists.txt | 6 +- REUSE.toml | 5 +- .../net.shadps4.shadPS4.desktop | 2 +- dist/net.shadps4.shadPS4.metainfo.xml | 63 ++++++++++++++++++ dist/net.shadps4.shadPS4.releases.xml | 23 +++++++ dist/net.shadps4.shadPS4_metadata.pot | 65 +++++++++++++++++++ 8 files changed, 162 insertions(+), 6 deletions(-) rename .github/shadps4.desktop => dist/net.shadps4.shadPS4.desktop (81%) create mode 100644 dist/net.shadps4.shadPS4.metainfo.xml create mode 100644 dist/net.shadps4.shadPS4.releases.xml create mode 100644 dist/net.shadps4.shadPS4_metadata.pot diff --git a/.github/linux-appimage-qt.sh b/.github/linux-appimage-qt.sh index d176b17dc..776eed61e 100755 --- a/.github/linux-appimage-qt.sh +++ b/.github/linux-appimage-qt.sh @@ -27,7 +27,7 @@ chmod a+x linuxdeploy-plugin-checkrt-x86_64.sh cp -a "$GITHUB_WORKSPACE/build/translations" AppDir/usr/bin -./linuxdeploy-x86_64.AppImage --appdir AppDir -d "$GITHUB_WORKSPACE"/.github/shadps4.desktop -e "$GITHUB_WORKSPACE"/build/shadps4 -i "$GITHUB_WORKSPACE"/src/images/net.shadps4.shadPS4.svg --plugin qt +./linuxdeploy-x86_64.AppImage --appdir AppDir -d "$GITHUB_WORKSPACE"/dist/net.shadps4.shadPS4.desktop -e "$GITHUB_WORKSPACE"/build/shadps4 -i "$GITHUB_WORKSPACE"/src/images/net.shadps4.shadPS4.svg --plugin qt rm AppDir/usr/plugins/multimedia/libgstreamermediaplugin.so ./linuxdeploy-x86_64.AppImage --appdir AppDir --output appimage mv shadPS4-x86_64.AppImage Shadps4-qt.AppImage diff --git a/.github/linux-appimage-sdl.sh b/.github/linux-appimage-sdl.sh index d747abbe6..7961f5312 100755 --- a/.github/linux-appimage-sdl.sh +++ b/.github/linux-appimage-sdl.sh @@ -17,5 +17,5 @@ chmod a+x linuxdeploy-plugin-checkrt-x86_64.sh # Build AppImage ./linuxdeploy-x86_64.AppImage --appdir AppDir ./linuxdeploy-plugin-checkrt-x86_64.sh --appdir AppDir -./linuxdeploy-x86_64.AppImage --appdir AppDir -d "$GITHUB_WORKSPACE"/.github/shadps4.desktop -e "$GITHUB_WORKSPACE"/build/shadps4 -i "$GITHUB_WORKSPACE"/src/images/net.shadps4.shadPS4.svg --output appimage +./linuxdeploy-x86_64.AppImage --appdir AppDir -d "$GITHUB_WORKSPACE"/dist/net.shadps4.shadPS4.desktop -e "$GITHUB_WORKSPACE"/build/shadps4 -i "$GITHUB_WORKSPACE"/src/images/net.shadps4.shadPS4.svg --output appimage mv shadPS4-x86_64.AppImage Shadps4-sdl.AppImage diff --git a/CMakeLists.txt b/CMakeLists.txt index e0e877a52..d320d49e3 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -943,7 +943,9 @@ endif() install(TARGETS shadps4 BUNDLE DESTINATION .) if (ENABLE_QT_GUI AND CMAKE_SYSTEM_NAME STREQUAL "Linux") - install(FILES ".github/shadps4.desktop" DESTINATION "share/applications") - install(FILES ".github/shadps4.png" DESTINATION "share/icons/hicolor/512x512/apps") + install(FILES "dist/net.shadps4.shadPS4.desktop" DESTINATION "share/applications") + install(FILES "dist/net.shadps4.shadPS4.releases.xml" DESTINATION "share/metainfo/releases") + install(FILES "dist/net.shadps4.shadPS4.metainfo.xml" DESTINATION "share/metainfo") + install(FILES ".github/shadps4.png" DESTINATION "share/icons/hicolor/512x512/apps" RENAME "net.shadps4.shadPS4.png") install(FILES "src/images/net.shadps4.shadPS4.svg" DESTINATION "share/icons/hicolor/scalable/apps") endif() diff --git a/REUSE.toml b/REUSE.toml index b04969735..405156231 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -5,9 +5,12 @@ path = [ "REUSE.toml", "CMakeSettings.json", ".github/FUNDING.yml", - ".github/shadps4.desktop", ".github/shadps4.png", ".gitmodules", + "dist/net.shadps4.shadPS4.desktop", + "dist/net.shadps4.shadPS4_metadata.pot", + "dist/net.shadps4.shadPS4.metainfo.xml", + "dist/net.shadps4.shadPS4.releases.xml", "documents/changelog.txt", "documents/Quickstart/2.png", "documents/Screenshots/*", diff --git a/.github/shadps4.desktop b/dist/net.shadps4.shadPS4.desktop similarity index 81% rename from .github/shadps4.desktop rename to dist/net.shadps4.shadPS4.desktop index b9070c4f4..fbefa0566 100644 --- a/.github/shadps4.desktop +++ b/dist/net.shadps4.shadPS4.desktop @@ -4,6 +4,6 @@ Exec=shadps4 Terminal=false Type=Application Icon=net.shadps4.shadPS4 -Comment=shadPS4 Emulator +Comment=PlayStation 4 emulator Categories=Game; StartupWMClass=shadps4; diff --git a/dist/net.shadps4.shadPS4.metainfo.xml b/dist/net.shadps4.shadPS4.metainfo.xml new file mode 100644 index 000000000..bef0bec4c --- /dev/null +++ b/dist/net.shadps4.shadPS4.metainfo.xml @@ -0,0 +1,63 @@ + + + net.shadps4.shadPS4 + shadPS4 + + shadPS4 Contributors + https://github.com/shadps4-emu/shadps4/graphs/contributors + + PS4 Emulator + CC0-1.0 + GPL-2.0 + net.shadps4.shadPS4.desktop + https://shadps4.net/ + +

shadPS4 is an early PlayStation 4 emulator for Windows, Linux and macOS written in C++.

+

The emulator is still early in development, so don't expect a flawless experience. Nonetheless, the emulator can already run a number of commercial games.

+
+ + + https://cdn.jsdelivr.net/gh/shadps4-emu/shadps4@main/documents/Screenshots/1.png + Bloodborne + + + https://cdn.jsdelivr.net/gh/shadps4-emu/shadps4@main/documents/Screenshots/2.png + Hatsune Miku: Project DIVA Future Tone + + + https://cdn.jsdelivr.net/gh/shadps4-emu/shadps4@main/documents/Screenshots/3.png + Yakuza Kiwami + + + https://cdn.jsdelivr.net/gh/shadps4-emu/shadps4@main/documents/Screenshots/4.png + Persona 4 Golden + + + + Game + + + + + + + + + keyboard + + + gamepad + + + offline-only + + + shadps4 + + + emulator + emulation + playstation + ps4 + +
diff --git a/dist/net.shadps4.shadPS4.releases.xml b/dist/net.shadps4.shadPS4.releases.xml new file mode 100644 index 000000000..8da203fe4 --- /dev/null +++ b/dist/net.shadps4.shadPS4.releases.xml @@ -0,0 +1,23 @@ + + + https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.4.0 + + + https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.3.0 + + + https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.2.0 + + + https://github.com/shadps4-emu/shadPS4/releases/tag/0.1.0 + + + https://github.com/shadps4-emu/shadPS4/releases/tag/v0.0.3 + + + https://github.com/shadps4-emu/shadPS4/releases/tag/v0.0.2 + + + https://github.com/shadps4-emu/shadPS4/releases/tag/v0.0.1 + + diff --git a/dist/net.shadps4.shadPS4_metadata.pot b/dist/net.shadps4.shadPS4_metadata.pot new file mode 100644 index 000000000..d77947b7a --- /dev/null +++ b/dist/net.shadps4.shadPS4_metadata.pot @@ -0,0 +1,65 @@ +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2024-11-08 09:07+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#. (itstool) path: component/name +#: ./net.shadps4.shadPS4.metainfo.xml:4 +msgid "shadPS4" +msgstr "" + +#. (itstool) path: developer/name +#: ./net.shadps4.shadPS4.metainfo.xml:6 +msgid "shadPS4 Contributors" +msgstr "" + +#. (itstool) path: component/summary +#: ./net.shadps4.shadPS4.metainfo.xml:9 +msgid "PS4 Emulator" +msgstr "" + +#. (itstool) path: description/p +#: ./net.shadps4.shadPS4.metainfo.xml:16 +msgid "shadPS4 is an early PlayStation 4 emulator for Windows, Linux and macOS written in C++." +msgstr "" + +#. (itstool) path: description/p +#: ./net.shadps4.shadPS4.metainfo.xml:17 +msgid "The emulator is still early in development, so don't expect a flawless experience. Nonetheless, the emulator can already run a number of commercial games." +msgstr "" + +#. (itstool) path: screenshot/caption +#: ./net.shadps4.shadPS4.metainfo.xml:22 +msgid "Bloodborne" +msgstr "" + +#. (itstool) path: screenshot/caption +#: ./net.shadps4.shadPS4.metainfo.xml:26 +msgid "Hatsune Miku: Project DIVA Future Tone" +msgstr "" + +#. (itstool) path: screenshot/caption +#: ./net.shadps4.shadPS4.metainfo.xml:30 +msgid "Yakuza Kiwami" +msgstr "" + +#. (itstool) path: screenshot/caption +#: ./net.shadps4.shadPS4.metainfo.xml:34 +msgid "Persona 4 Golden" +msgstr "" + +#. (itstool) path: keywords/keyword +#: ./net.shadps4.shadPS4.metainfo.xml:59 +msgid "emulator" +msgstr "" + +#. (itstool) path: keywords/keyword +#: ./net.shadps4.shadPS4.metainfo.xml:60 +msgid "emulation" +msgstr "" From 8e281575b582eba835045957fff64346eacfae6e Mon Sep 17 00:00:00 2001 From: Martin <67326368+Martini-141@users.noreply.github.com> Date: Thu, 14 Nov 2024 11:00:59 +0100 Subject: [PATCH 019/549] =?UTF-8?q?Update=20Norwegian=20Bokm=C3=A5l=20tran?= =?UTF-8?q?slations=20(#1525)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * specify nb_NO * Update Norwegian Bokmål translation * change patch wording nb_NO --- src/qt_gui/settings_dialog.cpp | 4 +- src/qt_gui/translations/{nb.ts => nb_NO.ts} | 306 ++++++++++---------- 2 files changed, 155 insertions(+), 155 deletions(-) rename src/qt_gui/translations/{nb.ts => nb_NO.ts} (81%) diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index da2e3bd11..83582663a 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -31,7 +31,7 @@ QStringList languageNames = {"Arabic", "Italian", "Japanese", "Korean", - "Norwegian", + "Norwegian (Bokmaal)", "Polish", "Portuguese (Brazil)", "Portuguese (Portugal)", @@ -548,4 +548,4 @@ bool SettingsDialog::eventFilter(QObject* obj, QEvent* event) { } } return QDialog::eventFilter(obj, event); -} \ No newline at end of file +} diff --git a/src/qt_gui/translations/nb.ts b/src/qt_gui/translations/nb_NO.ts similarity index 81% rename from src/qt_gui/translations/nb.ts rename to src/qt_gui/translations/nb_NO.ts index 940c6c9b6..e02f24182 100644 --- a/src/qt_gui/translations/nb.ts +++ b/src/qt_gui/translations/nb_NO.ts @@ -8,7 +8,7 @@ About shadPS4 - About shadPS4 + Om shadPS4 @@ -18,12 +18,12 @@ shadPS4 is an experimental open-source emulator for the PlayStation 4. - shadPS4 is an experimental open-source emulator for the PlayStation 4. + shadPS4 er en eksperimentell åpen kildekode-etterligner for PlayStation 4. This software should not be used to play games you have not legally obtained. - This software should not be used to play games you have not legally obtained. + Denne programvaren skal ikke brukes til å spille spill du ikke har fått lovlig. @@ -31,7 +31,7 @@ Open Folder - Open Folder + Åpne mappe @@ -39,17 +39,17 @@ Loading game list, please wait :3 - Loading game list, please wait :3 + Laster spill-liste, vennligst vent :3 Cancel - Cancel + Avbryt Loading... - Loading... + Laster... @@ -57,12 +57,12 @@ shadPS4 - Choose directory - shadPS4 - Choose directory + shadPS4 - Velg mappe Select which directory you want to install to. - Select which directory you want to install to. + Velg hvilken mappe du vil installere til. @@ -70,27 +70,27 @@ shadPS4 - Choose directory - shadPS4 - Choose directory + shadPS4 - Velg mappe Directory to install games - Directory to install games + Mappe for å installere spill Browse - Browse + Bla gjennom Error - Error + Feil The value for location to install games is not valid. - The value for location to install games is not valid. + Verdien for mappen for å installere spill er ikke gyldig. @@ -98,22 +98,22 @@ Create Shortcut - Create Shortcut + Lag snarvei Cheats / Patches - Juks / Oppdateringer + Juks / Programrettelse SFO Viewer - SFO Viewer + SFO Viser Trophy Viewer - Trophy Viewer + Trofé Viser @@ -138,92 +138,92 @@ Copy info... - Copy info... + Kopier info... Copy Name - Copy Name + Kopier Navn Copy Serial - Copy Serial + Kopier Serienummer Copy All - Copy All + Kopier Alle Delete... - Delete... + Slett... Delete Game - Delete Game + Slett Spill Delete Update - Delete Update + Slett Oppdatering Delete DLC - Delete DLC + Slett DLC Shortcut creation - Shortcut creation + Snarvei opprettelse Shortcut created successfully!\n %1 - Shortcut created successfully!\n %1 + Snarvei opprettet!\n %1 Error - Error + Feil Error creating shortcut!\n %1 - Error creating shortcut!\n %1 + Feil ved opprettelse av snarvei!\n %1 Install PKG - Install PKG + Installer PKG Game - Game + Spill requiresEnableSeparateUpdateFolder_MSG - This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + Denne funksjonen krever 'Aktiver seperat oppdateringsmappe' konfigurasjonsalternativet. Hvis du vil bruke denne funksjonen, vennligst aktiver den. This game has no update to delete! - This game has no update to delete! + Dette spillet har ingen oppdatering å slette! Update - Update + Oppdater This game has no DLC to delete! - This game has no DLC to delete! + Dette spillet har ingen DLC å slette! @@ -233,12 +233,12 @@ Delete %1 - Delete %1 + Slett %1 Are you sure you want to delete %1's %2 directory? - Are you sure you want to delete %1's %2 directory? + Er du sikker på at du vil slette %1's %2 directory? @@ -246,17 +246,17 @@ Open/Add Elf Folder - Open/Add Elf Folder + Åpne/Legg til Elf-mappe Install Packages (PKG) - Install Packages (PKG) + Installer Pakker (PKG) Boot Game - Boot Game + Start Spill @@ -266,57 +266,57 @@ About shadPS4 - About shadPS4 + Om shadPS4 Configure... - Configure... + Konfigurer... Install application from a .pkg file - Install application from a .pkg file + Installer fra en .pkg fil Recent Games - Recent Games + Nylige Spill Exit - Exit + Avslutt Exit shadPS4 - Exit shadPS4 + Avslutt shadPS4 Exit the application. - Exit the application. + Avslutt programmet. Show Game List - Show Game List + Vis Spill-listen Game List Refresh - Game List Refresh + Oppdater Spill-listen Tiny - Tiny + Bitteliten Small - Small + Liten @@ -326,82 +326,82 @@ Large - Large + Stor List View - List View + Liste-visning Grid View - Grid View + Rute-visning Elf Viewer - Elf Viewer + Elf-visning Game Install Directory - Game Install Directory + Spillinstallasjons-mappe Download Cheats/Patches - Last ned Juks / Oppdateringer + Last ned Juks /Programrettelse Dump Game List - Dump Game List + Dump Spill-liste PKG Viewer - PKG Viewer + PKG Viser Search... - Search... + Søk... File - File + Fil View - View + Oversikt Game List Icons - Game List Icons + Spill-liste Ikoner Game List Mode - Game List Mode + Spill-liste Modus Settings - Settings + Innstillinger Utils - Utils + Verktøy Themes - Themes + Tema @@ -411,32 +411,32 @@ Dark - Dark + Mørk Light - Light + Lys Green - Green + Grønn Blue - Blue + Blå Violet - Violet + Lilla toolBar - toolBar + Verktøylinje @@ -444,7 +444,7 @@ Open Folder - Open Folder + Åpne Mappe @@ -452,7 +452,7 @@ Trophy Viewer - Trophy Viewer + Trofé Viser @@ -460,12 +460,12 @@ Settings - Settings + Innstillinger General - General + Generell @@ -475,37 +475,37 @@ Console Language - Console Language + Konsollspråk Emulator Language - Emulator Language + Etterlignerspråk Emulator - Emulator + Etterligner Enable Fullscreen - Enable Fullscreen + Aktiver Fullskjerm Enable Separate Update Folder - Enable Separate Update Folder + Aktiver Seperat Oppdateringsmappe Show Splash - Show Splash + Vis Velkomst Is PS4 Pro - Is PS4 Pro + Er PS4 Pro @@ -515,7 +515,7 @@ Username - Username + Brukernavn @@ -540,17 +540,17 @@ Cursor - Markør + Musepeker Hide Cursor - Skjul markør + Skjul musepeker Hide Cursor Idle Timeout - Timeout for å skjule markør ved inaktivitet + Skjul musepeker ved inaktivitet @@ -560,47 +560,47 @@ Back Button Behavior - Tilbakeknapp atferd + Tilbakeknapp Atferd Graphics - Graphics + Grafikk Graphics Device - Graphics Device + Grafikkenhet Width - Width + Bredde Height - Height + Høyde Vblank Divider - Vblank Divider + Vblank Skillelinje Advanced - Advanced + Avansert Enable Shaders Dumping - Enable Shaders Dumping + Aktiver Skyggelegger Dumping Enable NULL GPU - Enable NULL GPU + Aktiver NULL GPU @@ -625,27 +625,27 @@ Debug - Debug + Feilretting Enable Debug Dumping - Enable Debug Dumping + Aktiver Feilretting Dumping Enable Vulkan Validation Layers - Enable Vulkan Validation Layers + Aktiver Vulkan Valideringslag Enable Vulkan Synchronization Validation - Enable Vulkan Synchronization Validation + Aktiver Vulkan Synkroniseringslag Enable RenderDoc Debugging - Enable RenderDoc Debugging + Aktiver RenderDoc Feilretting @@ -688,17 +688,17 @@ Game List - Spilliste + Spill-liste * Unsupported Vulkan Version - * Ikke støttet Vulkan-versjon + * Ustøttet Vulkan-versjon Download Cheats For All Installed Games - Last ned jukser for alle installerte spill + Last ned juks for alle installerte spill @@ -713,17 +713,17 @@ You have downloaded cheats for all the games you have installed. - Du har lastet ned jukser for alle spillene du har installert. + Du har lastet ned juks for alle spillene du har installert. Patches Downloaded Successfully! - Oppdateringer lastet ned vellykket! + Programrettelser lastet ned vellykket! All Patches available for all games have been downloaded. - Alle oppdateringer tilgjengelige for alle spillene har blitt lastet ned. + Programrettelser tilgjengelige for alle spill har blitt lastet ned. @@ -758,7 +758,7 @@ Patch detected! - Oppdatering oppdaget! + Programrettelse oppdaget! @@ -783,7 +783,7 @@ Would you like to install Patch: - Ønsker du å installere oppdateringen: + Ønsker du å installere programrettelsen: @@ -818,12 +818,12 @@ Extracting PKG %1/%2 - Ekstraherer PKG %1/%2 + Pakker ut PKG %1/%2 Extraction Finished - Ekstrahering fullført + Utpakking fullført @@ -833,7 +833,7 @@ File doesn't appear to be a valid PKG file - Fil ser ikke ut til å være en gyldig PKG-fil + Filen ser ikke ut til å være en gyldig PKG-fil @@ -841,12 +841,12 @@ Cheats / Patches - Jukser / Oppdateringer + Juks / Programrettelse defaultTextEdit_MSG - Cheats/Patches er eksperimentelle.\nBruk med forsiktighet.\n\nLast ned cheats individuelt ved å velge depotet og klikke på nedlastingsknappen.\nPå fanen Patches kan du laste ned alle patches samtidig, velge hvilke du ønsker å bruke, og lagre valget ditt.\n\nSiden vi ikke utvikler Cheats/Patches,\nvær vennlig å rapportere problemer til cheat-utvikleren.\n\nHar du laget en ny cheat? Besøk:\nhttps://github.com/shadps4-emu/ps4_cheats + Juks/programrettelse er eksperimentelle.\nBruk med forsiktighet.\n\nLast ned juks individuelt ved å velge pakkebrønn og klikke på nedlastingsknappen.\nPå fanen programrettelse kan du laste ned alle programrettelser samtidig, velge hvilke du ønsker å bruke, og lagre valget ditt.\n\nSiden vi ikke utvikler Juksene/Programrettelsene,\nvær vennlig å rapportere problemer til juks-utvikleren.\n\nHar du laget en ny juks? Besøk:\nhttps://github.com/shadps4-emu/ps4_cheats @@ -876,12 +876,12 @@ Repository: - Depot: + Pakkebrønn: Download Cheats - Last ned jukser + Last ned juks @@ -906,12 +906,12 @@ Select Patch File: - Velg oppdateringsfil: + Velg programrettelse-filen: Download Patches - Last ned oppdateringer + Last ned programrettelse @@ -926,7 +926,7 @@ Patches - Oppdateringer + Programrettelse @@ -936,7 +936,7 @@ No patch selected. - Ingen oppdatering valgt. + Ingen programrettelse valgt. @@ -946,7 +946,7 @@ No patch file found for the current serial. - Ingen oppdateringsfil funnet for det aktuelle serienummeret. + Ingen programrettelse-fil funnet for det aktuelle serienummeret. @@ -961,7 +961,7 @@ Failed to parse XML: - Feil ved parsing av XML: + Feil ved tolkning av XML: @@ -986,12 +986,12 @@ File Exists - Fil eksisterer + Filen eksisterer File already exists. Do you want to replace it? - Fil eksisterer allerede. Ønsker du å erstatte den? + Filen eksisterer allerede. Ønsker du å erstatte den? @@ -1011,7 +1011,7 @@ CheatsNotFound_MSG - Ingen jukser funnet for dette spillet i denne versjonen av det valgte depotet,prøv et annet depot eller en annen versjon av spillet. + Ingen juks funnet for dette spillet i denne versjonen av den valgte pakkebrønnen,prøv en annen pakkebrønn eller en annen versjon av spillet. @@ -1021,7 +1021,7 @@ CheatsDownloadedSuccessfully_MSG - Du har lastet ned jukser vellykket for denne versjonen av spillet fra det valgte depotet. Du kan prøve å laste ned fra et annet depot, hvis det er tilgjengelig, vil det også være mulig å bruke det ved å velge filen fra listen. + Du har lastet ned jukser vellykket for denne versjonen av spillet fra den valgte pakkebrønnen. Du kan prøve å laste ned fra en annen pakkebrønn, hvis det er tilgjengelig, vil det også være mulig å bruke det ved å velge filen fra listen. @@ -1041,7 +1041,7 @@ DownloadComplete_MSG - Oppdateringer lastet ned vellykket! Alle oppdateringer tilgjengelige for alle spill har blitt lastet ned, det er ikke nødvendig å laste dem ned individuelt for hvert spill som skjer med jukser. Hvis oppdateringen ikke vises, kan det hende at den ikke finnes for den spesifikke serienummeret og versjonen av spillet. + Oppdateringer lastet ned vellykket! Alle programrettelsene tilgjengelige for alle spill har blitt lastet ned, det er ikke nødvendig å laste dem ned individuelt for hvert spill som skjer med jukser. Hvis programrettelsen ikke vises, kan det hende at den ikke finnes for den spesifikke serienummeret og versjonen av spillet. @@ -1061,7 +1061,7 @@ The downloaded patch only works on version: %1 - Den nedlastede patchen fungerer bare på versjon: %1 + Den nedlastede programrettelsen fungerer bare på versjon: %1 @@ -1071,7 +1071,7 @@ Incompatibility Notice - Inkompatibilitetsvarsel + Inkompatibilitets-varsel @@ -1096,7 +1096,7 @@ Directory does not exist: - Direktory eksisterer ikke: + Mappen eksisterer ikke: @@ -1119,7 +1119,7 @@ Save - Lag + Lagre @@ -1139,7 +1139,7 @@ Point your mouse at an option to display its description. - Hold musen over et valg for at vise beskrivelsen. + Pek musen over et alternativ for å vise beskrivelsen. @@ -1149,7 +1149,7 @@ emulatorLanguageGroupBox - Emulatorspråk:\nAngir språket for emulatorens brukergrensesnitt. + Etterlignerspråket:\nAngir språket for etterlignerens brukergrensesnitt. @@ -1159,7 +1159,7 @@ separateUpdatesCheckBox - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + Aktiver separat oppdateringsmappe:\nAktiverer installering av spill i en egen mappe for enkel administrasjon. @@ -1169,12 +1169,12 @@ ps4proCheckBox - Er PS4 Pro:\nFår emulatoren til å fungere som en PS4 PRO, noe som kan aktivere spesielle funksjoner i spill som støtter det. + Er PS4 Pro:\nFår emulatoren til å fungere som en PS4 PRO, noe som kan aktivere spesielle funksjoner i spill som støtter dette. discordRPCCheckbox - Aktiver Discord Rich Presence:\nViser emulatorikonet og relevant informasjon på Discord-profilen din. + Aktiver Discord Rich Presence:\nViser etterlignerikonet og relevant informasjon på Discord-profilen din. @@ -1184,7 +1184,7 @@ logTypeGroupBox - Logtype:\nAngir om loggvinduets utdata skal synkroniseres for ytelse. Kan ha negative effekter på emulering. + Logtype:\nAngir om loggvinduets utdata skal synkroniseres for ytelse. Kan ha negative effekter for etterligneren. @@ -1199,17 +1199,17 @@ GUIgroupBox - Spille tittelmusikk:\nHvis et spill støtter det, aktiverer det å spille spesiell musikk når du velger spillet i GUI. + Spille tittelmusikk:\nHvis et spill støtter det, aktiverer spesiell musikk når du velger spillet i menyen. hideCursorGroupBox - Skjul musepeker:\nVelg når musepekeren skal forsvinne:\nAldri: Du vil alltid se musen.\nInaktiv: Sett en tid for at den skal forsvinne etter å ha vært inaktiv.\nAlltid: du vil aldri se musen. + Skjul musepeker:\nVelg når musepekeren skal forsvinne:\nAldri: Du vil alltid se musepekeren.\nInaktiv: Sett en tid for at den skal forsvinne etter å ha vært inaktiv.\nAlltid: du vil aldri se musepekeren. idleTimeoutGroupBox - Sett en tid for når musen forsvinner etter å ha vært inaktiv. + Sett en tid for når musepekeren forsvinner etter å ha vært inaktiv. @@ -1254,27 +1254,27 @@ graphicsAdapterGroupBox - Grafikkdevice:\nI systemer med flere GPU-er, velg GPU-en emulatoren skal bruke fra rullegardinlisten,\neller velg "Auto Select" for å bestemme det automatisk. + Grafikkenhet:\nI systemer med flere GPU-er, velg GPU-en etterligneren skal bruke fra rullegardinlisten,\neller velg "Auto Select" for å bestemme det automatisk. resolutionLayout - Bredde/Høyde:\nAngir størrelsen på emulatorkvinduet ved oppstart, som kan endres under spillingen.\nDette er forskjellig fra oppløsningen i spillet. + Bredde/Høyde:\nAngir størrelsen på etterlignerkvinduet ved oppstart, som kan endres under spillingen.\nDette er forskjellig fra oppløsningen i spillet. heightDivider - Vblank divider:\nBilderaten som emulatoren oppdaterer ved, multipliseres med dette tallet. Endring av dette kan ha negative effekter, som å øke hastigheten på spillet, eller ødelegge kritisk spillfunksjonalitet som ikke forventer at dette endres! + Vblank Skillelinje:\nBildehastigheten som etterligneren oppdaterer ved, multipliseres med dette tallet. Endring av dette kan ha negative effekter, som å øke hastigheten på spillet, eller ødelegge kritisk spillfunksjonalitet som ikke forventer at dette endres! dumpShadersCheckBox - Aktiver shaderdumping:\nFor teknisk feilsøking lagrer shaderne fra spillet i en mappe mens de gjengis. + Aktiver skyggelegger-dumping:\nFor teknisk feilsøking lagrer skyggeleggene fra spillet i en mappe mens de gjengis. nullGpuCheckBox - Aktiver Null GPU:\nFor teknisk feilsøking deaktiverer spillrendering som om det ikke var noe grafikkort. + Aktiver Null GPU:\nFor teknisk feilsøking deaktiverer spillgjengivelse som om det ikke var noe grafikkort. @@ -1294,22 +1294,22 @@ debugDump - Aktiver feilsøking dumping:\nLagrer import- og eksport-symbolene og filoverskriftsinformasjonen til det nåværende kjørende PS4-programmet i en katalog. + Aktiver dumping av feilsøking:\nLagrer import- og eksport-symbolene og filoverskriftsinformasjonen til det nåværende kjørende PS4-programmet i en katalog. vkValidationCheckBox - Aktiver Vulkan valideringslag:\nAktiverer et system som validerer tilstanden til Vulkan-rendereren og logger informasjon om dens indre tilstand. Dette vil redusere ytelsen og sannsynligvis endre emuleringens oppførsel. + Aktiver Vulkan valideringslag:\nAktiverer et system som validerer tilstanden til Vulkan-gjengiveren og logger informasjon om dens indre tilstand. Dette vil redusere ytelsen og sannsynligvis endre etterlignerens oppførsel. vkSyncValidationCheckBox - Aktiver Vulkan synkronisering validering:\nAktiverer et system som validerer timingen av Vulkan-renderingsoppgaver. Dette vil redusere ytelsen og sannsynligvis endre emuleringens oppførsel. + Aktiver Vulkan synkronisering validering:\nAktiverer et system som validerer frekvens tiden av Vulkan-gjengivelsensoppgaver. Dette vil redusere ytelsen og sannsynligvis endre etterlignerens oppførsel. rdocCheckBox - Aktiver RenderDoc feilsøking:\nHvis aktivert, vil emulatoren gi kompatibilitet med Renderdoc for å tillate opptak og analyse av det nåværende renderte bildet. + Aktiver RenderDoc feilsøking:\nHvis aktivert, vil etterligneren gi kompatibilitet med Renderdoc for å tillate opptak og analyse av det nåværende gjengitte bildet. @@ -1337,7 +1337,7 @@ Firmware - Firmware + Fastvare @@ -1488,4 +1488,4 @@ Kunne ikke opprette oppdateringsskriptfilen - \ No newline at end of file + From bf239ebc04c2c2b7240608101193deda130acc5b Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Thu, 14 Nov 2024 13:01:20 +0300 Subject: [PATCH 020/549] ajm: handle single-frame decode jobs (+mp3 imrovements) (#1520) * ajm: handle single-frame decode jobs (+mp3 imrovements) * disable breaking the loop in multi-frame if storage is insufficient * simplified gapless decoding --- src/core/libraries/ajm/ajm_at9.cpp | 66 ++--- src/core/libraries/ajm/ajm_at9.h | 10 +- src/core/libraries/ajm/ajm_batch.cpp | 19 +- src/core/libraries/ajm/ajm_instance.cpp | 71 +++-- src/core/libraries/ajm/ajm_instance.h | 34 ++- src/core/libraries/ajm/ajm_mp3.cpp | 367 +++++++++++++++++++----- src/core/libraries/ajm/ajm_mp3.h | 39 ++- 7 files changed, 450 insertions(+), 156 deletions(-) diff --git a/src/core/libraries/ajm/ajm_at9.cpp b/src/core/libraries/ajm/ajm_at9.cpp index 7fff0ff0c..936929ae8 100644 --- a/src/core/libraries/ajm/ajm_at9.cpp +++ b/src/core/libraries/ajm/ajm_at9.cpp @@ -40,23 +40,11 @@ void AjmAt9Decoder::Initialize(const void* buffer, u32 buffer_size) { const auto params = reinterpret_cast(buffer); std::memcpy(m_config_data, params->config_data, ORBIS_AT9_CONFIG_DATA_SIZE); AjmAt9Decoder::Reset(); - m_pcm_buffer.resize(m_codec_info.frameSamples * m_codec_info.channels * GetPointCodeSize(), 0); + m_pcm_buffer.resize(m_codec_info.frameSamples * m_codec_info.channels * GetPCMSize(m_format), + 0); } -u8 AjmAt9Decoder::GetPointCodeSize() { - switch (m_format) { - case AjmFormatEncoding::S16: - return sizeof(s16); - case AjmFormatEncoding::S32: - return sizeof(s32); - case AjmFormatEncoding::Float: - return sizeof(float); - default: - UNREACHABLE(); - } -} - -void AjmAt9Decoder::GetInfo(void* out_info) { +void AjmAt9Decoder::GetInfo(void* out_info) const { auto* info = reinterpret_cast(out_info); info->super_frame_size = m_codec_info.superframeSize; info->frames_in_super_frame = m_codec_info.framesInSuperframe; @@ -65,8 +53,7 @@ void AjmAt9Decoder::GetInfo(void* out_info) { } std::tuple AjmAt9Decoder::ProcessData(std::span& in_buf, SparseOutputBuffer& output, - AjmSidebandGaplessDecode& gapless, - std::optional max_samples_per_channel) { + AjmInstanceGapless& gapless) { int ret = 0; int bytes_used = 0; switch (m_format) { @@ -91,32 +78,37 @@ std::tuple AjmAt9Decoder::ProcessData(std::span& in_buf, SparseOut m_superframe_bytes_remain -= bytes_used; - u32 skipped_samples = 0; - if (gapless.skipped_samples < gapless.skip_samples) { - skipped_samples = std::min(u32(m_codec_info.frameSamples), - u32(gapless.skip_samples - gapless.skipped_samples)); - gapless.skipped_samples += skipped_samples; + u32 skip_samples = 0; + if (gapless.current.skip_samples > 0) { + skip_samples = std::min(u16(m_codec_info.frameSamples), gapless.current.skip_samples); + gapless.current.skip_samples -= skip_samples; } - const auto max_samples = max_samples_per_channel.has_value() - ? max_samples_per_channel.value() * m_codec_info.channels - : std::numeric_limits::max(); + const auto max_pcm = gapless.init.total_samples != 0 + ? gapless.current.total_samples * m_codec_info.channels + : std::numeric_limits::max(); - size_t samples_written = 0; + size_t pcm_written = 0; switch (m_format) { case AjmFormatEncoding::S16: - samples_written = WriteOutputSamples(output, skipped_samples, max_samples); + pcm_written = WriteOutputSamples(output, skip_samples, max_pcm); break; case AjmFormatEncoding::S32: - samples_written = WriteOutputSamples(output, skipped_samples, max_samples); + pcm_written = WriteOutputSamples(output, skip_samples, max_pcm); break; case AjmFormatEncoding::Float: - samples_written = WriteOutputSamples(output, skipped_samples, max_samples); + pcm_written = WriteOutputSamples(output, skip_samples, max_pcm); break; default: UNREACHABLE(); } + const auto samples_written = pcm_written / m_codec_info.channels; + gapless.current.skipped_samples += m_codec_info.frameSamples - samples_written; + if (gapless.init.total_samples != 0) { + gapless.current.total_samples -= samples_written; + } + m_num_frames += 1; if ((m_num_frames % m_codec_info.framesInSuperframe) == 0) { if (m_superframe_bytes_remain) { @@ -126,18 +118,28 @@ std::tuple AjmAt9Decoder::ProcessData(std::span& in_buf, SparseOut m_num_frames = 0; } - return {1, samples_written / m_codec_info.channels}; + return {1, samples_written}; } -AjmSidebandFormat AjmAt9Decoder::GetFormat() { +AjmSidebandFormat AjmAt9Decoder::GetFormat() const { return AjmSidebandFormat{ .num_channels = u32(m_codec_info.channels), .channel_mask = GetChannelMask(u32(m_codec_info.channels)), .sampl_freq = u32(m_codec_info.samplingRate), .sample_encoding = m_format, - .bitrate = u32(m_codec_info.samplingRate * GetPointCodeSize() * 8), + .bitrate = u32((m_codec_info.samplingRate * m_codec_info.superframeSize * 8) / + (m_codec_info.framesInSuperframe * m_codec_info.frameSamples)), .reserved = 0, }; } +u32 AjmAt9Decoder::GetNextFrameSize(const AjmInstanceGapless& gapless) const { + const auto max_samples = + gapless.init.total_samples != 0 + ? std::min(gapless.current.total_samples, u32(m_codec_info.frameSamples)) + : m_codec_info.frameSamples; + const auto skip_samples = std::min(u32(gapless.current.skip_samples), max_samples); + return (max_samples - skip_samples) * m_codec_info.channels * GetPCMSize(m_format); +} + } // namespace Libraries::Ajm diff --git a/src/core/libraries/ajm/ajm_at9.h b/src/core/libraries/ajm/ajm_at9.h index e5f65db93..689681dec 100644 --- a/src/core/libraries/ajm/ajm_at9.h +++ b/src/core/libraries/ajm/ajm_at9.h @@ -33,15 +33,13 @@ struct AjmAt9Decoder final : AjmCodec { void Reset() override; void Initialize(const void* buffer, u32 buffer_size) override; - void GetInfo(void* out_info) override; - AjmSidebandFormat GetFormat() override; + void GetInfo(void* out_info) const override; + AjmSidebandFormat GetFormat() const override; + u32 GetNextFrameSize(const AjmInstanceGapless& gapless) const override; std::tuple ProcessData(std::span& input, SparseOutputBuffer& output, - AjmSidebandGaplessDecode& gapless, - std::optional max_samples) override; + AjmInstanceGapless& gapless) override; private: - u8 GetPointCodeSize(); - template size_t WriteOutputSamples(SparseOutputBuffer& output, u32 skipped_samples, u32 max_samples) { std::span pcm_data{reinterpret_cast(m_pcm_buffer.data()), diff --git a/src/core/libraries/ajm/ajm_batch.cpp b/src/core/libraries/ajm/ajm_batch.cpp index 5c76beae8..b1cec88b3 100644 --- a/src/core/libraries/ajm/ajm_batch.cpp +++ b/src/core/libraries/ajm/ajm_batch.cpp @@ -135,7 +135,10 @@ AjmJob AjmJobFromBatchBuffer(u32 instance_id, AjmBatchBuffer batch_buffer) { case Identifier::AjmIdentInputControlBuf: { ASSERT_MSG(!input_control_buffer.has_value(), "Only one instance of input control buffer is allowed per job"); - input_control_buffer = batch_buffer.Consume(); + const auto& buffer = batch_buffer.Consume(); + if (buffer.p_address != nullptr && buffer.size != 0) { + input_control_buffer = buffer; + } break; } case Identifier::AjmIdentControlFlags: @@ -155,19 +158,27 @@ AjmJob AjmJobFromBatchBuffer(u32 instance_id, AjmBatchBuffer batch_buffer) { case Identifier::AjmIdentInlineBuf: { ASSERT_MSG(!output_control_buffer.has_value(), "Only one instance of inline buffer is allowed per job"); - inline_buffer = batch_buffer.Consume(); + const auto& buffer = batch_buffer.Consume(); + if (buffer.p_address != nullptr && buffer.size != 0) { + inline_buffer = buffer; + } break; } case Identifier::AjmIdentOutputRunBuf: { auto& buffer = batch_buffer.Consume(); u8* p_begin = reinterpret_cast(buffer.p_address); - job.output.buffers.emplace_back(std::span(p_begin, p_begin + buffer.size)); + if (p_begin != nullptr && buffer.size != 0) { + job.output.buffers.emplace_back(std::span(p_begin, p_begin + buffer.size)); + } break; } case Identifier::AjmIdentOutputControlBuf: { ASSERT_MSG(!output_control_buffer.has_value(), "Only one instance of output control buffer is allowed per job"); - output_control_buffer = batch_buffer.Consume(); + const auto& buffer = batch_buffer.Consume(); + if (buffer.p_address != nullptr && buffer.size != 0) { + output_control_buffer = buffer; + } break; } default: diff --git a/src/core/libraries/ajm/ajm_instance.cpp b/src/core/libraries/ajm/ajm_instance.cpp index 85a6f54a1..4e04eea74 100644 --- a/src/core/libraries/ajm/ajm_instance.cpp +++ b/src/core/libraries/ajm/ajm_instance.cpp @@ -22,6 +22,19 @@ constexpr int ORBIS_AJM_RESULT_PRIORITY_PASSED = 0x00000200; constexpr int ORBIS_AJM_RESULT_CODEC_ERROR = 0x40000000; constexpr int ORBIS_AJM_RESULT_FATAL = 0x80000000; +u8 GetPCMSize(AjmFormatEncoding format) { + switch (format) { + case AjmFormatEncoding::S16: + return sizeof(s16); + case AjmFormatEncoding::S32: + return sizeof(s32); + case AjmFormatEncoding::Float: + return sizeof(float); + default: + UNREACHABLE(); + } +} + AjmInstance::AjmInstance(AjmCodecType codec_type, AjmInstanceFlags flags) : m_flags(flags) { switch (codec_type) { case AjmCodecType::At9Dec: { @@ -30,7 +43,8 @@ AjmInstance::AjmInstance(AjmCodecType codec_type, AjmInstanceFlags flags) : m_fl break; } case AjmCodecType::Mp3Dec: { - m_codec = std::make_unique(AjmFormatEncoding(flags.format)); + m_codec = std::make_unique(AjmFormatEncoding(flags.format), + AjmMp3CodecFlags(flags.codec)); break; } default: @@ -45,7 +59,6 @@ void AjmInstance::ExecuteJob(AjmJob& job) { m_format = {}; m_gapless = {}; m_resample_parameters = {}; - m_gapless_samples = 0; m_total_samples = 0; m_codec->Reset(); } @@ -64,27 +77,47 @@ void AjmInstance::ExecuteJob(AjmJob& job) { } if (job.input.gapless_decode.has_value()) { auto& params = job.input.gapless_decode.value(); - m_gapless.total_samples = params.total_samples; - m_gapless.skip_samples = params.skip_samples; + if (params.total_samples != 0) { + const auto max = std::max(params.total_samples, m_gapless.init.total_samples); + m_gapless.current.total_samples += max - m_gapless.init.total_samples; + m_gapless.init.total_samples = max; + } + if (params.skip_samples != 0) { + const auto max = std::max(params.skip_samples, m_gapless.init.skip_samples); + m_gapless.current.skip_samples += max - m_gapless.init.skip_samples; + m_gapless.init.skip_samples = max; + } } if (!job.input.buffer.empty() && !job.output.buffers.empty()) { - u32 frames_decoded = 0; std::span in_buf(job.input.buffer); SparseOutputBuffer out_buf(job.output.buffers); + u32 frames_decoded = 0; auto in_size = in_buf.size(); auto out_size = out_buf.Size(); - while (!in_buf.empty() && !out_buf.IsEmpty() && !IsGaplessEnd()) { - const auto samples_remain = - m_gapless.total_samples != 0 - ? std::optional{m_gapless.total_samples - m_gapless_samples} - : std::optional{}; - const auto [nframes, nsamples] = - m_codec->ProcessData(in_buf, out_buf, m_gapless, samples_remain); + while (!in_buf.empty() && !out_buf.IsEmpty() && !m_gapless.IsEnd()) { + if (!HasEnoughSpace(out_buf)) { + if (job.output.p_mframe == nullptr || frames_decoded == 0) { + job.output.p_result->result = ORBIS_AJM_RESULT_NOT_ENOUGH_ROOM; + break; + } + } + + const auto [nframes, nsamples] = m_codec->ProcessData(in_buf, out_buf, m_gapless); frames_decoded += nframes; m_total_samples += nsamples; - m_gapless_samples += nsamples; + + if (False(job.flags.run_flags & AjmJobRunFlags::MultipleFrames)) { + break; + } + } + + if (m_gapless.IsEnd()) { + in_buf = in_buf.subspan(in_buf.size()); + m_gapless.current.total_samples = m_gapless.init.total_samples; + m_gapless.current.skip_samples = m_gapless.init.skip_samples; + m_codec->Reset(); } if (job.output.p_mframe) { job.output.p_mframe->num_frames = frames_decoded; @@ -96,25 +129,19 @@ void AjmInstance::ExecuteJob(AjmJob& job) { } } - if (m_flags.gapless_loop && m_gapless.total_samples != 0 && - m_gapless_samples >= m_gapless.total_samples) { - m_gapless_samples = 0; - m_gapless.skipped_samples = 0; - m_codec->Reset(); - } if (job.output.p_format != nullptr) { *job.output.p_format = m_codec->GetFormat(); } if (job.output.p_gapless_decode != nullptr) { - *job.output.p_gapless_decode = m_gapless; + *job.output.p_gapless_decode = m_gapless.current; } if (job.output.p_codec_info != nullptr) { m_codec->GetInfo(job.output.p_codec_info); } } -bool AjmInstance::IsGaplessEnd() { - return m_gapless.total_samples != 0 && m_gapless_samples >= m_gapless.total_samples; +bool AjmInstance::HasEnoughSpace(const SparseOutputBuffer& output) const { + return output.Size() >= m_codec->GetNextFrameSize(m_gapless); } } // namespace Libraries::Ajm diff --git a/src/core/libraries/ajm/ajm_instance.h b/src/core/libraries/ajm/ajm_instance.h index d1d398ae8..9d0f6b9f3 100644 --- a/src/core/libraries/ajm/ajm_instance.h +++ b/src/core/libraries/ajm/ajm_instance.h @@ -14,6 +14,8 @@ namespace Libraries::Ajm { +u8 GetPCMSize(AjmFormatEncoding format); + class SparseOutputBuffer { public: SparseOutputBuffer(std::span> chunks) @@ -33,14 +35,17 @@ public: ++m_current; } } + if (!pcm.empty()) { + LOG_ERROR(Lib_Ajm, "Could not write {} samples", pcm.size()); + } return samples_written; } - bool IsEmpty() { + bool IsEmpty() const { return m_current == m_chunks.end(); } - size_t Size() { + size_t Size() const { size_t result = 0; for (auto it = m_current; it != m_chunks.end(); ++it) { result += it->size(); @@ -53,17 +58,26 @@ private: std::span>::iterator m_current; }; +struct AjmInstanceGapless { + AjmSidebandGaplessDecode init{}; + AjmSidebandGaplessDecode current{}; + + bool IsEnd() const { + return init.total_samples != 0 && current.total_samples == 0; + } +}; + class AjmCodec { public: virtual ~AjmCodec() = default; virtual void Initialize(const void* buffer, u32 buffer_size) = 0; virtual void Reset() = 0; - virtual void GetInfo(void* out_info) = 0; - virtual AjmSidebandFormat GetFormat() = 0; + virtual void GetInfo(void* out_info) const = 0; + virtual AjmSidebandFormat GetFormat() const = 0; + virtual u32 GetNextFrameSize(const AjmInstanceGapless& gapless) const = 0; virtual std::tuple ProcessData(std::span& input, SparseOutputBuffer& output, - AjmSidebandGaplessDecode& gapless, - std::optional max_samples_per_channel) = 0; + AjmInstanceGapless& gapless) = 0; }; class AjmInstance { @@ -73,16 +87,14 @@ public: void ExecuteJob(AjmJob& job); private: - bool IsGaplessEnd(); + bool HasEnoughSpace(const SparseOutputBuffer& output) const; + std::optional GetNumRemainingSamples() const; AjmInstanceFlags m_flags{}; AjmSidebandFormat m_format{}; - AjmSidebandGaplessDecode m_gapless{}; + AjmInstanceGapless m_gapless{}; AjmSidebandResampleParameters m_resample_parameters{}; - - u32 m_gapless_samples{}; u32 m_total_samples{}; - std::unique_ptr m_codec; }; diff --git a/src/core/libraries/ajm/ajm_mp3.cpp b/src/core/libraries/ajm/ajm_mp3.cpp index 90196bb91..3b464238d 100644 --- a/src/core/libraries/ajm/ajm_mp3.cpp +++ b/src/core/libraries/ajm/ajm_mp3.cpp @@ -15,18 +15,50 @@ extern "C" { namespace Libraries::Ajm { // Following tables have been reversed from AJM library -static constexpr std::array, 3> SamplerateTable = {{ - {0x5622, 0x5DC0, 0x3E80}, - {0xAC44, 0xBB80, 0x7D00}, - {0x2B11, 0x2EE0, 0x1F40}, -}}; +static constexpr std::array, 4> Mp3SampleRateTable = { + std::array{11025, 12000, 8000, 0}, + std::array{0, 0, 0, 0}, + std::array{22050, 24000, 16000, 0}, + std::array{44100, 48000, 32000, 0}, +}; -static constexpr std::array, 2> BitrateTable = {{ - {0, 0x20, 0x28, 0x30, 0x38, 0x40, 0x50, 0x60, 0x70, 0x80, 0xA0, 0xC0, 0xE0, 0x100, 0x140}, - {0, 0x8, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xA0}, -}}; +static constexpr std::array, 4> Mp3BitRateTable = { + std::array{0, 8, 16, 24, 32, 40, 48, 56, 64, 0, 0, 0, 0, 0, 0, 0}, + std::array{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + std::array{0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0}, + std::array{0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0}, +}; -static constexpr std::array UnkTable = {0x48, 0x90}; +enum class Mp3AudioVersion : u32 { + V2_5 = 0, + Reserved = 1, + V2 = 2, + V1 = 3, +}; + +enum class Mp3ChannelMode : u32 { + Stereo = 0, + JointStereo = 1, + DualChannel = 2, + SingleChannel = 3, +}; + +struct Mp3Header { + u32 emphasis : 2; + u32 original : 1; + u32 copyright : 1; + u32 mode_ext_idx : 2; + Mp3ChannelMode channel_mode : 2; + u32 : 1; + u32 padding : 1; + u32 sampling_rate_idx : 2; + u32 bitrate_idx : 4; + u32 protection_type : 1; + u32 layer_type : 2; + Mp3AudioVersion version : 2; + u32 sync : 11; +}; +static_assert(sizeof(Mp3Header) == sizeof(u32)); static AVSampleFormat AjmToAVSampleFormat(AjmFormatEncoding format) { switch (format) { @@ -62,7 +94,7 @@ AVFrame* AjmMp3Decoder::ConvertAudioFrame(AVFrame* frame) { swr_init(m_swr_context); const auto res = swr_convert_frame(m_swr_context, new_frame, frame); if (res < 0) { - LOG_ERROR(Lib_AvPlayer, "Could not convert to S16: {}", av_err2str(res)); + LOG_ERROR(Lib_AvPlayer, "Could not convert frame: {}", av_err2str(res)); av_frame_free(&new_frame); av_frame_free(&frame); return nullptr; @@ -71,48 +103,57 @@ AVFrame* AjmMp3Decoder::ConvertAudioFrame(AVFrame* frame) { return new_frame; } -AjmMp3Decoder::AjmMp3Decoder(AjmFormatEncoding format) - : m_format(format), m_codec(avcodec_find_decoder(AV_CODEC_ID_MP3)), - m_parser(av_parser_init(m_codec->id)) { - AjmMp3Decoder::Reset(); -} - -AjmMp3Decoder::~AjmMp3Decoder() { - swr_free(&m_swr_context); - avcodec_free_context(&m_codec_context); -} - -void AjmMp3Decoder::Reset() { - if (m_codec_context) { - avcodec_free_context(&m_codec_context); - } - m_codec_context = avcodec_alloc_context3(m_codec); - ASSERT_MSG(m_codec_context, "Could not allocate audio m_codec context"); +AjmMp3Decoder::AjmMp3Decoder(AjmFormatEncoding format, AjmMp3CodecFlags flags) + : m_format(format), m_flags(flags), m_codec(avcodec_find_decoder(AV_CODEC_ID_MP3)), + m_codec_context(avcodec_alloc_context3(m_codec)), m_parser(av_parser_init(m_codec->id)) { int ret = avcodec_open2(m_codec_context, m_codec, nullptr); ASSERT_MSG(ret >= 0, "Could not open m_codec"); } -void AjmMp3Decoder::GetInfo(void* out_info) { +AjmMp3Decoder::~AjmMp3Decoder() { + swr_free(&m_swr_context); + av_parser_close(m_parser); + avcodec_free_context(&m_codec_context); +} + +void AjmMp3Decoder::Reset() { + avcodec_flush_buffers(m_codec_context); + m_header.reset(); + m_frame_samples = 0; +} + +void AjmMp3Decoder::GetInfo(void* out_info) const { auto* info = reinterpret_cast(out_info); + if (m_header.has_value()) { + auto* header = reinterpret_cast(&m_header.value()); + info->header = std::byteswap(m_header.value()); + info->has_crc = header->protection_type; + info->channel_mode = static_cast(header->channel_mode); + info->mode_extension = header->mode_ext_idx; + info->copyright = header->copyright; + info->original = header->original; + info->emphasis = header->emphasis; + } } std::tuple AjmMp3Decoder::ProcessData(std::span& in_buf, SparseOutputBuffer& output, - AjmSidebandGaplessDecode& gapless, - std::optional max_samples_per_channel) { + AjmInstanceGapless& gapless) { AVPacket* pkt = av_packet_alloc(); + if ((!m_header.has_value() || m_frame_samples == 0) && in_buf.size() >= 4) { + m_header = std::byteswap(*reinterpret_cast(in_buf.data())); + AjmDecMp3ParseFrame info{}; + ParseMp3Header(in_buf.data(), in_buf.size(), false, &info); + m_frame_samples = info.samples_per_channel; + } + int ret = av_parser_parse2(m_parser, m_codec_context, &pkt->data, &pkt->size, in_buf.data(), in_buf.size(), AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0); ASSERT_MSG(ret >= 0, "Error while parsing {}", ret); in_buf = in_buf.subspan(ret); u32 frames_decoded = 0; - u32 samples_decoded = 0; - - auto max_samples = - max_samples_per_channel.has_value() - ? max_samples_per_channel.value() * m_codec_context->ch_layout.nb_channels - : std::numeric_limits::max(); + u32 samples_written = 0; if (pkt->size) { // Send the packet with the compressed data to the decoder @@ -135,31 +176,38 @@ std::tuple AjmMp3Decoder::ProcessData(std::span& in_buf, SparseOut frame = ConvertAudioFrame(frame); frames_decoded += 1; - u32 skipped_samples = 0; - if (gapless.skipped_samples < gapless.skip_samples) { - skipped_samples = std::min(u32(frame->nb_samples), - u32(gapless.skip_samples - gapless.skipped_samples)); - gapless.skipped_samples += skipped_samples; + u32 skip_samples = 0; + if (gapless.current.skip_samples > 0) { + skip_samples = std::min(u16(frame->nb_samples), gapless.current.skip_samples); + gapless.current.skip_samples -= skip_samples; } + const auto max_pcm = + gapless.init.total_samples != 0 + ? gapless.current.total_samples * m_codec_context->ch_layout.nb_channels + : std::numeric_limits::max(); + + u32 pcm_written = 0; switch (m_format) { case AjmFormatEncoding::S16: - samples_decoded += - WriteOutputSamples(frame, output, skipped_samples, max_samples); + pcm_written = WriteOutputPCM(frame, output, skip_samples, max_pcm); break; case AjmFormatEncoding::S32: - samples_decoded += - WriteOutputSamples(frame, output, skipped_samples, max_samples); + pcm_written = WriteOutputPCM(frame, output, skip_samples, max_pcm); break; case AjmFormatEncoding::Float: - samples_decoded += - WriteOutputSamples(frame, output, skipped_samples, max_samples); + pcm_written = WriteOutputPCM(frame, output, skip_samples, max_pcm); break; default: UNREACHABLE(); } - max_samples -= samples_decoded; + const auto samples = pcm_written / m_codec_context->ch_layout.nb_channels; + samples_written += samples; + gapless.current.skipped_samples += frame->nb_samples - samples; + if (gapless.init.total_samples != 0) { + gapless.current.total_samples -= samples; + } av_frame_free(&frame); } @@ -167,38 +215,221 @@ std::tuple AjmMp3Decoder::ProcessData(std::span& in_buf, SparseOut av_packet_free(&pkt); - return {frames_decoded, samples_decoded}; + return {frames_decoded, samples_written}; } -int AjmMp3Decoder::ParseMp3Header(const u8* buf, u32 stream_size, int parse_ofl, +u32 AjmMp3Decoder::GetNextFrameSize(const AjmInstanceGapless& gapless) const { + const auto max_samples = gapless.init.total_samples != 0 + ? std::min(gapless.current.total_samples, m_frame_samples) + : m_frame_samples; + const auto skip_samples = std::min(u32(gapless.current.skip_samples), max_samples); + return (max_samples - skip_samples) * m_codec_context->ch_layout.nb_channels * + GetPCMSize(m_format); +} + +class BitReader { +public: + BitReader(const u8* data) : m_data(data) {} + + template + T Read(u32 const nbits) { + T accumulator = 0; + for (unsigned i = 0; i < nbits; ++i) { + accumulator = (accumulator << 1) + GetBit(); + } + return accumulator; + } + + void Skip(size_t nbits) { + m_bit_offset += nbits; + } + + size_t GetCurrentOffset() { + return m_bit_offset; + } + +private: + u8 GetBit() { + const auto bit = (m_data[m_bit_offset / 8] >> (7 - (m_bit_offset % 8))) & 1; + m_bit_offset += 1; + return bit; + } + + const u8* m_data; + size_t m_bit_offset = 0; +}; + +int AjmMp3Decoder::ParseMp3Header(const u8* p_begin, u32 stream_size, int parse_ofl, AjmDecMp3ParseFrame* frame) { LOG_INFO(Lib_Ajm, "called stream_size = {} parse_ofl = {}", stream_size, parse_ofl); - if (buf == nullptr || stream_size < 4 || frame == nullptr) { - return ORBIS_AJM_ERROR_INVALID_PARAMETER; - } - if ((buf[0] & SYNCWORDH) != SYNCWORDH || (buf[1] & SYNCWORDL) != SYNCWORDL) { + + if (p_begin == nullptr || stream_size < 4 || frame == nullptr) { return ORBIS_AJM_ERROR_INVALID_PARAMETER; } - const u32 unk_idx = buf[1] >> 3 & 1; - const s32 version_idx = (buf[1] >> 3 & 3) ^ 2; - const s32 sr_idx = buf[2] >> 2 & 3; - const s32 br_idx = (buf[2] >> 4) & 0xf; - const s32 padding_bit = (buf[2] >> 1) & 0x1; + const auto* p_current = p_begin; + + auto bytes = std::byteswap(*reinterpret_cast(p_current)); + p_current += 4; + auto header = reinterpret_cast(&bytes); + if (header->sync != 0x7FF) { + return ORBIS_AJM_ERROR_INVALID_PARAMETER; + } + + frame->sample_rate = Mp3SampleRateTable[u32(header->version)][header->sampling_rate_idx]; + frame->bitrate = Mp3BitRateTable[u32(header->version)][header->bitrate_idx] * 1000; + frame->num_channels = header->channel_mode == Mp3ChannelMode::SingleChannel ? 1 : 2; + if (header->version == Mp3AudioVersion::V1) { + frame->frame_size = (144 * frame->bitrate) / frame->sample_rate + header->padding; + frame->samples_per_channel = 1152; + } else { + frame->frame_size = (72 * frame->bitrate) / frame->sample_rate + header->padding; + frame->samples_per_channel = 576; + } - frame->sample_rate = SamplerateTable[version_idx][sr_idx]; - frame->bitrate = BitrateTable[version_idx != 1][br_idx] * 1000; - frame->num_channels = (buf[3] < 0xc0) + 1; - frame->frame_size = (UnkTable[unk_idx] * frame->bitrate) / frame->sample_rate + padding_bit; - frame->samples_per_channel = UnkTable[unk_idx] * 8; frame->encoder_delay = 0; + frame->num_frames = 0; + frame->total_samples = 0; + frame->ofl_type = AjmDecMp3OflType::None; + + if (!parse_ofl) { + return ORBIS_OK; + } + + BitReader reader(p_current); + if (header->protection_type == 0) { + reader.Skip(16); // crc = reader.Read(16); + } + + if (header->version == Mp3AudioVersion::V1) { + // main_data_begin = reader.Read(9); + // if (header->channel_mode == Mp3ChannelMode::SingleChannel) { + // private_bits = reader.Read(5); + // } else { + // private_bits = reader.Read(3); + // } + // for (u32 ch = 0; ch < frame->num_channels; ++ch) { + // for (u8 band = 0; band < 4; ++band) { + // scfsi[ch][band] = reader.Read(1); + // } + // } + if (header->channel_mode == Mp3ChannelMode::SingleChannel) { + reader.Skip(18); + } else { + reader.Skip(20); + } + } else { + // main_data_begin = reader.Read(8); + // if (header->channel_mode == Mp3ChannelMode::SingleChannel) { + // private_bits = reader.Read(1); + // } else { + // private_bits = reader.Read(2); + // } + if (header->channel_mode == Mp3ChannelMode::SingleChannel) { + reader.Skip(9); + } else { + reader.Skip(10); + } + } + + u32 part2_3_length = 0; + // Number of granules (18x32 sub-band samples) + const u8 ngr = header->version == Mp3AudioVersion::V1 ? 2 : 1; + for (u8 gr = 0; gr < ngr; ++gr) { + for (u32 ch = 0; ch < frame->num_channels; ++ch) { + // part2_3_length[gr][ch] = reader.Read(12); + part2_3_length += reader.Read(12); + // big_values[gr][ch] = reader.Read(9); + // global_main[gr][ch] = reader.Read(8); + // if (header->version == Mp3AudioVersion::V1) { + // scalefac_compress[gr][ch] = reader.Read(4); + // } else { + // scalefac_compress[gr][ch] = reader.Read(9); + // } + // window_switching_flag = reader.Read(1); + // if (window_switching_flag) { + // block_type[gr][ch] = reader.Read(2); + // mixed_block_flag[gr][ch] = reader.Read(1); + // for (u8 region = 0; region < 2; ++region) { + // table_select[gr][ch][region] = reader.Read(5); + // } + // for (u8 window = 0; window < 3; ++window) { + // subblock_gain[gr][ch][window] = reader.Read(3); + // } + // } else { + // for (u8 region = 0; region < 3; ++region) { + // table_select[gr][ch][region] = reader.Read(5); + // } + // region0_count[gr][ch] = reader.Read(4); + // region1_count[gr][ch] = reader.Read(3); + // } + // if (header->version == Mp3AudioVersion::V1) { + // preflag[gr][ch] = reader.Read(1); + // } + // scalefac_scale[gr][ch] = reader.Read(1); + // count1table_select[gr][ch] = reader.Read(1); + if (header->version == Mp3AudioVersion::V1) { + reader.Skip(47); + } else { + reader.Skip(51); + } + } + } + reader.Skip(part2_3_length); + + p_current += ((reader.GetCurrentOffset() + 7) / 8); + + const auto* p_end = p_begin + frame->frame_size; + if (memcmp(p_current, "Xing", 4) == 0 || memcmp(p_current, "Info", 4) == 0) { + // TODO: Parse Xing/Lame header + LOG_ERROR(Lib_Ajm, "Xing/Lame header is not implemented."); + } else if (memcmp(p_current, "VBRI", 4) == 0) { + // TODO: Parse VBRI header + LOG_ERROR(Lib_Ajm, "VBRI header is not implemented."); + } else { + // Parse FGH header + constexpr auto fgh_indicator = 0xB4; + while ((p_current + 9) < p_end && *p_current != fgh_indicator) { + ++p_current; + } + auto p_fgh = p_current; + if ((p_current + 9) < p_end && *p_current == fgh_indicator) { + u8 crc = 0xFF; + auto crc_func = [](u8 c, u8 v, u8 s) { + if (((c >> 7) & 1) != ((v >> s) & 1)) { + return c * 2; + } + return (c * 2) ^ 0x45; + }; + for (u8 i = 0; i < 9; ++i, ++p_current) { + for (u8 j = 0; j < 8; ++j) { + crc = crc_func(crc, *p_current, 7 - j); + } + } + if (p_fgh[9] == crc) { + frame->encoder_delay = std::byteswap(*reinterpret_cast(p_fgh + 1)); + frame->total_samples = std::byteswap(*reinterpret_cast(p_fgh + 3)); + frame->ofl_type = AjmDecMp3OflType::Fgh; + } else { + LOG_ERROR(Lib_Ajm, "FGH header CRC is incorrect."); + } + } else { + LOG_ERROR(Lib_Ajm, "Could not find vendor header."); + } + } return ORBIS_OK; } -AjmSidebandFormat AjmMp3Decoder::GetFormat() { - LOG_ERROR(Lib_Ajm, "Unimplemented"); - return AjmSidebandFormat{}; +AjmSidebandFormat AjmMp3Decoder::GetFormat() const { + return AjmSidebandFormat{ + .num_channels = u32(m_codec_context->ch_layout.nb_channels), + .channel_mask = GetChannelMask(u32(m_codec_context->ch_layout.nb_channels)), + .sampl_freq = u32(m_codec_context->sample_rate), + .sample_encoding = m_format, + .bitrate = u32(m_codec_context->bit_rate), + .reserved = 0, + }; }; } // namespace Libraries::Ajm diff --git a/src/core/libraries/ajm/ajm_mp3.h b/src/core/libraries/ajm/ajm_mp3.h index b5da19e77..70c949550 100644 --- a/src/core/libraries/ajm/ajm_mp3.h +++ b/src/core/libraries/ajm/ajm_mp3.h @@ -13,7 +13,19 @@ struct SwrContext; namespace Libraries::Ajm { -enum class AjmDecMp3OflType : u32 { None = 0, Lame = 1, Vbri = 2, Fgh = 3, VbriAndFgh = 4 }; +enum class AjmDecMp3OflType : u32 { + None = 0, + Lame = 1, + Vbri = 2, + Fgh = 3, + VbriAndFgh = 4, +}; + +enum AjmMp3CodecFlags : u32 { + IgnoreOfl = 1 << 0, + VlcRewind = 1 << 8, +}; +DECLARE_ENUM_FLAG_OPERATORS(AjmMp3CodecFlags) // 11-bit syncword if MPEG 2.5 extensions are enabled static constexpr u8 SYNCWORDH = 0xff; @@ -51,39 +63,40 @@ struct AjmSidebandDecMp3CodecInfo { class AjmMp3Decoder : public AjmCodec { public: - explicit AjmMp3Decoder(AjmFormatEncoding format); + explicit AjmMp3Decoder(AjmFormatEncoding format, AjmMp3CodecFlags flags); ~AjmMp3Decoder() override; void Reset() override; void Initialize(const void* buffer, u32 buffer_size) override {} - void GetInfo(void* out_info) override; - AjmSidebandFormat GetFormat() override; + void GetInfo(void* out_info) const override; + AjmSidebandFormat GetFormat() const override; + u32 GetNextFrameSize(const AjmInstanceGapless& gapless) const override; std::tuple ProcessData(std::span& input, SparseOutputBuffer& output, - AjmSidebandGaplessDecode& gapless, - std::optional max_samples_per_channel) override; + AjmInstanceGapless& gapless) override; static int ParseMp3Header(const u8* buf, u32 stream_size, int parse_ofl, AjmDecMp3ParseFrame* frame); private: template - size_t WriteOutputSamples(AVFrame* frame, SparseOutputBuffer& output, u32 skipped_samples, - u32 max_samples) { - const auto size = frame->ch_layout.nb_channels * frame->nb_samples * sizeof(T); - std::span pcm_data(reinterpret_cast(frame->data[0]), size >> 1); + size_t WriteOutputPCM(AVFrame* frame, SparseOutputBuffer& output, u32 skipped_samples, + u32 max_pcm) { + std::span pcm_data(reinterpret_cast(frame->data[0]), + frame->nb_samples * frame->ch_layout.nb_channels); pcm_data = pcm_data.subspan(skipped_samples * frame->ch_layout.nb_channels); - const auto pcm_size = std::min(u32(pcm_data.size()), max_samples); - const auto samples_written = output.Write(pcm_data.subspan(0, pcm_size)); - return samples_written / frame->ch_layout.nb_channels; + return output.Write(pcm_data.subspan(0, std::min(u32(pcm_data.size()), max_pcm))); } AVFrame* ConvertAudioFrame(AVFrame* frame); const AjmFormatEncoding m_format; + const AjmMp3CodecFlags m_flags; const AVCodec* m_codec = nullptr; AVCodecContext* m_codec_context = nullptr; AVCodecParserContext* m_parser = nullptr; SwrContext* m_swr_context = nullptr; + std::optional m_header; + u32 m_frame_samples = 0; }; } // namespace Libraries::Ajm From 7be35c399751c65e9d824c7b47cff802d60785cb Mon Sep 17 00:00:00 2001 From: Osyotr Date: Thu, 14 Nov 2024 13:01:33 +0300 Subject: [PATCH 021/549] Save config in UTF-8 (#1524) --- src/common/config.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index 1dde7223c..e97a46005 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -658,7 +658,7 @@ void save(const std::filesystem::path& path) { // TODO Migration code, after a major release this should be removed. data.at("GUI").as_table().erase("installDir"); - std::ofstream file(path, std::ios::out); + std::ofstream file(path, std::ios::binary); file << data; file.close(); } From ce158290fbd7871a3f890323501e6fe3e8bec832 Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Thu, 14 Nov 2024 10:00:57 -0600 Subject: [PATCH 022/549] Minor Kernel Fixes (#1529) * Skip destruction of adaptive mutex initializers Based around similar behaviors implemented in the more-kernel branch. Hatsune Miku Project Diva X needs this. * Fix posix_lseek result overflow Seen when testing Spider-Man Miles Morales on more-kernel. * Add posix_fsync Used by Spider-Man Miles Morales. I've added the normal posix error handling to this function, though I'm aware sceKernelFsync doesn't return any errors currently. This is for future proofing and accuracy, as the actual libkernel does the usual error handling too. * Properly handle VirtualQuery calls on PoolReserved memory. * Export posix_getpagesize under libkernel Bloons TD 5 needs this. * Clang --- src/core/libraries/kernel/file_system.cpp | 14 +++++++++++++- src/core/libraries/kernel/libkernel.cpp | 1 + src/core/libraries/kernel/thread_management.cpp | 4 ++++ src/core/memory.cpp | 3 ++- 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index 7f86ee540..e6b657c3a 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -237,7 +237,7 @@ s64 PS4_SYSV_ABI sceKernelLseek(int d, s64 offset, int whence) { } s64 PS4_SYSV_ABI posix_lseek(int d, s64 offset, int whence) { - int result = sceKernelLseek(d, offset, whence); + s64 result = sceKernelLseek(d, offset, whence); if (result < 0) { LOG_ERROR(Kernel_Pthread, "posix_lseek: error = {}", result); ErrSceToPosix(result); @@ -490,6 +490,16 @@ s32 PS4_SYSV_ABI sceKernelFsync(int fd) { return ORBIS_OK; } +s32 PS4_SYSV_ABI posix_fsync(int fd) { + s32 result = sceKernelFsync(fd); + if (result < 0) { + LOG_ERROR(Kernel_Pthread, "posix_fstat: error = {}", result); + ErrSceToPosix(result); + return -1; + } + return result; +} + int PS4_SYSV_ABI sceKernelFtruncate(int fd, s64 length) { auto* h = Common::Singleton::Instance(); auto* file = h->GetFile(fd); @@ -642,6 +652,8 @@ void fileSystemSymbolsRegister(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("+r3rMFwItV4", "libkernel", 1, "libkernel", 1, 1, sceKernelPread); LIB_FUNCTION("uWyW3v98sU4", "libkernel", 1, "libkernel", 1, 1, sceKernelCheckReachability); LIB_FUNCTION("fTx66l5iWIA", "libkernel", 1, "libkernel", 1, 1, sceKernelFsync); + LIB_FUNCTION("juWbTNM+8hw", "libkernel", 1, "libkernel", 1, 1, posix_fsync); + LIB_FUNCTION("juWbTNM+8hw", "libScePosix", 1, "libkernel", 1, 1, posix_fsync); LIB_FUNCTION("j2AIqSqJP0w", "libkernel", 1, "libkernel", 1, 1, sceKernelGetdents); LIB_FUNCTION("taRWhTJFTgE", "libkernel", 1, "libkernel", 1, 1, sceKernelGetdirentries); LIB_FUNCTION("nKWi-N2HBV4", "libkernel", 1, "libkernel", 1, 1, sceKernelPwrite); diff --git a/src/core/libraries/kernel/libkernel.cpp b/src/core/libraries/kernel/libkernel.cpp index c621b4bca..b3eb81ec8 100644 --- a/src/core/libraries/kernel/libkernel.cpp +++ b/src/core/libraries/kernel/libkernel.cpp @@ -491,6 +491,7 @@ void LibKernel_Register(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("-o5uEDpN+oY", "libkernel", 1, "libkernel", 1, 1, sceKernelConvertUtcToLocaltime); LIB_FUNCTION("WB66evu8bsU", "libkernel", 1, "libkernel", 1, 1, sceKernelGetCompiledSdkVersion); LIB_FUNCTION("DRuBt2pvICk", "libkernel", 1, "libkernel", 1, 1, ps4__read); + LIB_FUNCTION("k+AXqu2-eBc", "libkernel", 1, "libkernel", 1, 1, posix_getpagesize); LIB_FUNCTION("k+AXqu2-eBc", "libScePosix", 1, "libkernel", 1, 1, posix_getpagesize); Libraries::Kernel::fileSystemSymbolsRegister(sym); diff --git a/src/core/libraries/kernel/thread_management.cpp b/src/core/libraries/kernel/thread_management.cpp index 39c0eaf80..85f618090 100644 --- a/src/core/libraries/kernel/thread_management.cpp +++ b/src/core/libraries/kernel/thread_management.cpp @@ -477,6 +477,10 @@ int PS4_SYSV_ABI scePthreadMutexDestroy(ScePthreadMutex* mutex) { return SCE_KERNEL_ERROR_EINVAL; } + if (*mutex == ORBIS_PTHREAD_MUTEX_ADAPTIVE_INITIALIZER) { + return ORBIS_OK; + } + int result = pthread_mutex_destroy(&(*mutex)->pth_mutex); LOG_DEBUG(Kernel_Pthread, "name={}, result={}", (*mutex)->name, result); diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 031b7b135..61eb421e5 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -514,7 +514,8 @@ int MemoryManager::VirtualQuery(VAddr addr, int flags, info->is_direct.Assign(vma.type == VMAType::Direct); info->is_stack.Assign(vma.type == VMAType::Stack); info->is_pooled.Assign(vma.type == VMAType::Pooled); - info->is_committed.Assign(vma.type != VMAType::Free && vma.type != VMAType::Reserved); + info->is_committed.Assign(vma.type != VMAType::Free && vma.type != VMAType::Reserved && + vma.type != VMAType::PoolReserved); vma.name.copy(info->name.data(), std::min(info->name.size(), vma.name.size())); if (vma.type == VMAType::Direct) { const auto dmem_it = FindDmemArea(vma.phys_base); From e1fecda74fe4fa5263157f6364835c1d0c3396fc Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Sat, 16 Nov 2024 20:17:43 +0300 Subject: [PATCH 023/549] Fix depth bias (#1538) --- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 212b8165f..6d214a18e 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -393,14 +393,12 @@ void Rasterizer::UpdateDynamicState(const GraphicsPipeline& pipeline) { if (regs.depth_control.depth_bounds_enable) { cmdbuf.setDepthBounds(regs.depth_bounds_min, regs.depth_bounds_max); } - if (regs.polygon_control.NeedsBias()) { - if (regs.polygon_control.enable_polygon_offset_front) { - cmdbuf.setDepthBias(regs.poly_offset.front_offset, regs.poly_offset.depth_bias, - regs.poly_offset.front_scale); - } else { - cmdbuf.setDepthBias(regs.poly_offset.back_offset, regs.poly_offset.depth_bias, - regs.poly_offset.back_scale); - } + if (regs.polygon_control.enable_polygon_offset_front) { + cmdbuf.setDepthBias(regs.poly_offset.front_offset, regs.poly_offset.depth_bias, + regs.poly_offset.front_scale / 16.f); + } else if (regs.polygon_control.enable_polygon_offset_back) { + cmdbuf.setDepthBias(regs.poly_offset.back_offset, regs.poly_offset.depth_bias, + regs.poly_offset.back_scale / 16.f); } if (regs.depth_control.stencil_enable) { const auto front = regs.stencil_ref_front; From 8fbd9187f8d87ef8683c1755a05e93d2e99dd554 Mon Sep 17 00:00:00 2001 From: psucien <168137814+psucien@users.noreply.github.com> Date: Mon, 18 Nov 2024 10:23:21 +0100 Subject: [PATCH 024/549] libraries: gnmdriver: few more functions implemented (#1544) --- src/core/libraries/gnmdriver/gnmdriver.cpp | 49 ++++++++++++++++--- src/core/libraries/gnmdriver/gnmdriver.h | 5 +- src/video_core/amdgpu/liverpool.cpp | 24 ++++++++- src/video_core/amdgpu/liverpool.h | 2 + src/video_core/amdgpu/pm4_cmds.h | 16 +++++- src/video_core/amdgpu/pm4_opcodes.h | 1 + .../renderer_vulkan/vk_instance.cpp | 1 + .../renderer_vulkan/vk_rasterizer.cpp | 35 +++++++++++-- .../renderer_vulkan/vk_rasterizer.h | 3 +- 9 files changed, 119 insertions(+), 17 deletions(-) diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index 08f534c72..ee37bc57d 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -308,6 +308,7 @@ struct AscQueueInfo { }; static Common::SlotVector asc_queues{}; static constexpr VAddr tessellation_factors_ring_addr = Core::SYSTEM_RESERVED_MAX - 0xFFFFFFF; +static constexpr u32 tessellation_offchip_buffer_size = 0x800000u; static void ResetSubmissionLock(Platform::InterruptId irq) { std::unique_lock lock{m_submission}; @@ -672,18 +673,50 @@ s32 PS4_SYSV_ABI sceGnmDrawIndexIndirect(u32* cmdbuf, u32 size, u32 data_offset, return -1; } -int PS4_SYSV_ABI sceGnmDrawIndexIndirectCountMulti() { - LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceGnmDrawIndexIndirectCountMulti(u32* cmdbuf, u32 size, u32 data_offset, + u32 max_count, u64 count_addr, u32 shader_stage, + u32 vertex_sgpr_offset, u32 instance_sgpr_offset, + u32 flags) { + LOG_TRACE(Lib_GnmDriver, "called"); + + if (cmdbuf && (size == 16) && (shader_stage < ShaderStages::Max) && + (vertex_sgpr_offset < 0x10u) && (instance_sgpr_offset < 0x10u)) { + + cmdbuf = WriteHeader(cmdbuf, 2); + cmdbuf = WriteBody(cmdbuf, 0u); + cmdbuf += 1; + + const auto predicate = flags & 1 ? PM4Predicate::PredEnable : PM4Predicate::PredDisable; + cmdbuf = WriteHeader( + cmdbuf, 9, PM4ShaderType::ShaderGraphics, predicate); + + const auto sgpr_offset = indirect_sgpr_offsets[shader_stage]; + + cmdbuf[0] = data_offset; + cmdbuf[1] = vertex_sgpr_offset == 0 ? 0 : (vertex_sgpr_offset & 0xffffu) + sgpr_offset; + cmdbuf[2] = instance_sgpr_offset == 0 ? 0 : (instance_sgpr_offset & 0xffffu) + sgpr_offset; + cmdbuf[3] = (count_addr != 0 ? 1u : 0u) << 0x1e; + cmdbuf[4] = max_count; + *(u64*)(&cmdbuf[5]) = count_addr; + cmdbuf[7] = AmdGpu::Liverpool::DrawIndexedIndirectArgsSize; + cmdbuf[8] = 0; + + cmdbuf += 9; + WriteTrailingNop<2>(cmdbuf); + return ORBIS_OK; + } + return -1; } int PS4_SYSV_ABI sceGnmDrawIndexIndirectMulti() { LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); + UNREACHABLE(); return ORBIS_OK; } int PS4_SYSV_ABI sceGnmDrawIndexMultiInstanced() { LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); + UNREACHABLE(); return ORBIS_OK; } @@ -730,11 +763,13 @@ s32 PS4_SYSV_ABI sceGnmDrawIndirect(u32* cmdbuf, u32 size, u32 data_offset, u32 int PS4_SYSV_ABI sceGnmDrawIndirectCountMulti() { LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); + UNREACHABLE(); return ORBIS_OK; } int PS4_SYSV_ABI sceGnmDrawIndirectMulti() { LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); + UNREACHABLE(); return ORBIS_OK; } @@ -992,8 +1027,8 @@ int PS4_SYSV_ABI sceGnmGetNumTcaUnits() { } int PS4_SYSV_ABI sceGnmGetOffChipTessellationBufferSize() { - LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); - return ORBIS_OK; + LOG_TRACE(Lib_GnmDriver, "called"); + return tessellation_offchip_buffer_size; } int PS4_SYSV_ABI sceGnmGetOwnerName() { @@ -2438,8 +2473,8 @@ int PS4_SYSV_ABI sceGnmValidateGetVersion() { } int PS4_SYSV_ABI sceGnmValidateOnSubmitEnabled() { - LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); - return ORBIS_OK; + LOG_TRACE(Lib_GnmDriver, "called"); + return 0; } int PS4_SYSV_ABI sceGnmValidateResetState() { diff --git a/src/core/libraries/gnmdriver/gnmdriver.h b/src/core/libraries/gnmdriver/gnmdriver.h index a95daa90d..115268ea8 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.h +++ b/src/core/libraries/gnmdriver/gnmdriver.h @@ -47,7 +47,10 @@ s32 PS4_SYSV_ABI sceGnmDrawIndexAuto(u32* cmdbuf, u32 size, u32 index_count, u32 s32 PS4_SYSV_ABI sceGnmDrawIndexIndirect(u32* cmdbuf, u32 size, u32 data_offset, u32 shader_stage, u32 vertex_sgpr_offset, u32 instance_sgpr_offset, u32 flags); -int PS4_SYSV_ABI sceGnmDrawIndexIndirectCountMulti(); +s32 PS4_SYSV_ABI sceGnmDrawIndexIndirectCountMulti(u32* cmdbuf, u32 size, u32 data_offset, + u32 max_count, u64 count_addr, u32 shader_stage, + u32 vertex_sgpr_offset, u32 instance_sgpr_offset, + u32 flags); int PS4_SYSV_ABI sceGnmDrawIndexIndirectMulti(); int PS4_SYSV_ABI sceGnmDrawIndexMultiInstanced(); s32 PS4_SYSV_ABI sceGnmDrawIndexOffset(u32* cmdbuf, u32 size, u32 index_offset, u32 index_count, diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index 53aab630e..3c359b8df 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -417,7 +417,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:DrawIndirect", cmd_address)); - rasterizer->DrawIndirect(false, ib_address, offset, size); + rasterizer->DrawIndirect(false, ib_address, offset, size, 1, 0); rasterizer->ScopeMarkerEnd(); } break; @@ -435,7 +435,27 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); rasterizer->ScopeMarkerBegin( fmt::format("dcb:{}:DrawIndexIndirect", cmd_address)); - rasterizer->DrawIndirect(true, ib_address, offset, size); + rasterizer->DrawIndirect(true, ib_address, offset, size, 1, 0); + rasterizer->ScopeMarkerEnd(); + } + break; + } + case PM4ItOpcode::DrawIndexIndirectCountMulti: { + const auto* draw_index_indirect = + reinterpret_cast(header); + const auto offset = draw_index_indirect->data_offset; + const auto ib_address = mapped_queues[GfxQueueId].indirect_args_addr; + const auto size = sizeof(PM4CmdDrawIndexIndirect::DrawIndexInstancedArgs); + if (DebugState.DumpingCurrentReg()) { + DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); + } + if (rasterizer) { + const auto cmd_address = reinterpret_cast(header); + rasterizer->ScopeMarkerBegin( + fmt::format("dcb:{}:DrawIndexIndirectCountMulti", cmd_address)); + rasterizer->DrawIndirect(true, ib_address, offset, size, + draw_index_indirect->count, + draw_index_indirect->countAddr); rasterizer->ScopeMarkerEnd(); } break; diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index a4cf79334..d94e4329a 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -57,6 +57,8 @@ struct Liverpool { static constexpr u32 ConfigRegWordOffset = 0x2000; static constexpr u32 ShRegWordOffset = 0x2C00; static constexpr u32 NumRegs = 0xD000; + static constexpr u32 DrawIndirectArgsSize = 0x10u; + static constexpr u32 DrawIndexedIndirectArgsSize = 0x14u; using UserData = std::array; diff --git a/src/video_core/amdgpu/pm4_cmds.h b/src/video_core/amdgpu/pm4_cmds.h index a956b030d..d6cab23d2 100644 --- a/src/video_core/amdgpu/pm4_cmds.h +++ b/src/video_core/amdgpu/pm4_cmds.h @@ -817,11 +817,25 @@ struct PM4CmdDrawIndexIndirect { BitField<0, 16, u32> base_vtx_loc; ///< Offset where the CP will write the ///< BaseVertexLocation it fetched from memory }; - union { // NOTE: this one is undocumented in AMD spec, but Gnm driver writes this field + union { u32 dw3; BitField<0, 16, u32> start_inst_loc; ///< Offset where the CP will write the ///< StartInstanceLocation it fetched from memory }; + + union { + u32 dw4; + struct { + BitField<0, 16, u32> drawIndexLoc; ///< register offset to write the Draw Index count + BitField<30, 1, u32> + countIndirectEnable; ///< Indicates the data structure count is in memory + BitField<31, 1, u32> + drawIndexEnable; ///< Enables writing of Draw Index count to DRAW_INDEX_LOC + }; + }; + u32 count; ///< Count of data structures to loop through before going to next packet + u64 countAddr; ///< DWord aligned Address[31:2]; Valid if countIndirectEnable is set + u32 stride; ///< Stride in memory from one data structure to the next u32 draw_initiator; ///< Draw Initiator Register }; diff --git a/src/video_core/amdgpu/pm4_opcodes.h b/src/video_core/amdgpu/pm4_opcodes.h index 4b853138b..ce388d1ba 100644 --- a/src/video_core/amdgpu/pm4_opcodes.h +++ b/src/video_core/amdgpu/pm4_opcodes.h @@ -71,6 +71,7 @@ enum class PM4ItOpcode : u32 { IncrementDeCounter = 0x85, WaitOnCeCounter = 0x86, WaitOnDeCounterDiff = 0x88, + DrawIndexIndirectCountMulti = 0x9d, }; } // namespace AmdGpu diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 0edc4228a..a16f35ed7 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -345,6 +345,7 @@ bool Instance::CreateDevice() { }, vk::PhysicalDeviceVulkan12Features{ .samplerMirrorClampToEdge = vk12_features.samplerMirrorClampToEdge, + .drawIndirectCount = vk12_features.drawIndirectCount, .shaderFloat16 = vk12_features.shaderFloat16, .scalarBlockLayout = vk12_features.scalarBlockLayout, .uniformBufferStandardLayout = vk12_features.uniformBufferStandardLayout, diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 6d214a18e..271203233 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -115,14 +115,14 @@ void Rasterizer::Draw(bool is_indexed, u32 index_offset) { } } -void Rasterizer::DrawIndirect(bool is_indexed, VAddr address, u32 offset, u32 size) { +void Rasterizer::DrawIndirect(bool is_indexed, VAddr arg_address, u32 offset, u32 size, + u32 max_count, VAddr count_address) { RENDERER_TRACE; if (!FilterDraw()) { return; } - const auto cmdbuf = scheduler.CommandBuffer(); const auto& regs = liverpool->regs; const GraphicsPipeline* pipeline = pipeline_cache.GetGraphicsPipeline(); if (!pipeline) { @@ -142,7 +142,13 @@ void Rasterizer::DrawIndirect(bool is_indexed, VAddr address, u32 offset, u32 si buffer_cache.BindVertexBuffers(vs_info); buffer_cache.BindIndexBuffer(is_indexed, 0); - const auto [buffer, base] = buffer_cache.ObtainBuffer(address + offset, size, false); + const auto [buffer, base] = buffer_cache.ObtainBuffer(arg_address + offset, size, false); + + VideoCore::Buffer* count_buffer{}; + u32 count_base{}; + if (count_address != 0) { + std::tie(count_buffer, count_base) = buffer_cache.ObtainBuffer(count_address, 4, false); + } BeginRendering(*pipeline); UpdateDynamicState(*pipeline); @@ -150,10 +156,29 @@ void Rasterizer::DrawIndirect(bool is_indexed, VAddr address, u32 offset, u32 si // We can safely ignore both SGPR UD indices and results of fetch shader parsing, as vertex and // instance offsets will be automatically applied by Vulkan from indirect args buffer. + const auto cmdbuf = scheduler.CommandBuffer(); if (is_indexed) { - cmdbuf.drawIndexedIndirect(buffer->Handle(), base, 1, 0); + static_assert(sizeof(VkDrawIndexedIndirectCommand) == + AmdGpu::Liverpool::DrawIndexedIndirectArgsSize); + + if (count_address != 0) { + cmdbuf.drawIndexedIndirectCount(buffer->Handle(), base, count_buffer->Handle(), + count_base, max_count, + AmdGpu::Liverpool::DrawIndexedIndirectArgsSize); + } else { + cmdbuf.drawIndexedIndirect(buffer->Handle(), base, max_count, + AmdGpu::Liverpool::DrawIndexedIndirectArgsSize); + } } else { - cmdbuf.drawIndirect(buffer->Handle(), base, 1, 0); + static_assert(sizeof(VkDrawIndirectCommand) == AmdGpu::Liverpool::DrawIndirectArgsSize); + + if (count_address != 0) { + cmdbuf.drawIndirectCount(buffer->Handle(), base, count_buffer->Handle(), count_base, + max_count, AmdGpu::Liverpool::DrawIndirectArgsSize); + } else { + cmdbuf.drawIndirect(buffer->Handle(), base, max_count, + AmdGpu::Liverpool::DrawIndirectArgsSize); + } } } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 9035ed9dc..b6813aec9 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -32,7 +32,8 @@ public: } void Draw(bool is_indexed, u32 index_offset = 0); - void DrawIndirect(bool is_indexed, VAddr address, u32 offset, u32 size); + void DrawIndirect(bool is_indexed, VAddr arg_address, u32 offset, u32 size, u32 max_count, + VAddr count_address); void DispatchDirect(); void DispatchIndirect(VAddr address, u32 offset, u32 size); From c45af9a2ca0e6d52bc21eed2c62236ef98adb3d1 Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Tue, 19 Nov 2024 19:55:05 +0300 Subject: [PATCH 025/549] Fix border color (#1548) --- src/video_core/amdgpu/resource.h | 4 ++-- src/video_core/renderer_vulkan/liverpool_to_vk.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/video_core/amdgpu/resource.h b/src/video_core/amdgpu/resource.h index 81fe43f4e..f43fc9800 100644 --- a/src/video_core/amdgpu/resource.h +++ b/src/video_core/amdgpu/resource.h @@ -358,8 +358,8 @@ enum class MipFilter : u64 { }; enum class BorderColor : u64 { - OpaqueBlack = 0, - TransparentBlack = 1, + TransparentBlack = 0, + OpaqueBlack = 1, White = 2, Custom = 3, }; diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp index 43dda4064..f42c5829b 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp @@ -285,10 +285,10 @@ vk::SamplerMipmapMode MipFilter(AmdGpu::MipFilter filter) { vk::BorderColor BorderColor(AmdGpu::BorderColor color) { switch (color) { - case AmdGpu::BorderColor::OpaqueBlack: - return vk::BorderColor::eFloatOpaqueBlack; case AmdGpu::BorderColor::TransparentBlack: return vk::BorderColor::eFloatTransparentBlack; + case AmdGpu::BorderColor::OpaqueBlack: + return vk::BorderColor::eFloatOpaqueBlack; case AmdGpu::BorderColor::White: return vk::BorderColor::eFloatOpaqueWhite; case AmdGpu::BorderColor::Custom: From 17c47bcd96e460d75da4b3b095ce580306302580 Mon Sep 17 00:00:00 2001 From: "Daniel R." <47796739+polybiusproxy@users.noreply.github.com> Date: Tue, 19 Nov 2024 21:38:32 +0100 Subject: [PATCH 026/549] shader_recompiler/frontend: Implement `bitcmp` instructions (#1550) --- .../frontend/translate/scalar_alu.cpp | 39 +++++++++++++++++++ .../frontend/translate/translate.cpp | 7 ++++ .../frontend/translate/translate.h | 1 + src/shader_recompiler/ir/ir_emitter.cpp | 5 +++ 4 files changed, 52 insertions(+) diff --git a/src/shader_recompiler/frontend/translate/scalar_alu.cpp b/src/shader_recompiler/frontend/translate/scalar_alu.cpp index 1e627d95c..de8b9da87 100644 --- a/src/shader_recompiler/frontend/translate/scalar_alu.cpp +++ b/src/shader_recompiler/frontend/translate/scalar_alu.cpp @@ -132,6 +132,16 @@ void Translator::EmitSOPC(const GcnInst& inst) { return S_CMP(ConditionOp::LT, false, inst); case Opcode::S_CMP_LE_U32: return S_CMP(ConditionOp::LE, false, inst); + + case Opcode::S_BITCMP0_B32: + return S_BITCMP(false, 32, inst); + case Opcode::S_BITCMP1_B32: + return S_BITCMP(true, 32, inst); + case Opcode::S_BITCMP0_B64: + return S_BITCMP(false, 64, inst); + case Opcode::S_BITCMP1_B64: + return S_BITCMP(true, 64, inst); + default: LogMissingOpcode(inst); } @@ -615,4 +625,33 @@ void Translator::S_CMP(ConditionOp cond, bool is_signed, const GcnInst& inst) { ir.SetScc(result); } +void Translator::S_BITCMP(bool compare_mode, u32 bits, const GcnInst& inst) { + const IR::U1 result = [&] { + const IR::U32 src0 = GetSrc(inst.src[0]); + const IR::U32 src1 = GetSrc(inst.src[1]); + + IR::U32 mask; + switch (bits) { + case 32: + mask = ir.Imm32(0x1f); + break; + case 64: + mask = ir.Imm32(0x3f); + break; + default: + UNREACHABLE(); + } + + const IR::U32 bitpos{ir.BitwiseAnd(src1, mask)}; + const IR::U32 bittest{ir.BitwiseAnd(ir.ShiftRightLogical(src0, bitpos), ir.Imm32(1))}; + + if (!compare_mode) { + return ir.IEqual(bittest, ir.Imm32(0)); + } else { + return ir.IEqual(bittest, ir.Imm32(1)); + } + }(); + ir.SetScc(result); +} + } // namespace Shader::Gcn diff --git a/src/shader_recompiler/frontend/translate/translate.cpp b/src/shader_recompiler/frontend/translate/translate.cpp index ccce31a24..005c4a7ff 100644 --- a/src/shader_recompiler/frontend/translate/translate.cpp +++ b/src/shader_recompiler/frontend/translate/translate.cpp @@ -174,6 +174,13 @@ T Translator::GetSrc(const InstOperand& operand) { value = ir.GetM0(); } break; + case OperandField::Scc: + if constexpr (is_float) { + UNREACHABLE(); + } else { + value = ir.BitCast(ir.GetScc()); + } + break; default: UNREACHABLE(); } diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index 79bc33f0c..9fb441f39 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -114,6 +114,7 @@ public: // SOPC void S_CMP(ConditionOp cond, bool is_signed, const GcnInst& inst); + void S_BITCMP(bool compare_mode, u32 bits, const GcnInst& inst); // SOPP void S_BARRIER(); diff --git a/src/shader_recompiler/ir/ir_emitter.cpp b/src/shader_recompiler/ir/ir_emitter.cpp index cfd044f9e..73b33432b 100644 --- a/src/shader_recompiler/ir/ir_emitter.cpp +++ b/src/shader_recompiler/ir/ir_emitter.cpp @@ -59,6 +59,11 @@ F64 IREmitter::Imm64(f64 value) const { return F64{Value{value}}; } +template <> +IR::U32 IREmitter::BitCast(const IR::U1& value) { + return IR::U32{Select(value, Imm32(1), Imm32(0))}; +} + template <> IR::U32 IREmitter::BitCast(const IR::F32& value) { return Inst(Opcode::BitCastU32F32, value); From 9f42a12baf2636e1168b34aec413f787203ae881 Mon Sep 17 00:00:00 2001 From: "Alexander Y." Date: Wed, 20 Nov 2024 07:41:35 +0100 Subject: [PATCH 027/549] Fix build when -DENABLE_DISCORD_RPC=OFF (#1551) * added ifdef for discordrpc * replaced discordrpc include correctly --- src/common/discord_rpc_handler.cpp | 2 +- src/qt_gui/main_window.cpp | 6 +++++- src/qt_gui/main_window.h | 1 - src/qt_gui/settings_dialog.cpp | 2 ++ 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/common/discord_rpc_handler.cpp b/src/common/discord_rpc_handler.cpp index 91b278a15..ce99d683b 100644 --- a/src/common/discord_rpc_handler.cpp +++ b/src/common/discord_rpc_handler.cpp @@ -3,7 +3,7 @@ #include #include -#include "src/common/discord_rpc_handler.h" +#include "discord_rpc_handler.h" namespace DiscordRPCHandler { diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 959b055c8..f2ee87891 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -22,6 +22,9 @@ #include "main_window.h" #include "settings_dialog.h" #include "video_core/renderer_vulkan/vk_instance.h" +#ifdef ENABLE_DISCORD_RPC +#include "common/discord_rpc_handler.h" +#endif MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); @@ -76,12 +79,13 @@ bool MainWindow::Init() { "Games: " + QString::number(numGames) + " (" + QString::number(duration.count()) + "ms)"; statusBar->showMessage(statusMessage); - // Initialize Discord RPC +#ifdef ENABLE_DISCORD_RPC if (Config::getEnableDiscordRPC()) { auto* rpc = Common::Singleton::Instance(); rpc->init(); rpc->setStatusIdling(); } +#endif return true; } diff --git a/src/qt_gui/main_window.h b/src/qt_gui/main_window.h index 7dc85f0a7..5ae2540ec 100644 --- a/src/qt_gui/main_window.h +++ b/src/qt_gui/main_window.h @@ -9,7 +9,6 @@ #include "background_music_player.h" #include "common/config.h" -#include "common/discord_rpc_handler.h" #include "common/path_util.h" #include "core/file_format/psf.h" #include "core/file_sys/fs.h" diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index 83582663a..abbd39edd 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -173,6 +173,7 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge BackgroundMusicPlayer::getInstance().setVolume(val); }); +#ifdef ENABLE_DISCORD_RPC connect(ui->discordRPCCheckbox, &QCheckBox::stateChanged, this, [](int val) { Config::setEnableDiscordRPC(val); auto* rpc = Common::Singleton::Instance(); @@ -183,6 +184,7 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge rpc->shutdown(); } }); +#endif } // Input TAB From da00235eaf5b92d73a2f6fd16cba920cd871e565 Mon Sep 17 00:00:00 2001 From: korenkonder Date: Wed, 20 Nov 2024 19:22:30 +0300 Subject: [PATCH 028/549] Don't read uninitialized memory in `sceSystemServiceReceiveEvent` (#1554) --- src/core/libraries/system/systemservice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/libraries/system/systemservice.cpp b/src/core/libraries/system/systemservice.cpp index 9c6fe8f2e..4e7f9257a 100644 --- a/src/core/libraries/system/systemservice.cpp +++ b/src/core/libraries/system/systemservice.cpp @@ -1943,7 +1943,7 @@ int PS4_SYSV_ABI sceSystemServiceRaiseExceptionLocalProcess() { } s32 PS4_SYSV_ABI sceSystemServiceReceiveEvent(OrbisSystemServiceEvent* event) { - LOG_ERROR(Lib_SystemService, "(STUBBED) called, event type = {:#x}", (int)event->eventType); + LOG_ERROR(Lib_SystemService, "(STUBBED) called"); if (event == nullptr) { return ORBIS_SYSTEM_SERVICE_ERROR_PARAMETER; } From 96cd79f2726daec1bca4dcc35ced897eab2793e4 Mon Sep 17 00:00:00 2001 From: Ruah Devlin <102224378+Ruahh@users.noreply.github.com> Date: Wed, 20 Nov 2024 16:23:59 +0000 Subject: [PATCH 029/549] Implement V_MED3_U32 vector ALU Opcode (#1553) --- src/shader_recompiler/frontend/translate/translate.h | 1 + .../frontend/translate/vector_alu.cpp | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index 9fb441f39..fd5a58899 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -225,6 +225,7 @@ public: void V_MAX3_U32(bool is_signed, const GcnInst& inst); void V_MED3_F32(const GcnInst& inst); void V_MED3_I32(const GcnInst& inst); + void V_MED3_U32(const GcnInst& inst); void V_SAD(const GcnInst& inst); void V_SAD_U32(const GcnInst& inst); void V_CVT_PK_U16_U32(const GcnInst& inst); diff --git a/src/shader_recompiler/frontend/translate/vector_alu.cpp b/src/shader_recompiler/frontend/translate/vector_alu.cpp index 433f9dce7..7ac3131c5 100644 --- a/src/shader_recompiler/frontend/translate/vector_alu.cpp +++ b/src/shader_recompiler/frontend/translate/vector_alu.cpp @@ -357,6 +357,8 @@ void Translator::EmitVectorAlu(const GcnInst& inst) { return V_MED3_F32(inst); case Opcode::V_MED3_I32: return V_MED3_I32(inst); + case Opcode::V_MED3_U32: + return V_MED3_U32(inst); case Opcode::V_SAD_U32: return V_SAD_U32(inst); case Opcode::V_CVT_PK_U16_U32: @@ -1092,6 +1094,14 @@ void Translator::V_MED3_I32(const GcnInst& inst) { SetDst(inst.dst[0], ir.SMax(ir.SMin(src0, src1), mmx)); } +void Translator::V_MED3_U32(const GcnInst& inst) { + const IR::U32 src0{GetSrc(inst.src[0])}; + const IR::U32 src1{GetSrc(inst.src[1])}; + const IR::U32 src2{GetSrc(inst.src[2])}; + const IR::U32 mmx = ir.UMin(ir.UMax(src0, src1), src2); + SetDst(inst.dst[0], ir.UMax(ir.UMin(src0, src1), mmx)); +} + void Translator::V_SAD(const GcnInst& inst) { const IR::U32 abs_diff = ir.IAbs(ir.ISub(GetSrc(inst.src[0]), GetSrc(inst.src[1]))); SetDst(inst.dst[0], ir.IAdd(abs_diff, GetSrc(inst.src[2]))); From b41664ac616894686e072ede61c609b422d79ed4 Mon Sep 17 00:00:00 2001 From: Vinicius Rangel Date: Wed, 20 Nov 2024 13:31:44 -0300 Subject: [PATCH 030/549] savedata: fix dir name search with wildcard (#1552) * savedata: fix dir name search with wildcard * psf: replace filesystem clock by system clock (utc) * savedatadialog_ui: macOS zoned_time formatting Signed-off-by: Vinicius Rangel --------- Signed-off-by: Vinicius Rangel Co-authored-by: squidbus <175574877+squidbus@users.noreply.github.com> --- src/core/file_format/psf.cpp | 9 ++++- src/core/file_format/psf.h | 5 ++- .../save_data/dialog/savedatadialog_ui.cpp | 37 ++++++++++++++++--- .../save_data/dialog/savedatadialog_ui.h | 2 +- src/core/libraries/save_data/savedata.cpp | 10 +++-- 5 files changed, 49 insertions(+), 14 deletions(-) diff --git a/src/core/file_format/psf.cpp b/src/core/file_format/psf.cpp index 0502f29d2..7e0ffc9a3 100644 --- a/src/core/file_format/psf.cpp +++ b/src/core/file_format/psf.cpp @@ -21,8 +21,13 @@ static inline u32 get_max_size(std::string_view key, u32 default_value) { } bool PSF::Open(const std::filesystem::path& filepath) { + using namespace std::chrono; if (std::filesystem::exists(filepath)) { - last_write = std::filesystem::last_write_time(filepath); + const auto t = std::filesystem::last_write_time(filepath); + const auto rel = + duration_cast(t - std::filesystem::file_time_type::clock::now()).count(); + const auto tp = system_clock::to_time_t(system_clock::now() + seconds{rel}); + last_write = system_clock::from_time_t(tp); } Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Read); @@ -99,7 +104,7 @@ bool PSF::Encode(const std::filesystem::path& filepath) const { return false; } - last_write = std::filesystem::file_time_type::clock::now(); + last_write = std::chrono::system_clock::now(); const auto psf_buffer = Encode(); const size_t written = file.Write(psf_buffer); diff --git a/src/core/file_format/psf.h b/src/core/file_format/psf.h index 6f35fa69a..0f6621315 100644 --- a/src/core/file_format/psf.h +++ b/src/core/file_format/psf.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include #include @@ -71,7 +72,7 @@ public: void AddString(std::string key, std::string value, bool update = false); void AddInteger(std::string key, s32 value, bool update = false); - [[nodiscard]] std::filesystem::file_time_type GetLastWrite() const { + [[nodiscard]] std::chrono::system_clock::time_point GetLastWrite() const { return last_write; } @@ -80,7 +81,7 @@ public: } private: - mutable std::filesystem::file_time_type last_write; + mutable std::chrono::system_clock::time_point last_write; std::vector entry_list; diff --git a/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp b/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp index 01e56f8b8..52abe9101 100644 --- a/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp +++ b/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp @@ -13,6 +13,33 @@ #include "imgui/imgui_std.h" #include "savedatadialog_ui.h" +#ifdef __APPLE__ +#include + +// Need to make a copy of the formatter for std::chrono::local_time for use with date::local_time +template +struct fmt::formatter, Char> : formatter { + FMT_CONSTEXPR formatter() { + this->format_str_ = fmt::detail::string_literal(); + } + + template + auto format(date::local_time val, FormatContext& ctx) const -> decltype(ctx.out()) { + using period = typename Duration::period; + if (period::num == 1 && period::den == 1 && + !std::is_floating_point::value) { + return formatter::format( + localtime(fmt::detail::to_time_t(date::current_zone()->to_sys(val))), ctx); + } + auto epoch = val.time_since_epoch(); + auto subsecs = fmt::detail::duration_cast( + epoch - fmt::detail::duration_cast(epoch)); + return formatter::do_format( + localtime(fmt::detail::to_time_t(date::current_zone()->to_sys(val))), ctx, &subsecs); + } +}; +#endif + using namespace ImGui; using namespace Libraries::CommonDialog; using Common::ElfInfo; @@ -98,12 +125,12 @@ SaveDialogState::SaveDialogState(const OrbisSaveDataDialogParam& param) { param_sfo.Open(param_sfo_path); auto last_write = param_sfo.GetLastWrite(); -#if defined(_WIN32) && !defined(__GNUC__) && !defined(__MINGW32__) && !defined(__MINGW64__) - auto utc_time = std::chrono::file_clock::to_utc(last_write); +#ifdef __APPLE__ + auto t = date::zoned_time{date::current_zone(), last_write}; #else - auto utc_time = std::chrono::file_clock::to_sys(last_write); + auto t = std::chrono::zoned_time{std::chrono::current_zone(), last_write}; #endif - std::string date_str = fmt::format("{:%d %b, %Y %R}", utc_time); + std::string date_str = fmt::format("{:%d %b, %Y %R}", t.get_local_time()); size_t size = Common::FS::GetDirectorySize(dir_path); std::string size_str = SpaceSizeToString(size); @@ -592,7 +619,7 @@ void SaveDialogUi::DrawList() { int idx = 0; int max_idx = 0; bool is_min = pos == FocusPos::DATAOLDEST; - std::filesystem::file_time_type max_write{}; + std::chrono::system_clock::time_point max_write{}; if (state->new_item.has_value()) { idx++; } diff --git a/src/core/libraries/save_data/dialog/savedatadialog_ui.h b/src/core/libraries/save_data/dialog/savedatadialog_ui.h index 3f414470f..aa67e1f5f 100644 --- a/src/core/libraries/save_data/dialog/savedatadialog_ui.h +++ b/src/core/libraries/save_data/dialog/savedatadialog_ui.h @@ -248,7 +248,7 @@ public: std::string date{}; std::string size{}; - std::filesystem::file_time_type last_write{}; + std::chrono::system_clock::time_point last_write{}; PSF pfo{}; bool is_corrupted{}; }; diff --git a/src/core/libraries/save_data/savedata.cpp b/src/core/libraries/save_data/savedata.cpp index 93b3c20a9..c515ebcbf 100644 --- a/src/core/libraries/save_data/savedata.cpp +++ b/src/core/libraries/save_data/savedata.cpp @@ -149,7 +149,7 @@ struct OrbisSaveDataIcon { size_t dataSize; std::array _reserved; - Error LoadIcon(const std::filesystem::path& icon_path) { + Error LoadIcon(const fs::path& icon_path) { try { const Common::FS::IOFile file(icon_path, Common::FS::FileAccessMode::Read); dataSize = file.GetSize(); @@ -345,7 +345,9 @@ static bool match(std::string_view str, std::string_view pattern) { if (*pat_it == '_') { // 1 character wildcard ++str_it; ++pat_it; - } else if (*pat_it != *str_it) { + continue; + } + if (*pat_it != *str_it) { return false; } ++str_it; @@ -1230,7 +1232,7 @@ Error PS4_SYSV_ABI sceSaveDataLoadIcon(const OrbisSaveDataMountPoint* mountPoint return Error::PARAMETER; } LOG_DEBUG(Lib_SaveData, "called"); - std::filesystem::path path; + fs::path path; const std::string_view mount_point_str{mountPoint->data}; for (const auto& instance : g_mount_slots) { if (instance.has_value() && instance->GetMountPoint() == mount_point_str) { @@ -1375,7 +1377,7 @@ Error PS4_SYSV_ABI sceSaveDataSaveIcon(const OrbisSaveDataMountPoint* mountPoint return Error::PARAMETER; } LOG_DEBUG(Lib_SaveData, "called"); - std::filesystem::path path; + fs::path path; const std::string_view mount_point_str{mountPoint->data}; for (const auto& instance : g_mount_slots) { if (instance.has_value() && instance->GetMountPoint() == mount_point_str) { From e585330744b3127a517cac343a8b01a0349a376f Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Wed, 20 Nov 2024 10:24:30 -0800 Subject: [PATCH 031/549] image_view: Use array view for color/depth buffers with multiple layers. (#1556) --- src/video_core/texture_cache/image_view.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/video_core/texture_cache/image_view.cpp b/src/video_core/texture_cache/image_view.cpp index 7dbf1230e..599235998 100644 --- a/src/video_core/texture_cache/image_view.cpp +++ b/src/video_core/texture_cache/image_view.cpp @@ -131,6 +131,7 @@ ImageViewInfo::ImageViewInfo(const AmdGpu::Liverpool::ColorBuffer& col_buffer, Vulkan::LiverpoolToVK::SurfaceFormat(col_buffer.info.format, col_buffer.NumFormat()); range.base.layer = col_buffer.view.slice_start; range.extent.layers = col_buffer.NumSlices() - range.base.layer; + type = range.extent.layers > 1 ? vk::ImageViewType::e2DArray : vk::ImageViewType::e2D; format = Vulkan::LiverpoolToVK::AdjustColorBufferFormat( base_format, col_buffer.info.comp_swap.Value(), is_vo_surface); } @@ -143,6 +144,7 @@ ImageViewInfo::ImageViewInfo(const AmdGpu::Liverpool::DepthBuffer& depth_buffer, is_storage = ctl.depth_write_enable; range.base.layer = view.slice_start; range.extent.layers = view.NumSlices() - range.base.layer; + type = range.extent.layers > 1 ? vk::ImageViewType::e2DArray : vk::ImageViewType::e2D; } ImageView::ImageView(const Vulkan::Instance& instance, const ImageViewInfo& info_, Image& image, From e98fab4b58504713896ad16db5f279015f55740f Mon Sep 17 00:00:00 2001 From: psucien Date: Thu, 21 Nov 2024 00:04:46 +0100 Subject: [PATCH 032/549] hot-fix: correct M-tiled surface size calculation when mip is less than a tile --- src/video_core/texture_cache/image_info.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index 055753dbc..0cf9f5605 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -116,9 +116,21 @@ static constexpr std::pair ImageSizeMicroTiled(u32 pitch, u32 heigh } static constexpr std::pair ImageSizeMacroTiled(u32 pitch, u32 height, u32 bpp, - u32 num_samples, u32 tiling_idx) { + u32 num_samples, u32 tiling_idx, + u32 mip_n) { const auto& [pitch_align, height_align] = GetMacroTileExtents(tiling_idx, bpp, num_samples); ASSERT(pitch_align != 0 && height_align != 0); + bool downgrade_to_micro = false; + if (mip_n > 0) { + const bool is_less_than_tile = pitch < pitch_align || height < height_align; + // TODO: threshold check + downgrade_to_micro = is_less_than_tile; + } + + if (downgrade_to_micro) { + return ImageSizeMicroTiled(pitch, height, bpp, num_samples); + } + const auto pitch_aligned = (pitch + pitch_align - 1) & ~(pitch_align - 1); const auto height_aligned = (height + height_align - 1) & ~(height_align - 1); const auto log_sz = pitch_aligned * height_aligned * num_samples; @@ -276,7 +288,7 @@ void ImageInfo::UpdateSize() { ASSERT(!props.is_block); ASSERT(num_samples == 1); std::tie(mip_info.pitch, mip_info.size) = - ImageSizeMacroTiled(mip_w, mip_h, bpp, num_samples, tiling_idx); + ImageSizeMacroTiled(mip_w, mip_h, bpp, num_samples, tiling_idx, mip); break; } default: { From 3d04765a3fa3425481036bb1fd5be0e99b1a42e6 Mon Sep 17 00:00:00 2001 From: psucien <168137814+psucien@users.noreply.github.com> Date: Thu, 21 Nov 2024 11:06:53 +0100 Subject: [PATCH 033/549] Respect game brightness settings (#1559) * `RendererVulkan` -> `Presenter` * support for Video Out gamma setting * sRGB hack removed * added post process pass to presenter * splash functionality restored --- CMakeLists.txt | 4 +- src/core/devtools/layer.cpp | 10 + src/core/libraries/gnmdriver/gnmdriver.cpp | 8 +- src/core/libraries/videoout/driver.cpp | 23 +- src/core/libraries/videoout/driver.h | 2 +- src/core/libraries/videoout/video_out.cpp | 29 ++ src/core/libraries/videoout/video_out.h | 7 + src/imgui/renderer/imgui_core.cpp | 2 +- src/video_core/host_shaders/CMakeLists.txt | 2 + src/video_core/host_shaders/fs_tri.vert | 15 + src/video_core/host_shaders/post_process.frag | 19 + .../renderer_vulkan/liverpool_to_vk.cpp | 18 +- .../renderer_vulkan/liverpool_to_vk.h | 2 +- .../renderer_vulkan/vk_pipeline_cache.cpp | 9 +- .../{renderer_vulkan.cpp => vk_presenter.cpp} | 439 +++++++++++++++--- .../{renderer_vulkan.h => vk_presenter.h} | 28 +- .../renderer_vulkan/vk_rasterizer.cpp | 2 +- src/video_core/texture_cache/image_view.cpp | 7 +- src/video_core/texture_cache/image_view.h | 2 +- .../texture_cache/texture_cache.cpp | 4 +- src/video_core/texture_cache/texture_cache.h | 6 +- 21 files changed, 518 insertions(+), 120 deletions(-) create mode 100644 src/video_core/host_shaders/fs_tri.vert create mode 100644 src/video_core/host_shaders/post_process.frag rename src/video_core/renderer_vulkan/{renderer_vulkan.cpp => vk_presenter.cpp} (51%) rename src/video_core/renderer_vulkan/{renderer_vulkan.h => vk_presenter.h} (79%) diff --git a/CMakeLists.txt b/CMakeLists.txt index d320d49e3..47cfd8595 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -657,8 +657,6 @@ set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp src/video_core/buffer_cache/word_manager.h src/video_core/renderer_vulkan/liverpool_to_vk.cpp src/video_core/renderer_vulkan/liverpool_to_vk.h - src/video_core/renderer_vulkan/renderer_vulkan.cpp - src/video_core/renderer_vulkan/renderer_vulkan.h src/video_core/renderer_vulkan/vk_common.cpp src/video_core/renderer_vulkan/vk_common.h src/video_core/renderer_vulkan/vk_compute_pipeline.cpp @@ -677,6 +675,8 @@ set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp src/video_core/renderer_vulkan/vk_pipeline_common.h src/video_core/renderer_vulkan/vk_platform.cpp src/video_core/renderer_vulkan/vk_platform.h + src/video_core/renderer_vulkan/vk_presenter.cpp + src/video_core/renderer_vulkan/vk_presenter.h src/video_core/renderer_vulkan/vk_rasterizer.cpp src/video_core/renderer_vulkan/vk_rasterizer.h src/video_core/renderer_vulkan/vk_resource_pool.cpp diff --git a/src/core/devtools/layer.cpp b/src/core/devtools/layer.cpp index 264b3be0d..2c4ce20b6 100644 --- a/src/core/devtools/layer.cpp +++ b/src/core/devtools/layer.cpp @@ -11,9 +11,12 @@ #include "imgui_internal.h" #include "layer.h" #include "options.h" +#include "video_core/renderer_vulkan/vk_presenter.h" #include "widget/frame_dump.h" #include "widget/frame_graph.h" +extern std::unique_ptr presenter; + using namespace ImGui; using namespace Core::Devtools; using L = Core::Devtools::Layer; @@ -71,6 +74,13 @@ void L::DrawMenuBar() { open_popup_help = MenuItem("Help & Tips"); ImGui::EndMenu(); } + if (BeginMenu("Display")) { + if (BeginMenu("Brightness")) { + SliderFloat("Gamma", &presenter->GetGammaRef(), 0.1f, 2.0f); + ImGui::EndMenu(); + } + ImGui::EndMenu(); + } EndMainMenuBar(); } diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index ee37bc57d..946622cef 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -19,10 +19,10 @@ #include "core/platform.h" #include "video_core/amdgpu/liverpool.h" #include "video_core/amdgpu/pm4_cmds.h" -#include "video_core/renderer_vulkan/renderer_vulkan.h" +#include "video_core/renderer_vulkan/vk_presenter.h" extern Frontend::WindowSDL* g_window; -std::unique_ptr renderer; +std::unique_ptr presenter; std::unique_ptr liverpool; namespace Libraries::GnmDriver { @@ -2743,9 +2743,9 @@ int PS4_SYSV_ABI Func_F916890425496553() { } void RegisterlibSceGnmDriver(Core::Loader::SymbolsResolver* sym) { - LOG_INFO(Lib_GnmDriver, "Initializing renderer"); + LOG_INFO(Lib_GnmDriver, "Initializing presenter"); liverpool = std::make_unique(); - renderer = std::make_unique(*g_window, liverpool.get()); + presenter = std::make_unique(*g_window, liverpool.get()); const int result = sceKernelGetCompiledSdkVersion(&sdk_version); if (result != ORBIS_OK) { diff --git a/src/core/libraries/videoout/driver.cpp b/src/core/libraries/videoout/driver.cpp index e2fd00028..13d67c4e7 100644 --- a/src/core/libraries/videoout/driver.cpp +++ b/src/core/libraries/videoout/driver.cpp @@ -13,9 +13,9 @@ #include "core/libraries/kernel/time_management.h" #include "core/libraries/videoout/driver.h" #include "core/platform.h" -#include "video_core/renderer_vulkan/renderer_vulkan.h" +#include "video_core/renderer_vulkan/vk_presenter.h" -extern std::unique_ptr renderer; +extern std::unique_ptr presenter; extern std::unique_ptr liverpool; namespace Libraries::VideoOut { @@ -136,7 +136,7 @@ int VideoOutDriver::RegisterBuffers(VideoOutPort* port, s32 startIndex, void* co .address_right = 0, }; - renderer->RegisterVideoOutSurface(group, address); + presenter->RegisterVideoOutSurface(group, address); LOG_INFO(Lib_VideoOut, "buffers[{}] = {:#x}", i + startIndex, address); } @@ -164,9 +164,9 @@ int VideoOutDriver::UnregisterBuffers(VideoOutPort* port, s32 attributeIndex) { void VideoOutDriver::Flip(const Request& req) { // Whatever the game is rendering show splash if it is active - if (!renderer->ShowSplash(req.frame)) { + if (!presenter->ShowSplash(req.frame)) { // Present the frame. - renderer->Present(req.frame); + presenter->Present(req.frame); } // Update flip status. @@ -201,8 +201,11 @@ void VideoOutDriver::Flip(const Request& req) { } void VideoOutDriver::DrawBlankFrame() { - const auto empty_frame = renderer->PrepareBlankFrame(false); - renderer->Present(empty_frame); + if (presenter->ShowSplash(nullptr)) { + return; + } + const auto empty_frame = presenter->PrepareBlankFrame(false); + presenter->Present(empty_frame); } bool VideoOutDriver::SubmitFlip(VideoOutPort* port, s32 index, s64 flip_arg, @@ -226,7 +229,7 @@ bool VideoOutDriver::SubmitFlip(VideoOutPort* port, s32 index, s64 flip_arg, // point VO surface is ready to be presented, and we will need have an actual state of // Vulkan image at the time of frame presentation. liverpool->SendCommand([=, this]() { - renderer->FlushDraw(); + presenter->FlushDraw(); SubmitFlipInternal(port, index, flip_arg, is_eop); }); } else { @@ -240,11 +243,11 @@ void VideoOutDriver::SubmitFlipInternal(VideoOutPort* port, s32 index, s64 flip_ bool is_eop /*= false*/) { Vulkan::Frame* frame; if (index == -1) { - frame = renderer->PrepareBlankFrame(is_eop); + frame = presenter->PrepareBlankFrame(is_eop); } else { const auto& buffer = port->buffer_slots[index]; const auto& group = port->groups[buffer.group_index]; - frame = renderer->PrepareFrame(group, buffer.address_left, is_eop); + frame = presenter->PrepareFrame(group, buffer.address_left, is_eop); } std::scoped_lock lock{mutex}; diff --git a/src/core/libraries/videoout/driver.h b/src/core/libraries/videoout/driver.h index 2e478b9ee..ec01b621f 100644 --- a/src/core/libraries/videoout/driver.h +++ b/src/core/libraries/videoout/driver.h @@ -74,7 +74,7 @@ struct ServiceThreadParams { class VideoOutDriver { public: - explicit VideoOutDriver(u32 width, u32 height); + VideoOutDriver(u32 width, u32 height); ~VideoOutDriver(); int Open(const ServiceThreadParams* params); diff --git a/src/core/libraries/videoout/video_out.cpp b/src/core/libraries/videoout/video_out.cpp index 31b8a21ca..0258074d3 100644 --- a/src/core/libraries/videoout/video_out.cpp +++ b/src/core/libraries/videoout/video_out.cpp @@ -11,6 +11,9 @@ #include "core/libraries/videoout/video_out.h" #include "core/loader/symbols_resolver.h" #include "core/platform.h" +#include "video_core/renderer_vulkan/vk_presenter.h" + +extern std::unique_ptr presenter; namespace Libraries::VideoOut { @@ -297,6 +300,28 @@ s32 PS4_SYSV_ABI sceVideoOutWaitVblank(s32 handle) { return ORBIS_OK; } +s32 PS4_SYSV_ABI sceVideoOutColorSettingsSetGamma(SceVideoOutColorSettings* settings, float gamma) { + if (gamma < 0.1f || gamma > 2.0f) { + return ORBIS_VIDEO_OUT_ERROR_INVALID_VALUE; + } + settings->gamma = gamma; + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVideoOutAdjustColor(s32 handle, const SceVideoOutColorSettings* settings) { + if (settings == nullptr) { + return ORBIS_VIDEO_OUT_ERROR_INVALID_ADDRESS; + } + + auto* port = driver->GetPort(handle); + if (!port) { + return ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE; + } + + presenter->GetGammaRef() = settings->gamma; + return ORBIS_OK; +} + void RegisterLib(Core::Loader::SymbolsResolver* sym) { driver = std::make_unique(Config::getScreenWidth(), Config::getScreenHeight()); @@ -329,6 +354,10 @@ void RegisterLib(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("U2JJtSqNKZI", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutGetEventId); LIB_FUNCTION("rWUTcKdkUzQ", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutGetEventData); + LIB_FUNCTION("DYhhWbJSeRg", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, + sceVideoOutColorSettingsSetGamma); + LIB_FUNCTION("pv9CI5VC+R0", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, + sceVideoOutAdjustColor); // openOrbis appears to have libSceVideoOut_v1 module libSceVideoOut_v1.1 LIB_FUNCTION("Up36PTk687E", "libSceVideoOut", 1, "libSceVideoOut", 1, 1, sceVideoOutOpen); diff --git a/src/core/libraries/videoout/video_out.h b/src/core/libraries/videoout/video_out.h index 63cd8fede..7f8421fba 100644 --- a/src/core/libraries/videoout/video_out.h +++ b/src/core/libraries/videoout/video_out.h @@ -88,6 +88,11 @@ struct SceVideoOutDeviceCapabilityInfo { u64 capability; }; +struct SceVideoOutColorSettings { + float gamma; + u32 reserved[3]; +}; + void PS4_SYSV_ABI sceVideoOutSetBufferAttribute(BufferAttribute* attribute, PixelFormat pixelFormat, u32 tilingMode, u32 aspectRatio, u32 width, u32 height, u32 pitchInPixel); @@ -106,6 +111,8 @@ s32 PS4_SYSV_ABI sceVideoOutOpen(SceUserServiceUserId userId, s32 busType, s32 i s32 PS4_SYSV_ABI sceVideoOutClose(s32 handle); int PS4_SYSV_ABI sceVideoOutGetEventId(const Kernel::SceKernelEvent* ev); int PS4_SYSV_ABI sceVideoOutGetEventData(const Kernel::SceKernelEvent* ev, int64_t* data); +s32 PS4_SYSV_ABI sceVideoOutColorSettingsSetGamma(SceVideoOutColorSettings* settings, float gamma); +s32 PS4_SYSV_ABI sceVideoOutAdjustColor(s32 handle, const SceVideoOutColorSettings* settings); // Internal system functions void sceVideoOutGetBufferLabelAddress(s32 handle, uintptr_t* label_addr); diff --git a/src/imgui/renderer/imgui_core.cpp b/src/imgui/renderer/imgui_core.cpp index 311e86a3c..47f60e8e4 100644 --- a/src/imgui/renderer/imgui_core.cpp +++ b/src/imgui/renderer/imgui_core.cpp @@ -14,7 +14,7 @@ #include "imgui_internal.h" #include "sdl_window.h" #include "texture_manager.h" -#include "video_core/renderer_vulkan/renderer_vulkan.h" +#include "video_core/renderer_vulkan/vk_presenter.h" #include "imgui_fonts/notosansjp_regular.ttf.g.cpp" #include "imgui_fonts/proggyvector_regular.ttf.g.cpp" diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index f2b6cc2d0..4ef8bcdba 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -7,6 +7,8 @@ set(SHADER_FILES detile_m32x1.comp detile_m32x2.comp detile_m32x4.comp + fs_tri.vert + post_process.frag ) set(SHADER_INCLUDE ${CMAKE_CURRENT_BINARY_DIR}/include) diff --git a/src/video_core/host_shaders/fs_tri.vert b/src/video_core/host_shaders/fs_tri.vert new file mode 100644 index 000000000..7b82c11a9 --- /dev/null +++ b/src/video_core/host_shaders/fs_tri.vert @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#version 450 + +layout(location = 0) out vec2 uv; + +void main() { + vec2 pos = vec2( + float((gl_VertexIndex & 1u) << 2u), + float((gl_VertexIndex & 2u) << 1u) + ); + gl_Position = vec4(pos - vec2(1.0, 1.0), 0.0, 1.0); + uv = pos * 0.5; +} diff --git a/src/video_core/host_shaders/post_process.frag b/src/video_core/host_shaders/post_process.frag new file mode 100644 index 000000000..fcced3232 --- /dev/null +++ b/src/video_core/host_shaders/post_process.frag @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#version 450 + +layout (location = 0) in vec2 uv; +layout (location = 0) out vec4 color; + +layout (binding = 0) uniform sampler2D texSampler; + +layout(push_constant) uniform settings { + float gamma; +} pp; + +void main() +{ + vec4 color_linear = texture(texSampler, uv); + color = pow(color_linear, vec4(1.0/(2.2 + 1.0 - pp.gamma))); +} diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp index f42c5829b..258e7f391 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp @@ -652,7 +652,7 @@ vk::Format SurfaceFormat(AmdGpu::DataFormat data_format, AmdGpu::NumberFormat nu } vk::Format AdjustColorBufferFormat(vk::Format base_format, - Liverpool::ColorBuffer::SwapMode comp_swap, bool is_vo_surface) { + Liverpool::ColorBuffer::SwapMode comp_swap) { const bool comp_swap_alt = comp_swap == Liverpool::ColorBuffer::SwapMode::Alternate; const bool comp_swap_reverse = comp_swap == Liverpool::ColorBuffer::SwapMode::StandardReverse; const bool comp_swap_alt_reverse = @@ -664,9 +664,9 @@ vk::Format AdjustColorBufferFormat(vk::Format base_format, case vk::Format::eB8G8R8A8Unorm: return vk::Format::eR8G8B8A8Unorm; case vk::Format::eR8G8B8A8Srgb: - return is_vo_surface ? vk::Format::eB8G8R8A8Unorm : vk::Format::eB8G8R8A8Srgb; + return vk::Format::eB8G8R8A8Srgb; case vk::Format::eB8G8R8A8Srgb: - return is_vo_surface ? vk::Format::eR8G8B8A8Unorm : vk::Format::eR8G8B8A8Srgb; + return vk::Format::eR8G8B8A8Srgb; case vk::Format::eA2B10G10R10UnormPack32: return vk::Format::eA2R10G10B10UnormPack32; default: @@ -677,20 +677,10 @@ vk::Format AdjustColorBufferFormat(vk::Format base_format, case vk::Format::eR8G8B8A8Unorm: return vk::Format::eA8B8G8R8UnormPack32; case vk::Format::eR8G8B8A8Srgb: - return is_vo_surface ? vk::Format::eA8B8G8R8UnormPack32 - : vk::Format::eA8B8G8R8SrgbPack32; + return vk::Format::eA8B8G8R8SrgbPack32; default: break; } - } else if (comp_swap_alt_reverse) { - return base_format; - } else { - if (is_vo_surface && base_format == vk::Format::eR8G8B8A8Srgb) { - return vk::Format::eR8G8B8A8Unorm; - } - if (is_vo_surface && base_format == vk::Format::eB8G8R8A8Srgb) { - return vk::Format::eB8G8R8A8Unorm; - } } return base_format; } diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.h b/src/video_core/renderer_vulkan/liverpool_to_vk.h index 5fb04e5f5..70e707fad 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.h +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.h @@ -51,7 +51,7 @@ std::span SurfaceFormats(); vk::Format SurfaceFormat(AmdGpu::DataFormat data_format, AmdGpu::NumberFormat num_format); vk::Format AdjustColorBufferFormat(vk::Format base_format, - Liverpool::ColorBuffer::SwapMode comp_swap, bool is_vo_surface); + Liverpool::ColorBuffer::SwapMode comp_swap); struct DepthFormatInfo { Liverpool::DepthBuffer::ZFormat z_format; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index da098fa37..245fddb65 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -11,13 +11,13 @@ #include "shader_recompiler/info.h" #include "shader_recompiler/recompiler.h" #include "shader_recompiler/runtime_info.h" -#include "video_core/renderer_vulkan/renderer_vulkan.h" #include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_pipeline_cache.h" +#include "video_core/renderer_vulkan/vk_presenter.h" #include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_shader_util.h" -extern std::unique_ptr renderer; +extern std::unique_ptr presenter; namespace Vulkan { @@ -265,9 +265,8 @@ bool PipelineCache::RefreshGraphicsKey() { } const auto base_format = LiverpoolToVK::SurfaceFormat(col_buf.info.format, col_buf.NumFormat()); - const bool is_vo_surface = renderer->IsVideoOutSurface(col_buf); - key.color_formats[remapped_cb] = LiverpoolToVK::AdjustColorBufferFormat( - base_format, col_buf.info.comp_swap.Value(), false /*is_vo_surface*/); + key.color_formats[remapped_cb] = + LiverpoolToVK::AdjustColorBufferFormat(base_format, col_buf.info.comp_swap.Value()); key.color_num_formats[remapped_cb] = col_buf.NumFormat(); if (base_format == key.color_formats[remapped_cb]) { key.mrt_swizzles[remapped_cb] = col_buf.info.comp_swap.Value(); diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/vk_presenter.cpp similarity index 51% rename from src/video_core/renderer_vulkan/renderer_vulkan.cpp rename to src/video_core/renderer_vulkan/vk_presenter.cpp index 64a483654..d4972cbad 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/vk_presenter.cpp @@ -4,18 +4,21 @@ #include "common/config.h" #include "common/debug.h" #include "common/singleton.h" +#include "core/debug_state.h" +#include "core/devtools/layer.h" #include "core/file_format/splash.h" #include "core/libraries/system/systemservice.h" #include "imgui/renderer/imgui_core.h" #include "sdl_window.h" -#include "video_core/renderer_vulkan/renderer_vulkan.h" +#include "video_core/renderer_vulkan/vk_presenter.h" #include "video_core/renderer_vulkan/vk_rasterizer.h" +#include "video_core/renderer_vulkan/vk_shader_util.h" #include "video_core/texture_cache/image.h" -#include +#include "video_core/host_shaders/fs_tri_vert.h" +#include "video_core/host_shaders/post_process_frag.h" -#include "core/debug_state.h" -#include "core/devtools/layer.h" +#include namespace Vulkan { @@ -92,7 +95,203 @@ bool CanBlitToSwapchain(const vk::PhysicalDevice physical_device, vk::Format for return MakeImageBlit(frame_width, frame_height, dst_width, dst_height, offset_x, offset_y); } -RendererVulkan::RendererVulkan(Frontend::WindowSDL& window_, AmdGpu::Liverpool* liverpool_) +static vk::Format FormatToUnorm(vk::Format fmt) { + switch (fmt) { + case vk::Format::eR8G8B8A8Srgb: + return vk::Format::eR8G8B8A8Unorm; + case vk::Format::eB8G8R8A8Srgb: + return vk::Format::eB8G8R8A8Unorm; + default: + UNREACHABLE(); + } +} + +void Presenter::CreatePostProcessPipeline() { + static const std::array pp_shaders{ + HostShaders::FS_TRI_VERT, + HostShaders::POST_PROCESS_FRAG, + }; + + boost::container::static_vector bindings{ + { + .binding = 0, + .descriptorType = vk::DescriptorType::eCombinedImageSampler, + .descriptorCount = 1, + .stageFlags = vk::ShaderStageFlagBits::eFragment, + }, + }; + + const vk::DescriptorSetLayoutCreateInfo desc_layout_ci = { + .flags = vk::DescriptorSetLayoutCreateFlagBits::ePushDescriptorKHR, + .bindingCount = static_cast(bindings.size()), + .pBindings = bindings.data(), + }; + auto desc_layout_result = instance.GetDevice().createDescriptorSetLayoutUnique(desc_layout_ci); + ASSERT_MSG(desc_layout_result.result == vk::Result::eSuccess, + "Failed to create descriptor set layout: {}", + vk::to_string(desc_layout_result.result)); + pp_desc_set_layout = std::move(desc_layout_result.value); + + const vk::PushConstantRange push_constants = { + .stageFlags = vk::ShaderStageFlagBits::eFragment, + .offset = 0, + .size = sizeof(PostProcessSettings), + }; + + const auto& vs_module = + Vulkan::Compile(pp_shaders[0], vk::ShaderStageFlagBits::eVertex, instance.GetDevice()); + ASSERT(vs_module); + Vulkan::SetObjectName(instance.GetDevice(), vs_module, "fs_tri.vert"); + + const auto& fs_module = + Vulkan::Compile(pp_shaders[1], vk::ShaderStageFlagBits::eFragment, instance.GetDevice()); + ASSERT(fs_module); + Vulkan::SetObjectName(instance.GetDevice(), vs_module, "post_process.frag"); + + const std::array shaders_ci{ + vk::PipelineShaderStageCreateInfo{ + .stage = vk::ShaderStageFlagBits::eVertex, + .module = vs_module, + .pName = "main", + }, + vk::PipelineShaderStageCreateInfo{ + .stage = vk::ShaderStageFlagBits::eFragment, + .module = fs_module, + .pName = "main", + }, + }; + + const vk::DescriptorSetLayout set_layout = *pp_desc_set_layout; + const vk::PipelineLayoutCreateInfo layout_info = { + .setLayoutCount = 1U, + .pSetLayouts = &set_layout, + .pushConstantRangeCount = 1, + .pPushConstantRanges = &push_constants, + }; + auto [layout_result, layout] = instance.GetDevice().createPipelineLayoutUnique(layout_info); + ASSERT_MSG(layout_result == vk::Result::eSuccess, "Failed to create pipeline layout: {}", + vk::to_string(layout_result)); + pp_pipeline_layout = std::move(layout); + + const std::array pp_color_formats{ + vk::Format::eB8G8R8A8Unorm, // swapchain.GetSurfaceFormat().format, + }; + const vk::PipelineRenderingCreateInfoKHR pipeline_rendering_ci = { + .colorAttachmentCount = 1u, + .pColorAttachmentFormats = pp_color_formats.data(), + }; + + const vk::PipelineVertexInputStateCreateInfo vertex_input_info = { + .vertexBindingDescriptionCount = 0u, + .vertexAttributeDescriptionCount = 0u, + }; + + const vk::PipelineInputAssemblyStateCreateInfo input_assembly = { + .topology = vk::PrimitiveTopology::eTriangleList, + }; + + const vk::Viewport viewport = { + .x = 0.0f, + .y = 0.0f, + .width = 1.0f, + .height = 1.0f, + .minDepth = 0.0f, + .maxDepth = 1.0f, + }; + + const vk::Rect2D scissor = { + .offset = {0, 0}, + .extent = {1, 1}, + }; + + const vk::PipelineViewportStateCreateInfo viewport_info = { + .viewportCount = 1, + .pViewports = &viewport, + .scissorCount = 1, + .pScissors = &scissor, + }; + + const vk::PipelineRasterizationStateCreateInfo raster_state = { + .depthClampEnable = false, + .rasterizerDiscardEnable = false, + .polygonMode = vk::PolygonMode::eFill, + .cullMode = vk::CullModeFlagBits::eBack, + .frontFace = vk::FrontFace::eClockwise, + .depthBiasEnable = false, + .lineWidth = 1.0f, + }; + + const vk::PipelineMultisampleStateCreateInfo multisampling = { + .rasterizationSamples = vk::SampleCountFlagBits::e1, + }; + + const std::array attachments{ + vk::PipelineColorBlendAttachmentState{ + .blendEnable = false, + .colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | + vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA, + }, + }; + + const vk::PipelineColorBlendStateCreateInfo color_blending = { + .logicOpEnable = false, + .logicOp = vk::LogicOp::eCopy, + .attachmentCount = attachments.size(), + .pAttachments = attachments.data(), + .blendConstants = std::array{1.0f, 1.0f, 1.0f, 1.0f}, + }; + + const std::array dynamic_states = { + vk::DynamicState::eViewport, + vk::DynamicState::eScissor, + }; + + const vk::PipelineDynamicStateCreateInfo dynamic_info = { + .dynamicStateCount = static_cast(dynamic_states.size()), + .pDynamicStates = dynamic_states.data(), + }; + + const vk::GraphicsPipelineCreateInfo pipeline_info = { + .pNext = &pipeline_rendering_ci, + .stageCount = static_cast(shaders_ci.size()), + .pStages = shaders_ci.data(), + .pVertexInputState = &vertex_input_info, + .pInputAssemblyState = &input_assembly, + .pViewportState = &viewport_info, + .pRasterizationState = &raster_state, + .pMultisampleState = &multisampling, + .pColorBlendState = &color_blending, + .pDynamicState = &dynamic_info, + .layout = *pp_pipeline_layout, + }; + + auto result = instance.GetDevice().createGraphicsPipelineUnique( + /*pipeline_cache*/ {}, pipeline_info); + if (result.result == vk::Result::eSuccess) { + pp_pipeline = std::move(result.value); + } else { + UNREACHABLE_MSG("Post process pipeline creation failed!"); + } + + // Once pipeline is compiled, we don't need the shader module anymore + instance.GetDevice().destroyShaderModule(vs_module); + instance.GetDevice().destroyShaderModule(fs_module); + + // Create sampler resource + const vk::SamplerCreateInfo sampler_ci = { + .magFilter = vk::Filter::eLinear, + .minFilter = vk::Filter::eLinear, + .mipmapMode = vk::SamplerMipmapMode::eNearest, + .addressModeU = vk::SamplerAddressMode::eClampToEdge, + .addressModeV = vk::SamplerAddressMode::eClampToEdge, + }; + auto [sampler_result, smplr] = instance.GetDevice().createSamplerUnique(sampler_ci); + ASSERT_MSG(sampler_result == vk::Result::eSuccess, "Failed to create sampler: {}", + vk::to_string(sampler_result)); + pp_sampler = std::move(smplr); +} + +Presenter::Presenter(Frontend::WindowSDL& window_, AmdGpu::Liverpool* liverpool_) : window{window_}, liverpool{liverpool_}, instance{window, Config::getGpuId(), Config::vkValidationEnabled(), Config::vkCrashDiagnosticEnabled()}, @@ -115,12 +314,15 @@ RendererVulkan::RendererVulkan(Frontend::WindowSDL& window_, AmdGpu::Liverpool* free_queue.push(&frame); } + CreatePostProcessPipeline(); + // Setup ImGui - ImGui::Core::Initialize(instance, window, num_images, swapchain.GetSurfaceFormat().format); + ImGui::Core::Initialize(instance, window, num_images, + FormatToUnorm(swapchain.GetSurfaceFormat().format)); ImGui::Layer::AddLayer(Common::Singleton::Instance()); } -RendererVulkan::~RendererVulkan() { +Presenter::~Presenter() { ImGui::Layer::RemoveLayer(Common::Singleton::Instance()); draw_scheduler.Finish(); const vk::Device device = instance.GetDevice(); @@ -132,7 +334,7 @@ RendererVulkan::~RendererVulkan() { ImGui::Core::Shutdown(device); } -void RendererVulkan::RecreateFrame(Frame* frame, u32 width, u32 height) { +void Presenter::RecreateFrame(Frame* frame, u32 width, u32 height) { const vk::Device device = instance.GetDevice(); if (frame->image_view) { device.destroyImageView(frame->image_view); @@ -143,6 +345,7 @@ void RendererVulkan::RecreateFrame(Frame* frame, u32 width, u32 height) { const vk::Format format = swapchain.GetSurfaceFormat().format; const vk::ImageCreateInfo image_info = { + .flags = vk::ImageCreateFlagBits::eMutableFormat, .imageType = vk::ImageType::e2D, .format = format, .extent = {width, height, 1}, @@ -177,7 +380,7 @@ void RendererVulkan::RecreateFrame(Frame* frame, u32 width, u32 height) { const vk::ImageViewCreateInfo view_info = { .image = frame->image, .viewType = vk::ImageViewType::e2D, - .format = format, + .format = FormatToUnorm(format), .subresourceRange{ .aspectMask = vk::ImageAspectFlagBits::eColor, .baseMipLevel = 0, @@ -194,7 +397,7 @@ void RendererVulkan::RecreateFrame(Frame* frame, u32 width, u32 height) { frame->height = height; } -bool RendererVulkan::ShowSplash(Frame* frame /*= nullptr*/) { +bool Presenter::ShowSplash(Frame* frame /*= nullptr*/) { const auto* splash = Common::Singleton::Instance(); if (splash->GetImageData().empty()) { return false; @@ -204,6 +407,9 @@ bool RendererVulkan::ShowSplash(Frame* frame /*= nullptr*/) { return false; } + draw_scheduler.EndRendering(); + const auto cmdbuf = draw_scheduler.CommandBuffer(); + if (!frame) { if (!splash_img.has_value()) { VideoCore::ImageInfo info{}; @@ -214,16 +420,74 @@ bool RendererVulkan::ShowSplash(Frame* frame /*= nullptr*/) { info.pitch = splash->GetImageInfo().width; info.guest_address = VAddr(splash->GetImageData().data()); info.guest_size_bytes = splash->GetImageData().size(); + info.mips_layout.emplace_back(splash->GetImageData().size(), + splash->GetImageInfo().width, + splash->GetImageInfo().height, 0); splash_img.emplace(instance, present_scheduler, info); texture_cache.RefreshImage(*splash_img); + + splash_img->Transit(vk::ImageLayout::eTransferSrcOptimal, + vk::AccessFlagBits2::eTransferRead, {}, cmdbuf); } - frame = PrepareFrameInternal(*splash_img); + + frame = GetRenderFrame(); } + + const auto frame_subresources = vk::ImageSubresourceRange{ + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }; + + const auto pre_barrier = + vk::ImageMemoryBarrier2{.srcStageMask = vk::PipelineStageFlagBits2::eTransfer, + .srcAccessMask = vk::AccessFlagBits2::eTransferRead, + .dstStageMask = vk::PipelineStageFlagBits2::eTransfer, + .dstAccessMask = vk::AccessFlagBits2::eTransferWrite, + .oldLayout = vk::ImageLayout::eUndefined, + .newLayout = vk::ImageLayout::eTransferDstOptimal, + .image = frame->image, + .subresourceRange{frame_subresources}}; + + cmdbuf.pipelineBarrier2(vk::DependencyInfo{ + .imageMemoryBarrierCount = 1, + .pImageMemoryBarriers = &pre_barrier, + }); + + cmdbuf.blitImage(splash_img->image, vk::ImageLayout::eTransferSrcOptimal, frame->image, + vk::ImageLayout::eTransferDstOptimal, + MakeImageBlitFit(splash->GetImageInfo().width, splash->GetImageInfo().height, + frame->width, frame->height), + vk::Filter::eLinear); + + const auto post_barrier = + vk::ImageMemoryBarrier2{.srcStageMask = vk::PipelineStageFlagBits2::eTransfer, + .srcAccessMask = vk::AccessFlagBits2::eTransferWrite, + .dstStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput, + .dstAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite, + .oldLayout = vk::ImageLayout::eTransferDstOptimal, + .newLayout = vk::ImageLayout::eGeneral, + .image = frame->image, + .subresourceRange{frame_subresources}}; + + cmdbuf.pipelineBarrier2(vk::DependencyInfo{ + .imageMemoryBarrierCount = 1, + .pImageMemoryBarriers = &post_barrier, + }); + + // Flush frame creation commands. + frame->ready_semaphore = draw_scheduler.GetMasterSemaphore()->Handle(); + frame->ready_tick = draw_scheduler.CurrentTick(); + SubmitInfo info{}; + draw_scheduler.Flush(info); + Present(frame); return true; } -Frame* RendererVulkan::PrepareFrameInternal(VideoCore::Image& image, bool is_eop) { +Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop) { // Request a free presentation frame. Frame* frame = GetRenderFrame(); @@ -234,9 +498,6 @@ Frame* RendererVulkan::PrepareFrameInternal(VideoCore::Image& image, bool is_eop scheduler.EndRendering(); const auto cmdbuf = scheduler.CommandBuffer(); - image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, {}, - cmdbuf); - const auto frame_subresources = vk::ImageSubresourceRange{ .aspectMask = vk::ImageAspectFlagBits::eColor, .baseMipLevel = 0, @@ -244,62 +505,114 @@ Frame* RendererVulkan::PrepareFrameInternal(VideoCore::Image& image, bool is_eop .baseArrayLayer = 0, .layerCount = VK_REMAINING_ARRAY_LAYERS, }; - const std::array pre_barrier{ - vk::ImageMemoryBarrier{ - .srcAccessMask = vk::AccessFlagBits::eTransferRead, - .dstAccessMask = vk::AccessFlagBits::eTransferWrite, - .oldLayout = vk::ImageLayout::eUndefined, - .newLayout = vk::ImageLayout::eTransferDstOptimal, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = frame->image, - .subresourceRange{frame_subresources}, - }, - }; - cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, - vk::PipelineStageFlagBits::eTransfer, vk::DependencyFlagBits::eByRegion, - {}, {}, pre_barrier); - // Clear the frame image before blitting to avoid artifacts. - const vk::ClearColorValue clear_color{std::array{0.0f, 0.0f, 0.0f, 1.0f}}; - cmdbuf.clearColorImage(frame->image, vk::ImageLayout::eTransferDstOptimal, clear_color, - frame_subresources); - - const auto blitBarrier = + const auto pre_barrier = vk::ImageMemoryBarrier2{.srcStageMask = vk::PipelineStageFlagBits2::eTransfer, - .srcAccessMask = vk::AccessFlagBits2::eTransferWrite, - .dstStageMask = vk::PipelineStageFlagBits2::eTransfer, - .dstAccessMask = vk::AccessFlagBits2::eTransferWrite, - .oldLayout = vk::ImageLayout::eTransferDstOptimal, - .newLayout = vk::ImageLayout::eTransferDstOptimal, + .srcAccessMask = vk::AccessFlagBits2::eTransferRead, + .dstStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput, + .dstAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite, + .oldLayout = vk::ImageLayout::eUndefined, + .newLayout = vk::ImageLayout::eColorAttachmentOptimal, .image = frame->image, .subresourceRange{frame_subresources}}; cmdbuf.pipelineBarrier2(vk::DependencyInfo{ .imageMemoryBarrierCount = 1, - .pImageMemoryBarriers = &blitBarrier, + .pImageMemoryBarriers = &pre_barrier, }); - // Post-processing (Anti-aliasing, FSR etc) goes here. For now just blit to the frame image. - cmdbuf.blitImage(image.image, image.last_state.layout, frame->image, - vk::ImageLayout::eTransferDstOptimal, - MakeImageBlitFit(image.info.size.width, image.info.size.height, frame->width, - frame->height), - vk::Filter::eLinear); + if (image_id != VideoCore::NULL_IMAGE_ID) { + auto& image = texture_cache.GetImage(image_id); + image.Transit(vk::ImageLayout::eShaderReadOnlyOptimal, vk::AccessFlagBits2::eShaderRead, {}, + cmdbuf); - const vk::ImageMemoryBarrier post_barrier{ - .srcAccessMask = vk::AccessFlagBits::eTransferWrite, - .dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite, - .oldLayout = vk::ImageLayout::eTransferDstOptimal, - .newLayout = vk::ImageLayout::eGeneral, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = frame->image, - .subresourceRange{frame_subresources}, - }; - cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands, - vk::PipelineStageFlagBits::eAllCommands, - vk::DependencyFlagBits::eByRegion, {}, {}, post_barrier); + static vk::DescriptorImageInfo image_info{ + .sampler = *pp_sampler, + .imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal, + }; + + VideoCore::ImageViewInfo info{}; + info.format = image.info.pixel_format; + if (auto view = image.FindView(info)) { + image_info.imageView = *texture_cache.GetImageView(view).image_view; + } else { + image_info.imageView = *texture_cache.RegisterImageView(image_id, info).image_view; + } + + static const std::array set_writes{ + vk::WriteDescriptorSet{ + .dstSet = VK_NULL_HANDLE, + .dstBinding = 0, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eCombinedImageSampler, + .pImageInfo = &image_info, + }, + }; + + cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, *pp_pipeline); + + const std::array viewports = { + vk::Viewport{ + .x = 0.0f, + .y = 0.0f, + .width = 1.0f * frame->width, + .height = 1.0f * frame->height, + .minDepth = 0.0f, + .maxDepth = 1.0f, + }, + }; + + const std::array scissors = { + vk::Rect2D{ + .offset = {0, 0}, + .extent = {frame->width, frame->height}, + }, + }; + cmdbuf.setViewport(0, viewports); + cmdbuf.setScissor(0, scissors); + + cmdbuf.pushDescriptorSetKHR(vk::PipelineBindPoint::eGraphics, *pp_pipeline_layout, 0, + set_writes); + cmdbuf.pushConstants(*pp_pipeline_layout, vk::ShaderStageFlagBits::eFragment, 0, + sizeof(PostProcessSettings), &pp_settings); + + const std::array attachments = {vk::RenderingAttachmentInfo{ + .imageView = frame->image_view, + .imageLayout = vk::ImageLayout::eColorAttachmentOptimal, + .loadOp = vk::AttachmentLoadOp::eDontCare, + .storeOp = vk::AttachmentStoreOp::eStore, + }}; + + vk::RenderingInfo rendering_info{ + .renderArea = + vk::Rect2D{ + .offset = {0, 0}, + .extent = {frame->width, frame->height}, + }, + .layerCount = 1, + .colorAttachmentCount = attachments.size(), + .pColorAttachments = attachments.data(), + }; + cmdbuf.beginRendering(rendering_info); + cmdbuf.draw(3, 1, 0, 0); + cmdbuf.endRendering(); + } + + const auto post_barrier = + vk::ImageMemoryBarrier2{.srcStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput, + .srcAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite, + .dstStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput, + .dstAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite, + .oldLayout = vk::ImageLayout::eColorAttachmentOptimal, + .newLayout = vk::ImageLayout::eGeneral, + .image = frame->image, + .subresourceRange{frame_subresources}}; + + cmdbuf.pipelineBarrier2(vk::DependencyInfo{ + .imageMemoryBarrierCount = 1, + .pImageMemoryBarriers = &post_barrier, + }); // Flush frame creation commands. frame->ready_semaphore = scheduler.GetMasterSemaphore()->Handle(); @@ -309,7 +622,7 @@ Frame* RendererVulkan::PrepareFrameInternal(VideoCore::Image& image, bool is_eop return frame; } -void RendererVulkan::Present(Frame* frame) { +void Presenter::Present(Frame* frame) { // Recreate the swapchain if the window was resized. if (window.getWidth() != swapchain.GetExtent().width || window.getHeight() != swapchain.GetExtent().height) { @@ -423,7 +736,7 @@ void RendererVulkan::Present(Frame* frame) { DebugState.IncFlipFrameNum(); } -Frame* RendererVulkan::GetRenderFrame() { +Frame* Presenter::GetRenderFrame() { // Wait for free presentation frames Frame* frame; { diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/vk_presenter.h similarity index 79% rename from src/video_core/renderer_vulkan/renderer_vulkan.h rename to src/video_core/renderer_vulkan/vk_presenter.h index a663622fc..cb44a352a 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.h +++ b/src/video_core/renderer_vulkan/vk_presenter.h @@ -40,23 +40,29 @@ enum SchedulerType { class Rasterizer; -class RendererVulkan { +class Presenter { + struct PostProcessSettings { + float gamma = 1.0f; + }; + public: - explicit RendererVulkan(Frontend::WindowSDL& window, AmdGpu::Liverpool* liverpool); - ~RendererVulkan(); + Presenter(Frontend::WindowSDL& window, AmdGpu::Liverpool* liverpool); + ~Presenter(); + + float& GetGammaRef() { + return pp_settings.gamma; + } Frame* PrepareFrame(const Libraries::VideoOut::BufferAttributeGroup& attribute, VAddr cpu_address, bool is_eop) { const auto info = VideoCore::ImageInfo{attribute, cpu_address}; const auto image_id = texture_cache.FindImage(info); texture_cache.UpdateImage(image_id, is_eop ? nullptr : &flip_scheduler); - auto& image = texture_cache.GetImage(image_id); - return PrepareFrameInternal(image, is_eop); + return PrepareFrameInternal(image_id, is_eop); } Frame* PrepareBlankFrame(bool is_eop) { - auto& image = texture_cache.GetImage(VideoCore::NULL_IMAGE_ID); - return PrepareFrameInternal(image, is_eop); + return PrepareFrameInternal(VideoCore::NULL_IMAGE_ID, is_eop); } VideoCore::Image& RegisterVideoOutSurface( @@ -83,10 +89,16 @@ public: } private: - Frame* PrepareFrameInternal(VideoCore::Image& image, bool is_eop = true); + void CreatePostProcessPipeline(); + Frame* PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop = true); Frame* GetRenderFrame(); private: + PostProcessSettings pp_settings{}; + vk::UniquePipeline pp_pipeline{}; + vk::UniquePipelineLayout pp_pipeline_layout{}; + vk::UniqueDescriptorSetLayout pp_desc_set_layout{}; + vk::UniqueSampler pp_sampler{}; Frontend::WindowSDL& window; AmdGpu::Liverpool* liverpool; Instance instance; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 271203233..a8b4728c0 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -271,7 +271,7 @@ void Rasterizer::BeginRendering(const GraphicsPipeline& pipeline) { const auto& hint = liverpool->last_cb_extent[col_buf_id]; VideoCore::ImageInfo image_info{col_buf, hint}; - VideoCore::ImageViewInfo view_info{col_buf, false /*!!image.info.usage.vo_buffer*/}; + VideoCore::ImageViewInfo view_info{col_buf}; const auto& image_view = texture_cache.FindRenderTarget(image_info, view_info); const auto& image = texture_cache.GetImage(image_view.image_id); state.width = std::min(state.width, image.info.size.width); diff --git a/src/video_core/texture_cache/image_view.cpp b/src/video_core/texture_cache/image_view.cpp index 599235998..8bde37941 100644 --- a/src/video_core/texture_cache/image_view.cpp +++ b/src/video_core/texture_cache/image_view.cpp @@ -125,15 +125,14 @@ ImageViewInfo::ImageViewInfo(const AmdGpu::Image& image, const Shader::ImageReso } } -ImageViewInfo::ImageViewInfo(const AmdGpu::Liverpool::ColorBuffer& col_buffer, - bool is_vo_surface) noexcept { +ImageViewInfo::ImageViewInfo(const AmdGpu::Liverpool::ColorBuffer& col_buffer) noexcept { const auto base_format = Vulkan::LiverpoolToVK::SurfaceFormat(col_buffer.info.format, col_buffer.NumFormat()); range.base.layer = col_buffer.view.slice_start; range.extent.layers = col_buffer.NumSlices() - range.base.layer; type = range.extent.layers > 1 ? vk::ImageViewType::e2DArray : vk::ImageViewType::e2D; - format = Vulkan::LiverpoolToVK::AdjustColorBufferFormat( - base_format, col_buffer.info.comp_swap.Value(), is_vo_surface); + format = Vulkan::LiverpoolToVK::AdjustColorBufferFormat(base_format, + col_buffer.info.comp_swap.Value()); } ImageViewInfo::ImageViewInfo(const AmdGpu::Liverpool::DepthBuffer& depth_buffer, diff --git a/src/video_core/texture_cache/image_view.h b/src/video_core/texture_cache/image_view.h index ba8d2c72b..23c703d23 100644 --- a/src/video_core/texture_cache/image_view.h +++ b/src/video_core/texture_cache/image_view.h @@ -19,7 +19,7 @@ namespace VideoCore { struct ImageViewInfo { ImageViewInfo() = default; ImageViewInfo(const AmdGpu::Image& image, const Shader::ImageResource& desc) noexcept; - ImageViewInfo(const AmdGpu::Liverpool::ColorBuffer& col_buffer, bool is_vo_surface) noexcept; + ImageViewInfo(const AmdGpu::Liverpool::ColorBuffer& col_buffer) noexcept; ImageViewInfo(const AmdGpu::Liverpool::DepthBuffer& depth_buffer, AmdGpu::Liverpool::DepthView view, AmdGpu::Liverpool::DepthControl ctl); diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index 27c288885..da7d0921b 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -28,7 +28,7 @@ TextureCache::TextureCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& info.num_bits = 32; info.UpdateSize(); const ImageId null_id = slot_images.insert(instance, scheduler, info); - ASSERT(null_id.index == 0); + ASSERT(null_id.index == NULL_IMAGE_ID.index); const vk::Image& null_image = slot_images[null_id].image; Vulkan::SetObjectName(instance.GetDevice(), null_image, "Null Image"); slot_images[null_id].flags = ImageFlagBits::Tracked; @@ -36,7 +36,7 @@ TextureCache::TextureCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& ImageViewInfo view_info; const auto null_view_id = slot_image_views.insert(instance, view_info, slot_images[null_id], null_id); - ASSERT(null_view_id.index == 0); + ASSERT(null_view_id.index == NULL_IMAGE_VIEW_ID.index); const vk::ImageView& null_image_view = slot_image_views[null_view_id].image_view.get(); Vulkan::SetObjectName(instance.GetDevice(), null_image_view, "Null Image View"); } diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 3bbfd952c..ef9c0efb7 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -102,6 +102,9 @@ public: return slot_image_views[id]; } + /// Registers an image view for provided image + ImageView& RegisterImageView(ImageId image_id, const ImageViewInfo& view_info); + bool IsMeta(VAddr address) const { return surface_metas.contains(address); } @@ -181,9 +184,6 @@ private: } } - /// Registers an image view for provided image - ImageView& RegisterImageView(ImageId image_id, const ImageViewInfo& view_info); - /// Create an image from the given parameters [[nodiscard]] ImageId InsertImage(const ImageInfo& info, VAddr cpu_addr); From c83ac654ce2ae0c4efabef58630c931563306aea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A5IGA?= <164882787+Xphalnos@users.noreply.github.com> Date: Thu, 21 Nov 2024 11:08:22 +0100 Subject: [PATCH 034/549] Bump to Clang 18 (#1549) --- .ci/clang-format.sh | 2 +- .github/workflows/build.yml | 6 +++--- src/core/devtools/widget/imgui_memory_editor.h | 2 +- src/core/libraries/kernel/event_flag/event_flag_obj.h | 2 +- src/shader_recompiler/ir/breadth_first_search.h | 8 ++++---- src/shader_recompiler/ir/opcodes.h | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.ci/clang-format.sh b/.ci/clang-format.sh index 0ccd4062d..c0d8c2c2d 100755 --- a/.ci/clang-format.sh +++ b/.ci/clang-format.sh @@ -10,7 +10,7 @@ if grep -nrI '\s$' src *.yml *.txt *.md Doxyfile .gitignore .gitmodules .ci* dis fi # Default clang-format points to default 3.5 version one -CLANG_FORMAT=clang-format-17 +CLANG_FORMAT=clang-format-18 $CLANG_FORMAT --version if [ "$GITHUB_EVENT_NAME" = "pull_request" ]; then diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a3b3ee8ec..53b4c7386 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,7 @@ jobs: continue-on-error: true steps: - uses: actions/checkout@v4 - - uses: fsfe/reuse-action@v4 + - uses: fsfe/reuse-action@v5 clang-format: runs-on: ubuntu-latest @@ -30,9 +30,9 @@ jobs: - name: Install run: | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - - sudo add-apt-repository 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-17 main' + sudo add-apt-repository 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main' sudo apt update - sudo apt install clang-format-17 + sudo apt install clang-format-18 - name: Build env: COMMIT_RANGE: ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} diff --git a/src/core/devtools/widget/imgui_memory_editor.h b/src/core/devtools/widget/imgui_memory_editor.h index fb1f46767..f90387a77 100644 --- a/src/core/devtools/widget/imgui_memory_editor.h +++ b/src/core/devtools/widget/imgui_memory_editor.h @@ -929,7 +929,7 @@ struct MemoryEditor { default: case ImGuiDataType_COUNT: break; - } // Switch + } // Switch IM_ASSERT(0); // Shouldn't reach } }; diff --git a/src/core/libraries/kernel/event_flag/event_flag_obj.h b/src/core/libraries/kernel/event_flag/event_flag_obj.h index 8d1624e2b..0454b4d78 100644 --- a/src/core/libraries/kernel/event_flag/event_flag_obj.h +++ b/src/core/libraries/kernel/event_flag/event_flag_obj.h @@ -22,7 +22,7 @@ public: EventFlagInternal(const std::string& name, ThreadMode thread_mode, QueueMode queue_mode, uint64_t bits) - : m_name(name), m_thread_mode(thread_mode), m_queue_mode(queue_mode), m_bits(bits){}; + : m_name(name), m_thread_mode(thread_mode), m_queue_mode(queue_mode), m_bits(bits) {}; int Wait(u64 bits, WaitMode wait_mode, ClearMode clear_mode, u64* result, u32* ptr_micros); int Poll(u64 bits, WaitMode wait_mode, ClearMode clear_mode, u64* result); diff --git a/src/shader_recompiler/ir/breadth_first_search.h b/src/shader_recompiler/ir/breadth_first_search.h index b042ae3d6..b3ed87204 100644 --- a/src/shader_recompiler/ir/breadth_first_search.h +++ b/src/shader_recompiler/ir/breadth_first_search.h @@ -14,8 +14,8 @@ namespace Shader::IR { // Use typename Instruction so the function can be used to return either const or mutable // Insts depending on the context. template -auto BreadthFirstSearch(Instruction* inst, Pred&& pred) - -> std::invoke_result_t { +auto BreadthFirstSearch(Instruction* inst, + Pred&& pred) -> std::invoke_result_t { // Most often case the instruction is the desired already. if (std::optional result = pred(inst)) { return result; @@ -53,8 +53,8 @@ auto BreadthFirstSearch(Instruction* inst, Pred&& pred) } template -auto BreadthFirstSearch(const Value& value, Pred&& pred) - -> std::invoke_result_t { +auto BreadthFirstSearch(const Value& value, + Pred&& pred) -> std::invoke_result_t { if (value.IsImmediate()) { // Nothing to do with immediates return std::nullopt; diff --git a/src/shader_recompiler/ir/opcodes.h b/src/shader_recompiler/ir/opcodes.h index 200d7f421..be640297a 100644 --- a/src/shader_recompiler/ir/opcodes.h +++ b/src/shader_recompiler/ir/opcodes.h @@ -53,7 +53,7 @@ constexpr Type F64x3{Type::F64x3}; constexpr Type F64x4{Type::F64x4}; constexpr Type StringLiteral{Type::StringLiteral}; -constexpr OpcodeMeta META_TABLE[]{ +constexpr OpcodeMeta META_TABLE[] { #define OPCODE(name_token, type_token, ...) \ { \ .name{#name_token}, \ From c55d7fbb6a1c2091b67a1d9441e097db27033ff0 Mon Sep 17 00:00:00 2001 From: psucien <168137814+psucien@users.noreply.github.com> Date: Thu, 21 Nov 2024 11:08:52 +0100 Subject: [PATCH 035/549] Proper MSAA surfaces support (#1560) --- .../renderer_vulkan/vk_pipeline_cache.cpp | 10 ++ src/video_core/texture_cache/image_info.cpp | 108 +++++++++++++++++- .../texture_cache/texture_cache.cpp | 4 + 3 files changed, 116 insertions(+), 6 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 245fddb65..960415d09 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -357,6 +357,8 @@ bool PipelineCache::RefreshGraphicsKey() { } } + u32 num_samples = 1u; + // Second pass to fill remain CB pipeline key data for (auto cb = 0u, remapped_cb = 0u; cb < Liverpool::NumColorBuffers; ++cb) { auto const& col_buf = regs.color_buffers[cb]; @@ -373,8 +375,16 @@ bool PipelineCache::RefreshGraphicsKey() { key.write_masks[remapped_cb] = vk::ColorComponentFlags{regs.color_target_mask.GetMask(cb)}; key.cb_shader_mask.SetMask(remapped_cb, regs.color_shader_mask.GetMask(cb)); + num_samples = std::max(num_samples, 1u << col_buf.attrib.num_samples_log2); + ++remapped_cb; } + + // It seems that the number of samples > 1 set in the AA config doesn't mean we're always + // rendering with MSAA, so we need to derive MS ratio from the CB settings. + num_samples = std::max(num_samples, regs.depth_buffer.NumSamples()); + key.num_samples = num_samples; + return true; } diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index 0cf9f5605..2956a2a3e 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -47,7 +47,7 @@ static vk::ImageType ConvertImageType(AmdGpu::ImageType type) noexcept { // clang-format off // The table of macro tiles parameters for given tiling index (row) and bpp (column) -static constexpr std::array macro_tile_extents{ +static constexpr std::array macro_tile_extents_x1{ std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, // 00 std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 01 std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 02 @@ -66,26 +66,123 @@ static constexpr std::array macro_tile_extents{ std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, // 0F std::pair{256u, 256u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, // 10 std::pair{256u, 256u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, // 11 - std::pair{256u, 256u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 12 + std::pair{256u, 256u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, // 12 std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 13 std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 14 std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 15 std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 16 std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 17 - std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{128u, 64u}, // 18 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 18 std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 19 std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 1A }; + +static constexpr std::array macro_tile_extents_x2{ + std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, // 00 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 01 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 02 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 03 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 04 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 05 + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 06 + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 07 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 08 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 09 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0A + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0B + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0C + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 0D + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0E + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0F + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 10 + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 11 + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 12 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 13 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 14 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 15 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 16 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 17 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 18 + std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 19 + std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 1A +}; + +static constexpr std::array macro_tile_extents_x4{ + std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, // 00 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 01 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 02 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 03 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 04 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 05 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 06 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 07 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 08 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 09 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0A + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0B + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0C + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 0D + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0E + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0F + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 10 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 11 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 12 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 13 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 14 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 15 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 16 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 17 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 18 + std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 19 + std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 1A +}; + +static constexpr std::array macro_tile_extents_x8{ + std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, // 00 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 01 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 02 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 03 + std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 04 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 05 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 06 + std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 07 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 08 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 09 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0A + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0B + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0C + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 0D + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0E + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0F + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 10 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 11 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 12 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 13 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 14 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 15 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 16 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 17 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 18 + std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 19 + std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 1A +}; + +static constexpr std::array macro_tile_extents{ + macro_tile_extents_x1, + macro_tile_extents_x2, + macro_tile_extents_x4, + macro_tile_extents_x8, +}; // clang-format on static constexpr std::pair micro_tile_extent{8u, 8u}; static constexpr auto hw_pipe_interleave = 256u; static constexpr std::pair GetMacroTileExtents(u32 tiling_idx, u32 bpp, u32 num_samples) { - ASSERT(num_samples == 1); + ASSERT(num_samples <= 8); const auto row = tiling_idx * 5; const auto column = std::bit_width(bpp) - 4; // bpps are 8, 16, 32, 64, 128 - return macro_tile_extents[row + column]; + return (macro_tile_extents[std::log2(num_samples)])[row + column]; } static constexpr std::pair ImageSizeLinearAligned(u32 pitch, u32 height, u32 bpp, @@ -286,7 +383,6 @@ void ImageInfo::UpdateSize() { case AmdGpu::TilingMode::Texture_MacroTiled: case AmdGpu::TilingMode::Depth_MacroTiled: { ASSERT(!props.is_block); - ASSERT(num_samples == 1); std::tie(mip_info.pitch, mip_info.size) = ImageSizeMacroTiled(mip_w, mip_h, bpp, num_samples, tiling_idx, mip); break; diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index da7d0921b..b45025d53 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -376,6 +376,10 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule return; } + if (image.info.num_samples > 1) { + return; + } + const auto& num_layers = image.info.resources.layers; const auto& num_mips = image.info.resources.levels; ASSERT(num_mips == image.info.mips_layout.size()); From 5054827441bd8dfa3bf890dae01c5b4d438a652c Mon Sep 17 00:00:00 2001 From: Florian Piesche Date: Thu, 21 Nov 2024 10:09:10 +0000 Subject: [PATCH 036/549] Don't use github as a CDN for Discord RPC icon (#1496) [jsdelivr](https://www.jsdelivr.com/?docs=gh) is a free CDN for opensource projects that offers mirroring and CDN services for files on GitHub with just a minor URL change, backed by a number of major organizations in the CDN industry. --- src/common/discord_rpc_handler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/discord_rpc_handler.cpp b/src/common/discord_rpc_handler.cpp index ce99d683b..448cb4a7f 100644 --- a/src/common/discord_rpc_handler.cpp +++ b/src/common/discord_rpc_handler.cpp @@ -17,7 +17,7 @@ void RPC::init() { void RPC::setStatusIdling() { DiscordRichPresence rpc{}; - rpc.largeImageKey = "https://github.com/shadps4-emu/shadPS4/raw/main/.github/shadps4.png"; + rpc.largeImageKey = "https://cdn.jsdelivr.net/gh/shadps4-emu/shadPS4@main/.github/shadps4.png"; rpc.largeImageText = "shadPS4 is a PS4 emulator"; rpc.startTimestamp = startTimestamp; rpc.details = "Idle"; From 2a0629477b44c5747294353e53c74e09316a8570 Mon Sep 17 00:00:00 2001 From: Marat Idrisov Date: Thu, 21 Nov 2024 20:02:30 +0300 Subject: [PATCH 037/549] [MacOS] Enable game mode when switching to full screen mode (#1541) * GH-1515: enable game mode when switching to full screen mode * GH-1515: minor fix * GH-1515: add MacOSBundleInfo.plist.in to REUSE.toml --- .gitignore | 3 +++ CMakeLists.txt | 5 +++- REUSE.toml | 1 + dist/MacOSBundleInfo.plist.in | 46 +++++++++++++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 dist/MacOSBundleInfo.plist.in diff --git a/.gitignore b/.gitignore index 61d9e32e1..c331f9e70 100644 --- a/.gitignore +++ b/.gitignore @@ -414,3 +414,6 @@ FodyWeavers.xsd # for macOS **/.DS_Store + +# JetBrains +.idea diff --git a/CMakeLists.txt b/CMakeLists.txt index 47cfd8595..35fc35f23 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -921,7 +921,10 @@ if (ENABLE_QT_GUI) set_target_properties(shadps4 PROPERTIES # WIN32_EXECUTABLE ON MACOSX_BUNDLE ON - MACOSX_BUNDLE_ICON_FILE shadPS4.icns) + MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/dist/MacOSBundleInfo.plist.in" + MACOSX_BUNDLE_ICON_FILE "shadPS4.icns" + MACOSX_BUNDLE_SHORT_VERSION_STRING "0.4.1" + ) set_source_files_properties(src/images/shadPS4.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources) diff --git a/REUSE.toml b/REUSE.toml index 405156231..7b2862e53 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -7,6 +7,7 @@ path = [ ".github/FUNDING.yml", ".github/shadps4.png", ".gitmodules", + "dist/MacOSBundleInfo.plist.in", "dist/net.shadps4.shadPS4.desktop", "dist/net.shadps4.shadPS4_metadata.pot", "dist/net.shadps4.shadPS4.metainfo.xml", diff --git a/dist/MacOSBundleInfo.plist.in b/dist/MacOSBundleInfo.plist.in new file mode 100644 index 000000000..70cbfb4ab --- /dev/null +++ b/dist/MacOSBundleInfo.plist.in @@ -0,0 +1,46 @@ + + + + + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + + CFBundleName + shadps4 + CFBundleIdentifier + com.shadps4-emu.shadps4 + CFBundleExecutable + shadps4 + + CFBundleVersion + 1.0.0 + CFBundleShortVersionString + ${MACOSX_BUNDLE_SHORT_VERSION_STRING} + + LSMinimumSystemVersion + ${CMAKE_OSX_DEPLOYMENT_TARGET} + LSApplicationCategoryType + public.app-category.games + GCSupportsGameMode + + + NSHumanReadableCopyright + + + CFBundleIconFile + ${MACOSX_BUNDLE_ICON_FILE} + + CFBundleDevelopmentRegion + en + CFBundleAllowMixedLocalizations + + + NSPrincipalClass + NSApplication + + NSSupportsAutomaticGraphicsSwitching + + + From e968b1c23f53d72a099423bf9dd3be5b71e83b2e Mon Sep 17 00:00:00 2001 From: "Daniel R." <47796739+polybiusproxy@users.noreply.github.com> Date: Thu, 21 Nov 2024 19:24:13 +0100 Subject: [PATCH 038/549] video_core/amdgpu: heuristic for shader binary info Games can strip the first shader instruction (meant for debugging) which we rely on for obtaining shader information (e.g. LittleBigPlanet 3). For this reason, we start a search through the code start until we arrive at the shader binary info. --- src/shader_recompiler/recompiler.cpp | 4 +- src/video_core/amdgpu/liverpool.h | 42 ++++++++++++++----- .../renderer_vulkan/vk_pipeline_cache.cpp | 4 +- 3 files changed, 37 insertions(+), 13 deletions(-) diff --git a/src/shader_recompiler/recompiler.cpp b/src/shader_recompiler/recompiler.cpp index 19579f665..64f842c42 100644 --- a/src/shader_recompiler/recompiler.cpp +++ b/src/shader_recompiler/recompiler.cpp @@ -32,7 +32,9 @@ IR::Program TranslateProgram(std::span code, Pools& pools, Info& info const RuntimeInfo& runtime_info, const Profile& profile) { // Ensure first instruction is expected. constexpr u32 token_mov_vcchi = 0xBEEB03FF; - ASSERT_MSG(code[0] == token_mov_vcchi, "First instruction is not s_mov_b32 vcc_hi, #imm"); + if (code[0] != token_mov_vcchi) { + LOG_WARNING(Render_Recompiler, "First instruction is not s_mov_b32 vcc_hi, #imm"); + } Gcn::GcnCodeSlice slice(code.data(), code.data() + code.size()); Gcn::GcnDecodeContext decoder; diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index d94e4329a..0595a242c 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -88,6 +88,32 @@ struct Liverpool { } }; + static const BinaryInfo& SearchBinaryInfo(const u32* code, size_t search_limit = 0x1000) { + constexpr u32 token_mov_vcchi = 0xBEEB03FF; + + if (code[0] == token_mov_vcchi) { + const auto* info = std::bit_cast(code + (code[1] + 1) * 2); + if (info->Valid()) { + return *info; + } + } + + // First instruction is not s_mov_b32 vcc_hi, #imm, + // which means we cannot get the binary info via said instruction. + // The easiest solution is to iterate through each dword and break + // on the first instance of the binary info. + constexpr size_t signature_size = sizeof(BinaryInfo::signature_ref) / sizeof(u8); + const u32* end = code + search_limit; + + for (const u32* it = code; it < end; ++it) { + if (const BinaryInfo* info = std::bit_cast(it); info->Valid()) { + return *info; + } + } + + UNREACHABLE_MSG("Shader binary info not found."); + } + struct ShaderProgram { u32 address_lo; BitField<0, 8, u32> address_hi; @@ -113,8 +139,7 @@ struct Liverpool { std::span Code() const { const u32* code = Address(); - BinaryInfo bininfo; - std::memcpy(&bininfo, code + (code[1] + 1) * 2, sizeof(bininfo)); + const BinaryInfo& bininfo = SearchBinaryInfo(code); const u32 num_dwords = bininfo.length / sizeof(u32); return std::span{code, num_dwords}; } @@ -166,27 +191,24 @@ struct Liverpool { std::span Code() const { const u32* code = Address(); - BinaryInfo bininfo; - std::memcpy(&bininfo, code + (code[1] + 1) * 2, sizeof(bininfo)); + const BinaryInfo& bininfo = SearchBinaryInfo(code); const u32 num_dwords = bininfo.length / sizeof(u32); return std::span{code, num_dwords}; } }; template - static constexpr auto* GetBinaryInfo(const Shader& sh) { + static constexpr const BinaryInfo& GetBinaryInfo(const Shader& sh) { const auto* code = sh.template Address(); - const auto* bininfo = std::bit_cast(code + (code[1] + 1) * 2); - // ASSERT_MSG(bininfo->Valid(), "Invalid shader binary header"); - return bininfo; + return SearchBinaryInfo(code); } static constexpr Shader::ShaderParams GetParams(const auto& sh) { - auto* bininfo = GetBinaryInfo(sh); + auto& bininfo = GetBinaryInfo(sh); return { .user_data = sh.user_data, .code = sh.Code(), - .hash = bininfo->shader_hash, + .hash = bininfo.shader_hash, }; } diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 960415d09..b576d4780 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -292,8 +292,8 @@ bool PipelineCache::RefreshGraphicsKey() { return false; } - const auto* bininfo = Liverpool::GetBinaryInfo(*pgm); - if (!bininfo->Valid()) { + const auto& bininfo = Liverpool::GetBinaryInfo(*pgm); + if (!bininfo.Valid()) { LOG_WARNING(Render_Vulkan, "Invalid binary info structure!"); key.stage_hashes[stage_out_idx] = 0; infos[stage_out_idx] = nullptr; From 6904764aab4ef8ce8f17e8bc82a95deb1708d57d Mon Sep 17 00:00:00 2001 From: "Daniel R." <47796739+polybiusproxy@users.noreply.github.com> Date: Thu, 21 Nov 2024 19:52:48 +0100 Subject: [PATCH 039/549] shader_recompiler/frontend: implement `V_MIN3_U32` --- src/shader_recompiler/frontend/translate/translate.h | 1 + src/shader_recompiler/frontend/translate/vector_alu.cpp | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index fd5a58899..f04038909 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -221,6 +221,7 @@ public: void V_FMA_F64(const GcnInst& inst); void V_MIN3_F32(const GcnInst& inst); void V_MIN3_I32(const GcnInst& inst); + void V_MIN3_U32(const GcnInst& inst); void V_MAX3_F32(const GcnInst& inst); void V_MAX3_U32(bool is_signed, const GcnInst& inst); void V_MED3_F32(const GcnInst& inst); diff --git a/src/shader_recompiler/frontend/translate/vector_alu.cpp b/src/shader_recompiler/frontend/translate/vector_alu.cpp index 7ac3131c5..eb90c256e 100644 --- a/src/shader_recompiler/frontend/translate/vector_alu.cpp +++ b/src/shader_recompiler/frontend/translate/vector_alu.cpp @@ -347,6 +347,8 @@ void Translator::EmitVectorAlu(const GcnInst& inst) { return V_MIN3_F32(inst); case Opcode::V_MIN3_I32: return V_MIN3_I32(inst); + case Opcode::V_MIN3_U32: + return V_MIN3_U32(inst); case Opcode::V_MAX3_F32: return V_MAX3_F32(inst); case Opcode::V_MAX3_I32: @@ -1064,6 +1066,13 @@ void Translator::V_MIN3_I32(const GcnInst& inst) { SetDst(inst.dst[0], ir.SMin(src0, ir.SMin(src1, src2))); } +void Translator::V_MIN3_U32(const GcnInst& inst) { + const IR::U32 src0{GetSrc(inst.src[0])}; + const IR::U32 src1{GetSrc(inst.src[1])}; + const IR::U32 src2{GetSrc(inst.src[2])}; + SetDst(inst.dst[0], ir.UMin(src0, ir.UMin(src1, src2))); +} + void Translator::V_MAX3_F32(const GcnInst& inst) { const IR::F32 src0{GetSrc(inst.src[0])}; const IR::F32 src1{GetSrc(inst.src[1])}; From c4506da0ae0fbd218e5ec3d2dc0d94a2ea788ec2 Mon Sep 17 00:00:00 2001 From: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> Date: Thu, 21 Nov 2024 22:59:38 +0200 Subject: [PATCH 040/549] kernel: Rewrite pthread emulation (#1440) * libkernel: Cleanup some function places * kernel: Refactor thread functions * kernel: It builds * kernel: Fix a bunch of bugs, kernel thread heap * kernel: File cleanup pt1 * File cleanup pt2 * File cleanup pt3 * File cleanup pt4 * kernel: Add missing funcs * kernel: Add basic exceptions for linux * gnmdriver: Add workload functions * kernel: Fix new pthreads code on macOS. (#1441) * kernel: Downgrade edeadlk to log * gnmdriver: Add sceGnmSubmitCommandBuffersForWorkload * exception: Add context register population for macOS. (#1444) * kernel: Pthread rewrite touchups for Windows * kernel: Multiplatform thread implementation * mutex: Remove spamming log * pthread_spec: Make assert into a log * pthread_spec: Zero initialize array * Attempt to fix non-Windows builds * hotfix: change incorrect NID for scePthreadAttrSetaffinity * scePthreadAttrSetaffinity implementation * Attempt to fix Linux * windows: Address a bunch of address space problems * address_space: Fix unmap of region surrounded by placeholders * libs: Reduce logging * pthread: Implement condvar with waitable atomics and sleepqueue * sleepq: Separate and make faster * time: Remove delay execution * Causes high cpu usage in Tohou Luna Nights * kernel: Cleanup files again * pthread: Add missing include * semaphore: Use binary_semaphore instead of condvar * Seems more reliable * libraries/sysmodule: log module on `sceSysmoduleIsLoaded` * libraries/kernel: implement `scePthreadSetPrio` --------- Co-authored-by: squidbus <175574877+squidbus@users.noreply.github.com> Co-authored-by: Daniel R. <47796739+polybiusproxy@users.noreply.github.com> --- CMakeLists.txt | 53 +- src/common/debug.h | 2 +- src/common/slab_heap.h | 163 ++ src/common/slot_vector.h | 3 - src/common/spin_lock.cpp | 53 + src/common/spin_lock.h | 33 + src/core/address_space.cpp | 208 +- src/core/debug_state.cpp | 2 +- src/core/file_sys/file.cpp | 114 ++ src/core/libraries/audio3d/audio3d.cpp | 8 +- src/core/libraries/audio3d/audio3d_impl.cpp | 2 +- src/core/libraries/avplayer/avplayer.cpp | 10 +- src/core/libraries/avplayer/avplayer.h | 4 +- .../libraries/avplayer/avplayer_common.cpp | 18 +- src/core/libraries/avplayer/avplayer_common.h | 10 +- .../avplayer/avplayer_file_streamer.cpp | 10 +- .../avplayer/avplayer_file_streamer.h | 6 +- src/core/libraries/avplayer/avplayer_impl.cpp | 37 +- src/core/libraries/avplayer/avplayer_impl.h | 8 +- .../libraries/avplayer/avplayer_source.cpp | 46 +- src/core/libraries/avplayer/avplayer_source.h | 24 +- .../libraries/avplayer/avplayer_state.cpp | 24 +- src/core/libraries/avplayer/avplayer_state.h | 13 +- src/core/libraries/fiber/fiber.cpp | 9 +- src/core/libraries/gnmdriver/gnmdriver.cpp | 58 +- src/core/libraries/gnmdriver/gnmdriver.h | 18 +- src/core/libraries/ime/ime.cpp | 12 +- src/core/libraries/ime/ime_dialog_ui.cpp | 13 +- src/core/libraries/kernel/cpu_management.cpp | 15 - .../kernel/{event_queues.cpp => equeue.cpp} | 153 +- .../kernel/{event_queue.h => equeue.h} | 21 +- .../libraries/kernel/event_flag/event_flag.h | 40 - .../kernel/event_flag/event_flag_codes.h | 14 - .../kernel/event_flag/event_flag_obj.cpp | 111 -- .../kernel/event_flag/event_flag_obj.h | 44 - src/core/libraries/kernel/event_queue.cpp | 150 -- src/core/libraries/kernel/event_queues.h | 26 - src/core/libraries/kernel/file_system.cpp | 6 +- src/core/libraries/kernel/file_system.h | 9 +- src/core/libraries/kernel/kernel.cpp | 250 +++ src/core/libraries/kernel/kernel.h | 56 + src/core/libraries/kernel/libkernel.cpp | 509 ----- src/core/libraries/kernel/libkernel.h | 42 - .../{memory_management.cpp => memory.cpp} | 124 +- .../kernel/{memory_management.h => memory.h} | 12 + src/core/libraries/kernel/process.cpp | 140 ++ .../kernel/{cpu_management.h => process.h} | 8 + .../libraries/kernel/thread_management.cpp | 1716 ----------------- src/core/libraries/kernel/thread_management.h | 225 --- src/core/libraries/kernel/threads.cpp | 22 + src/core/libraries/kernel/threads.h | 72 + src/core/libraries/kernel/threads/condvar.cpp | 360 ++++ .../{event_flag => threads}/event_flag.cpp | 171 +- .../libraries/kernel/threads/exception.cpp | 131 ++ src/core/libraries/kernel/threads/exception.h | 86 + src/core/libraries/kernel/threads/keys.cpp | 50 - src/core/libraries/kernel/threads/mutex.cpp | 468 +++++ src/core/libraries/kernel/threads/pthread.cpp | 526 +++++ src/core/libraries/kernel/threads/pthread.h | 349 ++++ .../libraries/kernel/threads/pthread_attr.cpp | 345 ++++ .../kernel/threads/pthread_clean.cpp | 54 + .../libraries/kernel/threads/pthread_spec.cpp | 158 ++ src/core/libraries/kernel/threads/rwlock.cpp | 543 +++--- .../libraries/kernel/threads/semaphore.cpp | 165 +- src/core/libraries/kernel/threads/sleepq.cpp | 101 + src/core/libraries/kernel/threads/sleepq.h | 38 + src/core/libraries/kernel/threads/stack.cpp | 141 ++ src/core/libraries/kernel/threads/tcb.cpp | 96 + .../libraries/kernel/threads/thread_state.cpp | 172 ++ .../libraries/kernel/threads/thread_state.h | 87 + src/core/libraries/kernel/threads/threads.h | 20 - .../kernel/{time_management.cpp => time.cpp} | 44 +- .../kernel/{time_management.h => time.h} | 21 +- src/core/libraries/libs.cpp | 6 +- src/core/libraries/libs.h | 37 +- src/core/libraries/network/net_ctl_obj.cpp | 81 +- src/core/libraries/network/net_ctl_obj.h | 17 +- src/core/libraries/network/netctl.cpp | 15 +- src/core/libraries/network/netctl.h | 2 +- src/core/libraries/ngs2/ngs2_impl.cpp | 2 +- src/core/libraries/np_manager/np_manager.cpp | 10 +- src/core/libraries/pad/pad.cpp | 1 + src/core/libraries/rtc/rtc.cpp | 8 +- src/core/libraries/system/sysmodule.cpp | 4 +- src/core/libraries/system/sysmodule.h | 2 +- src/core/libraries/videoout/driver.cpp | 3 +- src/core/libraries/videoout/video_out.h | 2 +- src/core/linker.cpp | 170 +- src/core/linker.h | 45 +- src/core/memory.cpp | 2 +- src/core/memory.h | 13 +- src/core/module.cpp | 46 +- src/core/module.h | 11 +- src/core/thread.cpp | 46 + src/core/thread.h | 33 + src/core/tls.cpp | 78 +- src/core/tls.h | 11 +- src/emulator.cpp | 6 +- src/input/controller.cpp | 2 +- .../frontend/translate/data_share.cpp | 5 +- src/video_core/amdgpu/liverpool.cpp | 2 +- src/video_core/page_manager.cpp | 15 +- .../texture_cache/texture_cache.cpp | 2 +- src/video_core/texture_cache/texture_cache.h | 4 +- 104 files changed, 5554 insertions(+), 3979 deletions(-) create mode 100644 src/common/slab_heap.h create mode 100755 src/common/spin_lock.cpp create mode 100755 src/common/spin_lock.h create mode 100644 src/core/file_sys/file.cpp delete mode 100644 src/core/libraries/kernel/cpu_management.cpp rename src/core/libraries/kernel/{event_queues.cpp => equeue.cpp} (56%) rename src/core/libraries/kernel/{event_queue.h => equeue.h} (89%) delete mode 100644 src/core/libraries/kernel/event_flag/event_flag.h delete mode 100644 src/core/libraries/kernel/event_flag/event_flag_codes.h delete mode 100644 src/core/libraries/kernel/event_flag/event_flag_obj.cpp delete mode 100644 src/core/libraries/kernel/event_flag/event_flag_obj.h delete mode 100644 src/core/libraries/kernel/event_queue.cpp delete mode 100644 src/core/libraries/kernel/event_queues.h create mode 100644 src/core/libraries/kernel/kernel.cpp create mode 100644 src/core/libraries/kernel/kernel.h delete mode 100644 src/core/libraries/kernel/libkernel.cpp delete mode 100644 src/core/libraries/kernel/libkernel.h rename src/core/libraries/kernel/{memory_management.cpp => memory.cpp} (75%) rename src/core/libraries/kernel/{memory_management.h => memory.h} (96%) create mode 100644 src/core/libraries/kernel/process.cpp rename src/core/libraries/kernel/{cpu_management.h => process.h} (60%) delete mode 100644 src/core/libraries/kernel/thread_management.cpp delete mode 100644 src/core/libraries/kernel/thread_management.h create mode 100644 src/core/libraries/kernel/threads.cpp create mode 100644 src/core/libraries/kernel/threads.h create mode 100644 src/core/libraries/kernel/threads/condvar.cpp rename src/core/libraries/kernel/{event_flag => threads}/event_flag.cpp (56%) create mode 100644 src/core/libraries/kernel/threads/exception.cpp create mode 100644 src/core/libraries/kernel/threads/exception.h delete mode 100644 src/core/libraries/kernel/threads/keys.cpp create mode 100644 src/core/libraries/kernel/threads/mutex.cpp create mode 100644 src/core/libraries/kernel/threads/pthread.cpp create mode 100644 src/core/libraries/kernel/threads/pthread.h create mode 100644 src/core/libraries/kernel/threads/pthread_attr.cpp create mode 100644 src/core/libraries/kernel/threads/pthread_clean.cpp create mode 100644 src/core/libraries/kernel/threads/pthread_spec.cpp create mode 100644 src/core/libraries/kernel/threads/sleepq.cpp create mode 100644 src/core/libraries/kernel/threads/sleepq.h create mode 100644 src/core/libraries/kernel/threads/stack.cpp create mode 100644 src/core/libraries/kernel/threads/tcb.cpp create mode 100644 src/core/libraries/kernel/threads/thread_state.cpp create mode 100644 src/core/libraries/kernel/threads/thread_state.h delete mode 100644 src/core/libraries/kernel/threads/threads.h rename src/core/libraries/kernel/{time_management.cpp => time.cpp} (88%) rename src/core/libraries/kernel/{time_management.h => time.h} (76%) create mode 100644 src/core/thread.cpp create mode 100644 src/core/thread.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 35fc35f23..e135794ec 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -208,30 +208,38 @@ set(GNM_LIB src/core/libraries/gnmdriver/gnmdriver.cpp src/core/libraries/gnmdriver/gnm_error.h ) -set(KERNEL_LIB src/core/libraries/kernel/event_flag/event_flag.cpp - src/core/libraries/kernel/event_flag/event_flag.h - src/core/libraries/kernel/event_flag/event_flag_obj.cpp - src/core/libraries/kernel/event_flag/event_flag_obj.h +set(KERNEL_LIB src/core/libraries/kernel/threads/condvar.cpp + src/core/libraries/kernel/threads/event_flag.cpp + src/core/libraries/kernel/threads/exception.cpp + src/core/libraries/kernel/threads/exception.h + src/core/libraries/kernel/threads/mutex.cpp + src/core/libraries/kernel/threads/pthread_attr.cpp + src/core/libraries/kernel/threads/pthread_clean.cpp + src/core/libraries/kernel/threads/pthread.cpp + src/core/libraries/kernel/threads/pthread_spec.cpp src/core/libraries/kernel/threads/rwlock.cpp src/core/libraries/kernel/threads/semaphore.cpp - src/core/libraries/kernel/threads/keys.cpp - src/core/libraries/kernel/threads/threads.h - src/core/libraries/kernel/cpu_management.cpp - src/core/libraries/kernel/cpu_management.h - src/core/libraries/kernel/event_queue.cpp - src/core/libraries/kernel/event_queue.h - src/core/libraries/kernel/event_queues.cpp - src/core/libraries/kernel/event_queues.h + src/core/libraries/kernel/threads/sleepq.cpp + src/core/libraries/kernel/threads/sleepq.h + src/core/libraries/kernel/threads/stack.cpp + src/core/libraries/kernel/threads/tcb.cpp + src/core/libraries/kernel/threads/pthread.h + src/core/libraries/kernel/threads/thread_state.cpp + src/core/libraries/kernel/threads/thread_state.h + src/core/libraries/kernel/process.cpp + src/core/libraries/kernel/process.h + src/core/libraries/kernel/equeue.cpp + src/core/libraries/kernel/equeue.h src/core/libraries/kernel/file_system.cpp src/core/libraries/kernel/file_system.h - src/core/libraries/kernel/libkernel.cpp - src/core/libraries/kernel/libkernel.h - src/core/libraries/kernel/memory_management.cpp - src/core/libraries/kernel/memory_management.h - src/core/libraries/kernel/thread_management.cpp - src/core/libraries/kernel/thread_management.h - src/core/libraries/kernel/time_management.cpp - src/core/libraries/kernel/time_management.h + src/core/libraries/kernel/kernel.cpp + src/core/libraries/kernel/kernel.h + src/core/libraries/kernel/memory.cpp + src/core/libraries/kernel/memory.h + src/core/libraries/kernel/threads.cpp + src/core/libraries/kernel/threads.h + src/core/libraries/kernel/time.cpp + src/core/libraries/kernel/time.h ) set(NETWORK_LIBS src/core/libraries/network/http.cpp @@ -453,7 +461,10 @@ set(COMMON src/common/logging/backend.cpp src/common/signal_context.h src/common/signal_context.cpp src/common/singleton.h + src/common/slab_heap.h src/common/slot_vector.h + src/common/spin_lock.cpp + src/common/spin_lock.h src/common/string_util.cpp src/common/string_util.h src/common/thread.cpp @@ -541,6 +552,8 @@ set(CORE src/core/aerolib/stubs.cpp src/core/platform.h src/core/signals.cpp src/core/signals.h + src/core/thread.cpp + src/core/thread.h src/core/tls.cpp src/core/tls.h src/core/virtual_memory.cpp diff --git a/src/common/debug.h b/src/common/debug.h index 596ad7b84..091c6191d 100644 --- a/src/common/debug.h +++ b/src/common/debug.h @@ -41,7 +41,7 @@ enum MarkersPalette : int { #define RENDERER_TRACE ZoneScopedC(RendererMarkerColor) #define HLE_TRACE ZoneScopedC(HleMarkerColor) -#define TRACE_HINT(str) ZoneText(str.c_str(), str.size()) +#define TRACE_HINT(str) ZoneText(str.data(), str.size()) #define TRACE_WARN(msg) \ [](const auto& msg) { TracyMessageC(msg.c_str(), msg.size(), tracy::Color::DarkOrange); }(msg); diff --git a/src/common/slab_heap.h b/src/common/slab_heap.h new file mode 100644 index 000000000..7648ebea3 --- /dev/null +++ b/src/common/slab_heap.h @@ -0,0 +1,163 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "common/assert.h" +#include "common/spin_lock.h" + +namespace Common { + +class SlabHeapImpl { +public: + struct Node { + Node* next{}; + }; + +public: + constexpr SlabHeapImpl() = default; + + void Initialize() { + ASSERT(m_head == nullptr); + } + + Node* GetHead() const { + return m_head; + } + + void* Allocate() { + m_lock.lock(); + + Node* ret = m_head; + if (ret != nullptr) { + m_head = ret->next; + } + + m_lock.unlock(); + return ret; + } + + void Free(void* obj) { + m_lock.lock(); + + Node* node = static_cast(obj); + node->next = m_head; + m_head = node; + + m_lock.unlock(); + } + +private: + std::atomic m_head{}; + Common::SpinLock m_lock; +}; + +class SlabHeapBase : protected SlabHeapImpl { +private: + size_t m_obj_size{}; + uintptr_t m_peak{}; + uintptr_t m_start{}; + uintptr_t m_end{}; + +public: + constexpr SlabHeapBase() = default; + + bool Contains(uintptr_t address) const { + return m_start <= address && address < m_end; + } + + void Initialize(size_t obj_size, void* memory, size_t memory_size) { + // Ensure we don't initialize a slab using null memory. + ASSERT(memory != nullptr); + + // Set our object size. + m_obj_size = obj_size; + + // Initialize the base allocator. + SlabHeapImpl::Initialize(); + + // Set our tracking variables. + const size_t num_obj = (memory_size / obj_size); + m_start = reinterpret_cast(memory); + m_end = m_start + num_obj * obj_size; + m_peak = m_start; + + // Free the objects. + u8* cur = reinterpret_cast(m_end); + + for (size_t i = 0; i < num_obj; i++) { + cur -= obj_size; + SlabHeapImpl::Free(cur); + } + } + + size_t GetSlabHeapSize() const { + return (m_end - m_start) / this->GetObjectSize(); + } + + size_t GetObjectSize() const { + return m_obj_size; + } + + void* Allocate() { + void* obj = SlabHeapImpl::Allocate(); + return obj; + } + + void Free(void* obj) { + // Don't allow freeing an object that wasn't allocated from this heap. + const bool contained = this->Contains(reinterpret_cast(obj)); + ASSERT(contained); + SlabHeapImpl::Free(obj); + } + + size_t GetObjectIndex(const void* obj) const { + return (reinterpret_cast(obj) - m_start) / this->GetObjectSize(); + } + + size_t GetPeakIndex() const { + return this->GetObjectIndex(reinterpret_cast(m_peak)); + } + + uintptr_t GetSlabHeapAddress() const { + return m_start; + } + + size_t GetNumRemaining() const { + // Only calculate the number of remaining objects under debug configuration. + return 0; + } +}; + +template +class SlabHeap final : public SlabHeapBase { +private: + using BaseHeap = SlabHeapBase; + +public: + constexpr SlabHeap() = default; + + void Initialize(void* memory, size_t memory_size) { + BaseHeap::Initialize(sizeof(T), memory, memory_size); + } + + T* Allocate() { + T* obj = static_cast(BaseHeap::Allocate()); + + if (obj != nullptr) [[likely]] { + std::construct_at(obj); + } + return obj; + } + + void Free(T* obj) { + BaseHeap::Free(obj); + } + + size_t GetObjectIndex(const T* obj) const { + return BaseHeap::GetObjectIndex(obj); + } +}; + +} // namespace Common diff --git a/src/common/slot_vector.h b/src/common/slot_vector.h index 36e647971..d4ac51361 100644 --- a/src/common/slot_vector.h +++ b/src/common/slot_vector.h @@ -3,10 +3,7 @@ #pragma once -#include -#include #include -#include #include #include #include "common/assert.h" diff --git a/src/common/spin_lock.cpp b/src/common/spin_lock.cpp new file mode 100755 index 000000000..9d4cfe36b --- /dev/null +++ b/src/common/spin_lock.cpp @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/spin_lock.h" + +#if _MSC_VER +#include +#if _M_AMD64 +#define __x86_64__ 1 +#endif +#if _M_ARM64 +#define __aarch64__ 1 +#endif +#else +#if __x86_64__ +#include +#endif +#endif + +namespace { + +void ThreadPause() { +#if __x86_64__ + _mm_pause(); +#elif __aarch64__ && _MSC_VER + __yield(); +#elif __aarch64__ + asm("yield"); +#endif +} + +} // Anonymous namespace + +namespace Common { + +void SpinLock::lock() { + while (lck.test_and_set(std::memory_order_acquire)) { + ThreadPause(); + } +} + +void SpinLock::unlock() { + lck.clear(std::memory_order_release); +} + +bool SpinLock::try_lock() { + if (lck.test_and_set(std::memory_order_acquire)) { + return false; + } + return true; +} + +} // namespace Common diff --git a/src/common/spin_lock.h b/src/common/spin_lock.h new file mode 100755 index 000000000..3229a8c6a --- /dev/null +++ b/src/common/spin_lock.h @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +namespace Common { + +/** + * SpinLock class + * a lock similar to mutex that forces a thread to spin wait instead calling the + * supervisor. Should be used on short sequences of code. + */ +class SpinLock { +public: + SpinLock() = default; + + SpinLock(const SpinLock&) = delete; + SpinLock& operator=(const SpinLock&) = delete; + + SpinLock(SpinLock&&) = delete; + SpinLock& operator=(SpinLock&&) = delete; + + void lock(); + void unlock(); + [[nodiscard]] bool try_lock(); + +private: + std::atomic_flag lck = ATOMIC_FLAG_INIT; +}; + +} // namespace Common diff --git a/src/core/address_space.cpp b/src/core/address_space.cpp index 8ba99e32d..24f5e9f87 100644 --- a/src/core/address_space.cpp +++ b/src/core/address_space.cpp @@ -1,13 +1,14 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include #include "common/alignment.h" #include "common/arch.h" #include "common/assert.h" #include "common/error.h" #include "core/address_space.h" -#include "core/libraries/kernel/memory_management.h" +#include "core/libraries/kernel/memory.h" #include "core/memory.h" #include "libraries/error_codes.h" @@ -40,6 +41,12 @@ static constexpr size_t BackingSize = SCE_KERNEL_MAIN_DMEM_SIZE_PRO; } } +struct MemoryRegion { + VAddr base; + size_t size; + bool is_mapped; +}; + struct AddressSpace::Impl { Impl() : process{GetCurrentProcess()} { // Allocate virtual address placeholder for our address space. @@ -75,6 +82,7 @@ struct AddressSpace::Impl { Common::GetLastErrorMsg()); // Take the reduction off of the system managed area, and leave the others unchanged. + reduction = size_t(virtual_base - SYSTEM_MANAGED_MIN); system_managed_base = virtual_base; system_managed_size = SystemManagedSize - reduction; system_reserved_base = reinterpret_cast(SYSTEM_RESERVED_MIN); @@ -95,7 +103,8 @@ struct AddressSpace::Impl { const uintptr_t system_managed_addr = reinterpret_cast(system_managed_base); const uintptr_t system_reserved_addr = reinterpret_cast(system_reserved_base); const uintptr_t user_addr = reinterpret_cast(user_base); - placeholders.insert({system_managed_addr, virtual_size - reduction}); + regions.emplace(system_managed_addr, + MemoryRegion{system_managed_addr, virtual_size - reduction, false}); // Allocate backing file that represents the total physical memory. backing_handle = @@ -132,42 +141,15 @@ struct AddressSpace::Impl { } void* Map(VAddr virtual_addr, PAddr phys_addr, size_t size, ULONG prot, uintptr_t fd = 0) { - const size_t aligned_size = Common::AlignUp(size, 16_KB); - const auto it = placeholders.find(virtual_addr); - ASSERT_MSG(it != placeholders.end(), "Cannot map already mapped region"); - ASSERT_MSG(virtual_addr >= it->lower() && virtual_addr + aligned_size <= it->upper(), - "Map range must be fully contained in a placeholder"); - - // Windows only allows splitting a placeholder into two. - // This means that if the map range is fully - // contained the the placeholder we need to perform two split operations, - // one at the start and at the end. - const VAddr placeholder_start = it->lower(); - const VAddr placeholder_end = it->upper(); - const VAddr virtual_end = virtual_addr + aligned_size; - - // If the placeholder doesn't exactly start at virtual_addr, split it at the start. - if (placeholder_start != virtual_addr) { - VirtualFreeEx(process, reinterpret_cast(placeholder_start), - virtual_addr - placeholder_start, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); - } - - // If the placeholder doesn't exactly end at virtual_end, split it at the end. - if (placeholder_end != virtual_end) { - VirtualFreeEx(process, reinterpret_cast(virtual_end), - placeholder_end - virtual_end, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); - } - - // Remove the placeholder. - placeholders.erase({virtual_addr, virtual_end}); - - // Perform the map. + // Before mapping we must carve a placeholder with the exact properties of our mapping. + auto* region = EnsureSplitRegionForMapping(virtual_addr, size); + region->is_mapped = true; void* ptr = nullptr; if (phys_addr != -1) { HANDLE backing = fd ? reinterpret_cast(fd) : backing_handle; if (fd && prot == PAGE_READONLY) { DWORD resultvar; - ptr = VirtualAlloc2(process, reinterpret_cast(virtual_addr), aligned_size, + ptr = VirtualAlloc2(process, reinterpret_cast(virtual_addr), size, MEM_RESERVE | MEM_COMMIT | MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0); bool ret = ReadFile(backing, ptr, size, &resultvar, NULL); @@ -176,12 +158,11 @@ struct AddressSpace::Impl { ASSERT_MSG(ret, "VirtualProtect failed. {}", Common::GetLastErrorMsg()); } else { ptr = MapViewOfFile3(backing, process, reinterpret_cast(virtual_addr), - phys_addr, aligned_size, MEM_REPLACE_PLACEHOLDER, prot, - nullptr, 0); + phys_addr, size, MEM_REPLACE_PLACEHOLDER, prot, nullptr, 0); } } else { ptr = - VirtualAlloc2(process, reinterpret_cast(virtual_addr), aligned_size, + VirtualAlloc2(process, reinterpret_cast(virtual_addr), size, MEM_RESERVE | MEM_COMMIT | MEM_REPLACE_PLACEHOLDER, prot, nullptr, 0); } ASSERT_MSG(ptr, "{}", Common::GetLastErrorMsg()); @@ -202,33 +183,118 @@ struct AddressSpace::Impl { // The unmap call will create a new placeholder region. We need to see if we can coalesce it // with neighbors. - VAddr placeholder_start = virtual_addr; - VAddr placeholder_end = virtual_addr + size; + JoinRegionsAfterUnmap(virtual_addr, size); + } + + // The following code is inspired from Dolphin's MemArena + // https://github.com/dolphin-emu/dolphin/blob/deee3ee4/Source/Core/Common/MemArenaWin.cpp#L212 + MemoryRegion* EnsureSplitRegionForMapping(VAddr address, size_t size) { + // Find closest region that is <= the given address by using upper bound and decrementing + auto it = regions.upper_bound(address); + ASSERT_MSG(it != regions.begin(), "Invalid address {:#x}", address); + --it; + ASSERT_MSG(!it->second.is_mapped, + "Attempt to map {:#x} with size {:#x} which overlaps with {:#x} mapping", + address, size, it->second.base); + auto& [base, region] = *it; + + const VAddr mapping_address = region.base; + const size_t region_size = region.size; + if (mapping_address == address) { + // If this region is already split up correctly we don't have to do anything + if (region_size == size) { + return ®ion; + } + + ASSERT_MSG(region_size >= size, + "Region with address {:#x} and size {:#x} can't fit {:#x}", mapping_address, + region_size, size); + + // Split the placeholder. + if (!VirtualFreeEx(process, LPVOID(address), size, + MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER)) { + UNREACHABLE_MSG("Region splitting failed: {}", Common::GetLastErrorMsg()); + return nullptr; + } + + // Update tracked mappings and return the first of the two + region.size = size; + const VAddr new_mapping_start = address + size; + regions.emplace_hint(std::next(it), new_mapping_start, + MemoryRegion(new_mapping_start, region_size - size, false)); + return ®ion; + } + + ASSERT(mapping_address < address); + + // Is there enough space to map this? + const size_t offset_in_region = address - mapping_address; + const size_t minimum_size = size + offset_in_region; + ASSERT(region_size >= minimum_size); + + // Split the placeholder. + if (!VirtualFreeEx(process, LPVOID(address), size, + MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER)) { + UNREACHABLE_MSG("Region splitting failed: {}", Common::GetLastErrorMsg()); + return nullptr; + } + + // Do we now have two regions or three regions? + if (region_size == minimum_size) { + // Split into two; update tracked mappings and return the second one + region.size = offset_in_region; + it = regions.emplace_hint(std::next(it), address, MemoryRegion(address, size, false)); + return &it->second; + } else { + // Split into three; update tracked mappings and return the middle one + region.size = offset_in_region; + const VAddr middle_mapping_start = address; + const size_t middle_mapping_size = size; + const VAddr after_mapping_start = address + size; + const size_t after_mapping_size = region_size - minimum_size; + it = regions.emplace_hint(std::next(it), after_mapping_start, + MemoryRegion(after_mapping_start, after_mapping_size, false)); + it = regions.emplace_hint( + it, middle_mapping_start, + MemoryRegion(middle_mapping_start, middle_mapping_size, false)); + return &it->second; + } + } + + void JoinRegionsAfterUnmap(VAddr address, size_t size) { + // There should be a mapping that matches the request exactly, find it + auto it = regions.find(address); + ASSERT_MSG(it != regions.end() && it->second.size == size, + "Invalid address/size given to unmap."); + auto& [base, region] = *it; + region.is_mapped = false; // Check if a placeholder exists right before us. - const auto left_it = placeholders.find(virtual_addr - 1); - if (left_it != placeholders.end()) { - ASSERT_MSG(left_it->upper() == virtual_addr, - "Left placeholder does not end at virtual_addr!"); - placeholder_start = left_it->lower(); - VirtualFreeEx(process, reinterpret_cast(placeholder_start), - placeholder_end - placeholder_start, - MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS); + auto it_prev = it != regions.begin() ? std::prev(it) : regions.end(); + if (it_prev != regions.end() && !it_prev->second.is_mapped) { + const size_t total_size = it_prev->second.size + size; + if (!VirtualFreeEx(process, LPVOID(it_prev->first), total_size, + MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS)) { + UNREACHABLE_MSG("Region coalescing failed: {}", Common::GetLastErrorMsg()); + } + + it_prev->second.size = total_size; + regions.erase(it); + it = it_prev; } // Check if a placeholder exists right after us. - const auto right_it = placeholders.find(placeholder_end + 1); - if (right_it != placeholders.end()) { - ASSERT_MSG(right_it->lower() == placeholder_end, - "Right placeholder does not start at virtual_end!"); - placeholder_end = right_it->upper(); - VirtualFreeEx(process, reinterpret_cast(placeholder_start), - placeholder_end - placeholder_start, - MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS); - } + auto it_next = std::next(it); + if (it_next != regions.end() && !it_next->second.is_mapped) { + const size_t total_size = it->second.size + it_next->second.size; + if (!VirtualFreeEx(process, LPVOID(it->first), total_size, + MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS)) { + UNREACHABLE_MSG("Region coalescing failed: {}", Common::GetLastErrorMsg()); + } - // Insert the new placeholder. - placeholders.insert({placeholder_start, placeholder_end}); + it->second.size = total_size; + regions.erase(it_next); + } } void Protect(VAddr virtual_addr, size_t size, bool read, bool write, bool execute) { @@ -251,18 +317,22 @@ struct AddressSpace::Impl { return; } - DWORD old_flags{}; - bool success = - VirtualProtect(reinterpret_cast(virtual_addr), size, new_flags, &old_flags); - - if (!success) { - LOG_ERROR(Common_Memory, - "Failed to change virtual memory protection for address {:#x}, size {}", - virtual_addr, size); + const VAddr virtual_end = virtual_addr + size; + auto it = --regions.upper_bound(virtual_addr); + for (; it->first < virtual_end; it++) { + if (!it->second.is_mapped) { + continue; + } + const auto& region = it->second; + const size_t range_addr = std::max(region.base, virtual_addr); + const size_t range_size = std::min(region.base + region.size, virtual_end) - range_addr; + DWORD old_flags{}; + if (!VirtualProtectEx(process, LPVOID(range_addr), range_size, new_flags, &old_flags)) { + UNREACHABLE_MSG( + "Failed to change virtual memory protection for address {:#x}, size {}", + range_addr, range_size); + } } - - // Use assert to ensure success in debug builds - DEBUG_ASSERT(success && "Failed to change virtual memory protection"); } HANDLE process{}; @@ -275,7 +345,7 @@ struct AddressSpace::Impl { size_t system_reserved_size{}; u8* user_base{}; size_t user_size{}; - boost::icl::separate_interval_set placeholders; + std::map regions; }; #else diff --git a/src/core/debug_state.cpp b/src/core/debug_state.cpp index adcb0cadb..1dc4297c3 100644 --- a/src/core/debug_state.cpp +++ b/src/core/debug_state.cpp @@ -8,7 +8,7 @@ #include "common/singleton.h" #include "debug_state.h" #include "devtools/widget/common.h" -#include "libraries/kernel/time_management.h" +#include "libraries/kernel/time.h" #include "libraries/system/msgdialog.h" #include "video_core/amdgpu/pm4_cmds.h" diff --git a/src/core/file_sys/file.cpp b/src/core/file_sys/file.cpp new file mode 100644 index 000000000..be6bc76bb --- /dev/null +++ b/src/core/file_sys/file.cpp @@ -0,0 +1,114 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/assert.h" +#include "common/error.h" +#include "core/file_sys/file.h" + +#ifdef _WIN64 +#include +#include +#include +#include "common/ntapi.h" +#endif + +namespace Core::FileSys { + +#ifdef _WIN64 + +int File::Open(const std::filesystem::path& path, Common::FS::FileAccessMode f_access) { + DWORD access{}; + if (f_access == Common::FS::FileAccessMode::Read) { + access = GENERIC_READ; + } else if (f_access == Common::FS::FileAccessMode::Write) { + access = GENERIC_WRITE; + } else if (f_access == Common::FS::FileAccessMode::ReadWrite) { + access = GENERIC_READ | GENERIC_WRITE; + } else { + UNREACHABLE(); + } + handle = CreateFileW(path.native().c_str(), access, FILE_SHARE_READ, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + if (handle == INVALID_HANDLE_VALUE) { + return ENOENT; + } +} + +s64 File::Read(void* buf, size_t nbytes) { + DWORD bytes_read; + if (!ReadFile(handle, buf, nbytes, &bytes_read, nullptr)) { + UNREACHABLE_MSG("ReadFile failed: {}", Common::GetLastErrorMsg()); + } + return bytes_read; +} + +s64 File::Pread(void* buf, size_t nbytes, s64 offset) { + OVERLAPPED ol{}; + ol.Offset = offset; + ol.OffsetHigh = offset >> 32; + DWORD bytes_read; + if (!ReadFile(handle, buf, nbytes, &bytes_read, &ol)) { + UNREACHABLE_MSG("ReadFile failed: {}", Common::GetLastErrorMsg()); + } + return bytes_read; +} + +s64 File::Write(const void* buf, size_t nbytes) { + DWORD bytes_written; + if (!WriteFile(handle, buf, nbytes, &bytes_written, nullptr)) { + UNREACHABLE_MSG("WriteFile failed: {}", Common::GetLastErrorMsg()); + } + return bytes_written; +} + +s64 File::Pwrite(const void* buf, size_t nbytes, s64 offset) { + OVERLAPPED ol{}; + ol.Offset = offset; + ol.OffsetHigh = offset >> 32; + DWORD bytes_written; + if (!WriteFile(handle, buf, nbytes, &bytes_written, &ol)) { + UNREACHABLE_MSG("WriteFile failed: {}", Common::GetLastErrorMsg()); + } + return bytes_written; +} + +void File::SetSize(s64 size) { + Lseek(size, 0); + if (!SetEndOfFile(handle)) { + UNREACHABLE_MSG("SetEndOfFile failed: {}", Common::GetLastErrorMsg()); + } +} + +void File::Flush() { + FlushFileBuffers(handle); +} + +s64 File::Lseek(s64 offset, int whence) { + LARGE_INTEGER new_file_pointer; + DWORD origin{}; + if (whence == 0) { + origin = FILE_BEGIN; + } else if (whence == 1) { + origin = FILE_CURRENT; + } else if (whence == 2) { + origin = FILE_END; + } + if (!SetFilePointerEx(handle, LARGE_INTEGER{.QuadPart = offset}, &new_file_pointer, origin)) { + UNREACHABLE_MSG("SetFilePointerEx failed: {}", Common::GetLastErrorMsg()); + } + return new_file_pointer.QuadPart; +} + +void File::Unlink() { + FILE_DISPOSITION_INFORMATION disposition; + IO_STATUS_BLOCK iosb; + disposition.DeleteFile = TRUE; + NtSetInformationFile(handle, &iosb, &disposition, sizeof(disposition), + FileDispositionInformation); +} + +#else + +#endif + +} // namespace Core::FileSys \ No newline at end of file diff --git a/src/core/libraries/audio3d/audio3d.cpp b/src/core/libraries/audio3d/audio3d.cpp index 63815a068..2978445e8 100644 --- a/src/core/libraries/audio3d/audio3d.cpp +++ b/src/core/libraries/audio3d/audio3d.cpp @@ -65,12 +65,12 @@ int PS4_SYSV_ABI sceAudio3dPortFlush(OrbisAudio3dPortId uiPortId) { } int PS4_SYSV_ABI sceAudio3dPortAdvance(OrbisAudio3dPortId uiPortId) { - LOG_INFO(Lib_Audio3d, "uiPortId = {}", uiPortId); + LOG_TRACE(Lib_Audio3d, "uiPortId = {}", uiPortId); return ORBIS_OK; } int PS4_SYSV_ABI sceAudio3dPortPush(OrbisAudio3dPortId uiPortId, OrbisAudio3dBlocking eBlocking) { - LOG_INFO(Lib_Audio3d, "uiPortId = {}", uiPortId); + LOG_TRACE(Lib_Audio3d, "uiPortId = {}", uiPortId); return ORBIS_OK; } @@ -110,7 +110,7 @@ int PS4_SYSV_ABI sceAudio3dObjectSetAttributes(OrbisAudio3dPortId uiPortId, int PS4_SYSV_ABI sceAudio3dBedWrite(OrbisAudio3dPortId uiPortId, unsigned int uiNumChannels, OrbisAudio3dFormat eFormat, const void* pBuffer, unsigned int uiNumSamples) { - LOG_INFO(Lib_Audio3d, "uiPortId = {}, uiNumChannels = {}, uiNumSamples = {}", uiPortId, + LOG_TRACE(Lib_Audio3d, "uiPortId = {}, uiNumChannels = {}, uiNumSamples = {}", uiPortId, uiNumChannels, uiNumSamples); return ORBIS_OK; } @@ -191,7 +191,7 @@ s32 PS4_SYSV_ABI sceAudio3dAudioOutClose(s32 handle) { } s32 PS4_SYSV_ABI sceAudio3dAudioOutOutput(s32 handle, const void* ptr) { - LOG_INFO(Lib_Audio3d, "handle = {}", handle); + LOG_TRACE(Lib_Audio3d, "handle = {}", handle); if (ptr == nullptr) { LOG_ERROR(Lib_Audio3d, "invalid Output ptr"); return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; diff --git a/src/core/libraries/audio3d/audio3d_impl.cpp b/src/core/libraries/audio3d/audio3d_impl.cpp index c267c096f..3069e8800 100644 --- a/src/core/libraries/audio3d/audio3d_impl.cpp +++ b/src/core/libraries/audio3d/audio3d_impl.cpp @@ -6,7 +6,7 @@ #include "common/logging/log.h" #include "core/libraries/error_codes.h" -#include "core/libraries/kernel/libkernel.h" +#include "core/libraries/kernel/kernel.h" using namespace Libraries::Kernel; diff --git a/src/core/libraries/avplayer/avplayer.cpp b/src/core/libraries/avplayer/avplayer.cpp index 60d68c4f7..1257473e1 100644 --- a/src/core/libraries/avplayer/avplayer.cpp +++ b/src/core/libraries/avplayer/avplayer.cpp @@ -1,18 +1,14 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "avplayer.h" - -#include "avplayer_impl.h" #include "common/logging/log.h" +#include "core/libraries/avplayer/avplayer.h" +#include "core/libraries/avplayer/avplayer_impl.h" #include "core/libraries/error_codes.h" -#include "core/libraries/kernel/thread_management.h" #include "core/libraries/libs.h" namespace Libraries::AvPlayer { -using namespace Kernel; - s32 PS4_SYSV_ABI sceAvPlayerAddSource(SceAvPlayerHandle handle, const char* filename) { LOG_TRACE(Lib_AvPlayer, "filename = {}", filename); if (handle == nullptr) { @@ -309,7 +305,7 @@ void RegisterlibSceAvPlayer(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("XC9wM+xULz8", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerJumpToTime); LIB_FUNCTION("9y5v+fGN4Wk", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerPause); LIB_FUNCTION("HD1YKVU26-M", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerPostInit); - LIB_FUNCTION("agig-iDRrTE", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerPrintf); + // LIB_FUNCTION("agig-iDRrTE", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerPrintf); LIB_FUNCTION("w5moABNwnRY", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerResume); LIB_FUNCTION("k-q+xOxdc3E", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerSetAvSyncMode); diff --git a/src/core/libraries/avplayer/avplayer.h b/src/core/libraries/avplayer/avplayer.h index 98e932070..9f100bef3 100644 --- a/src/core/libraries/avplayer/avplayer.h +++ b/src/core/libraries/avplayer/avplayer.h @@ -5,8 +5,8 @@ #include "common/types.h" -#include // va_list -#include // size_t +#include // va_list +#include // size_t namespace Core::Loader { class SymbolsResolver; diff --git a/src/core/libraries/avplayer/avplayer_common.cpp b/src/core/libraries/avplayer/avplayer_common.cpp index 306603e29..805920a3d 100644 --- a/src/core/libraries/avplayer/avplayer_common.cpp +++ b/src/core/libraries/avplayer/avplayer_common.cpp @@ -1,24 +1,16 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "avplayer.h" -#include "avplayer_common.h" +#include // std::equal +#include // std::tolower -#include // std::equal -#include // std::tolower -#include // std::string_view +#include "core/libraries/avplayer/avplayer.h" +#include "core/libraries/avplayer/avplayer_common.h" namespace Libraries::AvPlayer { -using namespace Kernel; - -static bool ichar_equals(char a, char b) { - return std::tolower(static_cast(a)) == - std::tolower(static_cast(b)); -} - static bool iequals(std::string_view l, std::string_view r) { - return std::ranges::equal(l, r, ichar_equals); + return std::ranges::equal(l, r, [](u8 a, u8 b) { return std::tolower(a) == std::tolower(b); }); } SceAvPlayerSourceType GetSourceType(std::string_view path) { diff --git a/src/core/libraries/avplayer/avplayer_common.h b/src/core/libraries/avplayer/avplayer_common.h index a53696ecf..dc3cd787f 100644 --- a/src/core/libraries/avplayer/avplayer_common.h +++ b/src/core/libraries/avplayer/avplayer_common.h @@ -3,16 +3,14 @@ #pragma once -#include "avplayer.h" - -#include "common/assert.h" -#include "common/logging/log.h" -#include "core/libraries/kernel/thread_management.h" - +#include #include +#include #include #include +#include "core/libraries/avplayer/avplayer.h" + #define AVPLAYER_IS_ERROR(x) ((x) < 0) namespace Libraries::AvPlayer { diff --git a/src/core/libraries/avplayer/avplayer_file_streamer.cpp b/src/core/libraries/avplayer/avplayer_file_streamer.cpp index c7bd5b5de..3323ee9b6 100644 --- a/src/core/libraries/avplayer/avplayer_file_streamer.cpp +++ b/src/core/libraries/avplayer/avplayer_file_streamer.cpp @@ -1,20 +1,16 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "avplayer_file_streamer.h" - -#include "avplayer_common.h" - +#include // std::max, std::min #include +#include "core/libraries/avplayer/avplayer_file_streamer.h" extern "C" { #include #include } -#include // std::max, std::min - -#define AVPLAYER_AVIO_BUFFER_SIZE 4096 +constexpr u32 AVPLAYER_AVIO_BUFFER_SIZE = 4096; namespace Libraries::AvPlayer { diff --git a/src/core/libraries/avplayer/avplayer_file_streamer.h b/src/core/libraries/avplayer/avplayer_file_streamer.h index 034e40dd4..bc096bccc 100644 --- a/src/core/libraries/avplayer/avplayer_file_streamer.h +++ b/src/core/libraries/avplayer/avplayer_file_streamer.h @@ -3,11 +3,9 @@ #pragma once -#include "avplayer.h" -#include "avplayer_data_streamer.h" - #include -#include +#include "core/libraries/avplayer/avplayer.h" +#include "core/libraries/avplayer/avplayer_data_streamer.h" struct AVIOContext; diff --git a/src/core/libraries/avplayer/avplayer_impl.cpp b/src/core/libraries/avplayer/avplayer_impl.cpp index 1c414c961..0f39acfdc 100644 --- a/src/core/libraries/avplayer/avplayer_impl.cpp +++ b/src/core/libraries/avplayer/avplayer_impl.cpp @@ -1,17 +1,10 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "avplayer_common.h" -#include "avplayer_file_streamer.h" -#include "avplayer_impl.h" - -#include "common/logging/log.h" -#include "common/singleton.h" +#include "core/libraries/avplayer/avplayer_common.h" +#include "core/libraries/avplayer/avplayer_impl.h" #include "core/libraries/error_codes.h" -#include "core/libraries/kernel/libkernel.h" -#include "core/linker.h" - -using namespace Libraries::Kernel; +#include "core/tls.h" namespace Libraries::AvPlayer { @@ -19,32 +12,28 @@ void* PS4_SYSV_ABI AvPlayer::Allocate(void* handle, u32 alignment, u32 size) { const auto* const self = reinterpret_cast(handle); const auto allocate = self->m_init_data_original.memory_replacement.allocate; const auto ptr = self->m_init_data_original.memory_replacement.object_ptr; - const auto* linker = Common::Singleton::Instance(); - return linker->ExecuteGuest(allocate, ptr, alignment, size); + return Core::ExecuteGuest(allocate, ptr, alignment, size); } void PS4_SYSV_ABI AvPlayer::Deallocate(void* handle, void* memory) { const auto* const self = reinterpret_cast(handle); const auto deallocate = self->m_init_data_original.memory_replacement.deallocate; const auto ptr = self->m_init_data_original.memory_replacement.object_ptr; - const auto* linker = Common::Singleton::Instance(); - return linker->ExecuteGuest(deallocate, ptr, memory); + return Core::ExecuteGuest(deallocate, ptr, memory); } void* PS4_SYSV_ABI AvPlayer::AllocateTexture(void* handle, u32 alignment, u32 size) { const auto* const self = reinterpret_cast(handle); const auto allocate = self->m_init_data_original.memory_replacement.allocate_texture; const auto ptr = self->m_init_data_original.memory_replacement.object_ptr; - const auto* linker = Common::Singleton::Instance(); - return linker->ExecuteGuest(allocate, ptr, alignment, size); + return Core::ExecuteGuest(allocate, ptr, alignment, size); } void PS4_SYSV_ABI AvPlayer::DeallocateTexture(void* handle, void* memory) { const auto* const self = reinterpret_cast(handle); const auto deallocate = self->m_init_data_original.memory_replacement.deallocate_texture; const auto ptr = self->m_init_data_original.memory_replacement.object_ptr; - const auto* linker = Common::Singleton::Instance(); - return linker->ExecuteGuest(deallocate, ptr, memory); + return Core::ExecuteGuest(deallocate, ptr, memory); } int PS4_SYSV_ABI AvPlayer::OpenFile(void* handle, const char* filename) { @@ -53,8 +42,7 @@ int PS4_SYSV_ABI AvPlayer::OpenFile(void* handle, const char* filename) { const auto open = self->m_init_data_original.file_replacement.open; const auto ptr = self->m_init_data_original.file_replacement.object_ptr; - const auto* linker = Common::Singleton::Instance(); - return linker->ExecuteGuest(open, ptr, filename); + return Core::ExecuteGuest(open, ptr, filename); } int PS4_SYSV_ABI AvPlayer::CloseFile(void* handle) { @@ -63,8 +51,7 @@ int PS4_SYSV_ABI AvPlayer::CloseFile(void* handle) { const auto close = self->m_init_data_original.file_replacement.close; const auto ptr = self->m_init_data_original.file_replacement.object_ptr; - const auto* linker = Common::Singleton::Instance(); - return linker->ExecuteGuest(close, ptr); + return Core::ExecuteGuest(close, ptr); } int PS4_SYSV_ABI AvPlayer::ReadOffsetFile(void* handle, u8* buffer, u64 position, u32 length) { @@ -73,8 +60,7 @@ int PS4_SYSV_ABI AvPlayer::ReadOffsetFile(void* handle, u8* buffer, u64 position const auto read_offset = self->m_init_data_original.file_replacement.readOffset; const auto ptr = self->m_init_data_original.file_replacement.object_ptr; - const auto* linker = Common::Singleton::Instance(); - return linker->ExecuteGuest(read_offset, ptr, buffer, position, length); + return Core::ExecuteGuest(read_offset, ptr, buffer, position, length); } u64 PS4_SYSV_ABI AvPlayer::SizeFile(void* handle) { @@ -83,8 +69,7 @@ u64 PS4_SYSV_ABI AvPlayer::SizeFile(void* handle) { const auto size = self->m_init_data_original.file_replacement.size; const auto ptr = self->m_init_data_original.file_replacement.object_ptr; - const auto* linker = Common::Singleton::Instance(); - return linker->ExecuteGuest(size, ptr); + return Core::ExecuteGuest(size, ptr); } SceAvPlayerInitData AvPlayer::StubInitData(const SceAvPlayerInitData& data) { diff --git a/src/core/libraries/avplayer/avplayer_impl.h b/src/core/libraries/avplayer/avplayer_impl.h index d7f28094e..984d81499 100644 --- a/src/core/libraries/avplayer/avplayer_impl.h +++ b/src/core/libraries/avplayer/avplayer_impl.h @@ -3,11 +3,8 @@ #pragma once -#include "avplayer.h" -#include "avplayer_data_streamer.h" -#include "avplayer_state.h" - -#include "core/libraries/kernel/thread_management.h" +#include "core/libraries/avplayer/avplayer.h" +#include "core/libraries/avplayer/avplayer_state.h" #include @@ -17,7 +14,6 @@ extern "C" { } #include -#include namespace Libraries::AvPlayer { diff --git a/src/core/libraries/avplayer/avplayer_source.cpp b/src/core/libraries/avplayer/avplayer_source.cpp index 19925ba0c..8a65377c2 100644 --- a/src/core/libraries/avplayer/avplayer_source.cpp +++ b/src/core/libraries/avplayer/avplayer_source.cpp @@ -1,16 +1,12 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "avplayer_source.h" - -#include "avplayer_file_streamer.h" - #include "common/alignment.h" #include "common/singleton.h" #include "common/thread.h" - #include "core/file_sys/fs.h" -#include "core/libraries/kernel/time_management.h" +#include "core/libraries/avplayer/avplayer_file_streamer.h" +#include "core/libraries/avplayer/avplayer_source.h" #include @@ -35,8 +31,6 @@ av_always_inline std::string av_err2string(int errnum) { namespace Libraries::AvPlayer { -using namespace Kernel; - AvPlayerSource::AvPlayerSource(AvPlayerStateCallback& state, bool use_vdec2) : m_state(state), m_use_vdec2(use_vdec2) {} @@ -258,11 +252,9 @@ bool AvPlayerSource::Start() { LOG_ERROR(Lib_AvPlayer, "Could not start playback. NULL context."); return false; } - m_demuxer_thread = std::jthread([this](std::stop_token stop) { this->DemuxerThread(stop); }); - m_video_decoder_thread = - std::jthread([this](std::stop_token stop) { this->VideoDecoderThread(stop); }); - m_audio_decoder_thread = - std::jthread([this](std::stop_token stop) { this->AudioDecoderThread(stop); }); + m_demuxer_thread.Run([this](std::stop_token stop) { this->DemuxerThread(stop); }); + m_video_decoder_thread.Run([this](std::stop_token stop) { this->VideoDecoderThread(stop); }); + m_audio_decoder_thread.Run([this](std::stop_token stop) { this->AudioDecoderThread(stop); }); m_start_time = std::chrono::high_resolution_clock::now(); return true; } @@ -275,18 +267,10 @@ bool AvPlayerSource::Stop() { return false; } - m_video_decoder_thread.request_stop(); - m_audio_decoder_thread.request_stop(); - m_demuxer_thread.request_stop(); - if (m_demuxer_thread.joinable()) { - m_demuxer_thread.join(); - } - if (m_video_decoder_thread.joinable()) { - m_video_decoder_thread.join(); - } - if (m_audio_decoder_thread.joinable()) { - m_audio_decoder_thread.join(); - } + m_video_decoder_thread.Stop(); + m_audio_decoder_thread.Stop(); + m_demuxer_thread.Stop(); + if (m_current_audio_frame.has_value()) { m_audio_buffers.Push(std::move(m_current_audio_frame.value())); m_current_audio_frame.reset(); @@ -510,12 +494,8 @@ void AvPlayerSource::DemuxerThread(std::stop_token stop) { m_video_frames_cv.Notify(); m_audio_frames_cv.Notify(); - if (m_video_decoder_thread.joinable()) { - m_video_decoder_thread.join(); - } - if (m_audio_decoder_thread.joinable()) { - m_audio_decoder_thread.join(); - } + m_video_decoder_thread.Join(); + m_audio_decoder_thread.Join(); m_state.OnEOF(); LOG_INFO(Lib_AvPlayer, "Demuxer Thread exited normally"); @@ -808,8 +788,8 @@ void AvPlayerSource::AudioDecoderThread(std::stop_token stop) { } bool AvPlayerSource::HasRunningThreads() const { - return m_demuxer_thread.joinable() || m_video_decoder_thread.joinable() || - m_audio_decoder_thread.joinable(); + return m_demuxer_thread.Joinable() || m_video_decoder_thread.Joinable() || + m_audio_decoder_thread.Joinable(); } } // namespace Libraries::AvPlayer diff --git a/src/core/libraries/avplayer/avplayer_source.h b/src/core/libraries/avplayer/avplayer_source.h index 505d74465..7e199c457 100644 --- a/src/core/libraries/avplayer/avplayer_source.h +++ b/src/core/libraries/avplayer/avplayer_source.h @@ -3,20 +3,18 @@ #pragma once -#include "avplayer.h" -#include "avplayer_common.h" -#include "avplayer_data_streamer.h" - -#include "common/polyfill_thread.h" -#include "common/types.h" -#include "core/libraries/kernel/thread_management.h" - #include #include #include #include #include -#include +#include + +#include "common/assert.h" +#include "core/libraries/avplayer/avplayer.h" +#include "core/libraries/avplayer/avplayer_common.h" +#include "core/libraries/avplayer/avplayer_data_streamer.h" +#include "core/libraries/kernel/threads.h" struct AVCodecContext; struct AVFormatContext; @@ -139,8 +137,6 @@ public: bool IsActive(); private: - using ScePthread = Kernel::ScePthread; - static void ReleaseAVPacket(AVPacket* packet); static void ReleaseAVFrame(AVFrame* frame); static void ReleaseAVCodecContext(AVCodecContext* context); @@ -204,9 +200,9 @@ private: EventCV m_stop_cv{}; std::mutex m_state_mutex{}; - std::jthread m_demuxer_thread{}; - std::jthread m_video_decoder_thread{}; - std::jthread m_audio_decoder_thread{}; + Kernel::Thread m_demuxer_thread{}; + Kernel::Thread m_video_decoder_thread{}; + Kernel::Thread m_audio_decoder_thread{}; AVFormatContextPtr m_avformat_context{nullptr, &ReleaseAVFormatContext}; AVCodecContextPtr m_video_codec_context{nullptr, &ReleaseAVCodecContext}; diff --git a/src/core/libraries/avplayer/avplayer_state.cpp b/src/core/libraries/avplayer/avplayer_state.cpp index e66100679..3d9840a1b 100644 --- a/src/core/libraries/avplayer/avplayer_state.cpp +++ b/src/core/libraries/avplayer/avplayer_state.cpp @@ -1,22 +1,17 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "avplayer_file_streamer.h" -#include "avplayer_source.h" -#include "avplayer_state.h" - -#include "common/singleton.h" +#include "common/logging/log.h" #include "common/thread.h" +#include "core/libraries/avplayer/avplayer_source.h" +#include "core/libraries/avplayer/avplayer_state.h" #include "core/libraries/error_codes.h" -#include "core/libraries/kernel/time_management.h" -#include "core/linker.h" +#include "core/tls.h" #include namespace Libraries::AvPlayer { -using namespace Kernel; - void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, SceAvPlayerEvents event_id, s32 source_id, void* event_data) { auto const self = reinterpret_cast(opaque); @@ -96,8 +91,7 @@ void AvPlayerState::DefaultEventCallback(void* opaque, SceAvPlayerEvents event_i const auto callback = self->m_event_replacement.event_callback; const auto ptr = self->m_event_replacement.object_ptr; if (callback != nullptr) { - const auto* linker = Common::Singleton::Instance(); - linker->ExecuteGuest(callback, ptr, event_id, 0, event_data); + Core::ExecuteGuest(callback, ptr, event_id, 0, event_data); } } @@ -123,10 +117,7 @@ AvPlayerState::~AvPlayerState() { std::unique_lock lock(m_source_mutex); m_up_source.reset(); } - if (m_controller_thread.joinable()) { - m_controller_thread.request_stop(); - m_controller_thread.join(); - } + m_controller_thread.Stop(); m_event_queue.Clear(); } @@ -227,8 +218,7 @@ void AvPlayerState::WarningEvent(s32 id) { // Called inside GAME thread void AvPlayerState::StartControllerThread() { - m_controller_thread = - std::jthread([this](std::stop_token stop) { this->AvControllerThread(stop); }); + m_controller_thread.Run([this](std::stop_token stop) { this->AvControllerThread(stop); }); } // Called inside GAME thread diff --git a/src/core/libraries/avplayer/avplayer_state.h b/src/core/libraries/avplayer/avplayer_state.h index d106127e4..8a0d7f4f8 100644 --- a/src/core/libraries/avplayer/avplayer_state.h +++ b/src/core/libraries/avplayer/avplayer_state.h @@ -3,17 +3,14 @@ #pragma once -#include "avplayer.h" -#include "avplayer_data_streamer.h" -#include "avplayer_source.h" - -#include "common/polyfill_thread.h" -#include "core/libraries/kernel/thread_management.h" - #include #include #include +#include "core/libraries/avplayer/avplayer.h" +#include "core/libraries/avplayer/avplayer_source.h" +#include "core/libraries/kernel/threads.h" + namespace Libraries::AvPlayer { class Stream; @@ -83,7 +80,7 @@ private: std::shared_mutex m_source_mutex{}; std::mutex m_state_machine_mutex{}; std::mutex m_event_handler_mutex{}; - std::jthread m_controller_thread{}; + Kernel::Thread m_controller_thread{}; AvPlayerQueue m_event_queue{}; }; diff --git a/src/core/libraries/fiber/fiber.cpp b/src/core/libraries/fiber/fiber.cpp index a0bfd6850..92bf894b5 100644 --- a/src/core/libraries/fiber/fiber.cpp +++ b/src/core/libraries/fiber/fiber.cpp @@ -4,10 +4,9 @@ #include "fiber.h" #include "common/logging/log.h" -#include "common/singleton.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" -#include "core/linker.h" +#include "core/tls.h" #ifdef _WIN64 #include @@ -31,9 +30,7 @@ void FiberEntry(void* param) { argRun = *fiber->pArgRun; } - const auto* linker = Common::Singleton::Instance(); - linker->ExecuteGuest(fiber->entry, fiber->argOnInitialize, argRun); - + Core::ExecuteGuest(fiber->entry, fiber->argOnInitialize, argRun); UNREACHABLE(); } @@ -281,4 +278,4 @@ void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("JzyT91ucGDc", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberRename); } -} // namespace Libraries::Fiber \ No newline at end of file +} // namespace Libraries::Fiber diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index 946622cef..9aede3304 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -8,12 +8,11 @@ #include "common/config.h" #include "common/debug.h" #include "common/logging/log.h" -#include "common/path_util.h" #include "common/slot_vector.h" #include "core/address_space.h" #include "core/debug_state.h" #include "core/libraries/error_codes.h" -#include "core/libraries/kernel/libkernel.h" +#include "core/libraries/kernel/process.h" #include "core/libraries/libs.h" #include "core/libraries/videoout/video_out.h" #include "core/platform.h" @@ -377,9 +376,12 @@ int PS4_SYSV_ABI sceGnmAreSubmitsAllowed() { return submission_lock == 0; } -int PS4_SYSV_ABI sceGnmBeginWorkload() { - LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceGnmBeginWorkload(u32 workload_stream, u64* workload) { + if (workload) { + *workload = (-(u32)(workload_stream < 0x10) & 1); + return 0xf < workload_stream; + } + return 3; } s32 PS4_SYSV_ABI sceGnmComputeWaitOnAddress(u32* cmdbuf, u32 size, uintptr_t addr, u32 mask, @@ -413,9 +415,12 @@ int PS4_SYSV_ABI sceGnmComputeWaitSemaphore() { return ORBIS_OK; } -int PS4_SYSV_ABI sceGnmCreateWorkloadStream() { - LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceGnmCreateWorkloadStream(u64 param1, u32* workload_stream) { + if (param1 != 0 && workload_stream) { + *workload_stream = 1; + return 0; + } + return 3; } int PS4_SYSV_ABI sceGnmDebuggerGetAddressWatch() { @@ -952,9 +957,11 @@ int PS4_SYSV_ABI sceGnmDriverTriggerCapture() { return ORBIS_OK; } -int PS4_SYSV_ABI sceGnmEndWorkload() { - LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceGnmEndWorkload(u64 workload) { + if (workload != 0) { + return (0xf < ((workload >> 0x38) & 0xff)) * 2; + } + return 2; } s32 PS4_SYSV_ABI sceGnmFindResourcesPublic() { @@ -2124,6 +2131,14 @@ s32 PS4_SYSV_ABI sceGnmSubmitAndFlipCommandBuffers(u32 count, u32* dcb_gpu_addrs u32* dcb_sizes_in_bytes, u32* ccb_gpu_addrs[], u32* ccb_sizes_in_bytes, u32 vo_handle, u32 buf_idx, u32 flip_mode, u32 flip_arg) { + return sceGnmSubmitAndFlipCommandBuffersForWorkload( + count, count, dcb_gpu_addrs, dcb_sizes_in_bytes, ccb_gpu_addrs, ccb_sizes_in_bytes, + vo_handle, buf_idx, flip_mode, flip_arg); +} + +s32 PS4_SYSV_ABI sceGnmSubmitAndFlipCommandBuffersForWorkload( + u32 workload, u32 count, u32* dcb_gpu_addrs[], u32* dcb_sizes_in_bytes, u32* ccb_gpu_addrs[], + u32* ccb_sizes_in_bytes, u32 vo_handle, u32 buf_idx, u32 flip_mode, u32 flip_arg) { LOG_DEBUG(Lib_GnmDriver, "called [buf = {}]", buf_idx); auto* cmdbuf = dcb_gpu_addrs[count - 1]; @@ -2140,14 +2155,11 @@ s32 PS4_SYSV_ABI sceGnmSubmitAndFlipCommandBuffers(u32 count, u32* dcb_gpu_addrs ccb_sizes_in_bytes); } -int PS4_SYSV_ABI sceGnmSubmitAndFlipCommandBuffersForWorkload() { - LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceGnmSubmitCommandBuffers(u32 count, const u32* dcb_gpu_addrs[], - u32* dcb_sizes_in_bytes, const u32* ccb_gpu_addrs[], - u32* ccb_sizes_in_bytes) { +int PS4_SYSV_ABI sceGnmSubmitCommandBuffersForWorkload(u32 workload, u32 count, + const u32* dcb_gpu_addrs[], + u32* dcb_sizes_in_bytes, + const u32* ccb_gpu_addrs[], + u32* ccb_sizes_in_bytes) { LOG_DEBUG(Lib_GnmDriver, "called"); if (!dcb_gpu_addrs || !dcb_sizes_in_bytes) { @@ -2232,9 +2244,11 @@ s32 PS4_SYSV_ABI sceGnmSubmitCommandBuffers(u32 count, const u32* dcb_gpu_addrs[ return ORBIS_OK; } -int PS4_SYSV_ABI sceGnmSubmitCommandBuffersForWorkload() { - LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceGnmSubmitCommandBuffers(u32 count, const u32* dcb_gpu_addrs[], + u32* dcb_sizes_in_bytes, const u32* ccb_gpu_addrs[], + u32* ccb_sizes_in_bytes) { + return sceGnmSubmitCommandBuffersForWorkload(count, count, dcb_gpu_addrs, dcb_sizes_in_bytes, + ccb_gpu_addrs, ccb_sizes_in_bytes); } int PS4_SYSV_ABI sceGnmSubmitDone() { diff --git a/src/core/libraries/gnmdriver/gnmdriver.h b/src/core/libraries/gnmdriver/gnmdriver.h index 115268ea8..5307b3baa 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.h +++ b/src/core/libraries/gnmdriver/gnmdriver.h @@ -4,7 +4,7 @@ #pragma once #include "common/types.h" -#include "core/libraries/kernel/event_queues.h" +#include "core/libraries/kernel/equeue.h" namespace Core::Loader { class SymbolsResolver; @@ -16,11 +16,11 @@ using namespace Kernel; s32 PS4_SYSV_ABI sceGnmAddEqEvent(SceKernelEqueue eq, u64 id, void* udata); int PS4_SYSV_ABI sceGnmAreSubmitsAllowed(); -int PS4_SYSV_ABI sceGnmBeginWorkload(); +int PS4_SYSV_ABI sceGnmBeginWorkload(u32 workload_stream, u64* workload); s32 PS4_SYSV_ABI sceGnmComputeWaitOnAddress(u32* cmdbuf, u32 size, uintptr_t addr, u32 mask, u32 cmp_func, u32 ref); int PS4_SYSV_ABI sceGnmComputeWaitSemaphore(); -int PS4_SYSV_ABI sceGnmCreateWorkloadStream(); +int PS4_SYSV_ABI sceGnmCreateWorkloadStream(u64 param1, u32* workload_stream); int PS4_SYSV_ABI sceGnmDebuggerGetAddressWatch(); int PS4_SYSV_ABI sceGnmDebuggerHaltWavefront(); int PS4_SYSV_ABI sceGnmDebuggerReadGds(); @@ -77,7 +77,7 @@ int PS4_SYSV_ABI sceGnmDriverInternalRetrieveGnmInterfaceForValidation(); int PS4_SYSV_ABI sceGnmDriverInternalVirtualQuery(); int PS4_SYSV_ABI sceGnmDriverTraceInProgress(); int PS4_SYSV_ABI sceGnmDriverTriggerCapture(); -int PS4_SYSV_ABI sceGnmEndWorkload(); +int PS4_SYSV_ABI sceGnmEndWorkload(u64 workload); s32 PS4_SYSV_ABI sceGnmFindResourcesPublic(); void PS4_SYSV_ABI sceGnmFlushGarlic(); int PS4_SYSV_ABI sceGnmGetCoredumpAddress(); @@ -210,11 +210,17 @@ s32 PS4_SYSV_ABI sceGnmSubmitAndFlipCommandBuffers(u32 count, u32* dcb_gpu_addrs u32* dcb_sizes_in_bytes, u32* ccb_gpu_addrs[], u32* ccb_sizes_in_bytes, u32 vo_handle, u32 buf_idx, u32 flip_mode, u32 flip_arg); -int PS4_SYSV_ABI sceGnmSubmitAndFlipCommandBuffersForWorkload(); +int PS4_SYSV_ABI sceGnmSubmitAndFlipCommandBuffersForWorkload( + u32 workload, u32 count, u32* dcb_gpu_addrs[], u32* dcb_sizes_in_bytes, u32* ccb_gpu_addrs[], + u32* ccb_sizes_in_bytes, u32 vo_handle, u32 buf_idx, u32 flip_mode, u32 flip_arg); s32 PS4_SYSV_ABI sceGnmSubmitCommandBuffers(u32 count, const u32* dcb_gpu_addrs[], u32* dcb_sizes_in_bytes, const u32* ccb_gpu_addrs[], u32* ccb_sizes_in_bytes); -int PS4_SYSV_ABI sceGnmSubmitCommandBuffersForWorkload(); +int PS4_SYSV_ABI sceGnmSubmitCommandBuffersForWorkload(u32 workload, u32 count, + const u32* dcb_gpu_addrs[], + u32* dcb_sizes_in_bytes, + const u32* ccb_gpu_addrs[], + u32* ccb_sizes_in_bytes); int PS4_SYSV_ABI sceGnmSubmitDone(); int PS4_SYSV_ABI sceGnmUnmapComputeQueue(); int PS4_SYSV_ABI sceGnmUnregisterAllResourcesForOwner(); diff --git a/src/core/libraries/ime/ime.cpp b/src/core/libraries/ime/ime.cpp index 0310c5153..efb988c72 100644 --- a/src/core/libraries/ime/ime.cpp +++ b/src/core/libraries/ime/ime.cpp @@ -70,21 +70,19 @@ public: } void Execute(OrbisImeEventHandler handler, OrbisImeEvent* event, bool use_param_handler) { - const auto* linker = Common::Singleton::Instance(); - if (m_ime_mode) { OrbisImeParam param = m_param.ime; if (use_param_handler) { - linker->ExecuteGuest(param.handler, param.arg, event); + Core::ExecuteGuest(param.handler, param.arg, event); } else { - linker->ExecuteGuest(handler, param.arg, event); + Core::ExecuteGuest(handler, param.arg, event); } } else { OrbisImeKeyboardParam param = m_param.key; if (use_param_handler) { - linker->ExecuteGuest(param.handler, param.arg, event); + Core::ExecuteGuest(param.handler, param.arg, event); } else { - linker->ExecuteGuest(handler, param.arg, event); + Core::ExecuteGuest(handler, param.arg, event); } } } @@ -503,4 +501,4 @@ void RegisterlibSceIme(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("fwcPR7+7Rks", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshUpdateContext2); }; -} // namespace Libraries::Ime \ No newline at end of file +} // namespace Libraries::Ime diff --git a/src/core/libraries/ime/ime_dialog_ui.cpp b/src/core/libraries/ime/ime_dialog_ui.cpp index bba45d0fe..de64de5fa 100644 --- a/src/core/libraries/ime/ime_dialog_ui.cpp +++ b/src/core/libraries/ime/ime_dialog_ui.cpp @@ -11,7 +11,7 @@ #include "common/singleton.h" #include "core/libraries/ime/ime_dialog.h" #include "core/libraries/ime/ime_dialog_ui.h" -#include "core/linker.h" +#include "core/tls.h" #include "imgui/imgui_std.h" using namespace ImGui; @@ -124,9 +124,8 @@ bool ImeDialogState::CallTextFilter() { return false; } - auto* linker = Common::Singleton::Instance(); int ret = - linker->ExecuteGuest(text_filter, out_text, &out_text_length, src_text, src_text_length); + Core::ExecuteGuest(text_filter, out_text, &out_text_length, src_text, src_text_length); if (ret != 0) { return false; @@ -147,15 +146,12 @@ bool ImeDialogState::CallKeyboardFilter(const OrbisImeKeycode* src_keycode, u16* return true; } - auto* linker = Common::Singleton::Instance(); - int ret = linker->ExecuteGuest(keyboard_filter, src_keycode, out_keycode, out_status, nullptr); - + int ret = Core::ExecuteGuest(keyboard_filter, src_keycode, out_keycode, out_status, nullptr); return ret == 0; } bool ImeDialogState::ConvertOrbisToUTF8(const char16_t* orbis_text, std::size_t orbis_text_len, char* utf8_text, std::size_t utf8_text_len) { - std::fill(utf8_text, utf8_text + utf8_text_len, '\0'); const ImWchar* orbis_text_ptr = reinterpret_cast(orbis_text); ImTextStrToUtf8(utf8_text, utf8_text_len, orbis_text_ptr, orbis_text_ptr + orbis_text_len); @@ -165,7 +161,6 @@ bool ImeDialogState::ConvertOrbisToUTF8(const char16_t* orbis_text, std::size_t bool ImeDialogState::ConvertUTF8ToOrbis(const char* utf8_text, std::size_t utf8_text_len, char16_t* orbis_text, std::size_t orbis_text_len) { - std::fill(orbis_text, orbis_text + orbis_text_len, u'\0'); ImTextStrFromUtf8(reinterpret_cast(orbis_text), orbis_text_len, utf8_text, nullptr); @@ -387,4 +382,4 @@ int ImeDialogUi::InputTextCallback(ImGuiInputTextCallbackData* data) { return 0; } -} // namespace Libraries::ImeDialog \ No newline at end of file +} // namespace Libraries::ImeDialog diff --git a/src/core/libraries/kernel/cpu_management.cpp b/src/core/libraries/kernel/cpu_management.cpp deleted file mode 100644 index 3bf609dfe..000000000 --- a/src/core/libraries/kernel/cpu_management.cpp +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/config.h" -#include "common/logging/log.h" -#include "core/libraries/kernel/cpu_management.h" - -namespace Libraries::Kernel { - -int PS4_SYSV_ABI sceKernelIsNeoMode() { - LOG_DEBUG(Kernel_Sce, "called"); - return Config::isNeoMode(); -} - -} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/event_queues.cpp b/src/core/libraries/kernel/equeue.cpp similarity index 56% rename from src/core/libraries/kernel/event_queues.cpp rename to src/core/libraries/kernel/equeue.cpp index 540c20c43..d4f7eabaa 100644 --- a/src/core/libraries/kernel/event_queues.cpp +++ b/src/core/libraries/kernel/equeue.cpp @@ -5,10 +5,143 @@ #include "common/debug.h" #include "common/logging/log.h" #include "core/libraries/error_codes.h" -#include "core/libraries/kernel/event_queues.h" +#include "core/libraries/kernel/equeue.h" +#include "core/libraries/libs.h" namespace Libraries::Kernel { +bool EqueueInternal::AddEvent(EqueueEvent& event) { + std::scoped_lock lock{m_mutex}; + + event.time_added = std::chrono::steady_clock::now(); + + const auto& it = std::ranges::find(m_events, event); + if (it != m_events.cend()) { + *it = std::move(event); + } else { + m_events.emplace_back(std::move(event)); + } + + return true; +} + +bool EqueueInternal::RemoveEvent(u64 id) { + bool has_found = false; + std::scoped_lock lock{m_mutex}; + + const auto& it = + std::ranges::find_if(m_events, [id](auto& ev) { return ev.event.ident == id; }); + if (it != m_events.cend()) { + m_events.erase(it); + has_found = true; + } + return has_found; +} + +int EqueueInternal::WaitForEvents(SceKernelEvent* ev, int num, u32 micros) { + int count = 0; + + const auto predicate = [&] { + count = GetTriggeredEvents(ev, num); + return count > 0; + }; + + if (micros == 0) { + std::unique_lock lock{m_mutex}; + m_cond.wait(lock, predicate); + } else { + std::unique_lock lock{m_mutex}; + m_cond.wait_for(lock, std::chrono::microseconds(micros), predicate); + } + + if (HasSmallTimer()) { + if (count > 0) { + const auto time_waited = std::chrono::duration_cast( + std::chrono::steady_clock::now() - m_events[0].time_added) + .count(); + count = WaitForSmallTimer(ev, num, std::max(0l, long(micros - time_waited))); + } + small_timer_event.event.data = 0; + } + + if (ev->flags & SceKernelEvent::Flags::OneShot) { + for (auto ev_id = 0u; ev_id < count; ++ev_id) { + RemoveEvent(ev->ident); + } + } + + return count; +} + +bool EqueueInternal::TriggerEvent(u64 ident, s16 filter, void* trigger_data) { + bool has_found = false; + { + std::scoped_lock lock{m_mutex}; + + for (auto& event : m_events) { + if ((event.event.ident == ident) && (event.event.filter == filter)) { + event.Trigger(trigger_data); + has_found = true; + } + } + } + m_cond.notify_one(); + return has_found; +} + +int EqueueInternal::GetTriggeredEvents(SceKernelEvent* ev, int num) { + int count = 0; + + for (auto& event : m_events) { + if (event.IsTriggered()) { + if (event.event.flags & SceKernelEvent::Flags::Clear) { + event.Reset(); + } + ev[count++] = event.event; + if (count == num) { + break; + } + } + } + + return count; +} + +bool EqueueInternal::AddSmallTimer(EqueueEvent& ev) { + // We assume that only one timer event (with the same ident across calls) + // can be posted to the queue, based on observations so far. In the opposite case, + // the small timer storage and wait logic should be reworked. + ASSERT(!HasSmallTimer() || small_timer_event.event.ident == ev.event.ident); + ev.time_added = std::chrono::steady_clock::now(); + small_timer_event = std::move(ev); + return true; +} + +int EqueueInternal::WaitForSmallTimer(SceKernelEvent* ev, int num, u32 micros) { + int count{}; + + ASSERT(num == 1); + + auto curr_clock = std::chrono::steady_clock::now(); + const auto wait_end_us = curr_clock + std::chrono::microseconds{micros}; + + do { + curr_clock = std::chrono::steady_clock::now(); + { + std::scoped_lock lock{m_mutex}; + if ((curr_clock - small_timer_event.time_added) > + std::chrono::microseconds{small_timer_event.event.data}) { + ev[count++] = small_timer_event.event; + small_timer_event.event.data = 0; + break; + } + } + std::this_thread::yield(); + } while (curr_clock < wait_end_us); + + return count; +} + extern boost::asio::io_context io_context; extern void KernelSignalRequest(); @@ -42,8 +175,7 @@ int PS4_SYSV_ABI sceKernelCreateEqueue(SceKernelEqueue* eq, const char* name) { LOG_INFO(Kernel_Event, "name = {}", name); - *eq = new EqueueInternal; - (*eq)->setName(std::string(name)); + *eq = new EqueueInternal(name); return ORBIS_OK; } @@ -211,4 +343,19 @@ int PS4_SYSV_ABI sceKernelDeleteUserEvent(SceKernelEqueue eq, int id) { s16 PS4_SYSV_ABI sceKernelGetEventFilter(const SceKernelEvent* ev) { return ev->filter; } + +void RegisterEventQueue(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("D0OdFMjp46I", "libkernel", 1, "libkernel", 1, 1, sceKernelCreateEqueue); + LIB_FUNCTION("jpFjmgAC5AE", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteEqueue); + LIB_FUNCTION("fzyMKs9kim0", "libkernel", 1, "libkernel", 1, 1, sceKernelWaitEqueue); + LIB_FUNCTION("vz+pg2zdopI", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventUserData); + LIB_FUNCTION("4R6-OvI2cEA", "libkernel", 1, "libkernel", 1, 1, sceKernelAddUserEvent); + LIB_FUNCTION("WDszmSbWuDk", "libkernel", 1, "libkernel", 1, 1, sceKernelAddUserEventEdge); + LIB_FUNCTION("R74tt43xP6k", "libkernel", 1, "libkernel", 1, 1, sceKernelAddHRTimerEvent); + LIB_FUNCTION("F6e0kwo4cnk", "libkernel", 1, "libkernel", 1, 1, sceKernelTriggerUserEvent); + LIB_FUNCTION("LJDwdSNTnDg", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteUserEvent); + LIB_FUNCTION("mJ7aghmgvfc", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventId); + LIB_FUNCTION("23CPPI1tyBY", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventFilter); +} + } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/event_queue.h b/src/core/libraries/kernel/equeue.h similarity index 89% rename from src/core/libraries/kernel/event_queue.h rename to src/core/libraries/kernel/equeue.h index 30fdb41e3..5a13bdecd 100644 --- a/src/core/libraries/kernel/event_queue.h +++ b/src/core/libraries/kernel/equeue.h @@ -7,11 +7,14 @@ #include #include #include - #include #include "common/types.h" +namespace Core::Loader { +class SymbolsResolver; +} + namespace Libraries::Kernel { class EqueueInternal; @@ -89,14 +92,12 @@ private: class EqueueInternal { public: - EqueueInternal() = default; - virtual ~EqueueInternal(); - void setName(const std::string& m_name) { - this->m_name = m_name; - } - const auto& GetName() const { + explicit EqueueInternal(std::string_view name) : m_name(name) {} + + std::string_view GetName() const { return m_name; } + bool AddEvent(EqueueEvent& event); bool RemoveEvent(u64 id); int WaitForEvents(SceKernelEvent* ev, int num, u32 micros); @@ -107,6 +108,7 @@ public: bool HasSmallTimer() const { return small_timer_event.event.data != 0; } + int WaitForSmallTimer(SceKernelEvent* ev, int num, u32 micros); private: @@ -117,4 +119,9 @@ private: std::condition_variable m_cond; }; +using SceKernelUseconds = u32; +using SceKernelEqueue = EqueueInternal*; + +void RegisterEventQueue(Core::Loader::SymbolsResolver* sym); + } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/event_flag/event_flag.h b/src/core/libraries/kernel/event_flag/event_flag.h deleted file mode 100644 index 2147e3f15..000000000 --- a/src/core/libraries/kernel/event_flag/event_flag.h +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/types.h" -#include "event_flag_codes.h" -#include "event_flag_obj.h" - -namespace Core::Loader { -class SymbolsResolver; -} - -namespace Libraries::Kernel { - -using OrbisKernelUseconds = u32; -using OrbisKernelEventFlag = EventFlagInternal*; - -struct OrbisKernelEventFlagOptParam { - size_t size; -}; - -int PS4_SYSV_ABI sceKernelCreateEventFlag(OrbisKernelEventFlag* ef, const char* pName, u32 attr, - u64 initPattern, - const OrbisKernelEventFlagOptParam* pOptParam); -int PS4_SYSV_ABI sceKernelDeleteEventFlag(OrbisKernelEventFlag ef); -int PS4_SYSV_ABI sceKernelOpenEventFlag(); -int PS4_SYSV_ABI sceKernelCloseEventFlag(); -int PS4_SYSV_ABI sceKernelClearEventFlag(OrbisKernelEventFlag ef, u64 bitPattern); -int PS4_SYSV_ABI sceKernelCancelEventFlag(OrbisKernelEventFlag ef, u64 setPattern, - int* pNumWaitThreads); -int PS4_SYSV_ABI sceKernelSetEventFlag(OrbisKernelEventFlag ef, u64 bitPattern); -int PS4_SYSV_ABI sceKernelPollEventFlag(OrbisKernelEventFlag ef, u64 bitPattern, u32 waitMode, - u64* pResultPat); -int PS4_SYSV_ABI sceKernelWaitEventFlag(OrbisKernelEventFlag ef, u64 bitPattern, u32 waitMode, - u64* pResultPat, OrbisKernelUseconds* pTimeout); - -void RegisterKernelEventFlag(Core::Loader::SymbolsResolver* sym); - -} // namespace Libraries::Kernel \ No newline at end of file diff --git a/src/core/libraries/kernel/event_flag/event_flag_codes.h b/src/core/libraries/kernel/event_flag/event_flag_codes.h deleted file mode 100644 index 92b265c8d..000000000 --- a/src/core/libraries/kernel/event_flag/event_flag_codes.h +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -constexpr int ORBIS_KERNEL_EVF_ATTR_TH_FIFO = 0x01; -constexpr int ORBIS_KERNEL_EVF_ATTR_TH_PRIO = 0x02; -constexpr int ORBIS_KERNEL_EVF_ATTR_SINGLE = 0x10; -constexpr int ORBIS_KERNEL_EVF_ATTR_MULTI = 0x20; - -constexpr int ORBIS_KERNEL_EVF_WAITMODE_AND = 0x01; -constexpr int ORBIS_KERNEL_EVF_WAITMODE_OR = 0x02; -constexpr int ORBIS_KERNEL_EVF_WAITMODE_CLEAR_ALL = 0x10; -constexpr int ORBIS_KERNEL_EVF_WAITMODE_CLEAR_PAT = 0x20; \ No newline at end of file diff --git a/src/core/libraries/kernel/event_flag/event_flag_obj.cpp b/src/core/libraries/kernel/event_flag/event_flag_obj.cpp deleted file mode 100644 index 6d6dcf7a9..000000000 --- a/src/core/libraries/kernel/event_flag/event_flag_obj.cpp +++ /dev/null @@ -1,111 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include - -#include "core/libraries/error_codes.h" -#include "event_flag_obj.h" - -namespace Libraries::Kernel { -int EventFlagInternal::Wait(u64 bits, WaitMode wait_mode, ClearMode clear_mode, u64* result, - u32* ptr_micros) { - std::unique_lock lock{m_mutex}; - - uint32_t micros = 0; - bool infinitely = true; - if (ptr_micros != nullptr) { - micros = *ptr_micros; - infinitely = false; - } - - if (m_thread_mode == ThreadMode::Single && m_waiting_threads > 0) { - return ORBIS_KERNEL_ERROR_EPERM; - } - - auto const start = std::chrono::system_clock::now(); - m_waiting_threads++; - auto waitFunc = [this, wait_mode, bits] { - return (m_status == Status::Canceled || m_status == Status::Deleted || - (wait_mode == WaitMode::And && (m_bits & bits) == bits) || - (wait_mode == WaitMode::Or && (m_bits & bits) != 0)); - }; - - if (infinitely) { - m_cond_var.wait(lock, waitFunc); - } else { - if (!m_cond_var.wait_for(lock, std::chrono::microseconds(micros), waitFunc)) { - if (result != nullptr) { - *result = m_bits; - } - *ptr_micros = 0; - --m_waiting_threads; - return ORBIS_KERNEL_ERROR_ETIMEDOUT; - } - } - --m_waiting_threads; - if (result != nullptr) { - *result = m_bits; - } - - auto elapsed = std::chrono::duration_cast( - std::chrono::system_clock::now() - start) - .count(); - if (result != nullptr) { - *result = m_bits; - } - - if (ptr_micros != nullptr) { - *ptr_micros = (elapsed >= micros ? 0 : micros - elapsed); - } - - if (m_status == Status::Canceled) { - return ORBIS_KERNEL_ERROR_ECANCELED; - } else if (m_status == Status::Deleted) { - return ORBIS_KERNEL_ERROR_EACCES; - } - - if (clear_mode == ClearMode::All) { - m_bits = 0; - } else if (clear_mode == ClearMode::Bits) { - m_bits &= ~bits; - } - - return ORBIS_OK; -} - -int EventFlagInternal::Poll(u64 bits, WaitMode wait_mode, ClearMode clear_mode, u64* result) { - u32 micros = 0; - auto ret = Wait(bits, wait_mode, clear_mode, result, µs); - if (ret == ORBIS_KERNEL_ERROR_ETIMEDOUT) { - // Poll returns EBUSY instead. - ret = ORBIS_KERNEL_ERROR_EBUSY; - } - return ret; -} - -void EventFlagInternal::Set(u64 bits) { - std::unique_lock lock{m_mutex}; - - while (m_status != Status::Set) { - m_mutex.unlock(); - std::this_thread::sleep_for(std::chrono::microseconds(10)); - m_mutex.lock(); - } - - m_bits |= bits; - - m_cond_var.notify_all(); -} - -void EventFlagInternal::Clear(u64 bits) { - std::unique_lock lock{m_mutex}; - while (m_status != Status::Set) { - m_mutex.unlock(); - std::this_thread::sleep_for(std::chrono::microseconds(10)); - m_mutex.lock(); - } - - m_bits &= bits; -} - -} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/event_flag/event_flag_obj.h b/src/core/libraries/kernel/event_flag/event_flag_obj.h deleted file mode 100644 index 0454b4d78..000000000 --- a/src/core/libraries/kernel/event_flag/event_flag_obj.h +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include - -#include "common/types.h" - -namespace Libraries::Kernel { - -class EventFlagInternal { -public: - enum class ClearMode { None, All, Bits }; - - enum class WaitMode { And, Or }; - - enum class ThreadMode { Single, Multi }; - - enum class QueueMode { Fifo, ThreadPrio }; - - EventFlagInternal(const std::string& name, ThreadMode thread_mode, QueueMode queue_mode, - uint64_t bits) - : m_name(name), m_thread_mode(thread_mode), m_queue_mode(queue_mode), m_bits(bits) {}; - - int Wait(u64 bits, WaitMode wait_mode, ClearMode clear_mode, u64* result, u32* ptr_micros); - int Poll(u64 bits, WaitMode wait_mode, ClearMode clear_mode, u64* result); - void Set(u64 bits); - void Clear(u64 bits); - -private: - enum class Status { Set, Canceled, Deleted }; - - std::mutex m_mutex; - std::condition_variable m_cond_var; - Status m_status = Status::Set; - int m_waiting_threads = 0; - std::string m_name; - ThreadMode m_thread_mode = ThreadMode::Single; - QueueMode m_queue_mode = QueueMode::Fifo; - u64 m_bits = 0; -}; -} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/event_queue.cpp b/src/core/libraries/kernel/event_queue.cpp deleted file mode 100644 index 88918bf54..000000000 --- a/src/core/libraries/kernel/event_queue.cpp +++ /dev/null @@ -1,150 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include - -#include "common/assert.h" -#include "core/libraries/kernel/event_queue.h" - -namespace Libraries::Kernel { - -EqueueInternal::~EqueueInternal() = default; - -bool EqueueInternal::AddEvent(EqueueEvent& event) { - std::scoped_lock lock{m_mutex}; - - event.time_added = std::chrono::steady_clock::now(); - - const auto& it = std::ranges::find(m_events, event); - if (it != m_events.cend()) { - *it = std::move(event); - } else { - m_events.emplace_back(std::move(event)); - } - - return true; -} - -bool EqueueInternal::RemoveEvent(u64 id) { - bool has_found = false; - std::scoped_lock lock{m_mutex}; - - const auto& it = - std::ranges::find_if(m_events, [id](auto& ev) { return ev.event.ident == id; }); - if (it != m_events.cend()) { - m_events.erase(it); - has_found = true; - } - return has_found; -} - -int EqueueInternal::WaitForEvents(SceKernelEvent* ev, int num, u32 micros) { - int count = 0; - - const auto predicate = [&] { - count = GetTriggeredEvents(ev, num); - return count > 0; - }; - - if (micros == 0) { - std::unique_lock lock{m_mutex}; - m_cond.wait(lock, predicate); - } else { - std::unique_lock lock{m_mutex}; - m_cond.wait_for(lock, std::chrono::microseconds(micros), predicate); - } - - if (HasSmallTimer()) { - if (count > 0) { - const auto time_waited = std::chrono::duration_cast( - std::chrono::steady_clock::now() - m_events[0].time_added) - .count(); - count = WaitForSmallTimer(ev, num, std::max(0l, long(micros - time_waited))); - } - small_timer_event.event.data = 0; - } - - if (ev->flags & SceKernelEvent::Flags::OneShot) { - for (auto ev_id = 0u; ev_id < count; ++ev_id) { - RemoveEvent(ev->ident); - } - } - - return count; -} - -bool EqueueInternal::TriggerEvent(u64 ident, s16 filter, void* trigger_data) { - bool has_found = false; - { - std::scoped_lock lock{m_mutex}; - - for (auto& event : m_events) { - if ((event.event.ident == ident) && (event.event.filter == filter)) { - event.Trigger(trigger_data); - has_found = true; - } - } - } - m_cond.notify_one(); - return has_found; -} - -int EqueueInternal::GetTriggeredEvents(SceKernelEvent* ev, int num) { - int count = 0; - - for (auto& event : m_events) { - if (event.IsTriggered()) { - if (event.event.flags & SceKernelEvent::Flags::Clear) { - event.Reset(); - } - - ev[count++] = event.event; - - if (count == num) { - break; - } - } - } - - return count; -} - -bool EqueueInternal::AddSmallTimer(EqueueEvent& ev) { - // We assume that only one timer event (with the same ident across calls) - // can be posted to the queue, based on observations so far. In the opposite case, - // the small timer storage and wait logic should be reworked. - ASSERT(!HasSmallTimer() || small_timer_event.event.ident == ev.event.ident); - ev.time_added = std::chrono::steady_clock::now(); - small_timer_event = std::move(ev); - return true; -} - -int EqueueInternal::WaitForSmallTimer(SceKernelEvent* ev, int num, u32 micros) { - int count{}; - - ASSERT(num == 1); - - auto curr_clock = std::chrono::steady_clock::now(); - const auto wait_end_us = curr_clock + std::chrono::microseconds{micros}; - - do { - curr_clock = std::chrono::steady_clock::now(); - - { - std::unique_lock lock{m_mutex}; - if ((curr_clock - small_timer_event.time_added) > - std::chrono::microseconds{small_timer_event.event.data}) { - ev[count++] = small_timer_event.event; - small_timer_event.event.data = 0; - break; - } - } - - std::this_thread::yield(); - - } while (curr_clock < wait_end_us); - - return count; -} - -} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/event_queues.h b/src/core/libraries/kernel/event_queues.h deleted file mode 100644 index d400ff187..000000000 --- a/src/core/libraries/kernel/event_queues.h +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/libraries/kernel/event_queue.h" - -namespace Libraries::Kernel { - -using SceKernelUseconds = u32; -using SceKernelEqueue = EqueueInternal*; - -int PS4_SYSV_ABI sceKernelCreateEqueue(SceKernelEqueue* eq, const char* name); -int PS4_SYSV_ABI sceKernelDeleteEqueue(SceKernelEqueue eq); -int PS4_SYSV_ABI sceKernelWaitEqueue(SceKernelEqueue eq, SceKernelEvent* ev, int num, int* out, - SceKernelUseconds* timo); -void* PS4_SYSV_ABI sceKernelGetEventUserData(const SceKernelEvent* ev); -u64 PS4_SYSV_ABI sceKernelGetEventId(const SceKernelEvent* ev); -int PS4_SYSV_ABI sceKernelTriggerUserEvent(SceKernelEqueue eq, int id, void* udata); -int PS4_SYSV_ABI sceKernelDeleteUserEvent(SceKernelEqueue eq, int id); -int PS4_SYSV_ABI sceKernelAddUserEvent(SceKernelEqueue eq, int id); -int PS4_SYSV_ABI sceKernelAddUserEventEdge(SceKernelEqueue eq, int id); -s32 PS4_SYSV_ABI sceKernelAddHRTimerEvent(SceKernelEqueue eq, int id, timespec* ts, void* udata); -s16 PS4_SYSV_ABI sceKernelGetEventFilter(const SceKernelEvent* ev); - -} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index e6b657c3a..56286eb98 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -9,11 +9,11 @@ #include "core/libraries/error_codes.h" #include "core/libraries/kernel/file_system.h" #include "core/libraries/libs.h" -#include "libkernel.h" +#include "kernel.h" namespace Libraries::Kernel { -std::vector GetDirectoryEntries(const std::filesystem::path& path) { +auto GetDirectoryEntries(const std::filesystem::path& path) { std::vector files; for (const auto& entry : std::filesystem::directory_iterator(path)) { auto& dir_entry = files.emplace_back(); @@ -618,7 +618,7 @@ s32 PS4_SYSV_ABI sceKernelRename(const char* from, const char* to) { return ORBIS_OK; } -void fileSystemSymbolsRegister(Core::Loader::SymbolsResolver* sym) { +void RegisterFileSystem(Core::Loader::SymbolsResolver* sym) { std::srand(std::time(nullptr)); LIB_FUNCTION("1G3lF1Gg1k8", "libkernel", 1, "libkernel", 1, 1, sceKernelOpen); LIB_FUNCTION("wuCroIGjt2g", "libScePosix", 1, "libkernel", 1, 1, posix_open); diff --git a/src/core/libraries/kernel/file_system.h b/src/core/libraries/kernel/file_system.h index 1174dd86d..0bc473ec0 100644 --- a/src/core/libraries/kernel/file_system.h +++ b/src/core/libraries/kernel/file_system.h @@ -4,7 +4,7 @@ #pragma once #include "common/types.h" -#include "core/libraries/kernel/time_management.h" +#include "core/libraries/kernel/time.h" namespace Core::Loader { class SymbolsResolver; @@ -65,11 +65,6 @@ constexpr int ORBIS_KERNEL_O_DSYNC = 0x1000; constexpr int ORBIS_KERNEL_O_DIRECT = 0x00010000; constexpr int ORBIS_KERNEL_O_DIRECTORY = 0x00020000; -int PS4_SYSV_ABI sceKernelOpen(const char* path, int flags, /* SceKernelMode*/ u16 mode); - -int PS4_SYSV_ABI posix_open(const char* path, int flags, /* SceKernelMode*/ u16 mode); -s64 PS4_SYSV_ABI lseek(int d, s64 offset, int whence); - -void fileSystemSymbolsRegister(Core::Loader::SymbolsResolver* sym); +void RegisterFileSystem(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/kernel.cpp b/src/core/libraries/kernel/kernel.cpp new file mode 100644 index 000000000..512e77bc6 --- /dev/null +++ b/src/core/libraries/kernel/kernel.cpp @@ -0,0 +1,250 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include + +#include + +#include "common/assert.h" +#include "common/debug.h" +#include "common/logging/log.h" +#include "common/polyfill_thread.h" +#include "common/singleton.h" +#include "common/thread.h" +#include "core/file_sys/fs.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/kernel/equeue.h" +#include "core/libraries/kernel/file_system.h" +#include "core/libraries/kernel/kernel.h" +#include "core/libraries/kernel/memory.h" +#include "core/libraries/kernel/process.h" +#include "core/libraries/kernel/threads.h" +#include "core/libraries/kernel/threads/exception.h" +#include "core/libraries/kernel/time.h" +#include "core/libraries/libs.h" +#include "core/linker.h" + +#ifdef _WIN64 +#include +#include +#include +#else +#ifdef __APPLE__ +#include +#endif +#endif + +namespace Libraries::Kernel { + +static u64 g_stack_chk_guard = 0xDEADBEEF54321ABC; // dummy return + +boost::asio::io_context io_context; +std::mutex m_asio_req; +std::condition_variable_any cv_asio_req; +std::atomic asio_requests; +std::jthread service_thread; + +void KernelSignalRequest() { + std::unique_lock lock{m_asio_req}; + ++asio_requests; + cv_asio_req.notify_one(); +} + +static void KernelServiceThread(std::stop_token stoken) { + Common::SetCurrentThreadName("shadPS4:Kernel_ServiceThread"); + + while (!stoken.stop_requested()) { + HLE_TRACE; + { + std::unique_lock lock{m_asio_req}; + Common::CondvarWait(cv_asio_req, lock, stoken, [] { return asio_requests != 0; }); + } + if (stoken.stop_requested()) { + break; + } + + io_context.run(); + io_context.reset(); + + asio_requests = 0; + } +} + +static PS4_SYSV_ABI void stack_chk_fail() { + UNREACHABLE(); +} + +struct iovec { + void* iov_base; /* Base address. */ + size_t iov_len; /* Length. */ +}; + +size_t PS4_SYSV_ABI _writev(int fd, const struct iovec* iov, int iovcn) { + size_t total_written = 0; + for (int i = 0; i < iovcn; i++) { + total_written += ::fwrite(iov[i].iov_base, 1, iov[i].iov_len, stdout); + } + return total_written; +} + +static thread_local int g_posix_errno = 0; + +int* PS4_SYSV_ABI __Error() { + return &g_posix_errno; +} + +void ErrSceToPosix(int result) { + const int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP + ? result + -SCE_KERNEL_ERROR_UNKNOWN + : POSIX_EOTHER; + g_posix_errno = rt; +} + +int ErrnoToSceKernelError(int e) { + const auto res = SCE_KERNEL_ERROR_UNKNOWN + e; + return res > SCE_KERNEL_ERROR_ESTOP ? SCE_KERNEL_ERROR_UNKNOWN : res; +} + +void SetPosixErrno(int e) { + // Some error numbers are different between supported OSes or the PS4 + switch (e) { + case EPERM: + g_posix_errno = POSIX_EPERM; + break; + case EAGAIN: + g_posix_errno = POSIX_EAGAIN; + break; + case ENOMEM: + g_posix_errno = POSIX_ENOMEM; + break; + case EINVAL: + g_posix_errno = POSIX_EINVAL; + break; + case ENOSPC: + g_posix_errno = POSIX_ENOSPC; + break; + case ERANGE: + g_posix_errno = POSIX_ERANGE; + break; + case EDEADLK: + g_posix_errno = POSIX_EDEADLK; + break; + case ETIMEDOUT: + g_posix_errno = POSIX_ETIMEDOUT; + break; + default: + g_posix_errno = e; + } +} + +static uint64_t g_mspace_atomic_id_mask = 0; +static uint64_t g_mstate_table[64] = {0}; + +struct HeapInfoInfo { + uint64_t size = sizeof(HeapInfoInfo); + uint32_t flag; + uint32_t getSegmentInfo; + uint64_t* mspace_atomic_id_mask; + uint64_t* mstate_table; +}; + +void PS4_SYSV_ABI sceLibcHeapGetTraceInfo(HeapInfoInfo* info) { + info->mspace_atomic_id_mask = &g_mspace_atomic_id_mask; + info->mstate_table = g_mstate_table; + info->getSegmentInfo = 0; +} + +s64 PS4_SYSV_ABI ps4__write(int d, const char* buf, std::size_t nbytes) { + if (d <= 2) { // stdin,stdout,stderr + std::string_view str{buf}; + if (str[nbytes - 1] == '\n') { + str = str.substr(0, nbytes - 1); + } + LOG_INFO(Tty, "{}", str); + return nbytes; + } + LOG_ERROR(Kernel, "(STUBBED) called d = {} nbytes = {} ", d, nbytes); + UNREACHABLE(); + return ORBIS_OK; +} + +s64 PS4_SYSV_ABI ps4__read(int d, void* buf, u64 nbytes) { + ASSERT_MSG(d == 0, "d is not 0!"); + + return static_cast( + strlen(std::fgets(static_cast(buf), static_cast(nbytes), stdin))); +} + +struct OrbisKernelUuid { + u32 timeLow; + u16 timeMid; + u16 timeHiAndVersion; + u8 clockSeqHiAndReserved; + u8 clockSeqLow; + u8 node[6]; +}; + +int PS4_SYSV_ABI sceKernelUuidCreate(OrbisKernelUuid* orbisUuid) { +#ifdef _WIN64 + UUID uuid; + UuidCreate(&uuid); + orbisUuid->timeLow = uuid.Data1; + orbisUuid->timeMid = uuid.Data2; + orbisUuid->timeHiAndVersion = uuid.Data3; + orbisUuid->clockSeqHiAndReserved = uuid.Data4[0]; + orbisUuid->clockSeqLow = uuid.Data4[1]; + for (int i = 0; i < 6; i++) { + orbisUuid->node[i] = uuid.Data4[2 + i]; + } +#else + LOG_ERROR(Kernel, "sceKernelUuidCreate: Add linux"); +#endif + return 0; +} + +const char* PS4_SYSV_ABI sceKernelGetFsSandboxRandomWord() { + const char* path = "sys"; + return path; +} + +int PS4_SYSV_ABI posix_connect() { + return -1; +} + +int PS4_SYSV_ABI _sigprocmask() { + return ORBIS_OK; +} + +int PS4_SYSV_ABI posix_getpagesize() { + return 4096; +} + +void RegisterKernel(Core::Loader::SymbolsResolver* sym) { + service_thread = std::jthread{KernelServiceThread}; + + Libraries::Kernel::RegisterFileSystem(sym); + Libraries::Kernel::RegisterTime(sym); + Libraries::Kernel::RegisterThreads(sym); + Libraries::Kernel::RegisterKernelEventFlag(sym); + Libraries::Kernel::RegisterMemory(sym); + Libraries::Kernel::RegisterEventQueue(sym); + Libraries::Kernel::RegisterProcess(sym); + Libraries::Kernel::RegisterException(sym); + + LIB_OBJ("f7uOxY9mM1U", "libkernel", 1, "libkernel", 1, 1, &g_stack_chk_guard); + LIB_FUNCTION("JGfTMBOdUJo", "libkernel", 1, "libkernel", 1, 1, sceKernelGetFsSandboxRandomWord); + LIB_FUNCTION("XVL8So3QJUk", "libkernel", 1, "libkernel", 1, 1, posix_connect); + LIB_FUNCTION("6xVpy0Fdq+I", "libkernel", 1, "libkernel", 1, 1, _sigprocmask); + LIB_FUNCTION("Xjoosiw+XPI", "libkernel", 1, "libkernel", 1, 1, sceKernelUuidCreate); + LIB_FUNCTION("Ou3iL1abvng", "libkernel", 1, "libkernel", 1, 1, stack_chk_fail); + LIB_FUNCTION("9BcDykPmo1I", "libkernel", 1, "libkernel", 1, 1, __Error); + LIB_FUNCTION("YSHRBRLn2pI", "libkernel", 1, "libkernel", 1, 1, _writev); + LIB_FUNCTION("DRuBt2pvICk", "libkernel", 1, "libkernel", 1, 1, ps4__read); + LIB_FUNCTION("k+AXqu2-eBc", "libkernel", 1, "libkernel", 1, 1, posix_getpagesize); + LIB_FUNCTION("k+AXqu2-eBc", "libScePosix", 1, "libkernel", 1, 1, posix_getpagesize); + LIB_FUNCTION("NWtTN10cJzE", "libSceLibcInternalExt", 1, "libSceLibcInternal", 1, 1, + sceLibcHeapGetTraceInfo); + LIB_FUNCTION("FxVZqBAA7ks", "libkernel", 1, "libkernel", 1, 1, ps4__write); +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/kernel.h b/src/core/libraries/kernel/kernel.h new file mode 100644 index 000000000..f6865d22f --- /dev/null +++ b/src/core/libraries/kernel/kernel.h @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include "common/logging/log.h" +#include "common/types.h" +#include "core/libraries/error_codes.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::Kernel { + +void ErrSceToPosix(int result); +int ErrnoToSceKernelError(int e); +void SetPosixErrno(int e); + +template +struct StringLiteral { + constexpr StringLiteral(const char (&str)[N]) { + std::copy_n(str, N, value); + } + + char value[N]; +}; + +template +struct WrapperImpl; + +template +struct WrapperImpl { + static constexpr StringLiteral Name{name}; + static R PS4_SYSV_ABI wrap(Args... args) { + u32 ret = f(args...); + if (ret != 0) { + // LOG_ERROR(Lib_Kernel, "Function {} returned {}", std::string_view{name.value}, ret); + ret += SCE_KERNEL_ERROR_UNKNOWN; + } + return ret; + } +}; + +template +constexpr auto OrbisWrapper = WrapperImpl::wrap; + +#define ORBIS(func) WrapperImpl<#func, decltype(&func), func>::wrap + +int* PS4_SYSV_ABI __Error(); + +void RegisterKernel(Core::Loader::SymbolsResolver* sym); + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/libkernel.cpp b/src/core/libraries/kernel/libkernel.cpp deleted file mode 100644 index b3eb81ec8..000000000 --- a/src/core/libraries/kernel/libkernel.cpp +++ /dev/null @@ -1,509 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include - -#include - -#include "common/assert.h" -#include "common/debug.h" -#include "common/elf_info.h" -#include "common/logging/log.h" -#include "common/polyfill_thread.h" -#include "common/singleton.h" -#include "common/thread.h" -#include "core/file_format/psf.h" -#include "core/file_sys/fs.h" -#include "core/libraries/error_codes.h" -#include "core/libraries/kernel/cpu_management.h" -#include "core/libraries/kernel/event_flag/event_flag.h" -#include "core/libraries/kernel/event_queues.h" -#include "core/libraries/kernel/file_system.h" -#include "core/libraries/kernel/libkernel.h" -#include "core/libraries/kernel/memory_management.h" -#include "core/libraries/kernel/thread_management.h" -#include "core/libraries/kernel/time_management.h" -#include "core/libraries/libs.h" -#include "core/linker.h" -#include "core/memory.h" - -#ifdef _WIN64 -#include -#include -#include -#else -#include -#ifdef __APPLE__ -#include -#endif -#endif - -namespace Libraries::Kernel { - -static u64 g_stack_chk_guard = 0xDEADBEEF54321ABC; // dummy return - -boost::asio::io_context io_context; -std::mutex m_asio_req; -std::condition_variable_any cv_asio_req; -std::atomic asio_requests; -std::jthread service_thread; - -void KernelSignalRequest() { - std::unique_lock lock{m_asio_req}; - ++asio_requests; - cv_asio_req.notify_one(); -} - -static void KernelServiceThread(std::stop_token stoken) { - Common::SetCurrentThreadName("shadPS4:Kernel_ServiceThread"); - - while (!stoken.stop_requested()) { - HLE_TRACE; - { - std::unique_lock lock{m_asio_req}; - Common::CondvarWait(cv_asio_req, lock, stoken, [] { return asio_requests != 0; }); - } - if (stoken.stop_requested()) { - break; - } - - io_context.run(); - io_context.reset(); - - asio_requests = 0; - } -} - -static void* PS4_SYSV_ABI sceKernelGetProcParam() { - auto* linker = Common::Singleton::Instance(); - return reinterpret_cast(linker->GetProcParam()); -} - -static PS4_SYSV_ABI void stack_chk_fail() { - UNREACHABLE(); -} - -int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len) { - LOG_INFO(Kernel_Vmm, "addr = {}, len = {:#x}", fmt::ptr(addr), len); - if (len == 0) { - return ORBIS_OK; - } - auto* memory = Core::Memory::Instance(); - memory->UnmapMemory(std::bit_cast(addr), len); - return SCE_OK; -} - -struct iovec { - void* iov_base; /* Base address. */ - size_t iov_len; /* Length. */ -}; - -size_t PS4_SYSV_ABI _writev(int fd, const struct iovec* iov, int iovcn) { - // weird it gives fd ==0 and writes to stdout , i am not sure if it that is valid (found in - // openorbis) - size_t total_written = 0; - for (int i = 0; i < iovcn; i++) { - total_written += ::fwrite(iov[i].iov_base, 1, iov[i].iov_len, stdout); - } - return total_written; -} - -static thread_local int g_posix_errno = 0; -int* PS4_SYSV_ABI __Error() { - return &g_posix_errno; -} - -void ErrSceToPosix(int result) { - const int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - g_posix_errno = rt; -} - -int ErrnoToSceKernelError(int e) { - const auto res = SCE_KERNEL_ERROR_UNKNOWN + e; - return res > SCE_KERNEL_ERROR_ESTOP ? SCE_KERNEL_ERROR_UNKNOWN : res; -} - -void SetPosixErrno(int e) { - // Some error numbers are different between supported OSes or the PS4 - switch (e) { - case EPERM: - g_posix_errno = POSIX_EPERM; - break; - case EAGAIN: - g_posix_errno = POSIX_EAGAIN; - break; - case ENOMEM: - g_posix_errno = POSIX_ENOMEM; - break; - case EINVAL: - g_posix_errno = POSIX_EINVAL; - break; - case ENOSPC: - g_posix_errno = POSIX_ENOSPC; - break; - case ERANGE: - g_posix_errno = POSIX_ERANGE; - break; - case EDEADLK: - g_posix_errno = POSIX_EDEADLK; - break; - case ETIMEDOUT: - g_posix_errno = POSIX_ETIMEDOUT; - break; - default: - g_posix_errno = e; - } -} - -int PS4_SYSV_ABI sceKernelMmap(void* addr, u64 len, int prot, int flags, int fd, size_t offset, - void** res) { - LOG_INFO(Kernel_Vmm, "called addr = {}, len = {}, prot = {}, flags = {}, fd = {}, offset = {}", - fmt::ptr(addr), len, prot, flags, fd, offset); - auto* h = Common::Singleton::Instance(); - auto* memory = Core::Memory::Instance(); - const auto mem_prot = static_cast(prot); - const auto mem_flags = static_cast(flags); - if (fd == -1) { - return memory->MapMemory(res, std::bit_cast(addr), len, mem_prot, mem_flags, - Core::VMAType::Flexible); - } else { - const uintptr_t handle = h->GetFile(fd)->f.GetFileMapping(); - return memory->MapFile(res, std::bit_cast(addr), len, mem_prot, mem_flags, handle, - offset); - } -} - -void* PS4_SYSV_ABI posix_mmap(void* addr, u64 len, int prot, int flags, int fd, u64 offset) { - void* ptr; - LOG_INFO(Kernel_Vmm, "posix mmap redirect to sceKernelMmap"); - // posix call the difference is that there is a different behaviour when it doesn't return 0 or - // SCE_OK - const VAddr ret_addr = (VAddr)__builtin_return_address(0); - int result = sceKernelMmap(addr, len, prot, flags, fd, offset, &ptr); - ASSERT(result == 0); - return ptr; -} - -s32 PS4_SYSV_ABI sceKernelConfiguredFlexibleMemorySize(u64* sizeOut) { - if (sizeOut == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - - auto* memory = Core::Memory::Instance(); - *sizeOut = memory->GetTotalFlexibleSize(); - return ORBIS_OK; -} - -static uint64_t g_mspace_atomic_id_mask = 0; -static uint64_t g_mstate_table[64] = {0}; - -struct HeapInfoInfo { - uint64_t size = sizeof(HeapInfoInfo); - uint32_t flag; - uint32_t getSegmentInfo; - uint64_t* mspace_atomic_id_mask; - uint64_t* mstate_table; -}; - -void PS4_SYSV_ABI sceLibcHeapGetTraceInfo(HeapInfoInfo* info) { - info->mspace_atomic_id_mask = &g_mspace_atomic_id_mask; - info->mstate_table = g_mstate_table; - info->getSegmentInfo = 0; -} - -s64 PS4_SYSV_ABI ps4__write(int d, const void* buf, std::size_t nbytes) { - if (d <= 2) { // stdin,stdout,stderr - char* str = strdup((const char*)buf); - if (str[nbytes - 1] == '\n') - str[nbytes - 1] = 0; - LOG_INFO(Tty, "{}", str); - free(str); - return nbytes; - } - LOG_ERROR(Kernel, "(STUBBED) called d = {} nbytes = {} ", d, nbytes); - UNREACHABLE(); // normal write , is it a posix call?? - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time, - struct OrbisTimesec* st, unsigned long* dst_sec) { - LOG_TRACE(Kernel, "Called"); -#ifdef __APPLE__ - // std::chrono::current_zone() not available yet. - const auto* time_zone = date::current_zone(); -#else - const auto* time_zone = std::chrono::current_zone(); -#endif - auto info = time_zone->get_info(std::chrono::system_clock::now()); - - *local_time = info.offset.count() + info.save.count() * 60 + time; - - if (st != nullptr) { - st->t = time; - st->west_sec = info.offset.count() * 60; - st->dst_sec = info.save.count() * 60; - } - - if (dst_sec != nullptr) { - *dst_sec = info.save.count() * 60; - } - - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(int* ver) { - int version = Common::ElfInfo::Instance().RawFirmwareVer(); - LOG_DEBUG(Kernel, "returned system version = {:#x}", version); - *ver = version; - return (version > 0) ? ORBIS_OK : ORBIS_KERNEL_ERROR_EINVAL; -} - -s64 PS4_SYSV_ABI ps4__read(int d, void* buf, u64 nbytes) { - ASSERT_MSG(d == 0, "d is not 0!"); - - return static_cast( - strlen(std::fgets(static_cast(buf), static_cast(nbytes), stdin))); -} - -s32 PS4_SYSV_ABI sceKernelLoadStartModule(const char* moduleFileName, size_t args, const void* argp, - u32 flags, const void* pOpt, int* pRes) { - LOG_INFO(Lib_Kernel, "called filename = {}, args = {}", moduleFileName, args); - - if (flags != 0) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - - auto* mnt = Common::Singleton::Instance(); - const auto path = mnt->GetHostPath(moduleFileName); - - // Load PRX module and relocate any modules that import it. - auto* linker = Common::Singleton::Instance(); - u32 handle = linker->LoadModule(path, true); - if (handle == -1) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - auto* module = linker->GetModule(handle); - linker->RelocateAnyImports(module); - - // If the new module has a TLS image, trigger its load when TlsGetAddr is called. - if (module->tls.image_size != 0) { - linker->AdvanceGenerationCounter(); - } - - // Retrieve and verify proc param according to libkernel. - u64* param = module->GetProcParam(); - ASSERT_MSG(!param || param[0] >= 0x18, "Invalid module param size: {}", param[0]); - module->Start(args, argp, param); - - return handle; -} - -s32 PS4_SYSV_ABI sceKernelDlsym(s32 handle, const char* symbol, void** addrp) { - auto* linker = Common::Singleton::Instance(); - auto* module = linker->GetModule(handle); - *addrp = module->FindByName(symbol); - if (*addrp == nullptr) { - return ORBIS_KERNEL_ERROR_ESRCH; - } - return ORBIS_OK; -} - -static constexpr size_t ORBIS_DBG_MAX_NAME_LENGTH = 256; - -struct OrbisModuleInfoForUnwind { - u64 st_size; - std::array name; - VAddr eh_frame_hdr_addr; - VAddr eh_frame_addr; - u64 eh_frame_size; - VAddr seg0_addr; - u64 seg0_size; -}; - -s32 PS4_SYSV_ABI sceKernelGetModuleInfoForUnwind(VAddr addr, int flags, - OrbisModuleInfoForUnwind* info) { - if (flags >= 3) { - std::memset(info, 0, sizeof(OrbisModuleInfoForUnwind)); - return SCE_KERNEL_ERROR_EINVAL; - } - if (!info) { - return ORBIS_KERNEL_ERROR_EFAULT; - } - if (info->st_size <= sizeof(OrbisModuleInfoForUnwind)) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - - // Find module that contains specified address. - LOG_INFO(Lib_Kernel, "called addr = {:#x}, flags = {:#x}", addr, flags); - auto* linker = Common::Singleton::Instance(); - auto* module = linker->FindByAddress(addr); - const auto mod_info = module->GetModuleInfoEx(); - - // Fill in module info. - info->name = mod_info.name; - info->eh_frame_hdr_addr = mod_info.eh_frame_hdr_addr; - info->eh_frame_addr = mod_info.eh_frame_addr; - info->eh_frame_size = mod_info.eh_frame_size; - info->seg0_addr = mod_info.segments[0].address; - info->seg0_size = mod_info.segments[0].size; - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceKernelGetModuleInfoFromAddr(VAddr addr, int flags, - Core::OrbisKernelModuleInfoEx* info) { - LOG_INFO(Lib_Kernel, "called addr = {:#x}, flags = {:#x}", addr, flags); - auto* linker = Common::Singleton::Instance(); - auto* module = linker->FindByAddress(addr); - *info = module->GetModuleInfoEx(); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceKernelDebugRaiseException() { - UNREACHABLE(); - return 0; -} - -int PS4_SYSV_ABI sceKernelGetCpumode() { - return 0; -} - -void PS4_SYSV_ABI sched_yield() { - return std::this_thread::yield(); -} - -int PS4_SYSV_ABI sceKernelUuidCreate(OrbisKernelUuid* orbisUuid) { -#ifdef _WIN64 - UUID uuid; - UuidCreate(&uuid); - orbisUuid->timeLow = uuid.Data1; - orbisUuid->timeMid = uuid.Data2; - orbisUuid->timeHiAndVersion = uuid.Data3; - orbisUuid->clockSeqHiAndReserved = uuid.Data4[0]; - orbisUuid->clockSeqLow = uuid.Data4[1]; - for (int i = 0; i < 6; i++) { - orbisUuid->node[i] = uuid.Data4[2 + i]; - } -#else - LOG_ERROR(Kernel, "sceKernelUuidCreate: Add linux"); -#endif - return 0; -} - -const char* PS4_SYSV_ABI sceKernelGetFsSandboxRandomWord() { - const char* path = "sys"; - return path; -} - -int PS4_SYSV_ABI posix_connect() { - return -1; -} - -int PS4_SYSV_ABI _sigprocmask() { - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_getpagesize() { - return 4096; -} - -void LibKernel_Register(Core::Loader::SymbolsResolver* sym) { - service_thread = std::jthread{KernelServiceThread}; - - // obj - LIB_OBJ("f7uOxY9mM1U", "libkernel", 1, "libkernel", 1, 1, &g_stack_chk_guard); - - // misc - LIB_FUNCTION("JGfTMBOdUJo", "libkernel", 1, "libkernel", 1, 1, sceKernelGetFsSandboxRandomWord); - LIB_FUNCTION("XVL8So3QJUk", "libkernel", 1, "libkernel", 1, 1, posix_connect); - LIB_FUNCTION("6xVpy0Fdq+I", "libkernel", 1, "libkernel", 1, 1, _sigprocmask); - - // memory - LIB_FUNCTION("OMDRKKAZ8I4", "libkernel", 1, "libkernel", 1, 1, sceKernelDebugRaiseException); - LIB_FUNCTION("rTXw65xmLIA", "libkernel", 1, "libkernel", 1, 1, sceKernelAllocateDirectMemory); - LIB_FUNCTION("B+vc2AO2Zrc", "libkernel", 1, "libkernel", 1, 1, - sceKernelAllocateMainDirectMemory); - LIB_FUNCTION("C0f7TJcbfac", "libkernel", 1, "libkernel", 1, 1, - sceKernelAvailableDirectMemorySize); - LIB_FUNCTION("hwVSPCmp5tM", "libkernel", 1, "libkernel", 1, 1, - sceKernelCheckedReleaseDirectMemory); - LIB_FUNCTION("rVjRvHJ0X6c", "libkernel", 1, "libkernel", 1, 1, sceKernelVirtualQuery); - LIB_FUNCTION("7oxv3PPCumo", "libkernel", 1, "libkernel", 1, 1, sceKernelReserveVirtualRange); - LIB_FUNCTION("BC+OG5m9+bw", "libkernel", 1, "libkernel", 1, 1, sceKernelGetDirectMemoryType); - LIB_FUNCTION("pO96TwzOm5E", "libkernel", 1, "libkernel", 1, 1, sceKernelGetDirectMemorySize); - LIB_FUNCTION("NcaWUxfMNIQ", "libkernel", 1, "libkernel", 1, 1, sceKernelMapNamedDirectMemory); - LIB_FUNCTION("L-Q3LEjIbgA", "libkernel", 1, "libkernel", 1, 1, sceKernelMapDirectMemory); - LIB_FUNCTION("WFcfL2lzido", "libkernel", 1, "libkernel", 1, 1, sceKernelQueryMemoryProtection); - LIB_FUNCTION("BHouLQzh0X0", "libkernel", 1, "libkernel", 1, 1, sceKernelDirectMemoryQuery); - LIB_FUNCTION("MBuItvba6z8", "libkernel", 1, "libkernel", 1, 1, sceKernelReleaseDirectMemory); - LIB_FUNCTION("PGhQHd-dzv8", "libkernel", 1, "libkernel", 1, 1, sceKernelMmap); - LIB_FUNCTION("cQke9UuBQOk", "libkernel", 1, "libkernel", 1, 1, sceKernelMunmap); - LIB_FUNCTION("mL8NDH86iQI", "libkernel", 1, "libkernel", 1, 1, sceKernelMapNamedFlexibleMemory); - LIB_FUNCTION("aNz11fnnzi4", "libkernel", 1, "libkernel", 1, 1, - sceKernelAvailableFlexibleMemorySize); - LIB_FUNCTION("IWIBBdTHit4", "libkernel", 1, "libkernel", 1, 1, sceKernelMapFlexibleMemory); - LIB_FUNCTION("p5EcQeEeJAE", "libkernel", 1, "libkernel", 1, 1, - _sceKernelRtldSetApplicationHeapAPI); - LIB_FUNCTION("wzvqT4UqKX8", "libkernel", 1, "libkernel", 1, 1, sceKernelLoadStartModule); - LIB_FUNCTION("LwG8g3niqwA", "libkernel", 1, "libkernel", 1, 1, sceKernelDlsym); - LIB_FUNCTION("RpQJJVKTiFM", "libkernel", 1, "libkernel", 1, 1, sceKernelGetModuleInfoForUnwind); - LIB_FUNCTION("f7KBOafysXo", "libkernel", 1, "libkernel", 1, 1, sceKernelGetModuleInfoFromAddr); - LIB_FUNCTION("VOx8NGmHXTs", "libkernel", 1, "libkernel", 1, 1, sceKernelGetCpumode); - LIB_FUNCTION("Xjoosiw+XPI", "libkernel", 1, "libkernel", 1, 1, sceKernelUuidCreate); - - LIB_FUNCTION("2SKEx6bSq-4", "libkernel", 1, "libkernel", 1, 1, sceKernelBatchMap); - LIB_FUNCTION("kBJzF8x4SyE", "libkernel", 1, "libkernel", 1, 1, sceKernelBatchMap2); - LIB_FUNCTION("DGMG3JshrZU", "libkernel", 1, "libkernel", 1, 1, sceKernelSetVirtualRangeName); - LIB_FUNCTION("n1-v6FgU7MQ", "libkernel", 1, "libkernel", 1, 1, - sceKernelConfiguredFlexibleMemorySize); - - // Memory pool - LIB_FUNCTION("qCSfqDILlns", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolExpand); - LIB_FUNCTION("pU-QydtGcGY", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolReserve); - LIB_FUNCTION("Vzl66WmfLvk", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolCommit); - LIB_FUNCTION("LXo1tpFqJGs", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolDecommit); - - // equeue - LIB_FUNCTION("D0OdFMjp46I", "libkernel", 1, "libkernel", 1, 1, sceKernelCreateEqueue); - LIB_FUNCTION("jpFjmgAC5AE", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteEqueue); - LIB_FUNCTION("fzyMKs9kim0", "libkernel", 1, "libkernel", 1, 1, sceKernelWaitEqueue); - LIB_FUNCTION("vz+pg2zdopI", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventUserData); - LIB_FUNCTION("4R6-OvI2cEA", "libkernel", 1, "libkernel", 1, 1, sceKernelAddUserEvent); - LIB_FUNCTION("WDszmSbWuDk", "libkernel", 1, "libkernel", 1, 1, sceKernelAddUserEventEdge); - LIB_FUNCTION("R74tt43xP6k", "libkernel", 1, "libkernel", 1, 1, sceKernelAddHRTimerEvent); - LIB_FUNCTION("F6e0kwo4cnk", "libkernel", 1, "libkernel", 1, 1, sceKernelTriggerUserEvent); - LIB_FUNCTION("LJDwdSNTnDg", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteUserEvent); - LIB_FUNCTION("mJ7aghmgvfc", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventId); - LIB_FUNCTION("9bfdLIyuwCY", "libkernel", 1, "libkernel", 1, 1, sceKernelMTypeProtect); - LIB_FUNCTION("vSMAm3cxYTY", "libkernel", 1, "libkernel", 1, 1, sceKernelMProtect); - LIB_FUNCTION("23CPPI1tyBY", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventFilter); - - // misc - LIB_FUNCTION("WslcK1FQcGI", "libkernel", 1, "libkernel", 1, 1, sceKernelIsNeoMode); - LIB_FUNCTION("Ou3iL1abvng", "libkernel", 1, "libkernel", 1, 1, stack_chk_fail); - LIB_FUNCTION("9BcDykPmo1I", "libkernel", 1, "libkernel", 1, 1, __Error); - LIB_FUNCTION("BPE9s9vQQXo", "libkernel", 1, "libkernel", 1, 1, posix_mmap); - LIB_FUNCTION("BPE9s9vQQXo", "libScePosix", 1, "libkernel", 1, 1, posix_mmap); - LIB_FUNCTION("YSHRBRLn2pI", "libkernel", 1, "libkernel", 1, 1, _writev); - LIB_FUNCTION("959qrazPIrg", "libkernel", 1, "libkernel", 1, 1, sceKernelGetProcParam); - LIB_FUNCTION("-o5uEDpN+oY", "libkernel", 1, "libkernel", 1, 1, sceKernelConvertUtcToLocaltime); - LIB_FUNCTION("WB66evu8bsU", "libkernel", 1, "libkernel", 1, 1, sceKernelGetCompiledSdkVersion); - LIB_FUNCTION("DRuBt2pvICk", "libkernel", 1, "libkernel", 1, 1, ps4__read); - LIB_FUNCTION("k+AXqu2-eBc", "libkernel", 1, "libkernel", 1, 1, posix_getpagesize); - LIB_FUNCTION("k+AXqu2-eBc", "libScePosix", 1, "libkernel", 1, 1, posix_getpagesize); - - Libraries::Kernel::fileSystemSymbolsRegister(sym); - Libraries::Kernel::timeSymbolsRegister(sym); - Libraries::Kernel::pthreadSymbolsRegister(sym); - Libraries::Kernel::RegisterKernelEventFlag(sym); - - // temp - LIB_FUNCTION("NWtTN10cJzE", "libSceLibcInternalExt", 1, "libSceLibcInternal", 1, 1, - sceLibcHeapGetTraceInfo); - LIB_FUNCTION("FxVZqBAA7ks", "libkernel", 1, "libkernel", 1, 1, ps4__write); - LIB_FUNCTION("6XG4B33N09g", "libScePosix", 1, "libkernel", 1, 1, sched_yield); -} - -} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/libkernel.h b/src/core/libraries/kernel/libkernel.h deleted file mode 100644 index 73705cdc2..000000000 --- a/src/core/libraries/kernel/libkernel.h +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include - -#include "common/types.h" - -namespace Core::Loader { -class SymbolsResolver; -} - -namespace Libraries::Kernel { - -void ErrSceToPosix(int result); -int ErrnoToSceKernelError(int e); -void SetPosixErrno(int e); - -struct OrbisTimesec { - time_t t; - u32 west_sec; - u32 dst_sec; -}; - -typedef struct { - uint32_t timeLow; - uint16_t timeMid; - uint16_t timeHiAndVersion; - uint8_t clockSeqHiAndReserved; - uint8_t clockSeqLow; - uint8_t node[6]; -} OrbisKernelUuid; - -int* PS4_SYSV_ABI __Error(); -int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time, - struct OrbisTimesec* st, unsigned long* dst_sec); -int PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(int* ver); - -void LibKernel_Register(Core::Loader::SymbolsResolver* sym); - -} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/memory_management.cpp b/src/core/libraries/kernel/memory.cpp similarity index 75% rename from src/core/libraries/kernel/memory_management.cpp rename to src/core/libraries/kernel/memory.cpp index 5331f47f2..13b3a9f48 100644 --- a/src/core/libraries/kernel/memory_management.cpp +++ b/src/core/libraries/kernel/memory.cpp @@ -6,10 +6,12 @@ #include "common/alignment.h" #include "common/assert.h" #include "common/logging/log.h" +#include "common/scope_exit.h" #include "common/singleton.h" -#include "core/address_space.h" +#include "core/file_sys/fs.h" #include "core/libraries/error_codes.h" -#include "core/libraries/kernel/memory_management.h" +#include "core/libraries/kernel/memory.h" +#include "core/libraries/libs.h" #include "core/linker.h" #include "core/memory.h" @@ -144,11 +146,6 @@ s32 PS4_SYSV_ABI sceKernelReserveVirtualRange(void** addr, u64 len, int flags, u int PS4_SYSV_ABI sceKernelMapNamedDirectMemory(void** addr, u64 len, int prot, int flags, s64 directMemoryStart, u64 alignment, const char* name) { - LOG_INFO(Kernel_Vmm, - "addr = {}, len = {:#x}, prot = {:#x}, flags = {:#x}, directMemoryStart = {:#x}, " - "alignment = {:#x}", - fmt::ptr(*addr), len, prot, flags, directMemoryStart, alignment); - if (len == 0 || !Common::Is16KBAligned(len)) { LOG_ERROR(Kernel_Vmm, "Map size is either zero or not 16KB aligned!"); return SCE_KERNEL_ERROR_EINVAL; @@ -167,6 +164,14 @@ int PS4_SYSV_ABI sceKernelMapNamedDirectMemory(void** addr, u64 len, int prot, i const VAddr in_addr = reinterpret_cast(*addr); const auto mem_prot = static_cast(prot); const auto map_flags = static_cast(flags); + SCOPE_EXIT { + LOG_INFO(Kernel_Vmm, + "in_addr = {:#x}, out_addr = {}, len = {:#x}, prot = {:#x}, flags = {:#x}, " + "directMemoryStart = {:#x}, " + "alignment = {:#x}", + in_addr, fmt::ptr(*addr), len, prot, flags, directMemoryStart, alignment); + }; + auto* memory = Core::Memory::Instance(); return memory->MapMemory(addr, in_addr, len, mem_prot, map_flags, Core::VMAType::Direct, "", false, directMemoryStart, alignment); @@ -200,13 +205,14 @@ s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, std::size_t const VAddr in_addr = reinterpret_cast(*addr_in_out); const auto mem_prot = static_cast(prot); const auto map_flags = static_cast(flags); + SCOPE_EXIT { + LOG_INFO(Kernel_Vmm, + "in_addr = {:#x}, out_addr = {}, len = {:#x}, prot = {:#x}, flags = {:#x}", + in_addr, fmt::ptr(*addr_in_out), len, prot, flags); + }; auto* memory = Core::Memory::Instance(); - const int ret = memory->MapMemory(addr_in_out, in_addr, len, mem_prot, map_flags, - Core::VMAType::Flexible, name); - - LOG_INFO(Kernel_Vmm, "addr = {}, len = {:#x}, prot = {:#x}, flags = {:#x}", - fmt::ptr(*addr_in_out), len, prot, flags); - return ret; + return memory->MapMemory(addr_in_out, in_addr, len, mem_prot, map_flags, + Core::VMAType::Flexible, name); } s32 PS4_SYSV_ABI sceKernelMapFlexibleMemory(void** addr_in_out, std::size_t len, int prot, @@ -265,8 +271,6 @@ s32 PS4_SYSV_ABI sceKernelBatchMap(OrbisKernelBatchMapEntry* entries, int numEnt MemoryFlags::SCE_KERNEL_MAP_FIXED); // 0x10, 0x410? } -int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len); - s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEntries, int* numEntriesOut, int flags) { int result = ORBIS_OK; @@ -445,4 +449,94 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, size_t len, int flags) return ORBIS_OK; } +int PS4_SYSV_ABI sceKernelMmap(void* addr, u64 len, int prot, int flags, int fd, size_t offset, + void** res) { + LOG_INFO(Kernel_Vmm, "called addr = {}, len = {}, prot = {}, flags = {}, fd = {}, offset = {}", + fmt::ptr(addr), len, prot, flags, fd, offset); + auto* h = Common::Singleton::Instance(); + auto* memory = Core::Memory::Instance(); + const auto mem_prot = static_cast(prot); + const auto mem_flags = static_cast(flags); + if (fd == -1) { + return memory->MapMemory(res, std::bit_cast(addr), len, mem_prot, mem_flags, + Core::VMAType::Flexible); + } else { + const uintptr_t handle = h->GetFile(fd)->f.GetFileMapping(); + return memory->MapFile(res, std::bit_cast(addr), len, mem_prot, mem_flags, handle, + offset); + } +} + +void* PS4_SYSV_ABI posix_mmap(void* addr, u64 len, int prot, int flags, int fd, u64 offset) { + void* ptr; + LOG_INFO(Kernel_Vmm, "posix mmap redirect to sceKernelMmap"); + int result = sceKernelMmap(addr, len, prot, flags, fd, offset, &ptr); + ASSERT(result == 0); + return ptr; +} + +s32 PS4_SYSV_ABI sceKernelConfiguredFlexibleMemorySize(u64* sizeOut) { + if (sizeOut == nullptr) { + return ORBIS_KERNEL_ERROR_EINVAL; + } + + auto* memory = Core::Memory::Instance(); + *sizeOut = memory->GetTotalFlexibleSize(); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len) { + LOG_INFO(Kernel_Vmm, "addr = {}, len = {:#x}", fmt::ptr(addr), len); + if (len == 0) { + return ORBIS_OK; + } + auto* memory = Core::Memory::Instance(); + memory->UnmapMemory(std::bit_cast(addr), len); + return SCE_OK; +} + +void RegisterMemory(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("rTXw65xmLIA", "libkernel", 1, "libkernel", 1, 1, sceKernelAllocateDirectMemory); + LIB_FUNCTION("B+vc2AO2Zrc", "libkernel", 1, "libkernel", 1, 1, + sceKernelAllocateMainDirectMemory); + LIB_FUNCTION("C0f7TJcbfac", "libkernel", 1, "libkernel", 1, 1, + sceKernelAvailableDirectMemorySize); + LIB_FUNCTION("hwVSPCmp5tM", "libkernel", 1, "libkernel", 1, 1, + sceKernelCheckedReleaseDirectMemory); + LIB_FUNCTION("rVjRvHJ0X6c", "libkernel", 1, "libkernel", 1, 1, sceKernelVirtualQuery); + LIB_FUNCTION("7oxv3PPCumo", "libkernel", 1, "libkernel", 1, 1, sceKernelReserveVirtualRange); + LIB_FUNCTION("BC+OG5m9+bw", "libkernel", 1, "libkernel", 1, 1, sceKernelGetDirectMemoryType); + LIB_FUNCTION("pO96TwzOm5E", "libkernel", 1, "libkernel", 1, 1, sceKernelGetDirectMemorySize); + LIB_FUNCTION("NcaWUxfMNIQ", "libkernel", 1, "libkernel", 1, 1, sceKernelMapNamedDirectMemory); + LIB_FUNCTION("L-Q3LEjIbgA", "libkernel", 1, "libkernel", 1, 1, sceKernelMapDirectMemory); + LIB_FUNCTION("WFcfL2lzido", "libkernel", 1, "libkernel", 1, 1, sceKernelQueryMemoryProtection); + LIB_FUNCTION("BHouLQzh0X0", "libkernel", 1, "libkernel", 1, 1, sceKernelDirectMemoryQuery); + LIB_FUNCTION("MBuItvba6z8", "libkernel", 1, "libkernel", 1, 1, sceKernelReleaseDirectMemory); + LIB_FUNCTION("PGhQHd-dzv8", "libkernel", 1, "libkernel", 1, 1, sceKernelMmap); + LIB_FUNCTION("cQke9UuBQOk", "libkernel", 1, "libkernel", 1, 1, sceKernelMunmap); + LIB_FUNCTION("mL8NDH86iQI", "libkernel", 1, "libkernel", 1, 1, sceKernelMapNamedFlexibleMemory); + LIB_FUNCTION("aNz11fnnzi4", "libkernel", 1, "libkernel", 1, 1, + sceKernelAvailableFlexibleMemorySize); + LIB_FUNCTION("IWIBBdTHit4", "libkernel", 1, "libkernel", 1, 1, sceKernelMapFlexibleMemory); + LIB_FUNCTION("p5EcQeEeJAE", "libkernel", 1, "libkernel", 1, 1, + _sceKernelRtldSetApplicationHeapAPI); + LIB_FUNCTION("2SKEx6bSq-4", "libkernel", 1, "libkernel", 1, 1, sceKernelBatchMap); + LIB_FUNCTION("kBJzF8x4SyE", "libkernel", 1, "libkernel", 1, 1, sceKernelBatchMap2); + LIB_FUNCTION("DGMG3JshrZU", "libkernel", 1, "libkernel", 1, 1, sceKernelSetVirtualRangeName); + LIB_FUNCTION("n1-v6FgU7MQ", "libkernel", 1, "libkernel", 1, 1, + sceKernelConfiguredFlexibleMemorySize); + + LIB_FUNCTION("9bfdLIyuwCY", "libkernel", 1, "libkernel", 1, 1, sceKernelMTypeProtect); + LIB_FUNCTION("vSMAm3cxYTY", "libkernel", 1, "libkernel", 1, 1, sceKernelMProtect); + + // Memory pool + LIB_FUNCTION("qCSfqDILlns", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolExpand); + LIB_FUNCTION("pU-QydtGcGY", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolReserve); + LIB_FUNCTION("Vzl66WmfLvk", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolCommit); + LIB_FUNCTION("LXo1tpFqJGs", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolDecommit); + + LIB_FUNCTION("BPE9s9vQQXo", "libkernel", 1, "libkernel", 1, 1, posix_mmap); + LIB_FUNCTION("BPE9s9vQQXo", "libScePosix", 1, "libkernel", 1, 1, posix_mmap); +} + } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/memory_management.h b/src/core/libraries/kernel/memory.h similarity index 96% rename from src/core/libraries/kernel/memory_management.h rename to src/core/libraries/kernel/memory.h index 6e90204cf..2d19ceb49 100644 --- a/src/core/libraries/kernel/memory_management.h +++ b/src/core/libraries/kernel/memory.h @@ -10,6 +10,10 @@ constexpr u64 SCE_KERNEL_MAIN_DMEM_SIZE = 5056_MB; // ~ 5GB // TODO: Confirm this value on hardware. constexpr u64 SCE_KERNEL_MAIN_DMEM_SIZE_PRO = 5568_MB; // ~ 5.5GB +namespace Core::Loader { +class SymbolsResolver; +} + namespace Libraries::Kernel { enum MemoryTypes : u32 { @@ -123,4 +127,12 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addrIn, size_t len, size_t ali s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, size_t len, int type, int prot, int flags); s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, size_t len, int flags); +int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len); + +void* Malloc(size_t size); + +void Free(void* ptr); + +void RegisterMemory(Core::Loader::SymbolsResolver* sym); + } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/process.cpp b/src/core/libraries/kernel/process.cpp new file mode 100644 index 000000000..fc1d6e137 --- /dev/null +++ b/src/core/libraries/kernel/process.cpp @@ -0,0 +1,140 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/config.h" +#include "common/elf_info.h" +#include "common/logging/log.h" +#include "core/file_sys/fs.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/kernel/process.h" +#include "core/libraries/libs.h" +#include "core/linker.h" + +namespace Libraries::Kernel { + +int PS4_SYSV_ABI sceKernelIsNeoMode() { + LOG_DEBUG(Kernel_Sce, "called"); + return Config::isNeoMode(); +} + +int PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(int* ver) { + int version = Common::ElfInfo::Instance().RawFirmwareVer(); + *ver = version; + return (version > 0) ? ORBIS_OK : ORBIS_KERNEL_ERROR_EINVAL; +} + +int PS4_SYSV_ABI sceKernelGetCpumode() { + return 0; +} + +void* PS4_SYSV_ABI sceKernelGetProcParam() { + auto* linker = Common::Singleton::Instance(); + return linker->GetProcParam(); +} + +s32 PS4_SYSV_ABI sceKernelLoadStartModule(const char* moduleFileName, size_t args, const void* argp, + u32 flags, const void* pOpt, int* pRes) { + LOG_INFO(Lib_Kernel, "called filename = {}, args = {}", moduleFileName, args); + + if (flags != 0) { + return ORBIS_KERNEL_ERROR_EINVAL; + } + + auto* mnt = Common::Singleton::Instance(); + const auto path = mnt->GetHostPath(moduleFileName); + + // Load PRX module and relocate any modules that import it. + auto* linker = Common::Singleton::Instance(); + u32 handle = linker->LoadModule(path, true); + if (handle == -1) { + return ORBIS_KERNEL_ERROR_EINVAL; + } + auto* module = linker->GetModule(handle); + linker->RelocateAnyImports(module); + + // If the new module has a TLS image, trigger its load when TlsGetAddr is called. + if (module->tls.image_size != 0) { + linker->AdvanceGenerationCounter(); + } + + // Retrieve and verify proc param according to libkernel. + u64* param = module->GetProcParam(); + ASSERT_MSG(!param || param[0] >= 0x18, "Invalid module param size: {}", param[0]); + module->Start(args, argp, param); + + return handle; +} + +s32 PS4_SYSV_ABI sceKernelDlsym(s32 handle, const char* symbol, void** addrp) { + auto* linker = Common::Singleton::Instance(); + auto* module = linker->GetModule(handle); + *addrp = module->FindByName(symbol); + if (*addrp == nullptr) { + return ORBIS_KERNEL_ERROR_ESRCH; + } + return ORBIS_OK; +} + +static constexpr size_t ORBIS_DBG_MAX_NAME_LENGTH = 256; + +struct OrbisModuleInfoForUnwind { + u64 st_size; + std::array name; + VAddr eh_frame_hdr_addr; + VAddr eh_frame_addr; + u64 eh_frame_size; + VAddr seg0_addr; + u64 seg0_size; +}; + +s32 PS4_SYSV_ABI sceKernelGetModuleInfoForUnwind(VAddr addr, int flags, + OrbisModuleInfoForUnwind* info) { + if (flags >= 3) { + std::memset(info, 0, sizeof(OrbisModuleInfoForUnwind)); + return SCE_KERNEL_ERROR_EINVAL; + } + if (!info) { + return ORBIS_KERNEL_ERROR_EFAULT; + } + if (info->st_size < sizeof(OrbisModuleInfoForUnwind)) { + return ORBIS_KERNEL_ERROR_EINVAL; + } + + // Find module that contains specified address. + LOG_INFO(Lib_Kernel, "called addr = {:#x}, flags = {:#x}", addr, flags); + auto* linker = Common::Singleton::Instance(); + auto* module = linker->FindByAddress(addr); + const auto mod_info = module->GetModuleInfoEx(); + + // Fill in module info. + std::memset(info, 0, sizeof(OrbisModuleInfoForUnwind)); + info->name = mod_info.name; + info->eh_frame_hdr_addr = mod_info.eh_frame_hdr_addr; + info->eh_frame_addr = mod_info.eh_frame_addr; + info->eh_frame_size = mod_info.eh_frame_size; + info->seg0_addr = mod_info.segments[0].address; + info->seg0_size = mod_info.segments[0].size; + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceKernelGetModuleInfoFromAddr(VAddr addr, int flags, + Core::OrbisKernelModuleInfoEx* info) { + LOG_INFO(Lib_Kernel, "called addr = {:#x}, flags = {:#x}", addr, flags); + auto* linker = Common::Singleton::Instance(); + auto* module = linker->FindByAddress(addr); + *info = module->GetModuleInfoEx(); + return ORBIS_OK; +} + +void RegisterProcess(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("WB66evu8bsU", "libkernel", 1, "libkernel", 1, 1, sceKernelGetCompiledSdkVersion); + LIB_FUNCTION("WslcK1FQcGI", "libkernel", 1, "libkernel", 1, 1, sceKernelIsNeoMode); + LIB_FUNCTION("VOx8NGmHXTs", "libkernel", 1, "libkernel", 1, 1, sceKernelGetCpumode); + LIB_FUNCTION("959qrazPIrg", "libkernel", 1, "libkernel", 1, 1, sceKernelGetProcParam); + LIB_FUNCTION("wzvqT4UqKX8", "libkernel", 1, "libkernel", 1, 1, sceKernelLoadStartModule); + LIB_FUNCTION("LwG8g3niqwA", "libkernel", 1, "libkernel", 1, 1, sceKernelDlsym); + LIB_FUNCTION("RpQJJVKTiFM", "libkernel", 1, "libkernel", 1, 1, sceKernelGetModuleInfoForUnwind); + LIB_FUNCTION("f7KBOafysXo", "libkernel", 1, "libkernel", 1, 1, sceKernelGetModuleInfoFromAddr); +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/cpu_management.h b/src/core/libraries/kernel/process.h similarity index 60% rename from src/core/libraries/kernel/cpu_management.h rename to src/core/libraries/kernel/process.h index 814ea51df..0340a9793 100644 --- a/src/core/libraries/kernel/cpu_management.h +++ b/src/core/libraries/kernel/process.h @@ -5,8 +5,16 @@ #include "common/types.h" +namespace Core::Loader { +class SymbolsResolver; +} + namespace Libraries::Kernel { int PS4_SYSV_ABI sceKernelIsNeoMode(); +int PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(int* ver); + +void RegisterProcess(Core::Loader::SymbolsResolver* sym); + } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/thread_management.cpp b/src/core/libraries/kernel/thread_management.cpp deleted file mode 100644 index 85f618090..000000000 --- a/src/core/libraries/kernel/thread_management.cpp +++ /dev/null @@ -1,1716 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include -#include - -#include "common/alignment.h" -#include "common/assert.h" -#include "common/error.h" -#include "common/logging/log.h" -#include "common/singleton.h" -#include "common/thread.h" -#include "core/debug_state.h" -#include "core/libraries/error_codes.h" -#include "core/libraries/kernel/libkernel.h" -#include "core/libraries/kernel/thread_management.h" -#include "core/libraries/kernel/threads/threads.h" -#include "core/libraries/libs.h" -#include "core/linker.h" -#include "core/tls.h" -#ifdef _WIN64 -#include -#else -#include -#endif - -namespace Libraries::Kernel { - -thread_local ScePthread g_pthread_self{}; -PThreadCxt* g_pthread_cxt = nullptr; - -void init_pthreads() { - g_pthread_cxt = new PThreadCxt{}; - // default mutex init - ScePthreadMutexattr default_mutexattr = nullptr; - scePthreadMutexattrInit(&default_mutexattr); - g_pthread_cxt->setDefaultMutexattr(default_mutexattr); - ScePthreadMutexattr adaptive_mutexattr = nullptr; - scePthreadMutexattrInit(&adaptive_mutexattr); - scePthreadMutexattrSettype(&adaptive_mutexattr, ORBIS_PTHREAD_MUTEX_ADAPTIVE); - g_pthread_cxt->setAdaptiveMutexattr(adaptive_mutexattr); - // default cond init - ScePthreadCondattr default_condattr = nullptr; - scePthreadCondattrInit(&default_condattr); - g_pthread_cxt->setDefaultCondattr(default_condattr); - // default attr init - ScePthreadAttr default_attr = nullptr; - scePthreadAttrInit(&default_attr); - g_pthread_cxt->SetDefaultAttr(default_attr); - // default rw init - OrbisPthreadRwlockattr default_rwattr = nullptr; - scePthreadRwlockattrInit(&default_rwattr); - g_pthread_cxt->setDefaultRwattr(default_rwattr); - - g_pthread_cxt->SetPthreadPool(new PThreadPool); -} - -void pthreadInitSelfMainThread() { - const char* name = "Main_Thread"; - auto* pthread_pool = g_pthread_cxt->GetPthreadPool(); - g_pthread_self = pthread_pool->Create(name); - scePthreadAttrInit(&g_pthread_self->attr); - g_pthread_self->pth = pthread_self(); - g_pthread_self->name = name; -} - -int PS4_SYSV_ABI scePthreadAttrInit(ScePthreadAttr* attr) { - *attr = new PthreadAttrInternal{}; - - int result = pthread_attr_init(&(*attr)->pth_attr); - - (*attr)->affinity = 0x7f; - (*attr)->guard_size = 0x1000; - - SceKernelSchedParam param{}; - param.sched_priority = 700; - - result = (result == 0 ? scePthreadAttrSetinheritsched(attr, 4) : result); - result = (result == 0 ? scePthreadAttrSetschedparam(attr, ¶m) : result); - result = (result == 0 ? scePthreadAttrSetschedpolicy(attr, SCHED_OTHER) : result); - result = (result == 0 ? scePthreadAttrSetdetachstate(attr, PTHREAD_CREATE_JOINABLE) : result); - - switch (result) { - case 0: - return SCE_OK; - case ENOMEM: - return SCE_KERNEL_ERROR_ENOMEM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadAttrDestroy(ScePthreadAttr* attr) { - - int result = pthread_attr_destroy(&(*attr)->pth_attr); - - delete *attr; - *attr = nullptr; - - if (result == 0) { - return SCE_OK; - } - return SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrSetguardsize(ScePthreadAttr* attr, size_t guard_size) { - if (attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - (*attr)->guard_size = guard_size; - - return SCE_OK; -} - -int PS4_SYSV_ABI scePthreadAttrGetguardsize(const ScePthreadAttr* attr, size_t* guard_size) { - if (guard_size == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - *guard_size = (*attr)->guard_size; - - return SCE_OK; -} - -int PS4_SYSV_ABI scePthreadAttrGetinheritsched(const ScePthreadAttr* attr, int* inherit_sched) { - - if (inherit_sched == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_attr_getinheritsched(&(*attr)->pth_attr, inherit_sched); - - switch (*inherit_sched) { - case PTHREAD_EXPLICIT_SCHED: - *inherit_sched = 0; - break; - case PTHREAD_INHERIT_SCHED: - *inherit_sched = 4; - break; - default: - UNREACHABLE(); - } - - return (result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL); -} - -int PS4_SYSV_ABI scePthreadAttrGetdetachstate(const ScePthreadAttr* attr, int* state) { - if (state == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - // int result = pthread_attr_getdetachstate(&(*attr)->pth_attr, state); - int result = 0; - *state = ((*attr)->detached ? PTHREAD_CREATE_DETACHED : PTHREAD_CREATE_JOINABLE); - - switch (*state) { - case PTHREAD_CREATE_JOINABLE: - *state = 0; - break; - case PTHREAD_CREATE_DETACHED: - *state = 1; - break; - default: - UNREACHABLE(); - } - - return (result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL); -} - -int PS4_SYSV_ABI scePthreadAttrSetdetachstate(ScePthreadAttr* attr, int detachstate) { - if (attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int pstate = PTHREAD_CREATE_JOINABLE; - switch (detachstate) { - case 0: - pstate = PTHREAD_CREATE_JOINABLE; - break; - case 1: - pstate = PTHREAD_CREATE_DETACHED; - break; - default: - UNREACHABLE_MSG("Invalid detachstate: {}", detachstate); - } - - // int result = pthread_attr_setdetachstate(&(*attr)->pth_attr, pstate); - int result = 0; - (*attr)->detached = (pstate == PTHREAD_CREATE_DETACHED); - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrSetinheritsched(ScePthreadAttr* attr, int inheritSched) { - if (attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int pinherit_sched = PTHREAD_INHERIT_SCHED; - switch (inheritSched) { - case 0: - pinherit_sched = PTHREAD_EXPLICIT_SCHED; - break; - case 4: - pinherit_sched = PTHREAD_INHERIT_SCHED; - break; - default: - UNREACHABLE_MSG("Invalid inheritSched: {}", inheritSched); - } - - int result = pthread_attr_setinheritsched(&(*attr)->pth_attr, pinherit_sched); - - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrGetschedparam(const ScePthreadAttr* attr, - SceKernelSchedParam* param) { - - if (param == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_attr_getschedparam(&(*attr)->pth_attr, param); - - if (param->sched_priority <= -2) { - param->sched_priority = 767; - } else if (param->sched_priority >= +2) { - param->sched_priority = 256; - } else { - param->sched_priority = 700; - } - - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrSetschedparam(ScePthreadAttr* attr, - const SceKernelSchedParam* param) { - if (param == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - SceKernelSchedParam pparam{}; - if (param->sched_priority <= 478) { - pparam.sched_priority = +2; - } else if (param->sched_priority >= 733) { - pparam.sched_priority = -2; - } else { - pparam.sched_priority = 0; - } - - // We always use SCHED_OTHER for now, so don't call this for now. - // int result = pthread_attr_setschedparam(&(*attr)->pth_attr, &pparam); - int result = 0; - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrGetschedpolicy(const ScePthreadAttr* attr, int* policy) { - if (policy == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_attr_getschedpolicy(&(*attr)->pth_attr, policy); - - switch (*policy) { - case SCHED_OTHER: - *policy = (*attr)->policy; - break; - case SCHED_FIFO: - *policy = 1; - break; - case SCHED_RR: - *policy = 3; - break; - default: - UNREACHABLE(); - } - - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrSetschedpolicy(ScePthreadAttr* attr, int policy) { - if (attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int ppolicy = SCHED_OTHER; // winpthreads only supports SCHED_OTHER - if (policy != SCHED_OTHER) { - LOG_ERROR(Kernel_Pthread, "policy={} not supported by winpthreads", policy); - } - - (*attr)->policy = policy; - int result = pthread_attr_setschedpolicy(&(*attr)->pth_attr, ppolicy); - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -ScePthread PS4_SYSV_ABI scePthreadSelf() { - return g_pthread_self; -} - -int PS4_SYSV_ABI scePthreadAttrSetaffinity(ScePthreadAttr* pattr, - const /*SceKernelCpumask*/ u64 mask) { - LOG_DEBUG(Kernel_Pthread, "called"); - - if (pattr == nullptr || *pattr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - (*pattr)->affinity = mask; - return SCE_OK; -} - -int PS4_SYSV_ABI scePthreadAttrGetaffinity(const ScePthreadAttr* pattr, - /* SceKernelCpumask*/ u64* mask) { - if (pattr == nullptr || *pattr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - *mask = (*pattr)->affinity; - - return SCE_OK; -} - -int PS4_SYSV_ABI scePthreadAttrGetstackaddr(const ScePthreadAttr* attr, void** stack_addr) { - - if (stack_addr == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - size_t stack_size = 0; - int result = pthread_attr_getstack(&(*attr)->pth_attr, stack_addr, &stack_size); - - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrGetstacksize(const ScePthreadAttr* attr, size_t* stack_size) { - - if (stack_size == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_attr_getstacksize(&(*attr)->pth_attr, stack_size); - - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrSetstackaddr(ScePthreadAttr* attr, void* addr) { - - if (addr == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - size_t stack_size = 0; - pthread_attr_getstacksize(&(*attr)->pth_attr, &stack_size); - - int result = pthread_attr_setstack(&(*attr)->pth_attr, addr, stack_size); - - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrSetstacksize(ScePthreadAttr* attr, size_t stack_size) { - - if (stack_size == 0 || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_attr_setstacksize(&(*attr)->pth_attr, stack_size); - - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI posix_pthread_attr_init(ScePthreadAttr* attr) { - int result = scePthreadAttrInit(attr); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_attr_setstacksize(ScePthreadAttr* attr, size_t stacksize) { - int result = scePthreadAttrSetstacksize(attr, stacksize); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI scePthreadSetaffinity(ScePthread thread, const /*SceKernelCpumask*/ u64 mask) { - LOG_DEBUG(Kernel_Pthread, "called"); - - if (thread == nullptr) { - return SCE_KERNEL_ERROR_ESRCH; - } - - auto result = scePthreadAttrSetaffinity(&thread->attr, mask); - - return result; -} - -int PS4_SYSV_ABI scePthreadGetaffinity(ScePthread thread, /*SceKernelCpumask*/ u64* mask) { - LOG_INFO(Kernel_Pthread, "called"); - - if (thread == nullptr) { - return SCE_KERNEL_ERROR_ESRCH; - } - - auto result = scePthreadAttrGetaffinity(&thread->attr, mask); - - return result; -} - -ScePthreadMutex* createMutex(ScePthreadMutex* addr) { - if (addr == nullptr || - (*addr != nullptr && *addr != ORBIS_PTHREAD_MUTEX_ADAPTIVE_INITIALIZER)) { - return addr; - } - - const VAddr vaddr = reinterpret_cast(addr); - std::string name = fmt::format("mutex{:#x}", vaddr); - scePthreadMutexInit(addr, nullptr, name.c_str()); - return addr; -} - -int PS4_SYSV_ABI scePthreadMutexInit(ScePthreadMutex* mutex, const ScePthreadMutexattr* mutex_attr, - const char* name) { - const ScePthreadMutexattr* attr; - - if (mutex == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - if (mutex_attr == nullptr || *mutex_attr == nullptr) { - if (*mutex == ORBIS_PTHREAD_MUTEX_ADAPTIVE_INITIALIZER) { - attr = g_pthread_cxt->getAdaptiveMutexattr(); - } else { - attr = g_pthread_cxt->getDefaultMutexattr(); - } - } else { - attr = mutex_attr; - } - - *mutex = new PthreadMutexInternal{}; - if (name != nullptr) { - (*mutex)->name = name; - } else { - (*mutex)->name = "nonameMutex"; - } - - int result = pthread_mutex_init(&(*mutex)->pth_mutex, &(*attr)->pth_mutex_attr); - - if (name != nullptr) { - LOG_INFO(Kernel_Pthread, "name={}, result={}", name, result); - } - - switch (result) { - case 0: - return SCE_OK; - case EAGAIN: - return SCE_KERNEL_ERROR_EAGAIN; - case EINVAL: - return SCE_KERNEL_ERROR_EINVAL; - case ENOMEM: - return SCE_KERNEL_ERROR_ENOMEM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadMutexDestroy(ScePthreadMutex* mutex) { - - if (mutex == nullptr || *mutex == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - if (*mutex == ORBIS_PTHREAD_MUTEX_ADAPTIVE_INITIALIZER) { - return ORBIS_OK; - } - - int result = pthread_mutex_destroy(&(*mutex)->pth_mutex); - - LOG_DEBUG(Kernel_Pthread, "name={}, result={}", (*mutex)->name, result); - - delete *mutex; - *mutex = nullptr; - - switch (result) { - case 0: - return SCE_OK; - case EBUSY: - return SCE_KERNEL_ERROR_EBUSY; - case EINVAL: - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} -int PS4_SYSV_ABI scePthreadMutexattrInit(ScePthreadMutexattr* attr) { - *attr = new PthreadMutexattrInternal{}; - - int result = pthread_mutexattr_init(&(*attr)->pth_mutex_attr); - - result = (result == 0 ? scePthreadMutexattrSettype(attr, 1) : result); - result = (result == 0 ? scePthreadMutexattrSetprotocol(attr, 0) : result); - - switch (result) { - case 0: - return SCE_OK; - case ENOMEM: - return SCE_KERNEL_ERROR_ENOMEM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadMutexattrSettype(ScePthreadMutexattr* attr, int type) { - int ptype = PTHREAD_MUTEX_DEFAULT; - switch (type) { - case ORBIS_PTHREAD_MUTEX_ERRORCHECK: - ptype = PTHREAD_MUTEX_ERRORCHECK; - break; - case ORBIS_PTHREAD_MUTEX_RECURSIVE: - ptype = PTHREAD_MUTEX_RECURSIVE; - break; - case ORBIS_PTHREAD_MUTEX_NORMAL: - ptype = PTHREAD_MUTEX_NORMAL; - break; - case ORBIS_PTHREAD_MUTEX_ADAPTIVE: - LOG_ERROR(Kernel_Pthread, "Unimplemented adaptive mutex"); - ptype = PTHREAD_MUTEX_ERRORCHECK; - break; - default: - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_mutexattr_settype(&(*attr)->pth_mutex_attr, ptype); - ASSERT(result == 0); - - return SCE_OK; -} - -int PS4_SYSV_ABI scePthreadMutexattrSetprotocol(ScePthreadMutexattr* attr, int protocol) { - int pprotocol = PTHREAD_PRIO_NONE; - switch (protocol) { - case 0: - pprotocol = PTHREAD_PRIO_NONE; - break; - case 1: - pprotocol = PTHREAD_PRIO_INHERIT; - break; - case 2: - pprotocol = PTHREAD_PRIO_PROTECT; - break; - default: - UNREACHABLE_MSG("Invalid protocol: {}", protocol); - } - -#if _WIN64 - int result = 0; -#else - int result = pthread_mutexattr_setprotocol(&(*attr)->pth_mutex_attr, pprotocol); -#endif - (*attr)->pprotocol = pprotocol; - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadMutexLock(ScePthreadMutex* mutex) { - mutex = createMutex(mutex); - if (mutex == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_mutex_lock(&(*mutex)->pth_mutex); - if (result != 0) { - LOG_TRACE(Kernel_Pthread, "Locked name={}, result={}", (*mutex)->name, result); - } - - switch (result) { - case 0: - return SCE_OK; - case EAGAIN: - return SCE_KERNEL_ERROR_EAGAIN; - case EINVAL: - return SCE_KERNEL_ERROR_EINVAL; - case EDEADLK: - return SCE_KERNEL_ERROR_EDEADLK; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadMutexUnlock(ScePthreadMutex* mutex) { - if (mutex == nullptr || *mutex == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_mutex_unlock(&(*mutex)->pth_mutex); - if (result != 0) { - LOG_TRACE(Kernel_Pthread, "Unlocking name={}, result={}", (*mutex)->name, result); - } - - switch (result) { - case 0: - return SCE_OK; - case EINVAL: - return SCE_KERNEL_ERROR_EINVAL; - case EPERM: - return SCE_KERNEL_ERROR_EPERM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadMutexattrDestroy(ScePthreadMutexattr* attr) { - int result = pthread_mutexattr_destroy(&(*attr)->pth_mutex_attr); - - delete *attr; - *attr = nullptr; - - switch (result) { - case 0: - return SCE_OK; - case ENOMEM: - return SCE_KERNEL_ERROR_ENOMEM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -ScePthreadCond* createCond(ScePthreadCond* addr) { - if (addr == nullptr || *addr != nullptr) { - return addr; - } - static std::mutex mutex; - std::scoped_lock lk{mutex}; - if (*addr != nullptr) { - return addr; - } - const VAddr vaddr = reinterpret_cast(addr); - std::string name = fmt::format("cond{:#x}", vaddr); - scePthreadCondInit(static_cast(addr), nullptr, name.c_str()); - return addr; -} - -int PS4_SYSV_ABI scePthreadCondInit(ScePthreadCond* cond, const ScePthreadCondattr* attr, - const char* name) { - if (cond == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - if (attr == nullptr) { - attr = g_pthread_cxt->getDefaultCondattr(); - } - - *cond = new PthreadCondInternal{}; - - if (name != nullptr) { - (*cond)->name = name; - } else { - (*cond)->name = "nonameCond"; - } - - int result = pthread_cond_init(&(*cond)->cond, &(*attr)->cond_attr); - - if (name != nullptr) { - LOG_TRACE(Kernel_Pthread, "name={}, result={}", (*cond)->name, result); - } - - switch (result) { - case 0: - return SCE_OK; - case EAGAIN: - return SCE_KERNEL_ERROR_EAGAIN; - case EINVAL: - return SCE_KERNEL_ERROR_EINVAL; - case ENOMEM: - return SCE_KERNEL_ERROR_ENOMEM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadCondattrInit(ScePthreadCondattr* attr) { - *attr = new PthreadCondAttrInternal{}; - - int result = pthread_condattr_init(&(*attr)->cond_attr); - - switch (result) { - case 0: - return SCE_OK; - case ENOMEM: - return SCE_KERNEL_ERROR_ENOMEM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadCondBroadcast(ScePthreadCond* cond) { - cond = createCond(cond); - if (cond == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_cond_broadcast(&(*cond)->cond); - - LOG_TRACE(Kernel_Pthread, "called name={}, result={}", (*cond)->name, result); - - return (result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL); -} - -int PS4_SYSV_ABI scePthreadCondTimedwait(ScePthreadCond* cond, ScePthreadMutex* mutex, u64 usec) { - cond = createCond(cond); - if (cond == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - if (mutex == nullptr || *mutex == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - timespec time{}; - time.tv_sec = usec / 1000000; - time.tv_nsec = ((usec % 1000000) * 1000); - int result = pthread_cond_timedwait(&(*cond)->cond, &(*mutex)->pth_mutex, &time); - - // LOG_INFO(Kernel_Pthread, "scePthreadCondTimedwait, result={}", result); - - switch (result) { - case 0: - return SCE_OK; - case ETIMEDOUT: - return SCE_KERNEL_ERROR_ETIMEDOUT; - case EINTR: - return SCE_KERNEL_ERROR_EINTR; - case EAGAIN: - return SCE_KERNEL_ERROR_EAGAIN; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadCondDestroy(ScePthreadCond* cond) { - if (cond == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - int result = pthread_cond_destroy(&(*cond)->cond); - - LOG_DEBUG(Kernel_Pthread, "scePthreadCondDestroy, result={}", result); - - delete *cond; - *cond = nullptr; - - switch (result) { - case 0: - return SCE_OK; - case EBUSY: - return SCE_KERNEL_ERROR_EBUSY; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI posix_pthread_mutex_init(ScePthreadMutex* mutex, const ScePthreadMutexattr* attr) { - // LOG_INFO(Kernel_Pthread, "posix pthread_mutex_init redirect to scePthreadMutexInit"); - int result = scePthreadMutexInit(mutex, attr, nullptr); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_mutex_lock(ScePthreadMutex* mutex) { - // LOG_INFO(Kernel_Pthread, "posix pthread_mutex_lock redirect to scePthreadMutexLock"); - int result = scePthreadMutexLock(mutex); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_mutex_unlock(ScePthreadMutex* mutex) { - // LOG_INFO(Kernel_Pthread, "posix pthread_mutex_unlock redirect to scePthreadMutexUnlock"); - int result = scePthreadMutexUnlock(mutex); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_mutex_destroy(ScePthreadMutex* mutex) { - int result = scePthreadMutexDestroy(mutex); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_cond_wait(ScePthreadCond* cond, ScePthreadMutex* mutex) { - int result = scePthreadCondWait(cond, mutex); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_cond_timedwait(ScePthreadCond* cond, ScePthreadMutex* mutex, - u64 usec) { - int result = scePthreadCondTimedwait(cond, mutex, usec); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_cond_broadcast(ScePthreadCond* cond) { - int result = scePthreadCondBroadcast(cond); - if (result != 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_mutexattr_init(ScePthreadMutexattr* attr) { - int result = scePthreadMutexattrInit(attr); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_mutexattr_settype(ScePthreadMutexattr* attr, int type) { - int result = scePthreadMutexattrSettype(attr, type); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_mutexattr_destroy(ScePthreadMutexattr* attr) { - int result = scePthreadMutexattrDestroy(attr); - if (result < 0) { - UNREACHABLE(); - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_once(pthread_once_t* once_control, void (*init_routine)(void)) { - return pthread_once(once_control, init_routine); -} - -int PS4_SYSV_ABI posix_pthread_mutexattr_setprotocol(ScePthreadMutexattr* attr, int protocol) { - int result = scePthreadMutexattrSetprotocol(attr, protocol); - if (result < 0) { - UNREACHABLE(); - } - return result; -} - -#ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK -static int pthread_mutex_timedlock(pthread_mutex_t* mutex, const struct timespec* abstime) { - int rc; - while ((rc = pthread_mutex_trylock(mutex)) == EBUSY) { - struct timespec curr_time; - clock_gettime(CLOCK_REALTIME, &curr_time); - - s64 remaining_ns = 0; - remaining_ns += - (static_cast(abstime->tv_sec) - static_cast(curr_time.tv_sec)) * 1000000000L; - remaining_ns += static_cast(abstime->tv_nsec) - static_cast(curr_time.tv_nsec); - - if (remaining_ns <= 0) { - return ETIMEDOUT; - } - - struct timespec sleep_time; - sleep_time.tv_sec = 0; - if (remaining_ns < 5000000L) { - sleep_time.tv_nsec = remaining_ns; - } else { - sleep_time.tv_nsec = 5000000; - } - - nanosleep(&sleep_time, nullptr); - } - - return rc; -} -#endif - -int PS4_SYSV_ABI scePthreadMutexTimedlock(ScePthreadMutex* mutex, u64 usec) { - mutex = createMutex(mutex); - if (mutex == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - timespec time{}; - time.tv_sec = usec / 1000000; - time.tv_nsec = ((usec % 1000000) * 1000); - int result = pthread_mutex_timedlock(&(*mutex)->pth_mutex, &time); - - switch (result) { - case 0: - return SCE_OK; - case ETIMEDOUT: - return SCE_KERNEL_ERROR_ETIMEDOUT; - case EINTR: - return SCE_KERNEL_ERROR_EINTR; - case EAGAIN: - return SCE_KERNEL_ERROR_EAGAIN; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -static int pthread_copy_attributes(ScePthreadAttr* dst, const ScePthreadAttr* src) { - if (dst == nullptr || *dst == nullptr || src == nullptr || *src == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - u64 mask = 0; - int state = 0; - size_t guard_size = 0; - int inherit_sched = 0; - SceKernelSchedParam param = {}; - int policy = 0; - void* stack_addr = nullptr; - size_t stack_size = 0; - - int result = 0; - - result = (result == 0 ? scePthreadAttrGetaffinity(src, &mask) : result); - result = (result == 0 ? scePthreadAttrGetdetachstate(src, &state) : result); - result = (result == 0 ? scePthreadAttrGetguardsize(src, &guard_size) : result); - result = (result == 0 ? scePthreadAttrGetinheritsched(src, &inherit_sched) : result); - result = (result == 0 ? scePthreadAttrGetschedparam(src, ¶m) : result); - result = (result == 0 ? scePthreadAttrGetschedpolicy(src, &policy) : result); - result = (result == 0 ? scePthreadAttrGetstackaddr(src, &stack_addr) : result); - result = (result == 0 ? scePthreadAttrGetstacksize(src, &stack_size) : result); - - result = (result == 0 ? scePthreadAttrSetaffinity(dst, mask) : result); - result = (result == 0 ? scePthreadAttrSetdetachstate(dst, state) : result); - result = (result == 0 ? scePthreadAttrSetguardsize(dst, guard_size) : result); - result = (result == 0 ? scePthreadAttrSetinheritsched(dst, inherit_sched) : result); - result = (result == 0 ? scePthreadAttrSetschedparam(dst, ¶m) : result); - result = (result == 0 ? scePthreadAttrSetschedpolicy(dst, policy) : result); - if (stack_addr != nullptr) { - result = (result == 0 ? scePthreadAttrSetstackaddr(dst, stack_addr) : result); - } - if (stack_size != 0) { - result = (result == 0 ? scePthreadAttrSetstacksize(dst, stack_size) : result); - } - - return result; -} - -int PS4_SYSV_ABI scePthreadAttrGet(ScePthread thread, ScePthreadAttr* attr) { - if (thread == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - return pthread_copy_attributes(attr, &thread->attr); -} - -static void cleanup_thread(void* arg) { - auto* thread = static_cast(arg); - for (const auto& [key, destructor] : thread->key_destructors) { - if (void* value = pthread_getspecific(key); value != nullptr) { - destructor(value); - } - } - Core::SetTcbBase(nullptr); - thread->is_almost_done = true; - DebugState.RemoveCurrentThreadFromGuestList(); -} - -static void* run_thread(void* arg) { - auto* thread = static_cast(arg); - Common::SetCurrentThreadName(thread->name.c_str()); - const auto* linker = Common::Singleton::Instance(); - void* ret = nullptr; - g_pthread_self = thread; - pthread_cleanup_push(cleanup_thread, thread); - thread->is_started = true; - DebugState.AddCurrentThreadToGuestList(); - ret = linker->ExecuteGuest(thread->entry, thread->arg); - pthread_cleanup_pop(1); - return ret; -} - -int PS4_SYSV_ABI scePthreadCreate(ScePthread* thread, const ScePthreadAttr* attr, - PthreadEntryFunc start_routine, void* arg, const char* name) { - if (thread == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - auto* pthread_pool = g_pthread_cxt->GetPthreadPool(); - - if (attr == nullptr) { - attr = g_pthread_cxt->GetDefaultAttr(); - } - - *thread = pthread_pool->Create(name); - - if ((*thread)->attr != nullptr) { - scePthreadAttrDestroy(&(*thread)->attr); - } - scePthreadAttrInit(&(*thread)->attr); - - int result = pthread_copy_attributes(&(*thread)->attr, attr); - ASSERT(result == 0); - - if (name != NULL) { - (*thread)->name = name; - } else { - (*thread)->name = "no-name"; - } - (*thread)->entry = start_routine; - (*thread)->arg = arg; - (*thread)->is_almost_done = false; - (*thread)->is_detached = (*attr)->detached; - (*thread)->is_started = false; - - pthread_attr_setstacksize(&(*attr)->pth_attr, 2_MB); - result = pthread_create(&(*thread)->pth, &(*attr)->pth_attr, run_thread, *thread); - - LOG_INFO(Kernel_Pthread, "thread create name = {}", (*thread)->name); - - switch (result) { - case 0: - return SCE_OK; - case ENOMEM: - return SCE_KERNEL_ERROR_ENOMEM; - case EAGAIN: - return SCE_KERNEL_ERROR_EAGAIN; - case EDEADLK: - return SCE_KERNEL_ERROR_EDEADLK; - case EPERM: - return SCE_KERNEL_ERROR_EPERM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -ScePthread PThreadPool::Create(const char* name) { - std::scoped_lock lock{m_mutex}; - - for (auto* p : m_threads) { - if (p->is_free && name != nullptr && p->name == name) { - p->is_free = false; - return p; - } - } - - auto* ret = new PthreadInternal{}; - ret->is_free = false; - ret->is_detached = false; - ret->is_almost_done = false; - ret->attr = nullptr; - - m_threads.push_back(ret); - - return ret; -} - -void PS4_SYSV_ABI scePthreadYield() { - sched_yield(); -} - -void PS4_SYSV_ABI posix_pthread_yield() { - sched_yield(); -} - -int PS4_SYSV_ABI scePthreadAttrGetstack(ScePthreadAttr* attr, void** addr, size_t* size) { - - int result = pthread_attr_getstack(&(*attr)->pth_attr, addr, size); - LOG_INFO(Kernel_Pthread, "scePthreadAttrGetstack: result = {}", result); - - if (result == 0) { - return SCE_OK; - } - return SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrSetstack(ScePthreadAttr* attr, void* addr, size_t size) { - if (attr == nullptr || *attr == nullptr || addr == nullptr || size < 0x4000) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - int result = pthread_attr_setstack(&(*attr)->pth_attr, addr, size); - LOG_INFO(Kernel_Pthread, "scePthreadAttrSetstack: result = {}", result); - - if (result == 0) { - return ORBIS_OK; - } - return ORBIS_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadJoin(ScePthread thread, void** res) { - int result = pthread_join(thread->pth, res); - LOG_INFO(Kernel_Pthread, "scePthreadJoin result = {}", result); - thread->is_detached = false; - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_pthread_join(ScePthread thread, void** res) { - int result = pthread_join(thread->pth, res); - LOG_INFO(Kernel_Pthread, "posix_pthread_join result = {}", result); - thread->is_detached = false; - return ORBIS_OK; -} - -int PS4_SYSV_ABI scePthreadDetach(ScePthread thread) { - thread->is_detached = true; - return ORBIS_OK; -} - -ScePthread PS4_SYSV_ABI posix_pthread_self() { - return g_pthread_self; -} - -int PS4_SYSV_ABI scePthreadCondSignal(ScePthreadCond* cond) { - cond = createCond(cond); - if (cond == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_cond_signal(&(*cond)->cond); - - // LOG_INFO(Kernel_Pthread, "scePthreadCondSignal, result={}", result); - - switch (result) { - case 0: - return SCE_OK; - case EBUSY: - return SCE_KERNEL_ERROR_EBUSY; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadCondWait(ScePthreadCond* cond, ScePthreadMutex* mutex) { - cond = createCond(cond); - if (cond == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - if (mutex == nullptr || *mutex == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - int result = pthread_cond_wait(&(*cond)->cond, &(*mutex)->pth_mutex); - - LOG_DEBUG(Kernel_Pthread, "scePthreadCondWait, result={}", result); - - switch (result) { - case 0: - return SCE_OK; - case EINTR: - return SCE_KERNEL_ERROR_EINTR; - case EAGAIN: - return SCE_KERNEL_ERROR_EAGAIN; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadCondattrDestroy(ScePthreadCondattr* attr) { - if (attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - int result = pthread_condattr_destroy(&(*attr)->cond_attr); - - LOG_DEBUG(Kernel_Pthread, "scePthreadCondattrDestroy: result = {} ", result); - delete *attr; - - switch (result) { - case 0: - return SCE_OK; - case ENOMEM: - return SCE_KERNEL_ERROR_ENOMEM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadMutexTrylock(ScePthreadMutex* mutex) { - mutex = createMutex(mutex); - if (mutex == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - - int result = pthread_mutex_trylock(&(*mutex)->pth_mutex); - if (result != 0) { - LOG_TRACE(Kernel_Pthread, "name={}, result={}", (*mutex)->name, result); - } - - switch (result) { - case 0: - return ORBIS_OK; - case EAGAIN: - return ORBIS_KERNEL_ERROR_EAGAIN; - case EBUSY: - return ORBIS_KERNEL_ERROR_EBUSY; - case EINVAL: - default: - return ORBIS_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadEqual(ScePthread thread1, ScePthread thread2) { - return (thread1 == thread2 ? 1 : 0); -} - -int PS4_SYSV_ABI posix_pthread_equal(ScePthread thread1, ScePthread thread2) { - return (thread1 == thread2 ? 1 : 0); -} - -struct TlsIndex { - u64 ti_module; - u64 ti_offset; -}; - -void* PS4_SYSV_ABI __tls_get_addr(TlsIndex* index) { - auto* linker = Common::Singleton::Instance(); - return linker->TlsGetAddr(index->ti_module, index->ti_offset); -} - -int PS4_SYSV_ABI posix_sched_get_priority_max() { - return ORBIS_KERNEL_PRIO_FIFO_HIGHEST; -} - -int PS4_SYSV_ABI posix_sched_get_priority_min() { - return ORBIS_KERNEL_PRIO_FIFO_LOWEST; -} - -int PS4_SYSV_ABI posix_pthread_mutex_trylock(ScePthreadMutex* mutex) { - int result = scePthreadMutexTrylock(mutex); - if (result < 0) { - // UNREACHABLE(); - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_attr_destroy(ScePthreadAttr* attr) { - int result = scePthreadAttrDestroy(attr); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_attr_setschedparam(ScePthreadAttr* attr, - const SceKernelSchedParam* param) { - int result = scePthreadAttrSetschedparam(attr, param); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_attr_setinheritsched(ScePthreadAttr* attr, int inheritSched) { - int result = scePthreadAttrSetinheritsched(attr, inheritSched); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_setprio(ScePthread thread, int prio) { - int result = scePthreadSetprio(thread, prio); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_attr_setdetachstate(ScePthreadAttr* attr, int detachstate) { - // LOG_INFO(Kernel_Pthread, "posix pthread_mutexattr_init redirect to scePthreadMutexattrInit"); - int result = scePthreadAttrSetdetachstate(attr, detachstate); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_create_name_np(ScePthread* thread, const ScePthreadAttr* attr, - PthreadEntryFunc start_routine, void* arg, - const char* name) { - int result = scePthreadCreate(thread, attr, start_routine, arg, name); - if (result != 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_create(ScePthread* thread, const ScePthreadAttr* attr, - PthreadEntryFunc start_routine, void* arg) { - return posix_pthread_create_name_np(thread, attr, start_routine, arg, "NoName"); -} - -using Destructor = void (*)(void*); - -int PS4_SYSV_ABI posix_pthread_key_create(u32* key, Destructor func) { - pthread_key_t thread_key; - int rc = pthread_key_create(&thread_key, func); - *key = static_cast(thread_key); - return rc; -} - -int PS4_SYSV_ABI posix_pthread_setspecific(int key, const void* value) { - return pthread_setspecific(key, value); -} - -void* PS4_SYSV_ABI posix_pthread_getspecific(int key) { - return pthread_getspecific(key); -} - -int PS4_SYSV_ABI posix_pthread_cond_init(ScePthreadCond* cond, const ScePthreadCondattr* attr) { - // LOG_INFO(Kernel_Pthread, "posix pthread_mutex_init redirect to scePthreadMutexInit"); - int result = scePthreadCondInit(cond, attr, "NoName"); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_cond_signal(ScePthreadCond* cond) { - int result = scePthreadCondSignal(cond); - return result; -} - -int PS4_SYSV_ABI posix_pthread_cond_destroy(ScePthreadCond* cond) { - int result = scePthreadCondDestroy(cond); - return result; -} - -int PS4_SYSV_ABI posix_pthread_setcancelstate(int state, int* oldstate) { - return pthread_setcancelstate(state, oldstate); -} - -int PS4_SYSV_ABI posix_pthread_detach(ScePthread thread) { - return pthread_detach(thread->pth); -} - -int PS4_SYSV_ABI posix_sem_init(PthreadSemInternal** sem, int pshared, unsigned int value) { - if (value > ORBIS_KERNEL_SEM_VALUE_MAX) { - SetPosixErrno(EINVAL); - return -1; - } - if (sem != nullptr) { - *sem = new PthreadSemInternal{ - .semaphore = std::counting_semaphore{value}, - .value = {static_cast(value)}, - }; - } - return 0; -} - -int PS4_SYSV_ABI posix_sem_wait(PthreadSemInternal** sem) { - if (sem == nullptr || *sem == nullptr) { - SetPosixErrno(EINVAL); - return -1; - } - (*sem)->semaphore.acquire(); - --(*sem)->value; - return 0; -} - -int PS4_SYSV_ABI posix_sem_trywait(PthreadSemInternal** sem) { - if (sem == nullptr || *sem == nullptr) { - SetPosixErrno(EINVAL); - return -1; - } - if (!(*sem)->semaphore.try_acquire()) { - SetPosixErrno(EAGAIN); - return -1; - } - --(*sem)->value; - return 0; -} - -int PS4_SYSV_ABI posix_sem_timedwait(PthreadSemInternal** sem, const timespec* t) { - if (sem == nullptr || *sem == nullptr) { - SetPosixErrno(EINVAL); - return -1; - } - - using std::chrono::duration_cast; - using std::chrono::nanoseconds; - using std::chrono::seconds; - using std::chrono::system_clock; - - const system_clock::time_point time{ - duration_cast(seconds{t->tv_sec} + nanoseconds{t->tv_nsec})}; - if (!(*sem)->semaphore.try_acquire_until(time)) { - SetPosixErrno(ETIMEDOUT); - return -1; - } - --(*sem)->value; - return 0; -} - -int PS4_SYSV_ABI posix_sem_post(PthreadSemInternal** sem) { - if (sem == nullptr || *sem == nullptr) { - SetPosixErrno(EINVAL); - return -1; - } - if ((*sem)->value == ORBIS_KERNEL_SEM_VALUE_MAX) { - SetPosixErrno(EOVERFLOW); - return -1; - } - ++(*sem)->value; - (*sem)->semaphore.release(); - return 0; -} - -int PS4_SYSV_ABI posix_sem_destroy(PthreadSemInternal** sem) { - if (sem == nullptr || *sem == nullptr) { - SetPosixErrno(EINVAL); - return -1; - } - delete *sem; - *sem = nullptr; - return 0; -} - -int PS4_SYSV_ABI posix_sem_getvalue(PthreadSemInternal** sem, int* sval) { - if (sem == nullptr || *sem == nullptr) { - SetPosixErrno(EINVAL); - return -1; - } - if (sval) { - *sval = (*sem)->value; - } - return 0; -} - -int PS4_SYSV_ABI posix_pthread_attr_getstacksize(const pthread_attr_t* attr, size_t* size) { - return pthread_attr_getstacksize(attr, size); -} - -int PS4_SYSV_ABI scePthreadGetschedparam(ScePthread thread, int* policy, - SceKernelSchedParam* param) { - return pthread_getschedparam(thread->pth, policy, param); -} - -int PS4_SYSV_ABI scePthreadSetschedparam(ScePthread thread, int policy, - const SceKernelSchedParam* param) { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called policy={}, sched_priority={}", policy, - param->sched_priority); - return ORBIS_OK; -} - -int PS4_SYSV_ABI scePthreadOnce(int* once_control, void (*init_routine)(void)) { - return pthread_once(reinterpret_cast(once_control), init_routine); -} - -[[noreturn]] void PS4_SYSV_ABI scePthreadExit(void* value_ptr) { - g_pthread_self->is_free = true; - - pthread_exit(value_ptr); - UNREACHABLE(); -} - -[[noreturn]] void PS4_SYSV_ABI posix_pthread_exit(void* value_ptr) { - pthread_exit(value_ptr); - UNREACHABLE(); -} - -int PS4_SYSV_ABI scePthreadGetthreadid() { - return (int)(size_t)g_pthread_self; -} - -int PS4_SYSV_ABI scePthreadGetprio(ScePthread thread, int* prio) { - *prio = thread->prio; - return ORBIS_OK; -} -int PS4_SYSV_ABI scePthreadSetprio(ScePthread thread, int prio) { - if (thread == nullptr) { - LOG_ERROR(Kernel_Pthread, "scePthreadSetprio: thread is nullptr"); - return ORBIS_KERNEL_ERROR_EINVAL; - } - thread->prio = prio; - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_pthread_condattr_init(ScePthreadCondattr* attr) { - int result = scePthreadCondattrInit(attr); - LOG_INFO(Kernel_Pthread, - "posix_pthread_condattr_init redirect to scePthreadCondattrInit, result = {}", result); - return result; -} - -int PS4_SYSV_ABI posix_pthread_condattr_destroy(ScePthreadCondattr* attr) { - int result = scePthreadCondattrDestroy(attr); - LOG_INFO(Kernel_Pthread, - "posix_pthread_condattr_destroy redirect to scePthreadCondattrDestroy, result = {}", - result); - return result; -} - -int PS4_SYSV_ABI posix_pthread_condattr_setclock(ScePthreadCondattr* attr, clockid_t clock) { - (*attr)->clock = clock; - return SCE_OK; -} - -int PS4_SYSV_ABI posix_pthread_getschedparam(ScePthread thread, int* policy, - SceKernelSchedParam* param) { - return scePthreadGetschedparam(thread, policy, param); -} - -int PS4_SYSV_ABI posix_pthread_setschedparam(ScePthread thread, int policy, - const SceKernelSchedParam* param) { - return scePthreadSetschedparam(thread, policy, param); -} - -int PS4_SYSV_ABI posix_pthread_attr_getschedpolicy(const ScePthreadAttr* attr, int* policy) { - return scePthreadAttrGetschedpolicy(attr, policy); -} - -int PS4_SYSV_ABI scePthreadRename(ScePthread thread, const char* name) { - thread->name = name; - LOG_INFO(Kernel_Pthread, "scePthreadRename: name = {}", thread->name); - return SCE_OK; -} - -void pthreadSymbolsRegister(Core::Loader::SymbolsResolver* sym) { - LIB_FUNCTION("lZzFeSxPl08", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setcancelstate); - LIB_FUNCTION("0TyVk4MSLt0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_init); - LIB_FUNCTION("2MOy+rUfuhQ", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_signal); - LIB_FUNCTION("RXXqi4CtF8w", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_destroy); - LIB_FUNCTION("mqULNdimTn0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_key_create); - LIB_FUNCTION("0-KXaS70xy4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_getspecific); - LIB_FUNCTION("WrOLvHU0yQM", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setspecific); - LIB_FUNCTION("4+h9EzwKF4I", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetschedpolicy); - LIB_FUNCTION("-Wreprtu0Qs", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetdetachstate); - LIB_FUNCTION("JaRMy+QcpeU", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGetdetachstate); - LIB_FUNCTION("eXbUSpEaTsA", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetinheritsched); - LIB_FUNCTION("DzES9hQF4f4", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetschedparam); - LIB_FUNCTION("nsYoNRywwNg", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrInit); - LIB_FUNCTION("62KCwEMmzcM", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrDestroy); - LIB_FUNCTION("onNY9Byn-W8", "libkernel", 1, "libkernel", 1, 1, scePthreadJoin); - LIB_FUNCTION("4qGrR6eoP9Y", "libkernel", 1, "libkernel", 1, 1, scePthreadDetach); - LIB_FUNCTION("3PtV6p3QNX4", "libkernel", 1, "libkernel", 1, 1, scePthreadEqual); - LIB_FUNCTION("3kg7rT0NQIs", "libkernel", 1, "libkernel", 1, 1, scePthreadExit); - LIB_FUNCTION("FJrT5LuUBAU", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_exit); - LIB_FUNCTION("7Xl257M4VNI", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_equal); - LIB_FUNCTION("h9CcP3J0oVM", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_join); - LIB_FUNCTION("EI-5-jlq2dE", "libkernel", 1, "libkernel", 1, 1, scePthreadGetthreadid); - LIB_FUNCTION("1tKyG7RlMJo", "libkernel", 1, "libkernel", 1, 1, scePthreadGetprio); - LIB_FUNCTION("W0Hpm2X0uPE", "libkernel", 1, "libkernel", 1, 1, scePthreadSetprio); - LIB_FUNCTION("GBUY7ywdULE", "libkernel", 1, "libkernel", 1, 1, scePthreadRename); - LIB_FUNCTION("F+yfmduIBB8", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetstackaddr); - LIB_FUNCTION("El+cQ20DynU", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetguardsize); - - LIB_FUNCTION("aI+OeCz8xrQ", "libkernel", 1, "libkernel", 1, 1, scePthreadSelf); - LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self); - LIB_FUNCTION("EotR8a3ASf4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_self); - LIB_FUNCTION("3qxgM4ezETA", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetaffinity); - LIB_FUNCTION("8+s5BzZjxSg", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGetaffinity); - LIB_FUNCTION("x1X76arYMxU", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGet); - LIB_FUNCTION("FXPWHNk8Of0", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGetschedparam); - LIB_FUNCTION("P41kTWUS3EI", "libkernel", 1, "libkernel", 1, 1, scePthreadGetschedparam); - LIB_FUNCTION("oIRFTjoILbg", "libkernel", 1, "libkernel", 1, 1, scePthreadSetschedparam); - LIB_FUNCTION("UTXzJbWhhTE", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetstacksize); - LIB_FUNCTION("vNe1w4diLCs", "libkernel", 1, "libkernel", 1, 1, __tls_get_addr); - LIB_FUNCTION("OxhIB8LB-PQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_create); - LIB_FUNCTION("OxhIB8LB-PQ", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create); - LIB_FUNCTION("bt3CTBKmGyI", "libkernel", 1, "libkernel", 1, 1, scePthreadSetaffinity); - LIB_FUNCTION("rcrVFJsQWRY", "libkernel", 1, "libkernel", 1, 1, scePthreadGetaffinity); - LIB_FUNCTION("6UgtwV+0zb4", "libkernel", 1, "libkernel", 1, 1, scePthreadCreate); - LIB_FUNCTION("T72hz6ffq08", "libkernel", 1, "libkernel", 1, 1, scePthreadYield); - LIB_FUNCTION("B5GmVDKwpn0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_yield); - - LIB_FUNCTION("-quPa4SEJUw", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGetstack); - LIB_FUNCTION("Bvn74vj6oLo", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetstack); - LIB_FUNCTION("Ru36fiTtJzA", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGetstackaddr); - LIB_FUNCTION("-fA+7ZlGDQs", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGetstacksize); - LIB_FUNCTION("14bOACANTBo", "libkernel", 1, "libkernel", 1, 1, scePthreadOnce); - - // mutex calls - LIB_FUNCTION("cmo1RIYva9o", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexInit); - LIB_FUNCTION("2Of0f+3mhhE", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexDestroy); - LIB_FUNCTION("F8bUHwAG284", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexattrInit); - LIB_FUNCTION("smWEktiyyG0", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexattrDestroy); - LIB_FUNCTION("iMp8QpE+XO4", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexattrSettype); - LIB_FUNCTION("1FGvU0i9saQ", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexattrSetprotocol); - LIB_FUNCTION("9UK1vLZQft4", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexLock); - LIB_FUNCTION("tn3VlD0hG60", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexUnlock); - LIB_FUNCTION("upoVrzMHFeE", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexTrylock); - LIB_FUNCTION("IafI2PxcPnQ", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexTimedlock); - - // scePthreadMutexInitForInternalLibc, scePthreadMutexattrInitForInternalLibc - LIB_FUNCTION("qH1gXoq71RY", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexInit); - LIB_FUNCTION("n2MMpvU8igI", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexattrInit); - - // cond calls - LIB_FUNCTION("2Tb92quprl0", "libkernel", 1, "libkernel", 1, 1, scePthreadCondInit); - LIB_FUNCTION("m5-2bsNfv7s", "libkernel", 1, "libkernel", 1, 1, scePthreadCondattrInit); - LIB_FUNCTION("JGgj7Uvrl+A", "libkernel", 1, "libkernel", 1, 1, scePthreadCondBroadcast); - LIB_FUNCTION("WKAXJ4XBPQ4", "libkernel", 1, "libkernel", 1, 1, scePthreadCondWait); - LIB_FUNCTION("waPcxYiR3WA", "libkernel", 1, "libkernel", 1, 1, scePthreadCondattrDestroy); - LIB_FUNCTION("kDh-NfxgMtE", "libkernel", 1, "libkernel", 1, 1, scePthreadCondSignal); - LIB_FUNCTION("BmMjYxmew1w", "libkernel", 1, "libkernel", 1, 1, scePthreadCondTimedwait); - LIB_FUNCTION("g+PZd2hiacg", "libkernel", 1, "libkernel", 1, 1, scePthreadCondDestroy); - - // posix calls - LIB_FUNCTION("wtkt-teR1so", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_attr_init); - LIB_FUNCTION("2Q0z6rnBrTE", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_attr_setstacksize); - LIB_FUNCTION("ttHNfU+qDBU", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_init); - LIB_FUNCTION("7H0iTOciTLo", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_lock); - LIB_FUNCTION("2Z+PpY6CaJg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_unlock); - LIB_FUNCTION("ltCfaGr2JGE", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_destroy); - LIB_FUNCTION("Op8TBGY5KHg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_cond_wait); - LIB_FUNCTION("Op8TBGY5KHg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_wait); - LIB_FUNCTION("27bAgiJmOh0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_timedwait); - LIB_FUNCTION("mkx2fVhNMsg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_broadcast); - LIB_FUNCTION("dQHWEsJtoE4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutexattr_init); - LIB_FUNCTION("mDmgMOGVUqg", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_mutexattr_settype); - LIB_FUNCTION("5txKfcMUAok", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_mutexattr_setprotocol); - LIB_FUNCTION("HF7lK46xzjY", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_mutexattr_destroy); - LIB_FUNCTION("mKoTx03HRWA", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_condattr_init); - LIB_FUNCTION("dJcuQVn6-Iw", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_condattr_destroy); - LIB_FUNCTION("EjllaAqAPZo", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_condattr_setclock); - LIB_FUNCTION("Z4QosVuAsA0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_once); - LIB_FUNCTION("RtLRV-pBTTY", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_attr_getschedpolicy); - - // openorbis weird functions - LIB_FUNCTION("7H0iTOciTLo", "libkernel", 1, "libkernel", 1, 1, posix_pthread_mutex_lock); - LIB_FUNCTION("2Z+PpY6CaJg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_mutex_unlock); - LIB_FUNCTION("mkx2fVhNMsg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_cond_broadcast); - LIB_FUNCTION("K-jXhbt2gn4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_trylock); - LIB_FUNCTION("E+tyo3lp5Lw", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_attr_setdetachstate); - LIB_FUNCTION("zHchY8ft5pk", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_attr_destroy); - LIB_FUNCTION("euKRgm0Vn2M", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_attr_setschedparam); - LIB_FUNCTION("7ZlAakEf0Qg", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_attr_setinheritsched); - LIB_FUNCTION("a2P9wYGeZvc", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setprio); - LIB_FUNCTION("Jmi+9w9u0E4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create_name_np); - LIB_FUNCTION("OxhIB8LB-PQ", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create); - LIB_FUNCTION("+U1R4WtXvoc", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_detach); - LIB_FUNCTION("CBNtXOoef-E", "libScePosix", 1, "libkernel", 1, 1, posix_sched_get_priority_max); - LIB_FUNCTION("m0iS6jNsXds", "libScePosix", 1, "libkernel", 1, 1, posix_sched_get_priority_min); - LIB_FUNCTION("FIs3-UQT9sg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_getschedparam); - LIB_FUNCTION("Xs9hdiD7sAA", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setschedparam); - LIB_FUNCTION("pDuPEf3m4fI", "libScePosix", 1, "libkernel", 1, 1, posix_sem_init); - LIB_FUNCTION("YCV5dGGBcCo", "libScePosix", 1, "libkernel", 1, 1, posix_sem_wait); - LIB_FUNCTION("WBWzsRifCEA", "libScePosix", 1, "libkernel", 1, 1, posix_sem_trywait); - LIB_FUNCTION("w5IHyvahg-o", "libScePosix", 1, "libkernel", 1, 1, posix_sem_timedwait); - LIB_FUNCTION("IKP8typ0QUk", "libScePosix", 1, "libkernel", 1, 1, posix_sem_post); - LIB_FUNCTION("cDW233RAwWo", "libScePosix", 1, "libkernel", 1, 1, posix_sem_destroy); - LIB_FUNCTION("Bq+LRV-N6Hk", "libScePosix", 1, "libkernel", 1, 1, posix_sem_getvalue); - LIB_FUNCTION("0qOtCR-ZHck", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_attr_getstacksize); - // libs - RwlockSymbolsRegister(sym); - SemaphoreSymbolsRegister(sym); - KeySymbolsRegister(sym); -} - -} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/thread_management.h b/src/core/libraries/kernel/thread_management.h deleted file mode 100644 index 3cdb300f7..000000000 --- a/src/core/libraries/kernel/thread_management.h +++ /dev/null @@ -1,225 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include "common/types.h" - -#define ORBIS_PTHREAD_MUTEX_ADAPTIVE_INITIALIZER (reinterpret_cast(1)) - -namespace Core::Loader { -class SymbolsResolver; -} - -namespace Libraries::Kernel { -constexpr int ORBIS_KERNEL_PRIO_FIFO_DEFAULT = 700; -constexpr int ORBIS_KERNEL_PRIO_FIFO_HIGHEST = 256; -constexpr int ORBIS_KERNEL_PRIO_FIFO_LOWEST = 767; -constexpr int ORBIS_KERNEL_SEM_VALUE_MAX = 0x7FFFFFFF; - -constexpr int ORBIS_PTHREAD_MUTEX_ERRORCHECK = 1; -constexpr int ORBIS_PTHREAD_MUTEX_RECURSIVE = 2; -constexpr int ORBIS_PTHREAD_MUTEX_NORMAL = 3; -constexpr int ORBIS_PTHREAD_MUTEX_ADAPTIVE = 4; - -struct PthreadInternal; -struct PthreadAttrInternal; -struct PthreadMutexInternal; -struct PthreadMutexattrInternal; -struct PthreadCondInternal; -struct PthreadCondAttrInternal; -struct PthreadRwInternal; -struct PthreadRwLockAttrInternal; -class PthreadKeys; - -using SceKernelSchedParam = ::sched_param; -using ScePthread = PthreadInternal*; -using ScePthreadAttr = PthreadAttrInternal*; -using ScePthreadMutex = PthreadMutexInternal*; -using ScePthreadMutexattr = PthreadMutexattrInternal*; -using ScePthreadCond = PthreadCondInternal*; -using ScePthreadCondattr = PthreadCondAttrInternal*; -using OrbisPthreadRwlock = PthreadRwInternal*; -using OrbisPthreadRwlockattr = PthreadRwLockAttrInternal*; -using OrbisPthreadKey = u32; - -using PthreadKeyDestructor = PS4_SYSV_ABI void (*)(void*); -using PthreadEntryFunc = PS4_SYSV_ABI void* (*)(void*); - -struct PthreadInternal { - u8 reserved[4096]; - std::string name; - pthread_t pth; - ScePthreadAttr attr; - PthreadEntryFunc entry; - void* arg; - std::atomic_bool is_started; - std::atomic_bool is_detached; - std::atomic_bool is_almost_done; - std::atomic_bool is_free; - using Destructor = std::pair; - std::vector key_destructors; - int prio; -}; - -struct PthreadAttrInternal { - u8 reserved[64]; - u64 affinity; - size_t guard_size; - int policy; - bool detached; - pthread_attr_t pth_attr; -}; - -struct PthreadMutexInternal { - u8 reserved[256]; - std::string name; - pthread_mutex_t pth_mutex; -}; - -struct PthreadMutexattrInternal { - u8 reserved[64]; - pthread_mutexattr_t pth_mutex_attr; - int pprotocol; -}; - -struct PthreadCondInternal { - u8 reserved[256]; - std::string name; - pthread_cond_t cond; -}; - -struct PthreadCondAttrInternal { - u8 reserved[64]; - pthread_condattr_t cond_attr; - clockid_t clock; -}; - -struct PthreadRwLockAttrInternal { - u8 reserved[64]; - pthread_rwlockattr_t attr_rwlock; - int type; -}; - -struct PthreadRwInternal { - pthread_rwlock_t pth_rwlock; - std::string name; -}; - -struct PthreadSemInternal { - std::counting_semaphore semaphore; - std::atomic value; -}; - -class PThreadPool { -public: - ScePthread Create(const char* name); - -private: - std::vector m_threads; - std::mutex m_mutex; -}; - -class PThreadCxt { -public: - ScePthreadMutexattr* getDefaultMutexattr() { - return &m_default_mutexattr; - } - void setDefaultMutexattr(ScePthreadMutexattr attr) { - m_default_mutexattr = attr; - } - ScePthreadMutexattr* getAdaptiveMutexattr() { - return &m_adaptive_mutexattr; - } - void setAdaptiveMutexattr(ScePthreadMutexattr attr) { - m_adaptive_mutexattr = attr; - } - ScePthreadCondattr* getDefaultCondattr() { - return &m_default_condattr; - } - void setDefaultCondattr(ScePthreadCondattr attr) { - m_default_condattr = attr; - } - ScePthreadAttr* GetDefaultAttr() { - return &m_default_attr; - } - void SetDefaultAttr(ScePthreadAttr attr) { - m_default_attr = attr; - } - PThreadPool* GetPthreadPool() { - return m_pthread_pool; - } - void SetPthreadPool(PThreadPool* pool) { - m_pthread_pool = pool; - } - OrbisPthreadRwlockattr* getDefaultRwattr() { - return &m_default_Rwattr; - } - void setDefaultRwattr(OrbisPthreadRwlockattr attr) { - m_default_Rwattr = attr; - } - -private: - ScePthreadMutexattr m_default_mutexattr = nullptr; - ScePthreadMutexattr m_adaptive_mutexattr = nullptr; - ScePthreadCondattr m_default_condattr = nullptr; - ScePthreadAttr m_default_attr = nullptr; - PThreadPool* m_pthread_pool = nullptr; - OrbisPthreadRwlockattr m_default_Rwattr = nullptr; -}; - -void init_pthreads(); -void pthreadInitSelfMainThread(); - -int PS4_SYSV_ABI scePthreadAttrInit(ScePthreadAttr* attr); -int PS4_SYSV_ABI scePthreadAttrSetdetachstate(ScePthreadAttr* attr, int detachstate); -int PS4_SYSV_ABI scePthreadAttrSetinheritsched(ScePthreadAttr* attr, int inheritSched); -int PS4_SYSV_ABI scePthreadAttrSetschedparam(ScePthreadAttr* attr, - const SceKernelSchedParam* param); -int PS4_SYSV_ABI scePthreadAttrSetschedpolicy(ScePthreadAttr* attr, int policy); -ScePthread PS4_SYSV_ABI scePthreadSelf(); -int PS4_SYSV_ABI scePthreadAttrSetaffinity(ScePthreadAttr* pattr, - const /*SceKernelCpumask*/ u64 mask); -int PS4_SYSV_ABI scePthreadSetaffinity(ScePthread thread, const /*SceKernelCpumask*/ u64 mask); -int PS4_SYSV_ABI scePthreadGetaffinity(ScePthread thread, /*SceKernelCpumask*/ u64* mask); -int PS4_SYSV_ABI scePthreadCreate(ScePthread* thread, const ScePthreadAttr* attr, - PthreadEntryFunc start_routine, void* arg, const char* name); - -int PS4_SYSV_ABI scePthreadSetprio(ScePthread thread, int prio); - -/*** - * Mutex calls - */ -int PS4_SYSV_ABI scePthreadMutexInit(ScePthreadMutex* mutex, const ScePthreadMutexattr* attr, - const char* name); -int PS4_SYSV_ABI scePthreadMutexattrInit(ScePthreadMutexattr* attr); -int PS4_SYSV_ABI scePthreadMutexattrSettype(ScePthreadMutexattr* attr, int type); -int PS4_SYSV_ABI scePthreadMutexattrSetprotocol(ScePthreadMutexattr* attr, int protocol); -int PS4_SYSV_ABI scePthreadMutexLock(ScePthreadMutex* mutex); -int PS4_SYSV_ABI scePthreadMutexUnlock(ScePthreadMutex* mutex); -/**** - * Cond calls - */ -int PS4_SYSV_ABI scePthreadCondInit(ScePthreadCond* cond, const ScePthreadCondattr* attr, - const char* name); -int PS4_SYSV_ABI scePthreadCondattrInit(ScePthreadCondattr* attr); -int PS4_SYSV_ABI scePthreadCondBroadcast(ScePthreadCond* cond); -int PS4_SYSV_ABI scePthreadCondWait(ScePthreadCond* cond, ScePthreadMutex* mutex); -/**** - * Posix calls - */ -int PS4_SYSV_ABI posix_pthread_mutex_init(ScePthreadMutex* mutex, const ScePthreadMutexattr* attr); -int PS4_SYSV_ABI posix_pthread_mutex_lock(ScePthreadMutex* mutex); -int PS4_SYSV_ABI posix_pthread_mutex_unlock(ScePthreadMutex* mutex); -int PS4_SYSV_ABI posix_pthread_cond_broadcast(ScePthreadCond* cond); - -void pthreadSymbolsRegister(Core::Loader::SymbolsResolver* sym); -} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads.cpp b/src/core/libraries/kernel/threads.cpp new file mode 100644 index 000000000..082a52b67 --- /dev/null +++ b/src/core/libraries/kernel/threads.cpp @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/libraries/kernel/kernel.h" +#include "core/libraries/kernel/threads.h" +#include "core/libraries/kernel/threads/pthread.h" + +namespace Libraries::Kernel { + +void RegisterThreads(Core::Loader::SymbolsResolver* sym) { + RegisterMutex(sym); + RegisterCond(sym); + RegisterRwlock(sym); + RegisterSemaphore(sym); + RegisterSpec(sym); + RegisterThreadAttr(sym); + RegisterThread(sym); + RegisterRtld(sym); + RegisterPthreadClean(sym); +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads.h b/src/core/libraries/kernel/threads.h new file mode 100644 index 000000000..ad1393599 --- /dev/null +++ b/src/core/libraries/kernel/threads.h @@ -0,0 +1,72 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "common/polyfill_thread.h" +#include "core/libraries/kernel/threads/pthread.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::Kernel { + +int PS4_SYSV_ABI posix_pthread_attr_init(PthreadAttrT* attr); + +int PS4_SYSV_ABI posix_pthread_attr_destroy(PthreadAttrT* attr); + +int PS4_SYSV_ABI posix_pthread_create(PthreadT* thread, const PthreadAttrT* attr, + PthreadEntryFunc start_routine, void* arg); + +int PS4_SYSV_ABI posix_pthread_join(PthreadT pthread, void** thread_return); + +void RegisterThreads(Core::Loader::SymbolsResolver* sym); + +class Thread { +public: + explicit Thread() = default; + ~Thread() { + Stop(); + } + + void Run(std::function&& func) { + this->func = std::move(func); + PthreadAttrT attr{}; + posix_pthread_attr_init(&attr); + posix_pthread_create(&thread, &attr, RunWrapper, this); + posix_pthread_attr_destroy(&attr); + } + + void Join() { + if (thread) { + posix_pthread_join(thread, nullptr); + thread = nullptr; + } + } + + bool Joinable() const { + return thread != nullptr; + } + + void Stop() { + if (Joinable()) { + stop.request_stop(); + Join(); + } + } + + static void* PS4_SYSV_ABI RunWrapper(void* arg) { + Thread* thr = (Thread*)arg; + thr->func(thr->stop.get_token()); + return nullptr; + } + +private: + PthreadT thread{}; + std::function func; + std::stop_source stop; +}; + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/condvar.cpp b/src/core/libraries/kernel/threads/condvar.cpp new file mode 100644 index 000000000..5831df9be --- /dev/null +++ b/src/core/libraries/kernel/threads/condvar.cpp @@ -0,0 +1,360 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include "common/assert.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/kernel/kernel.h" +#include "core/libraries/kernel/threads/pthread.h" +#include "core/libraries/kernel/threads/sleepq.h" +#include "core/libraries/libs.h" + +namespace Libraries::Kernel { + +static std::mutex CondStaticLock; + +#define THR_COND_INITIALIZER ((PthreadCond*)NULL) +#define THR_COND_DESTROYED ((PthreadCond*)1) + +static constexpr PthreadCondAttr PhreadCondattrDefault = { + .c_pshared = 0, + .c_clockid = ClockId::Realtime, +}; + +static int CondInit(PthreadCondT* cond, const PthreadCondAttrT* cond_attr, const char* name) { + auto* cvp = new PthreadCond{}; + if (cvp == nullptr) { + return POSIX_ENOMEM; + } + + if (name) { + cvp->name = name; + } else { + static int CondId = 0; + cvp->name = fmt::format("Cond{}", CondId++); + } + + if (cond_attr == nullptr || *cond_attr == nullptr) { + cvp->clock_id = ClockId::Realtime; + } else { + // if ((*cond_attr)->c_pshared) { + // cvp->flags |= USYNC_PROCESS_SHARED; + // } + cvp->clock_id = (*cond_attr)->c_clockid; + } + *cond = cvp; + return 0; +} + +static int InitStatic(Pthread* thread, PthreadCondT* cond) { + std::scoped_lock lk{CondStaticLock}; + if (*cond == nullptr) { + return CondInit(cond, nullptr, nullptr); + } + return 0; +} + +#define CHECK_AND_INIT_COND \ + if (cvp = *cond; cvp <= THR_COND_DESTROYED) [[unlikely]] { \ + if (cvp == THR_COND_INITIALIZER) { \ + int ret; \ + ret = InitStatic(g_curthread, cond); \ + if (ret) \ + return (ret); \ + } else if (cvp == THR_COND_DESTROYED) { \ + return POSIX_EINVAL; \ + } \ + cvp = *cond; \ + } + +int PS4_SYSV_ABI posix_pthread_cond_init(PthreadCondT* cond, const PthreadCondAttrT* cond_attr) { + *cond = nullptr; + return CondInit(cond, cond_attr, nullptr); +} + +int PS4_SYSV_ABI scePthreadCondInit(PthreadCondT* cond, const PthreadCondAttrT* cond_attr, + const char* name) { + *cond = nullptr; + return CondInit(cond, cond_attr, name); +} + +int PS4_SYSV_ABI posix_pthread_cond_destroy(PthreadCondT* cond) { + PthreadCond* cvp = *cond; + if (cvp == THR_COND_INITIALIZER) { + return 0; + } + if (cvp == THR_COND_DESTROYED) { + return POSIX_EINVAL; + } + cvp = *cond; + *cond = THR_COND_DESTROYED; + delete cvp; + return 0; +} + +int PthreadCond::Wait(PthreadMutexT* mutex, const OrbisKernelTimespec* abstime, u64 usec) { + PthreadMutex* mp = *mutex; + if (int error = mp->IsOwned(g_curthread); error != 0) { + return error; + } + + Pthread* curthread = g_curthread; + ASSERT_MSG(curthread->wchan == nullptr, "Thread was already on queue."); + // _thr_testcancel(curthread); + SleepqLock(this); + + /* + * set __has_user_waiters before unlocking mutex, this allows + * us to check it without locking in pthread_cond_signal(). + */ + has_user_waiters = 1; + curthread->will_sleep = 1; + + int recurse; + mp->CvUnlock(&recurse); + + curthread->mutex_obj = mp; + SleepqAdd(this, curthread); + + int error = 0; + for (;;) { + void(curthread->wake_sema.try_acquire()); + SleepqUnlock(this); + + //_thr_cancel_enter2(curthread, 0); + int error = curthread->Sleep(abstime, usec) ? 0 : POSIX_ETIMEDOUT; + //_thr_cancel_leave(curthread, 0); + + SleepqLock(this); + if (curthread->wchan == nullptr) { + error = 0; + break; + } else if (curthread->ShouldCancel()) { + SleepQueue* sq = SleepqLookup(this); + has_user_waiters = SleepqRemove(sq, curthread); + SleepqUnlock(this); + curthread->mutex_obj = nullptr; + mp->CvLock(recurse); + return 0; + } else if (error == POSIX_ETIMEDOUT) { + SleepQueue* sq = SleepqLookup(this); + has_user_waiters = SleepqRemove(sq, curthread); + break; + } + UNREACHABLE(); + } + SleepqUnlock(this); + curthread->mutex_obj = nullptr; + mp->CvLock(recurse); + return error; +} + +int PS4_SYSV_ABI posix_pthread_cond_wait(PthreadCondT* cond, PthreadMutexT* mutex) { + PthreadCond* cvp{}; + CHECK_AND_INIT_COND + return cvp->Wait(mutex, nullptr); +} + +int PS4_SYSV_ABI posix_pthread_cond_timedwait(PthreadCondT* cond, PthreadMutexT* mutex, + const OrbisKernelTimespec* abstime) { + if (abstime == nullptr || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || + abstime->tv_nsec >= 1000000000) { + return POSIX_EINVAL; + } + + PthreadCond* cvp{}; + CHECK_AND_INIT_COND + return cvp->Wait(mutex, abstime); +} + +int PS4_SYSV_ABI posix_pthread_cond_reltimedwait_np(PthreadCondT* cond, PthreadMutexT* mutex, + u64 usec) { + PthreadCond* cvp{}; + CHECK_AND_INIT_COND + return cvp->Wait(mutex, THR_RELTIME, usec); +} + +int PthreadCond::Signal() { + Pthread* curthread = g_curthread; + + SleepqLock(this); + SleepQueue* sq = SleepqLookup(this); + if (sq == nullptr) { + SleepqUnlock(this); + return 0; + } + + Pthread* td = sq->sq_blocked.front(); + PthreadMutex* mp = td->mutex_obj; + has_user_waiters = SleepqRemove(sq, td); + + std::binary_semaphore* waddr = nullptr; + if (mp->m_owner == curthread) { + if (curthread->nwaiter_defer >= Pthread::MaxDeferWaiters) { + curthread->WakeAll(); + } + curthread->defer_waiters[curthread->nwaiter_defer++] = &td->wake_sema; + mp->m_flags |= PthreadMutexFlags::Defered; + } else { + waddr = &td->wake_sema; + } + + SleepqUnlock(this); + if (waddr != nullptr) { + waddr->release(); + } + return 0; +} + +struct BroadcastArg { + Pthread* curthread; + std::binary_semaphore* waddrs[Pthread::MaxDeferWaiters]; + int count; +}; + +int PthreadCond::Broadcast() { + BroadcastArg ba; + ba.curthread = g_curthread; + ba.count = 0; + + const auto drop_cb = [](Pthread* td, void* arg) { + BroadcastArg* ba = reinterpret_cast(arg); + Pthread* curthread = ba->curthread; + PthreadMutex* mp = td->mutex_obj; + + if (mp->m_owner == curthread) { + if (curthread->nwaiter_defer >= Pthread::MaxDeferWaiters) { + curthread->WakeAll(); + } + curthread->defer_waiters[curthread->nwaiter_defer++] = &td->wake_sema; + mp->m_flags |= PthreadMutexFlags::Defered; + } else { + if (ba->count >= Pthread::MaxDeferWaiters) { + for (int i = 0; i < ba->count; i++) { + ba->waddrs[i]->release(); + } + ba->count = 0; + } + ba->waddrs[ba->count++] = &td->wake_sema; + } + }; + + SleepqLock(this); + SleepQueue* sq = SleepqLookup(this); + if (sq == nullptr) { + SleepqUnlock(this); + return 0; + } + + SleepqDrop(sq, drop_cb, &ba); + has_user_waiters = 0; + SleepqUnlock(this); + + for (int i = 0; i < ba.count; i++) { + ba.waddrs[i]->release(); + } + return 0; +} + +int PS4_SYSV_ABI posix_pthread_cond_signal(PthreadCondT* cond) { + PthreadCond* cvp{}; + CHECK_AND_INIT_COND + return cvp->Signal(); +} + +int PS4_SYSV_ABI posix_pthread_cond_broadcast(PthreadCondT* cond) { + PthreadCond* cvp{}; + CHECK_AND_INIT_COND + cvp->Broadcast(); + return 0; +} + +int PS4_SYSV_ABI posix_pthread_condattr_init(PthreadCondAttrT* attr) { + PthreadCondAttr* pattr = new PthreadCondAttr{}; + if (pattr == nullptr) { + return POSIX_ENOMEM; + } + memcpy(pattr, &PhreadCondattrDefault, sizeof(PthreadCondAttr)); + *attr = pattr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_condattr_destroy(PthreadCondAttrT* attr) { + if (attr == nullptr || *attr == nullptr) { + return POSIX_EINVAL; + } + delete *attr; + *attr = nullptr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_condattr_getclock(const PthreadCondAttrT* attr, ClockId* clock_id) { + if (attr == nullptr || *attr == nullptr) { + return POSIX_EINVAL; + } + *clock_id = static_cast((*attr)->c_clockid); + return 0; +} + +int PS4_SYSV_ABI posix_pthread_condattr_setclock(PthreadCondAttrT* attr, ClockId clock_id) { + if (attr == nullptr || *attr == nullptr) { + return POSIX_EINVAL; + } + if (clock_id != ClockId::Realtime && clock_id != ClockId::Virtual && + clock_id != ClockId::Prof && clock_id != ClockId::Monotonic) { + return POSIX_EINVAL; + } + (*attr)->c_clockid = clock_id; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_condattr_getpshared(const PthreadCondAttrT* attr, int* pshared) { + if (attr == nullptr || *attr == nullptr) { + return POSIX_EINVAL; + } + *pshared = 0; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_condattr_setpshared(PthreadCondAttrT* attr, int pshared) { + if (attr == nullptr || *attr == nullptr) { + return POSIX_EINVAL; + } + if (pshared != 0) { + return POSIX_EINVAL; + } + return 0; +} + +void RegisterCond(Core::Loader::SymbolsResolver* sym) { + // Posix + LIB_FUNCTION("mKoTx03HRWA", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_condattr_init); + LIB_FUNCTION("0TyVk4MSLt0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_init); + LIB_FUNCTION("2MOy+rUfuhQ", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_signal); + LIB_FUNCTION("RXXqi4CtF8w", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_destroy); + LIB_FUNCTION("Op8TBGY5KHg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_wait); + LIB_FUNCTION("27bAgiJmOh0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_timedwait); + LIB_FUNCTION("mkx2fVhNMsg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_broadcast); + + // Posix-Kernel + LIB_FUNCTION("Op8TBGY5KHg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_cond_wait); + LIB_FUNCTION("mkx2fVhNMsg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_cond_broadcast); + + // Orbis + LIB_FUNCTION("2Tb92quprl0", "libkernel", 1, "libkernel", 1, 1, ORBIS(scePthreadCondInit)); + LIB_FUNCTION("m5-2bsNfv7s", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_condattr_init)); + LIB_FUNCTION("JGgj7Uvrl+A", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_cond_broadcast)); + LIB_FUNCTION("WKAXJ4XBPQ4", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_cond_wait)); + LIB_FUNCTION("waPcxYiR3WA", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_condattr_destroy)); + LIB_FUNCTION("kDh-NfxgMtE", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_cond_signal)); + LIB_FUNCTION("BmMjYxmew1w", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_cond_reltimedwait_np)); + LIB_FUNCTION("g+PZd2hiacg", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_cond_destroy)); +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/event_flag/event_flag.cpp b/src/core/libraries/kernel/threads/event_flag.cpp similarity index 56% rename from src/core/libraries/kernel/event_flag/event_flag.cpp rename to src/core/libraries/kernel/threads/event_flag.cpp index f83ae0a7f..134bd5491 100644 --- a/src/core/libraries/kernel/event_flag/event_flag.cpp +++ b/src/core/libraries/kernel/threads/event_flag.cpp @@ -1,13 +1,158 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include + #include "common/assert.h" #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" -#include "event_flag.h" namespace Libraries::Kernel { + +constexpr int ORBIS_KERNEL_EVF_ATTR_TH_FIFO = 0x01; +constexpr int ORBIS_KERNEL_EVF_ATTR_TH_PRIO = 0x02; +constexpr int ORBIS_KERNEL_EVF_ATTR_SINGLE = 0x10; +constexpr int ORBIS_KERNEL_EVF_ATTR_MULTI = 0x20; + +constexpr int ORBIS_KERNEL_EVF_WAITMODE_AND = 0x01; +constexpr int ORBIS_KERNEL_EVF_WAITMODE_OR = 0x02; +constexpr int ORBIS_KERNEL_EVF_WAITMODE_CLEAR_ALL = 0x10; +constexpr int ORBIS_KERNEL_EVF_WAITMODE_CLEAR_PAT = 0x20; + +class EventFlagInternal { +public: + enum class ClearMode { None, All, Bits }; + enum class WaitMode { And, Or }; + enum class ThreadMode { Single, Multi }; + enum class QueueMode { Fifo, ThreadPrio }; + + EventFlagInternal(const std::string& name, ThreadMode thread_mode, QueueMode queue_mode, + uint64_t bits) + : m_name(name), m_thread_mode(thread_mode), m_queue_mode(queue_mode), m_bits(bits){}; + + int Wait(u64 bits, WaitMode wait_mode, ClearMode clear_mode, u64* result, u32* ptr_micros) { + std::unique_lock lock{m_mutex}; + + uint32_t micros = 0; + bool infinitely = true; + if (ptr_micros != nullptr) { + micros = *ptr_micros; + infinitely = false; + } + + if (m_thread_mode == ThreadMode::Single && m_waiting_threads > 0) { + return ORBIS_KERNEL_ERROR_EPERM; + } + + auto const start = std::chrono::system_clock::now(); + m_waiting_threads++; + auto waitFunc = [this, wait_mode, bits] { + return (m_status == Status::Canceled || m_status == Status::Deleted || + (wait_mode == WaitMode::And && (m_bits & bits) == bits) || + (wait_mode == WaitMode::Or && (m_bits & bits) != 0)); + }; + + if (infinitely) { + m_cond_var.wait(lock, waitFunc); + } else { + if (!m_cond_var.wait_for(lock, std::chrono::microseconds(micros), waitFunc)) { + if (result != nullptr) { + *result = m_bits; + } + *ptr_micros = 0; + --m_waiting_threads; + return ORBIS_KERNEL_ERROR_ETIMEDOUT; + } + } + --m_waiting_threads; + if (result != nullptr) { + *result = m_bits; + } + + auto elapsed = std::chrono::duration_cast( + std::chrono::system_clock::now() - start) + .count(); + if (result != nullptr) { + *result = m_bits; + } + + if (ptr_micros != nullptr) { + *ptr_micros = (elapsed >= micros ? 0 : micros - elapsed); + } + + if (m_status == Status::Canceled) { + return ORBIS_KERNEL_ERROR_ECANCELED; + } else if (m_status == Status::Deleted) { + return ORBIS_KERNEL_ERROR_EACCES; + } + + if (clear_mode == ClearMode::All) { + m_bits = 0; + } else if (clear_mode == ClearMode::Bits) { + m_bits &= ~bits; + } + + return ORBIS_OK; + } + + int Poll(u64 bits, WaitMode wait_mode, ClearMode clear_mode, u64* result) { + u32 micros = 0; + auto ret = Wait(bits, wait_mode, clear_mode, result, µs); + if (ret == ORBIS_KERNEL_ERROR_ETIMEDOUT) { + // Poll returns EBUSY instead. + ret = ORBIS_KERNEL_ERROR_EBUSY; + } + return ret; + } + + void Set(u64 bits) { + std::unique_lock lock{m_mutex}; + + while (m_status != Status::Set) { + m_mutex.unlock(); + std::this_thread::sleep_for(std::chrono::microseconds(10)); + m_mutex.lock(); + } + + m_bits |= bits; + + m_cond_var.notify_all(); + } + + void Clear(u64 bits) { + std::unique_lock lock{m_mutex}; + while (m_status != Status::Set) { + m_mutex.unlock(); + std::this_thread::sleep_for(std::chrono::microseconds(10)); + m_mutex.lock(); + } + + m_bits &= bits; + } + +private: + enum class Status { Set, Canceled, Deleted }; + + std::mutex m_mutex; + std::condition_variable m_cond_var; + Status m_status = Status::Set; + int m_waiting_threads = 0; + std::string m_name; + ThreadMode m_thread_mode = ThreadMode::Single; + QueueMode m_queue_mode = QueueMode::Fifo; + u64 m_bits = 0; +}; + +using OrbisKernelUseconds = u32; +using OrbisKernelEventFlag = EventFlagInternal*; + +struct OrbisKernelEventFlagOptParam { + size_t size; +}; + int PS4_SYSV_ABI sceKernelCreateEventFlag(OrbisKernelEventFlag* ef, const char* pName, u32 attr, u64 initPattern, const OrbisKernelEventFlagOptParam* pOptParam) { @@ -25,9 +170,8 @@ int PS4_SYSV_ABI sceKernelCreateEventFlag(OrbisKernelEventFlag* ef, const char* return ORBIS_KERNEL_ERROR_ENAMETOOLONG; } - EventFlagInternal::ThreadMode thread_mode = EventFlagInternal::ThreadMode::Single; - EventFlagInternal::QueueMode queue_mode = EventFlagInternal::QueueMode::Fifo; - + auto thread_mode = EventFlagInternal::ThreadMode::Single; + auto queue_mode = EventFlagInternal::QueueMode::Fifo; switch (attr & 0xfu) { case 0x01: queue_mode = EventFlagInternal::QueueMode::Fifo; @@ -61,6 +205,7 @@ int PS4_SYSV_ABI sceKernelCreateEventFlag(OrbisKernelEventFlag* ef, const char* *ef = new EventFlagInternal(std::string(pName), thread_mode, queue_mode, initPattern); return ORBIS_OK; } + int PS4_SYSV_ABI sceKernelDeleteEventFlag(OrbisKernelEventFlag ef) { if (ef == nullptr) { return ORBIS_KERNEL_ERROR_ESRCH; @@ -69,24 +214,29 @@ int PS4_SYSV_ABI sceKernelDeleteEventFlag(OrbisKernelEventFlag ef) { delete ef; return ORBIS_OK; } + int PS4_SYSV_ABI sceKernelOpenEventFlag() { LOG_ERROR(Kernel_Event, "(STUBBED) called"); return ORBIS_OK; } + int PS4_SYSV_ABI sceKernelCloseEventFlag() { LOG_ERROR(Kernel_Event, "(STUBBED) called"); return ORBIS_OK; } + int PS4_SYSV_ABI sceKernelClearEventFlag(OrbisKernelEventFlag ef, u64 bitPattern) { LOG_DEBUG(Kernel_Event, "called"); ef->Clear(bitPattern); return ORBIS_OK; } + int PS4_SYSV_ABI sceKernelCancelEventFlag(OrbisKernelEventFlag ef, u64 setPattern, int* pNumWaitThreads) { LOG_ERROR(Kernel_Event, "(STUBBED) called"); return ORBIS_OK; } + int PS4_SYSV_ABI sceKernelSetEventFlag(OrbisKernelEventFlag ef, u64 bitPattern) { LOG_TRACE(Kernel_Event, "called"); if (ef == nullptr) { @@ -95,6 +245,7 @@ int PS4_SYSV_ABI sceKernelSetEventFlag(OrbisKernelEventFlag ef, u64 bitPattern) ef->Set(bitPattern); return ORBIS_OK; } + int PS4_SYSV_ABI sceKernelPollEventFlag(OrbisKernelEventFlag ef, u64 bitPattern, u32 waitMode, u64* pResultPat) { LOG_DEBUG(Kernel_Event, "called bitPattern = {:#x} waitMode = {:#x}", bitPattern, waitMode); @@ -107,9 +258,8 @@ int PS4_SYSV_ABI sceKernelPollEventFlag(OrbisKernelEventFlag ef, u64 bitPattern, return ORBIS_KERNEL_ERROR_EINVAL; } - EventFlagInternal::WaitMode wait = EventFlagInternal::WaitMode::And; - EventFlagInternal::ClearMode clear = EventFlagInternal::ClearMode::None; - + auto wait = EventFlagInternal::WaitMode::And; + auto clear = EventFlagInternal::ClearMode::None; switch (waitMode & 0xf) { case 0x01: wait = EventFlagInternal::WaitMode::And; @@ -154,9 +304,8 @@ int PS4_SYSV_ABI sceKernelWaitEventFlag(OrbisKernelEventFlag ef, u64 bitPattern, return ORBIS_KERNEL_ERROR_EINVAL; } - EventFlagInternal::WaitMode wait = EventFlagInternal::WaitMode::And; - EventFlagInternal::ClearMode clear = EventFlagInternal::ClearMode::None; - + auto wait = EventFlagInternal::WaitMode::And; + auto clear = EventFlagInternal::ClearMode::None; switch (waitMode & 0xf) { case 0x01: wait = EventFlagInternal::WaitMode::And; @@ -190,6 +339,7 @@ int PS4_SYSV_ABI sceKernelWaitEventFlag(OrbisKernelEventFlag ef, u64 bitPattern, return result; } + void RegisterKernelEventFlag(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("PZku4ZrXJqg", "libkernel", 1, "libkernel", 1, 1, sceKernelCancelEventFlag); LIB_FUNCTION("7uhBFWRAS60", "libkernel", 1, "libkernel", 1, 1, sceKernelClearEventFlag); @@ -201,4 +351,5 @@ void RegisterKernelEventFlag(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("IOnSvHzqu6A", "libkernel", 1, "libkernel", 1, 1, sceKernelSetEventFlag); LIB_FUNCTION("JTvBflhYazQ", "libkernel", 1, "libkernel", 1, 1, sceKernelWaitEventFlag); } + } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/exception.cpp b/src/core/libraries/kernel/threads/exception.cpp new file mode 100644 index 000000000..b6d89aae4 --- /dev/null +++ b/src/core/libraries/kernel/threads/exception.cpp @@ -0,0 +1,131 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/assert.h" +#include "core/libraries/kernel/threads/exception.h" +#include "core/libraries/kernel/threads/pthread.h" +#include "core/libraries/libs.h" + +#ifdef _WIN64 +#else +#include +#endif + +namespace Libraries::Kernel { + +static std::array Handlers{}; + +#ifndef _WIN64 +void SigactionHandler(int signum, siginfo_t* inf, ucontext_t* raw_context) { + const auto handler = Handlers[POSIX_SIGUSR1]; + if (handler) { + auto ctx = Ucontext{}; +#ifdef __APPLE__ + auto& regs = raw_context->uc_mcontext->__ss; + ctx.uc_mcontext.mc_r8 = regs.__r8; + ctx.uc_mcontext.mc_r9 = regs.__r9; + ctx.uc_mcontext.mc_r10 = regs.__r10; + ctx.uc_mcontext.mc_r11 = regs.__r11; + ctx.uc_mcontext.mc_r12 = regs.__r12; + ctx.uc_mcontext.mc_r13 = regs.__r13; + ctx.uc_mcontext.mc_r14 = regs.__r14; + ctx.uc_mcontext.mc_r15 = regs.__r15; + ctx.uc_mcontext.mc_rdi = regs.__rdi; + ctx.uc_mcontext.mc_rsi = regs.__rsi; + ctx.uc_mcontext.mc_rbp = regs.__rbp; + ctx.uc_mcontext.mc_rbx = regs.__rbx; + ctx.uc_mcontext.mc_rdx = regs.__rdx; + ctx.uc_mcontext.mc_rax = regs.__rax; + ctx.uc_mcontext.mc_rcx = regs.__rcx; + ctx.uc_mcontext.mc_rsp = regs.__rsp; + ctx.uc_mcontext.mc_fs = regs.__fs; + ctx.uc_mcontext.mc_gs = regs.__gs; +#else + auto& regs = raw_context->uc_mcontext.gregs; + ctx.uc_mcontext.mc_r8 = regs[REG_R8]; + ctx.uc_mcontext.mc_r9 = regs[REG_R9]; + ctx.uc_mcontext.mc_r10 = regs[REG_R10]; + ctx.uc_mcontext.mc_r11 = regs[REG_R11]; + ctx.uc_mcontext.mc_r12 = regs[REG_R12]; + ctx.uc_mcontext.mc_r13 = regs[REG_R13]; + ctx.uc_mcontext.mc_r14 = regs[REG_R14]; + ctx.uc_mcontext.mc_r15 = regs[REG_R15]; + ctx.uc_mcontext.mc_rdi = regs[REG_RDI]; + ctx.uc_mcontext.mc_rsi = regs[REG_RSI]; + ctx.uc_mcontext.mc_rbp = regs[REG_RBP]; + ctx.uc_mcontext.mc_rbx = regs[REG_RBX]; + ctx.uc_mcontext.mc_rdx = regs[REG_RDX]; + ctx.uc_mcontext.mc_rax = regs[REG_RAX]; + ctx.uc_mcontext.mc_rcx = regs[REG_RCX]; + ctx.uc_mcontext.mc_rsp = regs[REG_RSP]; + ctx.uc_mcontext.mc_fs = (regs[REG_CSGSFS] >> 32) & 0xFFFF; + ctx.uc_mcontext.mc_gs = (regs[REG_CSGSFS] >> 16) & 0xFFFF; +#endif + handler(POSIX_SIGUSR1, &ctx); + } +} +#endif + +int PS4_SYSV_ABI sceKernelInstallExceptionHandler(s32 signum, SceKernelExceptionHandler handler) { + if (signum != POSIX_SIGUSR1) { + LOG_ERROR(Lib_Kernel, "Installing non-supported exception handler for signal {}", signum); + return 0; + } + ASSERT_MSG(!Handlers[POSIX_SIGUSR1], "Invalid parameters"); + Handlers[POSIX_SIGUSR1] = handler; +#ifdef _WIN64 + UNREACHABLE_MSG("Missing exception implementation"); +#else + struct sigaction act = {}; + act.sa_flags = SA_SIGINFO | SA_RESTART; + act.sa_sigaction = reinterpret_cast(SigactionHandler); + sigaction(SIGUSR2, &act, nullptr); +#endif + return 0; +} + +int PS4_SYSV_ABI sceKernelRemoveExceptionHandler(s32 signum) { + if (signum != POSIX_SIGUSR1) { + LOG_ERROR(Lib_Kernel, "Installing non-supported exception handler for signal {}", signum); + return 0; + } + ASSERT_MSG(Handlers[POSIX_SIGUSR1], "Invalid parameters"); + Handlers[POSIX_SIGUSR1] = nullptr; +#ifdef _WIN64 + UNREACHABLE_MSG("Missing exception implementation"); +#else + struct sigaction act = {}; + act.sa_flags = SA_SIGINFO | SA_RESTART; + act.sa_sigaction = nullptr; + sigaction(SIGUSR2, &act, nullptr); +#endif + return 0; +} + +int PS4_SYSV_ABI sceKernelRaiseException(PthreadT thread, int signum) { + LOG_ERROR(Lib_Kernel, "Raising exception"); + ASSERT_MSG(signum == POSIX_SIGUSR1, "Attempting to raise non user defined signal!"); +#ifdef _WIN64 + UNREACHABLE_MSG("Missing exception implementation"); +#else + pthread_t pthr = *reinterpret_cast(thread->native_thr.GetHandle()); + pthread_kill(pthr, SIGUSR2); +#endif + return 0; +} + +int PS4_SYSV_ABI sceKernelDebugRaiseException() { + UNREACHABLE(); + return 0; +} + +void RegisterException(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("il03nluKfMk", "libkernel_unity", 1, "libkernel", 1, 1, sceKernelRaiseException); + LIB_FUNCTION("WkwEd3N7w0Y", "libkernel_unity", 1, "libkernel", 1, 1, + sceKernelInstallExceptionHandler); + LIB_FUNCTION("Qhv5ARAoOEc", "libkernel_unity", 1, "libkernel", 1, 1, + sceKernelRemoveExceptionHandler) + LIB_FUNCTION("OMDRKKAZ8I4", "libkernel", 1, "libkernel", 1, 1, sceKernelDebugRaiseException); +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/exception.h b/src/core/libraries/kernel/threads/exception.h new file mode 100644 index 000000000..985a7f56b --- /dev/null +++ b/src/core/libraries/kernel/threads/exception.h @@ -0,0 +1,86 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::Kernel { + +using SceKernelExceptionHandler = PS4_SYSV_ABI void (*)(int, void*); + +constexpr int POSIX_SIGSEGV = 11; +constexpr int POSIX_SIGUSR1 = 30; + +struct Mcontext { + u64 mc_onstack; + u64 mc_rdi; + u64 mc_rsi; + u64 mc_rdx; + u64 mc_rcx; + u64 mc_r8; + u64 mc_r9; + u64 mc_rax; + u64 mc_rbx; + u64 mc_rbp; + u64 mc_r10; + u64 mc_r11; + u64 mc_r12; + u64 mc_r13; + u64 mc_r14; + u64 mc_r15; + int mc_trapno; + u16 mc_fs; + u16 mc_gs; + u64 mc_addr; + int mc_flags; + u16 mc_es; + u16 mc_ds; + u64 mc_err; + u64 mc_rip; + u64 mc_cs; + u64 mc_rflags; + u64 mc_rsp; + u64 mc_ss; + u64 mc_len; + u64 mc_fpformat; + u64 mc_ownedfp; + u64 mc_lbrfrom; + u64 mc_lbrto; + u64 mc_aux1; + u64 mc_aux2; + u64 mc_fpstate[104]; + u64 mc_fsbase; + u64 mc_gsbase; + u64 mc_spare[6]; +}; + +struct Stack { + void* ss_sp; + std::size_t ss_size; + int ss_flags; + int _align; +}; + +struct Sigset { + u64 bits[2]; +}; + +struct Ucontext { + struct Sigset uc_sigmask; + int field1_0x10[12]; + struct Mcontext uc_mcontext; + struct Ucontext* uc_link; + struct Stack uc_stack; + int uc_flags; + int __spare[4]; + int field7_0x4f4[3]; +}; + +void RegisterException(Core::Loader::SymbolsResolver* sym); + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/keys.cpp b/src/core/libraries/kernel/threads/keys.cpp deleted file mode 100644 index cf5104d21..000000000 --- a/src/core/libraries/kernel/threads/keys.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/libraries/error_codes.h" -#include "core/libraries/kernel/thread_management.h" -#include "core/libraries/libs.h" - -namespace Libraries::Kernel { - -int PS4_SYSV_ABI scePthreadKeyCreate(OrbisPthreadKey* key, PthreadKeyDestructor destructor) { - if (key == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - - pthread_key_t thread_key; - int result = pthread_key_create(&thread_key, nullptr); - *key = static_cast(thread_key); - - if (destructor) { - auto thread = scePthreadSelf(); - thread->key_destructors.emplace_back(*key, destructor); - } - - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "scePthreadKeyCreate: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return result; -} - -void* PS4_SYSV_ABI scePthreadGetspecific(OrbisPthreadKey key) { - return pthread_getspecific(key); -} - -int PS4_SYSV_ABI scePthreadSetspecific(OrbisPthreadKey key, /* const*/ void* value) { - int result = pthread_setspecific(key, value); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "scePthreadSetspecific: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return result; -} - -void KeySymbolsRegister(Core::Loader::SymbolsResolver* sym) { - LIB_FUNCTION("geDaqgH9lTg", "libkernel", 1, "libkernel", 1, 1, scePthreadKeyCreate); - LIB_FUNCTION("eoht7mQOCmo", "libkernel", 1, "libkernel", 1, 1, scePthreadGetspecific); - LIB_FUNCTION("+BzXYkqYeLE", "libkernel", 1, "libkernel", 1, 1, scePthreadSetspecific); -} - -} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/mutex.cpp b/src/core/libraries/kernel/threads/mutex.cpp new file mode 100644 index 000000000..669eb3bda --- /dev/null +++ b/src/core/libraries/kernel/threads/mutex.cpp @@ -0,0 +1,468 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include "common/assert.h" +#include "common/scope_exit.h" +#include "common/types.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/kernel/kernel.h" +#include "core/libraries/kernel/threads/pthread.h" +#include "core/libraries/libs.h" + +namespace Libraries::Kernel { + +static constexpr u32 MUTEX_ADAPTIVE_SPINS = 2000; +static std::mutex MutxStaticLock; + +#define THR_MUTEX_INITIALIZER ((PthreadMutex*)NULL) +#define THR_ADAPTIVE_MUTEX_INITIALIZER ((PthreadMutex*)1) +#define THR_MUTEX_DESTROYED ((PthreadMutex*)2) + +#define CPU_SPINWAIT __asm__ volatile("pause") + +#define CHECK_AND_INIT_MUTEX \ + if (PthreadMutex* m = *mutex; m <= THR_MUTEX_DESTROYED) [[unlikely]] { \ + if (m == THR_MUTEX_DESTROYED) { \ + return POSIX_EINVAL; \ + } \ + if (s32 ret = InitStatic(g_curthread, mutex); ret) { \ + return ret; \ + } \ + m = *mutex; \ + } + +static constexpr PthreadMutexAttr PthreadMutexattrDefault = { + .m_type = PthreadMutexType::ErrorCheck, .m_protocol = PthreadMutexProt::None, .m_ceiling = 0}; + +static constexpr PthreadMutexAttr PthreadMutexattrAdaptiveDefault = { + .m_type = PthreadMutexType::AdaptiveNp, .m_protocol = PthreadMutexProt::None, .m_ceiling = 0}; + +using CallocFun = void* (*)(size_t, size_t); + +static int MutexInit(PthreadMutexT* mutex, const PthreadMutexAttr* mutex_attr, const char* name) { + const PthreadMutexAttr* attr; + if (mutex_attr == NULL) { + attr = &PthreadMutexattrDefault; + } else { + attr = mutex_attr; + if (attr->m_type < PthreadMutexType::ErrorCheck || attr->m_type >= PthreadMutexType::Max) { + return POSIX_EINVAL; + } + if (attr->m_protocol > PthreadMutexProt::Protect) { + return POSIX_EINVAL; + } + } + auto* pmutex = new PthreadMutex{}; + if (pmutex == nullptr) { + return POSIX_ENOMEM; + } + + if (name) { + pmutex->name = name; + } else { + static int MutexId = 0; + pmutex->name = fmt::format("Mutex{}", MutexId++); + } + + pmutex->m_flags = PthreadMutexFlags(attr->m_type); + pmutex->m_owner = nullptr; + pmutex->m_count = 0; + pmutex->m_spinloops = 0; + pmutex->m_yieldloops = 0; + pmutex->m_protocol = attr->m_protocol; + if (attr->m_type == PthreadMutexType::AdaptiveNp) { + pmutex->m_spinloops = MUTEX_ADAPTIVE_SPINS; + // pmutex->m_yieldloops = _thr_yieldloops; + } + + *mutex = pmutex; + return 0; +} + +static int InitStatic(Pthread* thread, PthreadMutexT* mutex) { + std::scoped_lock lk{MutxStaticLock}; + + if (*mutex == THR_MUTEX_INITIALIZER) { + return MutexInit(mutex, &PthreadMutexattrDefault, nullptr); + } else if (*mutex == THR_ADAPTIVE_MUTEX_INITIALIZER) { + return MutexInit(mutex, &PthreadMutexattrAdaptiveDefault, nullptr); + } + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutex_init(PthreadMutexT* mutex, + const PthreadMutexAttrT* mutex_attr) { + return MutexInit(mutex, mutex_attr ? *mutex_attr : nullptr, nullptr); +} + +int PS4_SYSV_ABI scePthreadMutexInit(PthreadMutexT* mutex, const PthreadMutexAttrT* mutex_attr, + const char* name) { + return MutexInit(mutex, mutex_attr ? *mutex_attr : nullptr, name); +} + +int PS4_SYSV_ABI posix_pthread_mutex_destroy(PthreadMutexT* mutex) { + PthreadMutexT m = *mutex; + if (m < THR_MUTEX_DESTROYED) { + return 0; + } + if (m == THR_MUTEX_DESTROYED) { + return POSIX_EINVAL; + } + if (m->m_owner != nullptr) { + return POSIX_EBUSY; + } + *mutex = THR_MUTEX_DESTROYED; + delete m; + return 0; +} + +int PthreadMutex::SelfTryLock() { + switch (Type()) { + case PthreadMutexType::ErrorCheck: + case PthreadMutexType::Normal: + return POSIX_EBUSY; + case PthreadMutexType::Recursive: { + /* Increment the lock count: */ + if (m_count + 1 > 0) { + m_count++; + return 0; + } + return POSIX_EAGAIN; + } + default: + return POSIX_EINVAL; + } +} + +int PthreadMutex::SelfLock(const OrbisKernelTimespec* abstime, u64 usec) { + const auto DoSleep = [&] { + if (abstime == THR_RELTIME) { + std::this_thread::sleep_for(std::chrono::microseconds(usec)); + return POSIX_ETIMEDOUT; + } else { + if (abstime->tv_sec < 0 || abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) { + return POSIX_EINVAL; + } else { + std::this_thread::sleep_until(abstime->TimePoint()); + return POSIX_ETIMEDOUT; + } + } + }; + switch (Type()) { + case PthreadMutexType::ErrorCheck: + case PthreadMutexType::AdaptiveNp: { + if (abstime) { + return DoSleep(); + } + /* + * POSIX specifies that mutexes should return + * EDEADLK if a recursive lock is detected. + */ + return POSIX_EDEADLK; + } + case PthreadMutexType::Normal: { + /* + * What SS2 define as a 'normal' mutex. Intentionally + * deadlock on attempts to get a lock you already own. + */ + if (abstime) { + return DoSleep(); + } + UNREACHABLE_MSG("Mutex deadlock occured"); + return 0; + } + case PthreadMutexType::Recursive: { + /* Increment the lock count: */ + if (m_count + 1 > 0) { + m_count++; + return 0; + } + return POSIX_EAGAIN; + } + default: + return POSIX_EINVAL; + } +} + +int PthreadMutex::Lock(const OrbisKernelTimespec* abstime, u64 usec) { + Pthread* curthread = g_curthread; + if (m_owner == curthread) { + return SelfLock(abstime, usec); + } + + /* + * For adaptive mutexes, spin for a bit in the expectation + * that if the application requests this mutex type then + * the lock is likely to be released quickly and it is + * faster than entering the kernel + */ + if (m_protocol == PthreadMutexProt::None) [[likely]] { + int count = m_spinloops; + while (count--) { + if (m_lock.try_lock()) { + m_owner = curthread; + return 0; + } + CPU_SPINWAIT; + } + + count = m_yieldloops; + while (count--) { + std::this_thread::yield(); + if (m_lock.try_lock()) { + m_owner = curthread; + return 0; + } + } + } + + int ret = 0; + if (abstime == nullptr) { + m_lock.lock(); + } else if (abstime != THR_RELTIME && (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)) + [[unlikely]] { + ret = POSIX_EINVAL; + } else { + if (THR_RELTIME) { + ret = m_lock.try_lock_for(std::chrono::microseconds(usec)) ? 0 : POSIX_ETIMEDOUT; + } else { + ret = m_lock.try_lock_until(abstime->TimePoint()) ? 0 : POSIX_ETIMEDOUT; + } + } + if (ret == 0) { + m_owner = curthread; + } + return ret; +} + +int PthreadMutex::TryLock() { + Pthread* curthread = g_curthread; + if (m_owner == curthread) { + return SelfTryLock(); + } + const int ret = m_lock.try_lock() ? 0 : POSIX_EBUSY; + if (ret == 0) { + m_owner = curthread; + } + return ret; +} + +int PS4_SYSV_ABI posix_pthread_mutex_trylock(PthreadMutexT* mutex) { + CHECK_AND_INIT_MUTEX + return (*mutex)->TryLock(); +} + +int PS4_SYSV_ABI posix_pthread_mutex_lock(PthreadMutexT* mutex) { + CHECK_AND_INIT_MUTEX + return (*mutex)->Lock(nullptr); +} + +int PS4_SYSV_ABI posix_pthread_mutex_timedlock(PthreadMutexT* mutex, + const OrbisKernelTimespec* abstime) { + CHECK_AND_INIT_MUTEX + UNREACHABLE(); + return (*mutex)->Lock(abstime); +} + +int PS4_SYSV_ABI posix_pthread_mutex_reltimedlock_np(PthreadMutexT* mutex, u64 usec) { + CHECK_AND_INIT_MUTEX + return (*mutex)->Lock(THR_RELTIME, usec); +} + +int PthreadMutex::Unlock() { + Pthread* curthread = g_curthread; + /* + * Check if the running thread is not the owner of the mutex. + */ + if (m_owner != curthread) [[unlikely]] { + return POSIX_EPERM; + } + + if (Type() == PthreadMutexType::Recursive && m_count > 0) [[unlikely]] { + m_count--; + } else { + int defered = True(m_flags & PthreadMutexFlags::Defered); + m_flags &= ~PthreadMutexFlags::Defered; + + m_owner = nullptr; + m_lock.unlock(); + + if (curthread->will_sleep == 0 && defered) { + curthread->WakeAll(); + } + } + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutex_unlock(PthreadMutexT* mutex) { + PthreadMutex* mp = *mutex; + if (mp <= THR_MUTEX_DESTROYED) [[unlikely]] { + if (mp == THR_MUTEX_DESTROYED) { + return POSIX_EINVAL; + } + return POSIX_EPERM; + } + return mp->Unlock(); +} + +int PS4_SYSV_ABI posix_pthread_mutex_getspinloops_np(PthreadMutexT* mutex, int* count) { + CHECK_AND_INIT_MUTEX + *count = (*mutex)->m_spinloops; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutex_setspinloops_np(PthreadMutexT* mutex, int count) { + CHECK_AND_INIT_MUTEX(*mutex)->m_spinloops = count; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutex_getyieldloops_np(PthreadMutexT* mutex, int* count) { + CHECK_AND_INIT_MUTEX + *count = (*mutex)->m_yieldloops; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutex_setyieldloops_np(PthreadMutexT* mutex, int count) { + CHECK_AND_INIT_MUTEX(*mutex)->m_yieldloops = count; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutex_isowned_np(PthreadMutexT* mutex) { + PthreadMutex* m = *mutex; + if (m <= THR_MUTEX_DESTROYED) { + return 0; + } + return m->m_owner == g_curthread; +} + +bool PthreadMutex::IsOwned(Pthread* curthread) const { + if (this <= THR_MUTEX_DESTROYED) [[unlikely]] { + if (this == THR_MUTEX_DESTROYED) { + return POSIX_EINVAL; + } + return POSIX_EPERM; + } + if (m_owner != curthread) { + return POSIX_EPERM; + } + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutexattr_init(PthreadMutexAttrT* attr) { + PthreadMutexAttrT pattr = new PthreadMutexAttr{}; + if (pattr == nullptr) { + return POSIX_ENOMEM; + } + memcpy(pattr, &PthreadMutexattrDefault, sizeof(PthreadMutexAttr)); + *attr = pattr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutexattr_setkind_np(PthreadMutexAttrT* attr, + PthreadMutexType kind) { + if (attr == nullptr || *attr == nullptr) { + *__Error() = POSIX_EINVAL; + return -1; + } + (*attr)->m_type = kind; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutexattr_getkind_np(PthreadMutexAttrT attr) { + if (attr == nullptr) { + *__Error() = POSIX_EINVAL; + return -1; + } + return static_cast(attr->m_type); +} + +int PS4_SYSV_ABI posix_pthread_mutexattr_settype(PthreadMutexAttrT* attr, PthreadMutexType type) { + if (attr == nullptr || *attr == nullptr || type >= PthreadMutexType::Max) { + return POSIX_EINVAL; + } + (*attr)->m_type = type; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutexattr_gettype(PthreadMutexAttrT* attr, PthreadMutexType* type) { + if (attr == nullptr || *attr == nullptr || (*attr)->m_type >= PthreadMutexType::Max) { + return POSIX_EINVAL; + } + *type = (*attr)->m_type; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutexattr_destroy(PthreadMutexAttrT* attr) { + if (attr == nullptr || *attr == nullptr) { + return POSIX_EINVAL; + } + delete *attr; + *attr = nullptr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutexattr_getprotocol(PthreadMutexAttrT* mattr, + PthreadMutexProt* protocol) { + if (mattr == nullptr || *mattr == nullptr) { + return POSIX_EINVAL; + } + *protocol = (*mattr)->m_protocol; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutexattr_setprotocol(PthreadMutexAttrT* mattr, + PthreadMutexProt protocol) { + if (mattr == nullptr || *mattr == nullptr || (protocol < PthreadMutexProt::None) || + (protocol > PthreadMutexProt::Protect)) { + return POSIX_EINVAL; + } + (*mattr)->m_protocol = protocol; + //(*mattr)->m_ceiling = THR_MAX_RR_PRIORITY; + return 0; +} + +void RegisterMutex(Core::Loader::SymbolsResolver* sym) { + // Posix + LIB_FUNCTION("ttHNfU+qDBU", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_init); + LIB_FUNCTION("7H0iTOciTLo", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_lock); + LIB_FUNCTION("2Z+PpY6CaJg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_unlock); + LIB_FUNCTION("ltCfaGr2JGE", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_destroy); + LIB_FUNCTION("dQHWEsJtoE4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutexattr_init); + LIB_FUNCTION("mDmgMOGVUqg", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_mutexattr_settype); + LIB_FUNCTION("5txKfcMUAok", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_mutexattr_setprotocol); + LIB_FUNCTION("HF7lK46xzjY", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_mutexattr_destroy); + LIB_FUNCTION("K-jXhbt2gn4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_trylock); + + // Posix-Kernel + LIB_FUNCTION("7H0iTOciTLo", "libkernel", 1, "libkernel", 1, 1, posix_pthread_mutex_lock); + LIB_FUNCTION("2Z+PpY6CaJg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_mutex_unlock); + + // Orbis + LIB_FUNCTION("cmo1RIYva9o", "libkernel", 1, "libkernel", 1, 1, ORBIS(scePthreadMutexInit)); + LIB_FUNCTION("2Of0f+3mhhE", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_mutex_destroy)); + LIB_FUNCTION("F8bUHwAG284", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_mutexattr_init)); + LIB_FUNCTION("smWEktiyyG0", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_mutexattr_destroy)); + LIB_FUNCTION("iMp8QpE+XO4", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_mutexattr_settype)); + LIB_FUNCTION("1FGvU0i9saQ", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_mutexattr_setprotocol)); + LIB_FUNCTION("9UK1vLZQft4", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_mutex_lock)); + LIB_FUNCTION("tn3VlD0hG60", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_mutex_unlock)); + LIB_FUNCTION("upoVrzMHFeE", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_mutex_trylock)); + LIB_FUNCTION("IafI2PxcPnQ", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_mutex_reltimedlock_np)); + LIB_FUNCTION("qH1gXoq71RY", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_mutex_init)); + LIB_FUNCTION("n2MMpvU8igI", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_mutexattr_init)); +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/pthread.cpp b/src/core/libraries/kernel/threads/pthread.cpp new file mode 100644 index 000000000..51d436d44 --- /dev/null +++ b/src/core/libraries/kernel/threads/pthread.cpp @@ -0,0 +1,526 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/assert.h" +#include "common/thread.h" +#include "core/debug_state.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/kernel/kernel.h" +#include "core/libraries/kernel/threads/pthread.h" +#include "core/libraries/kernel/threads/thread_state.h" +#include "core/libraries/libs.h" +#include "core/memory.h" + +namespace Libraries::Kernel { + +constexpr int PthreadInheritSched = 4; + +constexpr int ORBIS_KERNEL_PRIO_FIFO_DEFAULT = 700; +constexpr int ORBIS_KERNEL_PRIO_FIFO_HIGHEST = 256; +constexpr int ORBIS_KERNEL_PRIO_FIFO_LOWEST = 767; + +extern PthreadAttr PthreadAttrDefault; + +void _thread_cleanupspecific(); + +using ThreadDtor = void (*)(); +static ThreadDtor* ThreadDtors{}; + +void PS4_SYSV_ABI _sceKernelSetThreadDtors(ThreadDtor* dtor) { + ThreadDtors = dtor; +} + +static void ExitThread() { + Pthread* curthread = g_curthread; + + /* Check if there is thread specific data: */ + if (curthread->specific != nullptr) { + /* Run the thread-specific data destructors: */ + _thread_cleanupspecific(); + } + + auto* thread_state = ThrState::Instance(); + ASSERT(thread_state->active_threads.fetch_sub(1) != 1); + + curthread->lock.lock(); + curthread->state = PthreadState::Dead; + ASSERT(False(curthread->flags & ThreadFlags::NeedSuspend)); + + /* + * Thread was created with initial refcount 1, we drop the + * reference count to allow it to be garbage collected. + */ + curthread->refcount--; + thread_state->TryCollect(curthread); /* thread lock released */ + + /* + * Kernel will do wakeup at the address, so joiner thread + * will be resumed if it is sleeping at the address. + */ + curthread->tid.store(TidTerminated); + curthread->tid.notify_all(); + + curthread->native_thr.Exit(); + UNREACHABLE(); + /* Never reach! */ +} + +void PS4_SYSV_ABI posix_pthread_exit(void* status) { + Pthread* curthread = g_curthread; + + /* Check if this thread is already in the process of exiting: */ + ASSERT_MSG(!curthread->cancelling, "Thread {} has called pthread_exit from a destructor", + fmt::ptr(curthread)); + + /* Flag this thread as exiting. */ + curthread->cancelling = 1; + curthread->no_cancel = 1; + curthread->cancel_async = 0; + curthread->cancel_point = 0; + + /* Save the return value: */ + curthread->ret = status; + while (!curthread->cleanup.empty()) { + PthreadCleanup* old = curthread->cleanup.front(); + curthread->cleanup.pop_front(); + old->routine(old->routine_arg); + if (old->onheap) { + delete old; + } + } + /*if (ThreadDtors && *ThreadDtors) { + (*ThreadDtors)(); + }*/ + ExitThread(); +} + +static int JoinThread(PthreadT pthread, void** thread_return, const OrbisKernelTimespec* abstime) { + Pthread* curthread = g_curthread; + + if (pthread == nullptr) { + return POSIX_EINVAL; + } + + if (pthread == curthread) { + return POSIX_EDEADLK; + } + + auto* thread_state = ThrState::Instance(); + if (int ret = thread_state->FindThread(pthread, 1); ret != 0) { + return POSIX_ESRCH; + } + + int ret = 0; + if (True(pthread->flags & ThreadFlags::Detached)) { + ret = POSIX_EINVAL; + } else if (pthread->joiner != nullptr) { + /* Multiple joiners are not supported. */ + ret = POSIX_ENOTSUP; + } + if (ret) { + pthread->lock.unlock(); + return ret; + } + /* Set the running thread to be the joiner: */ + pthread->joiner = curthread; + pthread->lock.unlock(); + + const auto backout_join = [](void* arg) PS4_SYSV_ABI { + Pthread* pthread = (Pthread*)arg; + std::scoped_lock lk{pthread->lock}; + pthread->joiner = nullptr; + }; + + PthreadCleanup cup{backout_join, pthread, 0}; + curthread->cleanup.push_front(&cup); + + //_thr_cancel_enter(curthread); + + const int tid = pthread->tid; + while (pthread->tid.load() != TidTerminated) { + //_thr_testcancel(curthread); + ASSERT(abstime == nullptr); + pthread->tid.wait(tid); + } + + //_thr_cancel_leave(curthread, 0); + curthread->cleanup.pop_front(); + + if (ret == POSIX_ETIMEDOUT) { + backout_join(pthread); + return ret; + } + + void* tmp = pthread->ret; + pthread->lock.lock(); + pthread->flags |= ThreadFlags::Detached; + pthread->joiner = nullptr; + thread_state->TryCollect(pthread); /* thread lock released */ + if (thread_return != nullptr) { + *thread_return = tmp; + } + + return 0; +} + +int PS4_SYSV_ABI posix_pthread_join(PthreadT pthread, void** thread_return) { + return JoinThread(pthread, thread_return, NULL); +} + +int PS4_SYSV_ABI posix_pthread_timedjoin_np(PthreadT pthread, void** thread_return, + const OrbisKernelTimespec* abstime) { + if (abstime == nullptr || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || + abstime->tv_nsec >= 1000000000) { + return POSIX_EINVAL; + } + + return JoinThread(pthread, thread_return, abstime); +} + +int PS4_SYSV_ABI posix_pthread_detach(PthreadT pthread) { + if (pthread == nullptr) { + return POSIX_EINVAL; + } + + auto* thread_state = ThrState::Instance(); + if (int ret = thread_state->FindThread(pthread, 1); ret != 0) { + return ret; + } + + /* Check if the thread is already detached or has a joiner. */ + if (True(pthread->flags & ThreadFlags::Detached) || (pthread->joiner != NULL)) { + pthread->lock.unlock(); + return POSIX_EINVAL; + } + + /* Flag the thread as detached. */ + pthread->flags |= ThreadFlags::Detached; + thread_state->TryCollect(pthread); /* thread lock released */ + return 0; +} + +static void RunThread(void* arg) { + Pthread* curthread = (Pthread*)arg; + g_curthread = curthread; + Common::SetCurrentThreadName(curthread->name.c_str()); + DebugState.AddCurrentThreadToGuestList(); + + /* Run the current thread's start routine with argument: */ + void* ret = Core::ExecuteGuest(curthread->start_routine, curthread->arg); + + /* Remove thread from tracking */ + DebugState.RemoveCurrentThreadFromGuestList(); + posix_pthread_exit(ret); +} + +int PS4_SYSV_ABI posix_pthread_create_name_np(PthreadT* thread, const PthreadAttrT* attr, + PthreadEntryFunc start_routine, void* arg, + const char* name) { + Pthread* curthread = g_curthread; + auto* thread_state = ThrState::Instance(); + Pthread* new_thread = thread_state->Alloc(curthread); + if (new_thread == nullptr) { + return POSIX_EAGAIN; + } + + if (attr == nullptr || *attr == nullptr) { + new_thread->attr = PthreadAttrDefault; + } else { + new_thread->attr = *(*attr); + new_thread->attr.cpusetsize = 0; + } + if (new_thread->attr.sched_inherit == PthreadInheritSched) { + if (True(curthread->attr.flags & PthreadAttrFlags::ScopeSystem)) { + new_thread->attr.flags |= PthreadAttrFlags::ScopeSystem; + } else { + new_thread->attr.flags &= ~PthreadAttrFlags::ScopeSystem; + } + new_thread->attr.prio = curthread->attr.prio; + new_thread->attr.sched_policy = curthread->attr.sched_policy; + } + + static int TidCounter = 1; + new_thread->tid = ++TidCounter; + + if (thread_state->CreateStack(&new_thread->attr) != 0) { + /* Insufficient memory to create a stack: */ + thread_state->Free(curthread, new_thread); + return POSIX_EAGAIN; + } + + /* + * Write a magic value to the thread structure + * to help identify valid ones: + */ + new_thread->magic = Pthread::ThrMagic; + new_thread->start_routine = start_routine; + new_thread->arg = arg; + new_thread->cancel_enable = 1; + new_thread->cancel_async = 0; + + auto* memory = Core::Memory::Instance(); + if (name && memory->IsValidAddress(name)) { + new_thread->name = name; + } else { + new_thread->name = fmt::format("Thread{}", new_thread->tid.load()); + } + + ASSERT(new_thread->attr.suspend == 0); + new_thread->state = PthreadState::Running; + + if (True(new_thread->attr.flags & PthreadAttrFlags::Detached)) { + new_thread->flags |= ThreadFlags::Detached; + } + + /* Add the new thread. */ + new_thread->refcount = 1; + thread_state->Link(curthread, new_thread); + + /* Return thread pointer eariler so that new thread can use it. */ + (*thread) = new_thread; + + /* Create thread */ + new_thread->native_thr = Core::Thread(); + int ret = new_thread->native_thr.Create(RunThread, new_thread); + ASSERT_MSG(ret == 0, "Failed to create thread with error {}", ret); + if (ret) { + *thread = nullptr; + } + return ret; +} + +int PS4_SYSV_ABI posix_pthread_create(PthreadT* thread, const PthreadAttrT* attr, + PthreadEntryFunc start_routine, void* arg) { + return posix_pthread_create_name_np(thread, attr, start_routine, arg, nullptr); +} + +int PS4_SYSV_ABI posix_pthread_getthreadid_np() { + return g_curthread->tid; +} + +int PS4_SYSV_ABI posix_pthread_getname_np(PthreadT thread, char* name) { + std::memcpy(name, thread->name.data(), std::min(thread->name.size(), 32)); + return 0; +} + +int PS4_SYSV_ABI posix_pthread_equal(PthreadT thread1, PthreadT thread2) { + return (thread1 == thread2 ? 1 : 0); +} + +PthreadT PS4_SYSV_ABI posix_pthread_self() { + return g_curthread; +} + +void PS4_SYSV_ABI posix_pthread_yield() { + std::this_thread::yield(); +} + +void PS4_SYSV_ABI sched_yield() { + std::this_thread::yield(); +} + +int PS4_SYSV_ABI posix_pthread_once(PthreadOnce* once_control, void (*init_routine)()) { + for (;;) { + auto state = once_control->state.load(); + if (state == PthreadOnceState::Done) { + return 0; + } + if (state == PthreadOnceState::NeverDone) { + if (once_control->state.compare_exchange_strong(state, PthreadOnceState::InProgress, + std::memory_order_acquire)) { + break; + } + } else if (state == PthreadOnceState::InProgress) { + if (once_control->state.compare_exchange_strong(state, PthreadOnceState::Wait, + std::memory_order_acquire)) { + once_control->state.wait(PthreadOnceState::Wait); + } + } else if (state == PthreadOnceState::Wait) { + once_control->state.wait(state); + } else { + return POSIX_EINVAL; + } + } + + const auto once_cancel_handler = [](void* arg) PS4_SYSV_ABI { + PthreadOnce* once_control = (PthreadOnce*)arg; + auto state = PthreadOnceState::InProgress; + if (once_control->state.compare_exchange_strong(state, PthreadOnceState::NeverDone, + std::memory_order_release)) { + return; + } + + once_control->state.store(PthreadOnceState::NeverDone, std::memory_order_release); + once_control->state.notify_all(); + }; + + PthreadCleanup cup{once_cancel_handler, once_control, 0}; + g_curthread->cleanup.push_front(&cup); + init_routine(); + g_curthread->cleanup.pop_front(); + + auto state = PthreadOnceState::InProgress; + if (once_control->state.compare_exchange_strong(state, PthreadOnceState::Done, + std::memory_order_release)) { + return 0; + } + once_control->state.store(PthreadOnceState::Done); + once_control->state.notify_all(); + return 0; +} + +int PS4_SYSV_ABI posix_sched_get_priority_max() { + return ORBIS_KERNEL_PRIO_FIFO_HIGHEST; +} + +int PS4_SYSV_ABI posix_sched_get_priority_min() { + return ORBIS_KERNEL_PRIO_FIFO_LOWEST; +} + +int PS4_SYSV_ABI posix_pthread_rename_np(PthreadT thread, const char* name) { + LOG_INFO(Kernel_Pthread, "name = {}", name); + thread->name = name; + return SCE_OK; +} + +int PS4_SYSV_ABI posix_pthread_getschedparam(PthreadT pthread, SchedPolicy* policy, + SchedParam* param) { + if (policy == nullptr || param == nullptr) { + return POSIX_EINVAL; + } + + if (pthread == g_curthread) { + /* + * Avoid searching the thread list when it is the current + * thread. + */ + std::scoped_lock lk{g_curthread->lock}; + *policy = g_curthread->attr.sched_policy; + param->sched_priority = g_curthread->attr.prio; + return 0; + } + auto* thread_state = ThrState::Instance(); + /* Find the thread in the list of active threads. */ + if (int ret = thread_state->RefAdd(pthread, /*include dead*/ 0); ret != 0) { + return ret; + } + pthread->lock.lock(); + *policy = pthread->attr.sched_policy; + param->sched_priority = pthread->attr.prio; + pthread->lock.unlock(); + thread_state->RefDelete(pthread); + return 0; +} + +int PS4_SYSV_ABI scePthreadGetprio(PthreadT thread, int* priority) { + SchedParam param; + SchedPolicy policy; + + posix_pthread_getschedparam(thread, &policy, ¶m); + *priority = param.sched_priority; + return 0; +} + +int PS4_SYSV_ABI scePthreadSetprio(PthreadT thread, int prio) { + SchedParam param; + int ret; + + param.sched_priority = prio; + + auto* thread_state = ThrState::Instance(); + if (thread == g_curthread) { + g_curthread->lock.lock(); + } else if (int ret = thread_state->FindThread(thread, /*include dead*/0)) { + return ret; + } + + if (thread->attr.sched_policy == SchedPolicy::Other || + thread->attr.prio == prio) { + thread->attr.prio = prio; + ret = 0; + } else { + // TODO: _thr_setscheduler + thread->attr.prio = prio; + } + + thread->lock.unlock(); + return ret; +} + +enum class PthreadCancelState : u32 { + Enable = 0, + Disable = 1, +}; + +#define POSIX_PTHREAD_CANCELED ((void*)1) + +static inline void TestCancel(Pthread* curthread) { + if (curthread->ShouldCancel() && !curthread->InCritical()) [[unlikely]] { + posix_pthread_exit(POSIX_PTHREAD_CANCELED); + } +} + +int PS4_SYSV_ABI posix_pthread_setcancelstate(PthreadCancelState state, + PthreadCancelState* oldstate) { + Pthread* curthread = g_curthread; + int oldval = curthread->cancel_enable; + switch (state) { + case PthreadCancelState::Disable: + curthread->cancel_enable = 0; + break; + case PthreadCancelState::Enable: + curthread->cancel_enable = 1; + TestCancel(curthread); + break; + default: + return POSIX_EINVAL; + } + + if (oldstate) { + *oldstate = oldval ? PthreadCancelState::Enable : PthreadCancelState::Disable; + } + return 0; +} + +void RegisterThread(Core::Loader::SymbolsResolver* sym) { + // Posix + LIB_FUNCTION("Z4QosVuAsA0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_once); + LIB_FUNCTION("7Xl257M4VNI", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_equal); + LIB_FUNCTION("CBNtXOoef-E", "libScePosix", 1, "libkernel", 1, 1, posix_sched_get_priority_max); + LIB_FUNCTION("m0iS6jNsXds", "libScePosix", 1, "libkernel", 1, 1, posix_sched_get_priority_min); + LIB_FUNCTION("EotR8a3ASf4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_self); + LIB_FUNCTION("B5GmVDKwpn0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_yield); + LIB_FUNCTION("+U1R4WtXvoc", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_detach); + LIB_FUNCTION("FJrT5LuUBAU", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_exit); + LIB_FUNCTION("h9CcP3J0oVM", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_join); + LIB_FUNCTION("OxhIB8LB-PQ", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create); + LIB_FUNCTION("Jmi+9w9u0E4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create_name_np); + LIB_FUNCTION("lZzFeSxPl08", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setcancelstate); + LIB_FUNCTION("6XG4B33N09g", "libScePosix", 1, "libkernel", 1, 1, sched_yield); + + // Posix-Kernel + LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self); + LIB_FUNCTION("OxhIB8LB-PQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_create); + + // Orbis + LIB_FUNCTION("14bOACANTBo", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_once)); + LIB_FUNCTION("GBUY7ywdULE", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_rename_np)); + LIB_FUNCTION("6UgtwV+0zb4", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_create_name_np)); + LIB_FUNCTION("4qGrR6eoP9Y", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_detach)); + LIB_FUNCTION("onNY9Byn-W8", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_join)); + LIB_FUNCTION("P41kTWUS3EI", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_getschedparam)); + LIB_FUNCTION("How7B8Oet6k", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_getname_np)); + LIB_FUNCTION("3kg7rT0NQIs", "libkernel", 1, "libkernel", 1, 1, posix_pthread_exit); + LIB_FUNCTION("aI+OeCz8xrQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self); + LIB_FUNCTION("3PtV6p3QNX4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_equal); + LIB_FUNCTION("T72hz6ffq08", "libkernel", 1, "libkernel", 1, 1, posix_pthread_yield); + LIB_FUNCTION("EI-5-jlq2dE", "libkernel", 1, "libkernel", 1, 1, posix_pthread_getthreadid_np); + LIB_FUNCTION("1tKyG7RlMJo", "libkernel", 1, "libkernel", 1, 1, scePthreadGetprio); + LIB_FUNCTION("W0Hpm2X0uPE", "libkernel", 1, "libkernel", 1, 1, scePthreadSetprio); + LIB_FUNCTION("rNhWz+lvOMU", "libkernel", 1, "libkernel", 1, 1, _sceKernelSetThreadDtors); + LIB_FUNCTION("6XG4B33N09g", "libkernel", 1, "libkernel", 1, 1, sched_yield); +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/pthread.h b/src/core/libraries/kernel/threads/pthread.h new file mode 100644 index 000000000..0a3ab57dd --- /dev/null +++ b/src/core/libraries/kernel/threads/pthread.h @@ -0,0 +1,349 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "common/enum.h" +#include "core/libraries/kernel/time.h" +#include "core/thread.h" +#include "core/tls.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::Kernel { + +struct Pthread; + +enum class PthreadMutexFlags : u32 { + TypeMask = 0xff, + Defered = 0x200, +}; +DECLARE_ENUM_FLAG_OPERATORS(PthreadMutexFlags) + +enum class PthreadMutexType : u32 { + ErrorCheck = 1, + Recursive = 2, + Normal = 3, + AdaptiveNp = 4, + Max +}; + +enum class PthreadMutexProt : u32 { + None = 0, + Inherit = 1, + Protect = 2, +}; + +struct PthreadMutex { + std::timed_mutex m_lock; + PthreadMutexFlags m_flags; + Pthread* m_owner; + int m_count; + int m_spinloops; + int m_yieldloops; + PthreadMutexProt m_protocol; + std::string name; + + PthreadMutexType Type() const noexcept { + return static_cast(m_flags & PthreadMutexFlags::TypeMask); + } + + int SelfTryLock(); + int SelfLock(const OrbisKernelTimespec* abstime, u64 usec); + + int TryLock(); + int Lock(const OrbisKernelTimespec* abstime, u64 usec = 0); + + int CvLock(int recurse) { + const int error = Lock(nullptr); + if (error == 0) { + m_count = recurse; + } + return error; + } + + int Unlock(); + + int CvUnlock(int* recurse) { + *recurse = m_count; + m_count = 0; + return Unlock(); + } + + bool IsOwned(Pthread* curthread) const; +}; +using PthreadMutexT = PthreadMutex*; + +struct PthreadMutexAttr { + PthreadMutexType m_type; + PthreadMutexProt m_protocol; + int m_ceiling; +}; +using PthreadMutexAttrT = PthreadMutexAttr*; + +enum class PthreadCondFlags : u32 { + Private = 1, + Inited = 2, + Busy = 4, +}; + +enum class ClockId : u32 { + Realtime = 0, + Virtual = 1, + Prof = 2, + Monotonic = 4, + Uptime = 5, + UptimePrecise = 7, + UptimeFast = 8, + RealtimePrecise = 9, + RealtimeFast = 10, + MonotonicPrecise = 11, + MonotonicFast = 12, + Second = 13, + ThreadCputimeID = 14, +}; + +struct PthreadCond { + u32 has_user_waiters; + u32 has_kern_waiters; + u32 flags; + ClockId clock_id; + std::string name; + + int Wait(PthreadMutexT* mutex, const OrbisKernelTimespec* abstime, u64 usec = 0); + + int Signal(); + int Broadcast(); +}; +using PthreadCondT = PthreadCond*; + +struct PthreadCondAttr { + int c_pshared; + ClockId c_clockid; +}; +using PthreadCondAttrT = PthreadCondAttr*; + +using PthreadCleanupFunc = void PS4_SYSV_ABI (*)(void*); + +struct PthreadCleanup { + PthreadCleanupFunc routine; + void* routine_arg; + int onheap; +}; + +enum class PthreadAttrFlags : u32 { + Detached = 1, + ScopeSystem = 2, + InheritSched = 4, + NoFloat = 8, + StackUser = 0x100, +}; +DECLARE_ENUM_FLAG_OPERATORS(PthreadAttrFlags) + +enum class SchedPolicy : u32 { + Fifo = 0, + Other = 2, + RoundRobin = 3, +}; + +struct Cpuset { + u64 bits; +}; + +struct PthreadAttr { + SchedPolicy sched_policy; + int sched_inherit; + int prio; + int suspend; + PthreadAttrFlags flags; + void* stackaddr_attr; + size_t stacksize_attr; + size_t guardsize_attr; + size_t cpusetsize; + Cpuset* cpuset; +}; +using PthreadAttrT = PthreadAttr*; + +static constexpr u32 ThrStackDefault = 1_MB; +static constexpr u32 ThrStackInitial = 2_MB; +static constexpr u32 ThrPageSize = 16_KB; +static constexpr u32 ThrGuardDefault = ThrPageSize; + +struct PthreadRwlockAttr { + int pshared; +}; +using PthreadRwlockAttrT = PthreadRwlockAttr*; + +struct PthreadRwlock { + std::shared_timed_mutex lock; + Pthread* owner; + + int Wrlock(const OrbisKernelTimespec* abstime); + int Rdlock(const OrbisKernelTimespec* abstime); +}; +using PthreadRwlockT = PthreadRwlock*; + +enum class PthreadState : u32 { Running, Dead }; + +struct PthreadSpecificElem { + const void* data; + int seqno; +}; + +using PthreadKeyDestructor = void PS4_SYSV_ABI (*)(const void*); + +struct PthreadKey { + int allocated; + int seqno; + PthreadKeyDestructor destructor; +}; +using PthreadKeyT = s32; + +enum class PthreadOnceState : u32 { + NeverDone = 0, + Done = 1, + InProgress = 2, + Wait = 3, +}; + +struct PthreadOnce { + std::atomic state; + std::mutex mutex; +}; + +enum class ThreadFlags : u32 { + Private = 1, + NeedSuspend = 2, + Suspended = 4, + Detached = 8, +}; +DECLARE_ENUM_FLAG_OPERATORS(ThreadFlags) + +enum class ThreadListFlags : u32 { + GcSafe = 1, + InTdList = 2, + InGcList = 4, +}; + +using PthreadEntryFunc = void* PS4_SYSV_ABI (*)(void*); + +constexpr u32 TidTerminated = 1; + +struct SleepQueue; + +struct SchedParam { + int sched_priority; +}; + +#define THR_RELTIME (const OrbisKernelTimespec*)-1 + +struct Pthread { + static constexpr u32 ThrMagic = 0xd09ba115U; + static constexpr u32 MaxDeferWaiters = 50; + + std::atomic tid; + std::mutex lock; + u32 cycle; + int locklevel; + int critical_count; + int sigblock; + int refcount; + PthreadEntryFunc start_routine; + void* arg; + Core::Thread native_thr; + PthreadAttr attr; + bool cancel_enable; + bool cancel_pending; + bool cancel_point; + bool no_cancel; + bool cancel_async; + bool cancelling; + Cpuset sigmask; + bool unblock_sigcancel; + bool in_sigsuspend; + bool force_exit; + PthreadState state; + int error; + Pthread* joiner; + ThreadFlags flags; + ThreadListFlags tlflags; + void* ret; + PthreadSpecificElem* specific; + int specific_data_count; + int rdlock_count; + int rtld_bits; + Core::Tcb* tcb; + std::forward_list cleanup; + u32 pad[27]; + u32 magic; + int report_events; + int event_mask; + std::string name; + std::binary_semaphore wake_sema{0}; + SleepQueue* sleepqueue; + void* wchan; + PthreadMutex* mutex_obj; + bool will_sleep; + bool has_user_waiters; + int nwaiter_defer; + std::binary_semaphore* defer_waiters[MaxDeferWaiters]; + + bool InCritical() const noexcept { + return locklevel > 0 || critical_count > 0; + } + + bool ShouldCollect() const noexcept { + return refcount == 0 && state == PthreadState::Dead && True(flags & ThreadFlags::Detached); + } + + bool ShouldCancel() const noexcept { + return cancel_pending && cancel_enable && no_cancel == 0; + } + + void WakeAll() { + for (int i = 0; i < nwaiter_defer; i++) { + defer_waiters[i]->release(); + } + nwaiter_defer = 0; + } + + bool Sleep(const OrbisKernelTimespec* abstime, u64 usec) { + will_sleep = 0; + if (nwaiter_defer > 0) { + WakeAll(); + } + if (abstime == THR_RELTIME) { + return wake_sema.try_acquire_for(std::chrono::microseconds(usec)); + } else if (abstime != nullptr) { + return wake_sema.try_acquire_until(abstime->TimePoint()); + } else { + wake_sema.acquire(); + return true; + } + } +}; +using PthreadT = Pthread*; + +extern thread_local Pthread* g_curthread; + +void RegisterMutex(Core::Loader::SymbolsResolver* sym); +void RegisterCond(Core::Loader::SymbolsResolver* sym); +void RegisterRwlock(Core::Loader::SymbolsResolver* sym); +void RegisterSemaphore(Core::Loader::SymbolsResolver* sym); +void RegisterSpec(Core::Loader::SymbolsResolver* sym); +void RegisterThreadAttr(Core::Loader::SymbolsResolver* sym); +void RegisterThread(Core::Loader::SymbolsResolver* sym); +void RegisterRtld(Core::Loader::SymbolsResolver* sym); +void RegisterKernelEventFlag(Core::Loader::SymbolsResolver* sym); +void RegisterPthreadClean(Core::Loader::SymbolsResolver* sym); + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/pthread_attr.cpp b/src/core/libraries/kernel/threads/pthread_attr.cpp new file mode 100644 index 000000000..db32fa3d5 --- /dev/null +++ b/src/core/libraries/kernel/threads/pthread_attr.cpp @@ -0,0 +1,345 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/libraries/error_codes.h" +#include "core/libraries/kernel/kernel.h" +#include "core/libraries/kernel/threads/pthread.h" +#include "core/libraries/kernel/threads/thread_state.h" +#include "core/libraries/libs.h" + +namespace Libraries::Kernel { + +static constexpr u32 PthreadStackMin = 16_KB; + +struct PthreadPrio { + s32 pri_min; + s32 pri_max; + s32 pri_default; +}; + +static constexpr std::array ThrPriorities = {{ + {0x100, 0x2FF, 0x2BC}, // Fifo + {0x300, 0x3BF, 0x384}, // Other + {0x100, 0x2FF, 0x2BC}, // Round-Robin +}}; + +PthreadAttr PthreadAttrDefault = { + .sched_policy = SchedPolicy::Fifo, + .sched_inherit = 0, + .prio = 0, + .suspend = false, + .flags = PthreadAttrFlags::ScopeSystem, + .stackaddr_attr = NULL, + .stacksize_attr = ThrStackDefault, + .guardsize_attr = 0, + .cpusetsize = 0, + .cpuset = nullptr, +}; + +int PS4_SYSV_ABI posix_pthread_attr_destroy(PthreadAttrT* attr) { + if (attr == nullptr || *attr == nullptr) { + return POSIX_EINVAL; + } + delete *attr; + *attr = nullptr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_getdetachstate(const PthreadAttrT* attr, int* detachstate) { + if (attr == nullptr || *attr == nullptr || detachstate == nullptr) { + return POSIX_EINVAL; + } + *detachstate = True((*attr)->flags & PthreadAttrFlags::Detached); + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_getguardsize(const PthreadAttrT* attr, size_t* guardsize) { + if (attr == nullptr || *attr == nullptr || guardsize == nullptr) { + return POSIX_EINVAL; + } + *guardsize = (*attr)->guardsize_attr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_getinheritsched(const PthreadAttrT* attr, int* sched_inherit) { + if (attr == nullptr || *attr == nullptr) { + return POSIX_EINVAL; + } + *sched_inherit = (*attr)->sched_inherit; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_getschedparam(const PthreadAttrT* attr, SchedParam* param) { + if (attr == nullptr || *attr == nullptr || param == nullptr) { + return POSIX_EINVAL; + } + param->sched_priority = (*attr)->prio; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_getschedpolicy(const PthreadAttrT* attr, SchedPolicy* policy) { + if (attr == nullptr || *attr == nullptr || policy == nullptr) { + return POSIX_EINVAL; + } + *policy = (*attr)->sched_policy; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_getstack(const PthreadAttrT* attr, void** stackaddr, + size_t* stacksize) { + if (attr == nullptr || *attr == nullptr || stackaddr == nullptr || stacksize == nullptr) { + return POSIX_EINVAL; + } + *stackaddr = (*attr)->stackaddr_attr; + *stacksize = (*attr)->stacksize_attr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_getstackaddr(const PthreadAttrT* attr, void** stackaddr) { + if (attr == nullptr || *attr == nullptr || stackaddr == nullptr) { + return POSIX_EINVAL; + } + *stackaddr = (*attr)->stackaddr_attr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_getstacksize(const PthreadAttrT* attr, size_t* stacksize) { + if (attr == nullptr || *attr == nullptr || stacksize == nullptr) { + return POSIX_EINVAL; + } + *stacksize = (*attr)->stacksize_attr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_init(PthreadAttrT* attr) { + PthreadAttrT pattr = new PthreadAttr{}; + if (pattr == nullptr) { + return POSIX_ENOMEM; + } + memcpy(pattr, &PthreadAttrDefault, sizeof(PthreadAttr)); + *attr = pattr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_setschedpolicy(PthreadAttrT* attr, SchedPolicy policy) { + if (attr == NULL || *attr == NULL) { + return POSIX_EINVAL; + } else if ((policy < SchedPolicy::Fifo) || (policy > SchedPolicy::RoundRobin)) { + return POSIX_ENOTSUP; + } + (*attr)->sched_policy = policy; + (*attr)->prio = ThrPriorities[u32(policy) - 1].pri_default; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_setstack(PthreadAttrT* attr, void* stackaddr, + size_t stacksize) { + if (attr == nullptr || *attr == nullptr || stackaddr == nullptr || + stacksize < PthreadStackMin) { + return POSIX_EINVAL; + } + (*attr)->stackaddr_attr = stackaddr; + (*attr)->stacksize_attr = stacksize; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_setstackaddr(PthreadAttrT* attr, void* stackaddr) { + if (attr == nullptr || *attr == nullptr || stackaddr == nullptr) { + return POSIX_EINVAL; + } + (*attr)->stackaddr_attr = stackaddr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_setstacksize(PthreadAttrT* attr, size_t stacksize) { + if (attr == nullptr || *attr == nullptr || stacksize < PthreadStackMin) { + return POSIX_EINVAL; + } + (*attr)->stacksize_attr = stacksize; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_setdetachstate(PthreadAttrT* attr, int detachstate) { + if (attr == nullptr || *attr == nullptr || (detachstate != 1 && detachstate != 0)) { + return POSIX_EINVAL; + } + if (detachstate) { + (*attr)->flags |= PthreadAttrFlags::Detached; + } else { + (*attr)->flags &= ~PthreadAttrFlags::Detached; + } + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_setschedparam(PthreadAttrT* attr, SchedParam* param) { + if (attr == nullptr || *attr == nullptr) { + return POSIX_EINVAL; + } + if (param == nullptr) { + return POSIX_ENOTSUP; + } + + const auto policy = (*attr)->sched_policy; + if (policy == SchedPolicy::RoundRobin) { + if (param->sched_priority < ThrPriorities[u32(policy) - 1].pri_min || + param->sched_priority > ThrPriorities[u32(policy) - 1].pri_max) { + return POSIX_ENOTSUP; + } + } + (*attr)->prio = param->sched_priority; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_setinheritsched(PthreadAttrT* attr, int sched_inherit) { + if (attr == nullptr || *attr == nullptr) { + return POSIX_EINVAL; + } + if (sched_inherit != 4 && sched_inherit != 0) { + return POSIX_ENOTSUP; + } + + (*attr)->sched_inherit = sched_inherit; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_setguardsize(PthreadAttrT* attr, size_t guardsize) { + if (attr == nullptr || *attr == nullptr) { + return POSIX_EINVAL; + } + (*attr)->guardsize_attr = guardsize; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_get_np(PthreadT pthread, PthreadAttrT* dstattr) { + PthreadAttr* dst; + if (pthread == nullptr || dstattr == nullptr || (dst = *dstattr) == nullptr) { + return POSIX_EINVAL; + } + auto* thread_state = ThrState::Instance(); + int ret = thread_state->FindThread(pthread, /*include dead*/ 0); + if (ret != 0) { + return ret; + } + PthreadAttr attr = pthread->attr; + if (True(pthread->flags & ThreadFlags::Detached)) { + attr.flags |= PthreadAttrFlags::Detached; + } + pthread->lock.unlock(); + if (ret == 0) { + memcpy(dst, &attr, sizeof(PthreadAttr)); + } + return ret; +} + +int PS4_SYSV_ABI posix_pthread_attr_getaffinity_np(const PthreadAttrT* pattr, size_t cpusetsize, + Cpuset* cpusetp) { + if (pattr == nullptr) { + return POSIX_EINVAL; + } + PthreadAttrT attr = *pattr; + if (attr == nullptr) { + return POSIX_EINVAL; + } + if (attr->cpuset != nullptr) + memcpy(cpusetp, attr->cpuset, std::min(cpusetsize, attr->cpusetsize)); + else + memset(cpusetp, -1, sizeof(Cpuset)); + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_setaffinity_np(PthreadAttrT* pattr, size_t cpusetsize, + const Cpuset* cpusetp) { + if (pattr == nullptr) { + return POSIX_EINVAL; + } + PthreadAttrT attr = *pattr; + if (attr == nullptr) { + return POSIX_EINVAL; + } + if (cpusetsize == 0 || cpusetp == nullptr) { + if (attr->cpuset != nullptr) { + free(attr->cpuset); + attr->cpuset = NULL; + attr->cpusetsize = 0; + } + return 0; + } + if (attr->cpuset == nullptr) { + attr->cpuset = (Cpuset*)calloc(1, sizeof(Cpuset)); + attr->cpusetsize = sizeof(Cpuset); + } + memcpy(attr->cpuset, cpusetp, sizeof(Cpuset)); + return 0; +} + +int PS4_SYSV_ABI scePthreadAttrGetaffinity(PthreadAttrT* param_1, Cpuset* mask) { + Cpuset cpuset; + const int ret = posix_pthread_attr_getaffinity_np(param_1, 0x10, &cpuset); + if (ret == 0) { + *mask = cpuset; + } + return ret; +} + +int PS4_SYSV_ABI scePthreadAttrSetaffinity(PthreadAttrT* attr, const Cpuset mask) { + return posix_pthread_attr_setaffinity_np(attr, 0x10, &mask); +} + +void RegisterThreadAttr(Core::Loader::SymbolsResolver* sym) { + // Posix + LIB_FUNCTION("wtkt-teR1so", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_attr_init); + LIB_FUNCTION("2Q0z6rnBrTE", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_attr_setstacksize); + LIB_FUNCTION("RtLRV-pBTTY", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_attr_getschedpolicy); + LIB_FUNCTION("E+tyo3lp5Lw", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_attr_setdetachstate); + LIB_FUNCTION("zHchY8ft5pk", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_attr_destroy); + LIB_FUNCTION("euKRgm0Vn2M", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_attr_setschedparam); + LIB_FUNCTION("7ZlAakEf0Qg", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_attr_setinheritsched); + LIB_FUNCTION("0qOtCR-ZHck", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_attr_getstacksize); + LIB_FUNCTION("VUT1ZSrHT0I", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_attr_getdetachstate); + + // Orbis + LIB_FUNCTION("4+h9EzwKF4I", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_setschedpolicy)); + LIB_FUNCTION("-Wreprtu0Qs", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_setdetachstate)); + LIB_FUNCTION("JaRMy+QcpeU", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_getdetachstate)); + LIB_FUNCTION("eXbUSpEaTsA", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_setinheritsched)); + LIB_FUNCTION("DzES9hQF4f4", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_setschedparam)); + LIB_FUNCTION("nsYoNRywwNg", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_attr_init)); + LIB_FUNCTION("62KCwEMmzcM", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_destroy)); + LIB_FUNCTION("-quPa4SEJUw", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_getstack)); + LIB_FUNCTION("Bvn74vj6oLo", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_setstack)); + LIB_FUNCTION("Ru36fiTtJzA", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_getstackaddr)); + LIB_FUNCTION("-fA+7ZlGDQs", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_getstacksize)); + LIB_FUNCTION("x1X76arYMxU", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_get_np)); + LIB_FUNCTION("FXPWHNk8Of0", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_getschedparam)); + LIB_FUNCTION("UTXzJbWhhTE", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_setstacksize)); + LIB_FUNCTION("F+yfmduIBB8", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_setstackaddr)); + LIB_FUNCTION("El+cQ20DynU", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_setguardsize)); + LIB_FUNCTION("8+s5BzZjxSg", "libkernel", 1, "libkernel", 1, 1, + ORBIS(scePthreadAttrGetaffinity)); + LIB_FUNCTION("3qxgM4ezETA", "libkernel", 1, "libkernel", 1, 1, + ORBIS(scePthreadAttrSetaffinity)); +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/pthread_clean.cpp b/src/core/libraries/kernel/threads/pthread_clean.cpp new file mode 100644 index 000000000..4ed15f7a3 --- /dev/null +++ b/src/core/libraries/kernel/threads/pthread_clean.cpp @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/libraries/kernel/threads/pthread.h" +#include "core/libraries/libs.h" + +namespace Libraries::Kernel { + +void PS4_SYSV_ABI __pthread_cleanup_push_imp(PthreadCleanupFunc routine, void* arg, + PthreadCleanup* newbuf) { + newbuf->routine = routine; + newbuf->routine_arg = arg; + newbuf->onheap = 0; + g_curthread->cleanup.push_front(newbuf); +} + +void PS4_SYSV_ABI posix_pthread_cleanup_push(PthreadCleanupFunc routine, void* arg) { + Pthread* curthread = g_curthread; + PthreadCleanup* newbuf = new PthreadCleanup{}; + if (newbuf == nullptr) { + return; + } + + newbuf->routine = routine; + newbuf->routine_arg = arg; + newbuf->onheap = 1; + curthread->cleanup.push_front(newbuf); +} + +void PS4_SYSV_ABI posix_pthread_cleanup_pop(int execute) { + Pthread* curthread = g_curthread; + if (!curthread->cleanup.empty()) { + PthreadCleanup* old = curthread->cleanup.front(); + curthread->cleanup.pop_front(); + if (execute) { + old->routine(old->routine_arg); + } + if (old->onheap) { + delete old; + } + } +} + +void RegisterPthreadClean(Core::Loader::SymbolsResolver* sym) { + // Posix + LIB_FUNCTION("4ZeZWcMsAV0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cleanup_push); + LIB_FUNCTION("RVxb0Ssa5t0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cleanup_pop); + + // Posix-Kernel + LIB_FUNCTION("1xvtUVx1-Sg", "libkernel", 1, "libkernel", 1, 1, __pthread_cleanup_push_imp); + LIB_FUNCTION("iWsFlYMf3Kw", "libkernel", 1, "libkernel", 1, 1, posix_pthread_cleanup_pop); +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/pthread_spec.cpp b/src/core/libraries/kernel/threads/pthread_spec.cpp new file mode 100644 index 000000000..3d6b0f4d9 --- /dev/null +++ b/src/core/libraries/kernel/threads/pthread_spec.cpp @@ -0,0 +1,158 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/assert.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/kernel/kernel.h" +#include "core/libraries/kernel/threads/pthread.h" +#include "core/libraries/libs.h" + +namespace Libraries::Kernel { + +static constexpr u32 PthreadKeysMax = 256; +static constexpr u32 PthreadDestructorIterations = 4; + +static std::array ThreadKeytable{}; +static std::mutex KeytableLock; + +int PS4_SYSV_ABI posix_pthread_key_create(PthreadKeyT* key, PthreadKeyDestructor destructor) { + std::scoped_lock lk{KeytableLock}; + const auto it = std::ranges::find(ThreadKeytable, 0, &PthreadKey::allocated); + if (it != ThreadKeytable.end()) { + it->allocated = 1; + it->destructor = destructor; + it->seqno++; + *key = std::distance(ThreadKeytable.begin(), it); + return 0; + } + return POSIX_EAGAIN; +} + +int PS4_SYSV_ABI posix_pthread_key_delete(PthreadKeyT key) { + if (key >= PthreadKeysMax) { + return POSIX_EINVAL; + } + + std::scoped_lock lk{KeytableLock}; + if (!ThreadKeytable[key].allocated) { + return POSIX_EINVAL; + } + + ThreadKeytable[key].allocated = 0; + return 0; +} + +void _thread_cleanupspecific() { + Pthread* curthread = g_curthread; + PthreadKeyDestructor destructor; + const void* data = NULL; + + if (curthread->specific == nullptr) { + return; + } + + std::unique_lock lk{KeytableLock}; + for (int i = 0; (i < PthreadDestructorIterations) && (curthread->specific_data_count > 0); + i++) { + for (int key = 0; (key < PthreadKeysMax) && (curthread->specific_data_count > 0); key++) { + destructor = nullptr; + + if (ThreadKeytable[key].allocated && (curthread->specific[key].data != nullptr)) { + if (curthread->specific[key].seqno == ThreadKeytable[key].seqno) { + data = curthread->specific[key].data; + destructor = ThreadKeytable[key].destructor; + } + curthread->specific[key].data = nullptr; + curthread->specific_data_count--; + } else if (curthread->specific[key].data != NULL) { + /* + * This can happen if the key is deleted via + * pthread_key_delete without first setting the value + * to NULL in all threads. POSIX says that the + * destructor is not invoked in this case. + */ + curthread->specific[key].data = nullptr; + curthread->specific_data_count--; + } + + /* + * If there is a destructor, call it + * with the key table entry unlocked: + */ + if (destructor != nullptr) { + /* + * Don't hold the lock while calling the + * destructor: + */ + lk.unlock(); + Core::ExecuteGuest(destructor, data); + lk.lock(); + } + } + } + delete[] curthread->specific; + curthread->specific = nullptr; + if (curthread->specific_data_count > 0) { + LOG_WARNING(Lib_Kernel, "Thread has exited with leftover thread-specific data"); + } +} + +int PS4_SYSV_ABI posix_pthread_setspecific(PthreadKeyT key, const void* value) { + int ret = 0; + Pthread* pthread = g_curthread; + + if (!pthread->specific) { + pthread->specific = new PthreadSpecificElem[PthreadKeysMax]{}; + if (!pthread->specific) { + return POSIX_ENOMEM; + } + } + if (key >= PthreadKeysMax) { + return POSIX_EINVAL; + } + if (!ThreadKeytable[key].allocated) { + return POSIX_EINVAL; + } + + if (pthread->specific[key].data == nullptr) { + if (value != nullptr) { + pthread->specific_data_count++; + } + } else if (value == nullptr) { + pthread->specific_data_count--; + } + pthread->specific[key].data = value; + pthread->specific[key].seqno = ThreadKeytable[key].seqno; + return 0; +} + +const void* PS4_SYSV_ABI posix_pthread_getspecific(PthreadKeyT key) { + Pthread* pthread = g_curthread; + + if (!pthread->specific || key >= PthreadKeysMax) { + return nullptr; + } + + if (ThreadKeytable[key].allocated && + (pthread->specific[key].seqno == ThreadKeytable[key].seqno)) { + return pthread->specific[key].data; + } + + return nullptr; +} + +void RegisterSpec(Core::Loader::SymbolsResolver* sym) { + // Posix + LIB_FUNCTION("mqULNdimTn0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_key_create); + LIB_FUNCTION("6BpEZuDT7YI", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_key_delete); + LIB_FUNCTION("0-KXaS70xy4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_getspecific); + LIB_FUNCTION("WrOLvHU0yQM", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setspecific); + + // Orbis + LIB_FUNCTION("geDaqgH9lTg", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_key_create)); + LIB_FUNCTION("eoht7mQOCmo", "libkernel", 1, "libkernel", 1, 1, posix_pthread_getspecific); + LIB_FUNCTION("+BzXYkqYeLE", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_setspecific)); +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/rwlock.cpp b/src/core/libraries/kernel/threads/rwlock.cpp index 87271fe21..2d5a4cdb6 100644 --- a/src/core/libraries/kernel/threads/rwlock.cpp +++ b/src/core/libraries/kernel/threads/rwlock.cpp @@ -1,326 +1,243 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "common/logging/log.h" #include "core/libraries/error_codes.h" +#include "core/libraries/kernel/kernel.h" +#include "core/libraries/kernel/threads/pthread.h" #include "core/libraries/libs.h" -#include "threads.h" namespace Libraries::Kernel { -extern PThreadCxt* g_pthread_cxt; +static std::mutex RwlockStaticLock; -int PS4_SYSV_ABI posix_pthread_rwlock_destroy(OrbisPthreadRwlock* rwlock) { - int result = pthread_rwlock_destroy(&(*rwlock)->pth_rwlock); - delete *rwlock; +#define THR_RWLOCK_INITIALIZER ((PthreadRwlock*)NULL) +#define THR_RWLOCK_DESTROYED ((PthreadRwlock*)1) + +#define CHECK_AND_INIT_RWLOCK \ + if (prwlock = (*rwlock); prwlock <= THR_RWLOCK_DESTROYED) [[unlikely]] { \ + if (prwlock == THR_RWLOCK_INITIALIZER) { \ + int ret; \ + ret = InitStatic(g_curthread, rwlock); \ + if (ret) \ + return (ret); \ + } else if (prwlock == THR_RWLOCK_DESTROYED) { \ + return POSIX_EINVAL; \ + } \ + prwlock = *rwlock; \ + } + +static int RwlockInit(PthreadRwlockT* rwlock, const PthreadRwlockAttrT* attr) { + PthreadRwlock* prwlock = new PthreadRwlock{}; + if (prwlock == nullptr) { + return POSIX_ENOMEM; + } + *rwlock = prwlock; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_rwlock_destroy(PthreadRwlockT* rwlock) { + PthreadRwlockT prwlock = *rwlock; + if (prwlock == THR_RWLOCK_INITIALIZER) { + return 0; + } + if (prwlock == THR_RWLOCK_DESTROYED) { + return POSIX_EINVAL; + } + *rwlock = THR_RWLOCK_DESTROYED; + delete prwlock; + return 0; +} + +static int InitStatic(Pthread* thread, PthreadRwlockT* rwlock) { + std::scoped_lock lk{RwlockStaticLock}; + if (*rwlock == THR_RWLOCK_INITIALIZER) { + return RwlockInit(rwlock, nullptr); + } + return 0; +} + +int PS4_SYSV_ABI posix_pthread_rwlock_init(PthreadRwlockT* rwlock, const PthreadRwlockAttrT* attr) { *rwlock = nullptr; - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlock_destroy: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; + return RwlockInit(rwlock, attr); +} + +int PthreadRwlock::Rdlock(const OrbisKernelTimespec* abstime) { + Pthread* curthread = g_curthread; + + /* + * POSIX said the validity of the abstimeout parameter need + * not be checked if the lock can be immediately acquired. + */ + if (lock.try_lock_shared()) { + curthread->rdlock_count++; + return 0; } - return result; -} - -int PS4_SYSV_ABI posix_pthread_rwlock_init(OrbisPthreadRwlock* rwlock, - const OrbisPthreadRwlockattr* attr, const char* name) { - *rwlock = new PthreadRwInternal{}; - if (attr == nullptr || *attr == nullptr) { - attr = g_pthread_cxt->getDefaultRwattr(); - } - int result = pthread_rwlock_init(&(*rwlock)->pth_rwlock, &(*attr)->attr_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlock_init: error = {}", result); - } - return ORBIS_OK; -} - -OrbisPthreadRwlock* createRwlock(OrbisPthreadRwlock* rwlock) { - if (rwlock == nullptr || *rwlock != nullptr) { - return rwlock; - } - static std::mutex mutex; - std::scoped_lock lk{mutex}; - if (*rwlock != nullptr) { - return rwlock; - } - const VAddr addr = std::bit_cast(rwlock); - const auto name = fmt::format("rwlock{:#x}", addr); - posix_pthread_rwlock_init(rwlock, nullptr, name.c_str()); - return rwlock; -} - -int PS4_SYSV_ABI posix_pthread_rwlock_rdlock(OrbisPthreadRwlock* rwlock) { - rwlock = createRwlock(rwlock); - int result = pthread_rwlock_rdlock(&(*rwlock)->pth_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlock_rdlock: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_rwlock_reltimedrdlock_np() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_pthread_rwlock_reltimedwrlock_np() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_pthread_rwlock_setname_np() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_pthread_rwlock_timedrdlock() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_pthread_rwlock_timedwrlock() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_pthread_rwlock_tryrdlock(OrbisPthreadRwlock* rwlock) { - rwlock = createRwlock(rwlock); - if (rwlock == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - int result = pthread_rwlock_tryrdlock(&(*rwlock)->pth_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlock_tryrdlock: error = {}", result); - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_rwlock_trywrlock(OrbisPthreadRwlock* rwlock) { - rwlock = createRwlock(rwlock); - if (rwlock == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - int result = pthread_rwlock_trywrlock(&(*rwlock)->pth_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlock_trywrlock: error = {}", result); - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_rwlock_unlock(OrbisPthreadRwlock* rwlock) { - rwlock = createRwlock(rwlock); - if (rwlock == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - int result = pthread_rwlock_unlock(&(*rwlock)->pth_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlock_unlock: error = {}", result); - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_rwlock_wrlock(OrbisPthreadRwlock* rwlock) { - rwlock = createRwlock(rwlock); - if (rwlock == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - int result = pthread_rwlock_wrlock(&(*rwlock)->pth_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlock_wrlock: error = {}", result); - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_rwlockattr_destroy(OrbisPthreadRwlockattr* attr) { - int result = pthread_rwlockattr_destroy(&(*attr)->attr_rwlock); - delete *attr; - *attr = nullptr; - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlockattr_destroy: error = {}", result); - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_rwlockattr_getpshared() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_pthread_rwlockattr_gettype_np() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_pthread_rwlockattr_init(OrbisPthreadRwlockattr* attr) { - *attr = new PthreadRwLockAttrInternal{}; - int result = pthread_rwlockattr_init(&(*attr)->attr_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlockattr_init: error = {}", result); - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_rwlockattr_setpshared() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_pthread_rwlockattr_settype_np() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI scePthreadRwlockattrDestroy(OrbisPthreadRwlockattr* attr) { - int result = pthread_rwlockattr_destroy(&(*attr)->attr_rwlock); - delete *attr; - *attr = nullptr; - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "scePthreadRwlockattrDestroy: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return result; -} - -int PS4_SYSV_ABI scePthreadRwlockattrGetpshared() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI scePthreadRwlockattrGettype() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI scePthreadRwlockattrInit(OrbisPthreadRwlockattr* attr) { - *attr = new PthreadRwLockAttrInternal{}; - int result = pthread_rwlockattr_init(&(*attr)->attr_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "scePthreadRwlockattrInit: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return result; -} - -int PS4_SYSV_ABI scePthreadRwlockattrSetpshared() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI scePthreadRwlockattrSettype() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI scePthreadRwlockDestroy(OrbisPthreadRwlock* rwlock) { - int result = pthread_rwlock_destroy(&(*rwlock)->pth_rwlock); - delete *rwlock; - *rwlock = nullptr; - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "scePthreadRwlockDestroy: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return result; -} - -int PS4_SYSV_ABI scePthreadRwlockInit(OrbisPthreadRwlock* rwlock, - const OrbisPthreadRwlockattr* attr, const char* name) { - *rwlock = new PthreadRwInternal{}; - if (rwlock == nullptr || *rwlock == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; + if (abstime && (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0)) [[unlikely]] { + return POSIX_EINVAL; } - if (attr == nullptr || *attr == nullptr) { - attr = g_pthread_cxt->getDefaultRwattr(); + // Note: On interruption an attempt to relock the mutex is made. + if (abstime != nullptr) { + if (!lock.try_lock_shared_until(abstime->TimePoint())) { + return POSIX_ETIMEDOUT; + } + } else { + lock.lock_shared(); } - if (name != nullptr) { - (*rwlock)->name = name; - } - int result = pthread_rwlock_init(&(*rwlock)->pth_rwlock, &(*attr)->attr_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "scePthreadRwlockInit: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return ORBIS_OK; + + curthread->rdlock_count++; + return 0; } -int PS4_SYSV_ABI scePthreadRwlockRdlock(OrbisPthreadRwlock* rwlock) { - if (rwlock == nullptr || *rwlock == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; +int PthreadRwlock::Wrlock(const OrbisKernelTimespec* abstime) { + Pthread* curthread = g_curthread; + + /* + * POSIX said the validity of the abstimeout parameter need + * not be checked if the lock can be immediately acquired. + */ + if (lock.try_lock()) { + owner = curthread; + return 0; } - int result = pthread_rwlock_rdlock(&(*rwlock)->pth_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "scePthreadRwlockRdlock: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; + + if (abstime && (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0)) { + return POSIX_EINVAL; } - return result; + + // Note: On interruption an attempt to relock the mutex is made. + if (abstime != nullptr) { + if (!lock.try_lock_until(abstime->TimePoint())) { + return POSIX_ETIMEDOUT; + } + } else { + lock.lock(); + } + + owner = curthread; + return 0; } -int PS4_SYSV_ABI scePthreadRwlockTimedrdlock() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI posix_pthread_rwlock_rdlock(PthreadRwlockT* rwlock) { + PthreadRwlockT prwlock{}; + CHECK_AND_INIT_RWLOCK + return prwlock->Rdlock(nullptr); } -int PS4_SYSV_ABI scePthreadRwlockTimedwrlock() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI posix_pthread_rwlock_timedrdlock(PthreadRwlockT* rwlock, + const OrbisKernelTimespec* abstime) { + PthreadRwlockT prwlock{}; + CHECK_AND_INIT_RWLOCK + return prwlock->Rdlock(abstime); } -int PS4_SYSV_ABI scePthreadRwlockTryrdlock(OrbisPthreadRwlock* rwlock) { - if (rwlock == nullptr || *rwlock == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; +int PS4_SYSV_ABI posix_pthread_rwlock_tryrdlock(PthreadRwlockT* rwlock) { + Pthread* curthread = g_curthread; + PthreadRwlockT prwlock{}; + CHECK_AND_INIT_RWLOCK + + if (!prwlock->lock.try_lock_shared()) { + return POSIX_EBUSY; } - int result = pthread_rwlock_tryrdlock(&(*rwlock)->pth_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "scePthreadRwlockTryrdlock: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return result; + + curthread->rdlock_count++; + return 0; } -int PS4_SYSV_ABI scePthreadRwlockTrywrlock(OrbisPthreadRwlock* rwlock) { - if (rwlock == nullptr || *rwlock == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; +int PS4_SYSV_ABI posix_pthread_rwlock_trywrlock(PthreadRwlockT* rwlock) { + Pthread* curthread = g_curthread; + PthreadRwlockT prwlock{}; + CHECK_AND_INIT_RWLOCK + + if (!prwlock->lock.try_lock()) { + return POSIX_EBUSY; } - int result = pthread_rwlock_trywrlock(&(*rwlock)->pth_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "scePthreadRwlockTrywrlock: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return result; + prwlock->owner = curthread; + return 0; } -int PS4_SYSV_ABI scePthreadRwlockUnlock(OrbisPthreadRwlock* rwlock) { - if (rwlock == nullptr || *rwlock == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - int result = pthread_rwlock_unlock(&(*rwlock)->pth_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "scePthreadRwlockUnlock: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return result; +int PS4_SYSV_ABI posix_pthread_rwlock_wrlock(PthreadRwlockT* rwlock) { + PthreadRwlockT prwlock{}; + CHECK_AND_INIT_RWLOCK + return prwlock->Wrlock(nullptr); } -int PS4_SYSV_ABI scePthreadRwlockWrlock(OrbisPthreadRwlock* rwlock) { - rwlock = createRwlock(rwlock); - int result = pthread_rwlock_wrlock(&(*rwlock)->pth_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "scePthreadRwlockWrlock: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return result; +int PS4_SYSV_ABI posix_pthread_rwlock_timedwrlock(PthreadRwlockT* rwlock, + const OrbisKernelTimespec* abstime) { + PthreadRwlockT prwlock{}; + CHECK_AND_INIT_RWLOCK + return prwlock->Wrlock(abstime); } -void RwlockSymbolsRegister(Core::Loader::SymbolsResolver* sym) { +int PS4_SYSV_ABI posix_pthread_rwlock_unlock(PthreadRwlockT* rwlock) { + Pthread* curthread = g_curthread; + PthreadRwlockT prwlock = *rwlock; + if (prwlock <= THR_RWLOCK_DESTROYED) [[unlikely]] { + return POSIX_EINVAL; + } + + if (prwlock->owner == curthread) { + prwlock->lock.unlock(); + prwlock->owner = nullptr; + } else { + prwlock->lock.unlock_shared(); + if (prwlock->owner == nullptr) { + curthread->rdlock_count--; + } + } + + return 0; +} + +int PS4_SYSV_ABI posix_pthread_rwlockattr_destroy(PthreadRwlockAttrT* rwlockattr) { + if (rwlockattr == nullptr) { + return POSIX_EINVAL; + } + PthreadRwlockAttrT prwlockattr = *rwlockattr; + if (prwlockattr == nullptr) { + return POSIX_EINVAL; + } + + delete prwlockattr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_rwlockattr_getpshared(const PthreadRwlockAttrT* rwlockattr, + int* pshared) { + *pshared = (*rwlockattr)->pshared; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_rwlockattr_init(PthreadRwlockAttrT* rwlockattr) { + if (rwlockattr == nullptr) { + return POSIX_EINVAL; + } + + PthreadRwlockAttrT prwlockattr = new PthreadRwlockAttr{}; + if (prwlockattr == nullptr) { + return POSIX_ENOMEM; + } + + prwlockattr->pshared = 0; + *rwlockattr = prwlockattr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_rwlockattr_setpshared(PthreadRwlockAttrT* rwlockattr, int pshared) { + /* Only PTHREAD_PROCESS_PRIVATE is supported. */ + if (pshared != 0) { + return POSIX_EINVAL; + } + + (*rwlockattr)->pshared = pshared; + return 0; +} + +void RegisterRwlock(Core::Loader::SymbolsResolver* sym) { + // Posix-Kernel LIB_FUNCTION("1471ajPzxh0", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlock_destroy); LIB_FUNCTION("ytQULN-nhL4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlock_init); LIB_FUNCTION("iGjsr1WAtI0", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlock_rdlock); - LIB_FUNCTION("dYv-+If2GPk", "libkernel", 1, "libkernel", 1, 1, - posix_pthread_rwlock_reltimedrdlock_np); - LIB_FUNCTION("RRnSj8h8VR4", "libkernel", 1, "libkernel", 1, 1, - posix_pthread_rwlock_reltimedwrlock_np); - LIB_FUNCTION("Uwxgnsi3xeM", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlock_setname_np); LIB_FUNCTION("lb8lnYo-o7k", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlock_timedrdlock); LIB_FUNCTION("9zklzAl9CGM", "libkernel", 1, "libkernel", 1, 1, @@ -333,13 +250,11 @@ void RwlockSymbolsRegister(Core::Loader::SymbolsResolver* sym) { posix_pthread_rwlockattr_destroy); LIB_FUNCTION("VqEMuCv-qHY", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlockattr_getpshared); - LIB_FUNCTION("l+bG5fsYkhg", "libkernel", 1, "libkernel", 1, 1, - posix_pthread_rwlockattr_gettype_np); LIB_FUNCTION("xFebsA4YsFI", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlockattr_init); LIB_FUNCTION("OuKg+kRDD7U", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlockattr_setpshared); - LIB_FUNCTION("8NuOHiTr1Vw", "libkernel", 1, "libkernel", 1, 1, - posix_pthread_rwlockattr_settype_np); + + // Posix LIB_FUNCTION("1471ajPzxh0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_rwlock_destroy); LIB_FUNCTION("ytQULN-nhL4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_rwlock_init); LIB_FUNCTION("iGjsr1WAtI0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_rwlock_rdlock); @@ -357,27 +272,37 @@ void RwlockSymbolsRegister(Core::Loader::SymbolsResolver* sym) { posix_pthread_rwlockattr_destroy); LIB_FUNCTION("VqEMuCv-qHY", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_rwlockattr_getpshared); - LIB_FUNCTION("l+bG5fsYkhg", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_rwlockattr_gettype_np); LIB_FUNCTION("xFebsA4YsFI", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_rwlockattr_init); LIB_FUNCTION("OuKg+kRDD7U", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_rwlockattr_setpshared); - LIB_FUNCTION("8NuOHiTr1Vw", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_rwlockattr_settype_np); - LIB_FUNCTION("i2ifZ3fS2fo", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockattrDestroy); - LIB_FUNCTION("LcOZBHGqbFk", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockattrGetpshared); - LIB_FUNCTION("Kyls1ChFyrc", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockattrGettype); - LIB_FUNCTION("yOfGg-I1ZII", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockattrInit); - LIB_FUNCTION("-ZvQH18j10c", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockattrSetpshared); - LIB_FUNCTION("h-OifiouBd8", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockattrSettype); - LIB_FUNCTION("BB+kb08Tl9A", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockDestroy); - LIB_FUNCTION("6ULAa0fq4jA", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockInit); - LIB_FUNCTION("Ox9i0c7L5w0", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockRdlock); - LIB_FUNCTION("iPtZRWICjrM", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockTimedrdlock); - LIB_FUNCTION("adh--6nIqTk", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockTimedwrlock); - LIB_FUNCTION("XD3mDeybCnk", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockTryrdlock); - LIB_FUNCTION("bIHoZCTomsI", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockTrywrlock); - LIB_FUNCTION("+L98PIbGttk", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockUnlock); - LIB_FUNCTION("mqdNorrB+gI", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockWrlock); + + // Orbis + LIB_FUNCTION("i2ifZ3fS2fo", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlockattr_destroy)); + LIB_FUNCTION("LcOZBHGqbFk", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlockattr_getpshared)); + LIB_FUNCTION("yOfGg-I1ZII", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlockattr_init)); + LIB_FUNCTION("-ZvQH18j10c", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlockattr_setpshared)); + LIB_FUNCTION("BB+kb08Tl9A", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlock_destroy)); + LIB_FUNCTION("6ULAa0fq4jA", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlock_init)); + LIB_FUNCTION("Ox9i0c7L5w0", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlock_rdlock)); + LIB_FUNCTION("iPtZRWICjrM", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlock_timedrdlock)); + LIB_FUNCTION("adh--6nIqTk", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlock_timedwrlock)); + LIB_FUNCTION("XD3mDeybCnk", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlock_tryrdlock)); + LIB_FUNCTION("bIHoZCTomsI", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlock_trywrlock)); + LIB_FUNCTION("+L98PIbGttk", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlock_unlock)); + LIB_FUNCTION("mqdNorrB+gI", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlock_wrlock)); } + } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/semaphore.cpp b/src/core/libraries/kernel/threads/semaphore.cpp index 59099c1b8..9c9c11178 100644 --- a/src/core/libraries/kernel/threads/semaphore.cpp +++ b/src/core/libraries/kernel/threads/semaphore.cpp @@ -4,23 +4,31 @@ #include #include #include -#include - -#include "common/assert.h" +#include #include "common/logging/log.h" #include "core/libraries/error_codes.h" +#include "core/libraries/kernel/kernel.h" +#include "core/libraries/kernel/threads/pthread.h" +#include "core/libraries/kernel/time.h" #include "core/libraries/libs.h" namespace Libraries::Kernel { -class Semaphore { +constexpr int ORBIS_KERNEL_SEM_VALUE_MAX = 0x7FFFFFFF; + +struct PthreadSem { + explicit PthreadSem(s32 value_) : semaphore{value_}, value{value_} {} + + std::counting_semaphore semaphore; + std::atomic value; +}; + +class OrbisSem { public: - Semaphore(s32 init_count, s32 max_count, std::string_view name, bool is_fifo) + OrbisSem(s32 init_count, s32 max_count, std::string_view name, bool is_fifo) : name{name}, token_count{init_count}, max_count{max_count}, init_count{init_count}, is_fifo{is_fifo} {} - ~Semaphore() { - ASSERT(wait_list.empty()); - } + ~OrbisSem() = default; int Wait(bool can_block, s32 need_count, u32* timeout) { std::unique_lock lk{mutex}; @@ -41,7 +49,9 @@ public: const auto it = AddWaiter(&waiter); // Perform the wait. - const s32 result = waiter.Wait(lk, timeout); + lk.unlock(); + const s32 result = waiter.Wait(timeout); + lk.lock(); if (result == SCE_KERNEL_ERROR_ETIMEDOUT) { wait_list.erase(it); } @@ -64,7 +74,7 @@ public: } it = wait_list.erase(it); token_count -= waiter->need_count; - waiter->cv.notify_one(); + waiter->sema.release(); } return true; @@ -77,30 +87,35 @@ public: } for (auto* waiter : wait_list) { waiter->was_cancled = true; - waiter->cv.notify_one(); + waiter->sema.release(); } wait_list.clear(); token_count = set_count < 0 ? init_count : set_count; return ORBIS_OK; } + void Delete() { + std::scoped_lock lk{mutex}; + for (auto* waiter : wait_list) { + waiter->was_deleted = true; + waiter->sema.release(); + } + wait_list.clear(); + } + public: struct WaitingThread { - std::condition_variable cv; + std::binary_semaphore sema; u32 priority; s32 need_count; bool was_deleted{}; bool was_cancled{}; - explicit WaitingThread(s32 need_count, bool is_fifo) : need_count{need_count} { - if (is_fifo) { - return; - } + explicit WaitingThread(s32 need_count, bool is_fifo) : sema{0}, need_count{need_count} { // Retrieve calling thread priority for sorting into waiting threads list. - s32 policy; - sched_param param; - pthread_getschedparam(pthread_self(), &policy, ¶m); - priority = param.sched_priority; + if (!is_fifo) { + priority = g_curthread->attr.prio; + } } int GetResult(bool timed_out) { @@ -116,24 +131,24 @@ public: return SCE_OK; } - int Wait(std::unique_lock& lk, u32* timeout) { + int Wait(u32* timeout) { if (!timeout) { // Wait indefinitely until we are woken up. - cv.wait(lk); + sema.acquire(); return GetResult(false); } // Wait until timeout runs out, recording how much remaining time there was. const auto start = std::chrono::high_resolution_clock::now(); - const auto status = cv.wait_for(lk, std::chrono::microseconds(*timeout)); + const auto sema_timeout = !sema.try_acquire_for(std::chrono::microseconds(*timeout)); const auto end = std::chrono::high_resolution_clock::now(); const auto time = std::chrono::duration_cast(end - start).count(); - if (status == std::cv_status::timeout) { + if (sema_timeout) { *timeout = 0; } else { *timeout -= time; } - return GetResult(status == std::cv_status::timeout); + return GetResult(sema_timeout); } }; @@ -163,7 +178,7 @@ public: bool is_fifo; }; -using OrbisKernelSema = Semaphore*; +using OrbisKernelSema = OrbisSem*; s32 PS4_SYSV_ABI sceKernelCreateSema(OrbisKernelSema* sem, const char* pName, u32 attr, s32 initCount, s32 maxCount, const void* pOptParam) { @@ -171,7 +186,7 @@ s32 PS4_SYSV_ABI sceKernelCreateSema(OrbisKernelSema* sem, const char* pName, u3 LOG_ERROR(Lib_Kernel, "Semaphore creation parameters are invalid!"); return ORBIS_KERNEL_ERROR_EINVAL; } - *sem = new Semaphore(initCount, maxCount, pName, attr == 1); + *sem = new OrbisSem(initCount, maxCount, pName, attr == 1); return ORBIS_OK; } @@ -210,17 +225,109 @@ int PS4_SYSV_ABI sceKernelDeleteSema(OrbisKernelSema sem) { if (!sem) { return SCE_KERNEL_ERROR_ESRCH; } - delete sem; + sem->Delete(); return ORBIS_OK; } -void SemaphoreSymbolsRegister(Core::Loader::SymbolsResolver* sym) { +int PS4_SYSV_ABI posix_sem_init(PthreadSem** sem, int pshared, unsigned int value) { + if (value > ORBIS_KERNEL_SEM_VALUE_MAX) { + *__Error() = POSIX_EINVAL; + return -1; + } + if (sem != nullptr) { + *sem = new PthreadSem(value); + } + return 0; +} + +int PS4_SYSV_ABI posix_sem_wait(PthreadSem** sem) { + if (sem == nullptr || *sem == nullptr) { + *__Error() = POSIX_EINVAL; + return -1; + } + (*sem)->semaphore.acquire(); + --(*sem)->value; + return 0; +} + +int PS4_SYSV_ABI posix_sem_trywait(PthreadSem** sem) { + if (sem == nullptr || *sem == nullptr) { + *__Error() = POSIX_EINVAL; + return -1; + } + if (!(*sem)->semaphore.try_acquire()) { + *__Error() = POSIX_EAGAIN; + return -1; + } + --(*sem)->value; + return 0; +} + +int PS4_SYSV_ABI posix_sem_timedwait(PthreadSem** sem, const OrbisKernelTimespec* t) { + if (sem == nullptr || *sem == nullptr) { + *__Error() = POSIX_EINVAL; + return -1; + } + if (!(*sem)->semaphore.try_acquire_until(t->TimePoint())) { + *__Error() = POSIX_ETIMEDOUT; + return -1; + } + --(*sem)->value; + return 0; +} + +int PS4_SYSV_ABI posix_sem_post(PthreadSem** sem) { + if (sem == nullptr || *sem == nullptr) { + *__Error() = POSIX_EINVAL; + return -1; + } + if ((*sem)->value == ORBIS_KERNEL_SEM_VALUE_MAX) { + *__Error() = POSIX_EOVERFLOW; + return -1; + } + ++(*sem)->value; + (*sem)->semaphore.release(); + return 0; +} + +int PS4_SYSV_ABI posix_sem_destroy(PthreadSem** sem) { + if (sem == nullptr || *sem == nullptr) { + *__Error() = POSIX_EINVAL; + return -1; + } + delete *sem; + *sem = nullptr; + return 0; +} + +int PS4_SYSV_ABI posix_sem_getvalue(PthreadSem** sem, int* sval) { + if (sem == nullptr || *sem == nullptr) { + *__Error() = POSIX_EINVAL; + return -1; + } + if (sval) { + *sval = (*sem)->value; + } + return 0; +} + +void RegisterSemaphore(Core::Loader::SymbolsResolver* sym) { + // Orbis LIB_FUNCTION("188x57JYp0g", "libkernel", 1, "libkernel", 1, 1, sceKernelCreateSema); LIB_FUNCTION("Zxa0VhQVTsk", "libkernel", 1, "libkernel", 1, 1, sceKernelWaitSema); LIB_FUNCTION("4czppHBiriw", "libkernel", 1, "libkernel", 1, 1, sceKernelSignalSema); LIB_FUNCTION("12wOHk8ywb0", "libkernel", 1, "libkernel", 1, 1, sceKernelPollSema); LIB_FUNCTION("4DM06U2BNEY", "libkernel", 1, "libkernel", 1, 1, sceKernelCancelSema); LIB_FUNCTION("R1Jvn8bSCW8", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteSema); + + // Posix + LIB_FUNCTION("pDuPEf3m4fI", "libScePosix", 1, "libkernel", 1, 1, posix_sem_init); + LIB_FUNCTION("YCV5dGGBcCo", "libScePosix", 1, "libkernel", 1, 1, posix_sem_wait); + LIB_FUNCTION("WBWzsRifCEA", "libScePosix", 1, "libkernel", 1, 1, posix_sem_trywait); + LIB_FUNCTION("w5IHyvahg-o", "libScePosix", 1, "libkernel", 1, 1, posix_sem_timedwait); + LIB_FUNCTION("IKP8typ0QUk", "libScePosix", 1, "libkernel", 1, 1, posix_sem_post); + LIB_FUNCTION("cDW233RAwWo", "libScePosix", 1, "libkernel", 1, 1, posix_sem_destroy); + LIB_FUNCTION("Bq+LRV-N6Hk", "libScePosix", 1, "libkernel", 1, 1, posix_sem_getvalue); } } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/sleepq.cpp b/src/core/libraries/kernel/threads/sleepq.cpp new file mode 100644 index 000000000..d998141d0 --- /dev/null +++ b/src/core/libraries/kernel/threads/sleepq.cpp @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include "common/spin_lock.h" +#include "core/libraries/kernel/threads/pthread.h" +#include "core/libraries/kernel/threads/sleepq.h" + +namespace Libraries::Kernel { + +static constexpr int HASHSHIFT = 9; +static constexpr int HASHSIZE = (1 << HASHSHIFT); +#define SC_HASH(wchan) \ + ((u32)((((uintptr_t)(wchan) >> 3) ^ ((uintptr_t)(wchan) >> (HASHSHIFT + 3))) & (HASHSIZE - 1))) +#define SC_LOOKUP(wc) &sc_table[SC_HASH(wc)] + +struct SleepQueueChain { + Common::SpinLock sc_lock; + SleepqList sc_queues; + int sc_type; +}; + +static std::array sc_table{}; + +void SleepqLock(void* wchan) { + SleepQueueChain* sc = SC_LOOKUP(wchan); + sc->sc_lock.lock(); +} + +void SleepqUnlock(void* wchan) { + SleepQueueChain* sc = SC_LOOKUP(wchan); + sc->sc_lock.unlock(); +} + +SleepQueue* SleepqLookup(void* wchan) { + SleepQueueChain* sc = SC_LOOKUP(wchan); + for (auto& sq : sc->sc_queues) { + if (sq.sq_wchan == wchan) { + return std::addressof(sq); + } + } + return nullptr; +} + +void SleepqAdd(void* wchan, Pthread* td) { + SleepQueue* sq = SleepqLookup(wchan); + if (sq != nullptr) { + sq->sq_freeq.push_front(*td->sleepqueue); + } else { + SleepQueueChain* sc = SC_LOOKUP(wchan); + sq = td->sleepqueue; + sc->sc_queues.push_front(*sq); + sq->sq_wchan = wchan; + /* sq->sq_type = type; */ + } + td->sleepqueue = NULL; + td->wchan = wchan; + sq->sq_blocked.push_front(td); +} + +int SleepqRemove(SleepQueue* sq, Pthread* td) { + std::erase(sq->sq_blocked, td); + if (sq->sq_blocked.empty()) { + td->sleepqueue = sq; + sq->unlink(); + td->wchan = nullptr; + return 0; + } else { + td->sleepqueue = std::addressof(sq->sq_freeq.front()); + sq->sq_freeq.pop_front(); + td->wchan = nullptr; + return 1; + } +} + +void SleepqDrop(SleepQueue* sq, void (*callback)(Pthread*, void*), void* arg) { + if (sq->sq_blocked.empty()) { + return; + } + + sq->unlink(); + Pthread* td = sq->sq_blocked.front(); + sq->sq_blocked.pop_front(); + + callback(td, arg); + + td->sleepqueue = sq; + td->wchan = nullptr; + + auto sq2 = sq->sq_freeq.begin(); + for (Pthread* td : sq->sq_blocked) { + callback(td, arg); + td->sleepqueue = std::addressof(*sq2); + td->wchan = nullptr; + ++sq2; + } + sq->sq_blocked.clear(); + sq->sq_freeq.clear(); +} + +} // namespace Libraries::Kernel \ No newline at end of file diff --git a/src/core/libraries/kernel/threads/sleepq.h b/src/core/libraries/kernel/threads/sleepq.h new file mode 100644 index 000000000..9274942e3 --- /dev/null +++ b/src/core/libraries/kernel/threads/sleepq.h @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include "common/types.h" + +namespace Libraries::Kernel { + +struct Pthread; + +using ListBaseHook = + boost::intrusive::list_base_hook>; + +using SleepqList = boost::intrusive::list>; + +struct SleepQueue : public ListBaseHook { + std::list sq_blocked; + SleepqList sq_freeq; + void* sq_wchan; + int sq_type; +}; + +void SleepqLock(void* wchan); + +void SleepqUnlock(void* wchan); + +SleepQueue* SleepqLookup(void* wchan); + +void SleepqAdd(void* wchan, Pthread* td); + +int SleepqRemove(SleepQueue* sq, Pthread* td); + +void SleepqDrop(SleepQueue* sq, void (*callback)(Pthread*, void*), void* arg); + +} // namespace Libraries::Kernel \ No newline at end of file diff --git a/src/core/libraries/kernel/threads/stack.cpp b/src/core/libraries/kernel/threads/stack.cpp new file mode 100644 index 000000000..45715482a --- /dev/null +++ b/src/core/libraries/kernel/threads/stack.cpp @@ -0,0 +1,141 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/assert.h" +#include "core/libraries/kernel/threads/pthread.h" +#include "core/libraries/kernel/threads/thread_state.h" +#include "core/memory.h" + +namespace Libraries::Kernel { + +static constexpr size_t RoundUp(size_t size) { + if (size % ThrPageSize != 0) { + size = ((size / ThrPageSize) + 1) * ThrPageSize; + } + return size; +} + +int ThreadState::CreateStack(PthreadAttr* attr) { + if ((attr->stackaddr_attr) != NULL) { + attr->guardsize_attr = 0; + attr->flags |= PthreadAttrFlags::StackUser; + return 0; + } + + /* + * Round up stack size to nearest multiple of _thr_page_size so + * that mmap() * will work. If the stack size is not an even + * multiple, we end up initializing things such that there is + * unused space above the beginning of the stack, so the stack + * sits snugly against its guard. + */ + size_t stacksize = RoundUp(attr->stacksize_attr); + size_t guardsize = RoundUp(attr->guardsize_attr); + + attr->stackaddr_attr = NULL; + attr->flags &= ~PthreadAttrFlags::StackUser; + + /* + * Use the garbage collector lock for synchronization of the + * spare stack lists and allocations from usrstack. + */ + thread_list_lock.lock(); + + /* + * If the stack and guard sizes are default, try to allocate a stack + * from the default-size stack cache: + */ + if (stacksize == ThrStackDefault && guardsize == ThrGuardDefault) { + if (!dstackq.empty()) { + /* Use the spare stack. */ + Stack* spare_stack = dstackq.top(); + dstackq.pop(); + attr->stackaddr_attr = spare_stack->stackaddr; + } + } + /* + * The user specified a non-default stack and/or guard size, so try to + * allocate a stack from the non-default size stack cache, using the + * rounded up stack size (stack_size) in the search: + */ + else { + const auto it = std::ranges::find_if(mstackq, [&](Stack* stack) { + return stack->stacksize == stacksize && stack->guardsize == guardsize; + }); + if (it != mstackq.end()) { + attr->stackaddr_attr = (*it)->stackaddr; + mstackq.erase(it); + } + } + + /* A cached stack was found. Release the lock. */ + if (attr->stackaddr_attr != NULL) { + thread_list_lock.unlock(); + return 0; + } + + /* Allocate a stack from usrstack. */ + if (last_stack == 0) { + static constexpr VAddr UsrStack = 0x7EFFF8000ULL; + last_stack = UsrStack - ThrStackInitial - ThrGuardDefault; + } + + /* Allocate a new stack. */ + VAddr stackaddr = last_stack - stacksize - guardsize; + + /* + * Even if stack allocation fails, we don't want to try to + * use this location again, so unconditionally decrement + * last_stack. Under normal operating conditions, the most + * likely reason for an mmap() error is a stack overflow of + * the adjacent thread stack. + */ + last_stack -= (stacksize + guardsize); + + /* Release the lock before mmap'ing it. */ + thread_list_lock.unlock(); + + /* Map the stack and guard page together, and split guard + page from allocated space: */ + auto* memory = Core::Memory::Instance(); + int ret = memory->MapMemory(reinterpret_cast(&stackaddr), stackaddr, + stacksize + guardsize, Core::MemoryProt::CpuReadWrite, + Core::MemoryMapFlags::NoFlags, Core::VMAType::Stack); + ASSERT_MSG(ret == 0, "Unable to map stack memory"); + + if (guardsize != 0) { + ret = memory->Protect(stackaddr, guardsize, Core::MemoryProt::NoAccess); + ASSERT_MSG(ret == 0, "Unable to protect guard page"); + } + + stackaddr += guardsize; + attr->stackaddr_attr = (void*)stackaddr; + + if (attr->stackaddr_attr != nullptr) { + return 0; + } + return -1; +} + +void ThreadState::FreeStack(PthreadAttr* attr) { + if (!attr || True(attr->flags & PthreadAttrFlags::StackUser) || !attr->stackaddr_attr) { + return; + } + + char* stack_base = (char*)attr->stackaddr_attr; + Stack* spare_stack = (Stack*)(stack_base + attr->stacksize_attr - sizeof(Stack)); + spare_stack->stacksize = RoundUp(attr->stacksize_attr); + spare_stack->guardsize = RoundUp(attr->guardsize_attr); + spare_stack->stackaddr = attr->stackaddr_attr; + + if (spare_stack->stacksize == ThrStackDefault && spare_stack->guardsize == ThrGuardDefault) { + /* Default stack/guard size. */ + dstackq.push(spare_stack); + } else { + /* Non-default stack/guard size. */ + mstackq.push_back(spare_stack); + } + attr->stackaddr_attr = nullptr; +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/tcb.cpp b/src/core/libraries/kernel/threads/tcb.cpp new file mode 100644 index 000000000..e5a158216 --- /dev/null +++ b/src/core/libraries/kernel/threads/tcb.cpp @@ -0,0 +1,96 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/assert.h" +#include "common/singleton.h" +#include "core/libraries/kernel/threads/pthread.h" +#include "core/libraries/libs.h" +#include "core/linker.h" +#include "core/tls.h" + +namespace Libraries::Kernel { + +static constexpr size_t TlsTcbSize = 0x40; +static constexpr size_t TlsTcbAlign = 0x20; + +static std::shared_mutex RtldLock; + +Core::Tcb* TcbCtor(Pthread* thread, int initial) { + std::scoped_lock lk{RtldLock}; + + auto* linker = Common::Singleton::Instance(); + auto* addr_out = linker->AllocateTlsForThread(initial); + ASSERT_MSG(addr_out, "Unable to allocate guest TCB"); + + // Initialize allocated memory and allocate DTV table. + const u32 num_dtvs = linker->MaxTlsIndex(); + const auto static_tls_size = linker->StaticTlsSize(); + auto* dtv_table = new Core::DtvEntry[num_dtvs + 2]{}; + + // Initialize thread control block + u8* addr = reinterpret_cast(addr_out); + auto* tcb = reinterpret_cast(addr + static_tls_size); + memset(addr_out, 0, static_tls_size); + tcb->tcb_self = tcb; + tcb->tcb_dtv = dtv_table; + + // Dtv[0] is the generation counter. libkernel puts their number into dtv[1] + dtv_table[0].counter = linker->GenerationCounter(); + dtv_table[1].counter = num_dtvs; + + // Copy init image of main module. + auto* module = linker->GetModule(0); + u8* dest = reinterpret_cast(addr + static_tls_size - module->tls.offset); + + if (module->tls.image_size != 0) { + if (module->tls.image_virtual_addr != 0) { + const u8* src = reinterpret_cast(module->tls.image_virtual_addr); + memcpy(dest, src, module->tls.init_image_size); + } + ASSERT_MSG(module->tls.modid > 0 && module->tls.modid <= num_dtvs); + tcb->tcb_dtv[module->tls.modid + 1].pointer = dest; + } + + if (tcb) { + tcb->tcb_thread = thread; + } + return tcb; +} + +void TcbDtor(Core::Tcb* oldtls) { + std::scoped_lock lk{RtldLock}; + auto* dtv_table = oldtls->tcb_dtv; + + auto* linker = Common::Singleton::Instance(); + const u32 max_tls_index = linker->MaxTlsIndex(); + const u32 num_dtvs = dtv_table[1].counter; + ASSERT_MSG(num_dtvs <= max_tls_index, "Out of bounds DTV access"); + + const u32 static_tls_size = linker->StaticTlsSize(); + const u8* tls_base = (const u8*)oldtls - static_tls_size; + + for (int i = 1; i < num_dtvs; i++) { + u8* dtv_ptr = dtv_table[i + 1].pointer; + if (dtv_ptr && (dtv_ptr < tls_base || (const u8*)oldtls < dtv_ptr)) { + linker->FreeTlsForNonPrimaryThread(dtv_ptr); + } + } + + delete[] dtv_table; +} + +struct TlsIndex { + u64 ti_module; + u64 ti_offset; +}; + +void* PS4_SYSV_ABI __tls_get_addr(TlsIndex* index) { + auto* linker = Common::Singleton::Instance(); + return linker->TlsGetAddr(index->ti_module, index->ti_offset); +} + +void RegisterRtld(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("vNe1w4diLCs", "libkernel", 1, "libkernel", 1, 1, __tls_get_addr); +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/thread_state.cpp b/src/core/libraries/kernel/threads/thread_state.cpp new file mode 100644 index 000000000..e968c39ae --- /dev/null +++ b/src/core/libraries/kernel/threads/thread_state.cpp @@ -0,0 +1,172 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include "common/alignment.h" +#include "common/scope_exit.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/kernel/threads/pthread.h" +#include "core/libraries/kernel/threads/sleepq.h" +#include "core/libraries/kernel/threads/thread_state.h" +#include "core/memory.h" +#include "core/tls.h" + +namespace Libraries::Kernel { + +thread_local Pthread* g_curthread{}; + +Core::Tcb* TcbCtor(Pthread* thread, int initial); +void TcbDtor(Core::Tcb* oldtls); + +ThreadState::ThreadState() { + // Reserve memory for maximum amount of threads allowed. + auto* memory = Core::Memory::Instance(); + static constexpr u32 ThrHeapSize = Common::AlignUp(sizeof(Pthread) * MaxThreads, 16_KB); + void* heap_addr{}; + const int ret = memory->MapMemory(&heap_addr, Core::SYSTEM_RESERVED_MIN, ThrHeapSize, + Core::MemoryProt::CpuReadWrite, Core::MemoryMapFlags::NoFlags, + Core::VMAType::File, "ThrHeap"); + ASSERT_MSG(ret == 0, "Unable to allocate thread heap memory {}", ret); + thread_heap.Initialize(heap_addr, ThrHeapSize); +} + +void ThreadState::Collect(Pthread* curthread) { + boost::container::small_vector work_list; + { + std::scoped_lock lk{thread_list_lock}; + for (auto it = gc_list.begin(); it != gc_list.end();) { + Pthread* td = *it; + if (td->tid != TidTerminated) { + it++; + continue; + } + FreeStack(&td->attr); + work_list.push_back(td); + it = gc_list.erase(it); + } + } + for (Pthread* td : work_list) { + Free(curthread, td); + } +} + +void ThreadState::TryCollect(Pthread* thread) { + SCOPE_EXIT { + thread->lock.unlock(); + }; + if (!thread->ShouldCollect()) { + return; + } + + thread->refcount++; + thread->lock.unlock(); + std::scoped_lock lk{thread_list_lock}; + thread->lock.lock(); + thread->refcount--; + if (thread->ShouldCollect()) { + threads.erase(thread); + gc_list.push_back(thread); + } +} + +Pthread* ThreadState::Alloc(Pthread* curthread) { + Pthread* thread = nullptr; + if (curthread != nullptr) { + if (GcNeeded()) { + Collect(curthread); + } + if (!free_threads.empty()) { + std::scoped_lock lk{free_thread_lock}; + thread = free_threads.back(); + free_threads.pop_back(); + } + } + if (thread == nullptr) { + if (total_threads > MaxThreads) { + return nullptr; + } + total_threads.fetch_add(1); + thread = thread_heap.Allocate(); + if (thread == nullptr) { + total_threads.fetch_sub(1); + return nullptr; + } + } + Core::Tcb* tcb = nullptr; + if (curthread != nullptr) { + std::scoped_lock lk{tcb_lock}; + tcb = TcbCtor(thread, 0 /* not initial tls */); + } else { + tcb = TcbCtor(thread, 1 /* initial tls */); + } + if (tcb != nullptr) { + memset(thread, 0, sizeof(Pthread)); + std::construct_at(thread); + thread->tcb = tcb; + thread->sleepqueue = new SleepQueue{}; + } else { + thread_heap.Free(thread); + total_threads.fetch_sub(1); + thread = nullptr; + } + return thread; +} + +void ThreadState::Free(Pthread* curthread, Pthread* thread) { + if (curthread != nullptr) { + std::scoped_lock lk{tcb_lock}; + TcbDtor(thread->tcb); + } else { + TcbDtor(thread->tcb); + } + thread->tcb = nullptr; + std::destroy_at(thread); + if (free_threads.size() >= MaxCachedThreads) { + delete thread->sleepqueue; + thread_heap.Free(thread); + total_threads.fetch_sub(1); + } else { + std::scoped_lock lk{free_thread_lock}; + free_threads.push_back(thread); + } +} + +int ThreadState::FindThread(Pthread* thread, bool include_dead) { + if (thread == nullptr) { + return POSIX_EINVAL; + } + std::scoped_lock lk{thread_list_lock}; + const auto it = threads.find(thread); + if (it == threads.end()) { + return POSIX_ESRCH; + } + thread->lock.lock(); + if (!include_dead && thread->state == PthreadState::Dead) { + thread->lock.unlock(); + return POSIX_ESRCH; + } + return 0; +} + +int ThreadState::RefAdd(Pthread* thread, bool include_dead) { + if (thread == nullptr) { + /* Invalid thread: */ + return POSIX_EINVAL; + } + + if (int ret = FindThread(thread, include_dead); ret != 0) { + return ret; + } + + thread->refcount++; + thread->lock.unlock(); + return 0; +} + +void ThreadState::RefDelete(Pthread* thread) { + thread->lock.lock(); + thread->refcount--; + TryCollect(thread); +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/thread_state.h b/src/core/libraries/kernel/threads/thread_state.h new file mode 100644 index 000000000..c98f2083e --- /dev/null +++ b/src/core/libraries/kernel/threads/thread_state.h @@ -0,0 +1,87 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include +#include "common/singleton.h" +#include "common/slab_heap.h" +#include "common/types.h" + +namespace Libraries::Kernel { + +struct Pthread; +struct PthreadAttr; + +struct Stack { + size_t stacksize; /* Stack size (rounded up). */ + size_t guardsize; /* Guard size. */ + void* stackaddr; /* Stack address. */ +}; + +struct ThreadState { + static constexpr size_t GcThreshold = 5; + static constexpr size_t MaxThreads = 100000; + static constexpr size_t MaxCachedThreads = 100; + + explicit ThreadState(); + + bool GcNeeded() const noexcept { + return gc_list.size() >= GcThreshold; + } + + void Collect(Pthread* curthread); + + void TryCollect(Pthread* thread); + + Pthread* Alloc(Pthread* curthread); + + void Free(Pthread* curthread, Pthread* thread); + + int FindThread(Pthread* thread, bool include_dead); + + int RefAdd(Pthread* thread, bool include_dead); + + void RefDelete(Pthread* thread); + + int CreateStack(PthreadAttr* attr); + + void FreeStack(PthreadAttr* attr); + + void Link(Pthread* curthread, Pthread* thread) { + { + std::scoped_lock lk{thread_list_lock}; + threads.insert(thread); + } + active_threads.fetch_add(1); + } + + void Unlink(Pthread* curthread, Pthread* thread) { + { + std::scoped_lock lk{thread_list_lock}; + threads.erase(thread); + } + active_threads.fetch_sub(1); + } + + Common::SlabHeap thread_heap; + std::set threads; + std::list free_threads; + std::list gc_list; + std::mutex free_thread_lock; + std::mutex tcb_lock; + std::mutex thread_list_lock; + std::atomic total_threads{}; + std::atomic active_threads{}; + std::stack dstackq; + std::list mstackq; + VAddr last_stack = 0; +}; + +using ThrState = Common::Singleton; + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/threads.h b/src/core/libraries/kernel/threads/threads.h deleted file mode 100644 index a3fd354b0..000000000 --- a/src/core/libraries/kernel/threads/threads.h +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/libraries/kernel/thread_management.h" - -namespace Core::Loader { -class SymbolsResolver; -} - -namespace Libraries::Kernel { - -int PS4_SYSV_ABI scePthreadRwlockattrInit(OrbisPthreadRwlockattr* attr); - -void SemaphoreSymbolsRegister(Core::Loader::SymbolsResolver* sym); -void RwlockSymbolsRegister(Core::Loader::SymbolsResolver* sym); -void KeySymbolsRegister(Core::Loader::SymbolsResolver* sym); - -} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/time_management.cpp b/src/core/libraries/kernel/time.cpp similarity index 88% rename from src/core/libraries/kernel/time_management.cpp rename to src/core/libraries/kernel/time.cpp index 853f8d54c..76ea5e353 100644 --- a/src/core/libraries/kernel/time_management.cpp +++ b/src/core/libraries/kernel/time.cpp @@ -4,10 +4,9 @@ #include #include "common/assert.h" -#include "common/debug.h" #include "common/native_clock.h" #include "core/libraries/error_codes.h" -#include "core/libraries/kernel/time_management.h" +#include "core/libraries/kernel/time.h" #include "core/libraries/libs.h" #ifdef _WIN64 @@ -17,6 +16,9 @@ #include "common/ntapi.h" #else +#if __APPLE__ +#include +#endif #include #include #include @@ -50,14 +52,7 @@ u64 PS4_SYSV_ABI sceKernelReadTsc() { int PS4_SYSV_ABI sceKernelUsleep(u32 microseconds) { #ifdef _WIN64 - if (microseconds < 1000u) { - LARGE_INTEGER interval{ - .QuadPart = -1 * (microseconds * 10u), - }; - NtDelayExecution(FALSE, &interval); - } else { - std::this_thread::sleep_for(std::chrono::microseconds(microseconds)); - } + std::this_thread::sleep_for(std::chrono::microseconds(microseconds)); return 0; #else timespec start; @@ -258,7 +253,33 @@ Common::NativeClock* GetClock() { } // namespace Dev -void timeSymbolsRegister(Core::Loader::SymbolsResolver* sym) { +int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time, + struct OrbisTimesec* st, unsigned long* dst_sec) { + LOG_TRACE(Kernel, "Called"); +#ifdef __APPLE__ + // std::chrono::current_zone() not available yet. + const auto* time_zone = date::current_zone(); +#else + const auto* time_zone = std::chrono::current_zone(); +#endif + auto info = time_zone->get_info(std::chrono::system_clock::now()); + + *local_time = info.offset.count() + info.save.count() * 60 + time; + + if (st != nullptr) { + st->t = time; + st->west_sec = info.offset.count() * 60; + st->dst_sec = info.save.count() * 60; + } + + if (dst_sec != nullptr) { + *dst_sec = info.save.count() * 60; + } + + return ORBIS_OK; +} + +void RegisterTime(Core::Loader::SymbolsResolver* sym) { clock = std::make_unique(); initial_ptc = clock->GetUptime(); LIB_FUNCTION("4J2sUJmuHZQ", "libkernel", 1, "libkernel", 1, 1, sceKernelGetProcessTime); @@ -284,6 +305,7 @@ void timeSymbolsRegister(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("lLMT9vJAck0", "libScePosix", 1, "libkernel", 1, 1, posix_clock_gettime); LIB_FUNCTION("smIj7eqzZE8", "libScePosix", 1, "libkernel", 1, 1, posix_clock_getres); LIB_FUNCTION("0NTHN1NKONI", "libkernel", 1, "libkernel", 1, 1, sceKernelConvertLocaltimeToUtc); + LIB_FUNCTION("-o5uEDpN+oY", "libkernel", 1, "libkernel", 1, 1, sceKernelConvertUtcToLocaltime); } } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/time_management.h b/src/core/libraries/kernel/time.h similarity index 76% rename from src/core/libraries/kernel/time_management.h rename to src/core/libraries/kernel/time.h index f2216f3d3..508ef2152 100644 --- a/src/core/libraries/kernel/time_management.h +++ b/src/core/libraries/kernel/time.h @@ -3,8 +3,8 @@ #pragma once +#include #include - #include "common/types.h" namespace Common { @@ -30,6 +30,19 @@ struct OrbisKernelTimezone { struct OrbisKernelTimespec { s64 tv_sec; s64 tv_nsec; + + std::chrono::system_clock::time_point TimePoint() const noexcept { + using namespace std::chrono; + const auto duration = + duration_cast(seconds{tv_sec} + nanoseconds{tv_nsec}); + return system_clock::time_point{duration}; + } +}; + +struct OrbisTimesec { + time_t t; + u32 west_sec; + u32 dst_sec; }; constexpr int ORBIS_CLOCK_REALTIME = 0; @@ -66,6 +79,10 @@ int PS4_SYSV_ABI sceKernelClockGettime(s32 clock_id, OrbisKernelTimespec* tp); s32 PS4_SYSV_ABI sceKernelGettimezone(OrbisKernelTimezone* tz); int PS4_SYSV_ABI sceKernelConvertLocaltimeToUtc(time_t param_1, int64_t param_2, time_t* seconds, OrbisKernelTimezone* timezone, int* dst_seconds); -void timeSymbolsRegister(Core::Loader::SymbolsResolver* sym); + +int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time, OrbisTimesec* st, + unsigned long* dst_sec); + +void RegisterTime(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::Kernel diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index 23d751622..537862a6c 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -14,7 +14,7 @@ #include "core/libraries/ime/error_dialog.h" #include "core/libraries/ime/ime.h" #include "core/libraries/ime/ime_dialog.h" -#include "core/libraries/kernel/libkernel.h" +#include "core/libraries/kernel/kernel.h" #include "core/libraries/libc_internal/libc_internal.h" #include "core/libraries/libpng/pngdec.h" #include "core/libraries/libs.h" @@ -49,11 +49,9 @@ namespace Libraries { void InitHLELibs(Core::Loader::SymbolsResolver* sym) { LOG_INFO(Lib_Kernel, "Initializing HLE libraries"); - Libraries::Kernel::LibKernel_Register(sym); + Libraries::Kernel::RegisterKernel(sym); Libraries::GnmDriver::RegisterlibSceGnmDriver(sym); Libraries::VideoOut::RegisterLib(sym); - - // New libraries folder from autogen Libraries::UserService::RegisterlibSceUserService(sym); Libraries::SystemService::RegisterlibSceSystemService(sym); Libraries::CommonDialog::RegisterlibSceCommonDialog(sym); diff --git a/src/core/libraries/libs.h b/src/core/libraries/libs.h index ea928101e..aa5ba4a97 100644 --- a/src/core/libraries/libs.h +++ b/src/core/libraries/libs.h @@ -9,41 +9,6 @@ #include "core/loader/elf.h" #include "core/loader/symbols_resolver.h" -template -struct StringLiteral { - constexpr StringLiteral(const char (&str)[N]) { - std::copy_n(str, N, value); - } - - char value[N]; -}; - -template -struct wrapper_impl; - -template -struct wrapper_impl { - static R PS4_SYSV_ABI wrap(Args... args) { - if (std::string_view(name.value) != "scePthreadEqual" && - std::string_view(name.value) != "sceUserServiceGetEvent") { - // LOG_WARNING(Core_Linker, "Function {} called", name.value); - } - if constexpr (std::is_same_v || std::is_same_v) { - const u32 ret = f(args...); - if (ret != 0 && std::string_view(name.value) != "scePthreadEqual") { - LOG_WARNING(Core_Linker, "Function {} returned {:#x}", name.value, ret); - } - return ret; - } - // stuff - return f(args...); - } -}; - -template -constexpr auto wrapper = wrapper_impl::wrap; - -// #define W(foo) wrapper<#foo, decltype(&foo), foo> #define W(foo) foo #define LIB_FUNCTION(nid, lib, libversion, mod, moduleVersionMajor, moduleVersionMinor, function) \ @@ -56,7 +21,7 @@ constexpr auto wrapper = wrapper_impl::wrap; sr.module_version_major = moduleVersionMajor; \ sr.module_version_minor = moduleVersionMinor; \ sr.type = Core::Loader::SymbolType::Function; \ - auto func = reinterpret_cast(W(function)); \ + auto func = reinterpret_cast(function); \ sym->AddSymbol(sr, func); \ } diff --git a/src/core/libraries/network/net_ctl_obj.cpp b/src/core/libraries/network/net_ctl_obj.cpp index 07381d676..ad944cd9c 100644 --- a/src/core/libraries/network/net_ctl_obj.cpp +++ b/src/core/libraries/network/net_ctl_obj.cpp @@ -1,80 +1,63 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "common/singleton.h" -#include "core/linker.h" -#include "net_ctl_codes.h" -#include "net_ctl_obj.h" +#include +#include "core/libraries/network/net_ctl_codes.h" +#include "core/libraries/network/net_ctl_obj.h" +#include "core/tls.h" -Libraries::NetCtl::NetCtlInternal::NetCtlInternal() { - callbacks.fill({nullptr, nullptr}); - nptoolCallbacks.fill({nullptr, nullptr}); -} +namespace Libraries::NetCtl { -Libraries::NetCtl::NetCtlInternal::~NetCtlInternal() {} +NetCtlInternal::NetCtlInternal() = default; -s32 Libraries::NetCtl::NetCtlInternal::registerCallback(OrbisNetCtlCallback func, void* arg) { - std::unique_lock lock{m_mutex}; +NetCtlInternal::~NetCtlInternal() = default; + +s32 NetCtlInternal::RegisterCallback(OrbisNetCtlCallback func, void* arg) { + std::scoped_lock lock{m_mutex}; // Find the next available slot - int next_id = 0; - for (const auto& callback : callbacks) { - if (callback.func == nullptr) { - break; - } - next_id++; - } - - if (next_id == 8) { + const auto it = std::ranges::find(callbacks, nullptr, &NetCtlCallback::func); + if (it == callbacks.end()) { return ORBIS_NET_CTL_ERROR_CALLBACK_MAX; } + const int next_id = std::distance(callbacks.begin(), it); callbacks[next_id].func = func; callbacks[next_id].arg = arg; return next_id; } -s32 Libraries::NetCtl::NetCtlInternal::registerNpToolkitCallback( - OrbisNetCtlCallbackForNpToolkit func, void* arg) { - - std::unique_lock lock{m_mutex}; +s32 NetCtlInternal::RegisterNpToolkitCallback(OrbisNetCtlCallbackForNpToolkit func, void* arg) { + std::scoped_lock lock{m_mutex}; // Find the next available slot - int next_id = 0; - for (const auto& callback : nptoolCallbacks) { - if (callback.func == nullptr) { - break; - } - next_id++; - } - - if (next_id == 8) { + const auto it = std::ranges::find(nptool_callbacks, nullptr, &NetCtlCallbackForNpToolkit::func); + if (it == nptool_callbacks.end()) { return ORBIS_NET_CTL_ERROR_CALLBACK_MAX; } - nptoolCallbacks[next_id].func = func; - nptoolCallbacks[next_id].arg = arg; + const int next_id = std::distance(nptool_callbacks.begin(), it); + nptool_callbacks[next_id].func = func; + nptool_callbacks[next_id].arg = arg; return next_id; } -void Libraries::NetCtl::NetCtlInternal::checkCallback() { - std::unique_lock lock{m_mutex}; - const auto* linker = Common::Singleton::Instance(); - for (auto& callback : callbacks) { - if (callback.func != nullptr) { - linker->ExecuteGuest(callback.func, ORBIS_NET_CTL_EVENT_TYPE_DISCONNECTED, - callback.arg); +void NetCtlInternal::CheckCallback() { + std::scoped_lock lock{m_mutex}; + for (const auto [func, arg] : callbacks) { + if (func != nullptr) { + Core::ExecuteGuest(func, ORBIS_NET_CTL_EVENT_TYPE_DISCONNECTED, arg); } } } -void Libraries::NetCtl::NetCtlInternal::checkNpToolkitCallback() { - std::unique_lock lock{m_mutex}; - const auto* linker = Common::Singleton::Instance(); - for (auto& callback : nptoolCallbacks) { - if (callback.func != nullptr) { - linker->ExecuteGuest(callback.func, ORBIS_NET_CTL_EVENT_TYPE_DISCONNECTED, - callback.arg); +void NetCtlInternal::CheckNpToolkitCallback() { + std::scoped_lock lock{m_mutex}; + for (const auto [func, arg] : nptool_callbacks) { + if (func != nullptr) { + Core::ExecuteGuest(func, ORBIS_NET_CTL_EVENT_TYPE_DISCONNECTED, arg); } } } + +} // namespace Libraries::NetCtl diff --git a/src/core/libraries/network/net_ctl_obj.h b/src/core/libraries/network/net_ctl_obj.h index 3178677f4..cbbb33a4e 100644 --- a/src/core/libraries/network/net_ctl_obj.h +++ b/src/core/libraries/network/net_ctl_obj.h @@ -3,9 +3,7 @@ #pragma once -#include #include - #include "common/types.h" namespace Libraries::NetCtl { @@ -25,16 +23,17 @@ struct NetCtlCallbackForNpToolkit { class NetCtlInternal { public: - NetCtlInternal(); + explicit NetCtlInternal(); ~NetCtlInternal(); - s32 registerCallback(OrbisNetCtlCallback func, void* arg); - s32 registerNpToolkitCallback(OrbisNetCtlCallbackForNpToolkit func, void* arg); - void checkCallback(); - void checkNpToolkitCallback(); + + s32 RegisterCallback(OrbisNetCtlCallback func, void* arg); + s32 RegisterNpToolkitCallback(OrbisNetCtlCallbackForNpToolkit func, void* arg); + void CheckCallback(); + void CheckNpToolkitCallback(); public: - std::array nptoolCallbacks; - std::array callbacks; + std::array nptool_callbacks{}; + std::array callbacks{}; std::mutex m_mutex; }; } // namespace Libraries::NetCtl diff --git a/src/core/libraries/network/netctl.cpp b/src/core/libraries/network/netctl.cpp index d3f83c290..b167d2789 100644 --- a/src/core/libraries/network/netctl.cpp +++ b/src/core/libraries/network/netctl.cpp @@ -13,7 +13,6 @@ #endif #include "common/logging/log.h" -#include "common/singleton.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "core/libraries/network/net_ctl_codes.h" @@ -21,6 +20,8 @@ namespace Libraries::NetCtl { +static NetCtlInternal netctl; + int PS4_SYSV_ABI sceNetBweCheckCallbackIpcInt() { LOG_ERROR(Lib_NetCtl, "(STUBBED) called"); return ORBIS_OK; @@ -92,8 +93,7 @@ int PS4_SYSV_ABI sceNetCtlUnregisterCallbackV6() { } int PS4_SYSV_ABI sceNetCtlCheckCallback() { - auto* netctl = Common::Singleton::Instance(); - netctl->checkCallback(); + netctl.CheckCallback(); return ORBIS_OK; } @@ -298,8 +298,7 @@ int PS4_SYSV_ABI sceNetCtlRegisterCallback(OrbisNetCtlCallback func, void* arg, if (!func || !cid) { return ORBIS_NET_CTL_ERROR_INVALID_ADDR; } - auto* netctl = Common::Singleton::Instance(); - s32 result = netctl->registerCallback(func, arg); + s32 result = netctl.RegisterCallback(func, arg); if (result < 0) { return result; } else { @@ -374,8 +373,7 @@ int PS4_SYSV_ABI Func_D8DCB6973537A3DC() { } int PS4_SYSV_ABI sceNetCtlCheckCallbackForNpToolkit() { - auto* netctl = Common::Singleton::Instance(); - netctl->checkNpToolkitCallback(); + netctl.CheckNpToolkitCallback(); return ORBIS_OK; } @@ -389,8 +387,7 @@ int PS4_SYSV_ABI sceNetCtlRegisterCallbackForNpToolkit(OrbisNetCtlCallbackForNpT if (!func || !cid) { return ORBIS_NET_CTL_ERROR_INVALID_ADDR; } - auto* netctl = Common::Singleton::Instance(); - s32 result = netctl->registerNpToolkitCallback(func, arg); + s32 result = netctl.RegisterNpToolkitCallback(func, arg); if (result < 0) { return result; } else { diff --git a/src/core/libraries/network/netctl.h b/src/core/libraries/network/netctl.h index 89ba34c31..4482729a3 100644 --- a/src/core/libraries/network/netctl.h +++ b/src/core/libraries/network/netctl.h @@ -4,7 +4,7 @@ #pragma once #include "common/types.h" -#include "net_ctl_obj.h" +#include "core/libraries/network/net_ctl_obj.h" namespace Core::Loader { class SymbolsResolver; diff --git a/src/core/libraries/ngs2/ngs2_impl.cpp b/src/core/libraries/ngs2/ngs2_impl.cpp index 793435d83..b358a05f7 100644 --- a/src/core/libraries/ngs2/ngs2_impl.cpp +++ b/src/core/libraries/ngs2/ngs2_impl.cpp @@ -6,7 +6,7 @@ #include "common/logging/log.h" #include "core/libraries/error_codes.h" -#include "core/libraries/kernel/libkernel.h" +#include "core/libraries/kernel/kernel.h" using namespace Libraries::Kernel; diff --git a/src/core/libraries/np_manager/np_manager.cpp b/src/core/libraries/np_manager/np_manager.cpp index 4fff59003..00070ef89 100644 --- a/src/core/libraries/np_manager/np_manager.cpp +++ b/src/core/libraries/np_manager/np_manager.cpp @@ -1,13 +1,12 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include -#include #include "common/config.h" #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" -#include "np_manager.h" +#include "core/libraries/np_manager/np_manager.h" +#include "core/tls.h" namespace Libraries::NpManager { @@ -2519,10 +2518,7 @@ struct NpStateCallbackForNpToolkit { NpStateCallbackForNpToolkit NpStateCbForNp; int PS4_SYSV_ABI sceNpCheckCallbackForLib() { - // LOG_ERROR(Lib_NpManager, "(STUBBED) called"); - const auto* linker = Common::Singleton::Instance(); - linker->ExecuteGuest(NpStateCbForNp.func, 1, ORBIS_NP_STATE_SIGNED_OUT, - NpStateCbForNp.userdata); + Core::ExecuteGuest(NpStateCbForNp.func, 1, ORBIS_NP_STATE_SIGNED_OUT, NpStateCbForNp.userdata); return ORBIS_OK; } diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index d786647c2..a1897d047 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -25,6 +25,7 @@ int PS4_SYSV_ABI scePadConnectPort() { int PS4_SYSV_ABI scePadDeviceClassGetExtendedInformation( s32 handle, OrbisPadDeviceClassExtendedInformation* pExtInfo) { LOG_ERROR(Lib_Pad, "(STUBBED) called"); + std::memset(pExtInfo, 0, sizeof(OrbisPadDeviceClassExtendedInformation)); if (Config::getUseSpecialPad()) { pExtInfo->deviceClass = (OrbisPadDeviceClass)Config::getSpecialPadClass(); } diff --git a/src/core/libraries/rtc/rtc.cpp b/src/core/libraries/rtc/rtc.cpp index 7a46a1e31..1b8802970 100644 --- a/src/core/libraries/rtc/rtc.cpp +++ b/src/core/libraries/rtc/rtc.cpp @@ -5,11 +5,11 @@ #include "common/logging/log.h" #include "core/libraries/error_codes.h" -#include "core/libraries/kernel/libkernel.h" -#include "core/libraries/kernel/time_management.h" +#include "core/libraries/kernel/process.h" +#include "core/libraries/kernel/time.h" #include "core/libraries/libs.h" -#include "rtc.h" -#include "rtc_error.h" +#include "core/libraries/rtc/rtc.h" +#include "core/libraries/rtc/rtc_error.h" namespace Libraries::Rtc { diff --git a/src/core/libraries/system/sysmodule.cpp b/src/core/libraries/system/sysmodule.cpp index 596efc0af..d37c375bf 100644 --- a/src/core/libraries/system/sysmodule.cpp +++ b/src/core/libraries/system/sysmodule.cpp @@ -32,8 +32,8 @@ int PS4_SYSV_ABI sceSysmoduleIsCameraPreloaded() { return ORBIS_OK; } -int PS4_SYSV_ABI sceSysmoduleIsLoaded() { - LOG_ERROR(Lib_SysModule, "(STUBBED) called"); +int PS4_SYSV_ABI sceSysmoduleIsLoaded(OrbisSysModule id) { + LOG_ERROR(Lib_SysModule, "(DUMMY) called module = {}", magic_enum::enum_name(id)); return ORBIS_OK; } diff --git a/src/core/libraries/system/sysmodule.h b/src/core/libraries/system/sysmodule.h index d7a0c31b1..c9ec97ce9 100644 --- a/src/core/libraries/system/sysmodule.h +++ b/src/core/libraries/system/sysmodule.h @@ -151,7 +151,7 @@ int PS4_SYSV_ABI sceSysmoduleGetModuleHandleInternal(); int PS4_SYSV_ABI sceSysmoduleGetModuleInfoForUnwind(); int PS4_SYSV_ABI sceSysmoduleIsCalledFromSysModule(); int PS4_SYSV_ABI sceSysmoduleIsCameraPreloaded(); -int PS4_SYSV_ABI sceSysmoduleIsLoaded(); +int PS4_SYSV_ABI sceSysmoduleIsLoaded(OrbisSysModule id); int PS4_SYSV_ABI sceSysmoduleIsLoadedInternal(); int PS4_SYSV_ABI sceSysmoduleLoadModule(OrbisSysModule id); int PS4_SYSV_ABI sceSysmoduleLoadModuleByNameInternal(); diff --git a/src/core/libraries/videoout/driver.cpp b/src/core/libraries/videoout/driver.cpp index 13d67c4e7..d8013614c 100644 --- a/src/core/libraries/videoout/driver.cpp +++ b/src/core/libraries/videoout/driver.cpp @@ -2,7 +2,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include -#include #include "common/assert.h" #include "common/config.h" @@ -10,7 +9,7 @@ #include "common/thread.h" #include "core/debug_state.h" #include "core/libraries/error_codes.h" -#include "core/libraries/kernel/time_management.h" +#include "core/libraries/kernel/time.h" #include "core/libraries/videoout/driver.h" #include "core/platform.h" #include "video_core/renderer_vulkan/vk_presenter.h" diff --git a/src/core/libraries/videoout/video_out.h b/src/core/libraries/videoout/video_out.h index 7f8421fba..0680a8491 100644 --- a/src/core/libraries/videoout/video_out.h +++ b/src/core/libraries/videoout/video_out.h @@ -3,7 +3,7 @@ #pragma once -#include "core/libraries/kernel/event_queues.h" +#include "core/libraries/kernel/equeue.h" #include "core/libraries/videoout/buffer.h" namespace Core::Loader { diff --git a/src/core/linker.cpp b/src/core/linker.cpp index 2d7865c33..9592bee0f 100644 --- a/src/core/linker.cpp +++ b/src/core/linker.cpp @@ -11,27 +11,22 @@ #include "common/thread.h" #include "core/aerolib/aerolib.h" #include "core/aerolib/stubs.h" -#include "core/cpu_patches.h" -#include "core/libraries/kernel/memory_management.h" -#include "core/libraries/kernel/thread_management.h" +#include "core/libraries/kernel/memory.h" +#include "core/libraries/kernel/threads.h" #include "core/linker.h" #include "core/memory.h" #include "core/tls.h" #include "core/virtual_memory.h" -#include "debug_state.h" namespace Core { -using ExitFunc = PS4_SYSV_ABI void (*)(); - static PS4_SYSV_ABI void ProgramExitFunc() { - fmt::print("exit function called\n"); + LOG_ERROR(Core_Linker, "Exit function called"); } #ifdef ARCH_X86_64 -static PS4_SYSV_ABI void RunMainEntry(VAddr addr, EntryParams* params, ExitFunc exit_func) { - // reinterpret_cast(addr)(params, exit_func); // can't be used, stack has to have - // a specific layout +static PS4_SYSV_ABI void* RunMainEntry [[noreturn]] (EntryParams* params) { + // Start shared library modules asm volatile("andq $-16, %%rsp\n" // Align to 16 bytes "subq $8, %%rsp\n" // videoout_basic expects the stack to be misaligned @@ -47,8 +42,9 @@ static PS4_SYSV_ABI void RunMainEntry(VAddr addr, EntryParams* params, ExitFunc "jmp *%0\n" // can't use call here, as that would mangle the prepared stack. // there's no coming back : - : "r"(addr), "r"(params), "r"(exit_func) + : "r"(params->entry_addr), "r"(params), "r"(ProgramExitFunc) : "rax", "rsi", "rdi"); + UNREACHABLE(); } #endif @@ -62,10 +58,8 @@ void Linker::Execute() { } // Calculate static TLS size. - for (const auto& module : m_modules) { - static_tls_size += module->tls.image_size; - module->tls.offset = static_tls_size; - } + Module* module = m_modules[0].get(); + static_tls_size = module->tls.offset = module->tls.image_size; // Relocate all modules for (const auto& m : m_modules) { @@ -87,36 +81,17 @@ void Linker::Execute() { } } - // Init primary thread. - Common::SetCurrentThreadName("GAME_MainThread"); - DebugState.AddCurrentThreadToGuestList(); - Libraries::Kernel::pthreadInitSelfMainThread(); - EnsureThreadInitialized(true); + main_thread.Run([this, module](std::stop_token) { + Common::SetCurrentThreadName("GAME_MainThread"); + LoadSharedLibraries(); - // Start shared library modules - for (auto& m : m_modules) { - if (m->IsSharedLib()) { - m->Start(0, nullptr, nullptr); - } - } - - // Start main module. - EntryParams p{}; - p.argc = 1; - p.argv[0] = "eboot.bin"; - - for (auto& m : m_modules) { - if (!m->IsSharedLib()) { -#ifdef ARCH_X86_64 - ExecuteGuest(RunMainEntry, m->GetEntryAddress(), &p, ProgramExitFunc); -#else - UNIMPLEMENTED_MSG( - "Missing guest entrypoint implementation for target CPU architecture."); -#endif - } - } - - SetTcbBase(nullptr); + // Start main module. + EntryParams params{}; + params.argc = 1; + params.argv[0] = "eboot.bin"; + params.entry_addr = module->GetEntryAddress(); + RunMainEntry(¶ms); + }); } s32 Linker::LoadModule(const std::filesystem::path& elf_name, bool is_dynamic) { @@ -149,10 +124,9 @@ Module* Linker::FindByAddress(VAddr address) { } void Linker::Relocate(Module* module) { - module->ForEachRelocation([&](elf_relocation* rel, u32 i, bool isJmpRel) { - const u32 bit_idx = - (isJmpRel ? module->dynamic_info.relocation_table_size / sizeof(elf_relocation) : 0) + - i; + module->ForEachRelocation([&](elf_relocation* rel, u32 i, bool is_jmp_rel) { + const u32 num_relocs = module->dynamic_info.relocation_table_size / sizeof(elf_relocation); + const u32 bit_idx = (is_jmp_rel ? num_relocs : 0) + i; if (module->TestRelaBit(bit_idx)) { return; } @@ -160,7 +134,7 @@ void Linker::Relocate(Module* module) { auto symbol = rel->GetSymbol(); auto addend = rel->rel_addend; auto* symbol_table = module->dynamic_info.symbol_table; - auto* namesTlb = module->dynamic_info.str_table; + auto* names_tlb = module->dynamic_info.str_table; const VAddr rel_base_virtual_addr = module->GetBaseAddress(); const VAddr rel_virtual_addr = rel_base_virtual_addr + rel->rel_offset; @@ -216,7 +190,7 @@ void Linker::Relocate(Module* module) { break; case STB_GLOBAL: case STB_WEAK: { - rel_name = namesTlb + sym.st_name; + rel_name = names_tlb + sym.st_name; if (Resolve(rel_name, rel_sym_type, module, &symrec)) { // Only set the rela bit if the symbol was actually resolved and not stubbed. module->SetRelaBit(bit_idx); @@ -225,7 +199,7 @@ void Linker::Relocate(Module* module) { break; } default: - ASSERT_MSG(0, "unknown bind type {}", sym_bind); + UNREACHABLE_MSG("Unknown bind type {}", sym_bind); } rel_is_resolved = (symbol_virtual_addr != 0); rel_value = (rel_is_resolved ? symbol_virtual_addr + addend : 0); @@ -239,7 +213,7 @@ void Linker::Relocate(Module* module) { if (rel_is_resolved) { VirtualMemory::memory_patch(rel_virtual_addr, rel_value); } else { - LOG_INFO(Core_Linker, "function not patched! {}", rel_name); + LOG_INFO(Core_Linker, "Function not patched! {}", rel_name); } }); } @@ -310,7 +284,7 @@ void* Linker::TlsGetAddr(u64 module_index, u64 offset) { const u32 old_num_dtvs = dtv_table[1].counter; ASSERT_MSG(max_tls_index > old_num_dtvs, "Module unloading unsupported"); // Module was loaded, increase DTV table size. - DtvEntry* new_dtv_table = new DtvEntry[max_tls_index + 2]; + DtvEntry* new_dtv_table = new DtvEntry[max_tls_index + 2]{}; std::memcpy(new_dtv_table + 2, dtv_table + 2, old_num_dtvs * sizeof(DtvEntry)); new_dtv_table[0].counter = dtv_generation_counter; new_dtv_table[1].counter = max_tls_index; @@ -322,13 +296,13 @@ void* Linker::TlsGetAddr(u64 module_index, u64 offset) { } u8* addr = dtv_table[module_index + 1].pointer; + Module* module = m_modules[module_index - 1].get(); + // LOG_INFO(Core_Linker, "Got DTV addr {} from module index {} with name {}", + // fmt::ptr(addr), module_index, module->file.filename().string()); if (!addr) { // Module was just loaded by above code. Allocate TLS block for it. - Module* module = m_modules[module_index - 1].get(); const u32 init_image_size = module->tls.init_image_size; - // TODO: Determine if Windows will crash from this - u8* dest = - reinterpret_cast(ExecuteGuest(heap_api->heap_malloc, module->tls.image_size)); + u8* dest = reinterpret_cast(heap_api->heap_malloc(module->tls.image_size)); const u8* src = reinterpret_cast(module->tls.image_virtual_addr); std::memcpy(dest, src, init_image_size); std::memset(dest + init_image_size, 0, module->tls.image_size - init_image_size); @@ -338,18 +312,7 @@ void* Linker::TlsGetAddr(u64 module_index, u64 offset) { return addr + offset; } -thread_local std::once_flag init_tls_flag; - -void Linker::EnsureThreadInitialized(bool is_primary) const { - std::call_once(init_tls_flag, [this, is_primary] { -#ifdef ARCH_X86_64 - InitializeThreadPatchStack(); -#endif - InitTlsForThread(is_primary); - }); -} - -void Linker::InitTlsForThread(bool is_primary) const { +void* Linker::AllocateTlsForThread(bool is_primary) { static constexpr size_t TcbSize = 0x40; static constexpr size_t TlsAllocAlign = 0x20; const size_t total_tls_size = Common::AlignUp(static_tls_size, TlsAllocAlign) + TcbSize; @@ -370,54 +333,20 @@ void Linker::InitTlsForThread(bool is_primary) const { ASSERT_MSG(ret == 0, "Unable to allocate TLS+TCB for the primary thread"); } else { if (heap_api) { -#ifndef WIN32 - addr_out = ExecuteGuestWithoutTls(heap_api->heap_malloc, total_tls_size); + addr_out = Core::ExecuteGuest(heap_api->heap_malloc, total_tls_size); } else { addr_out = std::malloc(total_tls_size); -#else - // TODO: Windows tls malloc replacement, refer to rtld_tls_block_malloc - LOG_ERROR(Core_Linker, "TLS user malloc called, using std::malloc"); - addr_out = std::malloc(total_tls_size); - if (!addr_out) { - auto pth_id = pthread_self(); - auto handle = pthread_gethandle(pth_id); - ASSERT_MSG(addr_out, - "Cannot allocate TLS block defined for handle=%x, index=%d size=%d", - handle, pth_id, total_tls_size); - } -#endif } } + return addr_out; +} - // Initialize allocated memory and allocate DTV table. - const u32 num_dtvs = max_tls_index; - std::memset(addr_out, 0, total_tls_size); - DtvEntry* dtv_table = new DtvEntry[num_dtvs + 2]; - - // Initialize thread control block - u8* addr = reinterpret_cast(addr_out); - Tcb* tcb = reinterpret_cast(addr + static_tls_size); - tcb->tcb_self = tcb; - tcb->tcb_dtv = dtv_table; - - // Dtv[0] is the generation counter. libkernel puts their number into dtv[1] (why?) - dtv_table[0].counter = dtv_generation_counter; - dtv_table[1].counter = num_dtvs; - - // Copy init images to TLS thread blocks and map them to DTV slots. - for (u32 i = 0; i < num_static_modules; i++) { - auto* module = m_modules[i].get(); - if (module->tls.image_size == 0) { - continue; - } - u8* dest = reinterpret_cast(addr + static_tls_size - module->tls.offset); - const u8* src = reinterpret_cast(module->tls.image_virtual_addr); - std::memcpy(dest, src, module->tls.init_image_size); - tcb->tcb_dtv[module->tls.modid + 1].pointer = dest; +void Linker::FreeTlsForNonPrimaryThread(void* pointer) { + if (heap_api) { + Core::ExecuteGuest(heap_api->heap_free, pointer); + } else { + std::free(pointer); } - - // Set pointer to FS base - SetTcbBase(tcb); } void Linker::DebugDump() { @@ -425,17 +354,18 @@ void Linker::DebugDump() { const std::filesystem::path debug(log_dir / "debugdump"); std::filesystem::create_directory(debug); for (const auto& m : m_modules) { - // TODO make a folder with game id for being more unique? - const std::filesystem::path filepath(debug / m.get()->file.stem()); + Module* module = m.get(); + auto& elf = module->elf; + const std::filesystem::path filepath(debug / module->file.stem()); std::filesystem::create_directory(filepath); - m.get()->import_sym.DebugDump(filepath / "imports.txt"); - m.get()->export_sym.DebugDump(filepath / "exports.txt"); - if (m.get()->elf.IsSelfFile()) { - m.get()->elf.SelfHeaderDebugDump(filepath / "selfHeader.txt"); - m.get()->elf.SelfSegHeaderDebugDump(filepath / "selfSegHeaders.txt"); + module->import_sym.DebugDump(filepath / "imports.txt"); + module->export_sym.DebugDump(filepath / "exports.txt"); + if (elf.IsSelfFile()) { + elf.SelfHeaderDebugDump(filepath / "selfHeader.txt"); + elf.SelfSegHeaderDebugDump(filepath / "selfSegHeaders.txt"); } - m.get()->elf.ElfHeaderDebugDump(filepath / "elfHeader.txt"); - m.get()->elf.PHeaderDebugDump(filepath / "elfPHeaders.txt"); + elf.ElfHeaderDebugDump(filepath / "elfHeader.txt"); + elf.PHeaderDebugDump(filepath / "elfPHeaders.txt"); } } diff --git a/src/core/linker.h b/src/core/linker.h index fe1278d00..3a1aeb960 100644 --- a/src/core/linker.h +++ b/src/core/linker.h @@ -6,6 +6,7 @@ #include #include #include +#include "core/libraries/kernel/threads.h" #include "core/module.h" namespace Core { @@ -40,10 +41,15 @@ struct OrbisProcParam { u64 unknown1; }; +using ExitFunc = PS4_SYSV_ABI void (*)(); + +class Linker; + struct EntryParams { int argc; u32 padding; const char* argv[3]; + VAddr entry_addr; }; struct HeapAPI { @@ -79,6 +85,18 @@ public: return m_modules.at(index).get(); } + u32 MaxTlsIndex() const { + return max_tls_index; + } + + u32 GenerationCounter() const { + return dtv_generation_counter; + } + + size_t StaticTlsSize() const noexcept { + return static_tls_size; + } + void RelocateAnyImports(Module* m) { Relocate(m); for (auto& module : m_modules) { @@ -89,6 +107,14 @@ public: } } + void LoadSharedLibraries() { + for (auto& module : m_modules) { + if (module->IsSharedLib()) { + module->Start(0, nullptr, nullptr); + } + } + } + void SetHeapAPI(void* func[]) { heap_api = reinterpret_cast(func); } @@ -98,6 +124,8 @@ public: } void* TlsGetAddr(u64 module_index, u64 offset); + void* AllocateTlsForThread(bool is_primary); + void FreeTlsForNonPrimaryThread(void* pointer); s32 LoadModule(const std::filesystem::path& elf_name, bool is_dynamic = false); Module* FindByAddress(VAddr address); @@ -108,26 +136,11 @@ public: void Execute(); void DebugDump(); - template - ReturnType ExecuteGuest(PS4_SYSV_ABI ReturnType (*func)(FuncArgs...), - CallArgs&&... args) const { - // Make sure TLS is initialized for the thread before entering guest. - EnsureThreadInitialized(); - return ExecuteGuestWithoutTls(func, args...); - } - private: const Module* FindExportedModule(const ModuleInfo& m, const LibraryInfo& l); - void EnsureThreadInitialized(bool is_primary = false) const; - void InitTlsForThread(bool is_primary) const; - - template - ReturnType ExecuteGuestWithoutTls(PS4_SYSV_ABI ReturnType (*func)(FuncArgs...), - CallArgs&&... args) const { - return func(std::forward(args)...); - } MemoryManager* memory; + Libraries::Kernel::Thread main_thread; std::mutex mutex; u32 dtv_generation_counter{1}; size_t static_tls_size{}; diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 61eb421e5..f638e5e1a 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -6,7 +6,7 @@ #include "common/config.h" #include "common/debug.h" #include "core/libraries/error_codes.h" -#include "core/libraries/kernel/memory_management.h" +#include "core/libraries/kernel/memory.h" #include "core/memory.h" #include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_rasterizer.h" diff --git a/src/core/memory.h b/src/core/memory.h index 286f1c979..a9a42e1c2 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -10,7 +10,7 @@ #include "common/singleton.h" #include "common/types.h" #include "core/address_space.h" -#include "core/libraries/kernel/memory_management.h" +#include "core/libraries/kernel/memory.h" namespace Vulkan { class Rasterizer; @@ -133,6 +133,10 @@ public: rasterizer = rasterizer_; } + AddressSpace& GetAddressSpace() { + return impl; + } + u64 GetTotalDirectSize() const { return total_direct_size; } @@ -149,6 +153,13 @@ public: return impl.SystemReservedVirtualBase(); } + bool IsValidAddress(const void* addr) const noexcept { + const VAddr virtual_addr = reinterpret_cast(addr); + const auto end_it = std::prev(vma_map.end()); + const VAddr end_addr = end_it->first + end_it->second.size; + return virtual_addr >= vma_map.begin()->first && virtual_addr < end_addr; + } + bool TryWriteBacking(void* address, const void* data, u32 num_bytes); void SetupMemoryRegions(u64 flexible_size); diff --git a/src/core/module.cpp b/src/core/module.cpp index 5d3b40577..ef34f25c1 100644 --- a/src/core/module.cpp +++ b/src/core/module.cpp @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include + #include "common/alignment.h" #include "common/arch.h" #include "common/assert.h" @@ -9,10 +11,10 @@ #include "common/string_util.h" #include "core/aerolib/aerolib.h" #include "core/cpu_patches.h" -#include "core/linker.h" #include "core/loader/dwarf.h" #include "core/memory.h" #include "core/module.h" +#include "core/tls.h" namespace Core { @@ -56,6 +58,30 @@ static std::string EncodeId(u64 nVal) { return enc; } +static std::string StringToNid(std::string_view symbol) { + static constexpr std::array Salt = {0x51, 0x8D, 0x64, 0xA6, 0x35, 0xDE, 0xD8, 0xC1, + 0xE6, 0xB0, 0x39, 0xB1, 0xC3, 0xE5, 0x52, 0x30}; + std::vector input(symbol.size() + Salt.size()); + std::memcpy(input.data(), symbol.data(), symbol.size()); + std::memcpy(input.data() + symbol.size(), Salt.data(), Salt.size()); + + std::array hash; + CryptoPP::SHA1().CalculateDigest(hash.data(), input.data(), input.size()); + + u64 digest; + std::memcpy(&digest, hash.data(), sizeof(digest)); + + static constexpr std::string_view codes = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-"; + std::string dst(11, '\0'); + + for (int i = 0; i < 10; i++) { + dst[i] = codes[(digest >> (58 - i * 6)) & 0x3f]; + } + dst[10] = codes[(digest & 0xf) * 4]; + return dst; +} + Module::Module(Core::MemoryManager* memory_, const std::filesystem::path& file_, u32& max_tls_index) : memory{memory_}, file{file_}, name{file.stem().string()} { elf.Open(file); @@ -70,9 +96,8 @@ Module::~Module() = default; s32 Module::Start(size_t args, const void* argp, void* param) { LOG_INFO(Core_Linker, "Module started : {}", name); - const auto* linker = Common::Singleton::Instance(); const VAddr addr = dynamic_info.init_virtual_addr + GetBaseAddress(); - return linker->ExecuteGuest(reinterpret_cast(addr), args, argp, param); + return ExecuteGuest(reinterpret_cast(addr), args, argp, param); } void Module::LoadModuleToMemory(u32& max_tls_index) { @@ -167,9 +192,7 @@ void Module::LoadModuleToMemory(u32& max_tls_index) { tls.align = elf_pheader[i].p_align; tls.image_virtual_addr = elf_pheader[i].p_vaddr + base_virtual_addr; tls.image_size = GetAlignedSize(elf_pheader[i]); - if (tls.image_size != 0) { - tls.modid = ++max_tls_index; - } + tls.modid = ++max_tls_index; LOG_INFO(Core_Linker, "TLS virtual address = {:#x}", tls.image_virtual_addr); LOG_INFO(Core_Linker, "TLS image size = {}", tls.image_size); break; @@ -492,4 +515,15 @@ const LibraryInfo* Module::FindLibrary(std::string_view id) { return nullptr; } +void* Module::FindByName(std::string_view name) { + const auto nid_str = StringToNid(name); + const auto symbols = export_sym.GetSymbols(); + const auto it = std::ranges::find_if( + symbols, [&](const Loader::SymbolRecord& record) { return record.name.contains(nid_str); }); + if (it != symbols.end()) { + return reinterpret_cast(it->virtual_address); + } + return nullptr; +} + } // namespace Core diff --git a/src/core/module.h b/src/core/module.h index 007501f08..630c5d583 100644 --- a/src/core/module.h +++ b/src/core/module.h @@ -165,15 +165,6 @@ public: return elf.IsSharedLib(); } - void* FindByName(std::string_view name) { - const auto symbols = export_sym.GetSymbols(); - const auto it = std::ranges::find(symbols, name, &Loader::SymbolRecord::nid_name); - if (it != symbols.end()) { - return reinterpret_cast(it->virtual_address); - } - return nullptr; - } - template T GetProcParam() const noexcept { return reinterpret_cast(proc_param_virtual_addr); @@ -217,6 +208,8 @@ public: void LoadDynamicInfo(); void LoadSymbols(); + void* FindByName(std::string_view name); + OrbisKernelModuleInfoEx GetModuleInfoEx() const; const ModuleInfo* FindModule(std::string_view id); const LibraryInfo* FindLibrary(std::string_view id); diff --git a/src/core/thread.cpp b/src/core/thread.cpp new file mode 100644 index 000000000..e9c46b522 --- /dev/null +++ b/src/core/thread.cpp @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "thread.h" + +#ifdef _WIN64 +#include +#else +#include +#endif + +namespace Core { + +Thread::Thread() : native_handle{0} {} + +Thread::~Thread() {} + +int Thread::Create(ThreadFunc func, void* arg) { +#ifdef _WIN64 + native_handle = CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)func, arg, 0, nullptr); + return native_handle ? 0 : -1; +#else + pthread_t* pthr = reinterpret_cast(&native_handle); + pthread_attr_t pattr; + pthread_attr_init(&pattr); + return pthread_create(pthr, &pattr, (PthreadFunc)func, arg); +#endif +} + +void Thread::Exit() { + if (!native_handle) { + return; + } + +#ifdef _WIN64 + CloseHandle(native_handle); + native_handle = nullptr; + + // We call this assuming the thread has finished execution. + ExitThread(0); +#else + pthread_exit(nullptr); +#endif +} + +} // namespace Core \ No newline at end of file diff --git a/src/core/thread.h b/src/core/thread.h new file mode 100644 index 000000000..8665100af --- /dev/null +++ b/src/core/thread.h @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Core { + +class Thread { +public: + using ThreadFunc = void (*)(void*); + using PthreadFunc = void* (*)(void*); + + Thread(); + ~Thread(); + + int Create(ThreadFunc func, void* arg); + void Exit(); + + uintptr_t GetHandle() { + return reinterpret_cast(native_handle); + } + +private: +#if _WIN64 + void* native_handle; +#else + uintptr_t native_handle; +#endif +}; + +} // namespace Core \ No newline at end of file diff --git a/src/core/tls.cpp b/src/core/tls.cpp index eb07e7a72..9b3178171 100644 --- a/src/core/tls.cpp +++ b/src/core/tls.cpp @@ -5,6 +5,8 @@ #include "common/arch.h" #include "common/assert.h" #include "common/types.h" +#include "core/cpu_patches.h" +#include "core/libraries/kernel/threads/pthread.h" #include "core/tls.h" #ifdef _WIN32 @@ -52,8 +54,13 @@ Tcb* GetTcbBase() { // Reserve space in the 32-bit address range for allocating TCB pages. asm(".zerofill TCB_SPACE,TCB_SPACE,__guest_system,0x3FC000"); -static constexpr u64 ldt_region_base = 0x4000; -static constexpr u64 ldt_region_size = 0x3FC000; +struct LdtPage { + void* tcb; + u16 index; +}; + +static constexpr uintptr_t ldt_region_base = 0x4000; +static constexpr size_t ldt_region_size = 0x3FC000; static constexpr u16 ldt_block_size = 0x1000; static constexpr u16 ldt_index_base = 8; static constexpr u16 ldt_index_total = (ldt_region_size - ldt_region_base) / ldt_block_size; @@ -61,11 +68,13 @@ static constexpr u16 ldt_index_total = (ldt_region_size - ldt_region_base) / ldt static boost::icl::interval_set free_ldts{}; static std::mutex free_ldts_lock; static std::once_flag ldt_region_init_flag; +static pthread_key_t ldt_page_slot = 0; -static u16 GetLdtIndex() { - sel_t selector; - asm volatile("mov %%fs, %0" : "=r"(selector)); - return selector.index; +static void FreeLdtPage(void* raw) { + const auto* ldt_page = static_cast(raw); + + std::unique_lock lock{free_ldts_lock}; + free_ldts += ldt_page->index; } static void InitLdtRegion() { @@ -76,11 +85,20 @@ static void InitLdtRegion() { free_ldts += boost::icl::interval::right_open(ldt_index_base, ldt_index_base + ldt_index_total); + ASSERT_MSG(pthread_key_create(&ldt_page_slot, FreeLdtPage) == 0, + "Failed to create thread LDT page key: {}", errno); } -static void** SetupThreadLdt() { +void SetTcbBase(void* image_address) { std::call_once(ldt_region_init_flag, InitLdtRegion); + auto* ldt_page = static_cast(pthread_getspecific(ldt_page_slot)); + if (ldt_page != nullptr) { + // Update TCB pointer in existing page. + ldt_page->tcb = image_address; + return; + } + // Allocate a new LDT index for the current thread. u16 ldt_index; { @@ -89,10 +107,12 @@ static void** SetupThreadLdt() { ldt_index = first(*free_ldts.begin()); free_ldts -= ldt_index; } - const u64 addr = ldt_region_base + (ldt_index - ldt_index_base) * ldt_block_size; + + const uintptr_t addr = ldt_region_base + (ldt_index - ldt_index_base) * ldt_block_size; // Create an LDT entry for the TCB. - const ldt_entry ldt{.data{ + ldt_entry ldt{}; + ldt.data = { .base00 = static_cast(addr), .base16 = static_cast(addr >> 16), .base24 = static_cast(addr >> 24), @@ -103,34 +123,27 @@ static void** SetupThreadLdt() { .present = 1, // Segment present .stksz = DESC_DATA_32B, .granular = DESC_GRAN_BYTE, - }}; + }; int ret = i386_set_ldt(ldt_index, &ldt, 1); ASSERT_MSG(ret == ldt_index, - "Failed to set LDT for TLS area: expected {}, but syscall returned {}", ldt_index, - ret); + "Failed to set LDT {} at {:#x} for TLS area: syscall returned {}, errno {}", + ldt_index, addr, ret, errno); // Set the FS segment to the created LDT. - const sel_t sel{ + const sel_t new_selector{ .rpl = USER_PRIV, .ti = SEL_LDT, .index = ldt_index, }; - asm volatile("mov %0, %%fs" ::"r"(sel)); + asm volatile("mov %0, %%fs" ::"r"(new_selector)); - return reinterpret_cast(addr); -} + // Store the TCB base pointer and index in the created LDT area. + ldt_page = reinterpret_cast(addr); + ldt_page->tcb = image_address; + ldt_page->index = ldt_index; -static void FreeThreadLdt() { - std::unique_lock lock{free_ldts_lock}; - free_ldts += GetLdtIndex(); -} - -void SetTcbBase(void* image_address) { - if (image_address != nullptr) { - *SetupThreadLdt() = image_address; - } else { - FreeThreadLdt(); - } + ASSERT_MSG(pthread_setspecific(ldt_page_slot, ldt_page) == 0, + "Failed to store thread LDT page pointer: {}", errno); } Tcb* GetTcbBase() { @@ -181,4 +194,15 @@ Tcb* GetTcbBase() { #endif +thread_local std::once_flag init_tls_flag; + +void EnsureThreadInitialized() { + std::call_once(init_tls_flag, [] { +#ifdef ARCH_X86_64 + InitializeThreadPatchStack(); +#endif + SetTcbBase(Libraries::Kernel::g_curthread->tcb); + }); +} + } // namespace Core diff --git a/src/core/tls.h b/src/core/tls.h index f5bf33184..4df9e4ace 100644 --- a/src/core/tls.h +++ b/src/core/tls.h @@ -12,7 +12,7 @@ class CodeGenerator; namespace Core { union DtvEntry { - size_t counter; + std::size_t counter; u8* pointer; }; @@ -33,4 +33,13 @@ void SetTcbBase(void* image_address); /// Retrieves Tcb structure for the calling thread. Tcb* GetTcbBase(); +/// Makes sure TLS is initialized for the thread before entering guest. +void EnsureThreadInitialized(); + +template +ReturnType ExecuteGuest(PS4_SYSV_ABI ReturnType (*func)(FuncArgs...), CallArgs&&... args) { + EnsureThreadInitialized(); + return func(std::forward(args)...); +} + } // namespace Core diff --git a/src/emulator.cpp b/src/emulator.cpp index d9d32ec1d..24f5907a1 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -29,7 +29,6 @@ #include "core/file_sys/fs.h" #include "core/libraries/disc_map/disc_map.h" #include "core/libraries/fiber/fiber.h" -#include "core/libraries/kernel/thread_management.h" #include "core/libraries/libc_internal/libc_internal.h" #include "core/libraries/libs.h" #include "core/libraries/ngs2/ngs2.h" @@ -222,7 +221,6 @@ void Emulator::Run(const std::filesystem::path& file) { VideoCore::SetOutputDir(mount_captures_dir, id); // Initialize kernel and library facilities. - Libraries::Kernel::init_pthreads(); Libraries::InitHLELibs(&linker->GetHLESymbols()); // Load the module with the linker @@ -257,9 +255,7 @@ void Emulator::Run(const std::filesystem::path& file) { } #endif - // start execution - std::jthread mainthread = - std::jthread([this](std::stop_token stop_token) { linker->Execute(); }); + linker->Execute(); window->initTimers(); while (window->isOpen()) { diff --git a/src/input/controller.cpp b/src/input/controller.cpp index 2187608e5..cd7d3d8af 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -4,7 +4,7 @@ #include "controller.h" #include "common/assert.h" -#include "core/libraries/kernel/time_management.h" +#include "core/libraries/kernel/time.h" #include "core/libraries/pad/pad.h" #include diff --git a/src/shader_recompiler/frontend/translate/data_share.cpp b/src/shader_recompiler/frontend/translate/data_share.cpp index a453023fc..adc2bbbc2 100644 --- a/src/shader_recompiler/frontend/translate/data_share.cpp +++ b/src/shader_recompiler/frontend/translate/data_share.cpp @@ -68,10 +68,9 @@ void Translator::V_READFIRSTLANE_B32(const GcnInst& inst) { } void Translator::V_READLANE_B32(const GcnInst& inst) { - const IR::ScalarReg dst{inst.dst[0].code}; const IR::U32 value{GetSrc(inst.src[0])}; const IR::U32 lane{GetSrc(inst.src[1])}; - ir.SetScalarReg(dst, ir.ReadLane(value, lane)); + SetDst(inst.dst[0], ir.ReadLane(value, lane)); } void Translator::V_WRITELANE_B32(const GcnInst& inst) { @@ -155,7 +154,7 @@ void Translator::DS_SWIZZLE_B32(const GcnInst& inst) { const u8 offset0 = inst.control.ds.offset0; const u8 offset1 = inst.control.ds.offset1; const IR::U32 src{GetSrc(inst.src[1])}; - ASSERT(offset1 & 0x80); + // ASSERT(offset1 & 0x80); const IR::U32 lane_id = ir.LaneId(); const IR::U32 id_in_group = ir.BitwiseAnd(lane_id, ir.Imm32(0b11)); const IR::U32 base = ir.ShiftLeftLogical(id_in_group, ir.Imm32(1)); diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index 3c359b8df..12b5de436 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -570,7 +570,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spansrc_sel == DmaDataSrc::Gds && dma_data->dst_sel == DmaDataDst::Memory) { - LOG_WARNING(Render_Vulkan, "GDS memory read"); + // LOG_WARNING(Render_Vulkan, "GDS memory read"); } else if (dma_data->src_sel == DmaDataSrc::Memory && dma_data->dst_sel == DmaDataDst::Memory) { rasterizer->InlineData(dma_data->DstAddress(), diff --git a/src/video_core/page_manager.cpp b/src/video_core/page_manager.cpp index a49fff43a..8c20ee6ed 100644 --- a/src/video_core/page_manager.cpp +++ b/src/video_core/page_manager.cpp @@ -7,6 +7,7 @@ #include "common/assert.h" #include "common/error.h" #include "common/signal_context.h" +#include "core/memory.h" #include "core/signals.h" #include "video_core/page_manager.h" #include "video_core/renderer_vulkan/vk_rasterizer.h" @@ -145,15 +146,11 @@ struct PageManager::Impl { ASSERT_MSG(owned_ranges.find(address) != owned_ranges.end(), "Attempted to track non-GPU memory at address {:#x}, size {:#x}.", address, size); -#ifdef _WIN32 - DWORD prot = allow_write ? PAGE_READWRITE : PAGE_READONLY; - DWORD old_prot{}; - BOOL result = VirtualProtect(std::bit_cast(address), size, prot, &old_prot); - ASSERT_MSG(result != 0, "Region protection failed"); -#else - mprotect(reinterpret_cast(address), size, - PROT_READ | (allow_write ? PROT_WRITE : 0)); -#endif + auto* memory = Core::Memory::Instance(); + auto& impl = memory->GetAddressSpace(); + impl.Protect(address, size, + allow_write ? Core::MemoryPermission::ReadWrite + : Core::MemoryPermission::Read); } static bool GuestFaultSignalHandler(void* context, void* fault_address) { diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index b45025d53..0e8dd7ccc 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -99,7 +99,7 @@ ImageId TextureCache::ResolveDepthOverlap(const ImageInfo& requested_info, Image if (cache_info.resources == requested_info.resources) { return cache_image_id; } else { - UNREACHABLE(); + // UNREACHABLE(); } } diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index ef9c0efb7..96970bfc8 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -36,8 +36,8 @@ static constexpr u32 MaxInvalidateDist = 12_MB; class TextureCache { struct Traits { using Entry = boost::container::small_vector; - static constexpr size_t AddressSpaceBits = 39; - static constexpr size_t FirstLevelBits = 9; + static constexpr size_t AddressSpaceBits = 40; + static constexpr size_t FirstLevelBits = 10; static constexpr size_t PageBits = 20; }; using PageTable = MultiLevelPageTable; From f9ae945a5575324c4875b2d635a73d2038e29b07 Mon Sep 17 00:00:00 2001 From: psucien Date: Thu, 21 Nov 2024 22:23:09 +0100 Subject: [PATCH 041/549] hot-fix: clang-format --- src/core/libraries/audio3d/audio3d.cpp | 2 +- src/core/libraries/kernel/threads/event_flag.cpp | 2 +- src/core/libraries/kernel/threads/mutex.cpp | 2 +- src/core/libraries/kernel/threads/pthread.cpp | 5 ++--- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/core/libraries/audio3d/audio3d.cpp b/src/core/libraries/audio3d/audio3d.cpp index 2978445e8..8aeb88da8 100644 --- a/src/core/libraries/audio3d/audio3d.cpp +++ b/src/core/libraries/audio3d/audio3d.cpp @@ -111,7 +111,7 @@ int PS4_SYSV_ABI sceAudio3dBedWrite(OrbisAudio3dPortId uiPortId, unsigned int ui OrbisAudio3dFormat eFormat, const void* pBuffer, unsigned int uiNumSamples) { LOG_TRACE(Lib_Audio3d, "uiPortId = {}, uiNumChannels = {}, uiNumSamples = {}", uiPortId, - uiNumChannels, uiNumSamples); + uiNumChannels, uiNumSamples); return ORBIS_OK; } diff --git a/src/core/libraries/kernel/threads/event_flag.cpp b/src/core/libraries/kernel/threads/event_flag.cpp index 134bd5491..b0f08b91c 100644 --- a/src/core/libraries/kernel/threads/event_flag.cpp +++ b/src/core/libraries/kernel/threads/event_flag.cpp @@ -31,7 +31,7 @@ public: EventFlagInternal(const std::string& name, ThreadMode thread_mode, QueueMode queue_mode, uint64_t bits) - : m_name(name), m_thread_mode(thread_mode), m_queue_mode(queue_mode), m_bits(bits){}; + : m_name(name), m_thread_mode(thread_mode), m_queue_mode(queue_mode), m_bits(bits) {}; int Wait(u64 bits, WaitMode wait_mode, ClearMode clear_mode, u64* result, u32* ptr_micros) { std::unique_lock lock{m_mutex}; diff --git a/src/core/libraries/kernel/threads/mutex.cpp b/src/core/libraries/kernel/threads/mutex.cpp index 669eb3bda..2e3e689fa 100644 --- a/src/core/libraries/kernel/threads/mutex.cpp +++ b/src/core/libraries/kernel/threads/mutex.cpp @@ -216,7 +216,7 @@ int PthreadMutex::Lock(const OrbisKernelTimespec* abstime, u64 usec) { } } } - + int ret = 0; if (abstime == nullptr) { m_lock.lock(); diff --git a/src/core/libraries/kernel/threads/pthread.cpp b/src/core/libraries/kernel/threads/pthread.cpp index 51d436d44..28e2be06e 100644 --- a/src/core/libraries/kernel/threads/pthread.cpp +++ b/src/core/libraries/kernel/threads/pthread.cpp @@ -430,12 +430,11 @@ int PS4_SYSV_ABI scePthreadSetprio(PthreadT thread, int prio) { auto* thread_state = ThrState::Instance(); if (thread == g_curthread) { g_curthread->lock.lock(); - } else if (int ret = thread_state->FindThread(thread, /*include dead*/0)) { + } else if (int ret = thread_state->FindThread(thread, /*include dead*/ 0)) { return ret; } - if (thread->attr.sched_policy == SchedPolicy::Other || - thread->attr.prio == prio) { + if (thread->attr.sched_policy == SchedPolicy::Other || thread->attr.prio == prio) { thread->attr.prio = prio; ret = 0; } else { From 8f2d71d4587ee17668f9460b00342d5e88bfaac4 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Fri, 22 Nov 2024 06:55:55 +0100 Subject: [PATCH 042/549] Skip GUI if launching a game directly (#1507) * Skip GUI if launching a game directly * Rerun jobs 2 * Added --show-gui, --help, open game by ID and more * Clang-format * Added non-GUI related upgrades to SDL version * clang-format * Added --fullscreen argument * Update --help text --- src/main.cpp | 109 ++++++++++++++++++++++++++++++---- src/qt_gui/main.cpp | 142 ++++++++++++++++++++++++++++++++++++++------ 2 files changed, 222 insertions(+), 29 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index de1d92326..b12965677 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,13 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "functional" +#include "iostream" +#include "string" +#include "unordered_map" + #include +#include "common/config.h" #include "common/memory_patcher.h" #include "emulator.h" @@ -14,26 +20,105 @@ int main(int argc, char* argv[]) { SetConsoleOutputCP(CP_UTF8); #endif + bool has_game_argument = false; + std::string game_path; + + // Map of argument strings to lambda functions + std::unordered_map> arg_map = { + {"-h", + [&](int&) { + std::cout << "Usage: shadps4 [options] \n" + "Options:\n" + " -g, --game Specify game path to launch\n" + " -p, --patch Apply specified patch file\n" + " -f, --fullscreen Specify window initial fullscreen " + "state. Does not overwrite the config file." + " -h, --help Display this help message\n"; + exit(0); + }}, + {"--help", [&](int& i) { arg_map["-h"](i); }}, + + {"-g", + [&](int& i) { + if (i + 1 < argc) { + game_path = argv[++i]; + has_game_argument = true; + } else { + std::cerr << "Error: Missing argument for -g/--game\n"; + exit(1); + } + }}, + {"--game", [&](int& i) { arg_map["-g"](i); }}, + + {"-p", + [&](int& i) { + if (i + 1 < argc) { + MemoryPatcher::patchFile = argv[++i]; + } else { + std::cerr << "Error: Missing argument for -p/--patch\n"; + exit(1); + } + }}, + {"--patch", [&](int& i) { arg_map["-p"](i); }}, + {"-f", + [&](int& i) { + if (++i >= argc) { + std::cerr << "Error: Missing argument for -f/--fullscreen\n"; + exit(1); + } + std::string f_param(argv[i]); + bool is_fullscreen; + if (f_param == "true") { + is_fullscreen = true; + } else if (f_param == "false") { + is_fullscreen = false; + } else { + std::cerr + << "Error: Invalid argument for -f/--fullscreen. Use 'true' or 'false'.\n"; + exit(1); + } + // Set fullscreen mode without saving it to config file + Config::setFullscreenMode(is_fullscreen); + }}, + {"--fullscreen", [&](int& i) { arg_map["-f"](i); }}, + }; + if (argc == 1) { - fmt::print("Usage: {} \n", argv[0]); - return -1; - } - // check if eboot file exists - if (!std::filesystem::exists(argv[1])) { - fmt::print("Eboot.bin file not found\n"); + int dummy = 0; // one does not simply pass 0 directly + arg_map.at("-h")(dummy); return -1; } - for (int i = 0; i < argc; i++) { - std::string curArg = argv[i]; - if (curArg == "-p") { - std::string patchFile = argv[i + 1]; - MemoryPatcher::patchFile = patchFile; + // Parse command-line arguments using the map + for (int i = 1; i < argc; ++i) { + std::string cur_arg = argv[i]; + auto it = arg_map.find(cur_arg); + if (it != arg_map.end()) { + it->second(i); // Call the associated lambda function + } else if (i == argc - 1 && !has_game_argument) { + // Assume the last argument is the game file if not specified via -g/--game + game_path = argv[i]; + has_game_argument = true; + } else { + std::cerr << "Unknown argument: " << cur_arg << ", see --help for info.\n"; + return 1; } } + if (!has_game_argument) { + std::cerr << "Error: Please provide a game path or ID.\n"; + exit(1); + } + + // Check if the game path or ID exists + if (!std::filesystem::exists(game_path)) { + std::cerr << "Error: Game file not found\n"; + return -1; + } + + // Run the emulator with the specified game Core::Emulator emulator; - emulator.Run(argv[1]); + emulator.Run(game_path); return 0; } diff --git a/src/qt_gui/main.cpp b/src/qt_gui/main.cpp index da8804f69..7f9b29200 100644 --- a/src/qt_gui/main.cpp +++ b/src/qt_gui/main.cpp @@ -1,6 +1,9 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "iostream" +#include "unordered_map" + #include "common/config.h" #include "common/memory_patcher.h" #include "core/file_sys/fs.h" @@ -26,10 +29,90 @@ int main(int argc, char* argv[]) { const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); Config::load(user_dir / "config.toml"); - // Check if elf or eboot.bin path was passed as a command line argument bool has_command_line_argument = argc > 1; + bool show_gui = false, has_game_argument = false; + std::string gamePath; - // Check if the game install directory is set + // Map of argument strings to lambda functions + std::unordered_map> arg_map = { + {"-h", + [&](int&) { + std::cout << "Usage: shadps4 [options]\n" + "Options:\n" + " No arguments: Opens the GUI.\n" + " -g, --game Specify or " + " to launch\n" + " -p, --patch Apply specified patch file\n" + " -s, --show-gui Show the GUI\n" + " -f, --fullscreen Specify window initial fullscreen " + "state. Does not overwrite the config file." + " -h, --help Display this help message\n"; + exit(0); + }}, + {"--help", [&](int& i) { arg_map["-h"](i); }}, // Redirect --help to -h + + {"-s", [&](int&) { show_gui = true; }}, + {"--show-gui", [&](int& i) { arg_map["-s"](i); }}, + + {"-g", + [&](int& i) { + if (i + 1 < argc) { + gamePath = argv[++i]; + has_game_argument = true; + } else { + std::cerr << "Error: Missing argument for -g/--game\n"; + exit(1); + } + }}, + {"--game", [&](int& i) { arg_map["-g"](i); }}, + + {"-p", + [&](int& i) { + if (i + 1 < argc) { + MemoryPatcher::patchFile = argv[++i]; + } else { + std::cerr << "Error: Missing argument for -p\n"; + exit(1); + } + }}, + {"--patch", [&](int& i) { arg_map["-p"](i); }}, + {"-f", + [&](int& i) { + if (++i >= argc) { + std::cerr + << "Error: Invalid argument for -f/--fullscreen. Use 'true' or 'false'.\n"; + exit(1); + } + std::string f_param(argv[i]); + bool is_fullscreen; + if (f_param == "true") { + is_fullscreen = true; + } else if (f_param == "false") { + is_fullscreen = false; + } else { + std::cerr + << "Error: Invalid argument for -f/--fullscreen. Use 'true' or 'false'.\n"; + exit(1); + } + // Set fullscreen mode without saving it to config file + Config::setFullscreenMode(is_fullscreen); + }}, + {"--fullscreen", [&](int& i) { arg_map["-f"](i); }}, + }; + + // Parse command-line arguments using the map + for (int i = 1; i < argc; ++i) { + std::string cur_arg = argv[i]; + auto it = arg_map.find(cur_arg); + if (it != arg_map.end()) { + it->second(i); // Call the associated lambda function + } else { + std::cerr << "Unknown argument: " << cur_arg << ", see --help for info.\n"; + return 1; + } + } + + // If no game directory is set and no command line argument, prompt for it if (Config::getGameInstallDirs().empty() && !has_command_line_argument) { GameInstallDialog dlg; dlg.exec(); @@ -40,21 +123,46 @@ int main(int argc, char* argv[]) { // Initialize the main window MainWindow* m_main_window = new MainWindow(nullptr); - m_main_window->Init(); - - // Check for command line arguments - if (has_command_line_argument) { - Core::Emulator emulator; - for (int i = 0; i < argc; i++) { - std::string curArg = argv[i]; - if (curArg == "-p") { - std::string patchFile = argv[i + 1]; - MemoryPatcher::patchFile = patchFile; - } - } - emulator.Run(argv[1]); + if ((has_command_line_argument && show_gui) || !has_command_line_argument) { + m_main_window->Init(); } - // Run the Qt application + if (has_command_line_argument && !has_game_argument) { + std::cerr << "Error: Please provide a game path or ID.\n"; + exit(1); + } + + // Process game path or ID if provided + if (has_game_argument) { + std::filesystem::path game_file_path(gamePath); + + // Check if the provided path is a valid file + if (!std::filesystem::exists(game_file_path)) { + // If not a file, treat it as a game ID and search in install directories + bool game_found = false; + for (const auto& install_dir : Config::getGameInstallDirs()) { + auto potential_game_path = install_dir / gamePath / "eboot.bin"; + if (std::filesystem::exists(potential_game_path)) { + game_file_path = potential_game_path; + game_found = true; + break; + } + } + if (!game_found) { + std::cerr << "Error: Game ID or file path not found: " << gamePath << std::endl; + return 1; + } + } + + // Run the emulator with the resolved game path + Core::Emulator emulator; + emulator.Run(game_file_path.string()); + if (!show_gui) { + return 0; // Exit after running the emulator without showing the GUI + } + } + + // Show the main window and run the Qt application + m_main_window->show(); return a.exec(); -} +} \ No newline at end of file From 8c9d7d1a08305cc400f8b0035998f4c812cc4ec9 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Fri, 22 Nov 2024 12:42:53 +0200 Subject: [PATCH 043/549] Port libpngdec to libpng (#1555) * intial try to include libpng * fixing libpng cmake * cleanup structs and error codes * building libpng , destroying pkg ;/ * fixed pkg with zlib_comp mode * attemp to fix ci * rewrote png encoder with libpng * small corrections * clang fix * clang-fix? * take alpha value from decode parameters * more cleanup * fix stride calculation * libpng: avoid unnecessary allocation in decoding * libpng: interlaced support * libpng: lowered log level * revert wrong merge --------- Co-authored-by: Vinicius Rangel --- .gitmodules | 3 + CMakeLists.txt | 6 +- externals/CMakeLists.txt | 13 ++ externals/libpng | 1 + src/core/file_format/pkg.cpp | 17 +- src/core/libraries/error_codes.h | 12 +- src/core/libraries/libpng/pngdec.cpp | 257 +++++++++++++++++++-------- src/core/libraries/libpng/pngdec.h | 65 +++---- 8 files changed, 257 insertions(+), 117 deletions(-) create mode 160000 externals/libpng diff --git a/.gitmodules b/.gitmodules index 07d1d4ef7..fb859c87d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -102,3 +102,6 @@ [submodule "externals/LibAtrac9"] path = externals/LibAtrac9 url = https://github.com/shadps4-emu/ext-LibAtrac9.git +[submodule "externals/libpng"] + path = externals/libpng + url = https://github.com/pnggroup/libpng diff --git a/CMakeLists.txt b/CMakeLists.txt index e135794ec..a967c540c 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -830,7 +830,7 @@ endif() create_target_directory_groups(shadps4) -target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half) +target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half zlib-ng::zlib png_static) target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::SPIRV glslang::glslang SDL3::SDL3 pugixml::pugixml) target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h") @@ -860,9 +860,9 @@ if (NOT ENABLE_QT_GUI) endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND MSVC) - target_link_libraries(shadps4 PRIVATE cryptoppwin zlib-ng::zlib) + target_link_libraries(shadps4 PRIVATE cryptoppwin) else() - target_link_libraries(shadps4 PRIVATE cryptopp::cryptopp zlib-ng::zlib) + target_link_libraries(shadps4 PRIVATE cryptopp::cryptopp) endif() if (ENABLE_QT_GUI) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 17d710878..8d080acc3 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -58,8 +58,10 @@ if (NOT TARGET zlib-ng::zlib) set(WITH_GTEST OFF) set(WITH_NEW_STRATEGIES ON) set(WITH_NATIVE_INSTRUCTIONS ON) + set(ZLIB_COMPAT ON CACHE BOOL "" FORCE) add_subdirectory(zlib-ng) add_library(zlib-ng::zlib ALIAS zlib) + add_library(ZLIB::ZLIB ALIAS zlib) endif() # SDL3 @@ -153,6 +155,17 @@ if (NOT TARGET half::half) add_library(half::half ALIAS half) endif() +# libpng +set(PNG_SHARED OFF CACHE BOOL "" FORCE) +set(PNG_STATIC ON CACHE BOOL "" FORCE) +set(PNG_TESTS OFF CACHE BOOL "" FORCE) +set(PNG_TOOLS OFF CACHE BOOL "" FORCE) +set(SKIP_INSTALL_ALL OFF CACHE BOOL "" FORCE) +set(ZLIB_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/zlib-ng" CACHE STRING "" FORCE) +set(ZLIB_LIBRARY "${CMAKE_CURRENT_BINARY_DIR}/zlib-ng/zlibstatic-ngd" CACHE STRING "" FORCE) +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/libpng/zlib.h" "#include \"../zlib-ng/zlib.h\"") +add_subdirectory(libpng) + if (APPLE) # date if (NOT TARGET date::date-tz) diff --git a/externals/libpng b/externals/libpng new file mode 160000 index 000000000..c1cc0f3f4 --- /dev/null +++ b/externals/libpng @@ -0,0 +1 @@ +Subproject commit c1cc0f3f4c3d4abd11ca68c59446a29ff6f95003 diff --git a/src/core/file_format/pkg.cpp b/src/core/file_format/pkg.cpp index 0ae9f57eb..a6b5eb9a8 100644 --- a/src/core/file_format/pkg.cpp +++ b/src/core/file_format/pkg.cpp @@ -1,31 +1,30 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include +#include #include "common/io_file.h" #include "common/logging/formatter.h" #include "core/file_format/pkg.h" #include "core/file_format/pkg_type.h" -static void DecompressPFSC(std::span compressed_data, - std::span decompressed_data) { - zng_stream decompressStream; +static void DecompressPFSC(std::span compressed_data, std::span decompressed_data) { + z_stream decompressStream; decompressStream.zalloc = Z_NULL; decompressStream.zfree = Z_NULL; decompressStream.opaque = Z_NULL; - if (zng_inflateInit(&decompressStream) != Z_OK) { + if (inflateInit(&decompressStream) != Z_OK) { // std::cerr << "Error initializing zlib for deflation." << std::endl; } decompressStream.avail_in = compressed_data.size(); - decompressStream.next_in = reinterpret_cast(compressed_data.data()); + decompressStream.next_in = reinterpret_cast(compressed_data.data()); decompressStream.avail_out = decompressed_data.size(); - decompressStream.next_out = reinterpret_cast(decompressed_data.data()); + decompressStream.next_out = reinterpret_cast(decompressed_data.data()); - if (zng_inflate(&decompressStream, Z_FINISH)) { + if (inflate(&decompressStream, Z_FINISH)) { } - if (zng_inflateEnd(&decompressStream) != Z_OK) { + if (inflateEnd(&decompressStream) != Z_OK) { // std::cerr << "Error ending zlib inflate" << std::endl; } } diff --git a/src/core/libraries/error_codes.h b/src/core/libraries/error_codes.h index 6bbdc3080..280ddf1cb 100644 --- a/src/core/libraries/error_codes.h +++ b/src/core/libraries/error_codes.h @@ -593,7 +593,6 @@ constexpr int ORBIS_VIDEODEC2_ERROR_INVALID_SEQUENCE = 0x811D0303; constexpr int ORBIS_VIDEODEC2_ERROR_FATAL_STREAM = 0x811D0304; // Videodec library - constexpr int ORBIS_VIDEODEC_ERROR_API_FAIL = 0x80C10000; constexpr int ORBIS_VIDEODEC_ERROR_CODEC_TYPE = 0x80C10001; constexpr int ORBIS_VIDEODEC_ERROR_STRUCT_SIZE = 0x80C10002; @@ -616,3 +615,14 @@ constexpr int ORBIS_VIDEODEC_ERROR_MISMATCH_SPEC = 0x80C10012; constexpr int ORBIS_VIDEODEC_ERROR_INVALID_SEQUENCE = 0x80C10013; constexpr int ORBIS_VIDEODEC_ERROR_FATAL_STREAM = 0x80C10014; constexpr int ORBIS_VIDEODEC_ERROR_FATAL_STATE = 0x80C10015; + +// PngDec library +constexpr int ORBIS_PNG_DEC_ERROR_INVALID_ADDR = 0x80690001; +constexpr int ORBIS_PNG_DEC_ERROR_INVALID_SIZE = 0x80690002; +constexpr int ORBIS_PNG_DEC_ERROR_INVALID_PARAM = 0x80690003; +constexpr int ORBIS_PNG_DEC_ERROR_INVALID_HANDLE = 0x80690004; +constexpr int ORBIS_PNG_DEC_ERROR_INVALID_WORK_MEMORY = 0x80690005; +constexpr int ORBIS_PNG_DEC_ERROR_INVALID_DATA = 0x80690010; +constexpr int ORBIS_PNG_DEC_ERROR_UNSUPPORT_DATA = 0x80690011; +constexpr int ORBIS_PNG_DEC_ERROR_DECODE_ERROR = 0x80690012; +constexpr int ORBIS_PNG_DEC_ERROR_FATAL = 0x80690020; \ No newline at end of file diff --git a/src/core/libraries/libpng/pngdec.cpp b/src/core/libraries/libpng/pngdec.cpp index 3a5d1ba71..fb9fae4d6 100644 --- a/src/core/libraries/libpng/pngdec.cpp +++ b/src/core/libraries/libpng/pngdec.cpp @@ -1,55 +1,47 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include "common/assert.h" #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" -#include "externals/stb_image.h" #include "pngdec.h" namespace Libraries::PngDec { -void setImageInfoParams(OrbisPngDecImageInfo* imageInfo, int width, int height, int channels, - bool isInterlaced, bool isTransparent) { - if (imageInfo != nullptr) { - imageInfo->imageWidth = width; - imageInfo->imageHeight = height; - imageInfo->bitDepth = 8; // always 8? - switch (channels) { // clut missing - case 1: - imageInfo->colorSpace = OrbisPngDecColorSpace::ORBIS_PNG_DEC_COLOR_SPACE_GRAYSCALE; - break; - case 2: - imageInfo->colorSpace = - OrbisPngDecColorSpace::ORBIS_PNG_DEC_COLOR_SPACE_GRAYSCALE_ALPHA; - break; - case 3: - imageInfo->colorSpace = OrbisPngDecColorSpace::ORBIS_PNG_DEC_COLOR_SPACE_RGB; - break; - case 4: - imageInfo->colorSpace = OrbisPngDecColorSpace::ORBIS_PNG_DEC_COLOR_SPACE_RGBA; - break; - default: - imageInfo->colorSpace = OrbisPngDecColorSpace::ORBIS_PNG_DEC_COLOR_SPACE_RGB; - break; - } - imageInfo->imageFlag = 0; - if (isInterlaced) { - imageInfo->imageFlag |= ORBIS_PNG_DEC_IMAGE_FLAG_ADAM7_INTERLACE; - } - if (isTransparent) { - imageInfo->imageFlag |= ORBIS_PNG_DEC_IMAGE_FLAG_TRNS_CHUNK_EXIST; - } +struct PngHandler { + png_structp png_ptr; + png_infop info_ptr; +}; + +static inline OrbisPngDecColorSpace MapPngColor(int color) { + switch (color) { + case PNG_COLOR_TYPE_GRAY: + return OrbisPngDecColorSpace::ORBIS_PNG_DEC_COLOR_SPACE_GRAYSCALE; + + case PNG_COLOR_TYPE_GRAY_ALPHA: + return OrbisPngDecColorSpace::ORBIS_PNG_DEC_COLOR_SPACE_GRAYSCALE_ALPHA; + + case PNG_COLOR_TYPE_PALETTE: + return OrbisPngDecColorSpace::ORBIS_PNG_DEC_COLOR_SPACE_CLUT; + + case PNG_COLOR_TYPE_RGB: + return OrbisPngDecColorSpace::ORBIS_PNG_DEC_COLOR_SPACE_RGB; + + case PNG_COLOR_TYPE_RGB_ALPHA: + return OrbisPngDecColorSpace::ORBIS_PNG_DEC_COLOR_SPACE_RGBA; } + + UNREACHABLE_MSG("unknown png color type"); } -bool checktRNS(const u8* png_raw, int size) { - for (int i = 30; i < size - 4; i += 4) { - if (std::memcmp(png_raw + i, "tRNS", 4) == 0) { - return true; - } - } - return false; +void PngDecError(png_structp png_ptr, png_const_charp error_message) { + LOG_ERROR(Lib_Png, "PNG error {}", error_message); +} + +void PngDecWarning(png_structp png_ptr, png_const_charp error_message) { + LOG_ERROR(Lib_Png, "PNG warning {}", error_message); } s32 PS4_SYSV_ABI scePngDecCreate(const OrbisPngDecCreateParam* param, void* memoryAddress, @@ -66,11 +58,21 @@ s32 PS4_SYSV_ABI scePngDecCreate(const OrbisPngDecCreateParam* param, void* memo LOG_ERROR(Lib_Png, "Invalid size! width = {}", param->maxImageWidth); return ORBIS_PNG_DEC_ERROR_INVALID_SIZE; } - const OrbisPngDecCreateParam* nextParam = param + 1; - int ret = (8 << (reinterpret_cast(nextParam) & 0x1f)) * - (param->maxImageWidth + 0x47U & 0xfffffff8) + - 0xd000; - *handle = reinterpret_cast(ret); + auto pngh = (PngHandler*)memoryAddress; + + pngh->png_ptr = + png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, PngDecError, PngDecWarning); + + if (pngh->png_ptr == nullptr) + return ORBIS_PNG_DEC_ERROR_FATAL; + + pngh->info_ptr = png_create_info_struct(pngh->png_ptr); + if (pngh->info_ptr == nullptr) { + png_destroy_read_struct(&pngh->png_ptr, nullptr, nullptr); + return false; + } + + *handle = pngh; return ORBIS_OK; } @@ -88,22 +90,91 @@ s32 PS4_SYSV_ABI scePngDecDecode(OrbisPngDecHandle handle, const OrbisPngDecDeco LOG_ERROR(Lib_Png, "invalid image address!"); return ORBIS_PNG_DEC_ERROR_INVALID_ADDR; } + LOG_TRACE(Lib_Png, + "pngMemSize = {} , imageMemSize = {} , pixelFormat = {} , alphaValue = {} , " + "imagePitch = {}", + param->pngMemSize, param->imageMemSize, param->pixelFormat, param->alphaValue, + param->imagePitch); - int width, height, channels; - const u8* png_raw = (const u8*)param->pngMemAddr; - u8* img = stbi_load_from_memory(png_raw, param->pngMemSize, &width, &height, &channels, - STBI_rgb_alpha); // STBI_rgb_alpha? - if (!img) { - LOG_ERROR(Lib_Png, "Decoding failed!"); - return ORBIS_PNG_DEC_ERROR_DECODE_ERROR; + auto pngh = (PngHandler*)handle; + + struct pngstruct { + const u8* data; + size_t size; + u64 offset; + } pngdata = { + .data = (const u8*)param->pngMemAddr, + .size = param->pngMemSize, + .offset = 0, + }; + + // Read png from memory + png_set_read_fn(pngh->png_ptr, (void*)&pngdata, + [](png_structp ps, png_bytep data, png_size_t len) { + if (len == 0) + return; + auto pngdata = (pngstruct*)png_get_io_ptr(ps); + ::memcpy(data, pngdata->data + pngdata->offset, len); + pngdata->offset += len; + }); + + u32 width, height; + int color_type, bit_depth; + png_read_info(pngh->png_ptr, pngh->info_ptr); + + width = png_get_image_width(pngh->png_ptr, pngh->info_ptr); + height = png_get_image_height(pngh->png_ptr, pngh->info_ptr); + color_type = MapPngColor(png_get_color_type(pngh->png_ptr, pngh->info_ptr)); + bit_depth = png_get_bit_depth(pngh->png_ptr, pngh->info_ptr); + + if (imageInfo != nullptr) { + imageInfo->bitDepth = bit_depth; + imageInfo->imageWidth = width; + imageInfo->imageHeight = height; + imageInfo->colorSpace = color_type; + imageInfo->imageFlag = 0; + if (png_get_interlace_type(pngh->png_ptr, pngh->info_ptr) == 1) { + imageInfo->imageFlag |= OrbisPngDecImageFlag::ORBIS_PNG_DEC_IMAGE_FLAG_ADAM7_INTERLACE; + } + if (png_get_valid(pngh->png_ptr, pngh->info_ptr, PNG_INFO_tRNS)) { + + imageInfo->imageFlag |= ORBIS_PNG_DEC_IMAGE_FLAG_TRNS_CHUNK_EXIST; + } } - bool isInterlaced = (png_raw[28] == 1); - bool isTransparent = checktRNS(png_raw, param->pngMemSize); - setImageInfoParams(imageInfo, width, height, channels, isInterlaced, isTransparent); - u8* imageBuffer = (u8*)(param->imageMemAddr); - memcpy(imageBuffer, img, width * height * 4); // copy/pass decoded data - stbi_image_free(img); - return 0; + + if (bit_depth == 16) + png_set_strip_16(pngh->png_ptr); + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb(pngh->png_ptr); + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) + png_set_expand_gray_1_2_4_to_8(pngh->png_ptr); + if (png_get_valid(pngh->png_ptr, pngh->info_ptr, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha(pngh->png_ptr); + if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(pngh->png_ptr); + if (param->pixelFormat == OrbisPngDecPixelFormat::ORBIS_PNG_DEC_PIXEL_FORMAT_B8G8R8A8) + png_set_bgr(pngh->png_ptr); + if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_PALETTE) + png_set_add_alpha(pngh->png_ptr, param->alphaValue, PNG_FILLER_AFTER); + + int pass = png_set_interlace_handling(pngh->png_ptr); + png_read_update_info(pngh->png_ptr, pngh->info_ptr); + + auto const numChannels = png_get_channels(pngh->png_ptr, pngh->info_ptr); + auto horizontal_bytes = numChannels * width; + + int stride = param->imagePitch > 0 ? param->imagePitch : horizontal_bytes; + + for (int j = 0; j < pass; j++) { // interlaced + auto ptr = (png_bytep)param->imageMemAddr; + for (int y = 0; y < height; y++) { + png_read_row(pngh->png_ptr, ptr, nullptr); + ptr += stride; + } + } + + return (width > 32767 || height > 32767) ? 0 : (width << 16) | height; } s32 PS4_SYSV_ABI scePngDecDecodeWithInputControl() { @@ -112,8 +183,8 @@ s32 PS4_SYSV_ABI scePngDecDecodeWithInputControl() { } s32 PS4_SYSV_ABI scePngDecDelete(OrbisPngDecHandle handle) { - handle = nullptr; // ? - LOG_ERROR(Lib_Png, "(STUBBED)called"); + auto pngh = *(PngHandler**)handle; + png_destroy_read_struct(&pngh->png_ptr, &pngh->info_ptr, nullptr); return ORBIS_OK; } @@ -123,16 +194,60 @@ s32 PS4_SYSV_ABI scePngDecParseHeader(const OrbisPngDecParseParam* param, LOG_ERROR(Lib_Png, "Invalid param!"); return ORBIS_PNG_DEC_ERROR_INVALID_PARAM; } - int width, height, channels; - const u8* png_raw = (const u8*)(param->pngMemAddr); - int img = stbi_info_from_memory(png_raw, param->pngMemSize, &width, &height, &channels); - if (img == 0) { - LOG_ERROR(Lib_Png, "Decoding failed!"); - return ORBIS_PNG_DEC_ERROR_DECODE_ERROR; + + u8 header[8]; + memcpy(header, param->pngMemAddr, 8); + // Check if the header indicates a valid PNG file + if (png_sig_cmp(header, 0, 8)) { + LOG_ERROR(Lib_Png, "Memory doesn't contain a valid png file"); + return ORBIS_PNG_DEC_ERROR_INVALID_DATA; } - bool isInterlaced = (png_raw[28] == 1); - bool isTransparent = checktRNS(png_raw, param->pngMemSize); - setImageInfoParams(imageInfo, width, height, channels, isInterlaced, isTransparent); + // Create a libpng structure, also pass our custom error/warning functions + auto png_ptr = + png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, PngDecError, PngDecWarning); + + // Create a libpng info structure + auto info_ptr = png_create_info_struct(png_ptr); + + struct pngstruct { + const u8* data; + size_t size; + u64 offset; + } pngdata = { + .data = (const u8*)param->pngMemAddr, + .size = param->pngMemSize, + .offset = 0, + }; + + png_set_read_fn(png_ptr, (void*)&pngdata, [](png_structp ps, png_bytep data, png_size_t len) { + auto pngdata = (pngstruct*)png_get_io_ptr(ps); + ::memcpy(data, pngdata->data + pngdata->offset, len); + pngdata->offset += len; + }); + + // Now call png_read_info with our pngPtr as image handle, and infoPtr to receive the file + // info. + png_read_info(png_ptr, info_ptr); + + imageInfo->imageWidth = png_get_image_width(png_ptr, info_ptr); + imageInfo->imageHeight = png_get_image_height(png_ptr, info_ptr); + imageInfo->colorSpace = MapPngColor(png_get_color_type(png_ptr, info_ptr)); + imageInfo->bitDepth = png_get_bit_depth(png_ptr, info_ptr); + imageInfo->imageFlag = 0; + if (png_get_interlace_type(png_ptr, info_ptr) == 1) { + imageInfo->imageFlag |= OrbisPngDecImageFlag::ORBIS_PNG_DEC_IMAGE_FLAG_ADAM7_INTERLACE; + } + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { + + imageInfo->imageFlag |= ORBIS_PNG_DEC_IMAGE_FLAG_TRNS_CHUNK_EXIST; + } + + png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); + LOG_TRACE( + Lib_Png, + "imageWidth = {} , imageHeight = {} , colorSpace = {} , bitDepth = {} , imageFlag = {}", + imageInfo->imageWidth, imageInfo->imageHeight, imageInfo->colorSpace, imageInfo->bitDepth, + imageInfo->imageFlag); return ORBIS_OK; } @@ -149,9 +264,7 @@ s32 PS4_SYSV_ABI scePngDecQueryMemorySize(const OrbisPngDecCreateParam* param) { LOG_ERROR(Lib_Png, "Invalid size! width = {}", param->maxImageWidth); return ORBIS_PNG_DEC_ERROR_INVALID_SIZE; } - int ret = - (8 << ((u8)param->attribute & 0x1f)) * (param->maxImageWidth + 0x47U & 0xfffffff8) + 0xd090; - return ret; + return sizeof(PngHandler); } void RegisterlibScePngDec(Core::Loader::SymbolsResolver* sym) { diff --git a/src/core/libraries/libpng/pngdec.h b/src/core/libraries/libpng/pngdec.h index 35034a196..9d807166c 100644 --- a/src/core/libraries/libpng/pngdec.h +++ b/src/core/libraries/libpng/pngdec.h @@ -8,54 +8,55 @@ namespace Core::Loader { class SymbolsResolver; } + namespace Libraries::PngDec { -constexpr int ORBIS_PNG_DEC_ERROR_INVALID_ADDR = 0x80690001; -constexpr int ORBIS_PNG_DEC_ERROR_INVALID_SIZE = 0x80690002; -constexpr int ORBIS_PNG_DEC_ERROR_INVALID_PARAM = 0x80690003; -constexpr int ORBIS_PNG_DEC_ERROR_INVALID_HANDLE = 0x80690004; -constexpr int ORBIS_PNG_DEC_ERROR_INVALID_WORK_MEMORY = 0x80690005; -constexpr int ORBIS_PNG_DEC_ERROR_INVALID_DATA = 0x80690010; -constexpr int ORBIS_PNG_DEC_ERROR_UNSUPPORT_DATA = 0x80690011; -constexpr int ORBIS_PNG_DEC_ERROR_DECODE_ERROR = 0x80690012; -constexpr int ORBIS_PNG_DEC_ERROR_FATAL = 0x80690020; - -typedef struct OrbisPngDecParseParam { - const void* pngMemAddr; - u32 pngMemSize; - u32 reserved; -} OrbisPngDecParseParam; - -typedef struct OrbisPngDecImageInfo { - u32 imageWidth; - u32 imageHeight; - u16 colorSpace; - u16 bitDepth; - u32 imageFlag; -} OrbisPngDecImageInfo; - -typedef enum OrbisPngDecColorSpace { +enum OrbisPngDecColorSpace { ORBIS_PNG_DEC_COLOR_SPACE_GRAYSCALE = 2, ORBIS_PNG_DEC_COLOR_SPACE_RGB, ORBIS_PNG_DEC_COLOR_SPACE_CLUT, ORBIS_PNG_DEC_COLOR_SPACE_GRAYSCALE_ALPHA = 18, ORBIS_PNG_DEC_COLOR_SPACE_RGBA -} ScePngDecColorSpace; +}; -typedef enum OrbisPngDecImageFlag { +enum OrbisPngDecImageFlag { ORBIS_PNG_DEC_IMAGE_FLAG_ADAM7_INTERLACE = 1, ORBIS_PNG_DEC_IMAGE_FLAG_TRNS_CHUNK_EXIST = 2 -} OrbisPngDecImageFlag; +}; -typedef struct OrbisPngDecCreateParam { +enum OrbisPngDecPixelFormat { + ORBIS_PNG_DEC_PIXEL_FORMAT_R8G8B8A8 = 0, + ORBIS_PNG_DEC_PIXEL_FORMAT_B8G8R8A8 +}; + +enum OrbisPngDecAttribute { + ORBIS_PNG_DEC_ATTRIBUTE_NONE = 0, + ORBIS_PNG_DEC_ATTRIBUTE_BIT_DEPTH_16 +}; + +struct OrbisPngDecParseParam { + const void* pngMemAddr; + u32 pngMemSize; + u32 reserved; +}; + +struct OrbisPngDecImageInfo { + u32 imageWidth; + u32 imageHeight; + u16 colorSpace; + u16 bitDepth; + u32 imageFlag; +}; + +struct OrbisPngDecCreateParam { u32 thisSize; u32 attribute; u32 maxImageWidth; -} OrbisPngDecCreateParam; +}; typedef void* OrbisPngDecHandle; -typedef struct OrbisPngDecDecodeParam { +struct OrbisPngDecDecodeParam { const void* pngMemAddr; void* imageMemAddr; u32 pngMemSize; @@ -63,7 +64,7 @@ typedef struct OrbisPngDecDecodeParam { u16 pixelFormat; u16 alphaValue; u32 imagePitch; -} OrbisPngDecDecodeParam; +}; s32 PS4_SYSV_ABI scePngDecCreate(const OrbisPngDecCreateParam* param, void* memoryAddress, u32 memorySize, OrbisPngDecHandle* handle); From 9ed07f6f6e949bd763bedcbb6efeaad16fef2a7b Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Fri, 22 Nov 2024 14:53:16 +0200 Subject: [PATCH 044/549] @Roamic's hot region fix (#1570) --- src/video_core/buffer_cache/buffer_cache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index b15eace12..42d3deba7 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -362,7 +362,7 @@ bool BufferCache::IsRegionRegistered(VAddr addr, size_t size) { if (buf_start_addr < end_addr && addr < buf_end_addr) { return true; } - page = Common::DivCeil(end_addr, CACHING_PAGESIZE); + page = Common::DivCeil(buf_end_addr, CACHING_PAGESIZE); } return false; } From c71385ec9082895cd17b83eabfe7c80bf2b60864 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 22 Nov 2024 07:36:31 -0800 Subject: [PATCH 045/549] misc: Add JetBrains build directories to gitignore. (#1573) --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c331f9e70..683f6f0a6 100644 --- a/.gitignore +++ b/.gitignore @@ -417,3 +417,4 @@ FodyWeavers.xsd # JetBrains .idea +cmake-build-* From 15d6b095f730bfd6df91ad801c6a19b02bb16c3e Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 22 Nov 2024 08:29:41 -0800 Subject: [PATCH 046/549] savedatadialog_ui: Use fmt::localtime instead of formatting std::chrono::local_time (#1574) --- .../save_data/dialog/savedatadialog_ui.cpp | 36 ++----------------- 1 file changed, 3 insertions(+), 33 deletions(-) diff --git a/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp b/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp index 52abe9101..4e0d801a6 100644 --- a/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp +++ b/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp @@ -13,33 +13,6 @@ #include "imgui/imgui_std.h" #include "savedatadialog_ui.h" -#ifdef __APPLE__ -#include - -// Need to make a copy of the formatter for std::chrono::local_time for use with date::local_time -template -struct fmt::formatter, Char> : formatter { - FMT_CONSTEXPR formatter() { - this->format_str_ = fmt::detail::string_literal(); - } - - template - auto format(date::local_time val, FormatContext& ctx) const -> decltype(ctx.out()) { - using period = typename Duration::period; - if (period::num == 1 && period::den == 1 && - !std::is_floating_point::value) { - return formatter::format( - localtime(fmt::detail::to_time_t(date::current_zone()->to_sys(val))), ctx); - } - auto epoch = val.time_since_epoch(); - auto subsecs = fmt::detail::duration_cast( - epoch - fmt::detail::duration_cast(epoch)); - return formatter::do_format( - localtime(fmt::detail::to_time_t(date::current_zone()->to_sys(val))), ctx, &subsecs); - } -}; -#endif - using namespace ImGui; using namespace Libraries::CommonDialog; using Common::ElfInfo; @@ -125,12 +98,9 @@ SaveDialogState::SaveDialogState(const OrbisSaveDataDialogParam& param) { param_sfo.Open(param_sfo_path); auto last_write = param_sfo.GetLastWrite(); -#ifdef __APPLE__ - auto t = date::zoned_time{date::current_zone(), last_write}; -#else - auto t = std::chrono::zoned_time{std::chrono::current_zone(), last_write}; -#endif - std::string date_str = fmt::format("{:%d %b, %Y %R}", t.get_local_time()); + std::string date_str = + fmt::format("{:%d %b, %Y %R}", + fmt::localtime(std::chrono::system_clock::to_time_t(last_write))); size_t size = Common::FS::GetDirectorySize(dir_path); std::string size_str = SpaceSizeToString(size); From df8284c5f8bb7cd55f650e77c1820e24b508b3d3 Mon Sep 17 00:00:00 2001 From: "Daniel R." <47796739+polybiusproxy@users.noreply.github.com> Date: Fri, 22 Nov 2024 21:20:47 +0100 Subject: [PATCH 047/549] Fix splashes using the wrong image Supposedly, only pic1.png is used for the splash --- src/emulator.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/emulator.cpp b/src/emulator.cpp index 24f5907a1..c0b01229a 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -162,8 +162,7 @@ void Emulator::Run(const std::filesystem::path& file) { if (!playgo->Open(filepath)) { LOG_ERROR(Loader, "PlayGo: unable to open file"); } - } else if (entry.path().filename() == "pic0.png" || - entry.path().filename() == "pic1.png") { + } else if (entry.path().filename() == "pic1.png") { auto* splash = Common::Singleton::Instance(); if (splash->IsLoaded()) { continue; From 597630078802af6c67ca684a72b2d40593df8346 Mon Sep 17 00:00:00 2001 From: psucien Date: Sat, 23 Nov 2024 10:14:19 +0100 Subject: [PATCH 048/549] hot-fix: proper offset calculation for single offset lds instructions --- .../frontend/translate/data_share.cpp | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/shader_recompiler/frontend/translate/data_share.cpp b/src/shader_recompiler/frontend/translate/data_share.cpp index adc2bbbc2..9685ee352 100644 --- a/src/shader_recompiler/frontend/translate/data_share.cpp +++ b/src/shader_recompiler/frontend/translate/data_share.cpp @@ -86,7 +86,8 @@ void Translator::V_WRITELANE_B32(const GcnInst& inst) { void Translator::DS_ADD_U32(const GcnInst& inst, bool rtn) { const IR::U32 addr{GetSrc(inst.src[0])}; const IR::U32 data{GetSrc(inst.src[1])}; - const IR::U32 offset = ir.Imm32(u32(inst.control.ds.offset0)); + const IR::U32 offset = + ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0)); const IR::U32 addr_offset = ir.IAdd(addr, offset); const IR::Value original_val = ir.SharedAtomicIAdd(addr_offset, data); if (rtn) { @@ -97,7 +98,8 @@ void Translator::DS_ADD_U32(const GcnInst& inst, bool rtn) { void Translator::DS_MIN_U32(const GcnInst& inst, bool is_signed, bool rtn) { const IR::U32 addr{GetSrc(inst.src[0])}; const IR::U32 data{GetSrc(inst.src[1])}; - const IR::U32 offset = ir.Imm32(u32(inst.control.ds.offset0)); + const IR::U32 offset = + ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0)); const IR::U32 addr_offset = ir.IAdd(addr, offset); const IR::Value original_val = ir.SharedAtomicIMin(addr_offset, data, is_signed); if (rtn) { @@ -108,7 +110,8 @@ void Translator::DS_MIN_U32(const GcnInst& inst, bool is_signed, bool rtn) { void Translator::DS_MAX_U32(const GcnInst& inst, bool is_signed, bool rtn) { const IR::U32 addr{GetSrc(inst.src[0])}; const IR::U32 data{GetSrc(inst.src[1])}; - const IR::U32 offset = ir.Imm32(u32(inst.control.ds.offset0)); + const IR::U32 offset = + ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0)); const IR::U32 addr_offset = ir.IAdd(addr, offset); const IR::Value original_val = ir.SharedAtomicIMax(addr_offset, data, is_signed); if (rtn) { @@ -140,12 +143,14 @@ void Translator::DS_WRITE(int bit_size, bool is_signed, bool is_pair, bool strid addr1); } } else if (bit_size == 64) { - const IR::U32 addr0 = ir.IAdd(addr, ir.Imm32(u32(inst.control.ds.offset0))); + const IR::U32 addr0 = ir.IAdd( + addr, ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0))); const IR::Value data = ir.CompositeConstruct(ir.GetVectorReg(data0), ir.GetVectorReg(data0 + 1)); ir.WriteShared(bit_size, data, addr0); } else { - const IR::U32 addr0 = ir.IAdd(addr, ir.Imm32(u32(inst.control.ds.offset0))); + const IR::U32 addr0 = ir.IAdd( + addr, ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0))); ir.WriteShared(bit_size, ir.GetVectorReg(data0), addr0); } } @@ -187,26 +192,28 @@ void Translator::DS_READ(int bit_size, bool is_signed, bool is_pair, bool stride ir.SetVectorReg(dst_reg++, IR::U32{ir.CompositeExtract(data1, 1)}); } } else if (bit_size == 64) { - const IR::U32 addr0 = ir.IAdd(addr, ir.Imm32(u32(inst.control.ds.offset0))); + const IR::U32 addr0 = ir.IAdd( + addr, ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0))); const IR::Value data = ir.LoadShared(bit_size, is_signed, addr0); ir.SetVectorReg(dst_reg, IR::U32{ir.CompositeExtract(data, 0)}); ir.SetVectorReg(dst_reg + 1, IR::U32{ir.CompositeExtract(data, 1)}); } else { - const IR::U32 addr0 = ir.IAdd(addr, ir.Imm32(u32(inst.control.ds.offset0))); + const IR::U32 addr0 = ir.IAdd( + addr, ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0))); const IR::U32 data = IR::U32{ir.LoadShared(bit_size, is_signed, addr0)}; ir.SetVectorReg(dst_reg, data); } } void Translator::DS_APPEND(const GcnInst& inst) { - const u32 inst_offset = inst.control.ds.offset0; + const u32 inst_offset = (u32(inst.control.ds.offset1) << 8u) + inst.control.ds.offset0; const IR::U32 gds_offset = ir.IAdd(ir.GetM0(), ir.Imm32(inst_offset)); const IR::U32 prev = ir.DataAppend(gds_offset); SetDst(inst.dst[0], prev); } void Translator::DS_CONSUME(const GcnInst& inst) { - const u32 inst_offset = inst.control.ds.offset0; + const u32 inst_offset = (u32(inst.control.ds.offset1) << 8u) + inst.control.ds.offset0; const IR::U32 gds_offset = ir.IAdd(ir.GetM0(), ir.Imm32(inst_offset)); const IR::U32 prev = ir.DataConsume(gds_offset); SetDst(inst.dst[0], prev); From d7d28aa8da6b70d5f6641e52f1b087448ca27d4b Mon Sep 17 00:00:00 2001 From: psucien <168137814+psucien@users.noreply.github.com> Date: Sat, 23 Nov 2024 11:46:31 +0100 Subject: [PATCH 049/549] video_core: restored presenter aspect calculations (#1583) * video_core: restored presenter aspect calculations * code simplification --- .../renderer_vulkan/vk_presenter.cpp | 43 +++++++++++-------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_presenter.cpp b/src/video_core/renderer_vulkan/vk_presenter.cpp index d4972cbad..23d2981f5 100644 --- a/src/video_core/renderer_vulkan/vk_presenter.cpp +++ b/src/video_core/renderer_vulkan/vk_presenter.cpp @@ -75,13 +75,13 @@ bool CanBlitToSwapchain(const vk::PhysicalDevice physical_device, vk::Format for return MakeImageBlit(frame_width, frame_height, swapchain_width, swapchain_height, 0, 0); } -[[nodiscard]] vk::ImageBlit MakeImageBlitFit(s32 frame_width, s32 frame_height, s32 swapchain_width, - s32 swapchain_height) { +static vk::Rect2D FitImage(s32 frame_width, s32 frame_height, s32 swapchain_width, + s32 swapchain_height) { float frame_aspect = static_cast(frame_width) / frame_height; float swapchain_aspect = static_cast(swapchain_width) / swapchain_height; - s32 dst_width = swapchain_width; - s32 dst_height = swapchain_height; + u32 dst_width = swapchain_width; + u32 dst_height = swapchain_height; if (frame_aspect > swapchain_aspect) { dst_height = static_cast(swapchain_width / frame_aspect); @@ -89,10 +89,18 @@ bool CanBlitToSwapchain(const vk::PhysicalDevice physical_device, vk::Format for dst_width = static_cast(swapchain_height * frame_aspect); } - s32 offset_x = (swapchain_width - dst_width) / 2; - s32 offset_y = (swapchain_height - dst_height) / 2; + const s32 offset_x = (swapchain_width - dst_width) / 2; + const s32 offset_y = (swapchain_height - dst_height) / 2; - return MakeImageBlit(frame_width, frame_height, dst_width, dst_height, offset_x, offset_y); + return vk::Rect2D{{offset_x, offset_y}, {dst_width, dst_height}}; +} + +[[nodiscard]] vk::ImageBlit MakeImageBlitFit(s32 frame_width, s32 frame_height, s32 swapchain_width, + s32 swapchain_height) { + const auto& dst_rect = FitImage(frame_width, frame_height, swapchain_width, swapchain_height); + + return MakeImageBlit(frame_width, frame_height, dst_rect.extent.width, dst_rect.extent.height, + dst_rect.offset.x, dst_rect.offset.y); } static vk::Format FormatToUnorm(vk::Format fmt) { @@ -552,25 +560,22 @@ Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop) cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, *pp_pipeline); + const auto& dst_rect = + FitImage(image.info.size.width, image.info.size.height, frame->width, frame->height); + const std::array viewports = { vk::Viewport{ - .x = 0.0f, - .y = 0.0f, - .width = 1.0f * frame->width, - .height = 1.0f * frame->height, + .x = 1.0f * dst_rect.offset.x, + .y = 1.0f * dst_rect.offset.y, + .width = 1.0f * dst_rect.extent.width, + .height = 1.0f * dst_rect.extent.height, .minDepth = 0.0f, .maxDepth = 1.0f, }, }; - const std::array scissors = { - vk::Rect2D{ - .offset = {0, 0}, - .extent = {frame->width, frame->height}, - }, - }; cmdbuf.setViewport(0, viewports); - cmdbuf.setScissor(0, scissors); + cmdbuf.setScissor(0, {dst_rect}); cmdbuf.pushDescriptorSetKHR(vk::PipelineBindPoint::eGraphics, *pp_pipeline_layout, 0, set_writes); @@ -580,7 +585,7 @@ Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop) const std::array attachments = {vk::RenderingAttachmentInfo{ .imageView = frame->image_view, .imageLayout = vk::ImageLayout::eColorAttachmentOptimal, - .loadOp = vk::AttachmentLoadOp::eDontCare, + .loadOp = vk::AttachmentLoadOp::eClear, .storeOp = vk::AttachmentStoreOp::eStore, }}; From add6eeeb8af871bccb5e8c3c1f8d13d0fa76c29a Mon Sep 17 00:00:00 2001 From: psucien Date: Sun, 24 Nov 2024 11:37:14 +0100 Subject: [PATCH 050/549] update: Tracy 0.11.1 --- externals/tracy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externals/tracy b/externals/tracy index b8061982c..143a53d19 160000 --- a/externals/tracy +++ b/externals/tracy @@ -1 +1 @@ -Subproject commit b8061982cad0210b649541016c88ff5faa90733c +Subproject commit 143a53d1985b8e52a7590a0daca30a0a7c653b42 From fde1726af5e8859e671a90811f62041d26e2e219 Mon Sep 17 00:00:00 2001 From: baggins183 Date: Sun, 24 Nov 2024 02:49:59 -0800 Subject: [PATCH 051/549] recompiler: fix how srt pass handles step rate sharps in special case (#1587) --- .../ir/passes/flatten_extended_userdata_pass.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/shader_recompiler/ir/passes/flatten_extended_userdata_pass.cpp b/src/shader_recompiler/ir/passes/flatten_extended_userdata_pass.cpp index 6292edfd8..ef9319891 100644 --- a/src/shader_recompiler/ir/passes/flatten_extended_userdata_pass.cpp +++ b/src/shader_recompiler/ir/passes/flatten_extended_userdata_pass.cpp @@ -148,17 +148,21 @@ static void GenerateSrtProgram(Info& info, PassInfo& pass_info) { // Special case for V# step rate buffers in fetch shader for (const auto [sgpr_base, dword_offset, num_dwords] : info.srt_info.srt_reservations) { // get pointer to V# - c.mov(r10d, ptr[rdi + (sgpr_base << 2)]); - + if (sgpr_base != IR::NumScalarRegs) { + PushPtr(c, sgpr_base); + } u32 src_off = dword_offset << 2; for (auto j = 0; j < num_dwords; j++) { - c.mov(r11d, ptr[r10d + src_off]); + c.mov(r11d, ptr[rdi + src_off]); c.mov(ptr[rsi + (pass_info.dst_off_dw << 2)], r11d); src_off += 4; ++pass_info.dst_off_dw; } + if (sgpr_base != IR::NumScalarRegs) { + PopPtr(c); + } } ASSERT(pass_info.dst_off_dw == info.srt_info.flattened_bufsize_dw); From 16e1d679dc8f6ab5b2104339e6c40945c320dbed Mon Sep 17 00:00:00 2001 From: psucien <168137814+psucien@users.noreply.github.com> Date: Sun, 24 Nov 2024 15:43:28 +0100 Subject: [PATCH 052/549] video_core: clean-up of indirect draws logic (#1589) --- src/core/libraries/gnmdriver/gnmdriver.cpp | 2 +- src/video_core/amdgpu/liverpool.cpp | 9 ++- src/video_core/amdgpu/liverpool.h | 2 - src/video_core/amdgpu/pm4_cmds.h | 59 ++++++++++++------- .../renderer_vulkan/vk_rasterizer.cpp | 21 +++---- 5 files changed, 51 insertions(+), 42 deletions(-) diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index 9aede3304..70cf09a97 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -703,7 +703,7 @@ s32 PS4_SYSV_ABI sceGnmDrawIndexIndirectCountMulti(u32* cmdbuf, u32 size, u32 da cmdbuf[3] = (count_addr != 0 ? 1u : 0u) << 0x1e; cmdbuf[4] = max_count; *(u64*)(&cmdbuf[5]) = count_addr; - cmdbuf[7] = AmdGpu::Liverpool::DrawIndexedIndirectArgsSize; + cmdbuf[7] = sizeof(DrawIndexedIndirectArgs); cmdbuf[8] = 0; cmdbuf += 9; diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index 12b5de436..f7b710edd 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -410,7 +410,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); const auto offset = draw_indirect->data_offset; const auto ib_address = mapped_queues[GfxQueueId].indirect_args_addr; - const auto size = sizeof(PM4CmdDrawIndirect::DrawInstancedArgs); + const auto size = sizeof(DrawIndirectArgs); if (DebugState.DumpingCurrentReg()) { DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); } @@ -427,7 +427,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); const auto offset = draw_index_indirect->data_offset; const auto ib_address = mapped_queues[GfxQueueId].indirect_args_addr; - const auto size = sizeof(PM4CmdDrawIndexIndirect::DrawIndexInstancedArgs); + const auto size = sizeof(DrawIndexedIndirectArgs); if (DebugState.DumpingCurrentReg()) { DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); } @@ -442,10 +442,9 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); + reinterpret_cast(header); const auto offset = draw_index_indirect->data_offset; const auto ib_address = mapped_queues[GfxQueueId].indirect_args_addr; - const auto size = sizeof(PM4CmdDrawIndexIndirect::DrawIndexInstancedArgs); if (DebugState.DumpingCurrentReg()) { DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); } @@ -453,7 +452,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); rasterizer->ScopeMarkerBegin( fmt::format("dcb:{}:DrawIndexIndirectCountMulti", cmd_address)); - rasterizer->DrawIndirect(true, ib_address, offset, size, + rasterizer->DrawIndirect(true, ib_address, offset, draw_index_indirect->stride, draw_index_indirect->count, draw_index_indirect->countAddr); rasterizer->ScopeMarkerEnd(); diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index 0595a242c..0ef9397b0 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -57,8 +57,6 @@ struct Liverpool { static constexpr u32 ConfigRegWordOffset = 0x2000; static constexpr u32 ShRegWordOffset = 0x2C00; static constexpr u32 NumRegs = 0xD000; - static constexpr u32 DrawIndirectArgsSize = 0x10u; - static constexpr u32 DrawIndexedIndirectArgsSize = 0x14u; using UserData = std::array; diff --git a/src/video_core/amdgpu/pm4_cmds.h b/src/video_core/amdgpu/pm4_cmds.h index d6cab23d2..be6751285 100644 --- a/src/video_core/amdgpu/pm4_cmds.h +++ b/src/video_core/amdgpu/pm4_cmds.h @@ -778,14 +778,15 @@ struct PM4CmdDispatchIndirect { u32 dispatch_initiator; ///< Dispatch Initiator Register }; -struct PM4CmdDrawIndirect { - struct DrawInstancedArgs { - u32 vertex_count_per_instance; - u32 instance_count; - u32 start_vertex_location; - u32 start_instance_location; - }; +struct DrawIndirectArgs { + u32 vertex_count_per_instance; + u32 instance_count; + u32 start_vertex_location; + u32 start_instance_location; +}; +static_assert(sizeof(DrawIndirectArgs) == 0x10u); +struct PM4CmdDrawIndirect { PM4Type3Header header; ///< header u32 data_offset; ///< Byte aligned offset where the required data structure starts union { @@ -801,15 +802,16 @@ struct PM4CmdDrawIndirect { u32 draw_initiator; ///< Draw Initiator Register }; -struct PM4CmdDrawIndexIndirect { - struct DrawIndexInstancedArgs { - u32 index_count_per_instance; - u32 instance_count; - u32 start_index_location; - u32 base_vertex_location; - u32 start_instance_location; - }; +struct DrawIndexedIndirectArgs { + u32 index_count_per_instance; + u32 instance_count; + u32 start_index_location; + u32 base_vertex_location; + u32 start_instance_location; +}; +static_assert(sizeof(DrawIndexedIndirectArgs) == 0x14u); +struct PM4CmdDrawIndexIndirect { PM4Type3Header header; ///< header u32 data_offset; ///< Byte aligned offset where the required data structure starts union { @@ -822,16 +824,29 @@ struct PM4CmdDrawIndexIndirect { BitField<0, 16, u32> start_inst_loc; ///< Offset where the CP will write the ///< StartInstanceLocation it fetched from memory }; + u32 draw_initiator; ///< Draw Initiator Register +}; +struct PM4CmdDrawIndexIndirectMulti { + PM4Type3Header header; ///< header + u32 data_offset; ///< Byte aligned offset where the required data structure starts + union { + u32 dw2; + BitField<0, 16, u32> base_vtx_loc; ///< Offset where the CP will write the + ///< BaseVertexLocation it fetched from memory + }; + union { + u32 dw3; + BitField<0, 16, u32> start_inst_loc; ///< Offset where the CP will write the + ///< StartInstanceLocation it fetched from memory + }; union { u32 dw4; - struct { - BitField<0, 16, u32> drawIndexLoc; ///< register offset to write the Draw Index count - BitField<30, 1, u32> - countIndirectEnable; ///< Indicates the data structure count is in memory - BitField<31, 1, u32> - drawIndexEnable; ///< Enables writing of Draw Index count to DRAW_INDEX_LOC - }; + BitField<0, 16, u32> drawIndexLoc; ///< register offset to write the Draw Index count + BitField<30, 1, u32> + countIndirectEnable; ///< Indicates the data structure count is in memory + BitField<31, 1, u32> + drawIndexEnable; ///< Enables writing of Draw Index count to DRAW_INDEX_LOC }; u32 count; ///< Count of data structures to loop through before going to next packet u64 countAddr; ///< DWord aligned Address[31:2]; Valid if countIndirectEnable is set diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index a8b4728c0..3c4012a26 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -115,7 +115,7 @@ void Rasterizer::Draw(bool is_indexed, u32 index_offset) { } } -void Rasterizer::DrawIndirect(bool is_indexed, VAddr arg_address, u32 offset, u32 size, +void Rasterizer::DrawIndirect(bool is_indexed, VAddr arg_address, u32 offset, u32 stride, u32 max_count, VAddr count_address) { RENDERER_TRACE; @@ -142,7 +142,8 @@ void Rasterizer::DrawIndirect(bool is_indexed, VAddr arg_address, u32 offset, u3 buffer_cache.BindVertexBuffers(vs_info); buffer_cache.BindIndexBuffer(is_indexed, 0); - const auto [buffer, base] = buffer_cache.ObtainBuffer(arg_address + offset, size, false); + const auto [buffer, base] = + buffer_cache.ObtainBuffer(arg_address + offset, stride * max_count, false); VideoCore::Buffer* count_buffer{}; u32 count_base{}; @@ -158,26 +159,22 @@ void Rasterizer::DrawIndirect(bool is_indexed, VAddr arg_address, u32 offset, u3 const auto cmdbuf = scheduler.CommandBuffer(); if (is_indexed) { - static_assert(sizeof(VkDrawIndexedIndirectCommand) == - AmdGpu::Liverpool::DrawIndexedIndirectArgsSize); + ASSERT(sizeof(VkDrawIndexedIndirectCommand) == stride); if (count_address != 0) { cmdbuf.drawIndexedIndirectCount(buffer->Handle(), base, count_buffer->Handle(), - count_base, max_count, - AmdGpu::Liverpool::DrawIndexedIndirectArgsSize); + count_base, max_count, stride); } else { - cmdbuf.drawIndexedIndirect(buffer->Handle(), base, max_count, - AmdGpu::Liverpool::DrawIndexedIndirectArgsSize); + cmdbuf.drawIndexedIndirect(buffer->Handle(), base, max_count, stride); } } else { - static_assert(sizeof(VkDrawIndirectCommand) == AmdGpu::Liverpool::DrawIndirectArgsSize); + ASSERT(sizeof(VkDrawIndirectCommand) == stride); if (count_address != 0) { cmdbuf.drawIndirectCount(buffer->Handle(), base, count_buffer->Handle(), count_base, - max_count, AmdGpu::Liverpool::DrawIndirectArgsSize); + max_count, stride); } else { - cmdbuf.drawIndirect(buffer->Handle(), base, max_count, - AmdGpu::Liverpool::DrawIndirectArgsSize); + cmdbuf.drawIndirect(buffer->Handle(), base, max_count, stride); } } } From 3d95ad0e3a5677299712c409e331ca0e2aa15d00 Mon Sep 17 00:00:00 2001 From: psucien <168137814+psucien@users.noreply.github.com> Date: Sun, 24 Nov 2024 17:07:51 +0100 Subject: [PATCH 053/549] Image binding and texture cache interface refactor (1/2) (#1481) * video_core: texture_cache: interface refactor and better overlap handling * resources binding moved into vk_rasterizer * remove `virtual` flag leftover --- src/shader_recompiler/runtime_info.h | 2 +- src/video_core/buffer_cache/buffer_cache.cpp | 8 +- .../renderer_vulkan/vk_compute_pipeline.cpp | 92 +-- .../renderer_vulkan/vk_compute_pipeline.h | 5 - .../renderer_vulkan/vk_graphics_pipeline.cpp | 67 -- .../renderer_vulkan/vk_graphics_pipeline.h | 12 +- .../renderer_vulkan/vk_pipeline_cache.h | 2 - .../renderer_vulkan/vk_pipeline_common.cpp | 239 +------ .../renderer_vulkan/vk_pipeline_common.h | 39 +- src/video_core/renderer_vulkan/vk_presenter.h | 12 +- .../renderer_vulkan/vk_rasterizer.cpp | 609 +++++++++++++++--- .../renderer_vulkan/vk_rasterizer.h | 38 +- src/video_core/texture_cache/image.cpp | 22 +- src/video_core/texture_cache/image.h | 28 +- src/video_core/texture_cache/image_info.cpp | 71 +- src/video_core/texture_cache/image_info.h | 34 +- src/video_core/texture_cache/image_view.cpp | 2 +- .../texture_cache/texture_cache.cpp | 241 ++++--- src/video_core/texture_cache/texture_cache.h | 67 +- 19 files changed, 911 insertions(+), 679 deletions(-) diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index 03936f3a8..4662def93 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -20,7 +20,7 @@ enum class Stage : u32 { Local, Compute, }; -constexpr u32 MaxStageTypes = 6; +constexpr u32 MaxStageTypes = 7; [[nodiscard]] constexpr Stage StageFromIndex(size_t index) noexcept { return static_cast(index); diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index 42d3deba7..92c446fa9 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -620,10 +620,10 @@ void BufferCache::SynchronizeBuffer(Buffer& buffer, VAddr device_addr, u32 size, bool BufferCache::SynchronizeBufferFromImage(Buffer& buffer, VAddr device_addr, u32 size) { static constexpr FindFlags find_flags = FindFlags::NoCreate | FindFlags::RelaxDim | FindFlags::RelaxFmt | FindFlags::RelaxSize; - ImageInfo info{}; - info.guest_address = device_addr; - info.guest_size_bytes = size; - const ImageId image_id = texture_cache.FindImage(info, find_flags); + TextureCache::BaseDesc desc{}; + desc.info.guest_address = device_addr; + desc.info.guest_size_bytes = size; + const ImageId image_id = texture_cache.FindImage(desc, find_flags); if (!image_id) { return false; } diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp index 4ab290780..09d4e4195 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp @@ -15,8 +15,10 @@ ComputePipeline::ComputePipeline(const Instance& instance_, Scheduler& scheduler DescriptorHeap& desc_heap_, vk::PipelineCache pipeline_cache, u64 compute_key_, const Shader::Info& info_, vk::ShaderModule module) - : Pipeline{instance_, scheduler_, desc_heap_, pipeline_cache}, compute_key{compute_key_}, - info{&info_} { + : Pipeline{instance_, scheduler_, desc_heap_, pipeline_cache, true}, compute_key{compute_key_} { + auto& info = stages[int(Shader::Stage::Compute)]; + info = &info_; + const vk::PipelineShaderStageCreateInfo shader_ci = { .stage = vk::ShaderStageFlagBits::eCompute, .module = module, @@ -118,90 +120,4 @@ ComputePipeline::ComputePipeline(const Instance& instance_, Scheduler& scheduler ComputePipeline::~ComputePipeline() = default; -bool ComputePipeline::BindResources(VideoCore::BufferCache& buffer_cache, - VideoCore::TextureCache& texture_cache) const { - // Bind resource buffers and textures. - boost::container::small_vector set_writes; - BufferBarriers buffer_barriers; - Shader::PushData push_data{}; - Shader::Backend::Bindings binding{}; - - info->PushUd(binding, push_data); - - buffer_infos.clear(); - buffer_views.clear(); - image_infos.clear(); - - // Most of the time when a metadata is updated with a shader it gets cleared. It means - // we can skip the whole dispatch and update the tracked state instead. Also, it is not - // intended to be consumed and in such rare cases (e.g. HTile introspection, CRAA) we - // will need its full emulation anyways. For cases of metadata read a warning will be logged. - const auto IsMetaUpdate = [&](const auto& desc) { - const VAddr address = desc.GetSharp(*info).base_address; - if (desc.is_written) { - if (texture_cache.TouchMeta(address, true)) { - LOG_TRACE(Render_Vulkan, "Metadata update skipped"); - return true; - } - } else { - if (texture_cache.IsMeta(address)) { - LOG_WARNING(Render_Vulkan, "Unexpected metadata read by a CS shader (buffer)"); - } - } - return false; - }; - - for (const auto& desc : info->buffers) { - if (desc.is_gds_buffer) { - continue; - } - if (IsMetaUpdate(desc)) { - return false; - } - } - for (const auto& desc : info->texture_buffers) { - if (IsMetaUpdate(desc)) { - return false; - } - } - - BindBuffers(buffer_cache, texture_cache, *info, binding, push_data, set_writes, - buffer_barriers); - - BindTextures(texture_cache, *info, binding, set_writes); - - if (set_writes.empty()) { - return false; - } - - const auto cmdbuf = scheduler.CommandBuffer(); - if (!buffer_barriers.empty()) { - const auto dependencies = vk::DependencyInfo{ - .dependencyFlags = vk::DependencyFlagBits::eByRegion, - .bufferMemoryBarrierCount = u32(buffer_barriers.size()), - .pBufferMemoryBarriers = buffer_barriers.data(), - }; - scheduler.EndRendering(); - cmdbuf.pipelineBarrier2(dependencies); - } - - cmdbuf.pushConstants(*pipeline_layout, vk::ShaderStageFlagBits::eCompute, 0u, sizeof(push_data), - &push_data); - - // Bind descriptor set. - if (uses_push_descriptors) { - cmdbuf.pushDescriptorSetKHR(vk::PipelineBindPoint::eCompute, *pipeline_layout, 0, - set_writes); - return true; - } - const auto desc_set = desc_heap.Commit(*desc_layout); - for (auto& set_write : set_writes) { - set_write.dstSet = desc_set; - } - instance.GetDevice().updateDescriptorSets(set_writes, {}); - cmdbuf.bindDescriptorSets(vk::PipelineBindPoint::eCompute, *pipeline_layout, 0, desc_set, {}); - - return true; -} - } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.h b/src/video_core/renderer_vulkan/vk_compute_pipeline.h index f1bc7285a..ca429b58d 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.h @@ -24,13 +24,8 @@ public: vk::ShaderModule module); ~ComputePipeline(); - bool BindResources(VideoCore::BufferCache& buffer_cache, - VideoCore::TextureCache& texture_cache) const; - private: u64 compute_key; - const Shader::Info* info; - bool uses_push_descriptors{}; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 32e3bf8f8..d0d16ac75 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -16,10 +16,6 @@ namespace Vulkan { -static constexpr auto gp_stage_flags = vk::ShaderStageFlagBits::eVertex | - vk::ShaderStageFlagBits::eGeometry | - vk::ShaderStageFlagBits::eFragment; - GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& scheduler_, DescriptorHeap& desc_heap_, const GraphicsPipelineKey& key_, vk::PipelineCache pipeline_cache, @@ -389,67 +385,4 @@ void GraphicsPipeline::BuildDescSetLayout() { desc_layout = std::move(layout); } -void GraphicsPipeline::BindResources(const Liverpool::Regs& regs, - VideoCore::BufferCache& buffer_cache, - VideoCore::TextureCache& texture_cache) const { - // Bind resource buffers and textures. - boost::container::small_vector set_writes; - BufferBarriers buffer_barriers; - Shader::PushData push_data{}; - Shader::Backend::Bindings binding{}; - - buffer_infos.clear(); - buffer_views.clear(); - image_infos.clear(); - - for (const auto* stage : stages) { - if (!stage) { - continue; - } - if (stage->uses_step_rates) { - push_data.step0 = regs.vgt_instance_step_rate_0; - push_data.step1 = regs.vgt_instance_step_rate_1; - } - stage->PushUd(binding, push_data); - - BindBuffers(buffer_cache, texture_cache, *stage, binding, push_data, set_writes, - buffer_barriers); - - BindTextures(texture_cache, *stage, binding, set_writes); - } - - const auto cmdbuf = scheduler.CommandBuffer(); - SCOPE_EXIT { - cmdbuf.pushConstants(*pipeline_layout, gp_stage_flags, 0U, sizeof(push_data), &push_data); - cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, Handle()); - }; - - if (set_writes.empty()) { - return; - } - - if (!buffer_barriers.empty()) { - const auto dependencies = vk::DependencyInfo{ - .dependencyFlags = vk::DependencyFlagBits::eByRegion, - .bufferMemoryBarrierCount = u32(buffer_barriers.size()), - .pBufferMemoryBarriers = buffer_barriers.data(), - }; - scheduler.EndRendering(); - cmdbuf.pipelineBarrier2(dependencies); - } - - // Bind descriptor set. - if (uses_push_descriptors) { - cmdbuf.pushDescriptorSetKHR(vk::PipelineBindPoint::eGraphics, *pipeline_layout, 0, - set_writes); - return; - } - const auto desc_set = desc_heap.Commit(*desc_layout); - for (auto& set_write : set_writes) { - set_write.dstSet = desc_set; - } - instance.GetDevice().updateDescriptorSets(set_writes, {}); - cmdbuf.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, *pipeline_layout, 0, desc_set, {}); -} - } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index f7762eb12..4f4abfd16 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include + #include "common/types.h" #include "video_core/renderer_vulkan/liverpool_to_vk.h" #include "video_core/renderer_vulkan/vk_common.h" @@ -14,8 +15,8 @@ class TextureCache; namespace Vulkan { -static constexpr u32 MaxVertexBufferCount = 32; static constexpr u32 MaxShaderStages = 5; +static constexpr u32 MaxVertexBufferCount = 32; class Instance; class Scheduler; @@ -61,13 +62,6 @@ public: std::span modules); ~GraphicsPipeline(); - void BindResources(const Liverpool::Regs& regs, VideoCore::BufferCache& buffer_cache, - VideoCore::TextureCache& texture_cache) const; - - const Shader::Info& GetStage(Shader::Stage stage) const noexcept { - return *stages[u32(stage)]; - } - bool IsEmbeddedVs() const noexcept { static constexpr size_t EmbeddedVsHash = 0x9b2da5cf47f8c29f; return key.stage_hashes[u32(Shader::Stage::Vertex)] == EmbeddedVsHash; @@ -99,9 +93,7 @@ private: void BuildDescSetLayout(); private: - std::array stages{}; GraphicsPipelineKey key; - bool uses_push_descriptors{}; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index 7e44bbf09..43facbae4 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h @@ -38,8 +38,6 @@ struct Program { }; class PipelineCache { - static constexpr size_t MaxShaderStages = 5; - public: explicit PipelineCache(const Instance& instance, Scheduler& scheduler, AmdGpu::Liverpool* liverpool); diff --git a/src/video_core/renderer_vulkan/vk_pipeline_common.cpp b/src/video_core/renderer_vulkan/vk_pipeline_common.cpp index 4c297cd42..6b48a40a0 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_common.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_common.cpp @@ -12,230 +12,47 @@ namespace Vulkan { -boost::container::static_vector Pipeline::image_infos; -boost::container::static_vector Pipeline::buffer_views; -boost::container::static_vector Pipeline::buffer_infos; - Pipeline::Pipeline(const Instance& instance_, Scheduler& scheduler_, DescriptorHeap& desc_heap_, - vk::PipelineCache pipeline_cache) - : instance{instance_}, scheduler{scheduler_}, desc_heap{desc_heap_} {} + vk::PipelineCache pipeline_cache, bool is_compute_ /*= false*/) + : instance{instance_}, scheduler{scheduler_}, desc_heap{desc_heap_}, is_compute{is_compute_} {} Pipeline::~Pipeline() = default; -void Pipeline::BindBuffers(VideoCore::BufferCache& buffer_cache, - VideoCore::TextureCache& texture_cache, const Shader::Info& stage, - Shader::Backend::Bindings& binding, Shader::PushData& push_data, - DescriptorWrites& set_writes, BufferBarriers& buffer_barriers) const { - using BufferBindingInfo = std::pair; - static boost::container::static_vector buffer_bindings; +void Pipeline::BindResources(DescriptorWrites& set_writes, const BufferBarriers& buffer_barriers, + const Shader::PushData& push_data) const { + const auto cmdbuf = scheduler.CommandBuffer(); + const auto bind_point = + IsCompute() ? vk::PipelineBindPoint::eCompute : vk::PipelineBindPoint::eGraphics; - buffer_bindings.clear(); - - for (const auto& desc : stage.buffers) { - const auto vsharp = desc.GetSharp(stage); - if (!desc.is_gds_buffer && vsharp.base_address != 0 && vsharp.GetSize() > 0) { - const auto buffer_id = buffer_cache.FindBuffer(vsharp.base_address, vsharp.GetSize()); - buffer_bindings.emplace_back(buffer_id, vsharp); - } else { - buffer_bindings.emplace_back(VideoCore::BufferId{}, vsharp); - } + if (!buffer_barriers.empty()) { + const auto dependencies = vk::DependencyInfo{ + .dependencyFlags = vk::DependencyFlagBits::eByRegion, + .bufferMemoryBarrierCount = u32(buffer_barriers.size()), + .pBufferMemoryBarriers = buffer_barriers.data(), + }; + scheduler.EndRendering(); + cmdbuf.pipelineBarrier2(dependencies); } - using TexBufferBindingInfo = std::pair; - static boost::container::static_vector texbuffer_bindings; + const auto stage_flags = IsCompute() ? vk::ShaderStageFlagBits::eCompute : gp_stage_flags; + cmdbuf.pushConstants(*pipeline_layout, stage_flags, 0u, sizeof(push_data), &push_data); - texbuffer_bindings.clear(); - - for (const auto& desc : stage.texture_buffers) { - const auto vsharp = desc.GetSharp(stage); - if (vsharp.base_address != 0 && vsharp.GetSize() > 0 && - vsharp.GetDataFmt() != AmdGpu::DataFormat::FormatInvalid) { - const auto buffer_id = buffer_cache.FindBuffer(vsharp.base_address, vsharp.GetSize()); - texbuffer_bindings.emplace_back(buffer_id, vsharp); - } else { - texbuffer_bindings.emplace_back(VideoCore::BufferId{}, vsharp); - } + // Bind descriptor set. + if (set_writes.empty()) { + return; } - // Bind the flattened user data buffer as a UBO so it's accessible to the shader - if (stage.has_readconst) { - const auto [vk_buffer, offset] = buffer_cache.ObtainHostUBO(stage.flattened_ud_buf); - buffer_infos.emplace_back(vk_buffer->Handle(), offset, - stage.flattened_ud_buf.size() * sizeof(u32)); - set_writes.push_back({ - .dstSet = VK_NULL_HANDLE, - .dstBinding = binding.unified++, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = vk::DescriptorType::eUniformBuffer, - .pBufferInfo = &buffer_infos.back(), - }); - ++binding.buffer; + if (uses_push_descriptors) { + cmdbuf.pushDescriptorSetKHR(bind_point, *pipeline_layout, 0, set_writes); + return; } - // Second pass to re-bind buffers that were updated after binding - for (u32 i = 0; i < buffer_bindings.size(); i++) { - const auto& [buffer_id, vsharp] = buffer_bindings[i]; - const auto& desc = stage.buffers[i]; - const bool is_storage = desc.IsStorage(vsharp); - if (!buffer_id) { - if (desc.is_gds_buffer) { - const auto* gds_buf = buffer_cache.GetGdsBuffer(); - buffer_infos.emplace_back(gds_buf->Handle(), 0, gds_buf->SizeBytes()); - } else if (instance.IsNullDescriptorSupported()) { - buffer_infos.emplace_back(VK_NULL_HANDLE, 0, VK_WHOLE_SIZE); - } else { - auto& null_buffer = buffer_cache.GetBuffer(VideoCore::NULL_BUFFER_ID); - buffer_infos.emplace_back(null_buffer.Handle(), 0, VK_WHOLE_SIZE); - } - } else { - const auto [vk_buffer, offset] = buffer_cache.ObtainBuffer( - vsharp.base_address, vsharp.GetSize(), desc.is_written, false, buffer_id); - const u32 alignment = - is_storage ? instance.StorageMinAlignment() : instance.UniformMinAlignment(); - const u32 offset_aligned = Common::AlignDown(offset, alignment); - const u32 adjust = offset - offset_aligned; - ASSERT(adjust % 4 == 0); - push_data.AddOffset(binding.buffer, adjust); - buffer_infos.emplace_back(vk_buffer->Handle(), offset_aligned, - vsharp.GetSize() + adjust); - } - - set_writes.push_back({ - .dstSet = VK_NULL_HANDLE, - .dstBinding = binding.unified++, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = is_storage ? vk::DescriptorType::eStorageBuffer - : vk::DescriptorType::eUniformBuffer, - .pBufferInfo = &buffer_infos.back(), - }); - ++binding.buffer; - } - - const auto null_buffer_view = - instance.IsNullDescriptorSupported() ? VK_NULL_HANDLE : buffer_cache.NullBufferView(); - for (u32 i = 0; i < texbuffer_bindings.size(); i++) { - const auto& [buffer_id, vsharp] = texbuffer_bindings[i]; - const auto& desc = stage.texture_buffers[i]; - vk::BufferView& buffer_view = buffer_views.emplace_back(null_buffer_view); - if (buffer_id) { - const u32 alignment = instance.TexelBufferMinAlignment(); - const auto [vk_buffer, offset] = buffer_cache.ObtainBuffer( - vsharp.base_address, vsharp.GetSize(), desc.is_written, true, buffer_id); - const u32 fmt_stride = AmdGpu::NumBits(vsharp.GetDataFmt()) >> 3; - ASSERT_MSG(fmt_stride == vsharp.GetStride(), - "Texel buffer stride must match format stride"); - const u32 offset_aligned = Common::AlignDown(offset, alignment); - const u32 adjust = offset - offset_aligned; - ASSERT(adjust % fmt_stride == 0); - push_data.AddOffset(binding.buffer, adjust / fmt_stride); - buffer_view = - vk_buffer->View(offset_aligned, vsharp.GetSize() + adjust, desc.is_written, - vsharp.GetDataFmt(), vsharp.GetNumberFmt()); - if (auto barrier = - vk_buffer->GetBarrier(desc.is_written ? vk::AccessFlagBits2::eShaderWrite - : vk::AccessFlagBits2::eShaderRead, - vk::PipelineStageFlagBits2::eComputeShader)) { - buffer_barriers.emplace_back(*barrier); - } - if (desc.is_written) { - texture_cache.InvalidateMemoryFromGPU(vsharp.base_address, vsharp.GetSize()); - } - } - - set_writes.push_back({ - .dstSet = VK_NULL_HANDLE, - .dstBinding = binding.unified++, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = desc.is_written ? vk::DescriptorType::eStorageTexelBuffer - : vk::DescriptorType::eUniformTexelBuffer, - .pTexelBufferView = &buffer_view, - }); - ++binding.buffer; - } -} - -void Pipeline::BindTextures(VideoCore::TextureCache& texture_cache, const Shader::Info& stage, - Shader::Backend::Bindings& binding, - DescriptorWrites& set_writes) const { - - using ImageBindingInfo = std::tuple; - static boost::container::static_vector image_bindings; - - image_bindings.clear(); - - for (const auto& image_desc : stage.images) { - const auto tsharp = image_desc.GetSharp(stage); - if (tsharp.GetDataFmt() != AmdGpu::DataFormat::FormatInvalid) { - VideoCore::ImageInfo image_info{tsharp, image_desc}; - const auto image_id = texture_cache.FindImage(image_info); - auto& image = texture_cache.GetImage(image_id); - image.flags |= VideoCore::ImageFlagBits::Bound; - image_bindings.emplace_back(image_id, tsharp, image_desc); - } else { - image_bindings.emplace_back(VideoCore::ImageId{}, tsharp, image_desc); - } - - if (texture_cache.IsMeta(tsharp.Address())) { - LOG_WARNING(Render_Vulkan, "Unexpected metadata read by a PS shader (texture)"); - } - } - - // Second pass to re-bind images that were updated after binding - for (auto [image_id, tsharp, desc] : image_bindings) { - if (!image_id) { - if (instance.IsNullDescriptorSupported()) { - image_infos.emplace_back(VK_NULL_HANDLE, VK_NULL_HANDLE, vk::ImageLayout::eGeneral); - } else { - auto& null_image = texture_cache.GetImageView(VideoCore::NULL_IMAGE_VIEW_ID); - image_infos.emplace_back(VK_NULL_HANDLE, *null_image.image_view, - vk::ImageLayout::eGeneral); - } - } else { - auto& image = texture_cache.GetImage(image_id); - if (True(image.flags & VideoCore::ImageFlagBits::NeedsRebind)) { - image_id = texture_cache.FindImage(image.info); - } - VideoCore::ImageViewInfo view_info{tsharp, desc}; - auto& image_view = texture_cache.FindTexture(image_id, view_info); - image_infos.emplace_back(VK_NULL_HANDLE, *image_view.image_view, - texture_cache.GetImage(image_id).last_state.layout); - image.flags &= - ~(VideoCore::ImageFlagBits::NeedsRebind | VideoCore::ImageFlagBits::Bound); - } - - set_writes.push_back({ - .dstSet = VK_NULL_HANDLE, - .dstBinding = binding.unified++, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = desc.is_storage ? vk::DescriptorType::eStorageImage - : vk::DescriptorType::eSampledImage, - .pImageInfo = &image_infos.back(), - }); - } - - for (const auto& sampler : stage.samplers) { - auto ssharp = sampler.GetSharp(stage); - if (sampler.disable_aniso) { - const auto& tsharp = stage.images[sampler.associated_image].GetSharp(stage); - if (tsharp.base_level == 0 && tsharp.last_level == 0) { - ssharp.max_aniso.Assign(AmdGpu::AnisoRatio::One); - } - } - const auto vk_sampler = texture_cache.GetSampler(ssharp); - image_infos.emplace_back(vk_sampler, VK_NULL_HANDLE, vk::ImageLayout::eGeneral); - set_writes.push_back({ - .dstSet = VK_NULL_HANDLE, - .dstBinding = binding.unified++, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = vk::DescriptorType::eSampler, - .pImageInfo = &image_infos.back(), - }); + const auto desc_set = desc_heap.Commit(*desc_layout); + for (auto& set_write : set_writes) { + set_write.dstSet = desc_set; } + instance.GetDevice().updateDescriptorSets(set_writes, {}); + cmdbuf.bindDescriptorSets(bind_point, *pipeline_layout, 0, desc_set, {}); } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_pipeline_common.h b/src/video_core/renderer_vulkan/vk_pipeline_common.h index 75764bfa6..8c48c83f7 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_common.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_common.h @@ -6,14 +6,18 @@ #include "shader_recompiler/backend/bindings.h" #include "shader_recompiler/info.h" #include "video_core/renderer_vulkan/vk_common.h" +#include "video_core/texture_cache/texture_cache.h" namespace VideoCore { class BufferCache; -class TextureCache; } // namespace VideoCore namespace Vulkan { +static constexpr auto gp_stage_flags = vk::ShaderStageFlagBits::eVertex | + vk::ShaderStageFlagBits::eGeometry | + vk::ShaderStageFlagBits::eFragment; + class Instance; class Scheduler; class DescriptorHeap; @@ -21,7 +25,7 @@ class DescriptorHeap; class Pipeline { public: Pipeline(const Instance& instance, Scheduler& scheduler, DescriptorHeap& desc_heap, - vk::PipelineCache pipeline_cache); + vk::PipelineCache pipeline_cache, bool is_compute = false); virtual ~Pipeline(); vk::Pipeline Handle() const noexcept { @@ -32,16 +36,27 @@ public: return *pipeline_layout; } + auto GetStages() const { + if (is_compute) { + return std::span{stages.cend() - 1, stages.cend()}; + } else { + return std::span{stages.cbegin(), stages.cend() - 1}; + } + } + + const Shader::Info& GetStage(Shader::Stage stage) const noexcept { + return *stages[u32(stage)]; + } + + bool IsCompute() const { + return is_compute; + } + using DescriptorWrites = boost::container::small_vector; using BufferBarriers = boost::container::small_vector; - void BindBuffers(VideoCore::BufferCache& buffer_cache, VideoCore::TextureCache& texture_cache, - const Shader::Info& stage, Shader::Backend::Bindings& binding, - Shader::PushData& push_data, DescriptorWrites& set_writes, - BufferBarriers& buffer_barriers) const; - - void BindTextures(VideoCore::TextureCache& texture_cache, const Shader::Info& stage, - Shader::Backend::Bindings& binding, DescriptorWrites& set_writes) const; + void BindResources(DescriptorWrites& set_writes, const BufferBarriers& buffer_barriers, + const Shader::PushData& push_data) const; protected: const Instance& instance; @@ -50,9 +65,9 @@ protected: vk::UniquePipeline pipeline; vk::UniquePipelineLayout pipeline_layout; vk::UniqueDescriptorSetLayout desc_layout; - static boost::container::static_vector image_infos; - static boost::container::static_vector buffer_views; - static boost::container::static_vector buffer_infos; + std::array stages{}; + bool uses_push_descriptors{}; + const bool is_compute; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_presenter.h b/src/video_core/renderer_vulkan/vk_presenter.h index cb44a352a..4d9226dec 100644 --- a/src/video_core/renderer_vulkan/vk_presenter.h +++ b/src/video_core/renderer_vulkan/vk_presenter.h @@ -55,8 +55,8 @@ public: Frame* PrepareFrame(const Libraries::VideoOut::BufferAttributeGroup& attribute, VAddr cpu_address, bool is_eop) { - const auto info = VideoCore::ImageInfo{attribute, cpu_address}; - const auto image_id = texture_cache.FindImage(info); + auto desc = VideoCore::TextureCache::VideoOutDesc{attribute, cpu_address}; + const auto image_id = texture_cache.FindImage(desc); texture_cache.UpdateImage(image_id, is_eop ? nullptr : &flip_scheduler); return PrepareFrameInternal(image_id, is_eop); } @@ -68,9 +68,11 @@ public: VideoCore::Image& RegisterVideoOutSurface( const Libraries::VideoOut::BufferAttributeGroup& attribute, VAddr cpu_address) { vo_buffers_addr.emplace_back(cpu_address); - const auto info = VideoCore::ImageInfo{attribute, cpu_address}; - const auto image_id = texture_cache.FindImage(info); - return texture_cache.GetImage(image_id); + auto desc = VideoCore::TextureCache::VideoOutDesc{attribute, cpu_address}; + const auto image_id = texture_cache.FindImage(desc); + auto& image = texture_cache.GetImage(image_id); + image.usage.vo_surface = 1u; + return image; } bool IsVideoOutSurface(const AmdGpu::Liverpool::ColorBuffer& color_buffer) { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 3c4012a26..98e41fea7 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -75,6 +75,105 @@ bool Rasterizer::FilterDraw() { return true; } +RenderState Rasterizer::PrepareRenderState(u32 mrt_mask) { + // Prefetch color and depth buffers to let texture cache handle possible overlaps with bound + // textures (e.g. mipgen) + RenderState state; + + cb_descs.clear(); + db_desc.reset(); + + const auto& regs = liverpool->regs; + + if (regs.color_control.degamma_enable) { + LOG_WARNING(Render_Vulkan, "Color buffers require gamma correction"); + } + + for (auto col_buf_id = 0u; col_buf_id < Liverpool::NumColorBuffers; ++col_buf_id) { + const auto& col_buf = regs.color_buffers[col_buf_id]; + if (!col_buf) { + continue; + } + + // If the color buffer is still bound but rendering to it is disabled by the target + // mask, we need to prevent the render area from being affected by unbound render target + // extents. + if (!regs.color_target_mask.GetMask(col_buf_id)) { + continue; + } + + // Skip stale color buffers if shader doesn't output to them. Otherwise it will perform + // an unnecessary transition and may result in state conflict if the resource is already + // bound for reading. + if ((mrt_mask & (1 << col_buf_id)) == 0) { + continue; + } + + const bool is_clear = texture_cache.IsMetaCleared(col_buf.CmaskAddress()); + texture_cache.TouchMeta(col_buf.CmaskAddress(), false); + + const auto& hint = liverpool->last_cb_extent[col_buf_id]; + auto& [image_id, desc] = cb_descs.emplace_back(std::piecewise_construct, std::tuple{}, + std::tuple{col_buf, hint}); + const auto& image_view = texture_cache.FindRenderTarget(desc); + image_id = bound_images.emplace_back(image_view.image_id); + auto& image = texture_cache.GetImage(image_id); + image.binding.is_target = 1u; + + const auto mip = image_view.info.range.base.level; + state.width = std::min(state.width, std::max(image.info.size.width >> mip, 1u)); + state.height = std::min(state.height, std::max(image.info.size.height >> mip, 1u)); + state.color_images[state.num_color_attachments] = image.image; + state.color_attachments[state.num_color_attachments++] = { + .imageView = *image_view.image_view, + .imageLayout = vk::ImageLayout::eUndefined, + .loadOp = is_clear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, + .storeOp = vk::AttachmentStoreOp::eStore, + .clearValue = + is_clear ? LiverpoolToVK::ColorBufferClearValue(col_buf) : vk::ClearValue{}, + }; + } + + using ZFormat = AmdGpu::Liverpool::DepthBuffer::ZFormat; + using StencilFormat = AmdGpu::Liverpool::DepthBuffer::StencilFormat; + if (regs.depth_buffer.Address() != 0 && + ((regs.depth_control.depth_enable && regs.depth_buffer.z_info.format != ZFormat::Invalid) || + (regs.depth_control.stencil_enable && + regs.depth_buffer.stencil_info.format != StencilFormat::Invalid))) { + const auto htile_address = regs.depth_htile_data_base.GetAddress(); + const bool is_clear = regs.depth_render_control.depth_clear_enable || + texture_cache.IsMetaCleared(htile_address); + const auto& hint = liverpool->last_db_extent; + auto& [image_id, desc] = + db_desc.emplace(std::piecewise_construct, std::tuple{}, + std::tuple{regs.depth_buffer, regs.depth_view, regs.depth_control, + htile_address, hint}); + const auto& image_view = texture_cache.FindDepthTarget(desc); + image_id = bound_images.emplace_back(image_view.image_id); + auto& image = texture_cache.GetImage(image_id); + image.binding.is_target = 1u; + + state.width = std::min(state.width, image.info.size.width); + state.height = std::min(state.height, image.info.size.height); + state.depth_image = image.image; + state.depth_attachment = { + .imageView = *image_view.image_view, + .imageLayout = vk::ImageLayout::eUndefined, + .loadOp = is_clear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, + .storeOp = vk::AttachmentStoreOp::eStore, + .clearValue = vk::ClearValue{.depthStencil = {.depth = regs.depth_clear, + .stencil = regs.stencil_clear}}, + }; + texture_cache.TouchMeta(htile_address, false); + state.has_depth = + regs.depth_buffer.z_info.format != AmdGpu::Liverpool::DepthBuffer::ZFormat::Invalid; + state.has_stencil = regs.depth_buffer.stencil_info.format != + AmdGpu::Liverpool::DepthBuffer::StencilFormat::Invalid; + } + + return state; +} + void Rasterizer::Draw(bool is_indexed, u32 index_offset) { RENDERER_TRACE; @@ -82,28 +181,30 @@ void Rasterizer::Draw(bool is_indexed, u32 index_offset) { return; } - const auto cmdbuf = scheduler.CommandBuffer(); const auto& regs = liverpool->regs; const GraphicsPipeline* pipeline = pipeline_cache.GetGraphicsPipeline(); if (!pipeline) { return; } - try { - pipeline->BindResources(regs, buffer_cache, texture_cache); - } catch (...) { - UNREACHABLE(); + auto state = PrepareRenderState(pipeline->GetMrtMask()); + + if (!BindResources(pipeline)) { + return; } const auto& vs_info = pipeline->GetStage(Shader::Stage::Vertex); buffer_cache.BindVertexBuffers(vs_info); const u32 num_indices = buffer_cache.BindIndexBuffer(is_indexed, index_offset); - BeginRendering(*pipeline); + BeginRendering(*pipeline, state); UpdateDynamicState(*pipeline); const auto [vertex_offset, instance_offset] = vs_info.GetDrawOffsets(); + const auto cmdbuf = scheduler.CommandBuffer(); + cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline->Handle()); + if (is_indexed) { cmdbuf.drawIndexed(num_indices, regs.num_instances.NumInstances(), 0, s32(vertex_offset), instance_offset); @@ -113,6 +214,8 @@ void Rasterizer::Draw(bool is_indexed, u32 index_offset) { cmdbuf.draw(num_vertices, regs.num_instances.NumInstances(), vertex_offset, instance_offset); } + + ResetBindings(); } void Rasterizer::DrawIndirect(bool is_indexed, VAddr arg_address, u32 offset, u32 stride, @@ -123,19 +226,19 @@ void Rasterizer::DrawIndirect(bool is_indexed, VAddr arg_address, u32 offset, u3 return; } - const auto& regs = liverpool->regs; const GraphicsPipeline* pipeline = pipeline_cache.GetGraphicsPipeline(); if (!pipeline) { return; } + auto state = PrepareRenderState(pipeline->GetMrtMask()); + + const auto& regs = liverpool->regs; ASSERT_MSG(regs.primitive_type != AmdGpu::PrimitiveType::RectList, "Unsupported primitive type for indirect draw"); - try { - pipeline->BindResources(regs, buffer_cache, texture_cache); - } catch (...) { - UNREACHABLE(); + if (!BindResources(pipeline)) { + return; } const auto& vs_info = pipeline->GetStage(Shader::Stage::Vertex); @@ -151,13 +254,15 @@ void Rasterizer::DrawIndirect(bool is_indexed, VAddr arg_address, u32 offset, u3 std::tie(count_buffer, count_base) = buffer_cache.ObtainBuffer(count_address, 4, false); } - BeginRendering(*pipeline); + BeginRendering(*pipeline, state); UpdateDynamicState(*pipeline); // We can safely ignore both SGPR UD indices and results of fetch shader parsing, as vertex and // instance offsets will be automatically applied by Vulkan from indirect args buffer. const auto cmdbuf = scheduler.CommandBuffer(); + cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline->Handle()); + if (is_indexed) { ASSERT(sizeof(VkDrawIndexedIndirectCommand) == stride); @@ -177,6 +282,8 @@ void Rasterizer::DrawIndirect(bool is_indexed, VAddr arg_address, u32 offset, u3 cmdbuf.drawIndirect(buffer->Handle(), base, max_count, stride); } } + + ResetBindings(); } void Rasterizer::DispatchDirect() { @@ -189,18 +296,15 @@ void Rasterizer::DispatchDirect() { return; } - try { - const auto has_resources = pipeline->BindResources(buffer_cache, texture_cache); - if (!has_resources) { - return; - } - } catch (...) { - UNREACHABLE(); + if (!BindResources(pipeline)) { + return; } scheduler.EndRendering(); cmdbuf.bindPipeline(vk::PipelineBindPoint::eCompute, pipeline->Handle()); cmdbuf.dispatch(cs_program.dim_x, cs_program.dim_y, cs_program.dim_z); + + ResetBindings(); } void Rasterizer::DispatchIndirect(VAddr address, u32 offset, u32 size) { @@ -213,19 +317,16 @@ void Rasterizer::DispatchIndirect(VAddr address, u32 offset, u32 size) { return; } - try { - const auto has_resources = pipeline->BindResources(buffer_cache, texture_cache); - if (!has_resources) { - return; - } - } catch (...) { - UNREACHABLE(); + if (!BindResources(pipeline)) { + return; } scheduler.EndRendering(); cmdbuf.bindPipeline(vk::PipelineBindPoint::eCompute, pipeline->Handle()); const auto [buffer, base] = buffer_cache.ObtainBuffer(address + offset, size, false); cmdbuf.dispatchIndirect(buffer->Handle(), base); + + ResetBindings(); } u64 Rasterizer::Flush() { @@ -239,86 +340,386 @@ void Rasterizer::Finish() { scheduler.Finish(); } -void Rasterizer::BeginRendering(const GraphicsPipeline& pipeline) { +bool Rasterizer::BindResources(const Pipeline* pipeline) { + buffer_infos.clear(); + buffer_views.clear(); + image_infos.clear(); + const auto& regs = liverpool->regs; - RenderState state; - if (regs.color_control.degamma_enable) { - LOG_WARNING(Render_Vulkan, "Color buffers require gamma correction"); - } + if (pipeline->IsCompute()) { + const auto& info = pipeline->GetStage(Shader::Stage::Compute); - for (auto col_buf_id = 0u; col_buf_id < Liverpool::NumColorBuffers; ++col_buf_id) { - const auto& col_buf = regs.color_buffers[col_buf_id]; - if (!col_buf) { - continue; - } - - // If the color buffer is still bound but rendering to it is disabled by the target mask, - // we need to prevent the render area from being affected by unbound render target extents. - if (!regs.color_target_mask.GetMask(col_buf_id)) { - continue; - } - - // Skip stale color buffers if shader doesn't output to them. Otherwise it will perform - // an unnecessary transition and may result in state conflict if the resource is already - // bound for reading. - if ((pipeline.GetMrtMask() & (1 << col_buf_id)) == 0) { - continue; - } - - const auto& hint = liverpool->last_cb_extent[col_buf_id]; - VideoCore::ImageInfo image_info{col_buf, hint}; - VideoCore::ImageViewInfo view_info{col_buf}; - const auto& image_view = texture_cache.FindRenderTarget(image_info, view_info); - const auto& image = texture_cache.GetImage(image_view.image_id); - state.width = std::min(state.width, image.info.size.width); - state.height = std::min(state.height, image.info.size.height); - - const bool is_clear = texture_cache.IsMetaCleared(col_buf.CmaskAddress()); - state.color_images[state.num_color_attachments] = image.image; - state.color_attachments[state.num_color_attachments++] = { - .imageView = *image_view.image_view, - .imageLayout = vk::ImageLayout::eColorAttachmentOptimal, - .loadOp = is_clear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, - .storeOp = vk::AttachmentStoreOp::eStore, - .clearValue = - is_clear ? LiverpoolToVK::ColorBufferClearValue(col_buf) : vk::ClearValue{}, + // Most of the time when a metadata is updated with a shader it gets cleared. It means + // we can skip the whole dispatch and update the tracked state instead. Also, it is not + // intended to be consumed and in such rare cases (e.g. HTile introspection, CRAA) we + // will need its full emulation anyways. For cases of metadata read a warning will be + // logged. + const auto IsMetaUpdate = [&](const auto& desc) { + const VAddr address = desc.GetSharp(info).base_address; + if (desc.is_written) { + if (texture_cache.TouchMeta(address, true)) { + LOG_TRACE(Render_Vulkan, "Metadata update skipped"); + return true; + } + } else { + if (texture_cache.IsMeta(address)) { + LOG_WARNING(Render_Vulkan, "Unexpected metadata read by a CS shader (buffer)"); + } + } + return false; }; - texture_cache.TouchMeta(col_buf.CmaskAddress(), false); + + for (const auto& desc : info.buffers) { + if (desc.is_gds_buffer) { + continue; + } + if (IsMetaUpdate(desc)) { + return false; + } + } + for (const auto& desc : info.texture_buffers) { + if (IsMetaUpdate(desc)) { + return false; + } + } } - using ZFormat = AmdGpu::Liverpool::DepthBuffer::ZFormat; - using StencilFormat = AmdGpu::Liverpool::DepthBuffer::StencilFormat; - if (regs.depth_buffer.Address() != 0 && - ((regs.depth_control.depth_enable && regs.depth_buffer.z_info.format != ZFormat::Invalid) || - (regs.depth_control.stencil_enable && - regs.depth_buffer.stencil_info.format != StencilFormat::Invalid))) { - const auto htile_address = regs.depth_htile_data_base.GetAddress(); - const bool is_clear = regs.depth_render_control.depth_clear_enable || - texture_cache.IsMetaCleared(htile_address); - const auto& hint = liverpool->last_db_extent; - VideoCore::ImageInfo image_info{regs.depth_buffer, regs.depth_view.NumSlices(), - htile_address, hint}; - VideoCore::ImageViewInfo view_info{regs.depth_buffer, regs.depth_view, regs.depth_control}; - const auto& image_view = texture_cache.FindDepthTarget(image_info, view_info); - const auto& image = texture_cache.GetImage(image_view.image_id); - state.width = std::min(state.width, image.info.size.width); - state.height = std::min(state.height, image.info.size.height); - state.depth_image = image.image; - state.depth_attachment = { - .imageView = *image_view.image_view, - .imageLayout = image.last_state.layout, - .loadOp = is_clear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, - .storeOp = is_clear ? vk::AttachmentStoreOp::eNone : vk::AttachmentStoreOp::eStore, - .clearValue = vk::ClearValue{.depthStencil = {.depth = regs.depth_clear, - .stencil = regs.stencil_clear}}, - }; - texture_cache.TouchMeta(htile_address, false); - state.has_depth = - regs.depth_buffer.z_info.format != AmdGpu::Liverpool::DepthBuffer::ZFormat::Invalid; - state.has_stencil = regs.depth_buffer.stencil_info.format != - AmdGpu::Liverpool::DepthBuffer::StencilFormat::Invalid; + set_writes.clear(); + buffer_barriers.clear(); + + // Bind resource buffers and textures. + Shader::PushData push_data{}; + Shader::Backend::Bindings binding{}; + + for (const auto* stage : pipeline->GetStages()) { + if (!stage) { + continue; + } + if (stage->uses_step_rates) { + push_data.step0 = regs.vgt_instance_step_rate_0; + push_data.step1 = regs.vgt_instance_step_rate_1; + } + stage->PushUd(binding, push_data); + + BindBuffers(*stage, binding, push_data, set_writes, buffer_barriers); + BindTextures(*stage, binding, set_writes); } + + pipeline->BindResources(set_writes, buffer_barriers, push_data); + + return true; +} + +void Rasterizer::BindBuffers(const Shader::Info& stage, Shader::Backend::Bindings& binding, + Shader::PushData& push_data, Pipeline::DescriptorWrites& set_writes, + Pipeline::BufferBarriers& buffer_barriers) { + buffer_bindings.clear(); + + for (const auto& desc : stage.buffers) { + const auto vsharp = desc.GetSharp(stage); + if (!desc.is_gds_buffer && vsharp.base_address != 0 && vsharp.GetSize() > 0) { + const auto buffer_id = buffer_cache.FindBuffer(vsharp.base_address, vsharp.GetSize()); + buffer_bindings.emplace_back(buffer_id, vsharp); + } else { + buffer_bindings.emplace_back(VideoCore::BufferId{}, vsharp); + } + } + + texbuffer_bindings.clear(); + + for (const auto& desc : stage.texture_buffers) { + const auto vsharp = desc.GetSharp(stage); + if (vsharp.base_address != 0 && vsharp.GetSize() > 0 && + vsharp.GetDataFmt() != AmdGpu::DataFormat::FormatInvalid) { + const auto buffer_id = buffer_cache.FindBuffer(vsharp.base_address, vsharp.GetSize()); + texbuffer_bindings.emplace_back(buffer_id, vsharp); + } else { + texbuffer_bindings.emplace_back(VideoCore::BufferId{}, vsharp); + } + } + + // Bind the flattened user data buffer as a UBO so it's accessible to the shader + if (stage.has_readconst) { + const auto [vk_buffer, offset] = buffer_cache.ObtainHostUBO(stage.flattened_ud_buf); + buffer_infos.emplace_back(vk_buffer->Handle(), offset, + stage.flattened_ud_buf.size() * sizeof(u32)); + set_writes.push_back({ + .dstSet = VK_NULL_HANDLE, + .dstBinding = binding.unified++, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eUniformBuffer, + .pBufferInfo = &buffer_infos.back(), + }); + ++binding.buffer; + } + + // Second pass to re-bind buffers that were updated after binding + for (u32 i = 0; i < buffer_bindings.size(); i++) { + const auto& [buffer_id, vsharp] = buffer_bindings[i]; + const auto& desc = stage.buffers[i]; + const bool is_storage = desc.IsStorage(vsharp); + if (!buffer_id) { + if (desc.is_gds_buffer) { + const auto* gds_buf = buffer_cache.GetGdsBuffer(); + buffer_infos.emplace_back(gds_buf->Handle(), 0, gds_buf->SizeBytes()); + } else if (instance.IsNullDescriptorSupported()) { + buffer_infos.emplace_back(VK_NULL_HANDLE, 0, VK_WHOLE_SIZE); + } else { + auto& null_buffer = buffer_cache.GetBuffer(VideoCore::NULL_BUFFER_ID); + buffer_infos.emplace_back(null_buffer.Handle(), 0, VK_WHOLE_SIZE); + } + } else { + const auto [vk_buffer, offset] = buffer_cache.ObtainBuffer( + vsharp.base_address, vsharp.GetSize(), desc.is_written, false, buffer_id); + const u32 alignment = + is_storage ? instance.StorageMinAlignment() : instance.UniformMinAlignment(); + const u32 offset_aligned = Common::AlignDown(offset, alignment); + const u32 adjust = offset - offset_aligned; + ASSERT(adjust % 4 == 0); + push_data.AddOffset(binding.buffer, adjust); + buffer_infos.emplace_back(vk_buffer->Handle(), offset_aligned, + vsharp.GetSize() + adjust); + } + + set_writes.push_back({ + .dstSet = VK_NULL_HANDLE, + .dstBinding = binding.unified++, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = is_storage ? vk::DescriptorType::eStorageBuffer + : vk::DescriptorType::eUniformBuffer, + .pBufferInfo = &buffer_infos.back(), + }); + ++binding.buffer; + } + + const auto null_buffer_view = + instance.IsNullDescriptorSupported() ? VK_NULL_HANDLE : buffer_cache.NullBufferView(); + for (u32 i = 0; i < texbuffer_bindings.size(); i++) { + const auto& [buffer_id, vsharp] = texbuffer_bindings[i]; + const auto& desc = stage.texture_buffers[i]; + vk::BufferView& buffer_view = buffer_views.emplace_back(null_buffer_view); + if (buffer_id) { + const u32 alignment = instance.TexelBufferMinAlignment(); + const auto [vk_buffer, offset] = buffer_cache.ObtainBuffer( + vsharp.base_address, vsharp.GetSize(), desc.is_written, true, buffer_id); + const u32 fmt_stride = AmdGpu::NumBits(vsharp.GetDataFmt()) >> 3; + ASSERT_MSG(fmt_stride == vsharp.GetStride(), + "Texel buffer stride must match format stride"); + const u32 offset_aligned = Common::AlignDown(offset, alignment); + const u32 adjust = offset - offset_aligned; + ASSERT(adjust % fmt_stride == 0); + push_data.AddOffset(binding.buffer, adjust / fmt_stride); + buffer_view = + vk_buffer->View(offset_aligned, vsharp.GetSize() + adjust, desc.is_written, + vsharp.GetDataFmt(), vsharp.GetNumberFmt()); + if (auto barrier = + vk_buffer->GetBarrier(desc.is_written ? vk::AccessFlagBits2::eShaderWrite + : vk::AccessFlagBits2::eShaderRead, + vk::PipelineStageFlagBits2::eComputeShader)) { + buffer_barriers.emplace_back(*barrier); + } + if (desc.is_written) { + texture_cache.InvalidateMemoryFromGPU(vsharp.base_address, vsharp.GetSize()); + } + } + + set_writes.push_back({ + .dstSet = VK_NULL_HANDLE, + .dstBinding = binding.unified++, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = desc.is_written ? vk::DescriptorType::eStorageTexelBuffer + : vk::DescriptorType::eUniformTexelBuffer, + .pTexelBufferView = &buffer_view, + }); + ++binding.buffer; + } +} + +void Rasterizer::BindTextures(const Shader::Info& stage, Shader::Backend::Bindings& binding, + Pipeline::DescriptorWrites& set_writes) { + image_bindings.clear(); + + for (const auto& image_desc : stage.images) { + const auto tsharp = image_desc.GetSharp(stage); + if (texture_cache.IsMeta(tsharp.Address())) { + LOG_WARNING(Render_Vulkan, "Unexpected metadata read by a shader (texture)"); + } + + if (tsharp.GetDataFmt() == AmdGpu::DataFormat::FormatInvalid) { + image_bindings.emplace_back(std::piecewise_construct, std::tuple{}, std::tuple{}); + continue; + } + + auto& [image_id, desc] = image_bindings.emplace_back(std::piecewise_construct, std::tuple{}, + std::tuple{tsharp, image_desc}); + image_id = texture_cache.FindImage(desc); + auto& image = texture_cache.GetImage(image_id); + if (image.binding.is_bound) { + // The image is already bound. In case if it is about to be used as storage we need + // to force general layout on it. + image.binding.force_general |= image_desc.is_storage; + } + if (image.binding.is_target) { + // The image is already bound as target. Since we read and output to it need to force + // general layout too. + image.binding.force_general = 1u; + } + image.binding.is_bound = 1u; + } + + // Second pass to re-bind images that were updated after binding + for (auto& [image_id, desc] : image_bindings) { + bool is_storage = desc.type == VideoCore::TextureCache::BindingType::Storage; + if (!image_id) { + if (instance.IsNullDescriptorSupported()) { + image_infos.emplace_back(VK_NULL_HANDLE, VK_NULL_HANDLE, vk::ImageLayout::eGeneral); + } else { + auto& null_image = texture_cache.GetImageView(VideoCore::NULL_IMAGE_VIEW_ID); + image_infos.emplace_back(VK_NULL_HANDLE, *null_image.image_view, + vk::ImageLayout::eGeneral); + } + } else { + if (auto& old_image = texture_cache.GetImage(image_id); + old_image.binding.needs_rebind) { + old_image.binding.Reset(); // clean up previous image binding state + image_id = texture_cache.FindImage(desc); + } + + bound_images.emplace_back(image_id); + + auto& image = texture_cache.GetImage(image_id); + auto& image_view = texture_cache.FindTexture(image_id, desc.view_info); + + if (image.binding.force_general || image.binding.is_target) { + image.Transit(vk::ImageLayout::eGeneral, + vk::AccessFlagBits2::eShaderRead | + (image.info.IsDepthStencil() + ? vk::AccessFlagBits2::eDepthStencilAttachmentWrite + : vk::AccessFlagBits2::eColorAttachmentWrite), + {}); + } else { + if (is_storage) { + image.Transit(vk::ImageLayout::eGeneral, + vk::AccessFlagBits2::eShaderRead | + vk::AccessFlagBits2::eShaderWrite, + desc.view_info.range); + } else { + const auto new_layout = image.info.IsDepthStencil() + ? vk::ImageLayout::eDepthStencilReadOnlyOptimal + : vk::ImageLayout::eShaderReadOnlyOptimal; + image.Transit(new_layout, vk::AccessFlagBits2::eShaderRead, + desc.view_info.range); + } + } + image.usage.storage |= is_storage; + image.usage.texture |= !is_storage; + + image_infos.emplace_back(VK_NULL_HANDLE, *image_view.image_view, + image.last_state.layout); + } + + set_writes.push_back({ + .dstSet = VK_NULL_HANDLE, + .dstBinding = binding.unified++, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = + is_storage ? vk::DescriptorType::eStorageImage : vk::DescriptorType::eSampledImage, + .pImageInfo = &image_infos.back(), + }); + } + + for (const auto& sampler : stage.samplers) { + auto ssharp = sampler.GetSharp(stage); + if (sampler.disable_aniso) { + const auto& tsharp = stage.images[sampler.associated_image].GetSharp(stage); + if (tsharp.base_level == 0 && tsharp.last_level == 0) { + ssharp.max_aniso.Assign(AmdGpu::AnisoRatio::One); + } + } + const auto vk_sampler = texture_cache.GetSampler(ssharp); + image_infos.emplace_back(vk_sampler, VK_NULL_HANDLE, vk::ImageLayout::eGeneral); + set_writes.push_back({ + .dstSet = VK_NULL_HANDLE, + .dstBinding = binding.unified++, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eSampler, + .pImageInfo = &image_infos.back(), + }); + } +} + +void Rasterizer::BeginRendering(const GraphicsPipeline& pipeline, RenderState& state) { + int cb_index = 0; + for (auto& [image_id, desc] : cb_descs) { + if (auto& old_img = texture_cache.GetImage(image_id); old_img.binding.needs_rebind) { + auto& view = texture_cache.FindRenderTarget(desc); + ASSERT(view.image_id != image_id); + image_id = bound_images.emplace_back(view.image_id); + auto& image = texture_cache.GetImage(view.image_id); + state.color_attachments[cb_index].imageView = *view.image_view; + state.color_attachments[cb_index].imageLayout = image.last_state.layout; + state.color_images[cb_index] = image.image; + + const auto mip = view.info.range.base.level; + state.width = std::min(state.width, std::max(image.info.size.width >> mip, 1u)); + state.height = std::min(state.height, std::max(image.info.size.height >> mip, 1u)); + ASSERT(old_img.info.size.width == state.width); + ASSERT(old_img.info.size.height == state.height); + } + auto& image = texture_cache.GetImage(image_id); + if (image.binding.force_general) { + image.Transit( + vk::ImageLayout::eGeneral, + vk::AccessFlagBits2::eColorAttachmentWrite | vk::AccessFlagBits2::eShaderRead, {}); + + } else { + image.Transit(vk::ImageLayout::eColorAttachmentOptimal, + vk::AccessFlagBits2::eColorAttachmentWrite | + vk::AccessFlagBits2::eColorAttachmentRead, + desc.view_info.range); + } + image.usage.render_target = 1u; + state.color_attachments[cb_index].imageLayout = image.last_state.layout; + ++cb_index; + } + + if (db_desc) { + const auto& image_id = std::get<0>(*db_desc); + const auto& desc = std::get<1>(*db_desc); + auto& image = texture_cache.GetImage(image_id); + ASSERT(image.binding.needs_rebind == 0); + const bool has_stencil = image.usage.stencil; + if (has_stencil) { + image.aspect_mask |= vk::ImageAspectFlagBits::eStencil; + } + if (image.binding.force_general) { + image.Transit(vk::ImageLayout::eGeneral, + vk::AccessFlagBits2::eDepthStencilAttachmentWrite | + vk::AccessFlagBits2::eShaderRead, + {}); + } else { + const auto new_layout = desc.view_info.is_storage + ? has_stencil + ? vk::ImageLayout::eDepthStencilAttachmentOptimal + : vk::ImageLayout::eDepthAttachmentOptimal + : has_stencil ? vk::ImageLayout::eDepthStencilReadOnlyOptimal + : vk::ImageLayout::eDepthReadOnlyOptimal; + image.Transit(new_layout, + vk::AccessFlagBits2::eDepthStencilAttachmentWrite | + vk::AccessFlagBits2::eDepthStencilAttachmentRead, + desc.view_info.range); + } + state.depth_attachment.imageLayout = image.last_state.layout; + image.usage.depth_target = true; + image.usage.stencil = has_stencil; + } + scheduler.BeginRendering(state); } @@ -328,10 +729,12 @@ void Rasterizer::Resolve() { // Read from MRT0, average all samples, and write to MRT1, which is one-sample const auto& mrt0_hint = liverpool->last_cb_extent[0]; const auto& mrt1_hint = liverpool->last_cb_extent[1]; - VideoCore::ImageInfo mrt0_info{liverpool->regs.color_buffers[0], mrt0_hint}; - VideoCore::ImageInfo mrt1_info{liverpool->regs.color_buffers[1], mrt1_hint}; - auto& mrt0_image = texture_cache.GetImage(texture_cache.FindImage(mrt0_info)); - auto& mrt1_image = texture_cache.GetImage(texture_cache.FindImage(mrt1_info)); + VideoCore::TextureCache::RenderTargetDesc mrt0_desc{liverpool->regs.color_buffers[0], + mrt0_hint}; + VideoCore::TextureCache::RenderTargetDesc mrt1_desc{liverpool->regs.color_buffers[1], + mrt1_hint}; + auto& mrt0_image = texture_cache.GetImage(texture_cache.FindImage(mrt0_desc)); + auto& mrt1_image = texture_cache.GetImage(texture_cache.FindImage(mrt1_desc)); VideoCore::SubresourceRange mrt0_range; mrt0_range.base.layer = liverpool->regs.color_buffers[0].view.slice_start; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index b6813aec9..5102cda38 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -19,6 +19,7 @@ class MemoryManager; namespace Vulkan { class Scheduler; +class RenderState; class GraphicsPipeline; class Rasterizer { @@ -54,7 +55,8 @@ public: void Finish(); private: - void BeginRendering(const GraphicsPipeline& pipeline); + RenderState PrepareRenderState(u32 mrt_mask); + void BeginRendering(const GraphicsPipeline& pipeline, RenderState& state); void Resolve(); void UpdateDynamicState(const GraphicsPipeline& pipeline); @@ -63,6 +65,21 @@ private: bool FilterDraw(); + void BindBuffers(const Shader::Info& stage, Shader::Backend::Bindings& binding, + Shader::PushData& push_data, Pipeline::DescriptorWrites& set_writes, + Pipeline::BufferBarriers& buffer_barriers); + + void BindTextures(const Shader::Info& stage, Shader::Backend::Bindings& binding, + Pipeline::DescriptorWrites& set_writes); + + bool BindResources(const Pipeline* pipeline); + void ResetBindings() { + for (auto& image_id : bound_images) { + texture_cache.GetImage(image_id).binding.Reset(); + } + bound_images.clear(); + } + private: const Instance& instance; Scheduler& scheduler; @@ -72,6 +89,25 @@ private: AmdGpu::Liverpool* liverpool; Core::MemoryManager* memory; PipelineCache pipeline_cache; + + boost::container::static_vector< + std::pair, 8> + cb_descs; + std::optional> db_desc; + boost::container::static_vector image_infos; + boost::container::static_vector buffer_views; + boost::container::static_vector buffer_infos; + boost::container::static_vector bound_images; + + Pipeline::DescriptorWrites set_writes; + Pipeline::BufferBarriers buffer_barriers; + + using BufferBindingInfo = std::pair; + boost::container::static_vector buffer_bindings; + using TexBufferBindingInfo = std::pair; + boost::container::static_vector texbuffer_bindings; + using ImageBindingInfo = std::pair; + boost::container::static_vector image_bindings; }; } // namespace Vulkan diff --git a/src/video_core/texture_cache/image.cpp b/src/video_core/texture_cache/image.cpp index bea2ce4ff..dc43036c6 100644 --- a/src/video_core/texture_cache/image.cpp +++ b/src/video_core/texture_cache/image.cpp @@ -61,6 +61,15 @@ bool ImageInfo::IsDepthStencil() const { } } +bool ImageInfo::HasStencil() const { + if (pixel_format == vk::Format::eD32SfloatS8Uint || + pixel_format == vk::Format::eD24UnormS8Uint || + pixel_format == vk::Format::eD16UnormS8Uint) { + return true; + } + return false; +} + static vk::ImageUsageFlags ImageUsageFlags(const ImageInfo& info) { vk::ImageUsageFlags usage = vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst | @@ -143,14 +152,17 @@ Image::Image(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_, // the texture cache should re-create the resource with the usage requested vk::ImageCreateFlags flags{vk::ImageCreateFlagBits::eMutableFormat | vk::ImageCreateFlagBits::eExtendedUsage}; - if (info.props.is_cube || (info.type == vk::ImageType::e2D && info.resources.layers >= 6)) { + const bool can_be_cube = (info.type == vk::ImageType::e2D) && + (info.resources.layers % 6 == 0) && + (info.size.width == info.size.height); + if (info.props.is_cube || can_be_cube) { flags |= vk::ImageCreateFlagBits::eCubeCompatible; } else if (info.props.is_volume) { flags |= vk::ImageCreateFlagBits::e2DArrayCompatible; } - usage = ImageUsageFlags(info); - format_features = FormatFeatureFlags(usage); + usage_flags = ImageUsageFlags(info); + format_features = FormatFeatureFlags(usage_flags); switch (info.pixel_format) { case vk::Format::eD16Unorm: @@ -170,7 +182,7 @@ Image::Image(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_, constexpr auto tiling = vk::ImageTiling::eOptimal; const auto supported_format = instance->GetSupportedFormat(info.pixel_format, format_features); const auto properties = instance->GetPhysicalDevice().getImageFormatProperties( - supported_format, info.type, tiling, usage, flags); + supported_format, info.type, tiling, usage_flags, flags); const auto supported_samples = properties.result == vk::Result::eSuccess ? properties.value.sampleCounts : vk::SampleCountFlagBits::e1; @@ -188,7 +200,7 @@ Image::Image(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_, .arrayLayers = static_cast(info.resources.layers), .samples = LiverpoolToVK::NumSamples(info.num_samples, supported_samples), .tiling = tiling, - .usage = usage, + .usage = usage_flags, .initialLayout = vk::ImageLayout::eUndefined, }; diff --git a/src/video_core/texture_cache/image.h b/src/video_core/texture_cache/image.h index 312ff97e8..8d84277d8 100644 --- a/src/video_core/texture_cache/image.h +++ b/src/video_core/texture_cache/image.h @@ -30,8 +30,6 @@ enum ImageFlagBits : u32 { Registered = 1 << 6, ///< True when the image is registered Picked = 1 << 7, ///< Temporary flag to mark the image as picked MetaRegistered = 1 << 8, ///< True when metadata for this surface is known and registered - Bound = 1 << 9, ///< True when the image is bound to a descriptor set - NeedsRebind = 1 << 10, ///< True when the image needs to be rebound }; DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits) @@ -113,7 +111,15 @@ struct Image { std::vector image_view_ids; // Resource state tracking - vk::ImageUsageFlags usage; + struct { + u32 texture : 1; + u32 storage : 1; + u32 render_target : 1; + u32 depth_target : 1; + u32 stencil : 1; + u32 vo_surface : 1; + } usage{}; + vk::ImageUsageFlags usage_flags; vk::FormatFeatureFlags2 format_features; struct State { vk::Flags pl_stage = vk::PipelineStageFlagBits2::eAllCommands; @@ -124,6 +130,22 @@ struct Image { std::vector subresource_states{}; boost::container::small_vector mip_hashes{}; u64 tick_accessed_last{0}; + + struct { + union { + struct { + u32 is_bound : 1; // the image is bound to a descriptor set + u32 is_target : 1; // the image is bound as color/depth target + u32 needs_rebind : 1; // the image needs to be rebound + u32 force_general : 1; // the image needs to be used in general layout + }; + u32 raw{}; + }; + + void Reset() { + raw = 0u; + } + } binding{}; }; } // namespace VideoCore diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index 2956a2a3e..0ed36ee39 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -245,7 +245,6 @@ ImageInfo::ImageInfo(const Libraries::VideoOut::BufferAttributeGroup& group, size.width = attrib.width; size.height = attrib.height; pitch = attrib.tiling_mode == TilingMode::Linear ? size.width : (size.width + 127) & (~127); - usage.vo_buffer = true; num_bits = attrib.pixel_format != VideoOutFormat::A16R16G16B16Float ? 32 : 64; ASSERT(num_bits == 32); @@ -277,7 +276,6 @@ ImageInfo::ImageInfo(const AmdGpu::Liverpool::ColorBuffer& buffer, resources.layers = buffer.NumSlices(); meta_info.cmask_addr = buffer.info.fast_clear ? buffer.CmaskAddress() : 0; meta_info.fmask_addr = buffer.info.compression ? buffer.FmaskAddress() : 0; - usage.render_target = true; guest_address = buffer.Address(); const auto color_slice_sz = buffer.GetColorSliceSize(); @@ -299,9 +297,6 @@ ImageInfo::ImageInfo(const AmdGpu::Liverpool::DepthBuffer& buffer, u32 num_slice pitch = buffer.Pitch(); resources.layers = num_slices; meta_info.htile_addr = buffer.z_info.tile_surface_en ? htile_address : 0; - usage.depth_target = true; - usage.stencil = - buffer.stencil_info.format != AmdGpu::Liverpool::DepthBuffer::StencilFormat::Invalid; guest_address = buffer.Address(); const auto depth_slice_sz = buffer.GetDepthSliceSize(); @@ -330,7 +325,6 @@ ImageInfo::ImageInfo(const AmdGpu::Image& image, const Shader::ImageResource& de resources.layers = image.NumLayers(desc.is_array); num_samples = image.NumSamples(); num_bits = NumBits(image.GetDataFmt()); - usage.texture = true; guest_address = image.Address(); @@ -392,7 +386,6 @@ void ImageInfo::UpdateSize() { } } mip_info.size *= mip_d; - mip_info.offset = guest_size_bytes; mips_layout.emplace_back(mip_info); guest_size_bytes += mip_info.size; @@ -400,79 +393,87 @@ void ImageInfo::UpdateSize() { guest_size_bytes *= resources.layers; } -bool ImageInfo::IsMipOf(const ImageInfo& info) const { +int ImageInfo::IsMipOf(const ImageInfo& info) const { if (!IsCompatible(info)) { - return false; + return -1; + } + + if (IsTilingCompatible(info.tiling_idx, tiling_idx)) { + return -1; } // Currently we expect only on level to be copied. if (resources.levels != 1) { - return false; + return -1; } - const int mip = info.resources.levels - resources.levels; - if (mip < 1) { - return false; + if (info.mips_layout.empty()) { + UNREACHABLE(); } + // Find mip + auto mip = -1; + for (auto m = 0; m < info.mips_layout.size(); ++m) { + if (guest_address == (info.guest_address + info.mips_layout[m].offset)) { + mip = m; + break; + } + } + + if (mip < 0) { + return -1; + } + ASSERT(mip != 0); + const auto mip_w = std::max(info.size.width >> mip, 1u); const auto mip_h = std::max(info.size.height >> mip, 1u); if ((size.width != mip_w) || (size.height != mip_h)) { - return false; + return -1; } const auto mip_d = std::max(info.size.depth >> mip, 1u); if (info.type == vk::ImageType::e3D && type == vk::ImageType::e2D) { // In case of 2D array to 3D copy, make sure we have proper number of layers. if (resources.layers != mip_d) { - return false; + return -1; } } else { if (type != info.type) { - return false; + return -1; } } - // Check if the mip has correct size. - if (info.mips_layout.size() <= mip || info.mips_layout[mip].size != guest_size_bytes) { - return false; - } - - // Ensure that address matches too. - if ((info.guest_address + info.mips_layout[mip].offset) != guest_address) { - return false; - } - - return true; + return mip; } -bool ImageInfo::IsSliceOf(const ImageInfo& info) const { +int ImageInfo::IsSliceOf(const ImageInfo& info) const { if (!IsCompatible(info)) { - return false; + return -1; } // Array slices should be of the same type. if (type != info.type) { - return false; + return -1; } // 2D dimensions of both images should be the same. if ((size.width != info.size.width) || (size.height != info.size.height)) { - return false; + return -1; } // Check for size alignment. const bool slice_size = info.guest_size_bytes / info.resources.layers; if (guest_size_bytes % slice_size != 0) { - return false; + return -1; } // Ensure that address is aligned too. - if (((info.guest_address - guest_address) % guest_size_bytes) != 0) { - return false; + const auto addr_diff = guest_address - info.guest_address; + if ((addr_diff % guest_size_bytes) != 0) { + return -1; } - return true; + return addr_diff / guest_size_bytes; } } // namespace VideoCore diff --git a/src/video_core/texture_cache/image_info.h b/src/video_core/texture_cache/image_info.h index 2ae2547f7..e12ae3be1 100644 --- a/src/video_core/texture_cache/image_info.h +++ b/src/video_core/texture_cache/image_info.h @@ -28,14 +28,28 @@ struct ImageInfo { bool IsBlockCoded() const; bool IsPacked() const; bool IsDepthStencil() const; + bool HasStencil() const; - bool IsMipOf(const ImageInfo& info) const; - bool IsSliceOf(const ImageInfo& info) const; + int IsMipOf(const ImageInfo& info) const; + int IsSliceOf(const ImageInfo& info) const; /// Verifies if images are compatible for subresource merging. bool IsCompatible(const ImageInfo& info) const { - return (pixel_format == info.pixel_format && tiling_idx == info.tiling_idx && - num_samples == info.num_samples && num_bits == info.num_bits); + return (pixel_format == info.pixel_format && num_samples == info.num_samples && + num_bits == info.num_bits); + } + + bool IsTilingCompatible(u32 lhs, u32 rhs) const { + if (lhs == rhs) { + return true; + } + if (lhs == 0x0e && rhs == 0x0d) { + return true; + } + if (lhs == 0x0d && rhs == 0x0e) { + return true; + } + return false; } void UpdateSize(); @@ -46,15 +60,6 @@ struct ImageInfo { VAddr htile_addr; } meta_info{}; - struct { - u32 texture : 1; - u32 storage : 1; - u32 render_target : 1; - u32 depth_target : 1; - u32 stencil : 1; - u32 vo_buffer : 1; - } usage{}; // Usage data tracked during image lifetime - struct { u32 is_cube : 1; u32 is_volume : 1; @@ -81,6 +86,9 @@ struct ImageInfo { VAddr guest_address{0}; u32 guest_size_bytes{0}; u32 tiling_idx{0}; // TODO: merge with existing! + + VAddr stencil_addr{0}; + u32 stencil_size{0}; }; } // namespace VideoCore diff --git a/src/video_core/texture_cache/image_view.cpp b/src/video_core/texture_cache/image_view.cpp index 8bde37941..488d44a7f 100644 --- a/src/video_core/texture_cache/image_view.cpp +++ b/src/video_core/texture_cache/image_view.cpp @@ -149,7 +149,7 @@ ImageViewInfo::ImageViewInfo(const AmdGpu::Liverpool::DepthBuffer& depth_buffer, ImageView::ImageView(const Vulkan::Instance& instance, const ImageViewInfo& info_, Image& image, ImageId image_id_) : image_id{image_id_}, info{info_} { - vk::ImageViewUsageCreateInfo usage_ci{.usage = image.usage}; + vk::ImageViewUsageCreateInfo usage_ci{.usage = image.usage_flags}; if (!info.is_storage) { usage_ci.usage &= ~vk::ImageUsageFlagBits::eStorage; } diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index 0e8dd7ccc..516c110a4 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -77,84 +77,149 @@ void TextureCache::UnmapMemory(VAddr cpu_addr, size_t size) { } } -ImageId TextureCache::ResolveDepthOverlap(const ImageInfo& requested_info, ImageId cache_image_id) { - const auto& cache_info = slot_images[cache_image_id].info; +ImageId TextureCache::ResolveDepthOverlap(const ImageInfo& requested_info, BindingType binding, + ImageId cache_image_id) { + const auto& cache_image = slot_images[cache_image_id]; - const bool was_bound_as_texture = - !cache_info.usage.depth_target && (cache_info.usage.texture || cache_info.usage.storage); - if (requested_info.usage.depth_target && was_bound_as_texture) { - auto new_image_id = slot_images.insert(instance, scheduler, requested_info); + if (!cache_image.info.IsDepthStencil() && !requested_info.IsDepthStencil()) { + return {}; + } + + const bool stencil_match = requested_info.HasStencil() == cache_image.info.HasStencil(); + const bool bpp_match = requested_info.num_bits == cache_image.info.num_bits; + + // If an image in the cache has less slices we need to expand it + bool recreate = cache_image.info.resources < requested_info.resources; + + switch (binding) { + case BindingType::Texture: + // The guest requires a depth sampled texture, but cache can offer only Rxf. Need to + // recreate the image. + recreate |= requested_info.IsDepthStencil() && !cache_image.info.IsDepthStencil(); + break; + case BindingType::Storage: + // If the guest is going to use previously created depth as storage, the image needs to be + // recreated. (TODO: Probably a case with linear rgba8 aliasing is legit) + recreate |= cache_image.info.IsDepthStencil(); + break; + case BindingType::RenderTarget: + // Render target can have only Rxf format. If the cache contains only Dx[S8] we need to + // re-create the image. + ASSERT(!requested_info.IsDepthStencil()); + recreate |= cache_image.info.IsDepthStencil(); + break; + case BindingType::DepthTarget: + // The guest has requested previously allocated texture to be bound as a depth target. + // In this case we need to convert Rx float to a Dx[S8] as requested + recreate |= !cache_image.info.IsDepthStencil(); + + // The guest is trying to bind a depth target and cache has it. Need to be sure that aspects + // and bpp match + recreate |= cache_image.info.IsDepthStencil() && !(stencil_match && bpp_match); + break; + default: + break; + } + + if (recreate) { + auto new_info{requested_info}; + new_info.resources = std::max(requested_info.resources, cache_image.info.resources); + new_info.UpdateSize(); + const auto new_image_id = slot_images.insert(instance, scheduler, new_info); RegisterImage(new_image_id); + // Inherit image usage + auto& new_image = GetImage(new_image_id); + new_image.usage = cache_image.usage; + // TODO: perform a depth copy here FreeImage(cache_image_id); return new_image_id; } - const bool should_bind_as_texture = - !requested_info.usage.depth_target && - (requested_info.usage.texture || requested_info.usage.storage); - if (cache_info.usage.depth_target && should_bind_as_texture) { - if (cache_info.resources == requested_info.resources) { - return cache_image_id; - } else { - // UNREACHABLE(); - } - } - - return {}; + // Will be handled by view + return cache_image_id; } -ImageId TextureCache::ResolveOverlap(const ImageInfo& image_info, ImageId cache_image_id, - ImageId merged_image_id) { +std::tuple TextureCache::ResolveOverlap(const ImageInfo& image_info, + BindingType binding, + ImageId cache_image_id, + ImageId merged_image_id) { auto& tex_cache_image = slot_images[cache_image_id]; + // We can assume it is safe to delete the image if it wasn't accessed in some number of frames. + const bool safe_to_delete = + scheduler.CurrentTick() - tex_cache_image.tick_accessed_last > NumFramesBeforeRemoval; if (image_info.guest_address == tex_cache_image.info.guest_address) { // Equal address if (image_info.size != tex_cache_image.info.size) { - // Very likely this kind of overlap is caused by allocation from a pool. We can assume - // it is safe to delete the image if it wasn't accessed in some amount of frames. - if (scheduler.CurrentTick() - tex_cache_image.tick_accessed_last > - NumFramesBeforeRemoval) { - + // Very likely this kind of overlap is caused by allocation from a pool. + if (safe_to_delete) { FreeImage(cache_image_id); } - return merged_image_id; + return {merged_image_id, -1, -1}; } - if (auto depth_image_id = ResolveDepthOverlap(image_info, cache_image_id)) { - return depth_image_id; + if (const auto depth_image_id = ResolveDepthOverlap(image_info, binding, cache_image_id)) { + return {depth_image_id, -1, -1}; } if (image_info.pixel_format != tex_cache_image.info.pixel_format || image_info.guest_size_bytes <= tex_cache_image.info.guest_size_bytes) { auto result_id = merged_image_id ? merged_image_id : cache_image_id; const auto& result_image = slot_images[result_id]; - return IsVulkanFormatCompatible(image_info.pixel_format, result_image.info.pixel_format) - ? result_id - : ImageId{}; + return { + IsVulkanFormatCompatible(image_info.pixel_format, result_image.info.pixel_format) + ? result_id + : ImageId{}, + -1, -1}; } ImageId new_image_id{}; if (image_info.type == tex_cache_image.info.type) { + ASSERT(image_info.resources > tex_cache_image.info.resources); new_image_id = ExpandImage(image_info, cache_image_id); } else { UNREACHABLE(); } - return new_image_id; + return {new_image_id, -1, -1}; } // Right overlap, the image requested is a possible subresource of the image from cache. if (image_info.guest_address > tex_cache_image.info.guest_address) { - // Should be handled by view. No additional actions needed. + if (auto mip = image_info.IsMipOf(tex_cache_image.info); mip >= 0) { + return {cache_image_id, mip, -1}; + } + + if (auto slice = image_info.IsSliceOf(tex_cache_image.info); slice >= 0) { + return {cache_image_id, -1, slice}; + } + + // TODO: slice and mip + + if (safe_to_delete) { + FreeImage(cache_image_id); + } + + return {{}, -1, -1}; } else { // Left overlap, the image from cache is a possible subresource of the image requested if (!merged_image_id) { // We need to have a larger, already allocated image to copy this one into - return {}; + return {{}, -1, -1}; } - if (tex_cache_image.info.IsMipOf(image_info)) { + if (auto mip = tex_cache_image.info.IsMipOf(image_info); mip >= 0) { + if (tex_cache_image.binding.is_target) { + // We have a larger image created and a separate one, representing a subres of it, + // bound as render target. In this case we need to rebind render target. + tex_cache_image.binding.needs_rebind = 1u; + GetImage(merged_image_id).binding.is_target = 1u; + + FreeImage(cache_image_id); + return {merged_image_id, -1, -1}; + } + tex_cache_image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, {}); @@ -162,13 +227,13 @@ ImageId TextureCache::ResolveOverlap(const ImageInfo& image_info, ImageId cache_ ASSERT(num_mips_to_copy == 1); auto& merged_image = slot_images[merged_image_id]; - merged_image.CopyMip(tex_cache_image, image_info.resources.levels - 1); + merged_image.CopyMip(tex_cache_image, mip); FreeImage(cache_image_id); } } - return merged_image_id; + return {merged_image_id, -1, -1}; } ImageId TextureCache::ExpandImage(const ImageInfo& info, ImageId image_id) { @@ -181,8 +246,8 @@ ImageId TextureCache::ExpandImage(const ImageInfo& info, ImageId image_id) { src_image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, {}); new_image.CopyImage(src_image); - if (True(src_image.flags & ImageFlagBits::Bound)) { - src_image.flags |= ImageFlagBits::NeedsRebind; + if (src_image.binding.is_bound || src_image.binding.is_target) { + src_image.binding.needs_rebind = 1u; } FreeImage(image_id); @@ -192,9 +257,11 @@ ImageId TextureCache::ExpandImage(const ImageInfo& info, ImageId image_id) { return new_image_id; } -ImageId TextureCache::FindImage(const ImageInfo& info, FindFlags flags) { +ImageId TextureCache::FindImage(BaseDesc& desc, FindFlags flags) { + const auto& info = desc.info; + if (info.guest_address == 0) [[unlikely]] { - return NULL_IMAGE_VIEW_ID; + return NULL_IMAGE_ID; } std::scoped_lock lock{mutex}; @@ -231,10 +298,16 @@ ImageId TextureCache::FindImage(const ImageInfo& info, FindFlags flags) { } // Try to resolve overlaps (if any) + int view_mip{-1}; + int view_slice{-1}; if (!image_id) { for (const auto& cache_id : image_ids) { + view_mip = -1; + view_slice = -1; + const auto& merged_info = image_id ? slot_images[image_id].info : info; - image_id = ResolveOverlap(merged_info, cache_id, image_id); + std::tie(image_id, view_mip, view_slice) = + ResolveOverlap(merged_info, desc.type, cache_id, image_id); } } @@ -254,6 +327,10 @@ ImageId TextureCache::FindImage(const ImageInfo& info, FindFlags flags) { RegisterImage(image_id); } + if (view_mip > 0) { + desc.view_info.range.base.level = view_mip; + } + Image& image = slot_images[image_id]; image.tick_accessed_last = scheduler.CurrentTick(); @@ -275,100 +352,58 @@ ImageView& TextureCache::RegisterImageView(ImageId image_id, const ImageViewInfo ImageView& TextureCache::FindTexture(ImageId image_id, const ImageViewInfo& view_info) { Image& image = slot_images[image_id]; UpdateImage(image_id); - auto& usage = image.info.usage; - - if (view_info.is_storage) { - image.Transit(vk::ImageLayout::eGeneral, - vk::AccessFlagBits2::eShaderRead | vk::AccessFlagBits2::eShaderWrite, - view_info.range); - usage.storage = true; - } else { - const auto new_layout = image.info.IsDepthStencil() - ? vk::ImageLayout::eDepthStencilReadOnlyOptimal - : vk::ImageLayout::eShaderReadOnlyOptimal; - image.Transit(new_layout, vk::AccessFlagBits2::eShaderRead, view_info.range); - usage.texture = true; - } - return RegisterImageView(image_id, view_info); } -ImageView& TextureCache::FindRenderTarget(const ImageInfo& image_info, - const ImageViewInfo& view_info) { - const ImageId image_id = FindImage(image_info); +ImageView& TextureCache::FindRenderTarget(BaseDesc& desc) { + const ImageId image_id = FindImage(desc); Image& image = slot_images[image_id]; image.flags |= ImageFlagBits::GpuModified; + image.usage.render_target = 1u; UpdateImage(image_id); - image.Transit(vk::ImageLayout::eColorAttachmentOptimal, - vk::AccessFlagBits2::eColorAttachmentWrite | - vk::AccessFlagBits2::eColorAttachmentRead, - view_info.range); - // Register meta data for this color buffer if (!(image.flags & ImageFlagBits::MetaRegistered)) { - if (image_info.meta_info.cmask_addr) { + if (desc.info.meta_info.cmask_addr) { surface_metas.emplace( - image_info.meta_info.cmask_addr, + desc.info.meta_info.cmask_addr, MetaDataInfo{.type = MetaDataInfo::Type::CMask, .is_cleared = true}); - image.info.meta_info.cmask_addr = image_info.meta_info.cmask_addr; + image.info.meta_info.cmask_addr = desc.info.meta_info.cmask_addr; image.flags |= ImageFlagBits::MetaRegistered; } - if (image_info.meta_info.fmask_addr) { + if (desc.info.meta_info.fmask_addr) { surface_metas.emplace( - image_info.meta_info.fmask_addr, + desc.info.meta_info.fmask_addr, MetaDataInfo{.type = MetaDataInfo::Type::FMask, .is_cleared = true}); - image.info.meta_info.fmask_addr = image_info.meta_info.fmask_addr; + image.info.meta_info.fmask_addr = desc.info.meta_info.fmask_addr; image.flags |= ImageFlagBits::MetaRegistered; } } - // Update tracked image usage - image.info.usage.render_target = true; - - return RegisterImageView(image_id, view_info); + return RegisterImageView(image_id, desc.view_info); } -ImageView& TextureCache::FindDepthTarget(const ImageInfo& image_info, - const ImageViewInfo& view_info) { - const ImageId image_id = FindImage(image_info); +ImageView& TextureCache::FindDepthTarget(BaseDesc& desc) { + const ImageId image_id = FindImage(desc); Image& image = slot_images[image_id]; image.flags |= ImageFlagBits::GpuModified; image.flags &= ~ImageFlagBits::Dirty; - image.aspect_mask = vk::ImageAspectFlagBits::eDepth; - - const bool has_stencil = image_info.usage.stencil; - if (has_stencil) { - image.aspect_mask |= vk::ImageAspectFlagBits::eStencil; - } - - const auto new_layout = view_info.is_storage - ? has_stencil ? vk::ImageLayout::eDepthStencilAttachmentOptimal - : vk::ImageLayout::eDepthAttachmentOptimal - : has_stencil ? vk::ImageLayout::eDepthStencilReadOnlyOptimal - : vk::ImageLayout::eDepthReadOnlyOptimal; - image.Transit(new_layout, - vk::AccessFlagBits2::eDepthStencilAttachmentWrite | - vk::AccessFlagBits2::eDepthStencilAttachmentRead, - view_info.range); + image.usage.depth_target = 1u; + image.usage.stencil = image.info.HasStencil(); // Register meta data for this depth buffer if (!(image.flags & ImageFlagBits::MetaRegistered)) { - if (image_info.meta_info.htile_addr) { + if (desc.info.meta_info.htile_addr) { surface_metas.emplace( - image_info.meta_info.htile_addr, + desc.info.meta_info.htile_addr, MetaDataInfo{.type = MetaDataInfo::Type::HTile, .is_cleared = true}); - image.info.meta_info.htile_addr = image_info.meta_info.htile_addr; + image.info.meta_info.htile_addr = desc.info.meta_info.htile_addr; image.flags |= ImageFlagBits::MetaRegistered; } } - // Update tracked image usage - image.info.usage.depth_target = true; - image.info.usage.stencil = has_stencil; - - return RegisterImageView(image_id, view_info); + return RegisterImageView(image_id, desc.view_info); } void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_scheduler /*= nullptr*/) { @@ -472,7 +507,7 @@ void TextureCache::RegisterImage(ImageId image_id) { void TextureCache::UnregisterImage(ImageId image_id) { Image& image = slot_images[image_id]; ASSERT_MSG(True(image.flags & ImageFlagBits::Registered), - "Trying to unregister an already registered image"); + "Trying to unregister an already unregistered image"); image.flags &= ~ImageFlagBits::Registered; ForEachPage(image.cpu_addr, image.info.guest_size_bytes, [this, image_id](u64 page) { const auto page_it = page_table.find(page); diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 96970bfc8..8ac603f06 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -43,8 +43,55 @@ class TextureCache { using PageTable = MultiLevelPageTable; public: - explicit TextureCache(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler, - BufferCache& buffer_cache, PageManager& tracker); + enum class BindingType : u32 { + Texture, + Storage, + RenderTarget, + DepthTarget, + VideoOut, + }; + + struct BaseDesc { + ImageInfo info; + ImageViewInfo view_info; + BindingType type{BindingType::Texture}; + + BaseDesc() = default; + BaseDesc(BindingType type_, ImageInfo info_, ImageViewInfo view_info_) noexcept + : info{std::move(info_)}, view_info{std::move(view_info_)}, type{type_} {} + }; + + struct TextureDesc : public BaseDesc { + TextureDesc() = default; + TextureDesc(const AmdGpu::Image& image, const Shader::ImageResource& desc) + : BaseDesc{desc.is_storage ? BindingType::Storage : BindingType::Texture, + ImageInfo{image, desc}, ImageViewInfo{image, desc}} {} + }; + + struct RenderTargetDesc : public BaseDesc { + RenderTargetDesc(const AmdGpu::Liverpool::ColorBuffer& buffer, + const AmdGpu::Liverpool::CbDbExtent& hint = {}) + : BaseDesc{BindingType::RenderTarget, ImageInfo{buffer, hint}, ImageViewInfo{buffer}} {} + }; + + struct DepthTargetDesc : public BaseDesc { + DepthTargetDesc(const AmdGpu::Liverpool::DepthBuffer& buffer, + const AmdGpu::Liverpool::DepthView& view, + const AmdGpu::Liverpool::DepthControl& ctl, VAddr htile_address, + const AmdGpu::Liverpool::CbDbExtent& hint = {}) + : BaseDesc{BindingType::DepthTarget, + ImageInfo{buffer, view.NumSlices(), htile_address, hint}, + ImageViewInfo{buffer, view, ctl}} {} + }; + + struct VideoOutDesc : public BaseDesc { + VideoOutDesc(const Libraries::VideoOut::BufferAttributeGroup& group, VAddr cpu_address) + : BaseDesc{BindingType::VideoOut, ImageInfo{group, cpu_address}, ImageViewInfo{}} {} + }; + +public: + TextureCache(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler, + BufferCache& buffer_cache, PageManager& tracker); ~TextureCache(); /// Invalidates any image in the logical page range. @@ -57,18 +104,16 @@ public: void UnmapMemory(VAddr cpu_addr, size_t size); /// Retrieves the image handle of the image with the provided attributes. - [[nodiscard]] ImageId FindImage(const ImageInfo& info, FindFlags flags = {}); + [[nodiscard]] ImageId FindImage(BaseDesc& desc, FindFlags flags = {}); /// Retrieves an image view with the properties of the specified image id. [[nodiscard]] ImageView& FindTexture(ImageId image_id, const ImageViewInfo& view_info); /// Retrieves the render target with specified properties - [[nodiscard]] ImageView& FindRenderTarget(const ImageInfo& image_info, - const ImageViewInfo& view_info); + [[nodiscard]] ImageView& FindRenderTarget(BaseDesc& desc); /// Retrieves the depth target with specified properties - [[nodiscard]] ImageView& FindDepthTarget(const ImageInfo& image_info, - const ImageViewInfo& view_info); + [[nodiscard]] ImageView& FindDepthTarget(BaseDesc& desc); /// Updates image contents if it was modified by CPU. void UpdateImage(ImageId image_id, Vulkan::Scheduler* custom_scheduler = nullptr) { @@ -77,11 +122,13 @@ public: RefreshImage(image, custom_scheduler); } - [[nodiscard]] ImageId ResolveOverlap(const ImageInfo& info, ImageId cache_img_id, - ImageId merged_image_id); + [[nodiscard]] std::tuple ResolveOverlap(const ImageInfo& info, + BindingType binding, + ImageId cache_img_id, + ImageId merged_image_id); /// Resolves depth overlap and either re-creates the image or returns existing one - [[nodiscard]] ImageId ResolveDepthOverlap(const ImageInfo& requested_info, + [[nodiscard]] ImageId ResolveDepthOverlap(const ImageInfo& requested_info, BindingType binding, ImageId cache_img_id); [[nodiscard]] ImageId ExpandImage(const ImageInfo& info, ImageId image_id); From 82bd780a6b6a95bd7e52d8d27fef5633be80830d Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Sun, 24 Nov 2024 17:45:17 +0100 Subject: [PATCH 054/549] Fix --help text in cli (#1588) --- src/main.cpp | 2 +- src/qt_gui/main.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index b12965677..17b5c11fe 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -32,7 +32,7 @@ int main(int argc, char* argv[]) { " -g, --game Specify game path to launch\n" " -p, --patch Apply specified patch file\n" " -f, --fullscreen Specify window initial fullscreen " - "state. Does not overwrite the config file." + "state. Does not overwrite the config file.\n" " -h, --help Display this help message\n"; exit(0); }}, diff --git a/src/qt_gui/main.cpp b/src/qt_gui/main.cpp index 7f9b29200..318245053 100644 --- a/src/qt_gui/main.cpp +++ b/src/qt_gui/main.cpp @@ -45,7 +45,7 @@ int main(int argc, char* argv[]) { " -p, --patch Apply specified patch file\n" " -s, --show-gui Show the GUI\n" " -f, --fullscreen Specify window initial fullscreen " - "state. Does not overwrite the config file." + "state. Does not overwrite the config file.\n" " -h, --help Display this help message\n"; exit(0); }}, From 23bb5f09fd5b296a30cefe0704f25b45145bacad Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sun, 24 Nov 2024 08:47:19 -0800 Subject: [PATCH 055/549] devtools: Fix a few compiler warnings. (#1575) --- src/core/devtools/widget/cmd_list.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/core/devtools/widget/cmd_list.cpp b/src/core/devtools/widget/cmd_list.cpp index 9a42f8238..219d25d6a 100644 --- a/src/core/devtools/widget/cmd_list.cpp +++ b/src/core/devtools/widget/cmd_list.cpp @@ -3,6 +3,7 @@ // Credits to https://github.com/psucien/tlg-emu-tools/ +#include #include #include #include @@ -1224,12 +1225,12 @@ void CmdListViewer::Draw(bool only_batches_view) { } Text("queue : %s", queue_name); - Text("base addr: %08llX", cmdb_addr); + Text("base addr: %08" PRIXPTR, cmdb_addr); SameLine(); if (SmallButton("Memory >")) { cmdb_view.Open ^= true; } - Text("size : %04llX", cmdb_size); + Text("size : %04zX", cmdb_size); Separator(); { @@ -1292,12 +1293,12 @@ void CmdListViewer::Draw(bool only_batches_view) { if (batch.type == static_cast(0xFF)) { ignore_header = true; } else if (!batch.marker.empty()) { - snprintf(batch_hdr, sizeof(batch_hdr), "%08llX: batch-%03d %s | %s", + snprintf(batch_hdr, sizeof(batch_hdr), "%08" PRIXPTR ": batch-%03d %s | %s", cmdb_addr + batch.start_addr, batch.id, Gcn::GetOpCodeName(static_cast(batch.type)), batch.marker.c_str()); } else { - snprintf(batch_hdr, sizeof(batch_hdr), "%08llX: batch-%03d %s", + snprintf(batch_hdr, sizeof(batch_hdr), "%08" PRIXPTR ": batch-%03d %s", cmdb_addr + batch.start_addr, batch.id, Gcn::GetOpCodeName(static_cast(batch.type))); } @@ -1348,7 +1349,7 @@ void CmdListViewer::Draw(bool only_batches_view) { } if (show_batch_content) { - auto processed_size = 0ull; + size_t processed_size = 0; auto bb = ctx.LastItemData.Rect; if (group_batches && !ignore_header) { Indent(); @@ -1364,9 +1365,9 @@ void CmdListViewer::Draw(bool only_batches_view) { op = pm4_t3->opcode; char header_name[128]; - sprintf(header_name, "%08llX: %s", - cmdb_addr + batch.start_addr + processed_size, - Gcn::GetOpCodeName((u32)op)); + snprintf(header_name, sizeof(header_name), "%08" PRIXPTR ": %s", + cmdb_addr + batch.start_addr + processed_size, + Gcn::GetOpCodeName(static_cast(op))); bool open_pm4 = TreeNode(header_name); if (!group_batches) { From 001b94e802291037d939b1eca4a84d2ecb21ca2a Mon Sep 17 00:00:00 2001 From: psucien Date: Sun, 24 Nov 2024 18:33:38 +0100 Subject: [PATCH 056/549] hot-fix: skip indirect draw for quad lists * needs to be fixed properly with indirect args re-packing --- .../renderer_vulkan/vk_rasterizer.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 98e41fea7..8dc0771de 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -226,6 +226,18 @@ void Rasterizer::DrawIndirect(bool is_indexed, VAddr arg_address, u32 offset, u3 return; } + const auto& regs = liverpool->regs; + if (regs.primitive_type == AmdGpu::PrimitiveType::QuadList) { + // For QuadList we use generated index buffer to convert quads to triangles. Since it + // changes type of the draw, arguments are not valid for this case. We need to run a + // conversion pass to repack the indirect arguments buffer first. + LOG_WARNING(Render_Vulkan, "QuadList primitive type is not supported for indirect draw"); + return; + } + + ASSERT_MSG(regs.primitive_type != AmdGpu::PrimitiveType::RectList, + "Unsupported primitive type for indirect draw"); + const GraphicsPipeline* pipeline = pipeline_cache.GetGraphicsPipeline(); if (!pipeline) { return; @@ -233,10 +245,6 @@ void Rasterizer::DrawIndirect(bool is_indexed, VAddr arg_address, u32 offset, u3 auto state = PrepareRenderState(pipeline->GetMrtMask()); - const auto& regs = liverpool->regs; - ASSERT_MSG(regs.primitive_type != AmdGpu::PrimitiveType::RectList, - "Unsupported primitive type for indirect draw"); - if (!BindResources(pipeline)) { return; } @@ -245,7 +253,7 @@ void Rasterizer::DrawIndirect(bool is_indexed, VAddr arg_address, u32 offset, u3 buffer_cache.BindVertexBuffers(vs_info); buffer_cache.BindIndexBuffer(is_indexed, 0); - const auto [buffer, base] = + const auto& [buffer, base] = buffer_cache.ObtainBuffer(arg_address + offset, stride * max_count, false); VideoCore::Buffer* count_buffer{}; From 20b4cd2a3294c636e61e7f7bc6d29309c472a0d2 Mon Sep 17 00:00:00 2001 From: F1219R <109141852+F1219R@users.noreply.github.com> Date: Mon, 25 Nov 2024 13:26:12 +0100 Subject: [PATCH 057/549] Update sq translation (#1595) --- src/qt_gui/translations/sq.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index 5715371bf..f7144a001 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -118,22 +118,22 @@ Open Folder... - Hapni Dosjen... + Hap Dosjen... Open Game Folder - Hapni Dosjen e Lojës + Hap Dosjen e Lojës Open Save Data Folder - Hapni Dosjen e të Dhënave të Ruajtura + Hap Dosjen e të Dhënave të Ruajtura Open Log Folder - Hapni Dosjen e Regjistrimeve + Hap Dosjen e Ditarit From ff18b89e666c4d335ca576ccab19d1deecac6f98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A5IGA?= <164882787+Xphalnos@users.noreply.github.com> Date: Mon, 25 Nov 2024 13:26:40 +0100 Subject: [PATCH 058/549] Fix Game List Mode (#1591) --- src/qt_gui/main_window.cpp | 9 ++++++--- src/qt_gui/main_window_ui.h | 7 +++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index f2ee87891..02c7f18a5 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -102,6 +102,7 @@ void MainWindow::CreateActions() { m_list_mode_act_group = new QActionGroup(this); m_list_mode_act_group->addAction(ui->setlistModeListAct); m_list_mode_act_group->addAction(ui->setlistModeGridAct); + m_list_mode_act_group->addAction(ui->setlistElfAct); // create action group for themes m_theme_act_group = new QActionGroup(this); @@ -362,7 +363,7 @@ void MainWindow::CreateConnects() { ui->sizeSlider->setEnabled(true); ui->sizeSlider->setSliderPosition(slider_pos_grid); }); - // Elf + // Elf Viewer connect(ui->setlistElfAct, &QAction::triggered, m_dock_widget.data(), [this]() { BackgroundMusicPlayer::getInstance().stopMusic(); m_dock_widget->setWidget(m_elf_viewer.data()); @@ -622,10 +623,12 @@ void MainWindow::ConfigureGuiFromSettings() { Config::getMainWindowGeometryW(), Config::getMainWindowGeometryH()); ui->showGameListAct->setChecked(true); - if (isTableList) { + if (Config::getTableMode() == 0) { ui->setlistModeListAct->setChecked(true); - } else { + } else if (Config::getTableMode() == 1) { ui->setlistModeGridAct->setChecked(true); + } else if (Config::getTableMode() == 2) { + ui->setlistElfAct->setChecked(true); } BackgroundMusicPlayer::getInstance().setVolume(Config::getBGMvolume()); } diff --git a/src/qt_gui/main_window_ui.h b/src/qt_gui/main_window_ui.h index a51e37d1e..cb9aa5904 100644 --- a/src/qt_gui/main_window_ui.h +++ b/src/qt_gui/main_window_ui.h @@ -110,15 +110,14 @@ public: setIconSizeLargeAct->setCheckable(true); setlistModeListAct = new QAction(MainWindow); setlistModeListAct->setObjectName("setlistModeListAct"); - setlistModeListAct->setCheckable(true); - setlistModeListAct->setChecked(true); setlistModeListAct->setIcon(QIcon(":images/list_icon.png")); + setlistModeListAct->setCheckable(true); setlistModeGridAct = new QAction(MainWindow); setlistModeGridAct->setObjectName("setlistModeGridAct"); - setlistModeGridAct->setCheckable(true); setlistModeGridAct->setIcon(QIcon(":images/grid_icon.png")); + setlistModeGridAct->setCheckable(true); setlistElfAct = new QAction(MainWindow); - setlistElfAct->setObjectName("setlistModeGridAct"); + setlistElfAct->setObjectName("setlistElfAct"); setlistElfAct->setCheckable(true); gameInstallPathAct = new QAction(MainWindow); gameInstallPathAct->setObjectName("gameInstallPathAct"); From 2226175d9bea600028facc9c8cefb75a1ebe79ec Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Mon, 25 Nov 2024 09:26:56 -0300 Subject: [PATCH 059/549] Fix Shortcut (#1586) --- src/qt_gui/gui_context_menus.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h index 823ad921c..7da7341da 100644 --- a/src/qt_gui/gui_context_menus.h +++ b/src/qt_gui/gui_context_menus.h @@ -451,7 +451,7 @@ private: pShellLink->SetWorkingDirectory((LPCWSTR)QFileInfo(exePath).absolutePath().utf16()); // Set arguments, eboot.bin file location - QString arguments = QString("\"%1\"").arg(targetPath); + QString arguments = QString("-g \"%1\"").arg(targetPath); pShellLink->SetArguments((LPCWSTR)arguments.utf16()); // Set the icon for the shortcut From cea6d87472d29c1ae95175b32568cd3b203c497a Mon Sep 17 00:00:00 2001 From: psucien Date: Mon, 25 Nov 2024 20:52:09 +0100 Subject: [PATCH 060/549] hot-fix: downgrade Nx1 2D render targets to 1D --- src/video_core/texture_cache/image_info.cpp | 2 +- src/video_core/texture_cache/image_view.cpp | 2 +- src/video_core/texture_cache/image_view.h | 2 +- src/video_core/texture_cache/texture_cache.h | 7 ++++++- src/video_core/texture_cache/tile_manager.cpp | 1 + 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index 0ed36ee39..29fb02433 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -268,10 +268,10 @@ ImageInfo::ImageInfo(const AmdGpu::Liverpool::ColorBuffer& buffer, pixel_format = LiverpoolToVK::SurfaceFormat(buffer.info.format, buffer.NumFormat()); num_samples = 1 << buffer.attrib.num_fragments_log2; num_bits = NumBits(buffer.info.format); - type = vk::ImageType::e2D; size.width = hint.Valid() ? hint.width : buffer.Pitch(); size.height = hint.Valid() ? hint.height : buffer.Height(); size.depth = 1; + type = size.height == 1 ? vk::ImageType::e1D : vk::ImageType::e2D; pitch = buffer.Pitch(); resources.layers = buffer.NumSlices(); meta_info.cmask_addr = buffer.info.fast_clear ? buffer.CmaskAddress() : 0; diff --git a/src/video_core/texture_cache/image_view.cpp b/src/video_core/texture_cache/image_view.cpp index 488d44a7f..51519387e 100644 --- a/src/video_core/texture_cache/image_view.cpp +++ b/src/video_core/texture_cache/image_view.cpp @@ -137,7 +137,7 @@ ImageViewInfo::ImageViewInfo(const AmdGpu::Liverpool::ColorBuffer& col_buffer) n ImageViewInfo::ImageViewInfo(const AmdGpu::Liverpool::DepthBuffer& depth_buffer, AmdGpu::Liverpool::DepthView view, - AmdGpu::Liverpool::DepthControl ctl) { + AmdGpu::Liverpool::DepthControl ctl) noexcept { format = Vulkan::LiverpoolToVK::DepthFormat(depth_buffer.z_info.format, depth_buffer.stencil_info.format); is_storage = ctl.depth_write_enable; diff --git a/src/video_core/texture_cache/image_view.h b/src/video_core/texture_cache/image_view.h index 23c703d23..68e849172 100644 --- a/src/video_core/texture_cache/image_view.h +++ b/src/video_core/texture_cache/image_view.h @@ -21,7 +21,7 @@ struct ImageViewInfo { ImageViewInfo(const AmdGpu::Image& image, const Shader::ImageResource& desc) noexcept; ImageViewInfo(const AmdGpu::Liverpool::ColorBuffer& col_buffer) noexcept; ImageViewInfo(const AmdGpu::Liverpool::DepthBuffer& depth_buffer, - AmdGpu::Liverpool::DepthView view, AmdGpu::Liverpool::DepthControl ctl); + AmdGpu::Liverpool::DepthView view, AmdGpu::Liverpool::DepthControl ctl) noexcept; vk::ImageViewType type = vk::ImageViewType::e2D; vk::Format format = vk::Format::eR8G8B8A8Unorm; diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 8ac603f06..9c8b98b9f 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -71,7 +71,12 @@ public: struct RenderTargetDesc : public BaseDesc { RenderTargetDesc(const AmdGpu::Liverpool::ColorBuffer& buffer, const AmdGpu::Liverpool::CbDbExtent& hint = {}) - : BaseDesc{BindingType::RenderTarget, ImageInfo{buffer, hint}, ImageViewInfo{buffer}} {} + : BaseDesc{BindingType::RenderTarget, ImageInfo{buffer, hint}, ImageViewInfo{buffer}} { + if (info.size.height == 1) { + view_info.type = vk::ImageViewType::e1D; + ASSERT(info.resources.levels == 1); + } + } }; struct DepthTargetDesc : public BaseDesc { diff --git a/src/video_core/texture_cache/tile_manager.cpp b/src/video_core/texture_cache/tile_manager.cpp index c4f24420d..ec7101c12 100644 --- a/src/video_core/texture_cache/tile_manager.cpp +++ b/src/video_core/texture_cache/tile_manager.cpp @@ -187,6 +187,7 @@ vk::Format DemoteImageFormatForDetiling(vk::Format format) { case vk::Format::eR32Uint: case vk::Format::eR16G16Sfloat: case vk::Format::eR16G16Unorm: + case vk::Format::eR16G16Snorm: case vk::Format::eB10G11R11UfloatPack32: return vk::Format::eR32Uint; case vk::Format::eBc1RgbaSrgbBlock: From cd4f48cb8daa9925938c8f26bece6c59844c0f74 Mon Sep 17 00:00:00 2001 From: psucien Date: Mon, 25 Nov 2024 22:14:53 +0100 Subject: [PATCH 061/549] Revert "hot-fix: downgrade Nx1 2D render targets to 1D" due to regression This reverts commit cea6d87472d29c1ae95175b32568cd3b203c497a. --- src/video_core/texture_cache/image_info.cpp | 2 +- src/video_core/texture_cache/image_view.cpp | 2 +- src/video_core/texture_cache/image_view.h | 2 +- src/video_core/texture_cache/texture_cache.h | 7 +------ src/video_core/texture_cache/tile_manager.cpp | 1 - 5 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index 29fb02433..0ed36ee39 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -268,10 +268,10 @@ ImageInfo::ImageInfo(const AmdGpu::Liverpool::ColorBuffer& buffer, pixel_format = LiverpoolToVK::SurfaceFormat(buffer.info.format, buffer.NumFormat()); num_samples = 1 << buffer.attrib.num_fragments_log2; num_bits = NumBits(buffer.info.format); + type = vk::ImageType::e2D; size.width = hint.Valid() ? hint.width : buffer.Pitch(); size.height = hint.Valid() ? hint.height : buffer.Height(); size.depth = 1; - type = size.height == 1 ? vk::ImageType::e1D : vk::ImageType::e2D; pitch = buffer.Pitch(); resources.layers = buffer.NumSlices(); meta_info.cmask_addr = buffer.info.fast_clear ? buffer.CmaskAddress() : 0; diff --git a/src/video_core/texture_cache/image_view.cpp b/src/video_core/texture_cache/image_view.cpp index 51519387e..488d44a7f 100644 --- a/src/video_core/texture_cache/image_view.cpp +++ b/src/video_core/texture_cache/image_view.cpp @@ -137,7 +137,7 @@ ImageViewInfo::ImageViewInfo(const AmdGpu::Liverpool::ColorBuffer& col_buffer) n ImageViewInfo::ImageViewInfo(const AmdGpu::Liverpool::DepthBuffer& depth_buffer, AmdGpu::Liverpool::DepthView view, - AmdGpu::Liverpool::DepthControl ctl) noexcept { + AmdGpu::Liverpool::DepthControl ctl) { format = Vulkan::LiverpoolToVK::DepthFormat(depth_buffer.z_info.format, depth_buffer.stencil_info.format); is_storage = ctl.depth_write_enable; diff --git a/src/video_core/texture_cache/image_view.h b/src/video_core/texture_cache/image_view.h index 68e849172..23c703d23 100644 --- a/src/video_core/texture_cache/image_view.h +++ b/src/video_core/texture_cache/image_view.h @@ -21,7 +21,7 @@ struct ImageViewInfo { ImageViewInfo(const AmdGpu::Image& image, const Shader::ImageResource& desc) noexcept; ImageViewInfo(const AmdGpu::Liverpool::ColorBuffer& col_buffer) noexcept; ImageViewInfo(const AmdGpu::Liverpool::DepthBuffer& depth_buffer, - AmdGpu::Liverpool::DepthView view, AmdGpu::Liverpool::DepthControl ctl) noexcept; + AmdGpu::Liverpool::DepthView view, AmdGpu::Liverpool::DepthControl ctl); vk::ImageViewType type = vk::ImageViewType::e2D; vk::Format format = vk::Format::eR8G8B8A8Unorm; diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 9c8b98b9f..8ac603f06 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -71,12 +71,7 @@ public: struct RenderTargetDesc : public BaseDesc { RenderTargetDesc(const AmdGpu::Liverpool::ColorBuffer& buffer, const AmdGpu::Liverpool::CbDbExtent& hint = {}) - : BaseDesc{BindingType::RenderTarget, ImageInfo{buffer, hint}, ImageViewInfo{buffer}} { - if (info.size.height == 1) { - view_info.type = vk::ImageViewType::e1D; - ASSERT(info.resources.levels == 1); - } - } + : BaseDesc{BindingType::RenderTarget, ImageInfo{buffer, hint}, ImageViewInfo{buffer}} {} }; struct DepthTargetDesc : public BaseDesc { diff --git a/src/video_core/texture_cache/tile_manager.cpp b/src/video_core/texture_cache/tile_manager.cpp index ec7101c12..c4f24420d 100644 --- a/src/video_core/texture_cache/tile_manager.cpp +++ b/src/video_core/texture_cache/tile_manager.cpp @@ -187,7 +187,6 @@ vk::Format DemoteImageFormatForDetiling(vk::Format format) { case vk::Format::eR32Uint: case vk::Format::eR16G16Sfloat: case vk::Format::eR16G16Unorm: - case vk::Format::eR16G16Snorm: case vk::Format::eB10G11R11UfloatPack32: return vk::Format::eR32Uint; case vk::Format::eBc1RgbaSrgbBlock: From 3f1be5a4cea1c8b387965b0e86c10df9fbfa584d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A5IGA?= <164882787+Xphalnos@users.noreply.github.com> Date: Tue, 26 Nov 2024 07:48:14 +0100 Subject: [PATCH 062/549] Adding Utils Icon (#1600) --- REUSE.toml | 1 + src/images/utils_icon.png | Bin 0 -> 1470 bytes src/qt_gui/main_window.cpp | 1 + src/qt_gui/main_window_ui.h | 1 + src/shadps4.qrc | 1 + 5 files changed, 4 insertions(+) create mode 100644 src/images/utils_icon.png diff --git a/REUSE.toml b/REUSE.toml index 7b2862e53..2d94c9292 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -37,6 +37,7 @@ path = [ "src/images/refresh_icon.png", "src/images/settings_icon.png", "src/images/stop_icon.png", + "src/images/utils_icon.png", "src/images/shadPS4.icns", "src/images/shadps4.ico", "src/images/net.shadps4.shadPS4.svg", diff --git a/src/images/utils_icon.png b/src/images/utils_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7dfa3aa00f4de0d598c195719d8701c1357eb693 GIT binary patch literal 1470 zcmb`H{Xf$Q0LQ-!vF0f$^LQI6(Js~|k7bO_Lz?Hw%EN|hkxh11u9IpgxnVL7-Cd^} zUP%wgQ&@x%%ENhnI`bIjA=V_E{)ccHdIJ+2#l@RL%H?|R+&`P!s39F<;!(3&iU{j0~-{w zIc%KQIBOa9yj(mM)<^uj`<4|nQykMbR!^+B`m9vxtUT-v`*@ygpn7} zEjl{@C5GxMC_b?ay3pkOi-bc+yRlI}eoq4B+xUapDxcklY}zPrOqbZJ*ZFL3Rs)CN ziKI|j?MBVgYko^?)1q`-;J(N$2y_ymED$Ose#B=wNOOf6Zsj1#$RL)5gWs}VoI_%A zifXC_DtH4nHg&M#+C<&R==V+a3LO$bq0ctv*uM8eYf0Zt>;PplS;|z@`EUyE!A}Xn ze0I!P`t1-o5prHDM!@yJyM43}W*wuwUOcJn-2kJc#H7K}bNH-Fbq>w0^8{K{9_ZzIs=MzCvo7g6(un#XdD z5A`wegQY@HbVfxiPm5;#rwi4j{}Uo@rgK4!Ech~GNTIc6DP%BQ+$i*>h9|r?5jQLL zLDzDt@Yxv>v&-E(kGEaa175xe-X<4q)~<&PcA?EJ4zk7M(MdI}t+jc+RY=h1_VFD@ zR++IpEk^W5|G=$b{#3x{FZoe|kCM|&iIY`6}SwmlLe_bMkIW#i0UzJ%IDq^gc zV|N>GG&Qtg#2>Qz->2I#0;rMnFtV-kL!42Gf@0~rJBv%UlP2bailEcM35;1^0kyZQ zfJ5@s9mXHlO7`ePvW?QQPnpTZ2-Ga~Po0d15lYgWPQoBo> zcxA2`-qwZ2nz7G2{q>hkMcI+HM±H+D?ODdykm8mk4zHxPT*3zU${Z`8B-+#s))BnW}c_6{;|HX)Qm{-l*Ctv zRl0@Tqq#+3>#O8AuOrB|!c-fU=qs4{GPIZP?uc8gI>#ZXiE%YtcaDeSgGDq30d{qV zqDudfXIlSof^v$_RfUi1Mx{DgwuL8o0*mGzb%?ZAY5%fAiL+d7XE!EhivzPsoip4X z(%tfJ=4nop3qlMf7Aq`vM%0)*XLW!4)12cfoAsMR%QR7 sVoT_>eV`>wDxj}`u7Z(=$wcnRq7ID_8gameInstallPathAct->setIcon(RecolorIcon(ui->gameInstallPathAct->icon(), isWhite)); ui->menuThemes->setIcon(RecolorIcon(ui->menuThemes->icon(), isWhite)); ui->menuGame_List_Icons->setIcon(RecolorIcon(ui->menuGame_List_Icons->icon(), isWhite)); + ui->menuUtils->setIcon(RecolorIcon(ui->menuUtils->icon(), isWhite)); ui->playButton->setIcon(RecolorIcon(ui->playButton->icon(), isWhite)); ui->pauseButton->setIcon(RecolorIcon(ui->pauseButton->icon(), isWhite)); ui->stopButton->setIcon(RecolorIcon(ui->stopButton->icon(), isWhite)); diff --git a/src/qt_gui/main_window_ui.h b/src/qt_gui/main_window_ui.h index cb9aa5904..5ff572f86 100644 --- a/src/qt_gui/main_window_ui.h +++ b/src/qt_gui/main_window_ui.h @@ -249,6 +249,7 @@ public: menuSettings->setObjectName("menuSettings"); menuUtils = new QMenu(menuSettings); menuUtils->setObjectName("menuUtils"); + menuUtils->setIcon(QIcon(":images/utils_icon.png")); menuThemes = new QMenu(menuView); menuThemes->setObjectName("menuThemes"); menuThemes->setIcon(QIcon(":images/themes_icon.png")); diff --git a/src/shadps4.qrc b/src/shadps4.qrc index a59cb0621..e328f2c42 100644 --- a/src/shadps4.qrc +++ b/src/shadps4.qrc @@ -6,6 +6,7 @@ images/play_icon.png images/pause_icon.png images/stop_icon.png + images/utils_icon.png images/file_icon.png images/folder_icon.png images/themes_icon.png From 18a36c5daa1fd2ff298f555b82936903e0e44a71 Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Tue, 26 Nov 2024 23:45:15 +0300 Subject: [PATCH 063/549] Fixed false-positive image reuploads (#1557) * Fixed false-positive image reuploads * Fixed userfaultfd path, removed dead code, simplified calculations * oopsie * track potentially dirty images and hash them * untrack only first page of the image in case of head access * rebase, initialize hash, fix bounds check * include image tail in the calculations --- src/video_core/buffer_cache/buffer_cache.cpp | 2 +- src/video_core/page_manager.cpp | 16 +- src/video_core/page_manager.h | 3 + .../renderer_vulkan/vk_rasterizer.cpp | 6 +- .../renderer_vulkan/vk_rasterizer.h | 2 +- src/video_core/texture_cache/image.cpp | 3 +- src/video_core/texture_cache/image.h | 20 +- .../texture_cache/texture_cache.cpp | 176 +++++++++++++++--- src/video_core/texture_cache/texture_cache.h | 8 +- 9 files changed, 196 insertions(+), 40 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index 92c446fa9..77b353c2f 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -635,7 +635,7 @@ bool BufferCache::SynchronizeBufferFromImage(Buffer& buffer, VAddr device_addr, "Texel buffer aliases image subresources {:x} : {:x}", device_addr, image.info.guest_address); boost::container::small_vector copies; - u32 offset = buffer.Offset(image.cpu_addr); + u32 offset = buffer.Offset(image.info.guest_address); const u32 num_layers = image.info.resources.layers; const u32 max_offset = offset + size; for (u32 m = 0; m < image.info.resources.levels; m++) { diff --git a/src/video_core/page_manager.cpp b/src/video_core/page_manager.cpp index 8c20ee6ed..d26a7067a 100644 --- a/src/video_core/page_manager.cpp +++ b/src/video_core/page_manager.cpp @@ -114,8 +114,8 @@ struct PageManager::Impl { // Notify rasterizer about the fault. const VAddr addr = msg.arg.pagefault.address; - const VAddr addr_page = Common::AlignDown(addr, PAGESIZE); - rasterizer->InvalidateMemory(addr_page, PAGESIZE); + const VAddr addr_page = GetPageAddr(addr); + rasterizer->InvalidateMemory(addr, addr_page, PAGESIZE); } } @@ -157,8 +157,8 @@ struct PageManager::Impl { const auto addr = reinterpret_cast(fault_address); const bool is_write = Common::IsWriteError(context); if (is_write && owned_ranges.find(addr) != owned_ranges.end()) { - const VAddr addr_aligned = Common::AlignDown(addr, PAGESIZE); - rasterizer->InvalidateMemory(addr_aligned, PAGESIZE); + const VAddr addr_aligned = GetPageAddr(addr); + rasterizer->InvalidateMemory(addr, addr_aligned, PAGESIZE); return true; } return false; @@ -174,6 +174,14 @@ PageManager::PageManager(Vulkan::Rasterizer* rasterizer_) PageManager::~PageManager() = default; +VAddr PageManager::GetPageAddr(VAddr addr) { + return Common::AlignDown(addr, PAGESIZE); +} + +VAddr PageManager::GetNextPageAddr(VAddr addr) { + return Common::AlignUp(addr + 1, PAGESIZE); +} + void PageManager::OnGpuMap(VAddr address, size_t size) { impl->OnMap(address, size); } diff --git a/src/video_core/page_manager.h b/src/video_core/page_manager.h index 0dc022aa5..29a946a8f 100644 --- a/src/video_core/page_manager.h +++ b/src/video_core/page_manager.h @@ -28,6 +28,9 @@ public: /// Increase/decrease the number of surface in pages touching the specified region void UpdatePagesCachedCount(VAddr addr, u64 size, s32 delta); + static VAddr GetPageAddr(VAddr addr); + static VAddr GetNextPageAddr(VAddr addr); + private: struct Impl; std::unique_ptr impl; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 8dc0771de..e66d12517 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -792,9 +792,9 @@ u32 Rasterizer::ReadDataFromGds(u32 gds_offset) { return value; } -void Rasterizer::InvalidateMemory(VAddr addr, u64 size) { - buffer_cache.InvalidateMemory(addr, size); - texture_cache.InvalidateMemory(addr, size); +void Rasterizer::InvalidateMemory(VAddr addr, VAddr addr_aligned, u64 size) { + buffer_cache.InvalidateMemory(addr_aligned, size); + texture_cache.InvalidateMemory(addr, addr_aligned, size); } void Rasterizer::MapMemory(VAddr addr, u64 size) { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 5102cda38..fe8aceba7 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -46,7 +46,7 @@ public: void InlineData(VAddr address, const void* value, u32 num_bytes, bool is_gds); u32 ReadDataFromGds(u32 gsd_offset); - void InvalidateMemory(VAddr addr, u64 size); + void InvalidateMemory(VAddr addr, VAddr addr_aligned, u64 size); void MapMemory(VAddr addr, u64 size); void UnmapMemory(VAddr addr, u64 size); diff --git a/src/video_core/texture_cache/image.cpp b/src/video_core/texture_cache/image.cpp index dc43036c6..3d5202ad6 100644 --- a/src/video_core/texture_cache/image.cpp +++ b/src/video_core/texture_cache/image.cpp @@ -144,8 +144,7 @@ void UniqueImage::Create(const vk::ImageCreateInfo& image_ci) { Image::Image(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_, const ImageInfo& info_) : instance{&instance_}, scheduler{&scheduler_}, info{info_}, - image{instance->GetDevice(), instance->GetAllocator()}, cpu_addr{info.guest_address}, - cpu_addr_end{cpu_addr + info.guest_size_bytes} { + image{instance->GetDevice(), instance->GetAllocator()} { mip_hashes.resize(info.resources.levels); ASSERT(info.pixel_format != vk::Format::eUndefined); // Here we force `eExtendedUsage` as don't know all image usage cases beforehand. In normal case diff --git a/src/video_core/texture_cache/image.h b/src/video_core/texture_cache/image.h index 8d84277d8..a1b1b007f 100644 --- a/src/video_core/texture_cache/image.h +++ b/src/video_core/texture_cache/image.h @@ -22,11 +22,12 @@ VK_DEFINE_HANDLE(VmaAllocator) namespace VideoCore { enum ImageFlagBits : u32 { - CpuDirty = 1 << 1, ///< Contents have been modified from the CPU + Empty = 0, + MaybeCpuDirty = 1 << 0, ///< The page this image is in was touched before the image address + CpuDirty = 1 << 1, ///< Contents have been modified from the CPU GpuDirty = 1 << 2, ///< Contents have been modified from the GPU (valid data in buffer cache) - Dirty = CpuDirty | GpuDirty, + Dirty = MaybeCpuDirty | CpuDirty | GpuDirty, GpuModified = 1 << 3, ///< Contents have been modified from the GPU - Tracked = 1 << 4, ///< Writes and reads are being hooked from the CPU Registered = 1 << 6, ///< True when the image is registered Picked = 1 << 7, ///< Temporary flag to mark the image as picked MetaRegistered = 1 << 8, ///< True when metadata for this surface is known and registered @@ -78,7 +79,9 @@ struct Image { [[nodiscard]] bool Overlaps(VAddr overlap_cpu_addr, size_t overlap_size) const noexcept { const VAddr overlap_end = overlap_cpu_addr + overlap_size; - return cpu_addr < overlap_end && overlap_cpu_addr < cpu_addr_end; + const auto image_addr = info.guest_address; + const auto image_end = info.guest_address + info.guest_size_bytes; + return image_addr < overlap_end && overlap_cpu_addr < image_end; } ImageViewId FindView(const ImageViewInfo& info) const { @@ -99,14 +102,18 @@ struct Image { void CopyImage(const Image& image); void CopyMip(const Image& image, u32 mip); + bool IsTracked() { + return track_addr != 0 && track_addr_end != 0; + } + const Vulkan::Instance* instance; Vulkan::Scheduler* scheduler; ImageInfo info; UniqueImage image; vk::ImageAspectFlags aspect_mask = vk::ImageAspectFlagBits::eColor; ImageFlagBits flags = ImageFlagBits::Dirty; - VAddr cpu_addr = 0; - VAddr cpu_addr_end = 0; + VAddr track_addr = 0; + VAddr track_addr_end = 0; std::vector image_view_infos; std::vector image_view_ids; @@ -130,6 +137,7 @@ struct Image { std::vector subresource_states{}; boost::container::small_vector mip_hashes{}; u64 tick_accessed_last{0}; + u64 hash{0}; struct { union { diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index 516c110a4..4373fdc52 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -29,9 +29,12 @@ TextureCache::TextureCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& info.UpdateSize(); const ImageId null_id = slot_images.insert(instance, scheduler, info); ASSERT(null_id.index == NULL_IMAGE_ID.index); - const vk::Image& null_image = slot_images[null_id].image; + auto& img = slot_images[null_id]; + const vk::Image& null_image = img.image; Vulkan::SetObjectName(instance.GetDevice(), null_image, "Null Image"); - slot_images[null_id].flags = ImageFlagBits::Tracked; + img.flags = ImageFlagBits::Empty; + img.track_addr = img.info.guest_address; + img.track_addr_end = img.info.guest_address + img.info.guest_size_bytes; ImageViewInfo view_info; const auto null_view_id = @@ -43,13 +46,43 @@ TextureCache::TextureCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& TextureCache::~TextureCache() = default; -void TextureCache::InvalidateMemory(VAddr address, size_t size) { +void TextureCache::MarkAsMaybeDirty(ImageId image_id, Image& image) { + if (image.hash == 0) { + // Initialize hash + const u8* addr = std::bit_cast(image.info.guest_address); + image.hash = XXH3_64bits(addr, image.info.guest_size_bytes); + } + image.flags |= ImageFlagBits::MaybeCpuDirty; + UntrackImage(image_id); +} + +void TextureCache::InvalidateMemory(VAddr addr, VAddr page_addr, size_t size) { std::scoped_lock lock{mutex}; - ForEachImageInRegion(address, size, [&](ImageId image_id, Image& image) { - // Ensure image is reuploaded when accessed again. - image.flags |= ImageFlagBits::CpuDirty; - // Untrack image, so the range is unprotected and the guest can write freely. - UntrackImage(image_id); + ForEachImageInRegion(page_addr, size, [&](ImageId image_id, Image& image) { + const auto image_begin = image.info.guest_address; + const auto image_end = image.info.guest_address + image.info.guest_size_bytes; + const auto page_end = page_addr + size; + if (image_begin <= addr && addr < image_end) { + // This image was definitely accessed by this page fault. + // Untrack image, so the range is unprotected and the guest can write freely + image.flags |= ImageFlagBits::CpuDirty; + UntrackImage(image_id); + } else if (page_end < image_end) { + // This page access may or may not modify the image. + // We should not mark it as dirty now. If it really was modified + // it will receive more invalidations on its other pages. + // Remove tracking from this page only. + UntrackImageHead(image_id); + } else if (image_begin < page_addr) { + // This page access does not modify the image but the page should be untracked. + // We should not mark this image as dirty now. If it really was modified + // it will receive more invalidations on its other pages. + UntrackImageTail(image_id); + } else { + // Image begins and ends on this page so it can not receive any more invalidations. + // We will check it's hash later to see if it really was modified. + MarkAsMaybeDirty(image_id, image); + } }); } @@ -415,6 +448,23 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule return; } + if (True(image.flags & ImageFlagBits::MaybeCpuDirty) && + False(image.flags & ImageFlagBits::CpuDirty)) { + // The image size should be less than page size to be considered MaybeCpuDirty + // So this calculation should be very uncommon and reasonably fast + // For now we'll just check up to 64 first pixels + const auto addr = std::bit_cast(image.info.guest_address); + const auto w = std::min(image.info.size.width, u32(8)); + const auto h = std::min(image.info.size.height, u32(8)); + const auto size = w * h * image.info.num_bits / 8; + const u64 hash = XXH3_64bits(addr, size); + if (image.hash == hash) { + image.flags &= ~ImageFlagBits::MaybeCpuDirty; + return; + } + image.hash = hash; + } + const auto& num_layers = image.info.resources.layers; const auto& num_mips = image.info.resources.levels; ASSERT(num_mips == image.info.mips_layout.size()); @@ -425,14 +475,14 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule const u32 height = std::max(image.info.size.height >> m, 1u); const u32 depth = image.info.props.is_volume ? std::max(image.info.size.depth >> m, 1u) : 1u; - const auto& [mip_size, mip_pitch, mip_height, mip_ofs] = image.info.mips_layout[m]; + const auto& mip = image.info.mips_layout[m]; // Protect GPU modified resources from accidental CPU reuploads. const bool is_gpu_modified = True(image.flags & ImageFlagBits::GpuModified); const bool is_gpu_dirty = True(image.flags & ImageFlagBits::GpuDirty); if (is_gpu_modified && !is_gpu_dirty) { const u8* addr = std::bit_cast(image.info.guest_address); - const u64 hash = XXH3_64bits(addr + mip_ofs, mip_size); + const u64 hash = XXH3_64bits(addr + mip.offset, mip.size); if (image.mip_hashes[m] == hash) { continue; } @@ -440,9 +490,9 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule } image_copy.push_back({ - .bufferOffset = mip_ofs * num_layers, - .bufferRowLength = static_cast(mip_pitch), - .bufferImageHeight = static_cast(mip_height), + .bufferOffset = mip.offset * num_layers, + .bufferRowLength = static_cast(mip.pitch), + .bufferImageHeight = static_cast(mip.height), .imageSubresource{ .aspectMask = image.aspect_mask & ~vk::ImageAspectFlagBits::eStencil, .mipLevel = m, @@ -455,6 +505,7 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule } if (image_copy.empty()) { + image.flags &= ~ImageFlagBits::Dirty; return; } @@ -500,7 +551,7 @@ void TextureCache::RegisterImage(ImageId image_id) { ASSERT_MSG(False(image.flags & ImageFlagBits::Registered), "Trying to register an already registered image"); image.flags |= ImageFlagBits::Registered; - ForEachPage(image.cpu_addr, image.info.guest_size_bytes, + ForEachPage(image.info.guest_address, image.info.guest_size_bytes, [this, image_id](u64 page) { page_table[page].push_back(image_id); }); } @@ -509,7 +560,7 @@ void TextureCache::UnregisterImage(ImageId image_id) { ASSERT_MSG(True(image.flags & ImageFlagBits::Registered), "Trying to unregister an already unregistered image"); image.flags &= ~ImageFlagBits::Registered; - ForEachPage(image.cpu_addr, image.info.guest_size_bytes, [this, image_id](u64 page) { + ForEachPage(image.info.guest_address, image.info.guest_size_bytes, [this, image_id](u64 page) { const auto page_it = page_table.find(page); if (page_it == nullptr) { UNREACHABLE_MSG("Unregistering unregistered page=0x{:x}", page << PageShift); @@ -527,25 +578,106 @@ void TextureCache::UnregisterImage(ImageId image_id) { void TextureCache::TrackImage(ImageId image_id) { auto& image = slot_images[image_id]; - if (True(image.flags & ImageFlagBits::Tracked)) { + const auto image_begin = image.info.guest_address; + const auto image_end = image.info.guest_address + image.info.guest_size_bytes; + if (image_begin == image.track_addr && image_end == image.track_addr_end) { return; } - image.flags |= ImageFlagBits::Tracked; - tracker.UpdatePagesCachedCount(image.cpu_addr, image.info.guest_size_bytes, 1); + + if (!image.IsTracked()) { + // Re-track the whole image + image.track_addr = image_begin; + image.track_addr_end = image_end; + tracker.UpdatePagesCachedCount(image_begin, image.info.guest_size_bytes, 1); + } else { + if (image_begin < image.track_addr) { + TrackImageHead(image_id); + } + if (image.track_addr_end < image_end) { + TrackImageTail(image_id); + } + } +} + +void TextureCache::TrackImageHead(ImageId image_id) { + auto& image = slot_images[image_id]; + const auto image_begin = image.info.guest_address; + if (image_begin == image.track_addr) { + return; + } + ASSERT(image.track_addr != 0 && image_begin < image.track_addr); + const auto size = image.track_addr - image_begin; + image.track_addr = image_begin; + tracker.UpdatePagesCachedCount(image_begin, size, 1); +} + +void TextureCache::TrackImageTail(ImageId image_id) { + auto& image = slot_images[image_id]; + const auto image_end = image.info.guest_address + image.info.guest_size_bytes; + if (image_end == image.track_addr_end) { + return; + } + ASSERT(image.track_addr_end != 0 && image.track_addr_end < image_end); + const auto addr = image.track_addr_end; + const auto size = image_end - image.track_addr_end; + image.track_addr_end = image_end; + tracker.UpdatePagesCachedCount(addr, size, 1); } void TextureCache::UntrackImage(ImageId image_id) { auto& image = slot_images[image_id]; - if (False(image.flags & ImageFlagBits::Tracked)) { + if (!image.IsTracked()) { return; } - image.flags &= ~ImageFlagBits::Tracked; - tracker.UpdatePagesCachedCount(image.cpu_addr, image.info.guest_size_bytes, -1); + const auto addr = image.track_addr; + const auto size = image.track_addr_end - image.track_addr; + image.track_addr = 0; + image.track_addr_end = 0; + if (size != 0) { + tracker.UpdatePagesCachedCount(addr, size, -1); + } +} + +void TextureCache::UntrackImageHead(ImageId image_id) { + auto& image = slot_images[image_id]; + const auto image_begin = image.info.guest_address; + if (!image.IsTracked() || image_begin < image.track_addr) { + return; + } + const auto addr = tracker.GetNextPageAddr(image_begin); + const auto size = addr - image_begin; + image.track_addr = addr; + if (image.track_addr == image.track_addr_end) { + // This image spans only 2 pages and both are modified, + // but the image itself was not directly affected. + // Cehck its hash later. + MarkAsMaybeDirty(image_id, image); + } + tracker.UpdatePagesCachedCount(image_begin, size, -1); +} + +void TextureCache::UntrackImageTail(ImageId image_id) { + auto& image = slot_images[image_id]; + const auto image_end = image.info.guest_address + image.info.guest_size_bytes; + if (!image.IsTracked() || image.track_addr_end < image_end) { + return; + } + ASSERT(image.track_addr_end != 0); + const auto addr = tracker.GetPageAddr(image_end); + const auto size = image_end - addr; + image.track_addr_end = addr; + if (image.track_addr == image.track_addr_end) { + // This image spans only 2 pages and both are modified, + // but the image itself was not directly affected. + // Cehck its hash later. + MarkAsMaybeDirty(image_id, image); + } + tracker.UpdatePagesCachedCount(addr, size, -1); } void TextureCache::DeleteImage(ImageId image_id) { Image& image = slot_images[image_id]; - ASSERT_MSG(False(image.flags & ImageFlagBits::Tracked), "Image was not untracked"); + ASSERT_MSG(!image.IsTracked(), "Image was not untracked"); ASSERT_MSG(False(image.flags & ImageFlagBits::Registered), "Image was not unregistered"); // Remove any registered meta areas. diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 8ac603f06..fab4c832f 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -95,7 +95,7 @@ public: ~TextureCache(); /// Invalidates any image in the logical page range. - void InvalidateMemory(VAddr address, size_t size); + void InvalidateMemory(VAddr addr, VAddr page_addr, size_t size); /// Marks an image as dirty if it exists at the provided address. void InvalidateMemoryFromGPU(VAddr address, size_t max_size); @@ -242,9 +242,15 @@ private: /// Track CPU reads and writes for image void TrackImage(ImageId image_id); + void TrackImageHead(ImageId image_id); + void TrackImageTail(ImageId image_id); /// Stop tracking CPU reads and writes for image void UntrackImage(ImageId image_id); + void UntrackImageHead(ImageId image_id); + void UntrackImageTail(ImageId image_id); + + void MarkAsMaybeDirty(ImageId image_id, Image& image); /// Removes the image and any views/surface metas that reference it. void DeleteImage(ImageId image_id); From 471ebdb4f033c976a1fa9a8ab853eb92bf6bcca5 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Wed, 27 Nov 2024 01:50:57 -0800 Subject: [PATCH 064/549] libc_internal: Implement more functions. (#1606) --- .../libraries/libc_internal/libc_internal.cpp | 222 +++++++++++++++++- 1 file changed, 216 insertions(+), 6 deletions(-) diff --git a/src/core/libraries/libc_internal/libc_internal.cpp b/src/core/libraries/libc_internal/libc_internal.cpp index 8eea41eb3..eb6046c7a 100644 --- a/src/core/libraries/libc_internal/libc_internal.cpp +++ b/src/core/libraries/libc_internal/libc_internal.cpp @@ -3,8 +3,8 @@ #include +#include "common/assert.h" #include "common/logging/log.h" -#include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "libc_internal.h" @@ -36,28 +36,184 @@ int PS4_SYSV_ABI internal_strcpy_s(char* dest, size_t dest_size, const char* src #endif } +int PS4_SYSV_ABI internal_strcat_s(char* dest, size_t dest_size, const char* src) { +#ifdef _WIN64 + return strcat_s(dest, dest_size, src); +#else + std::strcat(dest, src); + return 0; // ALL OK +#endif +} + int PS4_SYSV_ABI internal_memcmp(const void* s1, const void* s2, size_t n) { return std::memcmp(s1, s2, n); } +int PS4_SYSV_ABI internal_strcmp(const char* str1, const char* str2) { + return std::strcmp(str1, str2); +} + int PS4_SYSV_ABI internal_strncmp(const char* str1, const char* str2, size_t num) { return std::strncmp(str1, str2, num); } -int PS4_SYSV_ABI internal_strlen(const char* str) { +size_t PS4_SYSV_ABI internal_strlen(const char* str) { return std::strlen(str); } +char* PS4_SYSV_ABI internal_strncpy(char* dest, const char* src, std::size_t count) { + return std::strncpy(dest, src, count); +} + +char* PS4_SYSV_ABI internal_strcat(char* dest, const char* src) { + return std::strcat(dest, src); +} + +const char* PS4_SYSV_ABI internal_strchr(const char* str, int c) { + return std::strchr(str, c); +} + +double PS4_SYSV_ABI internal_sin(double x) { + return std::sin(x); +} + +float PS4_SYSV_ABI internal_sinf(float x) { + return std::sinf(x); +} + +double PS4_SYSV_ABI internal_cos(double x) { + return std::cos(x); +} + +float PS4_SYSV_ABI internal_cosf(float x) { + return std::cosf(x); +} + +void PS4_SYSV_ABI internal_sincos(double x, double* sinp, double* cosp) { + *sinp = std::sin(x); + *cosp = std::cos(x); +} + +void PS4_SYSV_ABI internal_sincosf(float x, float* sinp, float* cosp) { + *sinp = std::sinf(x); + *cosp = std::cosf(x); +} + +double PS4_SYSV_ABI internal_tan(double x) { + return std::tan(x); +} + +float PS4_SYSV_ABI internal_tanf(float x) { + return std::tanf(x); +} + +double PS4_SYSV_ABI internal_asin(double x) { + return std::asin(x); +} + +float PS4_SYSV_ABI internal_asinf(float x) { + return std::asinf(x); +} + +double PS4_SYSV_ABI internal_acos(double x) { + return std::acos(x); +} + +float PS4_SYSV_ABI internal_acosf(float x) { + return std::acosf(x); +} + +double PS4_SYSV_ABI internal_atan(double x) { + return std::atan(x); +} + +float PS4_SYSV_ABI internal_atanf(float x) { + return std::atanf(x); +} + +double PS4_SYSV_ABI internal_atan2(double y, double x) { + return std::atan2(y, x); +} + +float PS4_SYSV_ABI internal_atan2f(float y, float x) { + return std::atan2f(y, x); +} + +double PS4_SYSV_ABI internal_exp(double x) { + return std::exp(x); +} + float PS4_SYSV_ABI internal_expf(float x) { - return expf(x); + return std::expf(x); +} + +double PS4_SYSV_ABI internal_exp2(double x) { + return std::exp2(x); +} + +float PS4_SYSV_ABI internal_exp2f(float x) { + return std::exp2f(x); +} + +double PS4_SYSV_ABI internal_pow(double x, double y) { + return std::pow(x, y); +} + +float PS4_SYSV_ABI internal_powf(float x, float y) { + return std::powf(x, y); +} + +double PS4_SYSV_ABI internal_log(double x) { + return std::log(x); +} + +float PS4_SYSV_ABI internal_logf(float x) { + return std::logf(x); +} + +double PS4_SYSV_ABI internal_log10(double x) { + return std::log10(x); +} + +float PS4_SYSV_ABI internal_log10f(float x) { + return std::log10f(x); } void* PS4_SYSV_ABI internal_malloc(size_t size) { return std::malloc(size); } -char* PS4_SYSV_ABI internal_strncpy(char* dest, const char* src, std::size_t count) { - return std::strncpy(dest, src, count); +void PS4_SYSV_ABI internal_free(void* ptr) { + std::free(ptr); +} + +void* PS4_SYSV_ABI internal_operator_new(size_t size) { + if (size == 0) { + // Size of 1 is used if 0 is provided. + size = 1; + } + void* ptr = std::malloc(size); + ASSERT_MSG(ptr, "Failed to allocate new object with size {}", size); + return ptr; +} + +void PS4_SYSV_ABI internal_operator_delete(void* ptr) { + if (ptr) { + std::free(ptr); + } +} + +int PS4_SYSV_ABI internal_posix_memalign(void** ptr, size_t alignment, size_t size) { +#ifdef _WIN64 + void* allocated = _aligned_malloc(size, alignment); + if (!allocated) { + return errno; + } + *ptr = allocated; + return 0; +#else + return posix_memalign(ptr, alignment, size); +#endif } void RegisterlibSceLibcInternal(Core::Loader::SymbolsResolver* sym) { @@ -69,17 +225,71 @@ void RegisterlibSceLibcInternal(Core::Loader::SymbolsResolver* sym) { internal_memset); LIB_FUNCTION("5Xa2ACNECdo", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_strcpy_s); + LIB_FUNCTION("K+gcnFFJKVc", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_strcat_s); LIB_FUNCTION("DfivPArhucg", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_memcmp); - LIB_FUNCTION("8zsu04XNsZ4", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_expf); LIB_FUNCTION("aesyjrHVWy4", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_strcmp); + LIB_FUNCTION("Ovb2dSJOAuE", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_strncmp); LIB_FUNCTION("j4ViWNHEgww", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_strlen); LIB_FUNCTION("6sJWiWSRuqk", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_strncpy); + LIB_FUNCTION("Ls4tzzhimqQ", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_strcat); + LIB_FUNCTION("ob5xAW4ln-0", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_strchr); + LIB_FUNCTION("H8ya2H00jbI", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_sin); + LIB_FUNCTION("Q4rRL34CEeE", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_sinf); + LIB_FUNCTION("2WE3BTYVwKM", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_cos); + LIB_FUNCTION("-P6FNMzk2Kc", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_cosf); + LIB_FUNCTION("jMB7EFyu30Y", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_sincos); + LIB_FUNCTION("pztV4AF18iI", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_sincosf); + LIB_FUNCTION("T7uyNqP7vQA", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_tan); + LIB_FUNCTION("ZE6RNL+eLbk", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_tanf); + LIB_FUNCTION("7Ly52zaL44Q", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_asin); + LIB_FUNCTION("GZWjF-YIFFk", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_asinf); + LIB_FUNCTION("JBcgYuW8lPU", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_acos); + LIB_FUNCTION("QI-x0SL8jhw", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_acosf); + LIB_FUNCTION("OXmauLdQ8kY", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_atan); + LIB_FUNCTION("weDug8QD-lE", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_atanf); + LIB_FUNCTION("HUbZmOnT-Dg", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_atan2); + LIB_FUNCTION("EH-x713A99c", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_atan2f); + LIB_FUNCTION("NVadfnzQhHQ", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_exp); + LIB_FUNCTION("8zsu04XNsZ4", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_expf); + LIB_FUNCTION("dnaeGXbjP6E", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_exp2); + LIB_FUNCTION("wuAQt-j+p4o", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_exp2f); + LIB_FUNCTION("9LCjpWyQ5Zc", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_pow); + LIB_FUNCTION("1D0H2KNjshE", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_powf); + LIB_FUNCTION("rtV7-jWC6Yg", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_log); + LIB_FUNCTION("RQXLbdT2lc4", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_logf); + LIB_FUNCTION("WuMbPBKN1TU", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_log10); + LIB_FUNCTION("lhpd6Wk6ccs", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_log10f); LIB_FUNCTION("gQX+4GDQjpM", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_malloc); + LIB_FUNCTION("tIhsqj0qsFE", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_free); + LIB_FUNCTION("fJnpuVVBbKk", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_operator_new); + LIB_FUNCTION("hdm0YfMa7TQ", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_operator_new); + LIB_FUNCTION("MLWl90SFWNE", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_operator_delete); + LIB_FUNCTION("z+P+xCnWLBk", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_operator_delete); + LIB_FUNCTION("cVSk9y8URbc", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_posix_memalign); }; } // namespace Libraries::LibcInternal From 286a288bfd647f118d37ed2fae62be941ba18b06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?= Date: Thu, 28 Nov 2024 21:25:37 +0100 Subject: [PATCH 065/549] Fix GetDents truncating the last character of filenames (#1610) --- src/core/libraries/kernel/file_system.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index 56286eb98..866d35d34 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -537,14 +537,13 @@ static int GetDents(int fd, char* buf, int nbytes, s64* basep) { } const auto& entry = file->dirents.at(file->dirents_index++); auto str = entry.name; - auto str_size = str.size() - 1; static int fileno = 1000; // random OrbisKernelDirent* sce_ent = (OrbisKernelDirent*)buf; sce_ent->d_fileno = fileno++; // TODO this should be unique but atm it changes maybe switch to a // hash or something? sce_ent->d_reclen = sizeof(OrbisKernelDirent); sce_ent->d_type = (entry.isFile ? 8 : 4); - sce_ent->d_namlen = str_size; + sce_ent->d_namlen = str.size(); strncpy(sce_ent->d_name, str.c_str(), ORBIS_MAX_PATH); sce_ent->d_name[ORBIS_MAX_PATH] = '\0'; From bd3371bdfb3083304c05ddefaae2cfc45925ea83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?= Date: Thu, 28 Nov 2024 21:26:44 +0100 Subject: [PATCH 066/549] implement sceKernelPreadv (#1611) --- src/core/libraries/kernel/file_system.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index 866d35d34..c4f1e4799 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -416,7 +416,7 @@ int PS4_SYSV_ABI sceKernelCheckReachability(const char* path) { return ORBIS_OK; } -s64 PS4_SYSV_ABI sceKernelPread(int d, void* buf, size_t nbytes, s64 offset) { +s64 PS4_SYSV_ABI sceKernelPreadv(int d, SceKernelIovec* iov, int iovcnt, s64 offset) { if (d < 3) { return ORBIS_KERNEL_ERROR_EPERM; } @@ -436,10 +436,19 @@ s64 PS4_SYSV_ABI sceKernelPread(int d, void* buf, size_t nbytes, s64 offset) { file->f.Seek(pos); }; if (!file->f.Seek(offset)) { - LOG_CRITICAL(Kernel_Fs, "sceKernelPread: failed to seek"); + LOG_CRITICAL(Kernel_Fs, "failed to seek"); return ORBIS_KERNEL_ERROR_EINVAL; } - return file->f.ReadRaw(buf, nbytes); + size_t total_read = 0; + for (int i = 0; i < iovcnt; i++) { + total_read += file->f.ReadRaw(iov[i].iov_base, iov[i].iov_len); + } + return total_read; +} + +s64 PS4_SYSV_ABI sceKernelPread(int d, void* buf, size_t nbytes, s64 offset) { + SceKernelIovec iovec{buf, nbytes}; + return sceKernelPreadv(d, &iovec, 1, offset); } int PS4_SYSV_ABI sceKernelFStat(int fd, OrbisKernelStat* sb) { @@ -649,6 +658,7 @@ void RegisterFileSystem(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("E6ao34wPw+U", "libScePosix", 1, "libkernel", 1, 1, posix_stat); LIB_FUNCTION("E6ao34wPw+U", "libkernel", 1, "libkernel", 1, 1, posix_stat); LIB_FUNCTION("+r3rMFwItV4", "libkernel", 1, "libkernel", 1, 1, sceKernelPread); + LIB_FUNCTION("yTj62I7kw4s", "libkernel", 1, "libkernel", 1, 1, sceKernelPreadv); LIB_FUNCTION("uWyW3v98sU4", "libkernel", 1, "libkernel", 1, 1, sceKernelCheckReachability); LIB_FUNCTION("fTx66l5iWIA", "libkernel", 1, "libkernel", 1, 1, sceKernelFsync); LIB_FUNCTION("juWbTNM+8hw", "libkernel", 1, "libkernel", 1, 1, posix_fsync); From 57a3c0132de1f69a1d025abec5acb3b80625a57b Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Thu, 28 Nov 2024 15:42:39 -0800 Subject: [PATCH 067/549] semaphore: Fix returned iterator from AddWaiter (#1614) --- src/core/libraries/kernel/threads/semaphore.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/core/libraries/kernel/threads/semaphore.cpp b/src/core/libraries/kernel/threads/semaphore.cpp index 9c9c11178..9fcbd4356 100644 --- a/src/core/libraries/kernel/threads/semaphore.cpp +++ b/src/core/libraries/kernel/threads/semaphore.cpp @@ -165,8 +165,7 @@ public: while (it != wait_list.end() && (*it)->priority > waiter->priority) { ++it; } - wait_list.insert(it, waiter); - return it; + return wait_list.insert(it, waiter); } WaitList wait_list; From d6d1ec4f22840298749844d2bb830aa3b1590739 Mon Sep 17 00:00:00 2001 From: psucien Date: Fri, 29 Nov 2024 14:17:53 +0100 Subject: [PATCH 068/549] hot-fix: apply vgt index offset to draw commands --- src/shader_recompiler/info.h | 7 ++++--- src/video_core/amdgpu/liverpool.h | 4 +++- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/shader_recompiler/info.h b/src/shader_recompiler/info.h index f9cbacaf2..c7ae2a1e5 100644 --- a/src/shader_recompiler/info.h +++ b/src/shader_recompiler/info.h @@ -17,6 +17,7 @@ #include "shader_recompiler/ir/type.h" #include "shader_recompiler/params.h" #include "shader_recompiler/runtime_info.h" +#include "video_core/amdgpu/liverpool.h" #include "video_core/amdgpu/resource.h" namespace Shader { @@ -251,10 +252,10 @@ struct Info { bnd.user_data += ud_mask.NumRegs(); } - [[nodiscard]] std::pair GetDrawOffsets() const { - u32 vertex_offset = 0; + [[nodiscard]] std::pair GetDrawOffsets(const AmdGpu::Liverpool::Regs& regs) const { + u32 vertex_offset = regs.index_offset; u32 instance_offset = 0; - if (vertex_offset_sgpr != -1) { + if (vertex_offset == 0 && vertex_offset_sgpr != -1) { vertex_offset = user_data[vertex_offset_sgpr]; } if (instance_offset_sgpr != -1) { diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index 0ef9397b0..2b2f2c00a 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -1111,7 +1111,8 @@ struct Liverpool { INSERT_PADDING_WORDS(2); std::array viewport_scissors; std::array viewport_depths; - INSERT_PADDING_WORDS(0xA103 - 0xA0D4); + INSERT_PADDING_WORDS(0xA102 - 0xA0D4); + u32 index_offset; u32 primitive_restart_index; INSERT_PADDING_WORDS(1); BlendConstants blend_constants; @@ -1380,6 +1381,7 @@ static_assert(GFX6_3D_REG_INDEX(color_target_mask) == 0xA08E); static_assert(GFX6_3D_REG_INDEX(color_shader_mask) == 0xA08F); static_assert(GFX6_3D_REG_INDEX(generic_scissor) == 0xA090); static_assert(GFX6_3D_REG_INDEX(viewport_scissors) == 0xA094); +static_assert(GFX6_3D_REG_INDEX(index_offset) == 0xA102); static_assert(GFX6_3D_REG_INDEX(primitive_restart_index) == 0xA103); static_assert(GFX6_3D_REG_INDEX(stencil_control) == 0xA10B); static_assert(GFX6_3D_REG_INDEX(viewports) == 0xA10F); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index e66d12517..a6fc872d9 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -200,7 +200,7 @@ void Rasterizer::Draw(bool is_indexed, u32 index_offset) { BeginRendering(*pipeline, state); UpdateDynamicState(*pipeline); - const auto [vertex_offset, instance_offset] = vs_info.GetDrawOffsets(); + const auto [vertex_offset, instance_offset] = vs_info.GetDrawOffsets(regs); const auto cmdbuf = scheduler.CommandBuffer(); cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline->Handle()); From 9f6261524b6305d0d718aed5ba6dd004d4f76810 Mon Sep 17 00:00:00 2001 From: "Daniel R." <47796739+polybiusproxy@users.noreply.github.com> Date: Fri, 29 Nov 2024 20:02:45 +0100 Subject: [PATCH 069/549] core: fix patch paths applying when no folder exists --- src/core/file_sys/fs.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/core/file_sys/fs.cpp b/src/core/file_sys/fs.cpp index 769940cf0..b6d0d188f 100644 --- a/src/core/file_sys/fs.cpp +++ b/src/core/file_sys/fs.cpp @@ -56,11 +56,12 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_rea std::filesystem::path host_path = mount->host_path / rel_path; std::filesystem::path patch_path = mount->host_path; patch_path += "-UPDATE"; - patch_path /= rel_path; - if ((corrected_path.starts_with("/app0") || corrected_path.starts_with("/hostapp")) && - std::filesystem::exists(patch_path)) { - return patch_path; + if (corrected_path.starts_with("/app0") || corrected_path.starts_with("/hostapp")) { + if (std::filesystem::exists(patch_path)) { + patch_path /= rel_path; + return patch_path; + } } if (!NeedsCaseInsensitiveSearch) { From 086c5802f4bec35ffa6457f287a4177537444965 Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Fri, 29 Nov 2024 23:13:36 +0300 Subject: [PATCH 070/549] Fixed DS_SWIZZLE_32 (#1619) --- src/shader_recompiler/frontend/translate/data_share.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/shader_recompiler/frontend/translate/data_share.cpp b/src/shader_recompiler/frontend/translate/data_share.cpp index 9685ee352..2f5932f80 100644 --- a/src/shader_recompiler/frontend/translate/data_share.cpp +++ b/src/shader_recompiler/frontend/translate/data_share.cpp @@ -158,13 +158,12 @@ void Translator::DS_WRITE(int bit_size, bool is_signed, bool is_pair, bool strid void Translator::DS_SWIZZLE_B32(const GcnInst& inst) { const u8 offset0 = inst.control.ds.offset0; const u8 offset1 = inst.control.ds.offset1; - const IR::U32 src{GetSrc(inst.src[1])}; + const IR::U32 src{GetSrc(inst.src[0])}; // ASSERT(offset1 & 0x80); const IR::U32 lane_id = ir.LaneId(); const IR::U32 id_in_group = ir.BitwiseAnd(lane_id, ir.Imm32(0b11)); const IR::U32 base = ir.ShiftLeftLogical(id_in_group, ir.Imm32(1)); - const IR::U32 index = - ir.IAdd(lane_id, ir.BitFieldExtract(ir.Imm32(offset0), base, ir.Imm32(2))); + const IR::U32 index = ir.BitFieldExtract(ir.Imm32(offset0), base, ir.Imm32(2)); SetDst(inst.dst[0], ir.QuadShuffle(src, index)); } From 6c215e672dce22ba1a923a34ad15e2d3eb837e8a Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 29 Nov 2024 12:57:56 -0800 Subject: [PATCH 071/549] Revert "core: fix patch paths applying when no folder exists" (#1620) This reverts commit 9f6261524b6305d0d718aed5ba6dd004d4f76810. --- src/core/file_sys/fs.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/core/file_sys/fs.cpp b/src/core/file_sys/fs.cpp index b6d0d188f..769940cf0 100644 --- a/src/core/file_sys/fs.cpp +++ b/src/core/file_sys/fs.cpp @@ -56,12 +56,11 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_rea std::filesystem::path host_path = mount->host_path / rel_path; std::filesystem::path patch_path = mount->host_path; patch_path += "-UPDATE"; + patch_path /= rel_path; - if (corrected_path.starts_with("/app0") || corrected_path.starts_with("/hostapp")) { - if (std::filesystem::exists(patch_path)) { - patch_path /= rel_path; - return patch_path; - } + if ((corrected_path.starts_with("/app0") || corrected_path.starts_with("/hostapp")) && + std::filesystem::exists(patch_path)) { + return patch_path; } if (!NeedsCaseInsensitiveSearch) { From 7b68004a4086577c766fe44caf18f0dcc1fe0e65 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sat, 30 Nov 2024 00:08:46 -0800 Subject: [PATCH 072/549] sysmodule: Remove need for libSceRazorCpu (#1622) --- CMakeLists.txt | 1 + src/core/libraries/system/sysmodule.cpp | 17 +++++++++++++++-- src/core/libraries/system/sysmodule.h | 6 +++++- src/core/libraries/system/system_error.h | 8 ++++++++ src/emulator.cpp | 3 +-- 5 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 src/core/libraries/system/system_error.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a967c540c..657b7c5bf 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -276,6 +276,7 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp src/core/libraries/save_data/dialog/savedatadialog_ui.h src/core/libraries/system/sysmodule.cpp src/core/libraries/system/sysmodule.h + src/core/libraries/system/system_error.h src/core/libraries/system/systemservice.cpp src/core/libraries/system/systemservice.h src/core/libraries/system/userservice.cpp diff --git a/src/core/libraries/system/sysmodule.cpp b/src/core/libraries/system/sysmodule.cpp index d37c375bf..8ea61e177 100644 --- a/src/core/libraries/system/sysmodule.cpp +++ b/src/core/libraries/system/sysmodule.cpp @@ -9,6 +9,7 @@ #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "core/libraries/system/sysmodule.h" +#include "core/libraries/system/system_error.h" namespace Libraries::SysModule { @@ -34,11 +35,23 @@ int PS4_SYSV_ABI sceSysmoduleIsCameraPreloaded() { int PS4_SYSV_ABI sceSysmoduleIsLoaded(OrbisSysModule id) { LOG_ERROR(Lib_SysModule, "(DUMMY) called module = {}", magic_enum::enum_name(id)); + if (static_cast(id) == 0) { + LOG_ERROR(Lib_SysModule, "Invalid sysmodule ID: {:#x}", static_cast(id)); + return ORBIS_SYSMODULE_INVALID_ID; + } return ORBIS_OK; } -int PS4_SYSV_ABI sceSysmoduleIsLoadedInternal() { - LOG_ERROR(Lib_SysModule, "(STUBBED) called"); +int PS4_SYSV_ABI sceSysmoduleIsLoadedInternal(OrbisSysModuleInternal id) { + LOG_ERROR(Lib_SysModule, "(DUMMY) called module = {:#x}", static_cast(id)); + if ((static_cast(id) & 0x7FFFFFFF) == 0) { + LOG_ERROR(Lib_SysModule, "Invalid internal sysmodule ID: {:#x}", static_cast(id)); + return ORBIS_SYSMODULE_INVALID_ID; + } + if (id == OrbisSysModuleInternal::ORBIS_SYSMODULE_INTERNAL_RAZOR_CPU) { + // Internal debugging library, report as not loaded so it won't be used. + return ORBIS_SYSMODULE_NOT_LOADED; + } return ORBIS_OK; } diff --git a/src/core/libraries/system/sysmodule.h b/src/core/libraries/system/sysmodule.h index c9ec97ce9..3630fb58e 100644 --- a/src/core/libraries/system/sysmodule.h +++ b/src/core/libraries/system/sysmodule.h @@ -147,12 +147,16 @@ enum class OrbisSysModule : u16 { ORBIS_SYSMODULE_KEYBOARD = 0x0106, }; +enum class OrbisSysModuleInternal : u32 { + ORBIS_SYSMODULE_INTERNAL_RAZOR_CPU = 0x80000019, // libSceRazorCpu.sprx +}; + int PS4_SYSV_ABI sceSysmoduleGetModuleHandleInternal(); int PS4_SYSV_ABI sceSysmoduleGetModuleInfoForUnwind(); int PS4_SYSV_ABI sceSysmoduleIsCalledFromSysModule(); int PS4_SYSV_ABI sceSysmoduleIsCameraPreloaded(); int PS4_SYSV_ABI sceSysmoduleIsLoaded(OrbisSysModule id); -int PS4_SYSV_ABI sceSysmoduleIsLoadedInternal(); +int PS4_SYSV_ABI sceSysmoduleIsLoadedInternal(OrbisSysModuleInternal id); int PS4_SYSV_ABI sceSysmoduleLoadModule(OrbisSysModule id); int PS4_SYSV_ABI sceSysmoduleLoadModuleByNameInternal(); int PS4_SYSV_ABI sceSysmoduleLoadModuleInternal(); diff --git a/src/core/libraries/system/system_error.h b/src/core/libraries/system/system_error.h new file mode 100644 index 000000000..615e4cd5f --- /dev/null +++ b/src/core/libraries/system/system_error.h @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +constexpr int ORBIS_SYSMODULE_INVALID_ID = 0x805A1000; +constexpr int ORBIS_SYSMODULE_NOT_LOADED = 0x805A1001; +constexpr int ORBIS_SYSMODULE_LOCK_FAILED = 0x805A10FF; \ No newline at end of file diff --git a/src/emulator.cpp b/src/emulator.cpp index c0b01229a..27291707d 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -269,7 +269,7 @@ void Emulator::Run(const std::filesystem::path& file) { } void Emulator::LoadSystemModules(const std::filesystem::path& file, std::string game_serial) { - constexpr std::array ModulesToLoad{ + constexpr std::array ModulesToLoad{ {{"libSceNgs2.sprx", &Libraries::Ngs2::RegisterlibSceNgs2}, {"libSceFiber.sprx", &Libraries::Fiber::RegisterlibSceFiber}, {"libSceUlt.sprx", nullptr}, @@ -279,7 +279,6 @@ void Emulator::LoadSystemModules(const std::filesystem::path& file, std::string {"libSceDiscMap.sprx", &Libraries::DiscMap::RegisterlibSceDiscMap}, {"libSceRtc.sprx", &Libraries::Rtc::RegisterlibSceRtc}, {"libSceJpegEnc.sprx", nullptr}, - {"libSceRazorCpu.sprx", nullptr}, {"libSceCesCs.sprx", nullptr}}}; std::vector found_modules; From b1a024efbd09a2babcae8abd3aee589f0ca8819c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A5IGA?= <164882787+Xphalnos@users.noreply.github.com> Date: Sat, 30 Nov 2024 09:25:55 +0100 Subject: [PATCH 073/549] Remove save migration code (#1621) --- src/common/config.cpp | 55 +++++++++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index e97a46005..5c857afb4 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -3,13 +3,14 @@ #include #include -#include #include #include // for wstring support #include -#include "common/logging/formatter.h" + #include "common/path_util.h" #include "config.h" +#include "logging/formatter.h" +#include "version.h" namespace toml { template @@ -81,7 +82,8 @@ std::vector m_pkg_viewer; std::vector m_elf_viewer; std::vector m_recent_files; std::string emulator_language = "en"; -// Settings + +// Language u32 m_language = 1; // english bool isNeoMode() { @@ -334,6 +336,7 @@ void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h) { main_window_geometry_w = w; main_window_geometry_h = h; } + bool addGameInstallDir(const std::filesystem::path& dir) { if (std::find(settings_install_dirs.begin(), settings_install_dirs.end(), dir) == settings_install_dirs.end()) { @@ -342,47 +345,60 @@ bool addGameInstallDir(const std::filesystem::path& dir) { } return false; } + void removeGameInstallDir(const std::filesystem::path& dir) { auto iterator = std::find(settings_install_dirs.begin(), settings_install_dirs.end(), dir); if (iterator != settings_install_dirs.end()) { settings_install_dirs.erase(iterator); } } + void setAddonInstallDir(const std::filesystem::path& dir) { settings_addon_install_dir = dir; } + void setMainWindowTheme(u32 theme) { mw_themes = theme; } + void setIconSize(u32 size) { m_icon_size = size; } + void setIconSizeGrid(u32 size) { m_icon_size_grid = size; } + void setSliderPosition(u32 pos) { m_slider_pos = pos; } + void setSliderPositionGrid(u32 pos) { m_slider_pos_grid = pos; } + void setTableMode(u32 mode) { m_table_mode = mode; } + void setMainWindowWidth(u32 width) { m_window_size_W = width; } + void setMainWindowHeight(u32 height) { m_window_size_H = height; } + void setPkgViewer(const std::vector& pkgList) { m_pkg_viewer.resize(pkgList.size()); m_pkg_viewer = pkgList; } + void setElfViewer(const std::vector& elfList) { m_elf_viewer.resize(elfList.size()); m_elf_viewer = elfList; } + void setRecentFiles(const std::vector& recentFiles) { m_recent_files.resize(recentFiles.size()); m_recent_files = recentFiles; @@ -395,18 +411,23 @@ void setEmulatorLanguage(std::string language) { u32 getMainWindowGeometryX() { return main_window_geometry_x; } + u32 getMainWindowGeometryY() { return main_window_geometry_y; } + u32 getMainWindowGeometryW() { return main_window_geometry_w; } + u32 getMainWindowGeometryH() { return main_window_geometry_h; } + const std::vector& getGameInstallDirs() { return settings_install_dirs; } + std::filesystem::path getAddonInstallDir() { if (settings_addon_install_dir.empty()) { // Default for users without a config file or a config file from before this option existed @@ -414,36 +435,47 @@ std::filesystem::path getAddonInstallDir() { } return settings_addon_install_dir; } + u32 getMainWindowTheme() { return mw_themes; } + u32 getIconSize() { return m_icon_size; } + u32 getIconSizeGrid() { return m_icon_size_grid; } + u32 getSliderPosition() { return m_slider_pos; } + u32 getSliderPositionGrid() { return m_slider_pos_grid; } + u32 getTableMode() { return m_table_mode; } + u32 getMainWindowWidth() { return m_window_size_W; } + u32 getMainWindowHeight() { return m_window_size_H; } + std::vector getPkgViewer() { return m_pkg_viewer; } + std::vector getElfViewer() { return m_elf_viewer; } + std::vector getRecentFiles() { return m_recent_files; } @@ -455,6 +487,7 @@ std::string getEmulatorLanguage() { u32 GetLanguage() { return m_language; } + void load(const std::filesystem::path& path) { // If the configuration file does not exist, create it and return std::error_code error; @@ -545,18 +578,6 @@ void load(const std::filesystem::path& path) { m_window_size_W = toml::find_or(gui, "mw_width", 0); m_window_size_H = toml::find_or(gui, "mw_height", 0); - // TODO Migration code, after a major release this should be removed. - auto old_game_install_dir = toml::find_fs_path_or(gui, "installDir", {}); - if (!old_game_install_dir.empty()) { - addGameInstallDir(std::filesystem::path{old_game_install_dir}); - } else { - const auto install_dir_array = - toml::find_or>(gui, "installDirs", {}); - for (const auto& dir : install_dir_array) { - addGameInstallDir(std::filesystem::path{dir}); - } - } - settings_addon_install_dir = toml::find_fs_path_or(gui, "addonInstallDir", {}); main_window_geometry_x = toml::find_or(gui, "geometry_x", 0); main_window_geometry_y = toml::find_or(gui, "geometry_y", 0); @@ -575,6 +596,7 @@ void load(const std::filesystem::path& path) { m_language = toml::find_or(settings, "consoleLanguage", 1); } } + void save(const std::filesystem::path& path) { toml::value data; @@ -655,9 +677,6 @@ void save(const std::filesystem::path& path) { data["Settings"]["consoleLanguage"] = m_language; - // TODO Migration code, after a major release this should be removed. - data.at("GUI").as_table().erase("installDir"); - std::ofstream file(path, std::ios::binary); file << data; file.close(); From 7e525a59e46fe0771c774f9fe38ee5fe3d6a5ade Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Sat, 30 Nov 2024 02:28:31 -0600 Subject: [PATCH 074/549] Kernel Fixes (#1605) * scePthreadSetprio Changes FindThread uses posix error codes, so the function export should apply the ORBIS wrapper to convert these. Since it uses posix codes, I've also renamed the function to align with other posix functions. Lastly, this fixes a compile warning about ret sometimes not getting initialized. * Implement posix_munmap Used by Hatsune Miku Project Diva X during intros. May help with stability on Linux, probably won't change anything on Windows. * Exports Some missing function exports I've seen in my tests. sceKernelAvailableFlexibleMemorySize export is used in Final Fantasy XV Episode Duscae posix_pthread_setprio and posix_pthread_getschedparam are used by Spider-Man Miles Morales scePthreadKeyDelete is used in UE4 games. I've also added in a typo fix related to my previous PR. * libScePosix export for posix_pthread_attr_setguardsize Used in Hatsune Miku Project Diva X v1.02 --- src/core/libraries/kernel/file_system.cpp | 2 +- src/core/libraries/kernel/memory.cpp | 15 +++++++++++++++ src/core/libraries/kernel/threads/pthread.cpp | 12 ++++++------ .../libraries/kernel/threads/pthread_attr.cpp | 2 ++ .../libraries/kernel/threads/pthread_spec.cpp | 1 + 5 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index c4f1e4799..d0ffa21ca 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -502,7 +502,7 @@ s32 PS4_SYSV_ABI sceKernelFsync(int fd) { s32 PS4_SYSV_ABI posix_fsync(int fd) { s32 result = sceKernelFsync(fd); if (result < 0) { - LOG_ERROR(Kernel_Pthread, "posix_fstat: error = {}", result); + LOG_ERROR(Kernel_Pthread, "posix_fsync: error = {}", result); ErrSceToPosix(result); return -1; } diff --git a/src/core/libraries/kernel/memory.cpp b/src/core/libraries/kernel/memory.cpp index 13b3a9f48..4d55fc2a3 100644 --- a/src/core/libraries/kernel/memory.cpp +++ b/src/core/libraries/kernel/memory.cpp @@ -10,6 +10,7 @@ #include "common/singleton.h" #include "core/file_sys/fs.h" #include "core/libraries/error_codes.h" +#include "core/libraries/kernel/kernel.h" #include "core/libraries/kernel/memory.h" #include "core/libraries/libs.h" #include "core/linker.h" @@ -495,6 +496,16 @@ int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len) { return SCE_OK; } +int PS4_SYSV_ABI posix_munmap(void* addr, size_t len) { + int result = sceKernelMunmap(addr, len); + if (result < 0) { + LOG_ERROR(Kernel_Pthread, "posix_munmap: error = {}", result); + ErrSceToPosix(result); + return -1; + } + return result; +} + void RegisterMemory(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("rTXw65xmLIA", "libkernel", 1, "libkernel", 1, 1, sceKernelAllocateDirectMemory); LIB_FUNCTION("B+vc2AO2Zrc", "libkernel", 1, "libkernel", 1, 1, @@ -517,6 +528,8 @@ void RegisterMemory(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("mL8NDH86iQI", "libkernel", 1, "libkernel", 1, 1, sceKernelMapNamedFlexibleMemory); LIB_FUNCTION("aNz11fnnzi4", "libkernel", 1, "libkernel", 1, 1, sceKernelAvailableFlexibleMemorySize); + LIB_FUNCTION("aNz11fnnzi4", "libkernel_avlfmem", 1, "libkernel", 1, 1, + sceKernelAvailableFlexibleMemorySize); LIB_FUNCTION("IWIBBdTHit4", "libkernel", 1, "libkernel", 1, 1, sceKernelMapFlexibleMemory); LIB_FUNCTION("p5EcQeEeJAE", "libkernel", 1, "libkernel", 1, 1, _sceKernelRtldSetApplicationHeapAPI); @@ -537,6 +550,8 @@ void RegisterMemory(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("BPE9s9vQQXo", "libkernel", 1, "libkernel", 1, 1, posix_mmap); LIB_FUNCTION("BPE9s9vQQXo", "libScePosix", 1, "libkernel", 1, 1, posix_mmap); + LIB_FUNCTION("UqDGjXA5yUM", "libkernel", 1, "libkernel", 1, 1, posix_munmap); + LIB_FUNCTION("UqDGjXA5yUM", "libScePosix", 1, "libkernel", 1, 1, posix_munmap); } } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/pthread.cpp b/src/core/libraries/kernel/threads/pthread.cpp index 28e2be06e..18ddcb9bf 100644 --- a/src/core/libraries/kernel/threads/pthread.cpp +++ b/src/core/libraries/kernel/threads/pthread.cpp @@ -421,29 +421,27 @@ int PS4_SYSV_ABI scePthreadGetprio(PthreadT thread, int* priority) { return 0; } -int PS4_SYSV_ABI scePthreadSetprio(PthreadT thread, int prio) { +int PS4_SYSV_ABI posix_pthread_setprio(PthreadT thread, int prio) { SchedParam param; - int ret; param.sched_priority = prio; auto* thread_state = ThrState::Instance(); if (thread == g_curthread) { g_curthread->lock.lock(); - } else if (int ret = thread_state->FindThread(thread, /*include dead*/ 0)) { + } else if (int ret = thread_state->FindThread(thread, /*include dead*/ 0); ret != 0) { return ret; } if (thread->attr.sched_policy == SchedPolicy::Other || thread->attr.prio == prio) { thread->attr.prio = prio; - ret = 0; } else { // TODO: _thr_setscheduler thread->attr.prio = prio; } thread->lock.unlock(); - return ret; + return 0; } enum class PthreadCancelState : u32 { @@ -495,6 +493,8 @@ void RegisterThread(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("OxhIB8LB-PQ", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create); LIB_FUNCTION("Jmi+9w9u0E4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create_name_np); LIB_FUNCTION("lZzFeSxPl08", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setcancelstate); + LIB_FUNCTION("a2P9wYGeZvc", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setprio); + LIB_FUNCTION("FIs3-UQT9sg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_getschedparam); LIB_FUNCTION("6XG4B33N09g", "libScePosix", 1, "libkernel", 1, 1, sched_yield); // Posix-Kernel @@ -517,7 +517,7 @@ void RegisterThread(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("T72hz6ffq08", "libkernel", 1, "libkernel", 1, 1, posix_pthread_yield); LIB_FUNCTION("EI-5-jlq2dE", "libkernel", 1, "libkernel", 1, 1, posix_pthread_getthreadid_np); LIB_FUNCTION("1tKyG7RlMJo", "libkernel", 1, "libkernel", 1, 1, scePthreadGetprio); - LIB_FUNCTION("W0Hpm2X0uPE", "libkernel", 1, "libkernel", 1, 1, scePthreadSetprio); + LIB_FUNCTION("W0Hpm2X0uPE", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_setprio)); LIB_FUNCTION("rNhWz+lvOMU", "libkernel", 1, "libkernel", 1, 1, _sceKernelSetThreadDtors); LIB_FUNCTION("6XG4B33N09g", "libkernel", 1, "libkernel", 1, 1, sched_yield); } diff --git a/src/core/libraries/kernel/threads/pthread_attr.cpp b/src/core/libraries/kernel/threads/pthread_attr.cpp index db32fa3d5..ead0ff7df 100644 --- a/src/core/libraries/kernel/threads/pthread_attr.cpp +++ b/src/core/libraries/kernel/threads/pthread_attr.cpp @@ -303,6 +303,8 @@ void RegisterThreadAttr(Core::Loader::SymbolsResolver* sym) { posix_pthread_attr_getstacksize); LIB_FUNCTION("VUT1ZSrHT0I", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_attr_getdetachstate); + LIB_FUNCTION("JKyG3SWyA10", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_attr_setguardsize); // Orbis LIB_FUNCTION("4+h9EzwKF4I", "libkernel", 1, "libkernel", 1, 1, diff --git a/src/core/libraries/kernel/threads/pthread_spec.cpp b/src/core/libraries/kernel/threads/pthread_spec.cpp index 3d6b0f4d9..f0803b671 100644 --- a/src/core/libraries/kernel/threads/pthread_spec.cpp +++ b/src/core/libraries/kernel/threads/pthread_spec.cpp @@ -150,6 +150,7 @@ void RegisterSpec(Core::Loader::SymbolsResolver* sym) { // Orbis LIB_FUNCTION("geDaqgH9lTg", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_key_create)); + LIB_FUNCTION("PrdHuuDekhY", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_key_delete)); LIB_FUNCTION("eoht7mQOCmo", "libkernel", 1, "libkernel", 1, 1, posix_pthread_getspecific); LIB_FUNCTION("+BzXYkqYeLE", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_setspecific)); From 7bf168e47fcead28585194560a37580edb71ddbb Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Sat, 30 Nov 2024 11:28:46 +0200 Subject: [PATCH 075/549] Revert "Remove save migration code (#1621)" This reverts commit b1a024efbd09a2babcae8abd3aee589f0ca8819c. --- src/common/config.cpp | 55 ++++++++++++++----------------------------- 1 file changed, 18 insertions(+), 37 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index 5c857afb4..e97a46005 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -3,14 +3,13 @@ #include #include +#include #include #include // for wstring support #include - +#include "common/logging/formatter.h" #include "common/path_util.h" #include "config.h" -#include "logging/formatter.h" -#include "version.h" namespace toml { template @@ -82,8 +81,7 @@ std::vector m_pkg_viewer; std::vector m_elf_viewer; std::vector m_recent_files; std::string emulator_language = "en"; - -// Language +// Settings u32 m_language = 1; // english bool isNeoMode() { @@ -336,7 +334,6 @@ void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h) { main_window_geometry_w = w; main_window_geometry_h = h; } - bool addGameInstallDir(const std::filesystem::path& dir) { if (std::find(settings_install_dirs.begin(), settings_install_dirs.end(), dir) == settings_install_dirs.end()) { @@ -345,60 +342,47 @@ bool addGameInstallDir(const std::filesystem::path& dir) { } return false; } - void removeGameInstallDir(const std::filesystem::path& dir) { auto iterator = std::find(settings_install_dirs.begin(), settings_install_dirs.end(), dir); if (iterator != settings_install_dirs.end()) { settings_install_dirs.erase(iterator); } } - void setAddonInstallDir(const std::filesystem::path& dir) { settings_addon_install_dir = dir; } - void setMainWindowTheme(u32 theme) { mw_themes = theme; } - void setIconSize(u32 size) { m_icon_size = size; } - void setIconSizeGrid(u32 size) { m_icon_size_grid = size; } - void setSliderPosition(u32 pos) { m_slider_pos = pos; } - void setSliderPositionGrid(u32 pos) { m_slider_pos_grid = pos; } - void setTableMode(u32 mode) { m_table_mode = mode; } - void setMainWindowWidth(u32 width) { m_window_size_W = width; } - void setMainWindowHeight(u32 height) { m_window_size_H = height; } - void setPkgViewer(const std::vector& pkgList) { m_pkg_viewer.resize(pkgList.size()); m_pkg_viewer = pkgList; } - void setElfViewer(const std::vector& elfList) { m_elf_viewer.resize(elfList.size()); m_elf_viewer = elfList; } - void setRecentFiles(const std::vector& recentFiles) { m_recent_files.resize(recentFiles.size()); m_recent_files = recentFiles; @@ -411,23 +395,18 @@ void setEmulatorLanguage(std::string language) { u32 getMainWindowGeometryX() { return main_window_geometry_x; } - u32 getMainWindowGeometryY() { return main_window_geometry_y; } - u32 getMainWindowGeometryW() { return main_window_geometry_w; } - u32 getMainWindowGeometryH() { return main_window_geometry_h; } - const std::vector& getGameInstallDirs() { return settings_install_dirs; } - std::filesystem::path getAddonInstallDir() { if (settings_addon_install_dir.empty()) { // Default for users without a config file or a config file from before this option existed @@ -435,47 +414,36 @@ std::filesystem::path getAddonInstallDir() { } return settings_addon_install_dir; } - u32 getMainWindowTheme() { return mw_themes; } - u32 getIconSize() { return m_icon_size; } - u32 getIconSizeGrid() { return m_icon_size_grid; } - u32 getSliderPosition() { return m_slider_pos; } - u32 getSliderPositionGrid() { return m_slider_pos_grid; } - u32 getTableMode() { return m_table_mode; } - u32 getMainWindowWidth() { return m_window_size_W; } - u32 getMainWindowHeight() { return m_window_size_H; } - std::vector getPkgViewer() { return m_pkg_viewer; } - std::vector getElfViewer() { return m_elf_viewer; } - std::vector getRecentFiles() { return m_recent_files; } @@ -487,7 +455,6 @@ std::string getEmulatorLanguage() { u32 GetLanguage() { return m_language; } - void load(const std::filesystem::path& path) { // If the configuration file does not exist, create it and return std::error_code error; @@ -578,6 +545,18 @@ void load(const std::filesystem::path& path) { m_window_size_W = toml::find_or(gui, "mw_width", 0); m_window_size_H = toml::find_or(gui, "mw_height", 0); + // TODO Migration code, after a major release this should be removed. + auto old_game_install_dir = toml::find_fs_path_or(gui, "installDir", {}); + if (!old_game_install_dir.empty()) { + addGameInstallDir(std::filesystem::path{old_game_install_dir}); + } else { + const auto install_dir_array = + toml::find_or>(gui, "installDirs", {}); + for (const auto& dir : install_dir_array) { + addGameInstallDir(std::filesystem::path{dir}); + } + } + settings_addon_install_dir = toml::find_fs_path_or(gui, "addonInstallDir", {}); main_window_geometry_x = toml::find_or(gui, "geometry_x", 0); main_window_geometry_y = toml::find_or(gui, "geometry_y", 0); @@ -596,7 +575,6 @@ void load(const std::filesystem::path& path) { m_language = toml::find_or(settings, "consoleLanguage", 1); } } - void save(const std::filesystem::path& path) { toml::value data; @@ -677,6 +655,9 @@ void save(const std::filesystem::path& path) { data["Settings"]["consoleLanguage"] = m_language; + // TODO Migration code, after a major release this should be removed. + data.at("GUI").as_table().erase("installDir"); + std::ofstream file(path, std::ios::binary); file << data; file.close(); From 99ac10a41723edfcb3419bb19934163aada23660 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sat, 30 Nov 2024 01:30:22 -0800 Subject: [PATCH 076/549] libraries: Add libSceRazorCpu HLE skeleton. (#1624) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Remove save migration code (#1621) * Kernel Fixes (#1605) * scePthreadSetprio Changes FindThread uses posix error codes, so the function export should apply the ORBIS wrapper to convert these. Since it uses posix codes, I've also renamed the function to align with other posix functions. Lastly, this fixes a compile warning about ret sometimes not getting initialized. * Implement posix_munmap Used by Hatsune Miku Project Diva X during intros. May help with stability on Linux, probably won't change anything on Windows. * Exports Some missing function exports I've seen in my tests. sceKernelAvailableFlexibleMemorySize export is used in Final Fantasy XV Episode Duscae posix_pthread_setprio and posix_pthread_getschedparam are used by Spider-Man Miles Morales scePthreadKeyDelete is used in UE4 games. I've also added in a typo fix related to my previous PR. * libScePosix export for posix_pthread_attr_setguardsize Used in Hatsune Miku Project Diva X v1.02 * libraries: Add libSceRazorCpu HLE skeleton. --------- Co-authored-by: ¥IGA <164882787+Xphalnos@users.noreply.github.com> Co-authored-by: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> --- CMakeLists.txt | 2 + src/common/config.cpp | 55 +++-- src/common/logging/filter.cpp | 1 + src/common/logging/types.h | 1 + src/core/libraries/libs.cpp | 2 + src/core/libraries/razor_cpu/razor_cpu.cpp | 249 +++++++++++++++++++++ src/core/libraries/razor_cpu/razor_cpu.h | 17 ++ src/core/libraries/system/sysmodule.cpp | 4 - 8 files changed, 309 insertions(+), 22 deletions(-) create mode 100644 src/core/libraries/razor_cpu/razor_cpu.cpp create mode 100644 src/core/libraries/razor_cpu/razor_cpu.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 657b7c5bf..55f1172f2 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -318,6 +318,8 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp src/core/libraries/remote_play/remoteplay.h src/core/libraries/share_play/shareplay.cpp src/core/libraries/share_play/shareplay.h + src/core/libraries/razor_cpu/razor_cpu.cpp + src/core/libraries/razor_cpu/razor_cpu.h ) set(VIDEOOUT_LIB src/core/libraries/videoout/buffer.h diff --git a/src/common/config.cpp b/src/common/config.cpp index e97a46005..5c857afb4 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -3,13 +3,14 @@ #include #include -#include #include #include // for wstring support #include -#include "common/logging/formatter.h" + #include "common/path_util.h" #include "config.h" +#include "logging/formatter.h" +#include "version.h" namespace toml { template @@ -81,7 +82,8 @@ std::vector m_pkg_viewer; std::vector m_elf_viewer; std::vector m_recent_files; std::string emulator_language = "en"; -// Settings + +// Language u32 m_language = 1; // english bool isNeoMode() { @@ -334,6 +336,7 @@ void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h) { main_window_geometry_w = w; main_window_geometry_h = h; } + bool addGameInstallDir(const std::filesystem::path& dir) { if (std::find(settings_install_dirs.begin(), settings_install_dirs.end(), dir) == settings_install_dirs.end()) { @@ -342,47 +345,60 @@ bool addGameInstallDir(const std::filesystem::path& dir) { } return false; } + void removeGameInstallDir(const std::filesystem::path& dir) { auto iterator = std::find(settings_install_dirs.begin(), settings_install_dirs.end(), dir); if (iterator != settings_install_dirs.end()) { settings_install_dirs.erase(iterator); } } + void setAddonInstallDir(const std::filesystem::path& dir) { settings_addon_install_dir = dir; } + void setMainWindowTheme(u32 theme) { mw_themes = theme; } + void setIconSize(u32 size) { m_icon_size = size; } + void setIconSizeGrid(u32 size) { m_icon_size_grid = size; } + void setSliderPosition(u32 pos) { m_slider_pos = pos; } + void setSliderPositionGrid(u32 pos) { m_slider_pos_grid = pos; } + void setTableMode(u32 mode) { m_table_mode = mode; } + void setMainWindowWidth(u32 width) { m_window_size_W = width; } + void setMainWindowHeight(u32 height) { m_window_size_H = height; } + void setPkgViewer(const std::vector& pkgList) { m_pkg_viewer.resize(pkgList.size()); m_pkg_viewer = pkgList; } + void setElfViewer(const std::vector& elfList) { m_elf_viewer.resize(elfList.size()); m_elf_viewer = elfList; } + void setRecentFiles(const std::vector& recentFiles) { m_recent_files.resize(recentFiles.size()); m_recent_files = recentFiles; @@ -395,18 +411,23 @@ void setEmulatorLanguage(std::string language) { u32 getMainWindowGeometryX() { return main_window_geometry_x; } + u32 getMainWindowGeometryY() { return main_window_geometry_y; } + u32 getMainWindowGeometryW() { return main_window_geometry_w; } + u32 getMainWindowGeometryH() { return main_window_geometry_h; } + const std::vector& getGameInstallDirs() { return settings_install_dirs; } + std::filesystem::path getAddonInstallDir() { if (settings_addon_install_dir.empty()) { // Default for users without a config file or a config file from before this option existed @@ -414,36 +435,47 @@ std::filesystem::path getAddonInstallDir() { } return settings_addon_install_dir; } + u32 getMainWindowTheme() { return mw_themes; } + u32 getIconSize() { return m_icon_size; } + u32 getIconSizeGrid() { return m_icon_size_grid; } + u32 getSliderPosition() { return m_slider_pos; } + u32 getSliderPositionGrid() { return m_slider_pos_grid; } + u32 getTableMode() { return m_table_mode; } + u32 getMainWindowWidth() { return m_window_size_W; } + u32 getMainWindowHeight() { return m_window_size_H; } + std::vector getPkgViewer() { return m_pkg_viewer; } + std::vector getElfViewer() { return m_elf_viewer; } + std::vector getRecentFiles() { return m_recent_files; } @@ -455,6 +487,7 @@ std::string getEmulatorLanguage() { u32 GetLanguage() { return m_language; } + void load(const std::filesystem::path& path) { // If the configuration file does not exist, create it and return std::error_code error; @@ -545,18 +578,6 @@ void load(const std::filesystem::path& path) { m_window_size_W = toml::find_or(gui, "mw_width", 0); m_window_size_H = toml::find_or(gui, "mw_height", 0); - // TODO Migration code, after a major release this should be removed. - auto old_game_install_dir = toml::find_fs_path_or(gui, "installDir", {}); - if (!old_game_install_dir.empty()) { - addGameInstallDir(std::filesystem::path{old_game_install_dir}); - } else { - const auto install_dir_array = - toml::find_or>(gui, "installDirs", {}); - for (const auto& dir : install_dir_array) { - addGameInstallDir(std::filesystem::path{dir}); - } - } - settings_addon_install_dir = toml::find_fs_path_or(gui, "addonInstallDir", {}); main_window_geometry_x = toml::find_or(gui, "geometry_x", 0); main_window_geometry_y = toml::find_or(gui, "geometry_y", 0); @@ -575,6 +596,7 @@ void load(const std::filesystem::path& path) { m_language = toml::find_or(settings, "consoleLanguage", 1); } } + void save(const std::filesystem::path& path) { toml::value data; @@ -655,9 +677,6 @@ void save(const std::filesystem::path& path) { data["Settings"]["consoleLanguage"] = m_language; - // TODO Migration code, after a major release this should be removed. - data.at("GUI").as_table().erase("installDir"); - std::ofstream file(path, std::ios::binary); file << data; file.close(); diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index 5ca594bf7..fc9fa0557 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -121,6 +121,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Lib, Fiber) \ SUB(Lib, Vdec2) \ SUB(Lib, Videodec) \ + SUB(Lib, RazorCpu) \ CLS(Frontend) \ CLS(Render) \ SUB(Render, Vulkan) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index 2821729d4..1422c3926 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -88,6 +88,7 @@ enum class Class : u8 { Lib_Fiber, ///< The LibSceFiber implementation. Lib_Vdec2, ///< The LibSceVideodec2 implementation. Lib_Videodec, ///< The LibSceVideodec implementation. + Lib_RazorCpu, ///< The LibRazorCpu implementation. Frontend, ///< Emulator UI Render, ///< Video Core Render_Vulkan, ///< Vulkan backend diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index 537862a6c..53b67b395 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -28,6 +28,7 @@ #include "core/libraries/pad/pad.h" #include "core/libraries/playgo/playgo.h" #include "core/libraries/random/random.h" +#include "core/libraries/razor_cpu/razor_cpu.h" #include "core/libraries/remote_play/remoteplay.h" #include "core/libraries/rtc/rtc.h" #include "core/libraries/save_data/dialog/savedatadialog.h" @@ -87,6 +88,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::SharePlay::RegisterlibSceSharePlay(sym); Libraries::Remoteplay::RegisterlibSceRemoteplay(sym); Libraries::Videodec::RegisterlibSceVideodec(sym); + Libraries::RazorCpu::RegisterlibSceRazorCpu(sym); } } // namespace Libraries diff --git a/src/core/libraries/razor_cpu/razor_cpu.cpp b/src/core/libraries/razor_cpu/razor_cpu.cpp new file mode 100644 index 000000000..99707e972 --- /dev/null +++ b/src/core/libraries/razor_cpu/razor_cpu.cpp @@ -0,0 +1,249 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "razor_cpu.h" + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" + +namespace Libraries::RazorCpu { + +s32 PS4_SYSV_ABI sceRazorCpuBeginLogicalFileAccess() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +void PS4_SYSV_ABI sceRazorCpuDisableFiberUserMarkers() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); +} + +s32 PS4_SYSV_ABI sceRazorCpuEndLogicalFileAccess() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuFiberLogNameChange() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuFiberSwitch() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +bool PS4_SYSV_ABI sceRazorCpuFlushOccurred() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return false; +} + +s32 PS4_SYSV_ABI sceRazorCpuGetDataTagStorageSize() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuGpuMarkerSync() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuInitDataTags() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuInitializeGpuMarkerContext() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuInitializeInternal() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +bool PS4_SYSV_ABI sceRazorCpuIsCapturing() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return false; +} + +s32 PS4_SYSV_ABI sceRazorCpuJobManagerDispatch() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuJobManagerJob() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuJobManagerSequence() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuNamedSync() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuPlotValue() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuPopMarker() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuPushMarker() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuPushMarkerStatic() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuResizeTaggedBuffer() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +void PS4_SYSV_ABI sceRazorCpuSetPopMarkerCallback() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); +} + +void PS4_SYSV_ABI sceRazorCpuSetPushMarkerCallback() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); +} + +void PS4_SYSV_ABI sceRazorCpuSetPushMarkerStaticCallback() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); +} + +s32 PS4_SYSV_ABI sceRazorCpuShutdownDataTags() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuStartCaptureInternal() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuStopCaptureInternal() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuSync() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuTagArray() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuTagBuffer() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuUnTagBuffer() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuWorkloadRunBegin() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuWorkloadRunEnd() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuWorkloadSubmit() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuWriteBookmark() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +void RegisterlibSceRazorCpu(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("JFzLJBlYIJE", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuBeginLogicalFileAccess); + LIB_FUNCTION("SfRTRZ1Sh+U", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuDisableFiberUserMarkers); + LIB_FUNCTION("gVioM9cbiDs", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuEndLogicalFileAccess); + LIB_FUNCTION("G90IIOtgFQ0", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuFiberLogNameChange); + LIB_FUNCTION("PAytDtFGpqY", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuFiberSwitch); + LIB_FUNCTION("sPhrQD31ClM", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuFlushOccurred); + LIB_FUNCTION("B782NptkGUc", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuGetDataTagStorageSize); + LIB_FUNCTION("EH9Au2RlSrE", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuGpuMarkerSync); + LIB_FUNCTION("A7oRMdaOJP8", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuInitDataTags); + LIB_FUNCTION("NFwh-J-BrI0", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuInitializeGpuMarkerContext); + LIB_FUNCTION("ElNyedXaa4o", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuInitializeInternal); + LIB_FUNCTION("EboejOQvLL4", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuIsCapturing); + LIB_FUNCTION("dnEdyY4+klQ", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuJobManagerDispatch); + LIB_FUNCTION("KP+TBWGHlgs", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuJobManagerJob); + LIB_FUNCTION("9FowWFMEIM8", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuJobManagerSequence); + LIB_FUNCTION("XCuZoBSVFG8", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, sceRazorCpuNamedSync); + LIB_FUNCTION("njGikRrxkC0", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, sceRazorCpuPlotValue); + LIB_FUNCTION("YpkGsMXP3ew", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, sceRazorCpuPopMarker); + LIB_FUNCTION("zw+celG7zSI", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, sceRazorCpuPushMarker); + LIB_FUNCTION("uZrOwuNJX-M", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuPushMarkerStatic); + LIB_FUNCTION("D0yUjM33QqU", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuResizeTaggedBuffer); + LIB_FUNCTION("jqYWaTfgZs0", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuSetPopMarkerCallback); + LIB_FUNCTION("DJsHcEb94n0", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuSetPushMarkerCallback); + LIB_FUNCTION("EZtqozPTS4M", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuSetPushMarkerStaticCallback); + LIB_FUNCTION("emklx7RK-LY", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuShutdownDataTags); + LIB_FUNCTION("TIytAjYeaik", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuStartCaptureInternal); + LIB_FUNCTION("jWpkVWdMrsM", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuStopCaptureInternal); + LIB_FUNCTION("Ax7NjOzctIM", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, sceRazorCpuSync); + LIB_FUNCTION("we3oTKSPSTw", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, sceRazorCpuTagArray); + LIB_FUNCTION("vyjdThnQfQQ", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, sceRazorCpuTagBuffer); + LIB_FUNCTION("0yNHPIkVTmw", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuUnTagBuffer); + LIB_FUNCTION("Crha9LvwvJM", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuWorkloadRunBegin); + LIB_FUNCTION("q1GxBfGHO0s", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuWorkloadRunEnd); + LIB_FUNCTION("6rUvx-6QmYc", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuWorkloadSubmit); + LIB_FUNCTION("G3brhegfyNg", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuWriteBookmark); +} + +} // namespace Libraries::RazorCpu \ No newline at end of file diff --git a/src/core/libraries/razor_cpu/razor_cpu.h b/src/core/libraries/razor_cpu/razor_cpu.h new file mode 100644 index 000000000..ec25e44b9 --- /dev/null +++ b/src/core/libraries/razor_cpu/razor_cpu.h @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +#include +#include + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::RazorCpu { +void RegisterlibSceRazorCpu(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::RazorCpu \ No newline at end of file diff --git a/src/core/libraries/system/sysmodule.cpp b/src/core/libraries/system/sysmodule.cpp index 8ea61e177..9bed4ef31 100644 --- a/src/core/libraries/system/sysmodule.cpp +++ b/src/core/libraries/system/sysmodule.cpp @@ -48,10 +48,6 @@ int PS4_SYSV_ABI sceSysmoduleIsLoadedInternal(OrbisSysModuleInternal id) { LOG_ERROR(Lib_SysModule, "Invalid internal sysmodule ID: {:#x}", static_cast(id)); return ORBIS_SYSMODULE_INVALID_ID; } - if (id == OrbisSysModuleInternal::ORBIS_SYSMODULE_INTERNAL_RAZOR_CPU) { - // Internal debugging library, report as not loaded so it won't be used. - return ORBIS_SYSMODULE_NOT_LOADED; - } return ORBIS_OK; } From 36044043bcf4600fcda33eb80efde4c4ec5a514d Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sat, 30 Nov 2024 02:08:34 -0800 Subject: [PATCH 077/549] config: Fix loading install directories. (#1626) --- src/common/config.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/common/config.cpp b/src/common/config.cpp index 5c857afb4..c01583990 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -578,6 +578,12 @@ void load(const std::filesystem::path& path) { m_window_size_W = toml::find_or(gui, "mw_width", 0); m_window_size_H = toml::find_or(gui, "mw_height", 0); + const auto install_dir_array = + toml::find_or>(gui, "installDirs", {}); + for (const auto& dir : install_dir_array) { + addGameInstallDir(std::filesystem::path{dir}); + } + settings_addon_install_dir = toml::find_fs_path_or(gui, "addonInstallDir", {}); main_window_geometry_x = toml::find_or(gui, "geometry_x", 0); main_window_geometry_y = toml::find_or(gui, "geometry_y", 0); From c0d43a1a5f304d21f4b7c9457108c2d21447905e Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Sat, 30 Nov 2024 17:05:08 +0300 Subject: [PATCH 078/549] Fixed incorrectly skipped RTs --- src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | 5 ++--- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 7 ------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index b576d4780..fb158c55f 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -260,7 +260,7 @@ bool PipelineCache::RefreshGraphicsKey() { // recompiler. for (auto cb = 0u, remapped_cb = 0u; cb < Liverpool::NumColorBuffers; ++cb) { auto const& col_buf = regs.color_buffers[cb]; - if (skip_cb_binding || !col_buf || !regs.color_target_mask.GetMask(cb)) { + if (skip_cb_binding || !col_buf) { continue; } const auto base_format = @@ -362,8 +362,7 @@ bool PipelineCache::RefreshGraphicsKey() { // Second pass to fill remain CB pipeline key data for (auto cb = 0u, remapped_cb = 0u; cb < Liverpool::NumColorBuffers; ++cb) { auto const& col_buf = regs.color_buffers[cb]; - if (skip_cb_binding || !col_buf || !regs.color_target_mask.GetMask(cb) || - (key.mrt_mask & (1u << cb)) == 0) { + if (skip_cb_binding || !col_buf || (key.mrt_mask & (1u << cb)) == 0) { key.color_formats[cb] = vk::Format::eUndefined; key.mrt_swizzles[cb] = Liverpool::ColorBuffer::SwapMode::Standard; continue; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index a6fc872d9..ff5e88141 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -95,13 +95,6 @@ RenderState Rasterizer::PrepareRenderState(u32 mrt_mask) { continue; } - // If the color buffer is still bound but rendering to it is disabled by the target - // mask, we need to prevent the render area from being affected by unbound render target - // extents. - if (!regs.color_target_mask.GetMask(col_buf_id)) { - continue; - } - // Skip stale color buffers if shader doesn't output to them. Otherwise it will perform // an unnecessary transition and may result in state conflict if the resource is already // bound for reading. From 07f4a0305ba9c53a220774c92aae5bbd9f9dae45 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sat, 30 Nov 2024 09:19:07 -0800 Subject: [PATCH 079/549] semaphore: Use condvars with separate signaled flag to prevent races (#1615) * Revert "semaphore: Use binary_semaphore instead of condvar" This reverts commit 85dc57b86875f7b0807dbc8d886cd5b1bdc407e7. * semaphore: Use separate signaled flag to prevent races * mutex: Few misc fixes * condvar: Few misc fixes * signals: Add thread name to unhandled signal message. --- src/core/libraries/kernel/threads/condvar.cpp | 7 +++-- src/core/libraries/kernel/threads/mutex.cpp | 5 +-- src/core/libraries/kernel/threads/pthread.h | 2 +- .../libraries/kernel/threads/semaphore.cpp | 31 ++++++++++--------- src/core/signals.cpp | 20 +++++++++--- 5 files changed, 40 insertions(+), 25 deletions(-) diff --git a/src/core/libraries/kernel/threads/condvar.cpp b/src/core/libraries/kernel/threads/condvar.cpp index 5831df9be..4437af964 100644 --- a/src/core/libraries/kernel/threads/condvar.cpp +++ b/src/core/libraries/kernel/threads/condvar.cpp @@ -122,7 +122,7 @@ int PthreadCond::Wait(PthreadMutexT* mutex, const OrbisKernelTimespec* abstime, SleepqUnlock(this); //_thr_cancel_enter2(curthread, 0); - int error = curthread->Sleep(abstime, usec) ? 0 : POSIX_ETIMEDOUT; + error = curthread->Sleep(abstime, usec) ? 0 : POSIX_ETIMEDOUT; //_thr_cancel_leave(curthread, 0); SleepqLock(this); @@ -145,7 +145,10 @@ int PthreadCond::Wait(PthreadMutexT* mutex, const OrbisKernelTimespec* abstime, } SleepqUnlock(this); curthread->mutex_obj = nullptr; - mp->CvLock(recurse); + int error2 = mp->CvLock(recurse); + if (error == 0) { + error = error2; + } return error; } diff --git a/src/core/libraries/kernel/threads/mutex.cpp b/src/core/libraries/kernel/threads/mutex.cpp index 2e3e689fa..fc0bf09bd 100644 --- a/src/core/libraries/kernel/threads/mutex.cpp +++ b/src/core/libraries/kernel/threads/mutex.cpp @@ -121,6 +121,7 @@ int PthreadMutex::SelfTryLock() { switch (Type()) { case PthreadMutexType::ErrorCheck: case PthreadMutexType::Normal: + case PthreadMutexType::AdaptiveNp: return POSIX_EBUSY; case PthreadMutexType::Recursive: { /* Increment the lock count: */ @@ -224,7 +225,7 @@ int PthreadMutex::Lock(const OrbisKernelTimespec* abstime, u64 usec) { [[unlikely]] { ret = POSIX_EINVAL; } else { - if (THR_RELTIME) { + if (abstime == THR_RELTIME) { ret = m_lock.try_lock_for(std::chrono::microseconds(usec)) ? 0 : POSIX_ETIMEDOUT; } else { ret = m_lock.try_lock_until(abstime->TimePoint()) ? 0 : POSIX_ETIMEDOUT; @@ -336,7 +337,7 @@ int PS4_SYSV_ABI posix_pthread_mutex_isowned_np(PthreadMutexT* mutex) { return m->m_owner == g_curthread; } -bool PthreadMutex::IsOwned(Pthread* curthread) const { +int PthreadMutex::IsOwned(Pthread* curthread) const { if (this <= THR_MUTEX_DESTROYED) [[unlikely]] { if (this == THR_MUTEX_DESTROYED) { return POSIX_EINVAL; diff --git a/src/core/libraries/kernel/threads/pthread.h b/src/core/libraries/kernel/threads/pthread.h index 0a3ab57dd..b41ca2abd 100644 --- a/src/core/libraries/kernel/threads/pthread.h +++ b/src/core/libraries/kernel/threads/pthread.h @@ -79,7 +79,7 @@ struct PthreadMutex { return Unlock(); } - bool IsOwned(Pthread* curthread) const; + int IsOwned(Pthread* curthread) const; }; using PthreadMutexT = PthreadMutex*; diff --git a/src/core/libraries/kernel/threads/semaphore.cpp b/src/core/libraries/kernel/threads/semaphore.cpp index 9fcbd4356..db14c298c 100644 --- a/src/core/libraries/kernel/threads/semaphore.cpp +++ b/src/core/libraries/kernel/threads/semaphore.cpp @@ -49,9 +49,7 @@ public: const auto it = AddWaiter(&waiter); // Perform the wait. - lk.unlock(); - const s32 result = waiter.Wait(timeout); - lk.lock(); + const s32 result = waiter.Wait(lk, timeout); if (result == SCE_KERNEL_ERROR_ETIMEDOUT) { wait_list.erase(it); } @@ -74,7 +72,8 @@ public: } it = wait_list.erase(it); token_count -= waiter->need_count; - waiter->sema.release(); + waiter->was_signaled = true; + waiter->cv.notify_one(); } return true; @@ -87,7 +86,7 @@ public: } for (auto* waiter : wait_list) { waiter->was_cancled = true; - waiter->sema.release(); + waiter->cv.notify_one(); } wait_list.clear(); token_count = set_count < 0 ? init_count : set_count; @@ -98,20 +97,21 @@ public: std::scoped_lock lk{mutex}; for (auto* waiter : wait_list) { waiter->was_deleted = true; - waiter->sema.release(); + waiter->cv.notify_one(); } wait_list.clear(); } public: struct WaitingThread { - std::binary_semaphore sema; + std::condition_variable cv; u32 priority; s32 need_count; + bool was_signaled{}; bool was_deleted{}; bool was_cancled{}; - explicit WaitingThread(s32 need_count, bool is_fifo) : sema{0}, need_count{need_count} { + explicit WaitingThread(s32 need_count, bool is_fifo) : need_count{need_count} { // Retrieve calling thread priority for sorting into waiting threads list. if (!is_fifo) { priority = g_curthread->attr.prio; @@ -131,24 +131,25 @@ public: return SCE_OK; } - int Wait(u32* timeout) { + int Wait(std::unique_lock& lk, u32* timeout) { if (!timeout) { // Wait indefinitely until we are woken up. - sema.acquire(); + cv.wait(lk); return GetResult(false); } // Wait until timeout runs out, recording how much remaining time there was. const auto start = std::chrono::high_resolution_clock::now(); - const auto sema_timeout = !sema.try_acquire_for(std::chrono::microseconds(*timeout)); + const auto signaled = cv.wait_for(lk, std::chrono::microseconds(*timeout), + [this] { return was_signaled; }); const auto end = std::chrono::high_resolution_clock::now(); const auto time = std::chrono::duration_cast(end - start).count(); - if (sema_timeout) { - *timeout = 0; - } else { + if (signaled) { *timeout -= time; + } else { + *timeout = 0; } - return GetResult(sema_timeout); + return GetResult(!signaled); } }; diff --git a/src/core/signals.cpp b/src/core/signals.cpp index 8faf794ed..89844ae25 100644 --- a/src/core/signals.cpp +++ b/src/core/signals.cpp @@ -41,6 +41,14 @@ static LONG WINAPI SignalHandler(EXCEPTION_POINTERS* pExp) noexcept { #else +static std::string GetThreadName() { + char name[256]; + if (pthread_getname_np(pthread_self(), name, sizeof(name)) != 0) { + return ""; + } + return std::string{name}; +} + static std::string DisassembleInstruction(void* code_address) { char buffer[256] = ""; @@ -71,16 +79,18 @@ static void SignalHandler(int sig, siginfo_t* info, void* raw_context) { case SIGBUS: { const bool is_write = Common::IsWriteError(raw_context); if (!signals->DispatchAccessViolation(raw_context, info->si_addr)) { - UNREACHABLE_MSG("Unhandled access violation at code address {}: {} address {}", - fmt::ptr(code_address), is_write ? "Write to" : "Read from", - fmt::ptr(info->si_addr)); + UNREACHABLE_MSG( + "Unhandled access violation in thread '{}' at code address {}: {} address {}", + GetThreadName(), fmt::ptr(code_address), is_write ? "Write to" : "Read from", + fmt::ptr(info->si_addr)); } break; } case SIGILL: if (!signals->DispatchIllegalInstruction(raw_context)) { - UNREACHABLE_MSG("Unhandled illegal instruction at code address {}: {}", - fmt::ptr(code_address), DisassembleInstruction(code_address)); + UNREACHABLE_MSG("Unhandled illegal instruction in thread '{}' at code address {}: {}", + GetThreadName(), fmt::ptr(code_address), + DisassembleInstruction(code_address)); } break; case SIGUSR1: { // Sleep thread until signal is received From 2002e37ce9a3581ab7668ee289e57a4d44a96a78 Mon Sep 17 00:00:00 2001 From: Vinicius Rangel Date: Sat, 30 Nov 2024 16:15:55 -0300 Subject: [PATCH 080/549] Allow shader patching (#1633) --- documents/patching-shader.md | 19 ++++++++++++ src/common/config.cpp | 7 +++++ src/common/config.h | 1 + .../renderer_vulkan/vk_pipeline_cache.cpp | 30 ++++++++++++++++++- .../renderer_vulkan/vk_pipeline_cache.h | 2 ++ 5 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 documents/patching-shader.md diff --git a/documents/patching-shader.md b/documents/patching-shader.md new file mode 100644 index 000000000..613e89bf9 --- /dev/null +++ b/documents/patching-shader.md @@ -0,0 +1,19 @@ + + +### Install Vulkan SDK and \*ensure `spirv-cross` and `glslc` are in PATH\*. + +1. Enable `dumpShaders` in config.toml + +2. Run `spirv-cross -V fs_0x000000.spv --output fs_0x000000.glsl` to decompile the SPIR-V IR to GLSL. + +3. Edit the GLSL file as you wish + +4. To compile back to SPIR-V, run (change the _**-fshader-stage**_ to correct stage): + `glslc --target-env=vulkan1.3 --target-spv=spv1.6 -fshader-stage=frag fs_0x000000.glsl -o fs_0x000000.spv` + +5. Put the updated .spv file to `shader/patch` folder with the same name as the original shader + +6. Enable `patchShaders` in config.toml \ No newline at end of file diff --git a/src/common/config.cpp b/src/common/config.cpp index c01583990..5c2c8cda6 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -52,6 +52,7 @@ static bool isAutoUpdate = false; static bool isNullGpu = false; static bool shouldCopyGPUBuffers = false; static bool shouldDumpShaders = false; +static bool shouldPatchShaders = true; static u32 vblankDivider = 1; static bool vkValidation = false; static bool vkValidationSync = false; @@ -178,6 +179,10 @@ bool dumpShaders() { return shouldDumpShaders; } +bool patchShaders() { + return shouldPatchShaders; +} + bool isRdocEnabled() { return rdocEnable; } @@ -546,6 +551,7 @@ void load(const std::filesystem::path& path) { isNullGpu = toml::find_or(gpu, "nullGpu", false); shouldCopyGPUBuffers = toml::find_or(gpu, "copyGPUBuffers", false); shouldDumpShaders = toml::find_or(gpu, "dumpShaders", false); + shouldPatchShaders = toml::find_or(gpu, "patchShaders", true); vblankDivider = toml::find_or(gpu, "vblankDivider", 1); } @@ -646,6 +652,7 @@ void save(const std::filesystem::path& path) { data["GPU"]["nullGpu"] = isNullGpu; data["GPU"]["copyGPUBuffers"] = shouldCopyGPUBuffers; data["GPU"]["dumpShaders"] = shouldDumpShaders; + data["GPU"]["patchShaders"] = shouldPatchShaders; data["GPU"]["vblankDivider"] = vblankDivider; data["Vulkan"]["gpuId"] = gpuId; data["Vulkan"]["validation"] = vkValidation; diff --git a/src/common/config.h b/src/common/config.h index 9c71c96a8..400115745 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -42,6 +42,7 @@ bool autoUpdate(); bool nullGpu(); bool copyGPUCmdBuffers(); bool dumpShaders(); +bool patchShaders(); bool isRdocEnabled(); u32 vblankDiv(); diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index b576d4780..d2220dec0 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -406,8 +406,13 @@ vk::ShaderModule PipelineCache::CompileModule(Shader::Info& info, DumpShader(code, info.pgm_hash, info.stage, perm_idx, "bin"); const auto ir_program = Shader::TranslateProgram(code, pools, info, runtime_info, profile); - const auto spv = Shader::Backend::SPIRV::EmitSPIRV(profile, runtime_info, ir_program, binding); + auto spv = Shader::Backend::SPIRV::EmitSPIRV(profile, runtime_info, ir_program, binding); DumpShader(spv, info.pgm_hash, info.stage, perm_idx, "spv"); + auto patch = GetShaderPatch(info.pgm_hash, info.stage, perm_idx, "spv"); + if (patch) { + spv = *patch; + LOG_INFO(Loader, "Loaded patch for {} shader {:#x}", info.stage, info.pgm_hash); + } const auto module = CompileSPV(spv, instance.GetDevice()); const auto name = fmt::format("{}_{:#x}_{}", info.stage, info.pgm_hash, perm_idx); @@ -465,4 +470,27 @@ void PipelineCache::DumpShader(std::span code, u64 hash, Shader::Stag file.WriteSpan(code); } +std::optional> PipelineCache::GetShaderPatch(u64 hash, Shader::Stage stage, + size_t perm_idx, + std::string_view ext) { + if (!Config::patchShaders()) { + return {}; + } + + using namespace Common::FS; + const auto patch_dir = GetUserPath(PathType::ShaderDir) / "patch"; + if (!std::filesystem::exists(patch_dir)) { + std::filesystem::create_directories(patch_dir); + } + const auto filename = fmt::format("{}_{:#018x}_{}.{}", stage, hash, perm_idx, ext); + const auto filepath = patch_dir / filename; + if (!std::filesystem::exists(filepath)) { + return {}; + } + const auto file = IOFile{patch_dir / filename, FileAccessMode::Read}; + std::vector code(file.GetSize() / sizeof(u32)); + file.Read(code); + return code; +} + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index 43facbae4..662bcbd80 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h @@ -56,6 +56,8 @@ private: void DumpShader(std::span code, u64 hash, Shader::Stage stage, size_t perm_idx, std::string_view ext); + std::optional> GetShaderPatch(u64 hash, Shader::Stage stage, size_t perm_idx, + std::string_view ext); vk::ShaderModule CompileModule(Shader::Info& info, const Shader::RuntimeInfo& runtime_info, std::span code, size_t perm_idx, Shader::Backend::Bindings& binding); From 7153838c4ec8b3363fd3e72a8c58880b142eb439 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sat, 30 Nov 2024 11:43:12 -0800 Subject: [PATCH 081/549] libraries: Add initial HLE JPEG encoder skeleton (#1607) * libraries: Add initial HLE JPEG encoder skeleton * jpegenc: Finish adding parameter validation. * updated enums , added logging * jpegenc: Clean up parameter validations. * jpegenc: Fix missing log. * externals: Update ffmpeg-core --------- Co-authored-by: georgemoralis --- CMakeLists.txt | 6 + externals/ffmpeg-core | 2 +- src/common/alignment.h | 6 + src/common/logging/filter.cpp | 1 + src/common/logging/types.h | 1 + src/core/libraries/jpeg/jpeg_error.h | 9 ++ src/core/libraries/jpeg/jpegenc.cpp | 207 +++++++++++++++++++++++++++ src/core/libraries/jpeg/jpegenc.h | 84 +++++++++++ src/emulator.cpp | 3 +- 9 files changed, 317 insertions(+), 2 deletions(-) create mode 100644 src/core/libraries/jpeg/jpeg_error.h create mode 100644 src/core/libraries/jpeg/jpegenc.cpp create mode 100644 src/core/libraries/jpeg/jpegenc.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 55f1172f2..c4f639bdd 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -354,6 +354,11 @@ set(PNG_LIB src/core/libraries/libpng/pngdec.cpp src/core/libraries/libpng/pngdec.h ) +set(JPEG_LIB src/core/libraries/jpeg/jpeg_error.h + src/core/libraries/jpeg/jpegenc.cpp + src/core/libraries/jpeg/jpegenc.h +) + set(PLAYGO_LIB src/core/libraries/playgo/playgo.cpp src/core/libraries/playgo/playgo.h src/core/libraries/playgo/playgo_types.h @@ -536,6 +541,7 @@ set(CORE src/core/aerolib/stubs.cpp ${VIDEOOUT_LIB} ${NP_LIBS} ${PNG_LIB} + ${JPEG_LIB} ${PLAYGO_LIB} ${RANDOM_LIB} ${USBD_LIB} diff --git a/externals/ffmpeg-core b/externals/ffmpeg-core index e30b7d7fe..27de97c82 160000 --- a/externals/ffmpeg-core +++ b/externals/ffmpeg-core @@ -1 +1 @@ -Subproject commit e30b7d7fe228bfb3f6e41ce1040b44a15eb7d5e0 +Subproject commit 27de97c826b6b40c255891c37ac046a25836a575 diff --git a/src/common/alignment.h b/src/common/alignment.h index 8480fae26..3fb961c63 100644 --- a/src/common/alignment.h +++ b/src/common/alignment.h @@ -22,6 +22,12 @@ template return static_cast(value - value % size); } +template + requires std::is_integral_v +[[nodiscard]] constexpr bool IsAligned(T value, std::size_t alignment) { + return (value & (alignment - 1)) == 0; +} + template requires std::is_integral_v [[nodiscard]] constexpr bool Is16KBAligned(T value) { diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index fc9fa0557..5f44658f0 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -105,6 +105,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Lib, Rtc) \ SUB(Lib, DiscMap) \ SUB(Lib, Png) \ + SUB(Lib, Jpeg) \ SUB(Lib, PlayGo) \ SUB(Lib, Random) \ SUB(Lib, Usbd) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index 1422c3926..a373e8f1b 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -72,6 +72,7 @@ enum class Class : u8 { Lib_Rtc, ///< The LibSceRtc implementation. Lib_DiscMap, ///< The LibSceDiscMap implementation. Lib_Png, ///< The LibScePng implementation. + Lib_Jpeg, ///< The LibSceJpeg implementation. Lib_PlayGo, ///< The LibScePlayGo implementation. Lib_Random, ///< The libSceRandom implementation. Lib_Usbd, ///< The LibSceUsbd implementation. diff --git a/src/core/libraries/jpeg/jpeg_error.h b/src/core/libraries/jpeg/jpeg_error.h new file mode 100644 index 000000000..b9aa7483c --- /dev/null +++ b/src/core/libraries/jpeg/jpeg_error.h @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +constexpr int ORBIS_JPEG_ENC_ERROR_INVALID_ADDR = 0x80650101; +constexpr int ORBIS_JPEG_ENC_ERROR_INVALID_SIZE = 0x80650102; +constexpr int ORBIS_JPEG_ENC_ERROR_INVALID_PARAM = 0x80650103; +constexpr int ORBIS_JPEG_ENC_ERROR_INVALID_HANDLE = 0x80650104; diff --git a/src/core/libraries/jpeg/jpegenc.cpp b/src/core/libraries/jpeg/jpegenc.cpp new file mode 100644 index 000000000..b664a2334 --- /dev/null +++ b/src/core/libraries/jpeg/jpegenc.cpp @@ -0,0 +1,207 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include "common/alignment.h" +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" +#include "jpeg_error.h" +#include "jpegenc.h" + +namespace Libraries::JpegEnc { + +constexpr s32 ORBIS_JPEG_ENC_MINIMUM_MEMORY_SIZE = 0x800; +constexpr u32 ORBIS_JPEG_ENC_MAX_IMAGE_DIMENSION = 0xFFFF; +constexpr u32 ORBIS_JPEG_ENC_MAX_IMAGE_PITCH = 0xFFFFFFF; +constexpr u32 ORBIS_JPEG_ENC_MAX_IMAGE_SIZE = 0x7FFFFFFF; + +static s32 ValidateJpegEncCreateParam(const OrbisJpegEncCreateParam* param) { + if (!param) { + return ORBIS_JPEG_ENC_ERROR_INVALID_ADDR; + } + if (param->size != sizeof(OrbisJpegEncCreateParam)) { + return ORBIS_JPEG_ENC_ERROR_INVALID_SIZE; + } + if (param->attr != ORBIS_JPEG_ENC_ATTRIBUTE_NONE) { + return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM; + } + return ORBIS_OK; +} + +static s32 ValidateJpegEncMemory(const void* memory, const u32 memory_size) { + if (!memory) { + return ORBIS_JPEG_ENC_ERROR_INVALID_ADDR; + } + if (memory_size < ORBIS_JPEG_ENC_MINIMUM_MEMORY_SIZE) { + return ORBIS_JPEG_ENC_ERROR_INVALID_SIZE; + } + return ORBIS_OK; +} + +static s32 ValidateJpegEncEncodeParam(const OrbisJpegEncEncodeParam* param) { + + // Validate addresses + if (!param) { + return ORBIS_JPEG_ENC_ERROR_INVALID_ADDR; + } + if (!param->image || (param->pixel_format != ORBIS_JPEG_ENC_PIXEL_FORMAT_Y8 && + !Common::IsAligned(reinterpret_cast(param->image), 4))) { + return ORBIS_JPEG_ENC_ERROR_INVALID_ADDR; + } + if (!param->jpeg) { + return ORBIS_JPEG_ENC_ERROR_INVALID_ADDR; + } + + // Validate sizes + if (param->image_size == 0 || param->jpeg_size == 0) { + return ORBIS_JPEG_ENC_ERROR_INVALID_SIZE; + } + + // Validate parameters + if (param->image_width > ORBIS_JPEG_ENC_MAX_IMAGE_DIMENSION || + param->image_height > ORBIS_JPEG_ENC_MAX_IMAGE_DIMENSION) { + return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM; + } + if (param->image_pitch == 0 || param->image_pitch > ORBIS_JPEG_ENC_MAX_IMAGE_PITCH || + (param->pixel_format != ORBIS_JPEG_ENC_PIXEL_FORMAT_Y8 && + !Common::IsAligned(param->image_pitch, 4))) { + return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM; + } + const auto calculated_size = param->image_height * param->image_pitch; + if (calculated_size > ORBIS_JPEG_ENC_MAX_IMAGE_SIZE || calculated_size > param->image_size) { + return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM; + } + if (param->encode_mode != ORBIS_JPEG_ENC_ENCODE_MODE_NORMAL && + param->encode_mode != ORBIS_JPEG_ENC_ENCODE_MODE_MJPEG) { + return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM; + } + if (param->color_space != ORBIS_JPEG_ENC_COLOR_SPACE_YCC && + param->color_space != ORBIS_JPEG_ENC_COLOR_SPACE_GRAYSCALE) { + return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM; + } + if (param->sampling_type != ORBIS_JPEG_ENC_SAMPLING_TYPE_FULL && + param->sampling_type != ORBIS_JPEG_ENC_SAMPLING_TYPE_422 && + param->sampling_type != ORBIS_JPEG_ENC_SAMPLING_TYPE_420) { + return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM; + } + if (param->restart_interval > ORBIS_JPEG_ENC_MAX_IMAGE_DIMENSION) { + return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM; + } + switch (param->pixel_format) { + case ORBIS_JPEG_ENC_PIXEL_FORMAT_R8G8B8A8: + case ORBIS_JPEG_ENC_PIXEL_FORMAT_B8G8R8A8: + if (param->image_pitch >> 2 < param->image_width || + param->color_space != ORBIS_JPEG_ENC_COLOR_SPACE_YCC || + param->sampling_type == ORBIS_JPEG_ENC_SAMPLING_TYPE_FULL) { + return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM; + } + break; + case ORBIS_JPEG_ENC_PIXEL_FORMAT_Y8U8Y8V8: + if (param->image_pitch >> 1 < Common::AlignUp(param->image_width, 2) || + param->color_space != ORBIS_JPEG_ENC_COLOR_SPACE_YCC || + param->sampling_type == ORBIS_JPEG_ENC_SAMPLING_TYPE_FULL) { + return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM; + } + break; + case ORBIS_JPEG_ENC_PIXEL_FORMAT_Y8: + if (param->image_pitch < param->image_width || + param->color_space != ORBIS_JPEG_ENC_COLOR_SPACE_GRAYSCALE || + param->sampling_type != ORBIS_JPEG_ENC_SAMPLING_TYPE_FULL) { + return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM; + } + break; + default: + return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM; + } + + return ORBIS_OK; +} + +static s32 ValidateJpecEngHandle(OrbisJpegEncHandle handle) { + if (!handle || !Common::IsAligned(reinterpret_cast(handle), 0x20) || + handle->handle != handle) { + return ORBIS_JPEG_ENC_ERROR_INVALID_HANDLE; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceJpegEncCreate(const OrbisJpegEncCreateParam* param, void* memory, + const u32 memory_size, OrbisJpegEncHandle* handle) { + if (auto param_ret = ValidateJpegEncCreateParam(param); param_ret != ORBIS_OK) { + LOG_ERROR(Lib_Jpeg, "Invalid create param"); + return param_ret; + } + if (auto memory_ret = ValidateJpegEncMemory(memory, memory_size); memory_ret != ORBIS_OK) { + LOG_ERROR(Lib_Jpeg, "Invalid memory"); + return memory_ret; + } + if (!handle) { + LOG_ERROR(Lib_Jpeg, "Invalid handle output"); + return ORBIS_JPEG_ENC_ERROR_INVALID_ADDR; + } + + auto* handle_internal = reinterpret_cast( + Common::AlignUp(reinterpret_cast(memory), 0x20)); + handle_internal->handle = handle_internal; + handle_internal->handle_size = sizeof(OrbisJpegEncHandleInternal*); + *handle = handle_internal; + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceJpegEncDelete(OrbisJpegEncHandle handle) { + if (auto handle_ret = ValidateJpecEngHandle(handle); handle_ret != ORBIS_OK) { + LOG_ERROR(Lib_Jpeg, "Invalid handle"); + return handle_ret; + } + handle->handle = nullptr; + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceJpegEncEncode(OrbisJpegEncHandle handle, const OrbisJpegEncEncodeParam* param, + OrbisJpegEncOutputInfo* output_info) { + if (auto handle_ret = ValidateJpecEngHandle(handle); handle_ret != ORBIS_OK) { + LOG_ERROR(Lib_Jpeg, "Invalid handle"); + return handle_ret; + } + if (auto param_ret = ValidateJpegEncEncodeParam(param); param_ret != ORBIS_OK) { + LOG_ERROR(Lib_Jpeg, "Invalid encode param"); + return param_ret; + } + + LOG_ERROR(Lib_Jpeg, + "(STUBBED) image_size = {} , jpeg_size = {} , image_width = {} , image_height = {} , " + "image_pitch = {} , pixel_format = {} , encode_mode = {} , color_space = {} , " + "sampling_type = {} , compression_ratio = {} , restart_interval = {}", + param->image_size, param->jpeg_size, param->image_width, param->image_height, + param->image_pitch, magic_enum::enum_name(param->pixel_format), + magic_enum::enum_name(param->encode_mode), magic_enum::enum_name(param->color_space), + magic_enum::enum_name(param->sampling_type), param->compression_ratio, + param->restart_interval); + + if (output_info) { + output_info->size = param->jpeg_size; + output_info->height = param->image_height; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceJpegEncQueryMemorySize(const OrbisJpegEncCreateParam* param) { + if (auto param_ret = ValidateJpegEncCreateParam(param); param_ret != ORBIS_OK) { + LOG_ERROR(Lib_Jpeg, "Invalid create param"); + return param_ret; + } + return ORBIS_JPEG_ENC_MINIMUM_MEMORY_SIZE; +} + +void RegisterlibSceJpegEnc(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("K+rocojkr-I", "libSceJpegEnc", 1, "libSceJpegEnc", 1, 1, sceJpegEncCreate); + LIB_FUNCTION("j1LyMdaM+C0", "libSceJpegEnc", 1, "libSceJpegEnc", 1, 1, sceJpegEncDelete); + LIB_FUNCTION("QbrU0cUghEM", "libSceJpegEnc", 1, "libSceJpegEnc", 1, 1, sceJpegEncEncode); + LIB_FUNCTION("o6ZgXfFdWXQ", "libSceJpegEnc", 1, "libSceJpegEnc", 1, 1, + sceJpegEncQueryMemorySize); +}; + +} // namespace Libraries::JpegEnc diff --git a/src/core/libraries/jpeg/jpegenc.h b/src/core/libraries/jpeg/jpegenc.h new file mode 100644 index 000000000..a6b4d311a --- /dev/null +++ b/src/core/libraries/jpeg/jpegenc.h @@ -0,0 +1,84 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::JpegEnc { + +enum OrbisJpegEncCreateParamAttributes : u32 { ORBIS_JPEG_ENC_ATTRIBUTE_NONE = 0 }; + +enum OrbisJpegEncEncodeParamPixelFormat : u16 { + ORBIS_JPEG_ENC_PIXEL_FORMAT_R8G8B8A8 = 0, + ORBIS_JPEG_ENC_PIXEL_FORMAT_B8G8R8A8 = 1, + ORBIS_JPEG_ENC_PIXEL_FORMAT_Y8U8Y8V8 = 10, + ORBIS_JPEG_ENC_PIXEL_FORMAT_Y8 = 11 +}; + +enum OrbisJpengEncEncodeParamEncodeMode : u16 { + ORBIS_JPEG_ENC_ENCODE_MODE_NORMAL = 0, + ORBIS_JPEG_ENC_ENCODE_MODE_MJPEG = 1 +}; + +enum OrbisJpengEncEncodeParamColorSpace : u16 { + ORBIS_JPEG_ENC_COLOR_SPACE_YCC = 1, + ORBIS_JPEG_ENC_COLOR_SPACE_GRAYSCALE = 2 +}; + +enum OrbisJpengEncEncodeParamSamplingType : u8 { + ORBIS_JPEG_ENC_SAMPLING_TYPE_FULL = 0, + ORBIS_JPEG_ENC_SAMPLING_TYPE_422 = 1, + ORBIS_JPEG_ENC_SAMPLING_TYPE_420 = 2 +}; + +struct OrbisJpegEncHandleInternal { + OrbisJpegEncHandleInternal* handle; + u32 handle_size; +}; +static_assert(sizeof(OrbisJpegEncHandleInternal) == 0x10); + +typedef OrbisJpegEncHandleInternal* OrbisJpegEncHandle; + +struct OrbisJpegEncCreateParam { + u32 size; + OrbisJpegEncCreateParamAttributes attr; +}; +static_assert(sizeof(OrbisJpegEncCreateParam) == 0x8); + +struct OrbisJpegEncEncodeParam { + void* image; + void* jpeg; + u32 image_size; + u32 jpeg_size; + u32 image_width; + u32 image_height; + u32 image_pitch; + OrbisJpegEncEncodeParamPixelFormat pixel_format; + OrbisJpengEncEncodeParamEncodeMode encode_mode; + OrbisJpengEncEncodeParamColorSpace color_space; + OrbisJpengEncEncodeParamSamplingType sampling_type; + u8 compression_ratio; + s32 restart_interval; +}; +static_assert(sizeof(OrbisJpegEncEncodeParam) == 0x30); + +struct OrbisJpegEncOutputInfo { + u32 size; + u32 height; +}; +static_assert(sizeof(OrbisJpegEncOutputInfo) == 0x8); + +s32 PS4_SYSV_ABI sceJpegEncCreate(const OrbisJpegEncCreateParam* param, void* memory, + u32 memory_size, OrbisJpegEncHandle* handle); +s32 PS4_SYSV_ABI sceJpegEncDelete(OrbisJpegEncHandle handle); +s32 PS4_SYSV_ABI sceJpegEncEncode(OrbisJpegEncHandle handle, const OrbisJpegEncEncodeParam* param, + OrbisJpegEncOutputInfo* output_info); +s32 PS4_SYSV_ABI sceJpegEncQueryMemorySize(const OrbisJpegEncCreateParam* param); + +void RegisterlibSceJpegEnc(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::JpegEnc diff --git a/src/emulator.cpp b/src/emulator.cpp index 27291707d..a01eb816b 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -29,6 +29,7 @@ #include "core/file_sys/fs.h" #include "core/libraries/disc_map/disc_map.h" #include "core/libraries/fiber/fiber.h" +#include "core/libraries/jpeg/jpegenc.h" #include "core/libraries/libc_internal/libc_internal.h" #include "core/libraries/libs.h" #include "core/libraries/ngs2/ngs2.h" @@ -278,7 +279,7 @@ void Emulator::LoadSystemModules(const std::filesystem::path& file, std::string {"libSceLibcInternal.sprx", &Libraries::LibcInternal::RegisterlibSceLibcInternal}, {"libSceDiscMap.sprx", &Libraries::DiscMap::RegisterlibSceDiscMap}, {"libSceRtc.sprx", &Libraries::Rtc::RegisterlibSceRtc}, - {"libSceJpegEnc.sprx", nullptr}, + {"libSceJpegEnc.sprx", &Libraries::JpegEnc::RegisterlibSceJpegEnc}, {"libSceCesCs.sprx", nullptr}}}; std::vector found_modules; From 3d0aacd43d315e031d2efea0876627f4983cc79a Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sat, 30 Nov 2024 12:24:32 -0800 Subject: [PATCH 082/549] ci: Limit build parallelism to number of processors. (#1632) --- .github/workflows/build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 53b4c7386..878c10868 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -92,7 +92,7 @@ jobs: run: cmake --fresh -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - name: Build - run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel + run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $env:NUMBER_OF_PROCESSORS - name: Upload Windows SDL artifact uses: actions/upload-artifact@v4 @@ -146,7 +146,7 @@ jobs: run: cmake --fresh -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - name: Build - run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel + run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $env:NUMBER_OF_PROCESSORS - name: Deploy and Package run: | @@ -315,7 +315,7 @@ jobs: run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - name: Build - run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel + run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc) - name: Package and Upload Linux(ubuntu64) SDL artifact run: | @@ -371,7 +371,7 @@ jobs: run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - name: Build - run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel3 + run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc) - name: Run AppImage packaging script run: ./.github/linux-appimage-qt.sh From 5b6e0ab238fe6893dbc0777d98bc2662b5aa7f96 Mon Sep 17 00:00:00 2001 From: TheTurtle Date: Sat, 30 Nov 2024 22:37:36 +0200 Subject: [PATCH 083/549] core: Library cleanup (#1631) * core: Split error codes into separate files * Reduces build times and is cleaner * core: Bring structs and enums to codebase style * core: More style changes --- CMakeLists.txt | 50 +- src/audio_core/sdl_audio.cpp | 166 ----- src/audio_core/sdl_audio.h | 39 -- src/common/memory_patcher.cpp | 10 +- src/core/cpu_patches.h | 2 + .../devtools/widget/imgui_memory_editor.h | 2 +- src/core/devtools/widget/text_editor.cpp | 2 +- src/core/file_format/playgo_chunk.h | 21 +- .../libraries/app_content/app_content.cpp | 59 +- src/core/libraries/app_content/app_content.h | 20 +- .../libraries/app_content/app_content_error.h | 11 + src/core/libraries/audio/audioout.cpp | 84 ++- src/core/libraries/audio/audioout.h | 56 +- src/core/libraries/audio/audioout_error.h | 34 + src/core/libraries/audio/sdl_audio.cpp | 141 ++++ src/core/libraries/audio/sdl_audio.h | 42 ++ src/core/libraries/audio3d/audio3d.cpp | 58 +- src/core/libraries/audio3d/audio3d.h | 123 ++-- src/core/libraries/audio3d/audio3d_error.h | 2 + src/core/libraries/audio3d/audio3d_impl.h | 2 +- src/core/libraries/avplayer/avplayer.cpp | 2 +- src/core/libraries/avplayer/avplayer.h | 106 +-- .../libraries/avplayer/avplayer_common.cpp | 12 +- src/core/libraries/avplayer/avplayer_error.h | 19 + src/core/libraries/avplayer/avplayer_impl.cpp | 2 +- .../libraries/avplayer/avplayer_source.cpp | 19 +- .../libraries/avplayer/avplayer_state.cpp | 33 +- src/core/libraries/avplayer/avplayer_state.h | 2 +- src/core/libraries/error_codes.h | 620 ------------------ src/core/libraries/fiber/fiber.cpp | 2 +- src/core/libraries/fiber/fiber.h | 7 +- src/core/libraries/fiber/fiber_error.h | 14 + src/core/libraries/gnmdriver/gnm_error.h | 2 + src/core/libraries/gnmdriver/gnmdriver.cpp | 3 +- src/core/libraries/ime/ime.cpp | 28 +- src/core/libraries/ime/ime.h | 37 +- src/core/libraries/ime/ime_common.h | 137 ++-- src/core/libraries/ime/ime_dialog.cpp | 58 +- src/core/libraries/ime/ime_dialog.h | 84 +-- src/core/libraries/ime/ime_dialog_ui.cpp | 68 +- src/core/libraries/ime/ime_dialog_ui.h | 4 +- src/core/libraries/ime/ime_error.h | 51 ++ src/core/libraries/ime/ime_ui.cpp | 38 +- src/core/libraries/kernel/equeue.cpp | 6 +- src/core/libraries/kernel/file_system.cpp | 50 +- src/core/libraries/kernel/file_system.h | 4 +- src/core/libraries/kernel/kernel.cpp | 35 +- src/core/libraries/kernel/kernel.h | 5 +- src/core/libraries/kernel/memory.cpp | 52 +- src/core/libraries/kernel/orbis_error.h | 108 +++ src/core/libraries/kernel/posix_error.h | 128 ++++ src/core/libraries/kernel/process.cpp | 4 +- src/core/libraries/kernel/threads/condvar.cpp | 2 +- .../libraries/kernel/threads/event_flag.cpp | 2 +- src/core/libraries/kernel/threads/mutex.cpp | 3 +- src/core/libraries/kernel/threads/pthread.cpp | 4 +- .../libraries/kernel/threads/pthread_attr.cpp | 2 +- .../libraries/kernel/threads/pthread_spec.cpp | 3 +- src/core/libraries/kernel/threads/rwlock.cpp | 2 +- .../libraries/kernel/threads/semaphore.cpp | 20 +- src/core/libraries/kernel/threads/sleepq.h | 7 +- .../libraries/kernel/threads/thread_state.cpp | 2 +- src/core/libraries/kernel/time.cpp | 24 +- src/core/libraries/kernel/time.h | 2 +- src/core/libraries/libpng/pngdec.cpp | 146 ++--- src/core/libraries/libpng/pngdec.h | 73 ++- src/core/libraries/libpng/pngdec_error.h | 17 + src/core/libraries/network/netctl.h | 4 +- src/core/libraries/ngs2/ngs2.cpp | 9 +- src/core/libraries/ngs2/ngs2.h | 11 +- src/core/libraries/np_manager/np_manager.cpp | 26 +- src/core/libraries/np_manager/np_manager.h | 10 +- src/core/libraries/np_trophy/np_trophy.cpp | 6 +- src/core/libraries/np_trophy/np_trophy.h | 68 +- .../libraries/np_trophy/np_trophy_error.h | 49 ++ src/core/libraries/pad/pad.cpp | 13 +- src/core/libraries/pad/pad.h | 67 +- src/core/libraries/pad/pad_errors.h | 22 + src/core/libraries/playgo/playgo.cpp | 231 ++++--- src/core/libraries/playgo/playgo_types.h | 50 +- src/core/libraries/random/random.h | 6 +- src/core/libraries/rtc/rtc.cpp | 87 ++- src/core/libraries/rtc/rtc.h | 27 +- src/core/libraries/rtc/rtc_error.h | 4 +- src/core/libraries/system/systemservice.cpp | 47 +- src/core/libraries/system/systemservice.h | 187 +++--- .../libraries/system/systemservice_error.h | 10 + src/core/libraries/system/userservice.cpp | 8 +- src/core/libraries/system/userservice.h | 18 +- src/core/libraries/system/userservice_error.h | 17 + src/core/libraries/videodec/videodec.cpp | 9 +- src/core/libraries/videodec/videodec2.cpp | 9 +- src/core/libraries/videodec/videodec2.h | 2 +- .../libraries/videodec/videodec2_impl.cpp | 3 +- src/core/libraries/videodec/videodec_error.h | 68 ++ src/core/libraries/videodec/videodec_impl.cpp | 2 +- src/core/libraries/videoout/driver.cpp | 38 +- src/core/libraries/videoout/video_out.cpp | 15 +- src/core/libraries/videoout/video_out.h | 36 +- src/core/libraries/videoout/videoout_error.h | 35 + src/core/linker.cpp | 5 +- src/core/memory.cpp | 5 +- src/core/virtual_memory.cpp | 180 ----- src/core/virtual_memory.h | 47 -- src/emulator.cpp | 6 +- src/imgui/renderer/imgui_core.cpp | 6 +- src/input/controller.cpp | 31 +- src/input/controller.h | 5 +- src/sdl_window.cpp | 176 +++-- src/sdl_window.h | 23 +- .../renderer_vulkan/vk_instance.cpp | 2 +- .../renderer_vulkan/vk_platform.cpp | 2 +- .../renderer_vulkan/vk_presenter.cpp | 10 +- .../renderer_vulkan/vk_swapchain.cpp | 2 +- 114 files changed, 2158 insertions(+), 2509 deletions(-) delete mode 100644 src/audio_core/sdl_audio.cpp delete mode 100644 src/audio_core/sdl_audio.h create mode 100644 src/core/libraries/app_content/app_content_error.h create mode 100644 src/core/libraries/audio/audioout_error.h create mode 100644 src/core/libraries/audio/sdl_audio.cpp create mode 100644 src/core/libraries/audio/sdl_audio.h create mode 100644 src/core/libraries/avplayer/avplayer_error.h create mode 100644 src/core/libraries/fiber/fiber_error.h create mode 100644 src/core/libraries/ime/ime_error.h create mode 100644 src/core/libraries/kernel/orbis_error.h create mode 100644 src/core/libraries/kernel/posix_error.h create mode 100644 src/core/libraries/libpng/pngdec_error.h create mode 100644 src/core/libraries/np_trophy/np_trophy_error.h create mode 100644 src/core/libraries/pad/pad_errors.h create mode 100644 src/core/libraries/system/systemservice_error.h create mode 100644 src/core/libraries/system/userservice_error.h create mode 100644 src/core/libraries/videodec/videodec_error.h create mode 100644 src/core/libraries/videoout/videoout_error.h delete mode 100644 src/core/virtual_memory.cpp delete mode 100644 src/core/virtual_memory.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c4f639bdd..a8c3275f7 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -176,10 +176,6 @@ if(ENABLE_QT_GUI) qt_add_resources(TRANSLATIONS ${TRANSLATIONS_QRC}) endif() -set(AUDIO_CORE src/audio_core/sdl_audio.cpp - src/audio_core/sdl_audio.h -) - set(AJM_LIB src/core/libraries/ajm/ajm.cpp src/core/libraries/ajm/ajm.h src/core/libraries/ajm/ajm_at9.cpp @@ -199,6 +195,9 @@ set(AUDIO_LIB src/core/libraries/audio/audioin.cpp src/core/libraries/audio/audioin.h src/core/libraries/audio/audioout.cpp src/core/libraries/audio/audioout.h + src/core/libraries/audio/sdl_audio.cpp + src/core/libraries/audio/sdl_audio.h + src/core/libraries/audio/audioout_error.h src/core/libraries/ngs2/ngs2.cpp src/core/libraries/ngs2/ngs2.h ) @@ -240,6 +239,8 @@ set(KERNEL_LIB src/core/libraries/kernel/threads/condvar.cpp src/core/libraries/kernel/threads.h src/core/libraries/kernel/time.cpp src/core/libraries/kernel/time.h + src/core/libraries/kernel/orbis_error.h + src/core/libraries/kernel/posix_error.h ) set(NETWORK_LIBS src/core/libraries/network/http.cpp @@ -255,6 +256,21 @@ set(NETWORK_LIBS src/core/libraries/network/http.cpp src/core/libraries/network/ssl.h ) +set(AVPLAYER_LIB src/core/libraries/avplayer/avplayer_common.cpp + src/core/libraries/avplayer/avplayer_common.h + src/core/libraries/avplayer/avplayer_file_streamer.cpp + src/core/libraries/avplayer/avplayer_file_streamer.h + src/core/libraries/avplayer/avplayer_impl.cpp + src/core/libraries/avplayer/avplayer_impl.h + src/core/libraries/avplayer/avplayer_source.cpp + src/core/libraries/avplayer/avplayer_source.h + src/core/libraries/avplayer/avplayer_state.cpp + src/core/libraries/avplayer/avplayer_state.h + src/core/libraries/avplayer/avplayer.cpp + src/core/libraries/avplayer/avplayer.h + src/core/libraries/avplayer/avplayer_error.h +) + set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp src/core/libraries/system/commondialog.h src/core/libraries/system/msgdialog.cpp @@ -279,28 +295,19 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp src/core/libraries/system/system_error.h src/core/libraries/system/systemservice.cpp src/core/libraries/system/systemservice.h + src/core/libraries/system/systemservice_error.h src/core/libraries/system/userservice.cpp src/core/libraries/system/userservice.h + src/core/libraries/system/userservice_error.h src/core/libraries/app_content/app_content.cpp src/core/libraries/app_content/app_content.h + src/core/libraries/app_content/app_content_error.h src/core/libraries/rtc/rtc.cpp src/core/libraries/rtc/rtc.h src/core/libraries/rtc/rtc_error.h src/core/libraries/disc_map/disc_map.cpp src/core/libraries/disc_map/disc_map.h src/core/libraries/disc_map/disc_map_codes.h - src/core/libraries/avplayer/avplayer_common.cpp - src/core/libraries/avplayer/avplayer_common.h - src/core/libraries/avplayer/avplayer_file_streamer.cpp - src/core/libraries/avplayer/avplayer_file_streamer.h - src/core/libraries/avplayer/avplayer_impl.cpp - src/core/libraries/avplayer/avplayer_impl.h - src/core/libraries/avplayer/avplayer_source.cpp - src/core/libraries/avplayer/avplayer_source.h - src/core/libraries/avplayer/avplayer_state.cpp - src/core/libraries/avplayer/avplayer_state.h - src/core/libraries/avplayer/avplayer.cpp - src/core/libraries/avplayer/avplayer.h src/core/libraries/ngs2/ngs2.cpp src/core/libraries/ngs2/ngs2.h src/core/libraries/ngs2/ngs2_error.h @@ -327,6 +334,7 @@ set(VIDEOOUT_LIB src/core/libraries/videoout/buffer.h src/core/libraries/videoout/driver.h src/core/libraries/videoout/video_out.cpp src/core/libraries/videoout/video_out.h + src/core/libraries/videoout/videoout_error.h ) set(LIBC_SOURCES src/core/libraries/libc_internal/libc_internal.cpp @@ -344,14 +352,17 @@ set(IME_LIB src/core/libraries/ime/error_dialog.cpp src/core/libraries/ime/ime_ui.h src/core/libraries/ime/ime.cpp src/core/libraries/ime/ime.h + src/core/libraries/ime/ime_error.h ) set(PAD_LIB src/core/libraries/pad/pad.cpp src/core/libraries/pad/pad.h + src/core/libraries/pad/pad_errors.h ) set(PNG_LIB src/core/libraries/libpng/pngdec.cpp src/core/libraries/libpng/pngdec.h + src/core/libraries/libpng/pngdec_error.h ) set(JPEG_LIB src/core/libraries/jpeg/jpeg_error.h @@ -375,6 +386,7 @@ set(USBD_LIB src/core/libraries/usbd/usbd.cpp set(FIBER_LIB src/core/libraries/fiber/fiber.cpp src/core/libraries/fiber/fiber.h + src/core/libraries/fiber/fiber_error.h ) set(VDEC_LIB src/core/libraries/videodec/videodec2_impl.cpp @@ -384,6 +396,7 @@ set(VDEC_LIB src/core/libraries/videodec/videodec2_impl.cpp src/core/libraries/videodec/videodec2_avc.h src/core/libraries/videodec/videodec.cpp src/core/libraries/videodec/videodec.h + src/core/libraries/videodec/videodec_error.h src/core/libraries/videodec/videodec_impl.cpp src/core/libraries/videodec/videodec_impl.h ) @@ -396,6 +409,7 @@ set(NP_LIBS src/core/libraries/np_manager/np_manager.cpp src/core/libraries/np_trophy/np_trophy.h src/core/libraries/np_trophy/trophy_ui.cpp src/core/libraries/np_trophy/trophy_ui.h + src/core/libraries/np_trophy/np_trophy_error.h ) set(MISC_LIBS src/core/libraries/screenshot/screenshot.cpp @@ -527,10 +541,10 @@ set(CORE src/core/aerolib/stubs.cpp src/core/loader/elf.h src/core/loader/symbols_resolver.h src/core/loader/symbols_resolver.cpp - src/core/libraries/error_codes.h src/core/libraries/libs.h src/core/libraries/libs.cpp ${AJM_LIB} + ${AVPLAYER_LIB} ${AUDIO_LIB} ${GNM_LIB} ${KERNEL_LIB} @@ -565,8 +579,6 @@ set(CORE src/core/aerolib/stubs.cpp src/core/thread.h src/core/tls.cpp src/core/tls.h - src/core/virtual_memory.cpp - src/core/virtual_memory.h ) if (ARCHITECTURE STREQUAL "x86_64") diff --git a/src/audio_core/sdl_audio.cpp b/src/audio_core/sdl_audio.cpp deleted file mode 100644 index 7fed42a44..000000000 --- a/src/audio_core/sdl_audio.cpp +++ /dev/null @@ -1,166 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "sdl_audio.h" - -#include "common/assert.h" -#include "core/libraries/error_codes.h" - -#include -#include -#include - -#include // std::unique_lock - -namespace Audio { - -constexpr int AUDIO_STREAM_BUFFER_THRESHOLD = 65536; // Define constant for buffer threshold - -s32 SDLAudio::AudioOutOpen(int type, u32 samples_num, u32 freq, - Libraries::AudioOut::OrbisAudioOutParamFormat format) { - using Libraries::AudioOut::OrbisAudioOutParamFormat; - std::unique_lock lock{m_mutex}; - for (int id = 0; id < portsOut.size(); id++) { - auto& port = portsOut[id]; - if (!port.isOpen) { - port.isOpen = true; - port.type = type; - port.samples_num = samples_num; - port.freq = freq; - port.format = format; - SDL_AudioFormat sampleFormat; - switch (format) { - case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_MONO: - sampleFormat = SDL_AUDIO_S16; - port.channels_num = 1; - port.sample_size = 2; - break; - case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_MONO: - sampleFormat = SDL_AUDIO_F32; - port.channels_num = 1; - port.sample_size = 4; - break; - case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_STEREO: - sampleFormat = SDL_AUDIO_S16; - port.channels_num = 2; - port.sample_size = 2; - break; - case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_STEREO: - sampleFormat = SDL_AUDIO_F32; - port.channels_num = 2; - port.sample_size = 4; - break; - case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_8CH: - sampleFormat = SDL_AUDIO_S16; - port.channels_num = 8; - port.sample_size = 2; - break; - case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_8CH: - sampleFormat = SDL_AUDIO_F32; - port.channels_num = 8; - port.sample_size = 4; - break; - case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_8CH_STD: - sampleFormat = SDL_AUDIO_S16; - port.channels_num = 8; - port.sample_size = 2; - break; - case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_8CH_STD: - sampleFormat = SDL_AUDIO_F32; - port.channels_num = 8; - port.sample_size = 4; - break; - default: - UNREACHABLE_MSG("Unknown format"); - } - - for (int i = 0; i < port.channels_num; i++) { - port.volume[i] = Libraries::AudioOut::SCE_AUDIO_OUT_VOLUME_0DB; - } - - SDL_AudioSpec fmt; - SDL_zero(fmt); - fmt.format = sampleFormat; - fmt.channels = port.channels_num; - fmt.freq = freq; // Set frequency from the argument - port.stream = - SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &fmt, NULL, NULL); - SDL_ResumeAudioDevice(SDL_GetAudioStreamDevice(port.stream)); - return id + 1; - } - } - - LOG_ERROR(Lib_AudioOut, "Audio ports are full"); - return ORBIS_AUDIO_OUT_ERROR_PORT_FULL; // all ports are used -} - -s32 SDLAudio::AudioOutOutput(s32 handle, const void* ptr) { - std::shared_lock lock{m_mutex}; - auto& port = portsOut[handle - 1]; - if (!port.isOpen) { - return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; - } - - const size_t data_size = port.samples_num * port.sample_size * port.channels_num; - - bool result = SDL_PutAudioStreamData(port.stream, ptr, data_size); - - lock.unlock(); // Unlock only after necessary operations - - while (SDL_GetAudioStreamAvailable(port.stream) > AUDIO_STREAM_BUFFER_THRESHOLD) { - SDL_Delay(0); - } - - return result ? ORBIS_OK : -1; -} - -s32 SDLAudio::AudioOutSetVolume(s32 handle, s32 bitflag, s32* volume) { - using Libraries::AudioOut::OrbisAudioOutParamFormat; - std::shared_lock lock{m_mutex}; - auto& port = portsOut[handle - 1]; - if (!port.isOpen) { - return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; - } - - for (int i = 0; i < port.channels_num; i++, bitflag >>= 1u) { - auto bit = bitflag & 0x1u; - - if (bit == 1) { - int src_index = i; - if (port.format == - OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_8CH_STD || - port.format == OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_8CH_STD) { - switch (i) { - case 4: - src_index = 6; - break; - case 5: - src_index = 7; - break; - case 6: - src_index = 4; - break; - case 7: - src_index = 5; - break; - default: - break; - } - } - port.volume[i] = volume[src_index]; - } - } - - return ORBIS_OK; -} - -s32 SDLAudio::AudioOutGetStatus(s32 handle, int* type, int* channels_num) { - std::shared_lock lock{m_mutex}; - auto& port = portsOut[handle - 1]; - *type = port.type; - *channels_num = port.channels_num; - - return ORBIS_OK; -} - -} // namespace Audio diff --git a/src/audio_core/sdl_audio.h b/src/audio_core/sdl_audio.h deleted file mode 100644 index 0d4783f19..000000000 --- a/src/audio_core/sdl_audio.h +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include "core/libraries/audio/audioout.h" - -namespace Audio { - -class SDLAudio { -public: - SDLAudio() = default; - virtual ~SDLAudio() = default; - - s32 AudioOutOpen(int type, u32 samples_num, u32 freq, - Libraries::AudioOut::OrbisAudioOutParamFormat format); - s32 AudioOutOutput(s32 handle, const void* ptr); - s32 AudioOutSetVolume(s32 handle, s32 bitflag, s32* volume); - s32 AudioOutGetStatus(s32 handle, int* type, int* channels_num); - -private: - struct PortOut { - SDL_AudioStream* stream = nullptr; - u32 samples_num = 0; - u32 freq = 0; - u32 format = -1; - int type = 0; - int channels_num = 0; - int volume[8] = {}; - u8 sample_size = 0; - bool isOpen = false; - }; - std::shared_mutex m_mutex; - std::array portsOut; -}; - -} // namespace Audio diff --git a/src/common/memory_patcher.cpp b/src/common/memory_patcher.cpp index d2930cf5e..6b79edb9f 100644 --- a/src/common/memory_patcher.cpp +++ b/src/common/memory_patcher.cpp @@ -28,7 +28,7 @@ std::string g_game_serial; std::string patchFile; std::vector pending_patches; -std::string toHex(unsigned long long value, size_t byteSize) { +std::string toHex(u64 value, size_t byteSize) { std::stringstream ss; ss << std::hex << std::setfill('0') << std::setw(byteSize * 2) << value; return ss.str(); @@ -38,16 +38,16 @@ std::string convertValueToHex(const std::string type, const std::string valueStr std::string result; if (type == "byte") { - unsigned int value = std::stoul(valueStr, nullptr, 16); + const u32 value = std::stoul(valueStr, nullptr, 16); result = toHex(value, 1); } else if (type == "bytes16") { - unsigned int value = std::stoul(valueStr, nullptr, 16); + const u32 value = std::stoul(valueStr, nullptr, 16); result = toHex(value, 2); } else if (type == "bytes32") { - unsigned long value = std::stoul(valueStr, nullptr, 16); + const u32 value = std::stoul(valueStr, nullptr, 16); result = toHex(value, 4); } else if (type == "bytes64") { - unsigned long long value = std::stoull(valueStr, nullptr, 16); + const u64 value = std::stoull(valueStr, nullptr, 16); result = toHex(value, 8); } else if (type == "float32") { union { diff --git a/src/core/cpu_patches.h b/src/core/cpu_patches.h index f9f7fe646..1ccac073a 100644 --- a/src/core/cpu_patches.h +++ b/src/core/cpu_patches.h @@ -3,6 +3,8 @@ #pragma once +#include "common/types.h" + namespace Core { /// Initializes a stack for the current thread for use by patch implementations. diff --git a/src/core/devtools/widget/imgui_memory_editor.h b/src/core/devtools/widget/imgui_memory_editor.h index f90387a77..086cfb1d2 100644 --- a/src/core/devtools/widget/imgui_memory_editor.h +++ b/src/core/devtools/widget/imgui_memory_editor.h @@ -458,7 +458,7 @@ struct MemoryEditor { data_write = data_next = true; if (data_editing_addr_next != (size_t)-1) data_write = data_next = false; - unsigned int data_input_value = 0; + u32 data_input_value = 0; if (!ReadOnly && data_write && sscanf(DataInputBuf, "%X", &data_input_value) == 1) { if (WriteFn) diff --git a/src/core/devtools/widget/text_editor.cpp b/src/core/devtools/widget/text_editor.cpp index f447e45a2..07f2f658d 100644 --- a/src/core/devtools/widget/text_editor.cpp +++ b/src/core/devtools/widget/text_editor.cpp @@ -131,7 +131,7 @@ static int UTF8CharLength(TextEditor::Char c) { } // "Borrowed" from ImGui source -static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c) { +static inline int ImTextCharToUtf8(char* buf, int buf_size, u32 c) { if (c < 0x80) { buf[0] = (char)c; return 1; diff --git a/src/core/file_format/playgo_chunk.h b/src/core/file_format/playgo_chunk.h index b6d38e0e1..2a234f5fe 100644 --- a/src/core/file_format/playgo_chunk.h +++ b/src/core/file_format/playgo_chunk.h @@ -97,24 +97,23 @@ struct PlaygoChunk { class PlaygoFile { public: - bool initialized; - OrbisPlayGoHandle handle; - OrbisPlayGoChunkId id; - OrbisPlayGoLocus locus; - OrbisPlayGoInstallSpeed speed; - s64 speed_tick; - OrbisPlayGoEta eta; - OrbisPlayGoLanguageMask langMask; + bool initialized = false; + OrbisPlayGoHandle handle = 0; + OrbisPlayGoChunkId id = 0; + OrbisPlayGoLocus locus = OrbisPlayGoLocus::NotDownloaded; + OrbisPlayGoInstallSpeed speed = OrbisPlayGoInstallSpeed::Trickle; + s64 speed_tick = 0; + OrbisPlayGoEta eta = 0; + OrbisPlayGoLanguageMask langMask = 0; std::vector chunks; public: - PlaygoFile() - : initialized(false), handle(0), id(0), locus(0), speed(ORBIS_PLAYGO_INSTALL_SPEED_TRICKLE), - speed_tick(0), eta(0), langMask(0), playgoHeader{0} {} + explicit PlaygoFile() = default; ~PlaygoFile() = default; bool Open(const std::filesystem::path& filepath); bool LoadChunks(const Common::FS::IOFile& file); + PlaygoHeader& GetPlaygoHeader() { return playgoHeader; } diff --git a/src/core/libraries/app_content/app_content.cpp b/src/core/libraries/app_content/app_content.cpp index f912639eb..ca3cdad39 100644 --- a/src/core/libraries/app_content/app_content.cpp +++ b/src/core/libraries/app_content/app_content.cpp @@ -6,34 +6,30 @@ #include "app_content.h" #include "common/assert.h" #include "common/config.h" -#include "common/io_file.h" #include "common/logging/log.h" -#include "common/path_util.h" #include "common/singleton.h" -#include "common/string_util.h" #include "core/file_format/psf.h" #include "core/file_sys/fs.h" -#include "core/libraries/error_codes.h" +#include "core/libraries/app_content/app_content_error.h" #include "core/libraries/libs.h" namespace Libraries::AppContent { -int32_t addcont_count = 0; - struct AddContInfo { - char entitlementLabel[ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE]; + char entitlement_label[ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE]; OrbisAppContentAddcontDownloadStatus status; OrbisAppContentGetEntitlementKey key; }; -std::array addcont_info = {{ +static std::array addcont_info = {{ {"0000000000000000", - ORBIS_APP_CONTENT_ADDCONT_DOWNLOAD_STATUS_INSTALLED, + OrbisAppContentAddcontDownloadStatus::Installed, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, }}; -std::string title_id; +static s32 addcont_count = 0; +static std::string title_id; int PS4_SYSV_ABI _Z5dummyv() { LOG_ERROR(Lib_AppContent, "(STUBBED) called"); @@ -64,12 +60,11 @@ int PS4_SYSV_ABI sceAppContentAddcontMount(u32 service_label, auto* mnt = Common::Singleton::Instance(); for (int i = 0; i < addcont_count; i++) { - if (strncmp(entitlement_label->data, addcont_info[i].entitlementLabel, + if (strncmp(entitlement_label->data, addcont_info[i].entitlement_label, ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE - 1) != 0) { continue; } - - if (addcont_info[i].status != ORBIS_APP_CONTENT_ADDCONT_DOWNLOAD_STATUS_INSTALLED) { + if (addcont_info[i].status != OrbisAppContentAddcontDownloadStatus::Installed) { return ORBIS_APP_CONTENT_ERROR_NOT_FOUND; } @@ -170,14 +165,14 @@ int PS4_SYSV_ABI sceAppContentGetAddcontInfo(u32 service_label, } for (auto i = 0; i < addcont_count; i++) { - if (strncmp(entitlementLabel->data, addcont_info[i].entitlementLabel, + if (strncmp(entitlementLabel->data, addcont_info[i].entitlement_label, ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE - 1) != 0) { continue; } LOG_INFO(Lib_AppContent, "found DLC {}", entitlementLabel->data); - strncpy(info->entitlement_label.data, addcont_info[i].entitlementLabel, + strncpy(info->entitlement_label.data, addcont_info[i].entitlement_label, ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE); info->status = addcont_info[i].status; return ORBIS_OK; @@ -202,7 +197,7 @@ int PS4_SYSV_ABI sceAppContentGetAddcontInfoList(u32 service_label, int dlcs_to_list = addcont_count < list_num ? addcont_count : list_num; for (int i = 0; i < dlcs_to_list; i++) { - strncpy(list[i].entitlement_label.data, addcont_info[i].entitlementLabel, + strncpy(list[i].entitlement_label.data, addcont_info[i].entitlement_label, ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE); list[i].status = addcont_info[i].status; } @@ -224,7 +219,7 @@ int PS4_SYSV_ABI sceAppContentGetEntitlementKey( } for (int i = 0; i < addcont_count; i++) { - if (strncmp(entitlement_label->data, addcont_info[i].entitlementLabel, + if (strncmp(entitlement_label->data, addcont_info[i].entitlement_label, ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE - 1) != 0) { continue; } @@ -252,21 +247,19 @@ int PS4_SYSV_ABI sceAppContentInitialize(const OrbisAppContentInitParam* initPar } else { UNREACHABLE_MSG("Failed to get TITLE_ID"); } - auto addon_path = addons_dir / title_id; - if (std::filesystem::exists(addon_path)) { - for (const auto& entry : std::filesystem::directory_iterator(addon_path)) { - if (entry.is_directory()) { - auto entitlement_label = entry.path().filename().string(); - - AddContInfo info{}; - info.status = ORBIS_APP_CONTENT_ADDCONT_DOWNLOAD_STATUS_INSTALLED; - strcpy(info.entitlementLabel, entitlement_label.c_str()); - - addcont_info[addcont_count++] = info; - } - } + const auto addon_path = addons_dir / title_id; + if (!std::filesystem::exists(addon_path)) { + return ORBIS_OK; } + for (const auto& entry : std::filesystem::directory_iterator(addon_path)) { + if (entry.is_directory()) { + auto entitlement_label = entry.path().filename().string(); + auto& info = addcont_info[addcont_count++]; + info.status = OrbisAppContentAddcontDownloadStatus::Installed; + entitlement_label.copy(info.entitlement_label, sizeof(info.entitlement_label)); + } + } return ORBIS_OK; } @@ -314,9 +307,11 @@ int PS4_SYSV_ABI sceAppContentTemporaryDataMount() { int PS4_SYSV_ABI sceAppContentTemporaryDataMount2(OrbisAppContentTemporaryDataOption option, OrbisAppContentMountPoint* mountPoint) { - if (mountPoint == nullptr) + if (mountPoint == nullptr) { return ORBIS_APP_CONTENT_ERROR_PARAMETER; - strncpy(mountPoint->data, "/temp0", 16); + } + static constexpr std::string_view TmpMount = "/temp0"; + TmpMount.copy(mountPoint->data, sizeof(mountPoint->data)); LOG_INFO(Lib_AppContent, "sceAppContentTemporaryDataMount2: option = {}, mountPoint = {}", option, mountPoint->data); return ORBIS_OK; diff --git a/src/core/libraries/app_content/app_content.h b/src/core/libraries/app_content/app_content.h index a16da5b40..f41f7dccf 100644 --- a/src/core/libraries/app_content/app_content.h +++ b/src/core/libraries/app_content/app_content.h @@ -30,7 +30,7 @@ struct OrbisAppContentBootParam { char reserved2[32]; }; -typedef u32 OrbisAppContentTemporaryDataOption; +using OrbisAppContentTemporaryDataOption = u32; constexpr int ORBIS_APP_CONTENT_MOUNTPOINT_DATA_MAXSIZE = 16; @@ -44,12 +44,12 @@ constexpr int ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE = 17; constexpr int ORBIS_APP_CONTENT_ENTITLEMENT_KEY_SIZE = 16; constexpr int ORBIS_APP_CONTENT_INFO_LIST_MAX_SIZE = 2500; -enum OrbisAppContentAddcontDownloadStatus : u32 { - ORBIS_APP_CONTENT_ADDCONT_DOWNLOAD_STATUS_NO_EXTRA_DATA = 0, - ORBIS_APP_CONTENT_ADDCONT_DOWNLOAD_STATUS_NO_IN_QUEUE = 1, - ORBIS_APP_CONTENT_ADDCONT_DOWNLOAD_STATUS_DOWNLOADING = 2, - ORBIS_APP_CONTENT_ADDCONT_DOWNLOAD_STATUS_DOWNLOAD_SUSPENDED = 3, - ORBIS_APP_CONTENT_ADDCONT_DOWNLOAD_STATUS_INSTALLED = 4 +enum class OrbisAppContentAddcontDownloadStatus : u32 { + NoExtraData = 0, + NoInQueue = 1, + Downloading = 2, + DownloadSuspended = 3, + Installed = 4 }; struct OrbisNpUnifiedEntitlementLabel { @@ -57,11 +57,11 @@ struct OrbisNpUnifiedEntitlementLabel { char padding[3]; }; -typedef u32 OrbisAppContentAppParamId; +using OrbisAppContentAppParamId = u32; struct OrbisAppContentAddcontInfo { OrbisNpUnifiedEntitlementLabel entitlement_label; - u32 status; + OrbisAppContentAddcontDownloadStatus status; }; struct OrbisAppContentGetEntitlementKey { @@ -119,4 +119,4 @@ int PS4_SYSV_ABI sceAppContentGetAddcontInfoListByIroTag(); int PS4_SYSV_ABI sceAppContentGetDownloadedStoreCountry(); void RegisterlibSceAppContent(Core::Loader::SymbolsResolver* sym); -} // namespace Libraries::AppContent \ No newline at end of file +} // namespace Libraries::AppContent diff --git a/src/core/libraries/app_content/app_content_error.h b/src/core/libraries/app_content/app_content_error.h new file mode 100644 index 000000000..3a582b998 --- /dev/null +++ b/src/core/libraries/app_content/app_content_error.h @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +// AppContent library +constexpr int ORBIS_APP_CONTENT_ERROR_PARAMETER = 0x80D90002; +constexpr int ORBIS_APP_CONTENT_ERROR_DRM_NO_ENTITLEMENT = 0x80D90007; +constexpr int ORBIS_APP_CONTENT_ERROR_NOT_FOUND = 0x80D90005; diff --git a/src/core/libraries/audio/audioout.cpp b/src/core/libraries/audio/audioout.cpp index 778d777c2..b92c75a8f 100644 --- a/src/core/libraries/audio/audioout.cpp +++ b/src/core/libraries/audio/audioout.cpp @@ -4,30 +4,30 @@ #include #include -#include "audio_core/sdl_audio.h" #include "common/assert.h" #include "common/logging/log.h" #include "core/libraries/audio/audioout.h" -#include "core/libraries/error_codes.h" +#include "core/libraries/audio/audioout_error.h" +#include "core/libraries/audio/sdl_audio.h" #include "core/libraries/libs.h" namespace Libraries::AudioOut { -static std::unique_ptr audio; +static std::unique_ptr audio; -static std::string_view GetAudioOutPort(u32 port) { +static std::string_view GetAudioOutPort(OrbisAudioOutPort port) { switch (port) { - case ORBIS_AUDIO_OUT_PORT_TYPE_MAIN: + case OrbisAudioOutPort::Main: return "MAIN"; - case ORBIS_AUDIO_OUT_PORT_TYPE_BGM: + case OrbisAudioOutPort::Bgm: return "BGM"; - case ORBIS_AUDIO_OUT_PORT_TYPE_VOICE: + case OrbisAudioOutPort::Voice: return "VOICE"; - case ORBIS_AUDIO_OUT_PORT_TYPE_PERSONAL: + case OrbisAudioOutPort::Personal: return "PERSONAL"; - case ORBIS_AUDIO_OUT_PORT_TYPE_PADSPK: + case OrbisAudioOutPort::Padspk: return "PADSPK"; - case ORBIS_AUDIO_OUT_PORT_TYPE_AUX: + case OrbisAudioOutPort::Aux: return "AUX"; default: return "INVALID"; @@ -36,21 +36,21 @@ static std::string_view GetAudioOutPort(u32 port) { static std::string_view GetAudioOutParamFormat(OrbisAudioOutParamFormat param) { switch (param) { - case ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_MONO: + case OrbisAudioOutParamFormat::S16Mono: return "S16_MONO"; - case ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_STEREO: + case OrbisAudioOutParamFormat::S16Stereo: return "S16_STEREO"; - case ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_8CH: + case OrbisAudioOutParamFormat::S16_8CH: return "S16_8CH"; - case ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_MONO: + case OrbisAudioOutParamFormat::FloatMono: return "FLOAT_MONO"; - case ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_STEREO: + case OrbisAudioOutParamFormat::FloatStereo: return "FLOAT_STEREO"; - case ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_8CH: + case OrbisAudioOutParamFormat::Float_8CH: return "FLOAT_8CH"; - case ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_8CH_STD: + case OrbisAudioOutParamFormat::S16_8CH_Std: return "S16_8CH_STD"; - case ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_8CH_STD: + case OrbisAudioOutParamFormat::Float_8CH_Std: return "FLOAT_8CH_STD"; default: return "INVALID"; @@ -59,11 +59,11 @@ static std::string_view GetAudioOutParamFormat(OrbisAudioOutParamFormat param) { static std::string_view GetAudioOutParamAttr(OrbisAudioOutParamAttr attr) { switch (attr) { - case ORBIS_AUDIO_OUT_PARAM_ATTR_NONE: + case OrbisAudioOutParamAttr::None: return "NONE"; - case ORBIS_AUDIO_OUT_PARAM_ATTR_RESTRICTED: + case OrbisAudioOutParamAttr::Restricted: return "RESTRICTED"; - case ORBIS_AUDIO_OUT_PARAM_ATTR_MIX_TO_MAIN: + case OrbisAudioOutParamAttr::MixToMain: return "MIX_TO_MAIN"; default: return "INVALID"; @@ -180,29 +180,23 @@ int PS4_SYSV_ABI sceAudioOutGetPortState(s32 handle, OrbisAudioOutPortState* sta return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; } - int type = 0; - int channels_num = 0; - - if (const auto err = audio->AudioOutGetStatus(handle, &type, &channels_num); err != ORBIS_OK) { - return err; - } - + const auto [type, channels_num] = audio->GetStatus(handle); state->rerouteCounter = 0; - state->volume = 127; // max volume + state->volume = 127; switch (type) { - case ORBIS_AUDIO_OUT_PORT_TYPE_MAIN: - case ORBIS_AUDIO_OUT_PORT_TYPE_BGM: - case ORBIS_AUDIO_OUT_PORT_TYPE_VOICE: + case OrbisAudioOutPort::Main: + case OrbisAudioOutPort::Bgm: + case OrbisAudioOutPort::Voice: state->output = 1; state->channel = (channels_num > 2 ? 2 : channels_num); break; - case ORBIS_AUDIO_OUT_PORT_TYPE_PERSONAL: - case ORBIS_AUDIO_OUT_PORT_TYPE_PADSPK: + case OrbisAudioOutPort::Personal: + case OrbisAudioOutPort::Padspk: state->output = 4; state->channel = 1; break; - case ORBIS_AUDIO_OUT_PORT_TYPE_AUX: + case OrbisAudioOutPort::Aux: state->output = 0; state->channel = 0; break; @@ -243,7 +237,7 @@ int PS4_SYSV_ABI sceAudioOutInit() { if (audio != nullptr) { return ORBIS_AUDIO_OUT_ERROR_ALREADY_INIT; } - audio = std::make_unique(); + audio = std::make_unique(); return ORBIS_OK; } @@ -287,7 +281,8 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id, user_id, GetAudioOutPort(port_type), index, length, sample_rate, GetAudioOutParamFormat(param_type.data_format), GetAudioOutParamAttr(param_type.attributes)); - if ((port_type < 0 || port_type > 4) && (port_type != 127)) { + if ((port_type < OrbisAudioOutPort::Main || port_type > OrbisAudioOutPort::Padspk) && + (port_type != OrbisAudioOutPort::Aux)) { LOG_ERROR(Lib_AudioOut, "Invalid port type"); return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT_TYPE; } @@ -303,18 +298,19 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id, if (index != 0) { LOG_ERROR(Lib_AudioOut, "index is not valid !=0 {}", index); } - OrbisAudioOutParamFormat format = param_type.data_format; - if (format < 0 || format > 7) { + const auto format = param_type.data_format.Value(); + if (format < OrbisAudioOutParamFormat::S16Mono || + format > OrbisAudioOutParamFormat::Float_8CH_Std) { LOG_ERROR(Lib_AudioOut, "Invalid format"); return ORBIS_AUDIO_OUT_ERROR_INVALID_FORMAT; } - OrbisAudioOutParamAttr attr = param_type.attributes; - if (attr < 0 || attr > 2) { + const auto attr = param_type.attributes; + if (attr < OrbisAudioOutParamAttr::None || attr > OrbisAudioOutParamAttr::MixToMain) { // TODO Handle attributes in output audio device LOG_ERROR(Lib_AudioOut, "Invalid format attribute"); return ORBIS_AUDIO_OUT_ERROR_INVALID_FORMAT; } - return audio->AudioOutOpen(port_type, length, sample_rate, format); + return audio->Open(port_type, length, sample_rate, format); } int PS4_SYSV_ABI sceAudioOutOpenEx() { @@ -330,7 +326,7 @@ s32 PS4_SYSV_ABI sceAudioOutOutput(s32 handle, const void* ptr) { // Nothing to output return ORBIS_OK; } - return audio->AudioOutOutput(handle, ptr); + return audio->Output(handle, ptr); } int PS4_SYSV_ABI sceAudioOutOutputs(OrbisAudioOutOutputParam* param, u32 num) { @@ -435,7 +431,7 @@ s32 PS4_SYSV_ABI sceAudioOutSetVolume(s32 handle, s32 flag, s32* vol) { if (handle < 1 || handle > SCE_AUDIO_OUT_NUM_PORTS) { return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; } - return audio->AudioOutSetVolume(handle, flag, vol); + return audio->SetVolume(handle, flag, vol); } int PS4_SYSV_ABI sceAudioOutSetVolumeDown() { diff --git a/src/core/libraries/audio/audioout.h b/src/core/libraries/audio/audioout.h index 95cfc1707..e8e718b87 100644 --- a/src/core/libraries/audio/audioout.h +++ b/src/core/libraries/audio/audioout.h @@ -9,46 +9,36 @@ namespace Libraries::AudioOut { -constexpr int SCE_AUDIO_OUT_VOLUME_0DB = 32768; // max volume value - -// main up to 8 ports, BGM 1 port, voice up to 4 ports, +// Main up to 8 ports, BGM 1 port, voice up to 4 ports, // personal up to 4 ports, padspk up to 5 ports, aux 1 port constexpr int SCE_AUDIO_OUT_NUM_PORTS = 22; +constexpr int SCE_AUDIO_OUT_VOLUME_0DB = 32768; // max volume value -enum OrbisAudioOutPort { - ORBIS_AUDIO_OUT_PORT_TYPE_MAIN = 0, - ORBIS_AUDIO_OUT_PORT_TYPE_BGM = 1, - ORBIS_AUDIO_OUT_PORT_TYPE_VOICE = 2, - ORBIS_AUDIO_OUT_PORT_TYPE_PERSONAL = 3, - ORBIS_AUDIO_OUT_PORT_TYPE_PADSPK = 4, - ORBIS_AUDIO_OUT_PORT_TYPE_AUX = 127 +enum class OrbisAudioOutPort { Main = 0, Bgm = 1, Voice = 2, Personal = 3, Padspk = 4, Aux = 127 }; + +enum class OrbisAudioOutParamFormat { + S16Mono = 0, + S16Stereo = 1, + S16_8CH = 2, + FloatMono = 3, + FloatStereo = 4, + Float_8CH = 5, + S16_8CH_Std = 6, + Float_8CH_Std = 7 }; -enum OrbisAudioOutParamFormat { - ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_MONO = 0, - ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_STEREO = 1, - ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_8CH = 2, - ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_MONO = 3, - ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_STEREO = 4, - ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_8CH = 5, - ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_8CH_STD = 6, - ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_8CH_STD = 7 +enum class OrbisAudioOutParamAttr { + None = 0, + Restricted = 1, + MixToMain = 2, }; -enum OrbisAudioOutParamAttr { - ORBIS_AUDIO_OUT_PARAM_ATTR_NONE = 0, - ORBIS_AUDIO_OUT_PARAM_ATTR_RESTRICTED = 1, - ORBIS_AUDIO_OUT_PARAM_ATTR_MIX_TO_MAIN = 2, -}; - -struct OrbisAudioOutParamExtendedInformation { - union { - BitField<0, 8, OrbisAudioOutParamFormat> data_format; - BitField<8, 8, u32> reserve0; - BitField<16, 4, OrbisAudioOutParamAttr> attributes; - BitField<20, 10, u32> reserve1; - BitField<31, 1, u32> unused; - }; +union OrbisAudioOutParamExtendedInformation { + BitField<0, 8, OrbisAudioOutParamFormat> data_format; + BitField<8, 8, u32> reserve0; + BitField<16, 4, OrbisAudioOutParamAttr> attributes; + BitField<20, 10, u32> reserve1; + BitField<31, 1, u32> unused; }; struct OrbisAudioOutOutputParam { diff --git a/src/core/libraries/audio/audioout_error.h b/src/core/libraries/audio/audioout_error.h new file mode 100644 index 000000000..7642e87e7 --- /dev/null +++ b/src/core/libraries/audio/audioout_error.h @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +// AudioOut library +constexpr int ORBIS_AUDIO_OUT_ERROR_NOT_OPENED = 0x80260001; +constexpr int ORBIS_AUDIO_OUT_ERROR_BUSY = 0x80260002; +constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_PORT = 0x80260003; +constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_POINTER = 0x80260004; +constexpr int ORBIS_AUDIO_OUT_ERROR_PORT_FULL = 0x80260005; +constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_SIZE = 0x80260006; +constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_FORMAT = 0x80260007; +constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_SAMPLE_FREQ = 0x80260008; +constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_VOLUME = 0x80260009; +constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_PORT_TYPE = 0x8026000A; +constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_CONF_TYPE = 0x8026000C; +constexpr int ORBIS_AUDIO_OUT_ERROR_OUT_OF_MEMORY = 0x8026000D; +constexpr int ORBIS_AUDIO_OUT_ERROR_ALREADY_INIT = 0x8026000E; +constexpr int ORBIS_AUDIO_OUT_ERROR_NOT_INIT = 0x8026000F; +constexpr int ORBIS_AUDIO_OUT_ERROR_MEMORY = 0x80260010; +constexpr int ORBIS_AUDIO_OUT_ERROR_SYSTEM_RESOURCE = 0x80260011; +constexpr int ORBIS_AUDIO_OUT_ERROR_TRANS_EVENT = 0x80260012; +constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_FLAG = 0x80260013; +constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_MIXLEVEL = 0x80260014; +constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_ARG = 0x80260015; +constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_PARAM = 0x80260016; +constexpr int ORBIS_AUDIO_OUT_ERROR_MASTERING_FATAL = 0x80260200; +constexpr int ORBIS_AUDIO_OUT_ERROR_MASTERING_INVALID_API_PARAM = 0x80260201; +constexpr int ORBIS_AUDIO_OUT_ERROR_MASTERING_INVALID_CONFIG = 0x80260202; +constexpr int ORBIS_AUDIO_OUT_ERROR_MASTERING_NOT_INITIALIZED = 0x80260203; +constexpr int ORBIS_AUDIO_OUT_ERROR_MASTERING_INVALID_STATES_ID = 0x80260204; diff --git a/src/core/libraries/audio/sdl_audio.cpp b/src/core/libraries/audio/sdl_audio.cpp new file mode 100644 index 000000000..8cc823abe --- /dev/null +++ b/src/core/libraries/audio/sdl_audio.cpp @@ -0,0 +1,141 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include + +#include "common/assert.h" +#include "core/libraries/audio/audioout_error.h" +#include "core/libraries/audio/sdl_audio.h" + +namespace Libraries::AudioOut { + +constexpr int AUDIO_STREAM_BUFFER_THRESHOLD = 65536; // Define constant for buffer threshold + +s32 SDLAudioOut::Open(OrbisAudioOutPort type, u32 samples_num, u32 freq, + OrbisAudioOutParamFormat format) { + std::scoped_lock lock{m_mutex}; + const auto port = std::ranges::find(ports_out, false, &PortOut::is_open); + if (port == ports_out.end()) { + LOG_ERROR(Lib_AudioOut, "Audio ports are full"); + return ORBIS_AUDIO_OUT_ERROR_PORT_FULL; + } + + port->is_open = true; + port->type = type; + port->samples_num = samples_num; + port->freq = freq; + port->format = format; + SDL_AudioFormat sampleFormat; + switch (format) { + case OrbisAudioOutParamFormat::S16Mono: + sampleFormat = SDL_AUDIO_S16; + port->channels_num = 1; + port->sample_size = 2; + break; + case OrbisAudioOutParamFormat::FloatMono: + sampleFormat = SDL_AUDIO_F32; + port->channels_num = 1; + port->sample_size = 4; + break; + case OrbisAudioOutParamFormat::S16Stereo: + sampleFormat = SDL_AUDIO_S16; + port->channels_num = 2; + port->sample_size = 2; + break; + case OrbisAudioOutParamFormat::FloatStereo: + sampleFormat = SDL_AUDIO_F32; + port->channels_num = 2; + port->sample_size = 4; + break; + case OrbisAudioOutParamFormat::S16_8CH: + sampleFormat = SDL_AUDIO_S16; + port->channels_num = 8; + port->sample_size = 2; + break; + case OrbisAudioOutParamFormat::Float_8CH: + sampleFormat = SDL_AUDIO_F32; + port->channels_num = 8; + port->sample_size = 4; + break; + case OrbisAudioOutParamFormat::S16_8CH_Std: + sampleFormat = SDL_AUDIO_S16; + port->channels_num = 8; + port->sample_size = 2; + break; + case OrbisAudioOutParamFormat::Float_8CH_Std: + sampleFormat = SDL_AUDIO_F32; + port->channels_num = 8; + port->sample_size = 4; + break; + default: + UNREACHABLE_MSG("Unknown format"); + } + + port->volume.fill(Libraries::AudioOut::SCE_AUDIO_OUT_VOLUME_0DB); + + SDL_AudioSpec fmt; + SDL_zero(fmt); + fmt.format = sampleFormat; + fmt.channels = port->channels_num; + fmt.freq = freq; + port->stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &fmt, NULL, NULL); + SDL_ResumeAudioDevice(SDL_GetAudioStreamDevice(port->stream)); + return std::distance(ports_out.begin(), port) + 1; +} + +s32 SDLAudioOut::Output(s32 handle, const void* ptr) { + auto& port = ports_out.at(handle - 1); + if (!port.is_open) { + return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; + } + + const size_t data_size = port.samples_num * port.sample_size * port.channels_num; + bool result = SDL_PutAudioStreamData(port.stream, ptr, data_size); + while (SDL_GetAudioStreamAvailable(port.stream) > AUDIO_STREAM_BUFFER_THRESHOLD) { + SDL_Delay(0); + } + return result ? ORBIS_OK : -1; +} + +s32 SDLAudioOut::SetVolume(s32 handle, s32 bitflag, s32* volume) { + using Libraries::AudioOut::OrbisAudioOutParamFormat; + auto& port = ports_out.at(handle - 1); + if (!port.is_open) { + return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; + } + + for (int i = 0; i < port.channels_num; i++, bitflag >>= 1u) { + auto bit = bitflag & 0x1u; + + if (bit == 1) { + int src_index = i; + if (port.format == OrbisAudioOutParamFormat::Float_8CH_Std || + port.format == OrbisAudioOutParamFormat::S16_8CH_Std) { + switch (i) { + case 4: + src_index = 6; + break; + case 5: + src_index = 7; + break; + case 6: + src_index = 4; + break; + case 7: + src_index = 5; + break; + default: + break; + } + } + port.volume[i] = volume[src_index]; + } + } + + return ORBIS_OK; +} + +} // namespace Libraries::AudioOut diff --git a/src/core/libraries/audio/sdl_audio.h b/src/core/libraries/audio/sdl_audio.h new file mode 100644 index 000000000..2c34f8e29 --- /dev/null +++ b/src/core/libraries/audio/sdl_audio.h @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include "core/libraries/audio/audioout.h" + +namespace Libraries::AudioOut { + +class SDLAudioOut { +public: + explicit SDLAudioOut() = default; + ~SDLAudioOut() = default; + + s32 Open(OrbisAudioOutPort type, u32 samples_num, u32 freq, OrbisAudioOutParamFormat format); + s32 Output(s32 handle, const void* ptr); + s32 SetVolume(s32 handle, s32 bitflag, s32* volume); + + constexpr std::pair GetStatus(s32 handle) const { + const auto& port = ports_out.at(handle - 1); + return std::make_pair(port.type, port.channels_num); + } + +private: + struct PortOut { + SDL_AudioStream* stream; + u32 samples_num; + u32 freq; + OrbisAudioOutParamFormat format; + OrbisAudioOutPort type; + int channels_num; + std::array volume; + u8 sample_size; + bool is_open; + }; + std::shared_mutex m_mutex; + std::array ports_out{}; +}; + +} // namespace Libraries::AudioOut diff --git a/src/core/libraries/audio3d/audio3d.cpp b/src/core/libraries/audio3d/audio3d.cpp index 8aeb88da8..44670d87b 100644 --- a/src/core/libraries/audio3d/audio3d.cpp +++ b/src/core/libraries/audio3d/audio3d.cpp @@ -1,19 +1,15 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "audio3d.h" -#include "audio3d_error.h" -#include "audio3d_impl.h" - #include "common/logging/log.h" #include "core/libraries/audio/audioout.h" +#include "core/libraries/audio3d/audio3d.h" +#include "core/libraries/audio3d/audio3d_error.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" namespace Libraries::Audio3d { -// Audio3d - int PS4_SYSV_ABI sceAudio3dInitialize(s64 iReserved) { LOG_INFO(Lib_Audio3d, "iReserved = {}", iReserved); return ORBIS_OK; @@ -25,18 +21,19 @@ int PS4_SYSV_ABI sceAudio3dTerminate() { return ORBIS_OK; } -void PS4_SYSV_ABI sceAudio3dGetDefaultOpenParameters(OrbisAudio3dOpenParameters* sParameters) { - if (sParameters != NULL) { - sParameters->szSizeThis = sizeof(OrbisAudio3dOpenParameters); - sParameters->uiGranularity = 256; - sParameters->eRate = ORBIS_AUDIO3D_RATE_48000; - sParameters->uiMaxObjects = 512; - sParameters->uiQueueDepth = 2; - sParameters->eBufferMode = ORBIS_AUDIO3D_BUFFER_ADVANCE_AND_PUSH; - sParameters->uiNumBeds = 2; - } else { +void PS4_SYSV_ABI sceAudio3dGetDefaultOpenParameters(OrbisAudio3dOpenParameters* parameters) { + if (parameters == nullptr) { LOG_ERROR(Lib_Audio3d, "Invalid OpenParameters ptr"); + return; } + + parameters->size_this = sizeof(OrbisAudio3dOpenParameters); + parameters->granularity = 256; + parameters->rate = OrbisAudio3dRate::Rate48000; + parameters->max_objects = 512; + parameters->queue_depth = 2; + parameters->buffer_mode = OrbisAudio3dBufferMode::AdvanceAndPush; + parameters->num_beds = 2; } int PS4_SYSV_ABI sceAudio3dPortOpen(OrbisUserServiceUserId iUserId, @@ -76,13 +73,13 @@ int PS4_SYSV_ABI sceAudio3dPortPush(OrbisAudio3dPortId uiPortId, OrbisAudio3dBlo int PS4_SYSV_ABI sceAudio3dPortGetAttributesSupported(OrbisAudio3dPortId uiPortId, OrbisAudio3dAttributeId* pCapabilities, - unsigned int* pNumCapabilities) { + u32* pNumCapabilities) { LOG_INFO(Lib_Audio3d, "uiPortId = {}", uiPortId); return ORBIS_OK; } -int PS4_SYSV_ABI sceAudio3dPortGetQueueLevel(OrbisAudio3dPortId uiPortId, unsigned int* pQueueLevel, - unsigned int* pQueueAvailable) { +int PS4_SYSV_ABI sceAudio3dPortGetQueueLevel(OrbisAudio3dPortId uiPortId, u32* pQueueLevel, + u32* pQueueAvailable) { LOG_INFO(Lib_Audio3d, "uiPortId = {}", uiPortId); return ORBIS_OK; } @@ -107,24 +104,24 @@ int PS4_SYSV_ABI sceAudio3dObjectSetAttributes(OrbisAudio3dPortId uiPortId, return ORBIS_OK; } -int PS4_SYSV_ABI sceAudio3dBedWrite(OrbisAudio3dPortId uiPortId, unsigned int uiNumChannels, +int PS4_SYSV_ABI sceAudio3dBedWrite(OrbisAudio3dPortId uiPortId, u32 uiNumChannels, OrbisAudio3dFormat eFormat, const void* pBuffer, - unsigned int uiNumSamples) { + u32 uiNumSamples) { LOG_TRACE(Lib_Audio3d, "uiPortId = {}, uiNumChannels = {}, uiNumSamples = {}", uiPortId, uiNumChannels, uiNumSamples); return ORBIS_OK; } -int PS4_SYSV_ABI sceAudio3dBedWrite2(OrbisAudio3dPortId uiPortId, unsigned int uiNumChannels, +int PS4_SYSV_ABI sceAudio3dBedWrite2(OrbisAudio3dPortId uiPortId, u32 uiNumChannels, OrbisAudio3dFormat eFormat, const void* pBuffer, - unsigned int uiNumSamples, - OrbisAudio3dOutputRoute eOutputRoute, bool bRestricted) { + u32 uiNumSamples, OrbisAudio3dOutputRoute eOutputRoute, + bool bRestricted) { LOG_INFO(Lib_Audio3d, "uiPortId = {}, uiNumChannels = {}, uiNumSamples = {}, bRestricted = {}", uiPortId, uiNumChannels, uiNumSamples, bRestricted); return ORBIS_OK; } -size_t PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMemorySize(unsigned int uiNumSpeakers, bool bIs3d) { +size_t PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMemorySize(u32 uiNumSpeakers, bool bIs3d) { LOG_INFO(Lib_Audio3d, "uiNumSpeakers = {}, bIs3d = {}", uiNumSpeakers, bIs3d); return ORBIS_OK; } @@ -152,7 +149,7 @@ int PS4_SYSV_ABI sceAudio3dDeleteSpeakerArray(OrbisAudio3dSpeakerArrayHandle han int PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMixCoefficients(OrbisAudio3dSpeakerArrayHandle handle, OrbisAudio3dPosition pos, float fSpread, float* pCoefficients, - unsigned int uiNumCoefficients) { + u32 uiNumCoefficients) { LOG_INFO(Lib_Audio3d, "fSpread = {}, uiNumCoefficients = {}", fSpread, uiNumCoefficients); if (handle == nullptr) { LOG_ERROR(Lib_Audio3d, "invalid SpeakerArrayHandle"); @@ -164,8 +161,7 @@ int PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMixCoefficients(OrbisAudio3dSpeakerArr int PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMixCoefficients2(OrbisAudio3dSpeakerArrayHandle handle, OrbisAudio3dPosition pos, float fSpread, float* pCoefficients, - unsigned int uiNumCoefficients, - bool bHeightAware, + u32 uiNumCoefficients, bool bHeightAware, float fDownmixSpreadRadius) { LOG_INFO(Lib_Audio3d, "fSpread = {}, uiNumCoefficients = {}, bHeightAware = {}, fDownmixSpreadRadius = {}", @@ -209,8 +205,8 @@ s32 PS4_SYSV_ABI sceAudio3dAudioOutOutputs(::Libraries::AudioOut::OrbisAudioOutO return ORBIS_OK; } -int PS4_SYSV_ABI sceAudio3dPortCreate(unsigned int uiGranularity, OrbisAudio3dRate eRate, - s64 iReserved, OrbisAudio3dPortId* pId) { +int PS4_SYSV_ABI sceAudio3dPortCreate(u32 uiGranularity, OrbisAudio3dRate eRate, s64 iReserved, + OrbisAudio3dPortId* pId) { LOG_INFO(Lib_Audio3d, "uiGranularity = {}, iReserved = {}", uiGranularity, iReserved); return ORBIS_OK; } @@ -341,4 +337,4 @@ void RegisterlibSceAudio3d(Core::Loader::SymbolsResolver* sym) { sceAudio3dSetGpuRenderer); }; -} // namespace Libraries::Audio3d \ No newline at end of file +} // namespace Libraries::Audio3d diff --git a/src/core/libraries/audio3d/audio3d.h b/src/core/libraries/audio3d/audio3d.h index 6cbe2d02f..6f344226f 100644 --- a/src/core/libraries/audio3d/audio3d.h +++ b/src/core/libraries/audio3d/audio3d.h @@ -15,56 +15,57 @@ namespace Libraries::Audio3d { class Audio3d; -typedef int OrbisUserServiceUserId; -typedef unsigned int OrbisAudio3dPortId; -typedef unsigned int OrbisAudio3dObjectId; -typedef unsigned int OrbisAudio3dAttributeId; +using OrbisUserServiceUserId = s32; +using OrbisAudio3dPortId = u32; +using OrbisAudio3dObjectId = u32; +using OrbisAudio3dAttributeId = u32; -enum OrbisAudio3dFormat { - ORBIS_AUDIO3D_FORMAT_S16 = 0, // s16 - ORBIS_AUDIO3D_FORMAT_FLOAT = 1 // f32 +enum class OrbisAudio3dFormat { + S16 = 0, + Float = 1, }; -enum OrbisAudio3dRate { ORBIS_AUDIO3D_RATE_48000 = 0 }; - -enum OrbisAudio3dBufferMode { - ORBIS_AUDIO3D_BUFFER_NO_ADVANCE = 0, - ORBIS_AUDIO3D_BUFFER_ADVANCE_NO_PUSH = 1, - ORBIS_AUDIO3D_BUFFER_ADVANCE_AND_PUSH = 2 +enum class OrbisAudio3dRate { + Rate48000 = 0, }; -enum OrbisAudio3dBlocking { ORBIS_AUDIO3D_BLOCKING_ASYNC = 0, ORBIS_AUDIO3D_BLOCKING_SYNC = 1 }; +enum class OrbisAudio3dBufferMode { NoAdvance = 0, AdvanceNoPush = 1, AdvanceAndPush = 2 }; -enum OrbisAudio3dPassthrough { - ORBIS_AUDIO3D_PASSTHROUGH_NONE = 0, - ORBIS_AUDIO3D_PASSTHROUGH_LEFT = 1, - ORBIS_AUDIO3D_PASSTHROUGH_RIGHT = 2 +enum class OrbisAudio3dBlocking { + Async = 0, + Sync = 1, }; -enum OrbisAudio3dOutputRoute { - ORBIS_AUDIO3D_OUTPUT_BOTH = 0, - ORBIS_AUDIO3D_OUTPUT_HMU_ONLY = 1, - ORBIS_AUDIO3D_OUTPUT_TV_ONLY = 2 +enum class OrbisAudio3dPassthrough { + None = 0, + Left = 1, + Right = 2, }; -enum OrbisAudio3dAmbisonics { - ORBIS_AUDIO3D_AMBISONICS_NONE = ~0, - ORBIS_AUDIO3D_AMBISONICS_W = 0, - ORBIS_AUDIO3D_AMBISONICS_X = 1, - ORBIS_AUDIO3D_AMBISONICS_Y = 2, - ORBIS_AUDIO3D_AMBISONICS_Z = 3, - ORBIS_AUDIO3D_AMBISONICS_R = 4, - ORBIS_AUDIO3D_AMBISONICS_S = 5, - ORBIS_AUDIO3D_AMBISONICS_T = 6, - ORBIS_AUDIO3D_AMBISONICS_U = 7, - ORBIS_AUDIO3D_AMBISONICS_V = 8, - ORBIS_AUDIO3D_AMBISONICS_K = 9, - ORBIS_AUDIO3D_AMBISONICS_L = 10, - ORBIS_AUDIO3D_AMBISONICS_M = 11, - ORBIS_AUDIO3D_AMBISONICS_N = 12, - ORBIS_AUDIO3D_AMBISONICS_O = 13, - ORBIS_AUDIO3D_AMBISONICS_P = 14, - ORBIS_AUDIO3D_AMBISONICS_Q = 15 +enum class OrbisAudio3dOutputRoute { + Both = 0, + HmuOnly = 1, + TvOnly = 2, +}; + +enum class OrbisAudio3dAmbisonics : u32 { + None = ~0U, + W = 0, + X = 1, + Y = 2, + Z = 3, + R = 4, + S = 5, + T = 6, + U = 7, + V = 8, + K = 9, + L = 10, + M = 11, + N = 12, + O = 13, + P = 14, + Q = 15 }; static const OrbisAudio3dAttributeId s_sceAudio3dAttributePcm = 0x00000001; @@ -86,21 +87,21 @@ struct OrbisAudio3dSpeakerArray; using OrbisAudio3dSpeakerArrayHandle = OrbisAudio3dSpeakerArray*; // head struct OrbisAudio3dOpenParameters { - size_t szSizeThis; - unsigned int uiGranularity; - OrbisAudio3dRate eRate; - unsigned int uiMaxObjects; - unsigned int uiQueueDepth; - OrbisAudio3dBufferMode eBufferMode; + size_t size_this; + u32 granularity; + OrbisAudio3dRate rate; + u32 max_objects; + u32 queue_depth; + OrbisAudio3dBufferMode buffer_mode; char padding[32]; - unsigned int uiNumBeds; + u32 num_beds; }; struct OrbisAudio3dAttribute { - OrbisAudio3dAttributeId uiAttributeId; + OrbisAudio3dAttributeId attribute_id; char padding[32]; - const void* pValue; - size_t szValue; + const void* p_value; + size_t value; }; struct OrbisAudio3dPosition { @@ -110,25 +111,25 @@ struct OrbisAudio3dPosition { }; struct OrbisAudio3dPcm { - OrbisAudio3dFormat eFormat; - const void* pSampleBuffer; - unsigned int uiNumSamples; + OrbisAudio3dFormat format; + const void* sample_buffer; + u32 num_samples; }; struct OrbisAudio3dSpeakerArrayParameters { - OrbisAudio3dPosition* pSpeakerPosition; - unsigned int uiNumSpeakers; - bool bIs3d; - void* pBuffer; - size_t szSize; + OrbisAudio3dPosition* speaker_position; + u32 num_speakers; + bool is_3d; + void* buffer; + size_t size; }; struct OrbisAudio3dApplicationSpecific { - size_t szSizeThis; - u8 cApplicationSpecific[32]; + size_t size_this; + u8 application_specific[32]; }; void PS4_SYSV_ABI sceAudio3dGetDefaultOpenParameters(OrbisAudio3dOpenParameters* sParameters); void RegisterlibSceAudio3d(Core::Loader::SymbolsResolver* sym); -} // namespace Libraries::Audio3d \ No newline at end of file +} // namespace Libraries::Audio3d diff --git a/src/core/libraries/audio3d/audio3d_error.h b/src/core/libraries/audio3d/audio3d_error.h index ff9d9749c..626ac8699 100644 --- a/src/core/libraries/audio3d/audio3d_error.h +++ b/src/core/libraries/audio3d/audio3d_error.h @@ -3,6 +3,8 @@ #pragma once +#include "core/libraries/error_codes.h" + constexpr int ORBIS_AUDIO3D_ERROR_UNKNOWN = 0x80EA0001; constexpr int ORBIS_AUDIO3D_ERROR_INVALID_PORT = 0x80EA0002; constexpr int ORBIS_AUDIO3D_ERROR_INVALID_OBJECT = 0x80EA0003; diff --git a/src/core/libraries/audio3d/audio3d_impl.h b/src/core/libraries/audio3d/audio3d_impl.h index 4e6342b1b..1213a030e 100644 --- a/src/core/libraries/audio3d/audio3d_impl.h +++ b/src/core/libraries/audio3d/audio3d_impl.h @@ -10,7 +10,7 @@ namespace Libraries::Audio3d { class Audio3d { public: private: - typedef unsigned int OrbisAudio3dPluginId; + using OrbisAudio3dPluginId = u32; }; } // namespace Libraries::Audio3d diff --git a/src/core/libraries/avplayer/avplayer.cpp b/src/core/libraries/avplayer/avplayer.cpp index 1257473e1..176fda137 100644 --- a/src/core/libraries/avplayer/avplayer.cpp +++ b/src/core/libraries/avplayer/avplayer.cpp @@ -3,8 +3,8 @@ #include "common/logging/log.h" #include "core/libraries/avplayer/avplayer.h" +#include "core/libraries/avplayer/avplayer_error.h" #include "core/libraries/avplayer/avplayer_impl.h" -#include "core/libraries/error_codes.h" #include "core/libraries/libs.h" namespace Libraries::AvPlayer { diff --git a/src/core/libraries/avplayer/avplayer.h b/src/core/libraries/avplayer/avplayer.h index 9f100bef3..2d472f801 100644 --- a/src/core/libraries/avplayer/avplayer.h +++ b/src/core/libraries/avplayer/avplayer.h @@ -18,17 +18,26 @@ class AvPlayer; using SceAvPlayerHandle = AvPlayer*; -enum SceAvPlayerUriType { SCE_AVPLAYER_URI_TYPE_SOURCE = 0 }; +enum class SceAvPlayerUriType : u32 { + Source = 0, +}; struct SceAvPlayerUri { const char* name; u32 length; }; -enum SceAvPlayerSourceType { - SCE_AVPLAYER_SOURCE_TYPE_UNKNOWN = 0, - SCE_AVPLAYER_SOURCE_TYPE_FILE_MP4 = 1, - SCE_AVPLAYER_SOURCE_TYPE_HLS = 8 +enum class SceAvPlayerSourceType { + Unknown = 0, + FileMp4 = 1, + Hls = 8, +}; + +enum class SceAvPlayerStreamType : u32 { + Video, + Audio, + TimedText, + Unknown, }; struct SceAvPlayerSourceDetails { @@ -50,7 +59,7 @@ struct SceAvPlayerVideo { u32 width; u32 height; f32 aspect_ratio; - u8 language_code[4]; + char language_code[4]; }; struct SceAvPlayerTextPosition { @@ -82,7 +91,7 @@ struct SceAvPlayerFrameInfo { }; struct SceAvPlayerStreamInfo { - u32 type; + SceAvPlayerStreamType type; u8 reserved[4]; SceAvPlayerStreamDetails details; u64 duration; @@ -135,10 +144,10 @@ struct SceAvPlayerFrameInfoEx { SceAvPlayerStreamDetailsEx details; }; -typedef void* PS4_SYSV_ABI (*SceAvPlayerAllocate)(void* p, u32 align, u32 size); -typedef void PS4_SYSV_ABI (*SceAvPlayerDeallocate)(void* p, void* mem); -typedef void* PS4_SYSV_ABI (*SceAvPlayerAllocateTexture)(void* p, u32 align, u32 size); -typedef void PS4_SYSV_ABI (*SceAvPlayerDeallocateTexture)(void* p, void* mem); +using SceAvPlayerAllocate = void* PS4_SYSV_ABI (*)(void* p, u32 align, u32 size); +using SceAvPlayerDeallocate = void PS4_SYSV_ABI (*)(void* p, void* mem); +using SceAvPlayerAllocateTexture = void* PS4_SYSV_ABI (*)(void* p, u32 align, u32 size); +using SceAvPlayerDeallocateTexture = void PS4_SYSV_ABI (*)(void* p, void* mem); struct SceAvPlayerMemAllocator { void* object_ptr; @@ -148,10 +157,10 @@ struct SceAvPlayerMemAllocator { SceAvPlayerDeallocateTexture deallocate_texture; }; -typedef s32 PS4_SYSV_ABI (*SceAvPlayerOpenFile)(void* p, const char* name); -typedef s32 PS4_SYSV_ABI (*SceAvPlayerCloseFile)(void* p); -typedef s32 PS4_SYSV_ABI (*SceAvPlayerReadOffsetFile)(void* p, u8* buf, u64 pos, u32 len); -typedef u64 PS4_SYSV_ABI (*SceAvPlayerSizeFile)(void* p); +using SceAvPlayerOpenFile = s32 PS4_SYSV_ABI (*)(void* p, const char* name); +using SceAvPlayerCloseFile = s32 PS4_SYSV_ABI (*)(void* p); +using SceAvPlayerReadOffsetFile = s32 PS4_SYSV_ABI (*)(void* p, u8* buf, u64 pos, u32 len); +using SceAvPlayerSizeFile = u64 PS4_SYSV_ABI (*)(void* p); struct SceAvPlayerFileReplacement { void* object_ptr; @@ -161,31 +170,31 @@ struct SceAvPlayerFileReplacement { SceAvPlayerSizeFile size; }; -enum SceAvPlayerEvents { - SCE_AVPLAYER_STATE_STOP = 0x01, - SCE_AVPLAYER_STATE_READY = 0x02, - SCE_AVPLAYER_STATE_PLAY = 0x03, - SCE_AVPLAYER_STATE_PAUSE = 0x04, - SCE_AVPLAYER_STATE_BUFFERING = 0x05, - SCE_AVPLAYER_TIMED_TEXT_DELIVERY = 0x10, - SCE_AVPLAYER_WARNING_ID = 0x20, - SCE_AVPLAYER_ENCRYPTION = 0x30, - SCE_AVPLAYER_DRM_ERROR = 0x40 +enum class SceAvPlayerEvents { + StateStop = 0x01, + StateReady = 0x02, + StatePlay = 0x03, + StatePause = 0x04, + StateBuffering = 0x05, + TimedTextDelivery = 0x10, + WarningId = 0x20, + Encryption = 0x30, + DrmError = 0x40, }; -typedef void PS4_SYSV_ABI (*SceAvPlayerEventCallback)(void* p, SceAvPlayerEvents event, s32 src_id, - void* data); +using SceAvPlayerEventCallback = void PS4_SYSV_ABI (*)(void* p, SceAvPlayerEvents event, s32 src_id, + void* data); struct SceAvPlayerEventReplacement { void* object_ptr; SceAvPlayerEventCallback event_callback; }; -enum SceAvPlayerDebuglevels { - SCE_AVPLAYER_DBG_NONE, - SCE_AVPLAYER_DBG_INFO, - SCE_AVPLAYER_DBG_WARNINGS, - SCE_AVPLAYER_DBG_ALL +enum class SceAvPlayerDebuglevels { + None, + Info, + Warnings, + All, }; struct SceAvPlayerInitData { @@ -224,24 +233,17 @@ struct SceAvPlayerInitDataEx { u8 reserved[3]; }; -enum SceAvPlayerStreamType { - SCE_AVPLAYER_VIDEO, - SCE_AVPLAYER_AUDIO, - SCE_AVPLAYER_TIMEDTEXT, - SCE_AVPLAYER_UNKNOWN +enum class SceAvPlayerVideoDecoderType { + Default = 0, + Reserved1, + Software, + Software2, }; -enum SceAvPlayerVideoDecoderType { - SCE_AVPLAYER_VIDEO_DECODER_TYPE_DEFAULT = 0, - SCE_AVPLAYER_VIDEO_DECODER_TYPE_RESERVED1, - SCE_AVPLAYER_VIDEO_DECODER_TYPE_SOFTWARE, - SCE_AVPLAYER_VIDEO_DECODER_TYPE_SOFTWARE2 -}; - -enum SceAvPlayerAudioDecoderType { - SCE_AVPLAYER_AUDIO_DECODER_TYPE_DEFAULT = 0, - SCE_AVPLAYER_AUDIO_DECODER_TYPE_RESERVED1, - SCE_AVPLAYER_AUDIO_DECODER_TYPE_RESERVED2 +enum class SceAvPlayerAudioDecoderType { + Default = 0, + Reserved1, + Reserved2, }; struct SceAvPlayerDecoderInit { @@ -281,12 +283,12 @@ struct SceAvPlayerPostInitData { u8 reserved[56]; }; -enum SceAvPlayerAvSyncMode { - SCE_AVPLAYER_AV_SYNC_MODE_DEFAULT = 0, - SCE_AVPLAYER_AV_SYNC_MODE_NONE +enum class SceAvPlayerAvSyncMode { + Default = 0, + None, }; -typedef int PS4_SYSV_ABI (*SceAvPlayerLogCallback)(void* p, const char* format, va_list args); +using SceAvPlayerLogCallback = int PS4_SYSV_ABI (*)(void* p, const char* format, va_list args); void RegisterlibSceAvPlayer(Core::Loader::SymbolsResolver* sym); diff --git a/src/core/libraries/avplayer/avplayer_common.cpp b/src/core/libraries/avplayer/avplayer_common.cpp index 805920a3d..28d7803a1 100644 --- a/src/core/libraries/avplayer/avplayer_common.cpp +++ b/src/core/libraries/avplayer/avplayer_common.cpp @@ -15,7 +15,7 @@ static bool iequals(std::string_view l, std::string_view r) { SceAvPlayerSourceType GetSourceType(std::string_view path) { if (path.empty()) { - return SCE_AVPLAYER_SOURCE_TYPE_UNKNOWN; + return SceAvPlayerSourceType::Unknown; } std::string_view name = path; @@ -25,14 +25,14 @@ SceAvPlayerSourceType GetSourceType(std::string_view path) { // -> schema://server.domain/path/to/file.ext/and/beyond name = path.substr(0, path.find_first_of("?#")); if (name.empty()) { - return SCE_AVPLAYER_SOURCE_TYPE_UNKNOWN; + return SceAvPlayerSourceType::Unknown; } } // schema://server.domain/path/to/file.ext/and/beyond -> .ext/and/beyond auto ext = name.substr(name.rfind('.')); if (ext.empty()) { - return SCE_AVPLAYER_SOURCE_TYPE_UNKNOWN; + return SceAvPlayerSourceType::Unknown; } // .ext/and/beyond -> .ext @@ -40,14 +40,14 @@ SceAvPlayerSourceType GetSourceType(std::string_view path) { if (iequals(ext, ".mp4") || iequals(ext, ".m4v") || iequals(ext, ".m3d") || iequals(ext, ".m4a") || iequals(ext, ".mov")) { - return SCE_AVPLAYER_SOURCE_TYPE_FILE_MP4; + return SceAvPlayerSourceType::FileMp4; } if (iequals(ext, ".m3u8")) { - return SCE_AVPLAYER_SOURCE_TYPE_HLS; + return SceAvPlayerSourceType::Hls; } - return SCE_AVPLAYER_SOURCE_TYPE_UNKNOWN; + return SceAvPlayerSourceType::Unknown; } } // namespace Libraries::AvPlayer diff --git a/src/core/libraries/avplayer/avplayer_error.h b/src/core/libraries/avplayer/avplayer_error.h new file mode 100644 index 000000000..ebe4d0dd3 --- /dev/null +++ b/src/core/libraries/avplayer/avplayer_error.h @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +// AvPlayer library +constexpr int ORBIS_AVPLAYER_ERROR_INVALID_PARAMS = 0x806A0001; +constexpr int ORBIS_AVPLAYER_ERROR_OPERATION_FAILED = 0x806A0002; +constexpr int ORBIS_AVPLAYER_ERROR_NO_MEMORY = 0x806A0003; +constexpr int ORBIS_AVPLAYER_ERROR_NOT_SUPPORTED = 0x806A0004; +constexpr int ORBIS_AVPLAYER_ERROR_WAR_FILE_NONINTERLEAVED = 0x806A00A0; +constexpr int ORBIS_AVPLAYER_ERROR_WAR_LOOPING_BACK = 0x806A00A1; +constexpr int ORBIS_AVPLAYER_ERROR_WAR_JUMP_COMPLETE = 0x806A00A3; +constexpr int ORBIS_AVPLAYER_ERROR_INFO_MARLIN_ENCRY = 0x806A00B0; +constexpr int ORBIS_AVPLAYER_ERROR_INFO_PLAYREADY_ENCRY = 0x806A00B4; +constexpr int ORBIS_AVPLAYER_ERROR_INFO_AES_ENCRY = 0x806A00B5; +constexpr int ORBIS_AVPLAYER_ERROR_INFO_OTHER_ENCRY = 0x806A00BF; diff --git a/src/core/libraries/avplayer/avplayer_impl.cpp b/src/core/libraries/avplayer/avplayer_impl.cpp index 0f39acfdc..d9a67134c 100644 --- a/src/core/libraries/avplayer/avplayer_impl.cpp +++ b/src/core/libraries/avplayer/avplayer_impl.cpp @@ -2,8 +2,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "core/libraries/avplayer/avplayer_common.h" +#include "core/libraries/avplayer/avplayer_error.h" #include "core/libraries/avplayer/avplayer_impl.h" -#include "core/libraries/error_codes.h" #include "core/tls.h" namespace Libraries::AvPlayer { diff --git a/src/core/libraries/avplayer/avplayer_source.cpp b/src/core/libraries/avplayer/avplayer_source.cpp index 8a65377c2..8e43e7277 100644 --- a/src/core/libraries/avplayer/avplayer_source.cpp +++ b/src/core/libraries/avplayer/avplayer_source.cpp @@ -85,17 +85,17 @@ s32 AvPlayerSource::GetStreamCount() { return m_avformat_context->nb_streams; } -static s32 CodecTypeToStreamType(AVMediaType codec_type) { +static SceAvPlayerStreamType CodecTypeToStreamType(AVMediaType codec_type) { switch (codec_type) { case AVMediaType::AVMEDIA_TYPE_VIDEO: - return SCE_AVPLAYER_VIDEO; + return SceAvPlayerStreamType::Video; case AVMediaType::AVMEDIA_TYPE_AUDIO: - return SCE_AVPLAYER_AUDIO; + return SceAvPlayerStreamType::Audio; case AVMediaType::AVMEDIA_TYPE_SUBTITLE: - return SCE_AVPLAYER_TIMEDTEXT; + return SceAvPlayerStreamType::TimedText; default: LOG_ERROR(Lib_AvPlayer, "Unexpected AVMediaType {}", magic_enum::enum_name(codec_type)); - return -1; + return SceAvPlayerStreamType::Unknown; } } @@ -124,7 +124,7 @@ bool AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info LOG_WARNING(Lib_AvPlayer, "Stream {} language is unknown", stream_index); } switch (info.type) { - case SCE_AVPLAYER_VIDEO: { + case SceAvPlayerStreamType::Video: { LOG_INFO(Lib_AvPlayer, "Stream {} is a video stream.", stream_index); info.details.video.aspect_ratio = f32(p_stream->codecpar->width) / p_stream->codecpar->height; @@ -142,7 +142,7 @@ bool AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info } break; } - case SCE_AVPLAYER_AUDIO: { + case SceAvPlayerStreamType::Audio: { LOG_INFO(Lib_AvPlayer, "Stream {} is an audio stream.", stream_index); info.details.audio.channel_count = p_stream->codecpar->ch_layout.nb_channels; info.details.audio.sample_rate = p_stream->codecpar->sample_rate; @@ -153,7 +153,7 @@ bool AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info } break; } - case SCE_AVPLAYER_TIMEDTEXT: { + case SceAvPlayerStreamType::TimedText: { LOG_WARNING(Lib_AvPlayer, "Stream {} is a timedtext stream.", stream_index); info.details.subs.font_size = 12; info.details.subs.text_size = 12; @@ -164,7 +164,8 @@ bool AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info break; } default: { - LOG_ERROR(Lib_AvPlayer, "Stream {} type is unknown: {}.", stream_index, info.type); + LOG_ERROR(Lib_AvPlayer, "Stream {} type is unknown: {}.", stream_index, + magic_enum::enum_name(info.type)); return false; } } diff --git a/src/core/libraries/avplayer/avplayer_state.cpp b/src/core/libraries/avplayer/avplayer_state.cpp index 3d9840a1b..c3694eec0 100644 --- a/src/core/libraries/avplayer/avplayer_state.cpp +++ b/src/core/libraries/avplayer/avplayer_state.cpp @@ -3,9 +3,9 @@ #include "common/logging/log.h" #include "common/thread.h" +#include "core/libraries/avplayer/avplayer_error.h" #include "core/libraries/avplayer/avplayer_source.h" #include "core/libraries/avplayer/avplayer_state.h" -#include "core/libraries/error_codes.h" #include "core/tls.h" #include @@ -16,7 +16,7 @@ void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, SceAvPlayer s32 source_id, void* event_data) { auto const self = reinterpret_cast(opaque); - if (event_id == SCE_AVPLAYER_STATE_READY) { + if (event_id == SceAvPlayerEvents::StateReady) { s32 video_stream_index = -1; s32 audio_stream_index = -1; s32 timedtext_stream_index = -1; @@ -36,36 +36,37 @@ void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, SceAvPlayer return; } - const std::string_view default_language( - reinterpret_cast(self->m_default_language)); + const std::string_view default_language{self->m_default_language}; switch (info.type) { - case SCE_AVPLAYER_VIDEO: + case SceAvPlayerStreamType::Video: if (video_stream_index == -1) { video_stream_index = stream_index; } if (!default_language.empty() && - default_language == reinterpret_cast(info.details.video.language_code)) { + default_language == info.details.video.language_code) { video_stream_index = stream_index; } break; - case SCE_AVPLAYER_AUDIO: + case SceAvPlayerStreamType::Audio: if (audio_stream_index == -1) { audio_stream_index = stream_index; } if (!default_language.empty() && - default_language == reinterpret_cast(info.details.video.language_code)) { + default_language == info.details.video.language_code) { audio_stream_index = stream_index; } break; - case SCE_AVPLAYER_TIMEDTEXT: + case SceAvPlayerStreamType::TimedText: if (default_language.empty()) { timedtext_stream_index = stream_index; break; } - if (default_language == reinterpret_cast(info.details.video.language_code)) { + if (default_language == info.details.video.language_code) { timedtext_stream_index = stream_index; } break; + default: + break; } } @@ -141,7 +142,7 @@ bool AvPlayerState::AddSource(std::string_view path, SceAvPlayerSourceType sourc m_up_source = std::make_unique( *this, m_post_init_data.video_decoder_init.decoderType.video_type == - SCE_AVPLAYER_VIDEO_DECODER_TYPE_SOFTWARE2); + SceAvPlayerVideoDecoderType::Software2); if (!m_up_source->Init(m_init_data, path)) { SetState(AvState::Error); m_up_source.reset(); @@ -317,23 +318,23 @@ void AvPlayerState::OnEOF() { void AvPlayerState::OnPlaybackStateChanged(AvState state) { switch (state) { case AvState::Ready: { - EmitEvent(SCE_AVPLAYER_STATE_READY); + EmitEvent(SceAvPlayerEvents::StateReady); break; } case AvState::Play: { - EmitEvent(SCE_AVPLAYER_STATE_PLAY); + EmitEvent(SceAvPlayerEvents::StatePlay); break; } case AvState::Stop: { - EmitEvent(SCE_AVPLAYER_STATE_STOP); + EmitEvent(SceAvPlayerEvents::StateStop); break; } case AvState::Pause: { - EmitEvent(SCE_AVPLAYER_STATE_PAUSE); + EmitEvent(SceAvPlayerEvents::StatePause); break; } case AvState::Buffering: { - EmitEvent(SCE_AVPLAYER_STATE_BUFFERING); + EmitEvent(SceAvPlayerEvents::StateBuffering); break; } default: diff --git a/src/core/libraries/avplayer/avplayer_state.h b/src/core/libraries/avplayer/avplayer_state.h index 8a0d7f4f8..48cd17bf2 100644 --- a/src/core/libraries/avplayer/avplayer_state.h +++ b/src/core/libraries/avplayer/avplayer_state.h @@ -69,7 +69,7 @@ private: SceAvPlayerPostInitData m_post_init_data{}; SceAvPlayerEventReplacement m_event_replacement{}; bool m_auto_start{}; - u8 m_default_language[4]{}; + char m_default_language[4]{}; std::atomic m_current_state; std::atomic m_previous_state; diff --git a/src/core/libraries/error_codes.h b/src/core/libraries/error_codes.h index 280ddf1cb..e1902b61c 100644 --- a/src/core/libraries/error_codes.h +++ b/src/core/libraries/error_codes.h @@ -3,626 +3,6 @@ #pragma once -// posix error codes -constexpr int POSIX_EPERM = 1; -constexpr int POSIX_ENOENT = 2; -constexpr int POSIX_ESRCH = 3; -constexpr int POSIX_EINTR = 4; -constexpr int POSIX_EIO = 5; -constexpr int POSIX_ENXIO = 6; -constexpr int POSIX_E2BIG = 7; -constexpr int POSIX_ENOEXEC = 8; -constexpr int POSIX_EBADF = 9; -constexpr int POSIX_ECHILD = 10; -constexpr int POSIX_EDEADLK = 11; -constexpr int POSIX_ENOMEM = 12; -constexpr int POSIX_EACCES = 13; -constexpr int POSIX_EFAULT = 14; -constexpr int POSIX_ENOTBLK = 15; -constexpr int POSIX_EBUSY = 16; -constexpr int POSIX_EEXIST = 17; -constexpr int POSIX_EXDEV = 18; -constexpr int POSIX_ENODEV = 19; -constexpr int POSIX_ENOTDIR = 20; -constexpr int POSIX_EISDIR = 21; -constexpr int POSIX_EINVAL = 22; -constexpr int POSIX_ENFILE = 23; -constexpr int POSIX_EMFILE = 24; -constexpr int POSIX_ENOTTY = 25; -constexpr int POSIX_ETXTBSY = 26; -constexpr int POSIX_EFBIG = 27; -constexpr int POSIX_ENOSPC = 28; -constexpr int POSIX_ESPIPE = 29; -constexpr int POSIX_EROFS = 30; -constexpr int POSIX_EMLINK = 31; -constexpr int POSIX_EPIPE = 32; -constexpr int POSIX_EDOM = 33; -constexpr int POSIX_ERANGE = 34; -constexpr int POSIX_EAGAIN = 35; -constexpr int POSIX_EWOULDBLOCK = 35; -constexpr int POSIX_EINPROGRESS = 36; -constexpr int POSIX_EALREADY = 37; -constexpr int POSIX_ENOTSOCK = 38; -constexpr int POSIX_EDESTADDRREQ = 39; -constexpr int POSIX_EMSGSIZE = 40; -constexpr int POSIX_EPROTOTYPE = 41; -constexpr int POSIX_ENOPROTOOPT = 42; -constexpr int POSIX_EPROTONOSUPPORT = 43; -constexpr int POSIX_ESOCKTNOSUPPORT = 44; -constexpr int POSIX_EOPNOTSUPP = 45; -constexpr int POSIX_ENOTSUP = 45; -constexpr int POSIX_EPFNOSUPPORT = 46; -constexpr int POSIX_EAFNOSUPPORT = 47; -constexpr int POSIX_EADDRINUSE = 48; -constexpr int POSIX_EADDRNOTAVAIL = 49; -constexpr int POSIX_ENETDOWN = 50; -constexpr int POSIX_ENETUNREACH = 51; -constexpr int POSIX_ENETRESET = 52; -constexpr int POSIX_ECONNABORTED = 53; -constexpr int POSIX_ECONNRESET = 54; -constexpr int POSIX_ENOBUFS = 55; -constexpr int POSIX_EISCONN = 56; -constexpr int POSIX_ENOTCONN = 57; -constexpr int POSIX_ESHUTDOWN = 58; -constexpr int POSIX_ETOOMANYREFS = 59; -constexpr int POSIX_ETIMEDOUT = 60; -constexpr int POSIX_ECONNREFUSED = 61; -constexpr int POSIX_ELOOP = 62; -constexpr int POSIX_ENAMETOOLONG = 63; -constexpr int POSIX_EHOSTDOWN = 64; -constexpr int POSIX_EHOSTUNREACH = 65; -constexpr int POSIX_ENOTEMPTY = 66; -constexpr int POSIX_EPROCLIM = 67; -constexpr int POSIX_EUSERS = 68; -constexpr int POSIX_EDQUOT = 69; -constexpr int POSIX_ESTALE = 70; -constexpr int POSIX_EREMOTE = 71; -constexpr int POSIX_EBADRPC = 72; -constexpr int POSIX_ERPCMISMATCH = 73; -constexpr int POSIX_EPROGUNAVAIL = 74; -constexpr int POSIX_EPROGMISMATCH = 75; -constexpr int POSIX_EPROCUNAVAIL = 76; -constexpr int POSIX_ENOLCK = 77; -constexpr int POSIX_ENOSYS = 78; -constexpr int POSIX_EFTYPE = 79; -constexpr int POSIX_EAUTH = 80; -constexpr int POSIX_ENEEDAUTH = 81; -constexpr int POSIX_EIDRM = 82; -constexpr int POSIX_ENOMSG = 83; -constexpr int POSIX_EOVERFLOW = 84; -constexpr int POSIX_ECANCELED = 85; -constexpr int POSIX_EILSEQ = 86; -constexpr int POSIX_ENOATTR = 87; -constexpr int POSIX_EDOOFUS = 88; -constexpr int POSIX_EBADMSG = 89; -constexpr int POSIX_EMULTIHOP = 90; -constexpr int POSIX_ENOLINK = 91; -constexpr int POSIX_EPROTO = 92; -constexpr int POSIX_ENOTCAPABLE = 93; -constexpr int POSIX_ECAPMODE = 94; -constexpr int POSIX_ENOBLK = 95; -constexpr int POSIX_EICV = 96; -constexpr int POSIX_ENOPLAYGOENT = 97; -constexpr int POSIX_EREVOKE = 98; -constexpr int POSIX_ESDKVERSION = 99; -constexpr int POSIX_ESTART = 100; -constexpr int POSIX_ESTOP = 101; -constexpr int POSIX_EINVALID2MB = 102; -constexpr int POSIX_ELAST = 102; -constexpr int POSIX_EADHOC = 160; -constexpr int POSIX_EINACTIVEDISABLED = 163; -constexpr int POSIX_ENETNODATA = 164; -constexpr int POSIX_ENETDESC = 165; -constexpr int POSIX_ENETDESCTIMEDOUT = 166; -constexpr int POSIX_ENETINTR = 167; -constexpr int POSIX_ERETURN = 205; -constexpr int POSIX_EFPOS = 152; -constexpr int POSIX_ENODATA = 1040; -constexpr int POSIX_ENOSR = 1050; -constexpr int POSIX_ENOSTR = 1051; -constexpr int POSIX_ENOTRECOVERABLE = 1056; -constexpr int POSIX_EOTHER = 1062; -constexpr int POSIX_EOWNERDEAD = 1064; -constexpr int POSIX_ETIME = 1074; - -constexpr int SCE_OK = 0; - -// kernel error codes -constexpr int SCE_KERNEL_ERROR_UNKNOWN = 0x80020000; -constexpr int SCE_KERNEL_ERROR_EPERM = 0x80020001; -constexpr int SCE_KERNEL_ERROR_ENOENT = 0x80020002; -constexpr int SCE_KERNEL_ERROR_ESRCH = 0x80020003; -constexpr int SCE_KERNEL_ERROR_EINTR = 0x80020004; -constexpr int SCE_KERNEL_ERROR_EIO = 0x80020005; -constexpr int SCE_KERNEL_ERROR_ENXIO = 0x80020006; -constexpr int SCE_KERNEL_ERROR_E2BIG = 0x80020007; -constexpr int SCE_KERNEL_ERROR_ENOEXEC = 0x80020008; -constexpr int SCE_KERNEL_ERROR_EBADF = 0x80020009; -constexpr int SCE_KERNEL_ERROR_ECHILD = 0x8002000A; -constexpr int SCE_KERNEL_ERROR_EDEADLK = 0x8002000B; -constexpr int SCE_KERNEL_ERROR_ENOMEM = 0x8002000C; -constexpr int SCE_KERNEL_ERROR_EACCES = 0x8002000D; -constexpr int SCE_KERNEL_ERROR_EFAULT = 0x8002000E; -constexpr int SCE_KERNEL_ERROR_ENOTBLK = 0x8002000F; -constexpr int SCE_KERNEL_ERROR_EBUSY = 0x80020010; -constexpr int SCE_KERNEL_ERROR_EEXIST = 0x80020011; -constexpr int SCE_KERNEL_ERROR_EXDEV = 0x80020012; -constexpr int SCE_KERNEL_ERROR_ENODEV = 0x80020013; -constexpr int SCE_KERNEL_ERROR_ENOTDIR = 0x80020014; -constexpr int SCE_KERNEL_ERROR_EISDIR = 0x80020015; -constexpr int SCE_KERNEL_ERROR_EINVAL = 0x80020016; -constexpr int SCE_KERNEL_ERROR_ENFILE = 0x80020017; -constexpr int SCE_KERNEL_ERROR_EMFILE = 0x80020018; -constexpr int SCE_KERNEL_ERROR_ENOTTY = 0x80020019; -constexpr int SCE_KERNEL_ERROR_ETXTBSY = 0x8002001A; -constexpr int SCE_KERNEL_ERROR_EFBIG = 0x8002001B; -constexpr int SCE_KERNEL_ERROR_ENOSPC = 0x8002001C; -constexpr int SCE_KERNEL_ERROR_ESPIPE = 0x8002001D; -constexpr int SCE_KERNEL_ERROR_EROFS = 0x8002001E; -constexpr int SCE_KERNEL_ERROR_EMLINK = 0x8002001F; -constexpr int SCE_KERNEL_ERROR_EPIPE = 0x80020020; -constexpr int SCE_KERNEL_ERROR_EDOM = 0x80020021; -constexpr int SCE_KERNEL_ERROR_ERANGE = 0x80020022; -constexpr int SCE_KERNEL_ERROR_EAGAIN = 0x80020023; -constexpr int SCE_KERNEL_ERROR_EWOULDBLOCK = 0x80020023; -constexpr int SCE_KERNEL_ERROR_EINPROGRESS = 0x80020024; -constexpr int SCE_KERNEL_ERROR_EALREADY = 0x80020025; -constexpr int SCE_KERNEL_ERROR_ENOTSOCK = 0x80020026; -constexpr int SCE_KERNEL_ERROR_EDESTADDRREQ = 0x80020027; -constexpr int SCE_KERNEL_ERROR_EMSGSIZE = 0x80020028; -constexpr int SCE_KERNEL_ERROR_EPROTOTYPE = 0x80020029; -constexpr int SCE_KERNEL_ERROR_ENOPROTOOPT = 0x8002002A; -constexpr int SCE_KERNEL_ERROR_EPROTONOSUPPORT = 0x8002002B; -constexpr int SCE_KERNEL_ERROR_ESOCKTNOSUPPORT = 0x8002002C; -constexpr int SCE_KERNEL_ERROR_EOPNOTSUPP = 0x8002002D; -constexpr int SCE_KERNEL_ERROR_ENOTSUP = 0x8002002D; -constexpr int SCE_KERNEL_ERROR_EPFNOSUPPORT = 0x8002002E; -constexpr int SCE_KERNEL_ERROR_EAFNOSUPPORT = 0x8002002F; -constexpr int SCE_KERNEL_ERROR_EADDRINUSE = 0x80020030; -constexpr int SCE_KERNEL_ERROR_EADDRNOTAVAIL = 0x80020031; -constexpr int SCE_KERNEL_ERROR_ENETDOWN = 0x80020032; -constexpr int SCE_KERNEL_ERROR_ENETUNREACH = 0x80020033; -constexpr int SCE_KERNEL_ERROR_ENETRESET = 0x80020034; -constexpr int SCE_KERNEL_ERROR_ECONNABORTED = 0x80020035; -constexpr int SCE_KERNEL_ERROR_ECONNRESET = 0x80020036; -constexpr int SCE_KERNEL_ERROR_ENOBUFS = 0x80020037; -constexpr int SCE_KERNEL_ERROR_EISCONN = 0x80020038; -constexpr int SCE_KERNEL_ERROR_ENOTCONN = 0x80020039; -constexpr int SCE_KERNEL_ERROR_ESHUTDOWN = 0x8002003A; -constexpr int SCE_KERNEL_ERROR_ETOOMANYREFS = 0x8002003B; -constexpr int SCE_KERNEL_ERROR_ETIMEDOUT = 0x8002003C; -constexpr int SCE_KERNEL_ERROR_ECONNREFUSED = 0x8002003D; -constexpr int SCE_KERNEL_ERROR_ELOOP = 0x8002003E; -constexpr int SCE_KERNEL_ERROR_ENAMETOOLONG = 0x8002003F; -constexpr int SCE_KERNEL_ERROR_EHOSTDOWN = 0x80020040; -constexpr int SCE_KERNEL_ERROR_EHOSTUNREACH = 0x80020041; -constexpr int SCE_KERNEL_ERROR_ENOTEMPTY = 0x80020042; -constexpr int SCE_KERNEL_ERROR_EPROCLIM = 0x80020043; -constexpr int SCE_KERNEL_ERROR_EUSERS = 0x80020044; -constexpr int SCE_KERNEL_ERROR_EDQUOT = 0x80020045; -constexpr int SCE_KERNEL_ERROR_ESTALE = 0x80020046; -constexpr int SCE_KERNEL_ERROR_EREMOTE = 0x80020047; -constexpr int SCE_KERNEL_ERROR_EBADRPC = 0x80020048; -constexpr int SCE_KERNEL_ERROR_ERPCMISMATCH = 0x80020049; -constexpr int SCE_KERNEL_ERROR_EPROGUNAVAIL = 0x8002004A; -constexpr int SCE_KERNEL_ERROR_EPROGMISMATCH = 0x8002004B; -constexpr int SCE_KERNEL_ERROR_EPROCUNAVAIL = 0x8002004C; -constexpr int SCE_KERNEL_ERROR_ENOLCK = 0x8002004D; -constexpr int SCE_KERNEL_ERROR_ENOSYS = 0x8002004E; -constexpr int SCE_KERNEL_ERROR_EFTYPE = 0x8002004F; -constexpr int SCE_KERNEL_ERROR_EAUTH = 0x80020050; -constexpr int SCE_KERNEL_ERROR_ENEEDAUTH = 0x80020051; -constexpr int SCE_KERNEL_ERROR_EIDRM = 0x80020052; -constexpr int SCE_KERNEL_ERROR_ENOMSG = 0x80020053; -constexpr int SCE_KERNEL_ERROR_EOVERFLOW = 0x80020054; -constexpr int SCE_KERNEL_ERROR_ECANCELED = 0x80020055; -constexpr int SCE_KERNEL_ERROR_EILSEQ = 0x80020056; -constexpr int SCE_KERNEL_ERROR_ENOATTR = 0x80020057; -constexpr int SCE_KERNEL_ERROR_EDOOFUS = 0x80020058; -constexpr int SCE_KERNEL_ERROR_EBADMSG = 0x80020059; -constexpr int SCE_KERNEL_ERROR_EMULTIHOP = 0x8002005A; -constexpr int SCE_KERNEL_ERROR_ENOLINK = 0x8002005B; -constexpr int SCE_KERNEL_ERROR_EPROTO = 0x8002005C; -constexpr int SCE_KERNEL_ERROR_ENOTCAPABLE = 0x8002005D; -constexpr int SCE_KERNEL_ERROR_ECAPMODE = 0x8002005E; -constexpr int SCE_KERNEL_ERROR_ENOBLK = 0x8002005F; -constexpr int SCE_KERNEL_ERROR_EICV = 0x80020060; -constexpr int SCE_KERNEL_ERROR_ENOPLAYGOENT = 0x80020061; -constexpr int SCE_KERNEL_ERROR_EREVOKE = 0x80020062; -constexpr int SCE_KERNEL_ERROR_ESDKVERSION = 0x80020063; -constexpr int SCE_KERNEL_ERROR_ESTART = 0x80020064; -constexpr int SCE_KERNEL_ERROR_ESTOP = 0x80020065; - -// videoOut -constexpr int SCE_VIDEO_OUT_ERROR_INVALID_VALUE = 0x80290001; // invalid argument -constexpr int SCE_VIDEO_OUT_ERROR_INVALID_ADDRESS = 0x80290002; // invalid addresses -constexpr int SCE_VIDEO_OUT_ERROR_INVALID_TILING_MODE = 0x80290007; // invalid tiling mode -constexpr int SCE_VIDEO_OUT_ERROR_INVALID_ASPECT_RATIO = 0x80290008; // invalid aspect ration -constexpr int SCE_VIDEO_OUT_ERROR_RESOURCE_BUSY = 0x80290009; // already opened -constexpr int SCE_VIDEO_OUT_ERROR_INVALID_INDEX = 0x8029000A; // invalid buffer index -constexpr int SCE_VIDEO_OUT_ERROR_INVALID_HANDLE = 0x8029000B; // invalid handle -constexpr int SCE_VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE = 0x8029000C; // Invalid event queue -constexpr int SCE_VIDEO_OUT_ERROR_SLOT_OCCUPIED = 0x80290010; // slot already used -constexpr int SCE_VIDEO_OUT_ERROR_FLIP_QUEUE_FULL = 0x80290012; // flip queue is full -constexpr int SCE_VIDEO_OUT_ERROR_INVALID_OPTION = 0x8029001A; // Invalid buffer attribute option - // Generic constexpr int ORBIS_OK = 0x00000000; constexpr int ORBIS_FAIL = 0xFFFFFFFF; - -// Libkernel library -constexpr int ORBIS_KERNEL_ERROR_UNKNOWN = 0x80020000; -constexpr int ORBIS_KERNEL_ERROR_EPERM = 0x80020001; -constexpr int ORBIS_KERNEL_ERROR_ENOENT = 0x80020002; -constexpr int ORBIS_KERNEL_ERROR_ESRCH = 0x80020003; -constexpr int ORBIS_KERNEL_ERROR_EINTR = 0x80020004; -constexpr int ORBIS_KERNEL_ERROR_EIO = 0x80020005; -constexpr int ORBIS_KERNEL_ERROR_ENXIO = 0x80020006; -constexpr int ORBIS_KERNEL_ERROR_E2BIG = 0x80020007; -constexpr int ORBIS_KERNEL_ERROR_ENOEXEC = 0x80020008; -constexpr int ORBIS_KERNEL_ERROR_EBADF = 0x80020009; -constexpr int ORBIS_KERNEL_ERROR_ECHILD = 0x8002000A; -constexpr int ORBIS_KERNEL_ERROR_EDEADLK = 0x8002000B; -constexpr int ORBIS_KERNEL_ERROR_ENOMEM = 0x8002000C; -constexpr int ORBIS_KERNEL_ERROR_EACCES = 0x8002000D; -constexpr int ORBIS_KERNEL_ERROR_EFAULT = 0x8002000E; -constexpr int ORBIS_KERNEL_ERROR_ENOTBLK = 0x8002000F; -constexpr int ORBIS_KERNEL_ERROR_EBUSY = 0x80020010; -constexpr int ORBIS_KERNEL_ERROR_EEXIST = 0x80020011; -constexpr int ORBIS_KERNEL_ERROR_EXDEV = 0x80020012; -constexpr int ORBIS_KERNEL_ERROR_ENODEV = 0x80020013; -constexpr int ORBIS_KERNEL_ERROR_ENOTDIR = 0x80020014; -constexpr int ORBIS_KERNEL_ERROR_EISDIR = 0x80020015; -constexpr int ORBIS_KERNEL_ERROR_EINVAL = 0x80020016; -constexpr int ORBIS_KERNEL_ERROR_ENFILE = 0x80020017; -constexpr int ORBIS_KERNEL_ERROR_EMFILE = 0x80020018; -constexpr int ORBIS_KERNEL_ERROR_ENOTTY = 0x80020019; -constexpr int ORBIS_KERNEL_ERROR_ETXTBSY = 0x8002001A; -constexpr int ORBIS_KERNEL_ERROR_EFBIG = 0x8002001B; -constexpr int ORBIS_KERNEL_ERROR_ENOSPC = 0x8002001C; -constexpr int ORBIS_KERNEL_ERROR_ESPIPE = 0x8002001D; -constexpr int ORBIS_KERNEL_ERROR_EROFS = 0x8002001E; -constexpr int ORBIS_KERNEL_ERROR_EMLINK = 0x8002001F; -constexpr int ORBIS_KERNEL_ERROR_EPIPE = 0x80020020; -constexpr int ORBIS_KERNEL_ERROR_EDOM = 0x80020021; -constexpr int ORBIS_KERNEL_ERROR_ERANGE = 0x80020022; -constexpr int ORBIS_KERNEL_ERROR_EAGAIN = 0x80020023; -constexpr int ORBIS_KERNEL_ERROR_EWOULDBLOCK = 0x80020023; -constexpr int ORBIS_KERNEL_ERROR_EINPROGRESS = 0x80020024; -constexpr int ORBIS_KERNEL_ERROR_EALREADY = 0x80020025; -constexpr int ORBIS_KERNEL_ERROR_ENOTSOCK = 0x80020026; -constexpr int ORBIS_KERNEL_ERROR_EDESTADDRREQ = 0x80020027; -constexpr int ORBIS_KERNEL_ERROR_EMSGSIZE = 0x80020028; -constexpr int ORBIS_KERNEL_ERROR_EPROTOTYPE = 0x80020029; -constexpr int ORBIS_KERNEL_ERROR_ENOPROTOOPT = 0x8002002A; -constexpr int ORBIS_KERNEL_ERROR_EPROTONOSUPPORT = 0x8002002B; -constexpr int ORBIS_KERNEL_ERROR_ESOCKTNOSUPPORT = 0x8002002C; -constexpr int ORBIS_KERNEL_ERROR_ENOTSUP = 0x8002002D; -constexpr int ORBIS_KERNEL_ERROR_EOPNOTSUPP = 0x8002002D; -constexpr int ORBIS_KERNEL_ERROR_EPFNOSUPPORT = 0x8002002E; -constexpr int ORBIS_KERNEL_ERROR_EAFNOSUPPORT = 0x8002002F; -constexpr int ORBIS_KERNEL_ERROR_EADDRINUSE = 0x80020030; -constexpr int ORBIS_KERNEL_ERROR_EADDRNOTAVAIL = 0x80020031; -constexpr int ORBIS_KERNEL_ERROR_ENETDOWN = 0x80020032; -constexpr int ORBIS_KERNEL_ERROR_ENETUNREACH = 0x80020033; -constexpr int ORBIS_KERNEL_ERROR_ENETRESET = 0x80020034; -constexpr int ORBIS_KERNEL_ERROR_ECONNABORTED = 0x80020035; -constexpr int ORBIS_KERNEL_ERROR_ECONNRESET = 0x80020036; -constexpr int ORBIS_KERNEL_ERROR_ENOBUFS = 0x80020037; -constexpr int ORBIS_KERNEL_ERROR_EISCONN = 0x80020038; -constexpr int ORBIS_KERNEL_ERROR_ENOTCONN = 0x80020039; -constexpr int ORBIS_KERNEL_ERROR_ESHUTDOWN = 0x8002003A; -constexpr int ORBIS_KERNEL_ERROR_ETOOMANYREFS = 0x8002003B; -constexpr int ORBIS_KERNEL_ERROR_ETIMEDOUT = 0x8002003C; -constexpr int ORBIS_KERNEL_ERROR_ECONNREFUSED = 0x8002003D; -constexpr int ORBIS_KERNEL_ERROR_ELOOP = 0x8002003E; -constexpr int ORBIS_KERNEL_ERROR_ENAMETOOLONG = 0x8002003F; -constexpr int ORBIS_KERNEL_ERROR_EHOSTDOWN = 0x80020040; -constexpr int ORBIS_KERNEL_ERROR_EHOSTUNREACH = 0x80020041; -constexpr int ORBIS_KERNEL_ERROR_ENOTEMPTY = 0x80020042; -constexpr int ORBIS_KERNEL_ERROR_EPROCLIM = 0x80020043; -constexpr int ORBIS_KERNEL_ERROR_EUSERS = 0x80020044; -constexpr int ORBIS_KERNEL_ERROR_EDQUOT = 0x80020045; -constexpr int ORBIS_KERNEL_ERROR_ESTALE = 0x80020046; -constexpr int ORBIS_KERNEL_ERROR_EREMOTE = 0x80020047; -constexpr int ORBIS_KERNEL_ERROR_EBADRPC = 0x80020048; -constexpr int ORBIS_KERNEL_ERROR_ERPCMISMATCH = 0x80020049; -constexpr int ORBIS_KERNEL_ERROR_EPROGUNAVAIL = 0x8002004A; -constexpr int ORBIS_KERNEL_ERROR_EPROGMISMATCH = 0x8002004B; -constexpr int ORBIS_KERNEL_ERROR_EPROCUNAVAIL = 0x8002004C; -constexpr int ORBIS_KERNEL_ERROR_ENOLCK = 0x8002004D; -constexpr int ORBIS_KERNEL_ERROR_ENOSYS = 0x8002004E; -constexpr int ORBIS_KERNEL_ERROR_EFTYPE = 0x8002004F; -constexpr int ORBIS_KERNEL_ERROR_EAUTH = 0x80020050; -constexpr int ORBIS_KERNEL_ERROR_ENEEDAUTH = 0x80020051; -constexpr int ORBIS_KERNEL_ERROR_EIDRM = 0x80020052; -constexpr int ORBIS_KERNEL_ERROR_ENOMSG = 0x80020053; -constexpr int ORBIS_KERNEL_ERROR_EOVERFLOW = 0x80020054; -constexpr int ORBIS_KERNEL_ERROR_ECANCELED = 0x80020055; -constexpr int ORBIS_KERNEL_ERROR_EILSEQ = 0x80020056; -constexpr int ORBIS_KERNEL_ERROR_ENOATTR = 0x80020057; -constexpr int ORBIS_KERNEL_ERROR_EDOOFUS = 0x80020058; -constexpr int ORBIS_KERNEL_ERROR_EBADMSG = 0x80020059; -constexpr int ORBIS_KERNEL_ERROR_EMULTIHOP = 0x8002005A; -constexpr int ORBIS_KERNEL_ERROR_ENOLINK = 0x8002005B; -constexpr int ORBIS_KERNEL_ERROR_EPROTO = 0x8002005C; -constexpr int ORBIS_KERNEL_ERROR_ENOTCAPABLE = 0x8002005D; -constexpr int ORBIS_KERNEL_ERROR_ECAPMODE = 0x8002005E; -constexpr int ORBIS_KERNEL_ERROR_ENOBLK = 0x8002005F; -constexpr int ORBIS_KERNEL_ERROR_EICV = 0x80020060; -constexpr int ORBIS_KERNEL_ERROR_ENOPLAYGOENT = 0x80020061; - -// AudioOut library -constexpr int ORBIS_AUDIO_OUT_ERROR_NOT_OPENED = 0x80260001; -constexpr int ORBIS_AUDIO_OUT_ERROR_BUSY = 0x80260002; -constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_PORT = 0x80260003; -constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_POINTER = 0x80260004; -constexpr int ORBIS_AUDIO_OUT_ERROR_PORT_FULL = 0x80260005; -constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_SIZE = 0x80260006; -constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_FORMAT = 0x80260007; -constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_SAMPLE_FREQ = 0x80260008; -constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_VOLUME = 0x80260009; -constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_PORT_TYPE = 0x8026000A; -constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_CONF_TYPE = 0x8026000C; -constexpr int ORBIS_AUDIO_OUT_ERROR_OUT_OF_MEMORY = 0x8026000D; -constexpr int ORBIS_AUDIO_OUT_ERROR_ALREADY_INIT = 0x8026000E; -constexpr int ORBIS_AUDIO_OUT_ERROR_NOT_INIT = 0x8026000F; -constexpr int ORBIS_AUDIO_OUT_ERROR_MEMORY = 0x80260010; -constexpr int ORBIS_AUDIO_OUT_ERROR_SYSTEM_RESOURCE = 0x80260011; -constexpr int ORBIS_AUDIO_OUT_ERROR_TRANS_EVENT = 0x80260012; -constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_FLAG = 0x80260013; -constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_MIXLEVEL = 0x80260014; -constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_ARG = 0x80260015; -constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_PARAM = 0x80260016; -constexpr int ORBIS_AUDIO_OUT_ERROR_MASTERING_FATAL = 0x80260200; -constexpr int ORBIS_AUDIO_OUT_ERROR_MASTERING_INVALID_API_PARAM = 0x80260201; -constexpr int ORBIS_AUDIO_OUT_ERROR_MASTERING_INVALID_CONFIG = 0x80260202; -constexpr int ORBIS_AUDIO_OUT_ERROR_MASTERING_NOT_INITIALIZED = 0x80260203; -constexpr int ORBIS_AUDIO_OUT_ERROR_MASTERING_INVALID_STATES_ID = 0x80260204; - -// VideoOut library -constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_VALUE = 0x80290001; -constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_ADDRESS = 0x80290002; -constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_PIXEL_FORMAT = 0x80290003; -constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_PITCH = 0x80290004; -constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_RESOLUTION = 0x80290005; -constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_FLIP_MODE = 0x80290006; -constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_TILING_MODE = 0x80290007; -constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_ASPECT_RATIO = 0x80290008; -constexpr int ORBIS_VIDEO_OUT_ERROR_RESOURCE_BUSY = 0x80290009; -constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_INDEX = 0x8029000A; -constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE = 0x8029000B; -constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE = 0x8029000C; -constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT = 0x8029000D; -constexpr int ORBIS_VIDEO_OUT_ERROR_NO_EMPTY_SLOT = 0x8029000F; -constexpr int ORBIS_VIDEO_OUT_ERROR_SLOT_OCCUPIED = 0x80290010; -constexpr int ORBIS_VIDEO_OUT_ERROR_FLIP_QUEUE_FULL = 0x80290012; -constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_MEMORY = 0x80290013; -constexpr int ORBIS_VIDEO_OUT_ERROR_MEMORY_NOT_PHYSICALLY_CONTIGUOUS = 0x80290014; -constexpr int ORBIS_VIDEO_OUT_ERROR_MEMORY_INVALID_ALIGNMENT = 0x80290015; -constexpr int ORBIS_VIDEO_OUT_ERROR_UNSUPPORTED_OUTPUT_MODE = 0x80290016; -constexpr int ORBIS_VIDEO_OUT_ERROR_OVERFLOW = 0x80290017; -constexpr int ORBIS_VIDEO_OUT_ERROR_NO_DEVICE = 0x80290018; -constexpr int ORBIS_VIDEO_OUT_ERROR_UNAVAILABLE_OUTPUT_MODE = 0x80290019; -constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_OPTION = 0x8029001A; -constexpr int ORBIS_VIDEO_OUT_ERROR_UNKNOWN = 0x802900FE; -constexpr int ORBIS_VIDEO_OUT_ERROR_FATAL = 0x802900FF; -constexpr int ORBIS_VIDEO_OUT_ERROR_ENOMEM = 0x8029100C; - -// Pad library -constexpr int ORBIS_PAD_ERROR_INVALID_ARG = 0x80920001; -constexpr int ORBIS_PAD_ERROR_INVALID_PORT = 0x80920002; -constexpr int ORBIS_PAD_ERROR_INVALID_HANDLE = 0x80920003; -constexpr int ORBIS_PAD_ERROR_ALREADY_OPENED = 0x80920004; -constexpr int ORBIS_PAD_ERROR_NOT_INITIALIZED = 0x80920005; -constexpr int ORBIS_PAD_ERROR_INVALID_LIGHTBAR_SETTING = 0x80920006; -constexpr int ORBIS_PAD_ERROR_DEVICE_NOT_CONNECTED = 0x80920007; -constexpr int ORBIS_PAD_ERROR_DEVICE_NO_HANDLE = 0x80920008; -constexpr int ORBIS_PAD_ERROR_FATAL = 0x809200FF; -constexpr int ORBIS_PAD_ERROR_NOT_PERMITTED = 0x80920101; -constexpr int ORBIS_PAD_ERROR_INVALID_BUFFER_LENGTH = 0x80920102; -constexpr int ORBIS_PAD_ERROR_INVALID_REPORT_LENGTH = 0x80920103; -constexpr int ORBIS_PAD_ERROR_INVALID_REPORT_ID = 0x80920104; -constexpr int ORBIS_PAD_ERROR_SEND_AGAIN = 0x80920105; - -// UserService library -constexpr int ORBIS_USER_SERVICE_ERROR_INTERNAL = 0x80960001; -constexpr int ORBIS_USER_SERVICE_ERROR_NOT_INITIALIZED = 0x80960002; -constexpr int ORBIS_USER_SERVICE_ERROR_ALREADY_INITIALIZED = 0x80960003; -constexpr int ORBIS_USER_SERVICE_ERROR_NO_MEMORY = 0x80960004; -constexpr int ORBIS_USER_SERVICE_ERROR_INVALID_ARGUMENT = 0x80960005; -constexpr int ORBIS_USER_SERVICE_ERROR_OPERATION_NOT_SUPPORTED = 0x80960006; -constexpr int ORBIS_USER_SERVICE_ERROR_NO_EVENT = 0x80960007; -constexpr int ORBIS_USER_SERVICE_ERROR_NOT_LOGGED_IN = 0x80960009; -constexpr int ORBIS_USER_SERVICE_ERROR_BUFFER_TOO_SHORT = 0x8096000A; - -// SystemService library -constexpr int ORBIS_SYSTEM_SERVICE_ERROR_PARAMETER = 0x80A10003; -constexpr int ORBIS_SYSTEM_SERVICE_ERROR_NO_EVENT = 0x80A10004; - -// NpTrophy library -constexpr int ORBIS_NP_TROPHY_ERROR_UNKNOWN = 0x80551600; -constexpr int ORBIS_NP_TROPHY_ERROR_NOT_INITIALIZED = 0x80551601; -constexpr int ORBIS_NP_TROPHY_ERROR_ALREADY_INITIALIZED = 0x80551602; -constexpr int ORBIS_NP_TROPHY_ERROR_OUT_OF_MEMORY = 0x80551603; -constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT = 0x80551604; -constexpr int ORBIS_NP_TROPHY_ERROR_INSUFFICIENT_BUFFER = 0x80551605; -constexpr int ORBIS_NP_TROPHY_ERROR_EXCEEDS_MAX = 0x80551606; -constexpr int ORBIS_NP_TROPHY_ERROR_ABORT = 0x80551607; -constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE = 0x80551608; -constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT = 0x80551609; -constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_TROPHY_ID = 0x8055160A; -constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_GROUP_ID = 0x8055160B; -constexpr int ORBIS_NP_TROPHY_ERROR_TROPHY_ALREADY_UNLOCKED = 0x8055160C; -constexpr int ORBIS_NP_TROPHY_ERROR_PLATINUM_CANNOT_UNLOCK = 0x8055160D; -constexpr int ORBIS_NP_TROPHY_ERROR_ACCOUNTID_NOT_MATCH = 0x8055160E; -constexpr int ORBIS_NP_TROPHY_ERROR_NOT_REGISTERED = 0x8055160F; -constexpr int ORBIS_NP_TROPHY_ERROR_ALREADY_REGISTERED = 0x80551610; -constexpr int ORBIS_NP_TROPHY_ERROR_BROKEN_DATA = 0x80551611; -constexpr int ORBIS_NP_TROPHY_ERROR_INSUFFICIENT_SPACE = 0x80551612; -constexpr int ORBIS_NP_TROPHY_ERROR_CONTEXT_ALREADY_EXISTS = 0x80551613; -constexpr int ORBIS_NP_TROPHY_ERROR_ICON_FILE_NOT_FOUND = 0x80551614; -constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_TRP_FILE_FORMAT = 0x80551616; -constexpr int ORBIS_NP_TROPHY_ERROR_UNSUPPORTED_TRP_FILE = 0x80551617; -constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_TROPHY_CONF_FORMAT = 0x80551618; -constexpr int ORBIS_NP_TROPHY_ERROR_UNSUPPORTED_TROPHY_CONF = 0x80551619; -constexpr int ORBIS_NP_TROPHY_ERROR_TROPHY_NOT_UNLOCKED = 0x8055161A; -constexpr int ORBIS_NP_TROPHY_ERROR_USER_NOT_FOUND = 0x8055161C; -constexpr int ORBIS_NP_TROPHY_ERROR_USER_NOT_LOGGED_IN = 0x8055161D; -constexpr int ORBIS_NP_TROPHY_ERROR_CONTEXT_USER_LOGOUT = 0x8055161E; -constexpr int ORBIS_NP_TROPHY_ERROR_USE_TRP_FOR_DEVELOPMENT = 0x8055161F; -constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_NP_SERVICE_LABEL = 0x80551621; -constexpr int ORBIS_NP_TROPHY_ERROR_NOT_SUPPORTED = 0x80551622; -constexpr int ORBIS_NP_TROPHY_ERROR_CONTEXT_EXCEEDS_MAX = 0x80551623; -constexpr int ORBIS_NP_TROPHY_ERROR_HANDLE_EXCEEDS_MAX = 0x80551624; -constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_USER_ID = 0x80551625; -constexpr int ORBIS_NP_TROPHY_ERROR_TITLE_CONF_NOT_INSTALLED = 0x80551626; -constexpr int ORBIS_NP_TROPHY_ERROR_BROKEN_TITLE_CONF = 0x80551627; -constexpr int ORBIS_NP_TROPHY_ERROR_INCONSISTENT_TITLE_CONF = 0x80551628; -constexpr int ORBIS_NP_TROPHY_ERROR_TITLE_BACKGROUND = 0x80551629; -constexpr int ORBIS_NP_TROPHY_ERROR_SCREENSHOT_DISABLED = 0x8055162B; -constexpr int ORBIS_NP_TROPHY_ERROR_SCREENSHOT_DISPLAY_BUFFER_NOT_IN_USE = 0x8055162D; - -// AvPlayer library -constexpr int ORBIS_AVPLAYER_ERROR_INVALID_PARAMS = 0x806A0001; -constexpr int ORBIS_AVPLAYER_ERROR_OPERATION_FAILED = 0x806A0002; -constexpr int ORBIS_AVPLAYER_ERROR_NO_MEMORY = 0x806A0003; -constexpr int ORBIS_AVPLAYER_ERROR_NOT_SUPPORTED = 0x806A0004; -constexpr int ORBIS_AVPLAYER_ERROR_WAR_FILE_NONINTERLEAVED = 0x806A00A0; -constexpr int ORBIS_AVPLAYER_ERROR_WAR_LOOPING_BACK = 0x806A00A1; -constexpr int ORBIS_AVPLAYER_ERROR_WAR_JUMP_COMPLETE = 0x806A00A3; -constexpr int ORBIS_AVPLAYER_ERROR_INFO_MARLIN_ENCRY = 0x806A00B0; -constexpr int ORBIS_AVPLAYER_ERROR_INFO_PLAYREADY_ENCRY = 0x806A00B4; -constexpr int ORBIS_AVPLAYER_ERROR_INFO_AES_ENCRY = 0x806A00B5; -constexpr int ORBIS_AVPLAYER_ERROR_INFO_OTHER_ENCRY = 0x806A00BF; - -// AppContent library -constexpr int ORBIS_APP_CONTENT_ERROR_PARAMETER = 0x80D90002; -constexpr int ORBIS_APP_CONTENT_ERROR_DRM_NO_ENTITLEMENT = 0x80D90007; -constexpr int ORBIS_APP_CONTENT_ERROR_NOT_FOUND = 0x80D90005; - -// Fiber library -constexpr int ORBIS_FIBER_ERROR_NULL = 0x80590001; -constexpr int ORBIS_FIBER_ERROR_ALIGNMENT = 0x80590002; -constexpr int ORBIS_FIBER_ERROR_RANGE = 0x80590003; -constexpr int ORBIS_FIBER_ERROR_INVALID = 0x80590004; -constexpr int ORBIS_FIBER_ERROR_PERMISSION = 0x80590005; -constexpr int ORBIS_FIBER_ERROR_STATE = 0x80590006; - -// ImeDialog library -constexpr int ORBIS_ERROR_DIALOG_ERROR_NOT_INITIALIZED = 0x80ED0001; -constexpr int ORBIS_ERROR_DIALOG_ERROR_ALREADY_INITIALIZED = 0x80ED0002; -constexpr int ORBIS_ERROR_DIALOG_ERROR_PARAM_INVALID = 0x80ED0003; -constexpr int ORBIS_ERROR_DIALOG_ERROR_UNEXPECTED_FATAL = 0x80ED0004; -constexpr int ORBIS_ERROR_DIALOG_ERROR_INVALID_STATE = 0x80ED0005; -constexpr int ORBIS_ERROR_DIALOG_ERROR_SERVICE_BUSY = 0x80ED0006; -constexpr int ORBIS_ERROR_DIALOG_ERROR_INVALID_USER_ID = 0x80ED0007; - -// Ime library -constexpr int ORBIS_IME_ERROR_BUSY = 0x80BC0001; -constexpr int ORBIS_IME_ERROR_NOT_OPENED = 0x80BC0002; -constexpr int ORBIS_IME_ERROR_NO_MEMORY = 0x80BC0003; -constexpr int ORBIS_IME_ERROR_CONNECTION_FAILED = 0x80BC0004; -constexpr int ORBIS_IME_ERROR_TOO_MANY_REQUESTS = 0x80BC0005; -constexpr int ORBIS_IME_ERROR_INVALID_TEXT = 0x80BC0006; -constexpr int ORBIS_IME_ERROR_EVENT_OVERFLOW = 0x80BC0007; -constexpr int ORBIS_IME_ERROR_NOT_ACTIVE = 0x80BC0008; -constexpr int ORBIS_IME_ERROR_IME_SUSPENDING = 0x80BC0009; -constexpr int ORBIS_IME_ERROR_DEVICE_IN_USE = 0x80BC000A; -constexpr int ORBIS_IME_ERROR_INVALID_USER_ID = 0x80BC0010; -constexpr int ORBIS_IME_ERROR_INVALID_TYPE = 0x80BC0011; -constexpr int ORBIS_IME_ERROR_INVALID_SUPPORTED_LANGUAGES = 0x80BC0012; -constexpr int ORBIS_IME_ERROR_INVALID_ENTER_LABEL = 0x80BC0013; -constexpr int ORBIS_IME_ERROR_INVALID_INPUT_METHOD = 0x80BC0014; -constexpr int ORBIS_IME_ERROR_INVALID_OPTION = 0x80BC0015; -constexpr int ORBIS_IME_ERROR_INVALID_MAX_TEXT_LENGTH = 0x80BC0016; -constexpr int ORBIS_IME_ERROR_INVALID_INPUT_TEXT_BUFFER = 0x80BC0017; -constexpr int ORBIS_IME_ERROR_INVALID_POSX = 0x80BC0018; -constexpr int ORBIS_IME_ERROR_INVALID_POSY = 0x80BC0019; -constexpr int ORBIS_IME_ERROR_INVALID_HORIZONTAL_ALIGNMENT = 0x80BC001A; -constexpr int ORBIS_IME_ERROR_INVALID_VERTICAL_ALIGNMENT = 0x80BC001B; -constexpr int ORBIS_IME_ERROR_INVALID_EXTENDED = 0x80BC001C; -constexpr int ORBIS_IME_ERROR_INVALID_KEYBOARD_TYPE = 0x80BC001D; -constexpr int ORBIS_IME_ERROR_INVALID_WORK = 0x80BC0020; -constexpr int ORBIS_IME_ERROR_INVALID_ARG = 0x80BC0021; -constexpr int ORBIS_IME_ERROR_INVALID_HANDLER = 0x80BC0022; -constexpr int ORBIS_IME_ERROR_NO_RESOURCE_ID = 0x80BC0023; -constexpr int ORBIS_IME_ERROR_INVALID_MODE = 0x80BC0024; -constexpr int ORBIS_IME_ERROR_INVALID_PARAM = 0x80BC0030; -constexpr int ORBIS_IME_ERROR_INVALID_ADDRESS = 0x80BC0031; -constexpr int ORBIS_IME_ERROR_INVALID_RESERVED = 0x80BC0032; -constexpr int ORBIS_IME_ERROR_INVALID_TIMING = 0x80BC0033; -constexpr int ORBIS_IME_ERROR_INTERNAL = 0x80BC00FF; - -// Videodec2 library -constexpr int ORBIS_VIDEODEC2_ERROR_API_FAIL = 0x811D0100; -constexpr int ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE = 0x811D0101; -constexpr int ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER = 0x811D0102; -constexpr int ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE = 0x811D0103; -constexpr int ORBIS_VIDEODEC2_ERROR_MEMORY_SIZE = 0x811D0104; -constexpr int ORBIS_VIDEODEC2_ERROR_MEMORY_POINTER = 0x811D0105; -constexpr int ORBIS_VIDEODEC2_ERROR_FRAME_BUFFER_SIZE = 0x811D0106; -constexpr int ORBIS_VIDEODEC2_ERROR_FRAME_BUFFER_POINTER = 0x811D0107; -constexpr int ORBIS_VIDEODEC2_ERROR_FRAME_BUFFER_ALIGNMENT = 0x811D0108; -constexpr int ORBIS_VIDEODEC2_ERROR_NOT_ONION_MEMORY = 0x811D0109; -constexpr int ORBIS_VIDEODEC2_ERROR_NOT_GARLIC_MEMORY = 0x811D010A; -constexpr int ORBIS_VIDEODEC2_ERROR_NOT_DIRECT_MEMORY = 0x811D010B; -constexpr int ORBIS_VIDEODEC2_ERROR_MEMORY_INFO = 0x811D010C; -constexpr int ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_SIZE = 0x811D010D; -constexpr int ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_POINTER = 0x811D010E; -constexpr int ORBIS_VIDEODEC2_ERROR_OUTPUT_INFO = 0x811D010F; -constexpr int ORBIS_VIDEODEC2_ERROR_COMPUTE_QUEUE = 0x811D0110; -constexpr int ORBIS_VIDEODEC2_ERROR_FATAL_STATE = 0x811D0111; -constexpr int ORBIS_VIDEODEC2_ERROR_PRESET_VALUE = 0x811D0112; -constexpr int ORBIS_VIDEODEC2_ERROR_CONFIG_INFO = 0x811D0200; -constexpr int ORBIS_VIDEODEC2_ERROR_COMPUTE_PIPE_ID = 0x811D0201; -constexpr int ORBIS_VIDEODEC2_ERROR_COMPUTE_QUEUE_ID = 0x811D0202; -constexpr int ORBIS_VIDEODEC2_ERROR_RESOURCE_TYPE = 0x811D0203; -constexpr int ORBIS_VIDEODEC2_ERROR_CODEC_TYPE = 0x811D0204; -constexpr int ORBIS_VIDEODEC2_ERROR_PROFILE_LEVEL = 0x811D0205; -constexpr int ORBIS_VIDEODEC2_ERROR_PIPELINE_DEPTH = 0x811D0206; -constexpr int ORBIS_VIDEODEC2_ERROR_AFFINITY_MASK = 0x811D0207; -constexpr int ORBIS_VIDEODEC2_ERROR_THREAD_PRIORITY = 0x811D0208; -constexpr int ORBIS_VIDEODEC2_ERROR_DPB_FRAME_COUNT = 0x811D0209; -constexpr int ORBIS_VIDEODEC2_ERROR_FRAME_WIDTH_HEIGHT = 0x811D020A; -constexpr int ORBIS_VIDEODEC2_ERROR_EXTRA_CONFIG_INFO = 0x811D020B; -constexpr int ORBIS_VIDEODEC2_ERROR_NEW_SEQUENCE = 0x811D0300; -constexpr int ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT = 0x811D0301; -constexpr int ORBIS_VIDEODEC2_ERROR_OVERSIZE_DECODE = 0x811D0302; -constexpr int ORBIS_VIDEODEC2_ERROR_INVALID_SEQUENCE = 0x811D0303; -constexpr int ORBIS_VIDEODEC2_ERROR_FATAL_STREAM = 0x811D0304; - -// Videodec library -constexpr int ORBIS_VIDEODEC_ERROR_API_FAIL = 0x80C10000; -constexpr int ORBIS_VIDEODEC_ERROR_CODEC_TYPE = 0x80C10001; -constexpr int ORBIS_VIDEODEC_ERROR_STRUCT_SIZE = 0x80C10002; -constexpr int ORBIS_VIDEODEC_ERROR_HANDLE = 0x80C10003; -constexpr int ORBIS_VIDEODEC_ERROR_CPU_MEMORY_SIZE = 0x80C10004; -constexpr int ORBIS_VIDEODEC_ERROR_CPU_MEMORY_POINTER = 0x80C10005; -constexpr int ORBIS_VIDEODEC_ERROR_CPU_GPU_MEMORY_SIZE = 0x80C10006; -constexpr int ORBIS_VIDEODEC_ERROR_CPU_GPU_MEMORY_POINTER = 0x80C10007; -constexpr int ORBIS_VIDEODEC_ERROR_SHADER_CONTEXT_POINTER = 0x80C10008; -constexpr int ORBIS_VIDEODEC_ERROR_AU_SIZE = 0x80C10009; -constexpr int ORBIS_VIDEODEC_ERROR_AU_POINTER = 0x80C1000A; -constexpr int ORBIS_VIDEODEC_ERROR_FRAME_BUFFER_SIZE = 0x80C1000B; -constexpr int ORBIS_VIDEODEC_ERROR_FRAME_BUFFER_POINTER = 0x80C1000C; -constexpr int ORBIS_VIDEODEC_ERROR_FRAME_BUFFER_ALIGNMENT = 0x80C1000D; -constexpr int ORBIS_VIDEODEC_ERROR_CONFIG_INFO = 0x80C1000E; -constexpr int ORBIS_VIDEODEC_ERROR_ARGUMENT_POINTER = 0x80C1000F; -constexpr int ORBIS_VIDEODEC_ERROR_NEW_SEQUENCE = 0x80C10010; -constexpr int ORBIS_VIDEODEC_ERROR_DECODE_AU = 0x80C10011; -constexpr int ORBIS_VIDEODEC_ERROR_MISMATCH_SPEC = 0x80C10012; -constexpr int ORBIS_VIDEODEC_ERROR_INVALID_SEQUENCE = 0x80C10013; -constexpr int ORBIS_VIDEODEC_ERROR_FATAL_STREAM = 0x80C10014; -constexpr int ORBIS_VIDEODEC_ERROR_FATAL_STATE = 0x80C10015; - -// PngDec library -constexpr int ORBIS_PNG_DEC_ERROR_INVALID_ADDR = 0x80690001; -constexpr int ORBIS_PNG_DEC_ERROR_INVALID_SIZE = 0x80690002; -constexpr int ORBIS_PNG_DEC_ERROR_INVALID_PARAM = 0x80690003; -constexpr int ORBIS_PNG_DEC_ERROR_INVALID_HANDLE = 0x80690004; -constexpr int ORBIS_PNG_DEC_ERROR_INVALID_WORK_MEMORY = 0x80690005; -constexpr int ORBIS_PNG_DEC_ERROR_INVALID_DATA = 0x80690010; -constexpr int ORBIS_PNG_DEC_ERROR_UNSUPPORT_DATA = 0x80690011; -constexpr int ORBIS_PNG_DEC_ERROR_DECODE_ERROR = 0x80690012; -constexpr int ORBIS_PNG_DEC_ERROR_FATAL = 0x80690020; \ No newline at end of file diff --git a/src/core/libraries/fiber/fiber.cpp b/src/core/libraries/fiber/fiber.cpp index 92bf894b5..c28d8d734 100644 --- a/src/core/libraries/fiber/fiber.cpp +++ b/src/core/libraries/fiber/fiber.cpp @@ -4,7 +4,7 @@ #include "fiber.h" #include "common/logging/log.h" -#include "core/libraries/error_codes.h" +#include "core/libraries/fiber/fiber_error.h" #include "core/libraries/libs.h" #include "core/tls.h" diff --git a/src/core/libraries/fiber/fiber.h b/src/core/libraries/fiber/fiber.h index 930409caa..00099f93b 100644 --- a/src/core/libraries/fiber/fiber.h +++ b/src/core/libraries/fiber/fiber.h @@ -26,17 +26,12 @@ struct SceFiber { u64 signature; FiberState state; SceFiberEntry entry; - u64 argOnInitialize; - u64 argRun; u64* pArgRun; - u64 argReturn; u64* pArgReturn; - u64 sizeContext; - char name[ORBIS_FIBER_MAX_NAME_LENGTH]; void* handle; }; @@ -53,7 +48,7 @@ struct SceFiberInfo { }; static_assert(sizeof(SceFiberInfo) <= 128); -typedef void* SceFiberOptParam; +using SceFiberOptParam = void*; s32 PS4_SYSV_ABI sceFiberInitialize(SceFiber* fiber, const char* name, SceFiberEntry entry, u64 argOnInitialize, void* addrContext, u64 sizeContext, diff --git a/src/core/libraries/fiber/fiber_error.h b/src/core/libraries/fiber/fiber_error.h new file mode 100644 index 000000000..c89fe30b1 --- /dev/null +++ b/src/core/libraries/fiber/fiber_error.h @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +// Fiber library +constexpr int ORBIS_FIBER_ERROR_NULL = 0x80590001; +constexpr int ORBIS_FIBER_ERROR_ALIGNMENT = 0x80590002; +constexpr int ORBIS_FIBER_ERROR_RANGE = 0x80590003; +constexpr int ORBIS_FIBER_ERROR_INVALID = 0x80590004; +constexpr int ORBIS_FIBER_ERROR_PERMISSION = 0x80590005; +constexpr int ORBIS_FIBER_ERROR_STATE = 0x80590006; diff --git a/src/core/libraries/gnmdriver/gnm_error.h b/src/core/libraries/gnmdriver/gnm_error.h index eab684a24..b8e57f6f5 100644 --- a/src/core/libraries/gnmdriver/gnm_error.h +++ b/src/core/libraries/gnmdriver/gnm_error.h @@ -3,6 +3,8 @@ #pragma once +#include "core/libraries/error_codes.h" + constexpr int ORBIS_GNM_ERROR_SUBMISSION_FAILED_INVALID_ARGUMENT = 0x80D11000; constexpr int ORBIS_GNM_ERROR_SUBMISSION_NOT_ENOUGH_RESOURCES = 0x80D11001; constexpr int ORBIS_GNM_ERROR_SUBMISSION_AND_FLIP_FAILED_INVALID_COMMAND_BUFFER = 0x80D11080; diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index 70cf09a97..10d121afe 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -11,7 +11,8 @@ #include "common/slot_vector.h" #include "core/address_space.h" #include "core/debug_state.h" -#include "core/libraries/error_codes.h" +#include "core/libraries/gnmdriver/gnm_error.h" +#include "core/libraries/kernel/orbis_error.h" #include "core/libraries/kernel/process.h" #include "core/libraries/libs.h" #include "core/libraries/videoout/video_out.h" diff --git a/src/core/libraries/ime/ime.cpp b/src/core/libraries/ime/ime.cpp index efb988c72..700585ff3 100644 --- a/src/core/libraries/ime/ime.cpp +++ b/src/core/libraries/ime/ime.cpp @@ -2,14 +2,12 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include -#include "ime.h" -#include "ime_ui.h" - #include "common/logging/log.h" -#include "common/singleton.h" -#include "core/libraries/error_codes.h" +#include "core/libraries/ime/ime.h" +#include "core/libraries/ime/ime_error.h" +#include "core/libraries/ime/ime_ui.h" #include "core/libraries/libs.h" -#include "core/linker.h" +#include "core/tls.h" namespace Libraries::Ime { @@ -37,7 +35,7 @@ public: // Open an event to let the game know the IME has started OrbisImeEvent openEvent{}; - openEvent.id = (ime_mode ? OrbisImeEventId::OPEN : OrbisImeEventId::KEYBOARD_OPEN); + openEvent.id = (ime_mode ? OrbisImeEventId::Open : OrbisImeEventId::KeyboardOpen); if (ime_mode) { sceImeGetPanelSize(&m_param.ime, &openEvent.param.rect.width, @@ -45,8 +43,8 @@ public: openEvent.param.rect.x = m_param.ime.posx; openEvent.param.rect.y = m_param.ime.posy; } else { - openEvent.param.resourceIdArray.userId = 1; - openEvent.param.resourceIdArray.resourceId[0] = 1; + openEvent.param.resource_id_array.userId = 1; + openEvent.param.resource_id_array.resource_id[0] = 1; } Execute(nullptr, &openEvent, true); @@ -215,15 +213,15 @@ s32 PS4_SYSV_ABI sceImeGetPanelSize(const OrbisImeParam* param, u32* width, u32* } switch (param->type) { - case OrbisImeType::DEFAULT: - case OrbisImeType::BASIC_LATIN: - case OrbisImeType::URL: - case OrbisImeType::MAIL: + case OrbisImeType::Default: + case OrbisImeType::BasicLatin: + case OrbisImeType::Url: + case OrbisImeType::Mail: // We set our custom sizes, commented sizes are the original ones *width = 500; // 793 *height = 100; // 408 break; - case OrbisImeType::NUMBER: + case OrbisImeType::Number: *width = 370; *height = 402; break; @@ -315,7 +313,7 @@ void PS4_SYSV_ABI sceImeParamInit(OrbisImeParam* param) { } memset(param, 0, sizeof(OrbisImeParam)); - param->userId = -1; + param->user_id = -1; } int PS4_SYSV_ABI sceImeSetCandidateIndex() { diff --git a/src/core/libraries/ime/ime.h b/src/core/libraries/ime/ime.h index fc98d426a..2915b70da 100644 --- a/src/core/libraries/ime/ime.h +++ b/src/core/libraries/ime/ime.h @@ -3,9 +3,9 @@ #pragma once +#include "common/enum.h" #include "common/types.h" - -#include "ime_common.h" +#include "core/libraries/ime/ime_common.h" namespace Core::Loader { class SymbolsResolver; @@ -16,13 +16,13 @@ namespace Libraries::Ime { constexpr u32 ORBIS_IME_MAX_TEXT_LENGTH = 2048; enum class OrbisImeKeyboardOption : u32 { - DEFAULT = 0, - REPEAT = 1, - REPEAT_EACH_KEY = 2, - ADD_OSK = 4, - EFFECTIVE_WITH_TIME = 8, - DISABLE_RESUME = 16, - DISABLE_CAPSLOCK_WITHOUT_SHIFT = 32, + Default = 0, + Repeat = 1, + RepeatEachKey = 2, + AddOsk = 4, + EffectiveWithTime = 8, + DisableResume = 16, + DisableCapslockWithoutShift = 32, }; DECLARE_ENUM_FLAG_OPERATORS(OrbisImeKeyboardOption) @@ -35,19 +35,19 @@ struct OrbisImeKeyboardParam { }; struct OrbisImeParam { - s32 userId; + s32 user_id; OrbisImeType type; - u64 supportedLanguages; - OrbisImeEnterLabel enterLabel; - OrbisImeInputMethod inputMethod; + u64 supported_languages; + OrbisImeEnterLabel enter_label; + OrbisImeInputMethod input_method; OrbisImeTextFilter filter; u32 option; - u32 maxTextLength; - char16_t* inputTextBuffer; + u32 max_text_length; + char16_t* input_text_buffer; float posx; float posy; - OrbisImeHorizontalAlignment horizontalAlignment; - OrbisImeVerticalAlignment verticalAlignment; + OrbisImeHorizontalAlignment horizontal_alignment; + OrbisImeVerticalAlignment vertical_alignment; void* work; void* arg; OrbisImeEventHandler handler; @@ -117,4 +117,5 @@ int PS4_SYSV_ABI sceImeVshUpdateContext(); int PS4_SYSV_ABI sceImeVshUpdateContext2(); void RegisterlibSceIme(Core::Loader::SymbolsResolver* sym); -} // namespace Libraries::Ime \ No newline at end of file + +} // namespace Libraries::Ime diff --git a/src/core/libraries/ime/ime_common.h b/src/core/libraries/ime/ime_common.h index 078a6469e..77f23d91d 100644 --- a/src/core/libraries/ime/ime_common.h +++ b/src/core/libraries/ime/ime_common.h @@ -2,65 +2,64 @@ // SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include -#include "common/enum.h" + #include "common/types.h" #include "core/libraries/rtc/rtc.h" enum class OrbisImeType : u32 { - DEFAULT = 0, - BASIC_LATIN = 1, - URL = 2, - MAIL = 3, - NUMBER = 4, + Default = 0, + BasicLatin = 1, + Url = 2, + Mail = 3, + Number = 4, }; enum class OrbisImeHorizontalAlignment : u32 { - LEFT = 0, - CENTER = 1, - RIGHT = 2, + Left = 0, + Center = 1, + Right = 2, }; enum class OrbisImeVerticalAlignment : u32 { - TOP = 0, - CENTER = 1, - BOTTOM = 2, + Top = 0, + Center = 1, + Bottom = 2, }; enum class OrbisImeEnterLabel : u32 { - DEFAULT = 0, - SEND = 1, - SEARCH = 2, - GO = 3, + Default = 0, + Send = 1, + Search = 2, + Go = 3, }; enum class OrbisImeInputMethod : u32 { - DEFAULT = 0, + Default = 0, }; enum class OrbisImeEventId : u32 { - OPEN = 0, - UPDATE_TEXT = 1, - UPDATE_CARET = 2, - PRESS_CLOSE = 4, - PRESS_ENTER = 5, - ABORT = 6, - CANDIDATE_LIST_START = 7, - CANDIDATE_LIST_END = 8, - CANDIDATE_WORD = 9, - CANDIDATE_INDEX = 10, - CANDIDATE_DONE = 11, - CANDIDATE_CANCEL = 12, - CHANGE_DEVICE = 14, - CHANGE_INPUT_METHOD_STATE = 18, + Open = 0, + UpdateText = 1, + UpdateCaret = 2, + PressClose = 4, + PressEnter = 5, + Abort = 6, + CandidateListStart = 7, + CandidateListEnd = 8, + CandidateWord = 9, + CandidateIndex = 10, + CandidateDone = 11, + CandidateCancel = 12, + ChangeDevice = 14, + ChangeInputMethodState = 18, - KEYBOARD_OPEN = 256, - KEYBOARD_KEYCODE_DOWN = 257, - KEYBOARD_KEYCODE_UP = 258, - KEYBOARD_KEYCODE_REPEAT = 259, - KEYBOARD_CONNECTION = 260, - KEYBOARD_DISCONNECTION = 261, - KEYBOARD_ABORT = 262, + KeyboardOpen = 256, + KeyboardKeycodeDoen = 257, + KeyboardKeycodeUp = 258, + KeyboardKeycodeRepeat = 259, + KeyboardConnection = 260, + KeyboardDisconnection = 261, + KeyboardAbort = 262, }; enum class OrbisImeKeyboardType : u32 { @@ -105,10 +104,10 @@ enum class OrbisImeKeyboardType : u32 { }; enum class OrbisImeDeviceType : u32 { - NONE = 0, - CONTROLLER = 1, - EXT_KEYBOARD = 2, - REMOTE_OSK = 3, + None = 0, + Controller = 1, + ExtKeyboard = 2, + RemoteOsk = 3, }; struct OrbisImeRect { @@ -126,9 +125,9 @@ struct OrbisImeTextAreaProperty { struct OrbisImeEditText { char16_t* str; - u32 caretIndex; - u32 areaNum; - OrbisImeTextAreaProperty textArea[4]; + u32 caret_index; + u32 area_num; + OrbisImeTextAreaProperty text_area[4]; }; struct OrbisImeKeycode { @@ -136,40 +135,40 @@ struct OrbisImeKeycode { char16_t character; u32 status; OrbisImeKeyboardType type; - s32 userId; - u32 resourceId; + s32 user_id; + u32 resource_id; Libraries::Rtc::OrbisRtcTick timestamp; }; struct OrbisImeKeyboardResourceIdArray { s32 userId; - u32 resourceId[6]; + u32 resource_id[6]; }; enum class OrbisImeCaretMovementDirection : u32 { - STILL = 0, - LEFT = 1, - RIGHT = 2, - UP = 3, - DOWN = 4, - HOME = 5, - END = 6, - PAGE_UP = 7, - PAGE_DOWN = 8, - TOP = 9, - BOTTOM = 10, + Still = 0, + Left = 1, + Right = 2, + Up = 3, + Down = 4, + Home = 5, + End = 6, + PageUp = 7, + PageDown = 8, + Top = 9, + Bottom = 10, }; union OrbisImeEventParam { OrbisImeRect rect; OrbisImeEditText text; - OrbisImeCaretMovementDirection caretMove; + OrbisImeCaretMovementDirection caret_move; OrbisImeKeycode keycode; - OrbisImeKeyboardResourceIdArray resourceIdArray; - char16_t* candidateWord; - s32 candidateIndex; - OrbisImeDeviceType deviceType; - u32 inputMethodState; + OrbisImeKeyboardResourceIdArray resource_id_array; + char16_t* candidate_word; + s32 candidate_index; + OrbisImeDeviceType device_type; + u32 input_method_state; s8 reserved[64]; }; @@ -178,7 +177,7 @@ struct OrbisImeEvent { OrbisImeEventParam param; }; -typedef PS4_SYSV_ABI int (*OrbisImeTextFilter)(char16_t* outText, u32* outTextLength, - const char16_t* srcText, u32 srcTextLength); +using OrbisImeTextFilter = PS4_SYSV_ABI int (*)(char16_t* outText, u32* outTextLength, + const char16_t* srcText, u32 srcTextLength); -typedef PS4_SYSV_ABI void (*OrbisImeEventHandler)(void* arg, const OrbisImeEvent* e); +using OrbisImeEventHandler = PS4_SYSV_ABI void (*)(void* arg, const OrbisImeEvent* e); diff --git a/src/core/libraries/ime/ime_dialog.cpp b/src/core/libraries/ime/ime_dialog.cpp index 63b52706a..d6d027885 100644 --- a/src/core/libraries/ime/ime_dialog.cpp +++ b/src/core/libraries/ime/ime_dialog.cpp @@ -14,24 +14,24 @@ static constexpr std::array MAX_Y_POSITIONS = {2160.0f, 1080.0f}; namespace Libraries::ImeDialog { -static OrbisImeDialogStatus g_ime_dlg_status = OrbisImeDialogStatus::NONE; +static OrbisImeDialogStatus g_ime_dlg_status = OrbisImeDialogStatus::None; static OrbisImeDialogResult g_ime_dlg_result{}; static ImeDialogState g_ime_dlg_state{}; static ImeDialogUi g_ime_dlg_ui; static bool IsValidOption(OrbisImeDialogOption option, OrbisImeType type) { if (False(~option & - (OrbisImeDialogOption::MULTILINE | OrbisImeDialogOption::NO_AUTO_COMPLETION))) { + (OrbisImeDialogOption::Multiline | OrbisImeDialogOption::NoAutoCompletion))) { return false; } - if (True(option & OrbisImeDialogOption::MULTILINE) && type != OrbisImeType::DEFAULT && - type != OrbisImeType::BASIC_LATIN) { + if (True(option & OrbisImeDialogOption::Multiline) && type != OrbisImeType::Default && + type != OrbisImeType::BasicLatin) { return false; } - if (True(option & OrbisImeDialogOption::NO_AUTO_COMPLETION) && type != OrbisImeType::NUMBER && - type != OrbisImeType::BASIC_LATIN) { + if (True(option & OrbisImeDialogOption::NoAutoCompletion) && type != OrbisImeType::Number && + type != OrbisImeType::BasicLatin) { return false; } @@ -39,29 +39,29 @@ static bool IsValidOption(OrbisImeDialogOption option, OrbisImeType type) { } Error PS4_SYSV_ABI sceImeDialogAbort() { - if (g_ime_dlg_status == OrbisImeDialogStatus::NONE) { + if (g_ime_dlg_status == OrbisImeDialogStatus::None) { LOG_INFO(Lib_ImeDialog, "IME dialog not in use"); return Error::DIALOG_NOT_IN_USE; } - if (g_ime_dlg_status != OrbisImeDialogStatus::RUNNING) { + if (g_ime_dlg_status != OrbisImeDialogStatus::Running) { LOG_INFO(Lib_ImeDialog, "IME dialog not running"); return Error::DIALOG_NOT_RUNNING; } - g_ime_dlg_status = OrbisImeDialogStatus::FINISHED; - g_ime_dlg_result.endstatus = OrbisImeDialogEndStatus::ABORTED; + g_ime_dlg_status = OrbisImeDialogStatus::Finished; + g_ime_dlg_result.endstatus = OrbisImeDialogEndStatus::Aborted; return Error::OK; } Error PS4_SYSV_ABI sceImeDialogForceClose() { - if (g_ime_dlg_status == OrbisImeDialogStatus::NONE) { + if (g_ime_dlg_status == OrbisImeDialogStatus::None) { LOG_INFO(Lib_ImeDialog, "IME dialog not in use"); return Error::DIALOG_NOT_IN_USE; } - g_ime_dlg_status = OrbisImeDialogStatus::NONE; + g_ime_dlg_status = OrbisImeDialogStatus::None; g_ime_dlg_ui = ImeDialogUi(); g_ime_dlg_state = ImeDialogState(); @@ -93,7 +93,7 @@ int PS4_SYSV_ABI sceImeDialogGetPanelSizeExtended() { } Error PS4_SYSV_ABI sceImeDialogGetResult(OrbisImeDialogResult* result) { - if (g_ime_dlg_status == OrbisImeDialogStatus::NONE) { + if (g_ime_dlg_status == OrbisImeDialogStatus::None) { LOG_INFO(Lib_ImeDialog, "IME dialog is not running"); return Error::DIALOG_NOT_IN_USE; } @@ -105,7 +105,7 @@ Error PS4_SYSV_ABI sceImeDialogGetResult(OrbisImeDialogResult* result) { result->endstatus = g_ime_dlg_result.endstatus; - if (g_ime_dlg_status == OrbisImeDialogStatus::RUNNING) { + if (g_ime_dlg_status == OrbisImeDialogStatus::Running) { return Error::DIALOG_NOT_FINISHED; } @@ -114,7 +114,7 @@ Error PS4_SYSV_ABI sceImeDialogGetResult(OrbisImeDialogResult* result) { } OrbisImeDialogStatus PS4_SYSV_ABI sceImeDialogGetStatus() { - if (g_ime_dlg_status == OrbisImeDialogStatus::RUNNING) { + if (g_ime_dlg_status == OrbisImeDialogStatus::Running) { g_ime_dlg_state.CallTextFilter(); } @@ -122,7 +122,7 @@ OrbisImeDialogStatus PS4_SYSV_ABI sceImeDialogGetStatus() { } Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExtended* extended) { - if (g_ime_dlg_status != OrbisImeDialogStatus::NONE) { + if (g_ime_dlg_status != OrbisImeDialogStatus::None) { LOG_INFO(Lib_ImeDialog, "IME dialog is already running"); return Error::BUSY; } @@ -142,24 +142,24 @@ Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExt if (param->posx < 0.0f || param->posx >= - MAX_X_POSITIONS[False(param->option & OrbisImeDialogOption::LARGE_RESOLUTION)]) { + MAX_X_POSITIONS[False(param->option & OrbisImeDialogOption::LargeResolution)]) { LOG_INFO(Lib_ImeDialog, "Invalid param->posx"); return Error::INVALID_POSX; } if (param->posy < 0.0f || param->posy >= - MAX_Y_POSITIONS[False(param->option & OrbisImeDialogOption::LARGE_RESOLUTION)]) { + MAX_Y_POSITIONS[False(param->option & OrbisImeDialogOption::LargeResolution)]) { LOG_INFO(Lib_ImeDialog, "Invalid param->posy"); return Error::INVALID_POSY; } - if (!magic_enum::enum_contains(param->horizontalAlignment)) { + if (!magic_enum::enum_contains(param->horizontal_alignment)) { LOG_INFO(Lib_ImeDialog, "Invalid param->horizontalAlignment"); return Error::INVALID_HORIZONTALIGNMENT; } - if (!magic_enum::enum_contains(param->verticalAlignment)) { + if (!magic_enum::enum_contains(param->vertical_alignment)) { LOG_INFO(Lib_ImeDialog, "Invalid param->verticalAlignment"); return Error::INVALID_VERTICALALIGNMENT; } @@ -169,7 +169,7 @@ Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExt return Error::INVALID_PARAM; } - if (param->inputTextBuffer == nullptr) { + if (param->input_text_buffer == nullptr) { LOG_INFO(Lib_ImeDialog, "Invalid param->inputTextBuffer"); return Error::INVALID_INPUT_TEXT_BUFFER; } @@ -182,25 +182,25 @@ Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExt // TODO: do correct extended->option validation - if ((extended->extKeyboardMode & 0xe3fffffc) != 0) { + if ((extended->ext_keyboard_mode & 0xe3fffffc) != 0) { LOG_INFO(Lib_ImeDialog, "Invalid extended->extKeyboardMode"); return Error::INVALID_EXTENDED; } - if (extended->disableDevice > 7) { + if (extended->disable_device > 7) { LOG_INFO(Lib_ImeDialog, "Invalid extended->disableDevice"); return Error::INVALID_EXTENDED; } } - if (param->maxTextLength > ORBIS_IME_DIALOG_MAX_TEXT_LENGTH) { + if (param->max_text_length > ORBIS_IME_DIALOG_MAX_TEXT_LENGTH) { LOG_INFO(Lib_ImeDialog, "Invalid param->maxTextLength"); return Error::INVALID_MAX_TEXT_LENGTH; } g_ime_dlg_result = {}; g_ime_dlg_state = ImeDialogState(param, extended); - g_ime_dlg_status = OrbisImeDialogStatus::RUNNING; + g_ime_dlg_status = OrbisImeDialogStatus::Running; g_ime_dlg_ui = ImeDialogUi(&g_ime_dlg_state, &g_ime_dlg_status, &g_ime_dlg_result); return Error::OK; @@ -227,17 +227,17 @@ int PS4_SYSV_ABI sceImeDialogSetPanelPosition() { } Error PS4_SYSV_ABI sceImeDialogTerm() { - if (g_ime_dlg_status == OrbisImeDialogStatus::NONE) { + if (g_ime_dlg_status == OrbisImeDialogStatus::None) { LOG_INFO(Lib_ImeDialog, "IME dialog not in use"); return Error::DIALOG_NOT_IN_USE; } - if (g_ime_dlg_status == OrbisImeDialogStatus::RUNNING) { + if (g_ime_dlg_status == OrbisImeDialogStatus::Running) { LOG_INFO(Lib_ImeDialog, "IME dialog is still running"); return Error::DIALOG_NOT_FINISHED; } - g_ime_dlg_status = OrbisImeDialogStatus::NONE; + g_ime_dlg_status = OrbisImeDialogStatus::None; g_ime_dlg_ui = ImeDialogUi(); g_ime_dlg_state = ImeDialogState(); @@ -274,4 +274,4 @@ void RegisterlibSceImeDialog(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("gyTyVn+bXMw", "libSceImeDialog", 1, "libSceImeDialog", 1, 1, sceImeDialogTerm); }; -} // namespace Libraries::ImeDialog \ No newline at end of file +} // namespace Libraries::ImeDialog diff --git a/src/core/libraries/ime/ime_dialog.h b/src/core/libraries/ime/ime_dialog.h index e99d29613..c8b228498 100644 --- a/src/core/libraries/ime/ime_dialog.h +++ b/src/core/libraries/ime/ime_dialog.h @@ -58,32 +58,32 @@ enum class Error : u32 { }; enum class OrbisImeDialogStatus : u32 { - NONE = 0, - RUNNING = 1, - FINISHED = 2, + None = 0, + Running = 1, + Finished = 2, }; enum class OrbisImeDialogEndStatus : u32 { - OK = 0, - USER_CANCELED = 1, - ABORTED = 2, + Ok = 0, + UserCanceled = 1, + Aborted = 2, }; enum class OrbisImeDialogOption : u32 { - DEFAULT = 0, - MULTILINE = 1, - NO_AUTO_CORRECTION = 2, - NO_AUTO_COMPLETION = 4, + Default = 0, + Multiline = 1, + NoAutoCorrection = 2, + NoAutoCompletion = 4, // TODO: Document missing options - LARGE_RESOLUTION = 1024, + LargeResolution = 1024, }; DECLARE_ENUM_FLAG_OPERATORS(OrbisImeDialogOption) enum class OrbisImePanelPriority : u32 { - DEFAULT = 0, - ALPHABET = 1, - SYMBOL = 2, - ACCENT = 3, + Default = 0, + Alphabet = 1, + Symbol = 2, + Accent = 3, }; struct OrbisImeColor { @@ -103,29 +103,29 @@ struct OrbisImeKeycode { char16_t character; u32 status; OrbisImeKeyboardType type; - s32 userId; - u32 resourceId; + s32 user_id; + u32 resource_id; u64 timestamp; }; -typedef PS4_SYSV_ABI int (*OrbisImeExtKeyboardFilter)(const OrbisImeKeycode* srcKeycode, - u16* outKeycode, u32* outStatus, - void* reserved); +using OrbisImeExtKeyboardFilter = PS4_SYSV_ABI int (*)(const OrbisImeKeycode* srcKeycode, + u16* outKeycode, u32* outStatus, + void* reserved); struct OrbisImeDialogParam { - s32 userId; + s32 user_id; OrbisImeType type; - u64 supportedLanguages; - OrbisImeEnterLabel enterLabel; - OrbisImeInputMethod inputMethod; + u64 supported_languages; + OrbisImeEnterLabel enter_label; + OrbisImeInputMethod input_method; OrbisImeTextFilter filter; OrbisImeDialogOption option; - u32 maxTextLength; - char16_t* inputTextBuffer; + u32 max_text_length; + char16_t* input_text_buffer; float posx; float posy; - OrbisImeHorizontalAlignment horizontalAlignment; - OrbisImeVerticalAlignment verticalAlignment; + OrbisImeHorizontalAlignment horizontal_alignment; + OrbisImeVerticalAlignment vertical_alignment; const char16_t* placeholder; const char16_t* title; s8 reserved[16]; @@ -133,20 +133,20 @@ struct OrbisImeDialogParam { struct OrbisImeParamExtended { u32 option; // OrbisImeDialogOptionExtended - OrbisImeColor colorBase; - OrbisImeColor colorLine; - OrbisImeColor colorTextField; - OrbisImeColor colorPreedit; - OrbisImeColor colorButtonDefault; - OrbisImeColor colorButtonFunction; - OrbisImeColor colorButtonSymbol; - OrbisImeColor colorText; - OrbisImeColor colorSpecial; + OrbisImeColor color_base; + OrbisImeColor color_line; + OrbisImeColor color_text_field; + OrbisImeColor color_preedit; + OrbisImeColor color_button_default; + OrbisImeColor color_button_function; + OrbisImeColor color_button_symbol; + OrbisImeColor color_text; + OrbisImeColor color_special; OrbisImePanelPriority priority; - char* additionalDictionaryPath; - OrbisImeExtKeyboardFilter extKeyboardFilter; - uint32_t disableDevice; - uint32_t extKeyboardMode; + char* additional_dictionary_path; + OrbisImeExtKeyboardFilter ext_keyboard_filter; + uint32_t disable_device; + uint32_t ext_keyboard_mode; int8_t reserved[60]; }; @@ -167,4 +167,4 @@ int PS4_SYSV_ABI sceImeDialogSetPanelPosition(); Error PS4_SYSV_ABI sceImeDialogTerm(); void RegisterlibSceImeDialog(Core::Loader::SymbolsResolver* sym); -} // namespace Libraries::ImeDialog \ No newline at end of file +} // namespace Libraries::ImeDialog diff --git a/src/core/libraries/ime/ime_dialog_ui.cpp b/src/core/libraries/ime/ime_dialog_ui.cpp index de64de5fa..5957606eb 100644 --- a/src/core/libraries/ime/ime_dialog_ui.cpp +++ b/src/core/libraries/ime/ime_dialog_ui.cpp @@ -8,7 +8,6 @@ #include "common/assert.h" #include "common/logging/log.h" -#include "common/singleton.h" #include "core/libraries/ime/ime_dialog.h" #include "core/libraries/ime/ime_dialog_ui.h" #include "core/tls.h" @@ -26,15 +25,15 @@ ImeDialogState::ImeDialogState(const OrbisImeDialogParam* param, return; } - userId = param->userId; - is_multiLine = True(param->option & OrbisImeDialogOption::MULTILINE); - is_numeric = param->type == OrbisImeType::NUMBER; + user_id = param->user_id; + is_multi_line = True(param->option & OrbisImeDialogOption::Multiline); + is_numeric = param->type == OrbisImeType::Number; type = param->type; - enter_label = param->enterLabel; + enter_label = param->enter_label; text_filter = param->filter; - keyboard_filter = extended ? extended->extKeyboardFilter : nullptr; - max_text_length = param->maxTextLength; - text_buffer = param->inputTextBuffer; + keyboard_filter = extended ? extended->ext_keyboard_filter : nullptr; + max_text_length = param->max_text_length; + text_buffer = param->input_text_buffer; if (param->title) { std::size_t title_len = std::char_traits::length(param->title); @@ -65,12 +64,12 @@ ImeDialogState::ImeDialogState(const OrbisImeDialogParam* param, } ImeDialogState::ImeDialogState(ImeDialogState&& other) noexcept - : input_changed(other.input_changed), userId(other.userId), is_multiLine(other.is_multiLine), - is_numeric(other.is_numeric), type(other.type), enter_label(other.enter_label), - text_filter(other.text_filter), keyboard_filter(other.keyboard_filter), - max_text_length(other.max_text_length), text_buffer(other.text_buffer), - title(std::move(other.title)), placeholder(std::move(other.placeholder)), - current_text(other.current_text) { + : input_changed(other.input_changed), user_id(other.user_id), + is_multi_line(other.is_multi_line), is_numeric(other.is_numeric), type(other.type), + enter_label(other.enter_label), text_filter(other.text_filter), + keyboard_filter(other.keyboard_filter), max_text_length(other.max_text_length), + text_buffer(other.text_buffer), title(std::move(other.title)), + placeholder(std::move(other.placeholder)), current_text(other.current_text) { other.text_buffer = nullptr; } @@ -78,8 +77,8 @@ ImeDialogState::ImeDialogState(ImeDialogState&& other) noexcept ImeDialogState& ImeDialogState::operator=(ImeDialogState&& other) { if (this != &other) { input_changed = other.input_changed; - userId = other.userId; - is_multiLine = other.is_multiLine; + user_id = other.user_id; + is_multi_line = other.is_multi_line; is_numeric = other.is_numeric; type = other.type; enter_label = other.enter_label; @@ -171,7 +170,7 @@ ImeDialogUi::ImeDialogUi(ImeDialogState* state, OrbisImeDialogStatus* status, OrbisImeDialogResult* result) : state(state), status(status), result(result) { - if (state && *status == OrbisImeDialogStatus::RUNNING) { + if (state && *status == OrbisImeDialogStatus::Running) { AddLayer(this); } } @@ -191,7 +190,7 @@ ImeDialogUi::ImeDialogUi(ImeDialogUi&& other) noexcept other.status = nullptr; other.result = nullptr; - if (state && *status == OrbisImeDialogStatus::RUNNING) { + if (state && *status == OrbisImeDialogStatus::Running) { AddLayer(this); } } @@ -208,7 +207,7 @@ ImeDialogUi& ImeDialogUi::operator=(ImeDialogUi&& other) { other.status = nullptr; other.result = nullptr; - if (state && *status == OrbisImeDialogStatus::RUNNING) { + if (state && *status == OrbisImeDialogStatus::Running) { AddLayer(this); } @@ -226,7 +225,7 @@ void ImeDialogUi::Draw() { return; } - if (!status || *status != OrbisImeDialogStatus::RUNNING) { + if (!status || *status != OrbisImeDialogStatus::Running) { return; } @@ -235,7 +234,7 @@ void ImeDialogUi::Draw() { ImVec2 window_size; - if (state->is_multiLine) { + if (state->is_multi_line) { window_size = {500.0f, 300.0f}; } else { window_size = {500.0f, 150.0f}; @@ -259,7 +258,7 @@ void ImeDialogUi::Draw() { SetWindowFontScale(1.0f); } - if (state->is_multiLine) { + if (state->is_multi_line) { DrawMultiLineInputText(); } else { DrawInputText(); @@ -270,16 +269,16 @@ void ImeDialogUi::Draw() { const char* button_text; switch (state->enter_label) { - case OrbisImeEnterLabel::GO: + case OrbisImeEnterLabel::Go: button_text = "Go##ImeDialogOK"; break; - case OrbisImeEnterLabel::SEARCH: + case OrbisImeEnterLabel::Search: button_text = "Search##ImeDialogOK"; break; - case OrbisImeEnterLabel::SEND: + case OrbisImeEnterLabel::Send: button_text = "Send##ImeDialogOK"; break; - case OrbisImeEnterLabel::DEFAULT: + case OrbisImeEnterLabel::Default: default: button_text = "OK##ImeDialogOK"; break; @@ -292,16 +291,16 @@ void ImeDialogUi::Draw() { SetCursorPosX(button_start_pos); if (Button(button_text, BUTTON_SIZE) || - (!state->is_multiLine && IsKeyPressed(ImGuiKey_Enter))) { - *status = OrbisImeDialogStatus::FINISHED; - result->endstatus = OrbisImeDialogEndStatus::OK; + (!state->is_multi_line && IsKeyPressed(ImGuiKey_Enter))) { + *status = OrbisImeDialogStatus::Finished; + result->endstatus = OrbisImeDialogEndStatus::Ok; } SameLine(0.0f, button_spacing); if (Button("Cancel##ImeDialogCancel", BUTTON_SIZE)) { - *status = OrbisImeDialogStatus::FINISHED; - result->endstatus = OrbisImeDialogEndStatus::USER_CANCELED; + *status = OrbisImeDialogStatus::Finished; + result->endstatus = OrbisImeDialogEndStatus::UserCanceled; } } End(); @@ -362,9 +361,10 @@ int ImeDialogUi::InputTextCallback(ImGuiInputTextCallbackData* data) { .status = 1, // ??? 1 = key pressed, 0 = key released .type = OrbisImeKeyboardType::ENGLISH_US, // TODO set this to the correct value (maybe use // the current language?) - .userId = ui->state->userId, - .resourceId = 0, - .timestamp = 0}; + .user_id = ui->state->user_id, + .resource_id = 0, + .timestamp = 0, + }; if (!ui->state->ConvertUTF8ToOrbis(event_char, 4, &src_keycode.character, 1)) { LOG_ERROR(Lib_ImeDialog, "Failed to convert orbis char to utf8"); diff --git a/src/core/libraries/ime/ime_dialog_ui.h b/src/core/libraries/ime/ime_dialog_ui.h index a4cf6e9f7..10dff5eeb 100644 --- a/src/core/libraries/ime/ime_dialog_ui.h +++ b/src/core/libraries/ime/ime_dialog_ui.h @@ -20,8 +20,8 @@ class ImeDialogState final { bool input_changed = false; - s32 userId{}; - bool is_multiLine{}; + s32 user_id{}; + bool is_multi_line{}; bool is_numeric{}; OrbisImeType type{}; OrbisImeEnterLabel enter_label{}; diff --git a/src/core/libraries/ime/ime_error.h b/src/core/libraries/ime/ime_error.h new file mode 100644 index 000000000..77eeada39 --- /dev/null +++ b/src/core/libraries/ime/ime_error.h @@ -0,0 +1,51 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +// ImeDialog library +constexpr int ORBIS_ERROR_DIALOG_ERROR_NOT_INITIALIZED = 0x80ED0001; +constexpr int ORBIS_ERROR_DIALOG_ERROR_ALREADY_INITIALIZED = 0x80ED0002; +constexpr int ORBIS_ERROR_DIALOG_ERROR_PARAM_INVALID = 0x80ED0003; +constexpr int ORBIS_ERROR_DIALOG_ERROR_UNEXPECTED_FATAL = 0x80ED0004; +constexpr int ORBIS_ERROR_DIALOG_ERROR_INVALID_STATE = 0x80ED0005; +constexpr int ORBIS_ERROR_DIALOG_ERROR_SERVICE_BUSY = 0x80ED0006; +constexpr int ORBIS_ERROR_DIALOG_ERROR_INVALID_USER_ID = 0x80ED0007; + +// Ime library +constexpr int ORBIS_IME_ERROR_BUSY = 0x80BC0001; +constexpr int ORBIS_IME_ERROR_NOT_OPENED = 0x80BC0002; +constexpr int ORBIS_IME_ERROR_NO_MEMORY = 0x80BC0003; +constexpr int ORBIS_IME_ERROR_CONNECTION_FAILED = 0x80BC0004; +constexpr int ORBIS_IME_ERROR_TOO_MANY_REQUESTS = 0x80BC0005; +constexpr int ORBIS_IME_ERROR_INVALID_TEXT = 0x80BC0006; +constexpr int ORBIS_IME_ERROR_EVENT_OVERFLOW = 0x80BC0007; +constexpr int ORBIS_IME_ERROR_NOT_ACTIVE = 0x80BC0008; +constexpr int ORBIS_IME_ERROR_IME_SUSPENDING = 0x80BC0009; +constexpr int ORBIS_IME_ERROR_DEVICE_IN_USE = 0x80BC000A; +constexpr int ORBIS_IME_ERROR_INVALID_USER_ID = 0x80BC0010; +constexpr int ORBIS_IME_ERROR_INVALID_TYPE = 0x80BC0011; +constexpr int ORBIS_IME_ERROR_INVALID_SUPPORTED_LANGUAGES = 0x80BC0012; +constexpr int ORBIS_IME_ERROR_INVALID_ENTER_LABEL = 0x80BC0013; +constexpr int ORBIS_IME_ERROR_INVALID_INPUT_METHOD = 0x80BC0014; +constexpr int ORBIS_IME_ERROR_INVALID_OPTION = 0x80BC0015; +constexpr int ORBIS_IME_ERROR_INVALID_MAX_TEXT_LENGTH = 0x80BC0016; +constexpr int ORBIS_IME_ERROR_INVALID_INPUT_TEXT_BUFFER = 0x80BC0017; +constexpr int ORBIS_IME_ERROR_INVALID_POSX = 0x80BC0018; +constexpr int ORBIS_IME_ERROR_INVALID_POSY = 0x80BC0019; +constexpr int ORBIS_IME_ERROR_INVALID_HORIZONTAL_ALIGNMENT = 0x80BC001A; +constexpr int ORBIS_IME_ERROR_INVALID_VERTICAL_ALIGNMENT = 0x80BC001B; +constexpr int ORBIS_IME_ERROR_INVALID_EXTENDED = 0x80BC001C; +constexpr int ORBIS_IME_ERROR_INVALID_KEYBOARD_TYPE = 0x80BC001D; +constexpr int ORBIS_IME_ERROR_INVALID_WORK = 0x80BC0020; +constexpr int ORBIS_IME_ERROR_INVALID_ARG = 0x80BC0021; +constexpr int ORBIS_IME_ERROR_INVALID_HANDLER = 0x80BC0022; +constexpr int ORBIS_IME_ERROR_NO_RESOURCE_ID = 0x80BC0023; +constexpr int ORBIS_IME_ERROR_INVALID_MODE = 0x80BC0024; +constexpr int ORBIS_IME_ERROR_INVALID_PARAM = 0x80BC0030; +constexpr int ORBIS_IME_ERROR_INVALID_ADDRESS = 0x80BC0031; +constexpr int ORBIS_IME_ERROR_INVALID_RESERVED = 0x80BC0032; +constexpr int ORBIS_IME_ERROR_INVALID_TIMING = 0x80BC0033; +constexpr int ORBIS_IME_ERROR_INTERNAL = 0x80BC00FF; diff --git a/src/core/libraries/ime/ime_ui.cpp b/src/core/libraries/ime/ime_ui.cpp index 09d361263..c5f41c5e8 100644 --- a/src/core/libraries/ime/ime_ui.cpp +++ b/src/core/libraries/ime/ime_ui.cpp @@ -16,7 +16,7 @@ ImeState::ImeState(const OrbisImeParam* param) { } work_buffer = param->work; - text_buffer = param->inputTextBuffer; + text_buffer = param->input_text_buffer; std::size_t text_len = std::char_traits::length(text_buffer); if (!ConvertOrbisToUTF8(text_buffer, text_len, current_text.begin(), @@ -52,13 +52,13 @@ void ImeState::SendEvent(OrbisImeEvent* event) { void ImeState::SendEnterEvent() { OrbisImeEvent enterEvent{}; - enterEvent.id = OrbisImeEventId::PRESS_ENTER; + enterEvent.id = OrbisImeEventId::PressEnter; SendEvent(&enterEvent); } void ImeState::SendCloseEvent() { OrbisImeEvent closeEvent{}; - closeEvent.id = OrbisImeEventId::PRESS_CLOSE; + closeEvent.id = OrbisImeEventId::PressClose; closeEvent.param.text.str = reinterpret_cast(work_buffer); SendEvent(&closeEvent); } @@ -180,7 +180,7 @@ void ImeUi::DrawInputText() { if (first_render) { SetKeyboardFocusHere(); } - if (InputTextEx("##ImeInput", nullptr, state->current_text.begin(), ime_param->maxTextLength, + if (InputTextEx("##ImeInput", nullptr, state->current_text.begin(), ime_param->max_text_length, input_size, ImGuiInputTextFlags_CallbackAlways, InputTextCallback, this)) { state->input_changed = true; } @@ -194,16 +194,16 @@ int ImeUi::InputTextCallback(ImGuiInputTextCallbackData* data) { if (lastCaretPos == -1) { lastCaretPos = data->CursorPos; } else if (data->CursorPos != lastCaretPos) { - OrbisImeCaretMovementDirection caretDirection = OrbisImeCaretMovementDirection::STILL; + OrbisImeCaretMovementDirection caretDirection = OrbisImeCaretMovementDirection::Still; if (data->CursorPos < lastCaretPos) { - caretDirection = OrbisImeCaretMovementDirection::LEFT; + caretDirection = OrbisImeCaretMovementDirection::Left; } else if (data->CursorPos > lastCaretPos) { - caretDirection = OrbisImeCaretMovementDirection::RIGHT; + caretDirection = OrbisImeCaretMovementDirection::Right; } OrbisImeEvent event{}; - event.id = OrbisImeEventId::UPDATE_CARET; - event.param.caretMove = caretDirection; + event.id = OrbisImeEventId::UpdateCaret; + event.param.caret_move = caretDirection; lastCaretPos = data->CursorPos; ui->state->SendEvent(&event); @@ -214,28 +214,28 @@ int ImeUi::InputTextCallback(ImGuiInputTextCallbackData* data) { if (currentText != lastText) { OrbisImeEditText eventParam{}; eventParam.str = reinterpret_cast(ui->ime_param->work); - eventParam.caretIndex = data->CursorPos; - eventParam.areaNum = 1; + eventParam.caret_index = data->CursorPos; + eventParam.area_num = 1; - eventParam.textArea[0].mode = 1; // Edit mode - eventParam.textArea[0].index = data->CursorPos; - eventParam.textArea[0].length = data->BufTextLen; + eventParam.text_area[0].mode = 1; // Edit mode + eventParam.text_area[0].index = data->CursorPos; + eventParam.text_area[0].length = data->BufTextLen; if (!ui->state->ConvertUTF8ToOrbis(data->Buf, data->BufTextLen, eventParam.str, - ui->ime_param->maxTextLength)) { + ui->ime_param->max_text_length)) { LOG_ERROR(Lib_ImeDialog, "Failed to convert Orbis char to UTF-8"); return 0; } if (!ui->state->ConvertUTF8ToOrbis(data->Buf, data->BufTextLen, - ui->ime_param->inputTextBuffer, - ui->ime_param->maxTextLength)) { + ui->ime_param->input_text_buffer, + ui->ime_param->max_text_length)) { LOG_ERROR(Lib_ImeDialog, "Failed to convert Orbis char to UTF-8"); return 0; } OrbisImeEvent event{}; - event.id = OrbisImeEventId::UPDATE_TEXT; + event.id = OrbisImeEventId::UpdateText; event.param.text = eventParam; lastText = currentText; @@ -249,4 +249,4 @@ void ImeUi::Free() { RemoveLayer(this); } -}; // namespace Libraries::Ime \ No newline at end of file +}; // namespace Libraries::Ime diff --git a/src/core/libraries/kernel/equeue.cpp b/src/core/libraries/kernel/equeue.cpp index d4f7eabaa..6543cc319 100644 --- a/src/core/libraries/kernel/equeue.cpp +++ b/src/core/libraries/kernel/equeue.cpp @@ -4,8 +4,8 @@ #include "common/assert.h" #include "common/debug.h" #include "common/logging/log.h" -#include "core/libraries/error_codes.h" #include "core/libraries/kernel/equeue.h" +#include "core/libraries/kernel/orbis_error.h" #include "core/libraries/libs.h" namespace Libraries::Kernel { @@ -77,9 +77,8 @@ bool EqueueInternal::TriggerEvent(u64 ident, s16 filter, void* trigger_data) { bool has_found = false; { std::scoped_lock lock{m_mutex}; - for (auto& event : m_events) { - if ((event.event.ident == ident) && (event.event.filter == filter)) { + if (event.event.ident == ident && event.event.filter == filter) { event.Trigger(trigger_data); has_found = true; } @@ -91,7 +90,6 @@ bool EqueueInternal::TriggerEvent(u64 ident, s16 filter, void* trigger_data) { int EqueueInternal::GetTriggeredEvents(SceKernelEvent* ev, int num) { int count = 0; - for (auto& event : m_events) { if (event.IsTriggered()) { if (event.event.flags & SceKernelEvent::Flags::Clear) { diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index d0ffa21ca..1b95e5270 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -6,8 +6,8 @@ #include "common/scope_exit.h" #include "common/singleton.h" #include "core/file_sys/fs.h" -#include "core/libraries/error_codes.h" #include "core/libraries/kernel/file_system.h" +#include "core/libraries/kernel/orbis_error.h" #include "core/libraries/libs.h" #include "kernel.h" @@ -128,12 +128,12 @@ int PS4_SYSV_ABI sceKernelClose(int d) { return ORBIS_KERNEL_ERROR_EPERM; } if (d == 2003) { // dev/urandom case - return SCE_OK; + return ORBIS_OK; } auto* h = Common::Singleton::Instance(); auto* file = h->GetFile(d); if (file == nullptr) { - return SCE_KERNEL_ERROR_EBADF; + return ORBIS_KERNEL_ERROR_EBADF; } if (!file->is_directory) { file->f.Close(); @@ -141,7 +141,7 @@ int PS4_SYSV_ABI sceKernelClose(int d) { file->is_opened = false; LOG_INFO(Kernel_Fs, "Closing {}", file->m_guest_name); h->DeleteHandle(d); - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI posix_close(int d) { @@ -166,7 +166,7 @@ size_t PS4_SYSV_ABI sceKernelWrite(int d, const void* buf, size_t nbytes) { auto* h = Common::Singleton::Instance(); auto* file = h->GetFile(d); if (file == nullptr) { - return SCE_KERNEL_ERROR_EBADF; + return ORBIS_KERNEL_ERROR_EBADF; } std::scoped_lock lk{file->m_mutex}; @@ -175,7 +175,7 @@ size_t PS4_SYSV_ABI sceKernelWrite(int d, const void* buf, size_t nbytes) { int PS4_SYSV_ABI sceKernelUnlink(const char* path) { if (path == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } auto* h = Common::Singleton::Instance(); @@ -184,15 +184,15 @@ int PS4_SYSV_ABI sceKernelUnlink(const char* path) { bool ro = false; const auto host_path = mnt->GetHostPath(path, &ro); if (host_path.empty()) { - return SCE_KERNEL_ERROR_EACCES; + return ORBIS_KERNEL_ERROR_EACCES; } if (ro) { - return SCE_KERNEL_ERROR_EROFS; + return ORBIS_KERNEL_ERROR_EROFS; } if (std::filesystem::is_directory(host_path)) { - return SCE_KERNEL_ERROR_EPERM; + return ORBIS_KERNEL_ERROR_EPERM; } auto* file = h->GetFile(host_path); @@ -201,7 +201,7 @@ int PS4_SYSV_ABI sceKernelUnlink(const char* path) { } LOG_INFO(Kernel_Fs, "Unlinked {}", path); - return SCE_OK; + return ORBIS_OK; } size_t PS4_SYSV_ABI _readv(int d, const SceKernelIovec* iov, int iovcnt) { @@ -231,7 +231,7 @@ s64 PS4_SYSV_ABI sceKernelLseek(int d, s64 offset, int whence) { std::scoped_lock lk{file->m_mutex}; if (!file->f.Seek(offset, origin)) { LOG_CRITICAL(Kernel_Fs, "sceKernelLseek: failed to seek"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } return file->f.Tell(); } @@ -257,7 +257,7 @@ s64 PS4_SYSV_ABI sceKernelRead(int d, void* buf, size_t nbytes) { auto* h = Common::Singleton::Instance(); auto* file = h->GetFile(d); if (file == nullptr) { - return SCE_KERNEL_ERROR_EBADF; + return ORBIS_KERNEL_ERROR_EBADF; } std::scoped_lock lk{file->m_mutex}; @@ -277,7 +277,7 @@ int PS4_SYSV_ABI posix_read(int d, void* buf, size_t nbytes) { int PS4_SYSV_ABI sceKernelMkdir(const char* path, u16 mode) { LOG_INFO(Kernel_Fs, "path = {} mode = {}", path, mode); if (path == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } auto* mnt = Common::Singleton::Instance(); @@ -285,21 +285,21 @@ int PS4_SYSV_ABI sceKernelMkdir(const char* path, u16 mode) { const auto dir_name = mnt->GetHostPath(path, &ro); if (std::filesystem::exists(dir_name)) { - return SCE_KERNEL_ERROR_EEXIST; + return ORBIS_KERNEL_ERROR_EEXIST; } if (ro) { - return SCE_KERNEL_ERROR_EROFS; + return ORBIS_KERNEL_ERROR_EROFS; } // CUSA02456: path = /aotl after sceSaveDataMount(mode = 1) std::error_code ec; if (dir_name.empty() || !std::filesystem::create_directory(dir_name, ec)) { - return SCE_KERNEL_ERROR_EIO; + return ORBIS_KERNEL_ERROR_EIO; } if (!std::filesystem::exists(dir_name)) { - return SCE_KERNEL_ERROR_ENOENT; + return ORBIS_KERNEL_ERROR_ENOENT; } return ORBIS_OK; } @@ -323,13 +323,13 @@ int PS4_SYSV_ABI sceKernelRmdir(const char* path) { if (dir_name.empty()) { LOG_ERROR(Kernel_Fs, "Failed to remove directory: {}, permission denied", fmt::UTF(dir_name.u8string())); - return SCE_KERNEL_ERROR_EACCES; + return ORBIS_KERNEL_ERROR_EACCES; } if (ro) { LOG_ERROR(Kernel_Fs, "Failed to remove directory: {}, directory is read only", fmt::UTF(dir_name.u8string())); - return SCE_KERNEL_ERROR_EROFS; + return ORBIS_KERNEL_ERROR_EROFS; } if (!std::filesystem::is_directory(dir_name)) { @@ -411,7 +411,7 @@ int PS4_SYSV_ABI sceKernelCheckReachability(const char* path) { auto* mnt = Common::Singleton::Instance(); const auto path_name = mnt->GetHostPath(path); if (!std::filesystem::exists(path_name)) { - return SCE_KERNEL_ERROR_ENOENT; + return ORBIS_KERNEL_ERROR_ENOENT; } return ORBIS_OK; } @@ -514,15 +514,15 @@ int PS4_SYSV_ABI sceKernelFtruncate(int fd, s64 length) { auto* file = h->GetFile(fd); if (file == nullptr) { - return SCE_KERNEL_ERROR_EBADF; + return ORBIS_KERNEL_ERROR_EBADF; } if (file->m_host_name.empty()) { - return SCE_KERNEL_ERROR_EACCES; + return ORBIS_KERNEL_ERROR_EACCES; } file->f.SetSize(length); - return SCE_OK; + return ORBIS_OK; } static int GetDents(int fd, char* buf, int nbytes, s64* basep) { @@ -605,11 +605,11 @@ s32 PS4_SYSV_ABI sceKernelRename(const char* from, const char* to) { return ORBIS_KERNEL_ERROR_ENOENT; } if (ro) { - return SCE_KERNEL_ERROR_EROFS; + return ORBIS_KERNEL_ERROR_EROFS; } const auto dst_path = mnt->GetHostPath(to, &ro); if (ro) { - return SCE_KERNEL_ERROR_EROFS; + return ORBIS_KERNEL_ERROR_EROFS; } const bool src_is_dir = std::filesystem::is_directory(src_path); const bool dst_is_dir = std::filesystem::is_directory(dst_path); diff --git a/src/core/libraries/kernel/file_system.h b/src/core/libraries/kernel/file_system.h index 0bc473ec0..dcbb3957d 100644 --- a/src/core/libraries/kernel/file_system.h +++ b/src/core/libraries/kernel/file_system.h @@ -37,8 +37,8 @@ struct OrbisKernelStat { u32 st_gen; s32 st_lspare; OrbisKernelTimespec st_birthtim; - unsigned int : (8 / 2) * (16 - static_cast(sizeof(OrbisKernelTimespec))); - unsigned int : (8 / 2) * (16 - static_cast(sizeof(OrbisKernelTimespec))); + u32 : (8 / 2) * (16 - static_cast(sizeof(OrbisKernelTimespec))); + u32 : (8 / 2) * (16 - static_cast(sizeof(OrbisKernelTimespec))); }; struct OrbisKernelDirent { diff --git a/src/core/libraries/kernel/kernel.cpp b/src/core/libraries/kernel/kernel.cpp index 512e77bc6..b310c7be9 100644 --- a/src/core/libraries/kernel/kernel.cpp +++ b/src/core/libraries/kernel/kernel.cpp @@ -2,36 +2,27 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include - #include #include "common/assert.h" #include "common/debug.h" #include "common/logging/log.h" #include "common/polyfill_thread.h" -#include "common/singleton.h" #include "common/thread.h" -#include "core/file_sys/fs.h" -#include "core/libraries/error_codes.h" #include "core/libraries/kernel/equeue.h" #include "core/libraries/kernel/file_system.h" #include "core/libraries/kernel/kernel.h" #include "core/libraries/kernel/memory.h" +#include "core/libraries/kernel/orbis_error.h" +#include "core/libraries/kernel/posix_error.h" #include "core/libraries/kernel/process.h" #include "core/libraries/kernel/threads.h" #include "core/libraries/kernel/threads/exception.h" #include "core/libraries/kernel/time.h" #include "core/libraries/libs.h" -#include "core/linker.h" #ifdef _WIN64 -#include -#include -#include -#else -#ifdef __APPLE__ -#include -#endif +#include #endif namespace Libraries::Kernel { @@ -39,10 +30,10 @@ namespace Libraries::Kernel { static u64 g_stack_chk_guard = 0xDEADBEEF54321ABC; // dummy return boost::asio::io_context io_context; -std::mutex m_asio_req; -std::condition_variable_any cv_asio_req; -std::atomic asio_requests; -std::jthread service_thread; +static std::mutex m_asio_req; +static std::condition_variable_any cv_asio_req; +static std::atomic asio_requests; +static std::jthread service_thread; void KernelSignalRequest() { std::unique_lock lock{m_asio_req}; @@ -93,16 +84,12 @@ int* PS4_SYSV_ABI __Error() { return &g_posix_errno; } -void ErrSceToPosix(int result) { - const int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - g_posix_errno = rt; +void ErrSceToPosix(int error) { + g_posix_errno = error - ORBIS_KERNEL_ERROR_UNKNOWN; } -int ErrnoToSceKernelError(int e) { - const auto res = SCE_KERNEL_ERROR_UNKNOWN + e; - return res > SCE_KERNEL_ERROR_ESTOP ? SCE_KERNEL_ERROR_UNKNOWN : res; +int ErrnoToSceKernelError(int error) { + return error + ORBIS_KERNEL_ERROR_UNKNOWN; } void SetPosixErrno(int e) { diff --git a/src/core/libraries/kernel/kernel.h b/src/core/libraries/kernel/kernel.h index f6865d22f..8e7f475ad 100644 --- a/src/core/libraries/kernel/kernel.h +++ b/src/core/libraries/kernel/kernel.h @@ -5,9 +5,8 @@ #include #include -#include "common/logging/log.h" #include "common/types.h" -#include "core/libraries/error_codes.h" +#include "core/libraries/kernel/orbis_error.h" namespace Core::Loader { class SymbolsResolver; @@ -38,7 +37,7 @@ struct WrapperImpl { u32 ret = f(args...); if (ret != 0) { // LOG_ERROR(Lib_Kernel, "Function {} returned {}", std::string_view{name.value}, ret); - ret += SCE_KERNEL_ERROR_UNKNOWN; + ret += ORBIS_KERNEL_ERROR_UNKNOWN; } return ret; } diff --git a/src/core/libraries/kernel/memory.cpp b/src/core/libraries/kernel/memory.cpp index 4d55fc2a3..606c5c185 100644 --- a/src/core/libraries/kernel/memory.cpp +++ b/src/core/libraries/kernel/memory.cpp @@ -9,9 +9,9 @@ #include "common/scope_exit.h" #include "common/singleton.h" #include "core/file_sys/fs.h" -#include "core/libraries/error_codes.h" #include "core/libraries/kernel/kernel.h" #include "core/libraries/kernel/memory.h" +#include "core/libraries/kernel/orbis_error.h" #include "core/libraries/libs.h" #include "core/linker.h" #include "core/memory.h" @@ -28,20 +28,20 @@ int PS4_SYSV_ABI sceKernelAllocateDirectMemory(s64 searchStart, s64 searchEnd, u u64 alignment, int memoryType, s64* physAddrOut) { if (searchStart < 0 || searchEnd <= searchStart) { LOG_ERROR(Kernel_Vmm, "Provided address range is invalid!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } const bool is_in_range = searchEnd - searchStart >= len; if (len <= 0 || !Common::Is16KBAligned(len) || !is_in_range) { LOG_ERROR(Kernel_Vmm, "Provided address range is invalid!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } if (alignment != 0 && !Common::Is16KBAligned(alignment)) { LOG_ERROR(Kernel_Vmm, "Alignment value is invalid!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } if (physAddrOut == nullptr) { LOG_ERROR(Kernel_Vmm, "Result physical address pointer is null!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } auto* memory = Core::Memory::Instance(); @@ -53,7 +53,7 @@ int PS4_SYSV_ABI sceKernelAllocateDirectMemory(s64 searchStart, s64 searchEnd, u "alignment = {:#x}, memoryType = {:#x}, physAddrOut = {:#x}", searchStart, searchEnd, len, alignment, memoryType, phys_addr); - return SCE_OK; + return ORBIS_OK; } s32 PS4_SYSV_ABI sceKernelAllocateMainDirectMemory(size_t len, size_t alignment, int memoryType, @@ -111,7 +111,7 @@ s32 PS4_SYSV_ABI sceKernelVirtualQuery(const void* addr, int flags, OrbisVirtual size_t infoSize) { LOG_INFO(Kernel_Vmm, "called addr = {}, flags = {:#x}", fmt::ptr(addr), flags); if (!addr) { - return SCE_KERNEL_ERROR_EACCES; + return ORBIS_KERNEL_ERROR_EACCES; } auto* memory = Core::Memory::Instance(); return memory->VirtualQuery(std::bit_cast(addr), flags, info); @@ -123,16 +123,16 @@ s32 PS4_SYSV_ABI sceKernelReserveVirtualRange(void** addr, u64 len, int flags, u if (addr == nullptr) { LOG_ERROR(Kernel_Vmm, "Address is invalid!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } if (len == 0 || !Common::Is16KBAligned(len)) { LOG_ERROR(Kernel_Vmm, "Map size is either zero or not 16KB aligned!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } if (alignment != 0) { if ((!std::has_single_bit(alignment) && !Common::Is16KBAligned(alignment))) { LOG_ERROR(Kernel_Vmm, "Alignment value is invalid!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } } @@ -141,7 +141,7 @@ s32 PS4_SYSV_ABI sceKernelReserveVirtualRange(void** addr, u64 len, int flags, u const auto map_flags = static_cast(flags); memory->Reserve(addr, in_addr, len, map_flags, alignment); - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceKernelMapNamedDirectMemory(void** addr, u64 len, int prot, int flags, @@ -149,16 +149,16 @@ int PS4_SYSV_ABI sceKernelMapNamedDirectMemory(void** addr, u64 len, int prot, i const char* name) { if (len == 0 || !Common::Is16KBAligned(len)) { LOG_ERROR(Kernel_Vmm, "Map size is either zero or not 16KB aligned!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } if (!Common::Is16KBAligned(directMemoryStart)) { LOG_ERROR(Kernel_Vmm, "Start address is not 16KB aligned!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } if (alignment != 0) { if ((!std::has_single_bit(alignment) && !Common::Is16KBAligned(alignment))) { LOG_ERROR(Kernel_Vmm, "Alignment value is invalid!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } } @@ -357,20 +357,20 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, size_ size_t alignment, u64* physAddrOut) { if (searchStart < 0 || searchEnd <= searchStart) { LOG_ERROR(Kernel_Vmm, "Provided address range is invalid!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } const bool is_in_range = searchEnd - searchStart >= len; if (len <= 0 || !Common::Is64KBAligned(len) || !is_in_range) { LOG_ERROR(Kernel_Vmm, "Provided address range is invalid!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } if (alignment != 0 && !Common::Is64KBAligned(alignment)) { LOG_ERROR(Kernel_Vmm, "Alignment value is invalid!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } if (physAddrOut == nullptr) { LOG_ERROR(Kernel_Vmm, "Result physical address pointer is null!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } auto* memory = Core::Memory::Instance(); @@ -391,16 +391,16 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addrIn, size_t len, size_t ali if (addrIn == nullptr) { LOG_ERROR(Kernel_Vmm, "Address is invalid!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } if (len == 0 || !Common::Is2MBAligned(len)) { LOG_ERROR(Kernel_Vmm, "Map size is either zero or not 2MB aligned!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } if (alignment != 0) { if ((!std::has_single_bit(alignment) && !Common::Is2MBAligned(alignment))) { LOG_ERROR(Kernel_Vmm, "Alignment value is invalid!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } } @@ -415,11 +415,11 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addrIn, size_t len, size_t ali s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, size_t len, int type, int prot, int flags) { if (addr == nullptr) { LOG_ERROR(Kernel_Vmm, "Address is invalid!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } if (len == 0 || !Common::Is64KBAligned(len)) { LOG_ERROR(Kernel_Vmm, "Map size is either zero or not 64KB aligned!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } LOG_INFO(Kernel_Vmm, "addr = {}, len = {:#x}, type = {:#x}, prot = {:#x}, flags = {:#x}", @@ -434,11 +434,11 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, size_t len, int type, int s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, size_t len, int flags) { if (addr == nullptr) { LOG_ERROR(Kernel_Vmm, "Address is invalid!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } if (len == 0 || !Common::Is64KBAligned(len)) { LOG_ERROR(Kernel_Vmm, "Map size is either zero or not 64KB aligned!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } LOG_INFO(Kernel_Vmm, "addr = {}, len = {:#x}, flags = {:#x}", fmt::ptr(addr), len, flags); @@ -493,7 +493,7 @@ int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len) { } auto* memory = Core::Memory::Instance(); memory->UnmapMemory(std::bit_cast(addr), len); - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI posix_munmap(void* addr, size_t len) { diff --git a/src/core/libraries/kernel/orbis_error.h b/src/core/libraries/kernel/orbis_error.h new file mode 100644 index 000000000..d19b3f3f1 --- /dev/null +++ b/src/core/libraries/kernel/orbis_error.h @@ -0,0 +1,108 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +// Libkernel library +constexpr int ORBIS_KERNEL_ERROR_UNKNOWN = 0x80020000; +constexpr int ORBIS_KERNEL_ERROR_EPERM = 0x80020001; +constexpr int ORBIS_KERNEL_ERROR_ENOENT = 0x80020002; +constexpr int ORBIS_KERNEL_ERROR_ESRCH = 0x80020003; +constexpr int ORBIS_KERNEL_ERROR_EINTR = 0x80020004; +constexpr int ORBIS_KERNEL_ERROR_EIO = 0x80020005; +constexpr int ORBIS_KERNEL_ERROR_ENXIO = 0x80020006; +constexpr int ORBIS_KERNEL_ERROR_E2BIG = 0x80020007; +constexpr int ORBIS_KERNEL_ERROR_ENOEXEC = 0x80020008; +constexpr int ORBIS_KERNEL_ERROR_EBADF = 0x80020009; +constexpr int ORBIS_KERNEL_ERROR_ECHILD = 0x8002000A; +constexpr int ORBIS_KERNEL_ERROR_EDEADLK = 0x8002000B; +constexpr int ORBIS_KERNEL_ERROR_ENOMEM = 0x8002000C; +constexpr int ORBIS_KERNEL_ERROR_EACCES = 0x8002000D; +constexpr int ORBIS_KERNEL_ERROR_EFAULT = 0x8002000E; +constexpr int ORBIS_KERNEL_ERROR_ENOTBLK = 0x8002000F; +constexpr int ORBIS_KERNEL_ERROR_EBUSY = 0x80020010; +constexpr int ORBIS_KERNEL_ERROR_EEXIST = 0x80020011; +constexpr int ORBIS_KERNEL_ERROR_EXDEV = 0x80020012; +constexpr int ORBIS_KERNEL_ERROR_ENODEV = 0x80020013; +constexpr int ORBIS_KERNEL_ERROR_ENOTDIR = 0x80020014; +constexpr int ORBIS_KERNEL_ERROR_EISDIR = 0x80020015; +constexpr int ORBIS_KERNEL_ERROR_EINVAL = 0x80020016; +constexpr int ORBIS_KERNEL_ERROR_ENFILE = 0x80020017; +constexpr int ORBIS_KERNEL_ERROR_EMFILE = 0x80020018; +constexpr int ORBIS_KERNEL_ERROR_ENOTTY = 0x80020019; +constexpr int ORBIS_KERNEL_ERROR_ETXTBSY = 0x8002001A; +constexpr int ORBIS_KERNEL_ERROR_EFBIG = 0x8002001B; +constexpr int ORBIS_KERNEL_ERROR_ENOSPC = 0x8002001C; +constexpr int ORBIS_KERNEL_ERROR_ESPIPE = 0x8002001D; +constexpr int ORBIS_KERNEL_ERROR_EROFS = 0x8002001E; +constexpr int ORBIS_KERNEL_ERROR_EMLINK = 0x8002001F; +constexpr int ORBIS_KERNEL_ERROR_EPIPE = 0x80020020; +constexpr int ORBIS_KERNEL_ERROR_EDOM = 0x80020021; +constexpr int ORBIS_KERNEL_ERROR_ERANGE = 0x80020022; +constexpr int ORBIS_KERNEL_ERROR_EAGAIN = 0x80020023; +constexpr int ORBIS_KERNEL_ERROR_EWOULDBLOCK = 0x80020023; +constexpr int ORBIS_KERNEL_ERROR_EINPROGRESS = 0x80020024; +constexpr int ORBIS_KERNEL_ERROR_EALREADY = 0x80020025; +constexpr int ORBIS_KERNEL_ERROR_ENOTSOCK = 0x80020026; +constexpr int ORBIS_KERNEL_ERROR_EDESTADDRREQ = 0x80020027; +constexpr int ORBIS_KERNEL_ERROR_EMSGSIZE = 0x80020028; +constexpr int ORBIS_KERNEL_ERROR_EPROTOTYPE = 0x80020029; +constexpr int ORBIS_KERNEL_ERROR_ENOPROTOOPT = 0x8002002A; +constexpr int ORBIS_KERNEL_ERROR_EPROTONOSUPPORT = 0x8002002B; +constexpr int ORBIS_KERNEL_ERROR_ESOCKTNOSUPPORT = 0x8002002C; +constexpr int ORBIS_KERNEL_ERROR_ENOTSUP = 0x8002002D; +constexpr int ORBIS_KERNEL_ERROR_EOPNOTSUPP = 0x8002002D; +constexpr int ORBIS_KERNEL_ERROR_EPFNOSUPPORT = 0x8002002E; +constexpr int ORBIS_KERNEL_ERROR_EAFNOSUPPORT = 0x8002002F; +constexpr int ORBIS_KERNEL_ERROR_EADDRINUSE = 0x80020030; +constexpr int ORBIS_KERNEL_ERROR_EADDRNOTAVAIL = 0x80020031; +constexpr int ORBIS_KERNEL_ERROR_ENETDOWN = 0x80020032; +constexpr int ORBIS_KERNEL_ERROR_ENETUNREACH = 0x80020033; +constexpr int ORBIS_KERNEL_ERROR_ENETRESET = 0x80020034; +constexpr int ORBIS_KERNEL_ERROR_ECONNABORTED = 0x80020035; +constexpr int ORBIS_KERNEL_ERROR_ECONNRESET = 0x80020036; +constexpr int ORBIS_KERNEL_ERROR_ENOBUFS = 0x80020037; +constexpr int ORBIS_KERNEL_ERROR_EISCONN = 0x80020038; +constexpr int ORBIS_KERNEL_ERROR_ENOTCONN = 0x80020039; +constexpr int ORBIS_KERNEL_ERROR_ESHUTDOWN = 0x8002003A; +constexpr int ORBIS_KERNEL_ERROR_ETOOMANYREFS = 0x8002003B; +constexpr int ORBIS_KERNEL_ERROR_ETIMEDOUT = 0x8002003C; +constexpr int ORBIS_KERNEL_ERROR_ECONNREFUSED = 0x8002003D; +constexpr int ORBIS_KERNEL_ERROR_ELOOP = 0x8002003E; +constexpr int ORBIS_KERNEL_ERROR_ENAMETOOLONG = 0x8002003F; +constexpr int ORBIS_KERNEL_ERROR_EHOSTDOWN = 0x80020040; +constexpr int ORBIS_KERNEL_ERROR_EHOSTUNREACH = 0x80020041; +constexpr int ORBIS_KERNEL_ERROR_ENOTEMPTY = 0x80020042; +constexpr int ORBIS_KERNEL_ERROR_EPROCLIM = 0x80020043; +constexpr int ORBIS_KERNEL_ERROR_EUSERS = 0x80020044; +constexpr int ORBIS_KERNEL_ERROR_EDQUOT = 0x80020045; +constexpr int ORBIS_KERNEL_ERROR_ESTALE = 0x80020046; +constexpr int ORBIS_KERNEL_ERROR_EREMOTE = 0x80020047; +constexpr int ORBIS_KERNEL_ERROR_EBADRPC = 0x80020048; +constexpr int ORBIS_KERNEL_ERROR_ERPCMISMATCH = 0x80020049; +constexpr int ORBIS_KERNEL_ERROR_EPROGUNAVAIL = 0x8002004A; +constexpr int ORBIS_KERNEL_ERROR_EPROGMISMATCH = 0x8002004B; +constexpr int ORBIS_KERNEL_ERROR_EPROCUNAVAIL = 0x8002004C; +constexpr int ORBIS_KERNEL_ERROR_ENOLCK = 0x8002004D; +constexpr int ORBIS_KERNEL_ERROR_ENOSYS = 0x8002004E; +constexpr int ORBIS_KERNEL_ERROR_EFTYPE = 0x8002004F; +constexpr int ORBIS_KERNEL_ERROR_EAUTH = 0x80020050; +constexpr int ORBIS_KERNEL_ERROR_ENEEDAUTH = 0x80020051; +constexpr int ORBIS_KERNEL_ERROR_EIDRM = 0x80020052; +constexpr int ORBIS_KERNEL_ERROR_ENOMSG = 0x80020053; +constexpr int ORBIS_KERNEL_ERROR_EOVERFLOW = 0x80020054; +constexpr int ORBIS_KERNEL_ERROR_ECANCELED = 0x80020055; +constexpr int ORBIS_KERNEL_ERROR_EILSEQ = 0x80020056; +constexpr int ORBIS_KERNEL_ERROR_ENOATTR = 0x80020057; +constexpr int ORBIS_KERNEL_ERROR_EDOOFUS = 0x80020058; +constexpr int ORBIS_KERNEL_ERROR_EBADMSG = 0x80020059; +constexpr int ORBIS_KERNEL_ERROR_EMULTIHOP = 0x8002005A; +constexpr int ORBIS_KERNEL_ERROR_ENOLINK = 0x8002005B; +constexpr int ORBIS_KERNEL_ERROR_EPROTO = 0x8002005C; +constexpr int ORBIS_KERNEL_ERROR_ENOTCAPABLE = 0x8002005D; +constexpr int ORBIS_KERNEL_ERROR_ECAPMODE = 0x8002005E; +constexpr int ORBIS_KERNEL_ERROR_ENOBLK = 0x8002005F; +constexpr int ORBIS_KERNEL_ERROR_EICV = 0x80020060; +constexpr int ORBIS_KERNEL_ERROR_ENOPLAYGOENT = 0x80020061; diff --git a/src/core/libraries/kernel/posix_error.h b/src/core/libraries/kernel/posix_error.h new file mode 100644 index 000000000..0f7cf3e50 --- /dev/null +++ b/src/core/libraries/kernel/posix_error.h @@ -0,0 +1,128 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +// Posix error codes +constexpr int POSIX_EPERM = 1; +constexpr int POSIX_ENOENT = 2; +constexpr int POSIX_ESRCH = 3; +constexpr int POSIX_EINTR = 4; +constexpr int POSIX_EIO = 5; +constexpr int POSIX_ENXIO = 6; +constexpr int POSIX_E2BIG = 7; +constexpr int POSIX_ENOEXEC = 8; +constexpr int POSIX_EBADF = 9; +constexpr int POSIX_ECHILD = 10; +constexpr int POSIX_EDEADLK = 11; +constexpr int POSIX_ENOMEM = 12; +constexpr int POSIX_EACCES = 13; +constexpr int POSIX_EFAULT = 14; +constexpr int POSIX_ENOTBLK = 15; +constexpr int POSIX_EBUSY = 16; +constexpr int POSIX_EEXIST = 17; +constexpr int POSIX_EXDEV = 18; +constexpr int POSIX_ENODEV = 19; +constexpr int POSIX_ENOTDIR = 20; +constexpr int POSIX_EISDIR = 21; +constexpr int POSIX_EINVAL = 22; +constexpr int POSIX_ENFILE = 23; +constexpr int POSIX_EMFILE = 24; +constexpr int POSIX_ENOTTY = 25; +constexpr int POSIX_ETXTBSY = 26; +constexpr int POSIX_EFBIG = 27; +constexpr int POSIX_ENOSPC = 28; +constexpr int POSIX_ESPIPE = 29; +constexpr int POSIX_EROFS = 30; +constexpr int POSIX_EMLINK = 31; +constexpr int POSIX_EPIPE = 32; +constexpr int POSIX_EDOM = 33; +constexpr int POSIX_ERANGE = 34; +constexpr int POSIX_EAGAIN = 35; +constexpr int POSIX_EWOULDBLOCK = 35; +constexpr int POSIX_EINPROGRESS = 36; +constexpr int POSIX_EALREADY = 37; +constexpr int POSIX_ENOTSOCK = 38; +constexpr int POSIX_EDESTADDRREQ = 39; +constexpr int POSIX_EMSGSIZE = 40; +constexpr int POSIX_EPROTOTYPE = 41; +constexpr int POSIX_ENOPROTOOPT = 42; +constexpr int POSIX_EPROTONOSUPPORT = 43; +constexpr int POSIX_ESOCKTNOSUPPORT = 44; +constexpr int POSIX_EOPNOTSUPP = 45; +constexpr int POSIX_ENOTSUP = 45; +constexpr int POSIX_EPFNOSUPPORT = 46; +constexpr int POSIX_EAFNOSUPPORT = 47; +constexpr int POSIX_EADDRINUSE = 48; +constexpr int POSIX_EADDRNOTAVAIL = 49; +constexpr int POSIX_ENETDOWN = 50; +constexpr int POSIX_ENETUNREACH = 51; +constexpr int POSIX_ENETRESET = 52; +constexpr int POSIX_ECONNABORTED = 53; +constexpr int POSIX_ECONNRESET = 54; +constexpr int POSIX_ENOBUFS = 55; +constexpr int POSIX_EISCONN = 56; +constexpr int POSIX_ENOTCONN = 57; +constexpr int POSIX_ESHUTDOWN = 58; +constexpr int POSIX_ETOOMANYREFS = 59; +constexpr int POSIX_ETIMEDOUT = 60; +constexpr int POSIX_ECONNREFUSED = 61; +constexpr int POSIX_ELOOP = 62; +constexpr int POSIX_ENAMETOOLONG = 63; +constexpr int POSIX_EHOSTDOWN = 64; +constexpr int POSIX_EHOSTUNREACH = 65; +constexpr int POSIX_ENOTEMPTY = 66; +constexpr int POSIX_EPROCLIM = 67; +constexpr int POSIX_EUSERS = 68; +constexpr int POSIX_EDQUOT = 69; +constexpr int POSIX_ESTALE = 70; +constexpr int POSIX_EREMOTE = 71; +constexpr int POSIX_EBADRPC = 72; +constexpr int POSIX_ERPCMISMATCH = 73; +constexpr int POSIX_EPROGUNAVAIL = 74; +constexpr int POSIX_EPROGMISMATCH = 75; +constexpr int POSIX_EPROCUNAVAIL = 76; +constexpr int POSIX_ENOLCK = 77; +constexpr int POSIX_ENOSYS = 78; +constexpr int POSIX_EFTYPE = 79; +constexpr int POSIX_EAUTH = 80; +constexpr int POSIX_ENEEDAUTH = 81; +constexpr int POSIX_EIDRM = 82; +constexpr int POSIX_ENOMSG = 83; +constexpr int POSIX_EOVERFLOW = 84; +constexpr int POSIX_ECANCELED = 85; +constexpr int POSIX_EILSEQ = 86; +constexpr int POSIX_ENOATTR = 87; +constexpr int POSIX_EDOOFUS = 88; +constexpr int POSIX_EBADMSG = 89; +constexpr int POSIX_EMULTIHOP = 90; +constexpr int POSIX_ENOLINK = 91; +constexpr int POSIX_EPROTO = 92; +constexpr int POSIX_ENOTCAPABLE = 93; +constexpr int POSIX_ECAPMODE = 94; +constexpr int POSIX_ENOBLK = 95; +constexpr int POSIX_EICV = 96; +constexpr int POSIX_ENOPLAYGOENT = 97; +constexpr int POSIX_EREVOKE = 98; +constexpr int POSIX_ESDKVERSION = 99; +constexpr int POSIX_ESTART = 100; +constexpr int POSIX_ESTOP = 101; +constexpr int POSIX_EINVALID2MB = 102; +constexpr int POSIX_ELAST = 102; +constexpr int POSIX_EADHOC = 160; +constexpr int POSIX_EINACTIVEDISABLED = 163; +constexpr int POSIX_ENETNODATA = 164; +constexpr int POSIX_ENETDESC = 165; +constexpr int POSIX_ENETDESCTIMEDOUT = 166; +constexpr int POSIX_ENETINTR = 167; +constexpr int POSIX_ERETURN = 205; +constexpr int POSIX_EFPOS = 152; +constexpr int POSIX_ENODATA = 1040; +constexpr int POSIX_ENOSR = 1050; +constexpr int POSIX_ENOSTR = 1051; +constexpr int POSIX_ENOTRECOVERABLE = 1056; +constexpr int POSIX_EOTHER = 1062; +constexpr int POSIX_EOWNERDEAD = 1064; +constexpr int POSIX_ETIME = 1074; diff --git a/src/core/libraries/kernel/process.cpp b/src/core/libraries/kernel/process.cpp index fc1d6e137..15e4ff820 100644 --- a/src/core/libraries/kernel/process.cpp +++ b/src/core/libraries/kernel/process.cpp @@ -5,7 +5,7 @@ #include "common/elf_info.h" #include "common/logging/log.h" #include "core/file_sys/fs.h" -#include "core/libraries/error_codes.h" +#include "core/libraries/kernel/orbis_error.h" #include "core/libraries/kernel/process.h" #include "core/libraries/libs.h" #include "core/linker.h" @@ -91,7 +91,7 @@ s32 PS4_SYSV_ABI sceKernelGetModuleInfoForUnwind(VAddr addr, int flags, OrbisModuleInfoForUnwind* info) { if (flags >= 3) { std::memset(info, 0, sizeof(OrbisModuleInfoForUnwind)); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } if (!info) { return ORBIS_KERNEL_ERROR_EFAULT; diff --git a/src/core/libraries/kernel/threads/condvar.cpp b/src/core/libraries/kernel/threads/condvar.cpp index 4437af964..cbe8f6ca7 100644 --- a/src/core/libraries/kernel/threads/condvar.cpp +++ b/src/core/libraries/kernel/threads/condvar.cpp @@ -3,8 +3,8 @@ #include #include "common/assert.h" -#include "core/libraries/error_codes.h" #include "core/libraries/kernel/kernel.h" +#include "core/libraries/kernel/posix_error.h" #include "core/libraries/kernel/threads/pthread.h" #include "core/libraries/kernel/threads/sleepq.h" #include "core/libraries/libs.h" diff --git a/src/core/libraries/kernel/threads/event_flag.cpp b/src/core/libraries/kernel/threads/event_flag.cpp index b0f08b91c..39925153c 100644 --- a/src/core/libraries/kernel/threads/event_flag.cpp +++ b/src/core/libraries/kernel/threads/event_flag.cpp @@ -7,7 +7,7 @@ #include "common/assert.h" #include "common/logging/log.h" -#include "core/libraries/error_codes.h" +#include "core/libraries/kernel/orbis_error.h" #include "core/libraries/libs.h" namespace Libraries::Kernel { diff --git a/src/core/libraries/kernel/threads/mutex.cpp b/src/core/libraries/kernel/threads/mutex.cpp index fc0bf09bd..4f11e32da 100644 --- a/src/core/libraries/kernel/threads/mutex.cpp +++ b/src/core/libraries/kernel/threads/mutex.cpp @@ -3,10 +3,9 @@ #include #include "common/assert.h" -#include "common/scope_exit.h" #include "common/types.h" -#include "core/libraries/error_codes.h" #include "core/libraries/kernel/kernel.h" +#include "core/libraries/kernel/posix_error.h" #include "core/libraries/kernel/threads/pthread.h" #include "core/libraries/libs.h" diff --git a/src/core/libraries/kernel/threads/pthread.cpp b/src/core/libraries/kernel/threads/pthread.cpp index 18ddcb9bf..4629980c9 100644 --- a/src/core/libraries/kernel/threads/pthread.cpp +++ b/src/core/libraries/kernel/threads/pthread.cpp @@ -4,8 +4,8 @@ #include "common/assert.h" #include "common/thread.h" #include "core/debug_state.h" -#include "core/libraries/error_codes.h" #include "core/libraries/kernel/kernel.h" +#include "core/libraries/kernel/posix_error.h" #include "core/libraries/kernel/threads/pthread.h" #include "core/libraries/kernel/threads/thread_state.h" #include "core/libraries/libs.h" @@ -380,7 +380,7 @@ int PS4_SYSV_ABI posix_sched_get_priority_min() { int PS4_SYSV_ABI posix_pthread_rename_np(PthreadT thread, const char* name) { LOG_INFO(Kernel_Pthread, "name = {}", name); thread->name = name; - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI posix_pthread_getschedparam(PthreadT pthread, SchedPolicy* policy, diff --git a/src/core/libraries/kernel/threads/pthread_attr.cpp b/src/core/libraries/kernel/threads/pthread_attr.cpp index ead0ff7df..a8e60ccf8 100644 --- a/src/core/libraries/kernel/threads/pthread_attr.cpp +++ b/src/core/libraries/kernel/threads/pthread_attr.cpp @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "core/libraries/error_codes.h" #include "core/libraries/kernel/kernel.h" +#include "core/libraries/kernel/posix_error.h" #include "core/libraries/kernel/threads/pthread.h" #include "core/libraries/kernel/threads/thread_state.h" #include "core/libraries/libs.h" diff --git a/src/core/libraries/kernel/threads/pthread_spec.cpp b/src/core/libraries/kernel/threads/pthread_spec.cpp index f0803b671..9e625da32 100644 --- a/src/core/libraries/kernel/threads/pthread_spec.cpp +++ b/src/core/libraries/kernel/threads/pthread_spec.cpp @@ -1,9 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "common/assert.h" -#include "core/libraries/error_codes.h" #include "core/libraries/kernel/kernel.h" +#include "core/libraries/kernel/posix_error.h" #include "core/libraries/kernel/threads/pthread.h" #include "core/libraries/libs.h" diff --git a/src/core/libraries/kernel/threads/rwlock.cpp b/src/core/libraries/kernel/threads/rwlock.cpp index 2d5a4cdb6..affaaf994 100644 --- a/src/core/libraries/kernel/threads/rwlock.cpp +++ b/src/core/libraries/kernel/threads/rwlock.cpp @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "core/libraries/error_codes.h" #include "core/libraries/kernel/kernel.h" +#include "core/libraries/kernel/posix_error.h" #include "core/libraries/kernel/threads/pthread.h" #include "core/libraries/libs.h" diff --git a/src/core/libraries/kernel/threads/semaphore.cpp b/src/core/libraries/kernel/threads/semaphore.cpp index db14c298c..e3c7e9092 100644 --- a/src/core/libraries/kernel/threads/semaphore.cpp +++ b/src/core/libraries/kernel/threads/semaphore.cpp @@ -5,9 +5,11 @@ #include #include #include + #include "common/logging/log.h" -#include "core/libraries/error_codes.h" #include "core/libraries/kernel/kernel.h" +#include "core/libraries/kernel/orbis_error.h" +#include "core/libraries/kernel/posix_error.h" #include "core/libraries/kernel/threads/pthread.h" #include "core/libraries/kernel/time.h" #include "core/libraries/libs.h" @@ -41,7 +43,7 @@ public: } if (timeout && *timeout == 0) { - return SCE_KERNEL_ERROR_ETIMEDOUT; + return ORBIS_KERNEL_ERROR_ETIMEDOUT; } // Create waiting thread object and add it into the list of waiters. @@ -50,7 +52,7 @@ public: // Perform the wait. const s32 result = waiter.Wait(lk, timeout); - if (result == SCE_KERNEL_ERROR_ETIMEDOUT) { + if (result == ORBIS_KERNEL_ERROR_ETIMEDOUT) { wait_list.erase(it); } return result; @@ -120,15 +122,15 @@ public: int GetResult(bool timed_out) { if (timed_out) { - return SCE_KERNEL_ERROR_ETIMEDOUT; + return ORBIS_KERNEL_ERROR_ETIMEDOUT; } if (was_deleted) { - return SCE_KERNEL_ERROR_EACCES; + return ORBIS_KERNEL_ERROR_EACCES; } if (was_cancled) { - return SCE_KERNEL_ERROR_ECANCELED; + return ORBIS_KERNEL_ERROR_ECANCELED; } - return SCE_OK; + return ORBIS_OK; } int Wait(std::unique_lock& lk, u32* timeout) { @@ -223,13 +225,13 @@ int PS4_SYSV_ABI sceKernelCancelSema(OrbisKernelSema sem, s32 setCount, s32* pNu int PS4_SYSV_ABI sceKernelDeleteSema(OrbisKernelSema sem) { if (!sem) { - return SCE_KERNEL_ERROR_ESRCH; + return ORBIS_KERNEL_ERROR_ESRCH; } sem->Delete(); return ORBIS_OK; } -int PS4_SYSV_ABI posix_sem_init(PthreadSem** sem, int pshared, unsigned int value) { +int PS4_SYSV_ABI posix_sem_init(PthreadSem** sem, int pshared, u32 value) { if (value > ORBIS_KERNEL_SEM_VALUE_MAX) { *__Error() = POSIX_EINVAL; return -1; diff --git a/src/core/libraries/kernel/threads/sleepq.h b/src/core/libraries/kernel/threads/sleepq.h index 9274942e3..5dc37645d 100644 --- a/src/core/libraries/kernel/threads/sleepq.h +++ b/src/core/libraries/kernel/threads/sleepq.h @@ -2,14 +2,13 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include -#include #include #include -#include "common/types.h" namespace Libraries::Kernel { struct Pthread; +struct SleepQueue; using ListBaseHook = boost::intrusive::list_base_hook>; @@ -17,7 +16,7 @@ using ListBaseHook = using SleepqList = boost::intrusive::list>; struct SleepQueue : public ListBaseHook { - std::list sq_blocked; + std::forward_list sq_blocked; SleepqList sq_freeq; void* sq_wchan; int sq_type; @@ -35,4 +34,4 @@ int SleepqRemove(SleepQueue* sq, Pthread* td); void SleepqDrop(SleepQueue* sq, void (*callback)(Pthread*, void*), void* arg); -} // namespace Libraries::Kernel \ No newline at end of file +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/thread_state.cpp b/src/core/libraries/kernel/threads/thread_state.cpp index e968c39ae..6c2d4d7ef 100644 --- a/src/core/libraries/kernel/threads/thread_state.cpp +++ b/src/core/libraries/kernel/threads/thread_state.cpp @@ -4,7 +4,7 @@ #include #include "common/alignment.h" #include "common/scope_exit.h" -#include "core/libraries/error_codes.h" +#include "core/libraries/kernel/posix_error.h" #include "core/libraries/kernel/threads/pthread.h" #include "core/libraries/kernel/threads/sleepq.h" #include "core/libraries/kernel/threads/thread_state.h" diff --git a/src/core/libraries/kernel/time.cpp b/src/core/libraries/kernel/time.cpp index 76ea5e353..b586431ab 100644 --- a/src/core/libraries/kernel/time.cpp +++ b/src/core/libraries/kernel/time.cpp @@ -5,7 +5,7 @@ #include "common/assert.h" #include "common/native_clock.h" -#include "core/libraries/error_codes.h" +#include "core/libraries/kernel/orbis_error.h" #include "core/libraries/kernel/time.h" #include "core/libraries/libs.h" @@ -80,7 +80,7 @@ u32 PS4_SYSV_ABI sceKernelSleep(u32 seconds) { int PS4_SYSV_ABI sceKernelClockGettime(s32 clock_id, OrbisKernelTimespec* tp) { if (tp == nullptr) { - return SCE_KERNEL_ERROR_EFAULT; + return ORBIS_KERNEL_ERROR_EFAULT; } clockid_t pclock_id = CLOCK_REALTIME; switch (clock_id) { @@ -105,9 +105,9 @@ int PS4_SYSV_ABI sceKernelClockGettime(s32 clock_id, OrbisKernelTimespec* tp) { tp->tv_sec = t.tv_sec; tp->tv_nsec = t.tv_nsec; if (result == 0) { - return SCE_OK; + return ORBIS_OK; } - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } int PS4_SYSV_ABI posix_clock_gettime(s32 clock_id, OrbisKernelTimespec* time) { @@ -126,11 +126,11 @@ int PS4_SYSV_ABI posix_nanosleep(const OrbisKernelTimespec* rqtp, OrbisKernelTim int PS4_SYSV_ABI sceKernelNanosleep(const OrbisKernelTimespec* rqtp, OrbisKernelTimespec* rmtp) { if (!rqtp || !rmtp) { - return SCE_KERNEL_ERROR_EFAULT; + return ORBIS_KERNEL_ERROR_EFAULT; } if (rqtp->tv_sec < 0 || rqtp->tv_nsec < 0) { - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } return posix_nanosleep(rqtp, rmtp); @@ -197,7 +197,7 @@ s32 PS4_SYSV_ABI sceKernelGettimezone(OrbisKernelTimezone* tz) { int PS4_SYSV_ABI posix_clock_getres(u32 clock_id, OrbisKernelTimespec* res) { if (res == nullptr) { - return SCE_KERNEL_ERROR_EFAULT; + return ORBIS_KERNEL_ERROR_EFAULT; } clockid_t pclock_id = CLOCK_REALTIME; switch (clock_id) { @@ -221,9 +221,9 @@ int PS4_SYSV_ABI posix_clock_getres(u32 clock_id, OrbisKernelTimespec* res) { res->tv_sec = t.tv_sec; res->tv_nsec = t.tv_nsec; if (result == 0) { - return SCE_OK; + return ORBIS_OK; } - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } int PS4_SYSV_ABI sceKernelConvertLocaltimeToUtc(time_t param_1, int64_t param_2, time_t* seconds, @@ -237,9 +237,9 @@ int PS4_SYSV_ABI sceKernelConvertLocaltimeToUtc(time_t param_1, int64_t param_2, if (dst_seconds) *dst_seconds = timezone->tz_dsttime * 60; } else { - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } - return SCE_OK; + return ORBIS_OK; } namespace Dev { @@ -254,7 +254,7 @@ Common::NativeClock* GetClock() { } // namespace Dev int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time, - struct OrbisTimesec* st, unsigned long* dst_sec) { + struct OrbisTimesec* st, u64* dst_sec) { LOG_TRACE(Kernel, "Called"); #ifdef __APPLE__ // std::chrono::current_zone() not available yet. diff --git a/src/core/libraries/kernel/time.h b/src/core/libraries/kernel/time.h index 508ef2152..6aa281aaf 100644 --- a/src/core/libraries/kernel/time.h +++ b/src/core/libraries/kernel/time.h @@ -81,7 +81,7 @@ int PS4_SYSV_ABI sceKernelConvertLocaltimeToUtc(time_t param_1, int64_t param_2, OrbisKernelTimezone* timezone, int* dst_seconds); int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time, OrbisTimesec* st, - unsigned long* dst_sec); + u64* dst_sec); void RegisterTime(Core::Loader::SymbolsResolver* sym); diff --git a/src/core/libraries/libpng/pngdec.cpp b/src/core/libraries/libpng/pngdec.cpp index fb9fae4d6..d6f291a62 100644 --- a/src/core/libraries/libpng/pngdec.cpp +++ b/src/core/libraries/libpng/pngdec.cpp @@ -4,9 +4,9 @@ #include #include "common/assert.h" #include "common/logging/log.h" -#include "core/libraries/error_codes.h" +#include "core/libraries/libpng/pngdec.h" +#include "core/libraries/libpng/pngdec_error.h" #include "core/libraries/libs.h" -#include "pngdec.h" namespace Libraries::PngDec { @@ -15,24 +15,25 @@ struct PngHandler { png_infop info_ptr; }; +struct PngStruct { + const u8* data; + size_t size; + u64 offset; +}; + static inline OrbisPngDecColorSpace MapPngColor(int color) { switch (color) { case PNG_COLOR_TYPE_GRAY: - return OrbisPngDecColorSpace::ORBIS_PNG_DEC_COLOR_SPACE_GRAYSCALE; - + return OrbisPngDecColorSpace::Grayscale; case PNG_COLOR_TYPE_GRAY_ALPHA: - return OrbisPngDecColorSpace::ORBIS_PNG_DEC_COLOR_SPACE_GRAYSCALE_ALPHA; - + return OrbisPngDecColorSpace::GrayscaleAlpha; case PNG_COLOR_TYPE_PALETTE: - return OrbisPngDecColorSpace::ORBIS_PNG_DEC_COLOR_SPACE_CLUT; - + return OrbisPngDecColorSpace::Clut; case PNG_COLOR_TYPE_RGB: - return OrbisPngDecColorSpace::ORBIS_PNG_DEC_COLOR_SPACE_RGB; - + return OrbisPngDecColorSpace::Rgb; case PNG_COLOR_TYPE_RGB_ALPHA: - return OrbisPngDecColorSpace::ORBIS_PNG_DEC_COLOR_SPACE_RGBA; + return OrbisPngDecColorSpace::Rgba; } - UNREACHABLE_MSG("unknown png color type"); } @@ -54,8 +55,8 @@ s32 PS4_SYSV_ABI scePngDecCreate(const OrbisPngDecCreateParam* param, void* memo LOG_ERROR(Lib_Png, "Invalid memory address!"); return ORBIS_PNG_DEC_ERROR_INVALID_ADDR; } - if (param->maxImageWidth - 1 > 1000000) { - LOG_ERROR(Lib_Png, "Invalid size! width = {}", param->maxImageWidth); + if (param->max_image_width - 1 > 1000000) { + LOG_ERROR(Lib_Png, "Invalid size! width = {}", param->max_image_width); return ORBIS_PNG_DEC_ERROR_INVALID_SIZE; } auto pngh = (PngHandler*)memoryAddress; @@ -86,7 +87,7 @@ s32 PS4_SYSV_ABI scePngDecDecode(OrbisPngDecHandle handle, const OrbisPngDecDeco LOG_ERROR(Lib_Png, "Invalid param!"); return ORBIS_PNG_DEC_ERROR_INVALID_PARAM; } - if (param->pngMemAddr == nullptr || param->pngMemAddr == nullptr) { + if (param->png_mem_addr == nullptr || param->image_mem_addr == nullptr) { LOG_ERROR(Lib_Png, "invalid image address!"); return ORBIS_PNG_DEC_ERROR_INVALID_ADDR; } @@ -98,76 +99,74 @@ s32 PS4_SYSV_ABI scePngDecDecode(OrbisPngDecHandle handle, const OrbisPngDecDeco auto pngh = (PngHandler*)handle; - struct pngstruct { - const u8* data; - size_t size; - u64 offset; - } pngdata = { - .data = (const u8*)param->pngMemAddr, - .size = param->pngMemSize, + const auto pngdata = PngStruct{ + .data = param->png_mem_addr, + .size = param->png_mem_size, .offset = 0, }; - - // Read png from memory png_set_read_fn(pngh->png_ptr, (void*)&pngdata, [](png_structp ps, png_bytep data, png_size_t len) { if (len == 0) return; - auto pngdata = (pngstruct*)png_get_io_ptr(ps); + auto pngdata = (PngStruct*)png_get_io_ptr(ps); ::memcpy(data, pngdata->data + pngdata->offset, len); pngdata->offset += len; }); - u32 width, height; - int color_type, bit_depth; png_read_info(pngh->png_ptr, pngh->info_ptr); - - width = png_get_image_width(pngh->png_ptr, pngh->info_ptr); - height = png_get_image_height(pngh->png_ptr, pngh->info_ptr); - color_type = MapPngColor(png_get_color_type(pngh->png_ptr, pngh->info_ptr)); - bit_depth = png_get_bit_depth(pngh->png_ptr, pngh->info_ptr); + const u32 width = png_get_image_width(pngh->png_ptr, pngh->info_ptr); + const u32 height = png_get_image_height(pngh->png_ptr, pngh->info_ptr); + const auto color_type = MapPngColor(png_get_color_type(pngh->png_ptr, pngh->info_ptr)); + const auto bit_depth = png_get_bit_depth(pngh->png_ptr, pngh->info_ptr); if (imageInfo != nullptr) { - imageInfo->bitDepth = bit_depth; - imageInfo->imageWidth = width; - imageInfo->imageHeight = height; - imageInfo->colorSpace = color_type; - imageInfo->imageFlag = 0; + imageInfo->bit_depth = bit_depth; + imageInfo->image_width = width; + imageInfo->image_height = height; + imageInfo->color_space = color_type; + imageInfo->image_flag = OrbisPngDecImageFlag::None; if (png_get_interlace_type(pngh->png_ptr, pngh->info_ptr) == 1) { - imageInfo->imageFlag |= OrbisPngDecImageFlag::ORBIS_PNG_DEC_IMAGE_FLAG_ADAM7_INTERLACE; + imageInfo->image_flag |= OrbisPngDecImageFlag::Adam7Interlace; } if (png_get_valid(pngh->png_ptr, pngh->info_ptr, PNG_INFO_tRNS)) { - - imageInfo->imageFlag |= ORBIS_PNG_DEC_IMAGE_FLAG_TRNS_CHUNK_EXIST; + imageInfo->image_flag |= OrbisPngDecImageFlag::TrnsChunkExist; } } - if (bit_depth == 16) + if (bit_depth == 16) { png_set_strip_16(pngh->png_ptr); - if (color_type == PNG_COLOR_TYPE_PALETTE) + } + if (color_type == OrbisPngDecColorSpace::Clut) { png_set_palette_to_rgb(pngh->png_ptr); - if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) + } + if (color_type == OrbisPngDecColorSpace::Grayscale && bit_depth < 8) { png_set_expand_gray_1_2_4_to_8(pngh->png_ptr); - if (png_get_valid(pngh->png_ptr, pngh->info_ptr, PNG_INFO_tRNS)) + } + if (png_get_valid(pngh->png_ptr, pngh->info_ptr, PNG_INFO_tRNS)) { png_set_tRNS_to_alpha(pngh->png_ptr); - if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + } + if (color_type == OrbisPngDecColorSpace::Grayscale || + color_type == OrbisPngDecColorSpace::GrayscaleAlpha) { png_set_gray_to_rgb(pngh->png_ptr); - if (param->pixelFormat == OrbisPngDecPixelFormat::ORBIS_PNG_DEC_PIXEL_FORMAT_B8G8R8A8) + } + if (param->pixel_format == OrbisPngDecPixelFormat::B8G8R8A8) { png_set_bgr(pngh->png_ptr); - if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY || - color_type == PNG_COLOR_TYPE_PALETTE) - png_set_add_alpha(pngh->png_ptr, param->alphaValue, PNG_FILLER_AFTER); + } + if (color_type == OrbisPngDecColorSpace::Rgb || + color_type == OrbisPngDecColorSpace::Grayscale || + color_type == OrbisPngDecColorSpace::Clut) { + png_set_add_alpha(pngh->png_ptr, param->alpha_value, PNG_FILLER_AFTER); + } - int pass = png_set_interlace_handling(pngh->png_ptr); + const s32 pass = png_set_interlace_handling(pngh->png_ptr); png_read_update_info(pngh->png_ptr, pngh->info_ptr); - auto const numChannels = png_get_channels(pngh->png_ptr, pngh->info_ptr); - auto horizontal_bytes = numChannels * width; + const s32 num_channels = png_get_channels(pngh->png_ptr, pngh->info_ptr); + const s32 horizontal_bytes = num_channels * width; + const s32 stride = param->image_pitch > 0 ? param->image_pitch : horizontal_bytes; - int stride = param->imagePitch > 0 ? param->imagePitch : horizontal_bytes; - - for (int j = 0; j < pass; j++) { // interlaced - auto ptr = (png_bytep)param->imageMemAddr; + for (int j = 0; j < pass; j++) { + auto ptr = reinterpret_cast(param->image_mem_addr); for (int y = 0; y < height; y++) { png_read_row(pngh->png_ptr, ptr, nullptr); ptr += stride; @@ -196,7 +195,7 @@ s32 PS4_SYSV_ABI scePngDecParseHeader(const OrbisPngDecParseParam* param, } u8 header[8]; - memcpy(header, param->pngMemAddr, 8); + memcpy(header, param->png_mem_addr, 8); // Check if the header indicates a valid PNG file if (png_sig_cmp(header, 0, 8)) { LOG_ERROR(Lib_Png, "Memory doesn't contain a valid png file"); @@ -209,18 +208,14 @@ s32 PS4_SYSV_ABI scePngDecParseHeader(const OrbisPngDecParseParam* param, // Create a libpng info structure auto info_ptr = png_create_info_struct(png_ptr); - struct pngstruct { - const u8* data; - size_t size; - u64 offset; - } pngdata = { - .data = (const u8*)param->pngMemAddr, - .size = param->pngMemSize, + const auto pngdata = PngStruct{ + .data = param->png_mem_addr, + .size = param->png_mem_size, .offset = 0, }; png_set_read_fn(png_ptr, (void*)&pngdata, [](png_structp ps, png_bytep data, png_size_t len) { - auto pngdata = (pngstruct*)png_get_io_ptr(ps); + auto pngdata = (PngStruct*)png_get_io_ptr(ps); ::memcpy(data, pngdata->data + pngdata->offset, len); pngdata->offset += len; }); @@ -229,17 +224,16 @@ s32 PS4_SYSV_ABI scePngDecParseHeader(const OrbisPngDecParseParam* param, // info. png_read_info(png_ptr, info_ptr); - imageInfo->imageWidth = png_get_image_width(png_ptr, info_ptr); - imageInfo->imageHeight = png_get_image_height(png_ptr, info_ptr); - imageInfo->colorSpace = MapPngColor(png_get_color_type(png_ptr, info_ptr)); - imageInfo->bitDepth = png_get_bit_depth(png_ptr, info_ptr); - imageInfo->imageFlag = 0; + imageInfo->image_width = png_get_image_width(png_ptr, info_ptr); + imageInfo->image_height = png_get_image_height(png_ptr, info_ptr); + imageInfo->color_space = MapPngColor(png_get_color_type(png_ptr, info_ptr)); + imageInfo->bit_depth = png_get_bit_depth(png_ptr, info_ptr); + imageInfo->image_flag = OrbisPngDecImageFlag::None; if (png_get_interlace_type(png_ptr, info_ptr) == 1) { - imageInfo->imageFlag |= OrbisPngDecImageFlag::ORBIS_PNG_DEC_IMAGE_FLAG_ADAM7_INTERLACE; + imageInfo->image_flag |= OrbisPngDecImageFlag::Adam7Interlace; } if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { - - imageInfo->imageFlag |= ORBIS_PNG_DEC_IMAGE_FLAG_TRNS_CHUNK_EXIST; + imageInfo->image_flag |= OrbisPngDecImageFlag::TrnsChunkExist; } png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); @@ -260,8 +254,8 @@ s32 PS4_SYSV_ABI scePngDecQueryMemorySize(const OrbisPngDecCreateParam* param) { LOG_ERROR(Lib_Png, "Invalid attribute! attribute = {}", param->attribute); return ORBIS_PNG_DEC_ERROR_INVALID_ADDR; } - if (param->maxImageWidth - 1 > 1000000) { - LOG_ERROR(Lib_Png, "Invalid size! width = {}", param->maxImageWidth); + if (param->max_image_width - 1 > 1000000) { + LOG_ERROR(Lib_Png, "Invalid size! width = {}", param->max_image_width); return ORBIS_PNG_DEC_ERROR_INVALID_SIZE; } return sizeof(PngHandler); @@ -279,4 +273,4 @@ void RegisterlibScePngDec(Core::Loader::SymbolsResolver* sym) { scePngDecDecodeWithInputControl); }; -} // namespace Libraries::PngDec \ No newline at end of file +} // namespace Libraries::PngDec diff --git a/src/core/libraries/libpng/pngdec.h b/src/core/libraries/libpng/pngdec.h index 9d807166c..c897d7c95 100644 --- a/src/core/libraries/libpng/pngdec.h +++ b/src/core/libraries/libpng/pngdec.h @@ -3,6 +3,7 @@ #pragma once +#include "common/enum.h" #include "common/types.h" namespace Core::Loader { @@ -11,59 +12,61 @@ class SymbolsResolver; namespace Libraries::PngDec { -enum OrbisPngDecColorSpace { - ORBIS_PNG_DEC_COLOR_SPACE_GRAYSCALE = 2, - ORBIS_PNG_DEC_COLOR_SPACE_RGB, - ORBIS_PNG_DEC_COLOR_SPACE_CLUT, - ORBIS_PNG_DEC_COLOR_SPACE_GRAYSCALE_ALPHA = 18, - ORBIS_PNG_DEC_COLOR_SPACE_RGBA +enum class OrbisPngDecColorSpace : u16 { + Grayscale = 2, + Rgb, + Clut, + GrayscaleAlpha = 18, + Rgba, }; -enum OrbisPngDecImageFlag { - ORBIS_PNG_DEC_IMAGE_FLAG_ADAM7_INTERLACE = 1, - ORBIS_PNG_DEC_IMAGE_FLAG_TRNS_CHUNK_EXIST = 2 +enum class OrbisPngDecImageFlag : u32 { + None = 0, + Adam7Interlace = 1, + TrnsChunkExist = 2, +}; +DECLARE_ENUM_FLAG_OPERATORS(OrbisPngDecImageFlag) + +enum class OrbisPngDecPixelFormat : u16 { + R8G8B8A8 = 0, + B8G8R8A8, }; -enum OrbisPngDecPixelFormat { - ORBIS_PNG_DEC_PIXEL_FORMAT_R8G8B8A8 = 0, - ORBIS_PNG_DEC_PIXEL_FORMAT_B8G8R8A8 -}; - -enum OrbisPngDecAttribute { - ORBIS_PNG_DEC_ATTRIBUTE_NONE = 0, - ORBIS_PNG_DEC_ATTRIBUTE_BIT_DEPTH_16 +enum class OrbisPngDecAttribute { + None = 0, + BitDepth16, }; struct OrbisPngDecParseParam { - const void* pngMemAddr; - u32 pngMemSize; + const u8* png_mem_addr; + u32 png_mem_size; u32 reserved; }; struct OrbisPngDecImageInfo { - u32 imageWidth; - u32 imageHeight; - u16 colorSpace; - u16 bitDepth; - u32 imageFlag; + u32 image_width; + u32 image_height; + OrbisPngDecColorSpace color_space; + u16 bit_depth; + OrbisPngDecImageFlag image_flag; }; struct OrbisPngDecCreateParam { - u32 thisSize; + u32 this_size; u32 attribute; - u32 maxImageWidth; + u32 max_image_width; }; -typedef void* OrbisPngDecHandle; +using OrbisPngDecHandle = void*; struct OrbisPngDecDecodeParam { - const void* pngMemAddr; - void* imageMemAddr; - u32 pngMemSize; - u32 imageMemSize; - u16 pixelFormat; - u16 alphaValue; - u32 imagePitch; + const u8* png_mem_addr; + u8* image_mem_addr; + u32 png_mem_size; + u32 image_mem_size; + OrbisPngDecPixelFormat pixel_format; + u16 alpha_value; + u32 image_pitch; }; s32 PS4_SYSV_ABI scePngDecCreate(const OrbisPngDecCreateParam* param, void* memoryAddress, @@ -77,4 +80,4 @@ s32 PS4_SYSV_ABI scePngDecParseHeader(const OrbisPngDecParseParam* param, s32 PS4_SYSV_ABI scePngDecQueryMemorySize(const OrbisPngDecCreateParam* param); void RegisterlibScePngDec(Core::Loader::SymbolsResolver* sym); -} // namespace Libraries::PngDec \ No newline at end of file +} // namespace Libraries::PngDec diff --git a/src/core/libraries/libpng/pngdec_error.h b/src/core/libraries/libpng/pngdec_error.h new file mode 100644 index 000000000..9745ed5d1 --- /dev/null +++ b/src/core/libraries/libpng/pngdec_error.h @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +// PngDec library +constexpr int ORBIS_PNG_DEC_ERROR_INVALID_ADDR = 0x80690001; +constexpr int ORBIS_PNG_DEC_ERROR_INVALID_SIZE = 0x80690002; +constexpr int ORBIS_PNG_DEC_ERROR_INVALID_PARAM = 0x80690003; +constexpr int ORBIS_PNG_DEC_ERROR_INVALID_HANDLE = 0x80690004; +constexpr int ORBIS_PNG_DEC_ERROR_INVALID_WORK_MEMORY = 0x80690005; +constexpr int ORBIS_PNG_DEC_ERROR_INVALID_DATA = 0x80690010; +constexpr int ORBIS_PNG_DEC_ERROR_UNSUPPORT_DATA = 0x80690011; +constexpr int ORBIS_PNG_DEC_ERROR_DECODE_ERROR = 0x80690012; +constexpr int ORBIS_PNG_DEC_ERROR_FATAL = 0x80690020; diff --git a/src/core/libraries/network/netctl.h b/src/core/libraries/network/netctl.h index 4482729a3..4992fffa9 100644 --- a/src/core/libraries/network/netctl.h +++ b/src/core/libraries/network/netctl.h @@ -23,7 +23,7 @@ constexpr int ORBIS_NET_CTL_HOSTNAME_LEN = 255 + 1; constexpr int ORBIS_NET_CTL_AUTH_NAME_LEN = 127 + 1; constexpr int ORBIS_NET_CTL_IPV4_ADDR_STR_LEN = 16; -typedef union OrbisNetCtlInfo { +union OrbisNetCtlInfo { u32 device; OrbisNetEtherAddr ether_addr; u32 mtu; @@ -45,7 +45,7 @@ typedef union OrbisNetCtlInfo { u32 http_proxy_config; char http_proxy_server[ORBIS_NET_CTL_HOSTNAME_LEN]; u16 http_proxy_port; -} SceNetCtlInfo; +}; // GetInfo codes constexpr int ORBIS_NET_CTL_INFO_DEVICE = 1; diff --git a/src/core/libraries/ngs2/ngs2.cpp b/src/core/libraries/ngs2/ngs2.cpp index b66c9b15b..7eb663413 100644 --- a/src/core/libraries/ngs2/ngs2.cpp +++ b/src/core/libraries/ngs2/ngs2.cpp @@ -1,13 +1,12 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "ngs2.h" -#include "ngs2_error.h" -#include "ngs2_impl.h" - #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" +#include "core/libraries/ngs2/ngs2.h" +#include "core/libraries/ngs2/ngs2_error.h" +#include "core/libraries/ngs2/ngs2_impl.h" namespace Libraries::Ngs2 { @@ -416,4 +415,4 @@ void RegisterlibSceNgs2(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("AbYvTOZ8Pts", "libSceNgs2", 1, "libSceNgs2", 1, 1, sceNgs2VoiceRunCommands); }; -} // namespace Libraries::Ngs2 \ No newline at end of file +} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2.h b/src/core/libraries/ngs2/ngs2.h index 0df83f565..a5f1f52a6 100644 --- a/src/core/libraries/ngs2/ngs2.h +++ b/src/core/libraries/ngs2/ngs2.h @@ -3,10 +3,8 @@ #pragma once -#include "common/types.h" - #include -#include +#include "common/types.h" namespace Core::Loader { class SymbolsResolver; @@ -18,7 +16,9 @@ class Ngs2; using SceNgs2Handle = Ngs2*; -enum SceNgs2HandleType { SCE_NGS2_HANDLE_TYPE_SYSTEM = 0 }; +enum class SceNgs2HandleType : u32 { + System = 0, +}; struct Ngs2Handle { void* selfPointer; @@ -69,4 +69,5 @@ struct StackBuffer { }; void RegisterlibSceNgs2(Core::Loader::SymbolsResolver* sym); -} // namespace Libraries::Ngs2 \ No newline at end of file + +} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/np_manager/np_manager.cpp b/src/core/libraries/np_manager/np_manager.cpp index 00070ef89..ec9cc6bf5 100644 --- a/src/core/libraries/np_manager/np_manager.cpp +++ b/src/core/libraries/np_manager/np_manager.cpp @@ -971,12 +971,11 @@ int PS4_SYSV_ABI sceNpGetGamePresenceStatusA() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNpGetNpId(OrbisUserServiceUserId userId, OrbisNpId* npId) { - LOG_INFO(Lib_NpManager, "userId {}", userId); - std::string name = Config::getUserName(); - // Fill the unused stuffs to 0 - memset(npId, 0, sizeof(*npId)); - strcpy(npId->handle.data, name.c_str()); +int PS4_SYSV_ABI sceNpGetNpId(OrbisUserServiceUserId user_id, OrbisNpId* np_id) { + LOG_INFO(Lib_NpManager, "user_id {}", user_id); + const auto name = Config::getUserName(); + std::memset(np_id, 0, sizeof(OrbisNpId)); + name.copy(np_id->handle.data, sizeof(np_id->handle.data)); return ORBIS_OK; } @@ -985,12 +984,11 @@ int PS4_SYSV_ABI sceNpGetNpReachabilityState() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNpGetOnlineId(s32 userId, OrbisNpOnlineId* onlineId) { - LOG_DEBUG(Lib_NpManager, "userId {}", userId); - std::string name = Config::getUserName(); - // Fill the unused stuffs to 0 - memset(onlineId, 0, sizeof(*onlineId)); - strcpy(onlineId->data, name.c_str()); +int PS4_SYSV_ABI sceNpGetOnlineId(s32 user_id, OrbisNpOnlineId* online_id) { + LOG_DEBUG(Lib_NpManager, "user_id {}", user_id); + const auto name = Config::getUserName(); + std::memset(online_id, 0, sizeof(OrbisNpOnlineId)); + name.copy(online_id->data, sizeof(online_id->data)); return ORBIS_OK; } @@ -1005,7 +1003,7 @@ int PS4_SYSV_ABI sceNpGetParentalControlInfoA() { } int PS4_SYSV_ABI sceNpGetState(s32 userId, OrbisNpState* state) { - *state = ORBIS_NP_STATE_SIGNED_OUT; + *state = OrbisNpState::SignedOut; LOG_DEBUG(Lib_NpManager, "Signed out"); return ORBIS_OK; } @@ -2518,7 +2516,7 @@ struct NpStateCallbackForNpToolkit { NpStateCallbackForNpToolkit NpStateCbForNp; int PS4_SYSV_ABI sceNpCheckCallbackForLib() { - Core::ExecuteGuest(NpStateCbForNp.func, 1, ORBIS_NP_STATE_SIGNED_OUT, NpStateCbForNp.userdata); + Core::ExecuteGuest(NpStateCbForNp.func, 1, OrbisNpState::SignedOut, NpStateCbForNp.userdata); return ORBIS_OK; } diff --git a/src/core/libraries/np_manager/np_manager.h b/src/core/libraries/np_manager/np_manager.h index 7e906cdc8..6ba588e5e 100644 --- a/src/core/libraries/np_manager/np_manager.h +++ b/src/core/libraries/np_manager/np_manager.h @@ -13,18 +13,14 @@ namespace Libraries::NpManager { constexpr int ORBIS_NP_ERROR_SIGNED_OUT = 0x80550006; -enum OrbisNpState { - ORBIS_NP_STATE_UNKNOWN = 0, - ORBIS_NP_STATE_SIGNED_OUT, - ORBIS_NP_STATE_SIGNED_IN -}; +enum class OrbisNpState : u32 { Unknown = 0, SignedOut, SignedIn }; using OrbisNpStateCallbackForNpToolkit = PS4_SYSV_ABI void (*)(s32 userId, OrbisNpState state, void* userdata); constexpr int ORBIS_NP_ONLINEID_MAX_LENGTH = 16; -typedef int OrbisUserServiceUserId; +using OrbisUserServiceUserId = s32; struct OrbisNpOnlineId { char data[ORBIS_NP_ONLINEID_MAX_LENGTH]; @@ -542,4 +538,4 @@ int PS4_SYSV_ABI sceNpRegisterStateCallbackForToolkit(OrbisNpStateCallbackForNpT int PS4_SYSV_ABI sceNpUnregisterStateCallbackForToolkit(); void RegisterlibSceNpManager(Core::Loader::SymbolsResolver* sym); -} // namespace Libraries::NpManager \ No newline at end of file +} // namespace Libraries::NpManager diff --git a/src/core/libraries/np_trophy/np_trophy.cpp b/src/core/libraries/np_trophy/np_trophy.cpp index e8fd57ef1..9324ed6bb 100644 --- a/src/core/libraries/np_trophy/np_trophy.cpp +++ b/src/core/libraries/np_trophy/np_trophy.cpp @@ -7,10 +7,10 @@ #include "common/logging/log.h" #include "common/path_util.h" #include "common/slot_vector.h" -#include "core/libraries/error_codes.h" #include "core/libraries/libs.h" -#include "np_trophy.h" -#include "trophy_ui.h" +#include "core/libraries/np_trophy/np_trophy.h" +#include "core/libraries/np_trophy/np_trophy_error.h" +#include "core/libraries/np_trophy/trophy_ui.h" namespace Libraries::NpTrophy { diff --git a/src/core/libraries/np_trophy/np_trophy.h b/src/core/libraries/np_trophy/np_trophy.h index ac13a9ab7..9abc795bc 100644 --- a/src/core/libraries/np_trophy/np_trophy.h +++ b/src/core/libraries/np_trophy/np_trophy.h @@ -29,14 +29,14 @@ constexpr int ORBIS_NP_TROPHY_INVALID_HANDLE = -1; constexpr int ORBIS_NP_TROPHY_INVALID_CONTEXT = -1; constexpr int ORBIS_NP_TROPHY_INVALID_TROPHY_ID = -1; -typedef int32_t OrbisNpTrophyHandle; -typedef int32_t OrbisNpTrophyContext; -typedef int32_t OrbisNpTrophyId; -typedef uint32_t OrbisNpTrophyFlagMask; +using OrbisNpTrophyHandle = s32; +using OrbisNpTrophyContext = s32; +using OrbisNpTrophyId = s32; +using OrbisNpTrophyFlagMask = u32; struct OrbisNpTrophyFlagArray { - OrbisNpTrophyFlagMask - flag_bits[ORBIS_NP_TROPHY_FLAG_SETSIZE >> ORBIS_NP_TROPHY_FLAG_BITS_SHIFT]; + static constexpr int NumMasks = ORBIS_NP_TROPHY_FLAG_SETSIZE >> ORBIS_NP_TROPHY_FLAG_BITS_SHIFT; + std::array flag_bits; }; void ORBIS_NP_TROPHY_FLAG_ZERO(OrbisNpTrophyFlagArray* p); @@ -49,18 +49,18 @@ struct OrbisNpTrophyData { size_t size; OrbisNpTrophyId trophy_id; bool unlocked; - uint8_t reserved[3]; + u8 reserved[3]; Rtc::OrbisRtcTick timestamp; }; -typedef int32_t OrbisNpTrophyGrade; +using OrbisNpTrophyGrade = s32; constexpr int ORBIS_NP_TROPHY_GRADE_UNKNOWN = 0; constexpr int ORBIS_NP_TROPHY_GRADE_PLATINUM = 1; constexpr int ORBIS_NP_TROPHY_GRADE_GOLD = 2; constexpr int ORBIS_NP_TROPHY_GRADE_SILVER = 3; constexpr int ORBIS_NP_TROPHY_GRADE_BRONZE = 4; -typedef int32_t OrbisNpTrophyGroupId; +using OrbisNpTrophyGroupId = s32; constexpr int ORBIS_NP_TROPHY_BASE_GAME_GROUP_ID = -1; constexpr int ORBIS_NP_TROPHY_INVALID_GROUP_ID = -2; @@ -70,29 +70,29 @@ struct OrbisNpTrophyDetails { OrbisNpTrophyGrade trophy_grade; OrbisNpTrophyGroupId group_id; bool hidden; - uint8_t reserved[3]; + u8 reserved[3]; char name[ORBIS_NP_TROPHY_NAME_MAX_SIZE]; char description[ORBIS_NP_TROPHY_DESCR_MAX_SIZE]; }; struct OrbisNpTrophyGameData { size_t size; - uint32_t unlocked_trophies; - uint32_t unlocked_platinum; - uint32_t unlocked_gold; - uint32_t unlocked_silver; - uint32_t unlocked_bronze; - uint32_t progress_percentage; + u32 unlocked_trophies; + u32 unlocked_platinum; + u32 unlocked_gold; + u32 unlocked_silver; + u32 unlocked_bronze; + u32 progress_percentage; }; struct OrbisNpTrophyGameDetails { size_t size; - uint32_t num_groups; - uint32_t num_trophies; - uint32_t num_platinum; - uint32_t num_gold; - uint32_t num_silver; - uint32_t num_bronze; + u32 num_groups; + u32 num_trophies; + u32 num_platinum; + u32 num_gold; + u32 num_silver; + u32 num_bronze; char title[ORBIS_NP_TROPHY_GAME_TITLE_MAX_SIZE]; char description[ORBIS_NP_TROPHY_GAME_DESCR_MAX_SIZE]; }; @@ -100,23 +100,23 @@ struct OrbisNpTrophyGameDetails { struct OrbisNpTrophyGroupData { size_t size; OrbisNpTrophyGroupId group_id; - uint32_t unlocked_trophies; - uint32_t unlocked_platinum; - uint32_t unlocked_gold; - uint32_t unlocked_silver; - uint32_t unlocked_bronze; - uint32_t progress_percentage; + u32 unlocked_trophies; + u32 unlocked_platinum; + u32 unlocked_gold; + u32 unlocked_silver; + u32 unlocked_bronze; + u32 progress_percentage; uint8_t reserved[4]; }; struct OrbisNpTrophyGroupDetails { size_t size; OrbisNpTrophyGroupId group_id; - uint32_t num_trophies; - uint32_t num_platinum; - uint32_t num_gold; - uint32_t num_silver; - uint32_t num_bronze; + u32 num_trophies; + u32 num_platinum; + u32 num_gold; + u32 num_silver; + u32 num_bronze; char title[ORBIS_NP_TROPHY_GROUP_TITLE_MAX_SIZE]; char description[ORBIS_NP_TROPHY_GROUP_DESCR_MAX_SIZE]; }; @@ -133,7 +133,7 @@ int PS4_SYSV_ABI sceNpTrophyConfigGetTrophySetVersion(); int PS4_SYSV_ABI sceNpTrophyConfigGetTrophyTitleDetails(); int PS4_SYSV_ABI sceNpTrophyConfigHasGroupFeature(); s32 PS4_SYSV_ABI sceNpTrophyCreateContext(OrbisNpTrophyContext* context, int32_t user_id, - uint32_t service_label, uint64_t options); + u32 service_label, uint64_t options); s32 PS4_SYSV_ABI sceNpTrophyCreateHandle(OrbisNpTrophyHandle* handle); int PS4_SYSV_ABI sceNpTrophyDestroyContext(OrbisNpTrophyContext context); s32 PS4_SYSV_ABI sceNpTrophyDestroyHandle(OrbisNpTrophyHandle handle); diff --git a/src/core/libraries/np_trophy/np_trophy_error.h b/src/core/libraries/np_trophy/np_trophy_error.h new file mode 100644 index 000000000..9506d9ca4 --- /dev/null +++ b/src/core/libraries/np_trophy/np_trophy_error.h @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +// NpTrophy library +constexpr int ORBIS_NP_TROPHY_ERROR_UNKNOWN = 0x80551600; +constexpr int ORBIS_NP_TROPHY_ERROR_NOT_INITIALIZED = 0x80551601; +constexpr int ORBIS_NP_TROPHY_ERROR_ALREADY_INITIALIZED = 0x80551602; +constexpr int ORBIS_NP_TROPHY_ERROR_OUT_OF_MEMORY = 0x80551603; +constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT = 0x80551604; +constexpr int ORBIS_NP_TROPHY_ERROR_INSUFFICIENT_BUFFER = 0x80551605; +constexpr int ORBIS_NP_TROPHY_ERROR_EXCEEDS_MAX = 0x80551606; +constexpr int ORBIS_NP_TROPHY_ERROR_ABORT = 0x80551607; +constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE = 0x80551608; +constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT = 0x80551609; +constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_TROPHY_ID = 0x8055160A; +constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_GROUP_ID = 0x8055160B; +constexpr int ORBIS_NP_TROPHY_ERROR_TROPHY_ALREADY_UNLOCKED = 0x8055160C; +constexpr int ORBIS_NP_TROPHY_ERROR_PLATINUM_CANNOT_UNLOCK = 0x8055160D; +constexpr int ORBIS_NP_TROPHY_ERROR_ACCOUNTID_NOT_MATCH = 0x8055160E; +constexpr int ORBIS_NP_TROPHY_ERROR_NOT_REGISTERED = 0x8055160F; +constexpr int ORBIS_NP_TROPHY_ERROR_ALREADY_REGISTERED = 0x80551610; +constexpr int ORBIS_NP_TROPHY_ERROR_BROKEN_DATA = 0x80551611; +constexpr int ORBIS_NP_TROPHY_ERROR_INSUFFICIENT_SPACE = 0x80551612; +constexpr int ORBIS_NP_TROPHY_ERROR_CONTEXT_ALREADY_EXISTS = 0x80551613; +constexpr int ORBIS_NP_TROPHY_ERROR_ICON_FILE_NOT_FOUND = 0x80551614; +constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_TRP_FILE_FORMAT = 0x80551616; +constexpr int ORBIS_NP_TROPHY_ERROR_UNSUPPORTED_TRP_FILE = 0x80551617; +constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_TROPHY_CONF_FORMAT = 0x80551618; +constexpr int ORBIS_NP_TROPHY_ERROR_UNSUPPORTED_TROPHY_CONF = 0x80551619; +constexpr int ORBIS_NP_TROPHY_ERROR_TROPHY_NOT_UNLOCKED = 0x8055161A; +constexpr int ORBIS_NP_TROPHY_ERROR_USER_NOT_FOUND = 0x8055161C; +constexpr int ORBIS_NP_TROPHY_ERROR_USER_NOT_LOGGED_IN = 0x8055161D; +constexpr int ORBIS_NP_TROPHY_ERROR_CONTEXT_USER_LOGOUT = 0x8055161E; +constexpr int ORBIS_NP_TROPHY_ERROR_USE_TRP_FOR_DEVELOPMENT = 0x8055161F; +constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_NP_SERVICE_LABEL = 0x80551621; +constexpr int ORBIS_NP_TROPHY_ERROR_NOT_SUPPORTED = 0x80551622; +constexpr int ORBIS_NP_TROPHY_ERROR_CONTEXT_EXCEEDS_MAX = 0x80551623; +constexpr int ORBIS_NP_TROPHY_ERROR_HANDLE_EXCEEDS_MAX = 0x80551624; +constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_USER_ID = 0x80551625; +constexpr int ORBIS_NP_TROPHY_ERROR_TITLE_CONF_NOT_INSTALLED = 0x80551626; +constexpr int ORBIS_NP_TROPHY_ERROR_BROKEN_TITLE_CONF = 0x80551627; +constexpr int ORBIS_NP_TROPHY_ERROR_INCONSISTENT_TITLE_CONF = 0x80551628; +constexpr int ORBIS_NP_TROPHY_ERROR_TITLE_BACKGROUND = 0x80551629; +constexpr int ORBIS_NP_TROPHY_ERROR_SCREENSHOT_DISABLED = 0x8055162B; +constexpr int ORBIS_NP_TROPHY_ERROR_SCREENSHOT_DISPLAY_BUFFER_NOT_IN_USE = 0x8055162D; diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index a1897d047..ec4186f11 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -1,12 +1,11 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "common/assert.h" #include "common/config.h" #include "common/logging/log.h" #include "common/singleton.h" -#include "core/libraries/error_codes.h" #include "core/libraries/libs.h" +#include "core/libraries/pad/pad_errors.h" #include "input/controller.h" #include "pad.h" @@ -99,8 +98,8 @@ int PS4_SYSV_ABI scePadGetControllerInformation(s32 handle, OrbisPadControllerIn pInfo->connectionType = ORBIS_PAD_PORT_TYPE_STANDARD; pInfo->connectedCount = 1; pInfo->connected = false; - pInfo->deviceClass = ORBIS_PAD_DEVICE_CLASS_STANDARD; - return SCE_OK; + pInfo->deviceClass = OrbisPadDeviceClass::Standard; + return ORBIS_OK; } pInfo->touchPadInfo.pixelDensity = 1; pInfo->touchPadInfo.resolution.x = 1920; @@ -110,12 +109,12 @@ int PS4_SYSV_ABI scePadGetControllerInformation(s32 handle, OrbisPadControllerIn pInfo->connectionType = ORBIS_PAD_PORT_TYPE_STANDARD; pInfo->connectedCount = 1; pInfo->connected = true; - pInfo->deviceClass = ORBIS_PAD_DEVICE_CLASS_STANDARD; + pInfo->deviceClass = OrbisPadDeviceClass::Standard; if (Config::getUseSpecialPad()) { pInfo->connectionType = ORBIS_PAD_PORT_TYPE_SPECIAL; pInfo->deviceClass = (OrbisPadDeviceClass)Config::getSpecialPadClass(); } - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI scePadGetDataInternal() { @@ -382,7 +381,7 @@ int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) { pData->connectedCount = 1; // connectedCount; pData->deviceUniqueDataLen = 0; - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI scePadReadStateExt() { diff --git a/src/core/libraries/pad/pad.h b/src/core/libraries/pad/pad.h index f94a642cf..68943b460 100644 --- a/src/core/libraries/pad/pad.h +++ b/src/core/libraries/pad/pad.h @@ -3,6 +3,7 @@ #pragma once +#include "common/enum.h" #include "common/types.h" namespace Core::Loader { @@ -18,18 +19,18 @@ constexpr int ORBIS_PAD_PORT_TYPE_STANDARD = 0; constexpr int ORBIS_PAD_PORT_TYPE_SPECIAL = 2; constexpr int ORBIS_PAD_PORT_TYPE_REMOTE_CONTROL = 16; -enum OrbisPadDeviceClass { - ORBIS_PAD_DEVICE_CLASS_INVALID = -1, - ORBIS_PAD_DEVICE_CLASS_STANDARD = 0, - ORBIS_PAD_DEVICE_CLASS_GUITAR = 1, - ORBIS_PAD_DEVICE_CLASS_DRUM = 2, - ORBIS_PAD_DEVICE_CLASS_DJ_TURNTABLE = 3, - ORBIS_PAD_DEVICE_CLASS_DANCEMAT = 4, - ORBIS_PAD_DEVICE_CLASS_NAVIGATION = 5, - ORBIS_PAD_DEVICE_CLASS_STEERING_WHEEL = 6, - ORBIS_PAD_DEVICE_CLASS_STICK = 7, - ORBIS_PAD_DEVICE_CLASS_FLIGHT_STICK = 8, - ORBIS_PAD_DEVICE_CLASS_GUN = 9, +enum class OrbisPadDeviceClass { + Invalid = -1, + Standard = 0, + Guitar = 1, + Drum = 2, + DjTurntable = 3, + Dancemat = 4, + Navigation = 5, + SteeringWheel = 6, + Stick = 7, + FightStick = 8, + Gun = 9, }; struct OrbisPadDeviceClassExtendedInformation { @@ -123,25 +124,27 @@ struct OrbisPadAnalogStick { u8 y; }; -enum OrbisPadButtonDataOffset { - ORBIS_PAD_BUTTON_L3 = 0x00000002, - ORBIS_PAD_BUTTON_R3 = 0x00000004, - ORBIS_PAD_BUTTON_OPTIONS = 0x00000008, - ORBIS_PAD_BUTTON_UP = 0x00000010, - ORBIS_PAD_BUTTON_RIGHT = 0x00000020, - ORBIS_PAD_BUTTON_DOWN = 0x00000040, - ORBIS_PAD_BUTTON_LEFT = 0x00000080, - ORBIS_PAD_BUTTON_L2 = 0x00000100, - ORBIS_PAD_BUTTON_R2 = 0x00000200, - ORBIS_PAD_BUTTON_L1 = 0x00000400, - ORBIS_PAD_BUTTON_R1 = 0x00000800, - ORBIS_PAD_BUTTON_TRIANGLE = 0x00001000, - ORBIS_PAD_BUTTON_CIRCLE = 0x00002000, - ORBIS_PAD_BUTTON_CROSS = 0x00004000, - ORBIS_PAD_BUTTON_SQUARE = 0x00008000, - ORBIS_PAD_BUTTON_TOUCH_PAD = 0x00100000, - ORBIS_PAD_BUTTON_INTERCEPTED = 0x80000000, +enum class OrbisPadButtonDataOffset : u32 { + None = 0, + L3 = 0x2, + R3 = 0x4, + Options = 0x8, + Up = 0x10, + Right = 0x20, + Down = 0x40, + Left = 0x80, + L2 = 0x100, + R2 = 0x200, + L1 = 0x400, + R1 = 0x800, + Triangle = 0x1000, + Circle = 0x2000, + Cross = 0x4000, + Square = 0x8000, + TouchPad = 0x100000, + Intercepted = 0x80000000, }; +DECLARE_ENUM_FLAG_OPERATORS(OrbisPadButtonDataOffset) struct OrbisFQuaternion { float x, y, z, w; @@ -173,7 +176,7 @@ struct OrbisPadExtensionUnitData { }; struct OrbisPadData { - u32 buttons; + OrbisPadButtonDataOffset buttons; OrbisPadAnalogStick leftStick; OrbisPadAnalogStick rightStick; OrbisPadAnalogButtons analogButtons; @@ -346,4 +349,4 @@ int PS4_SYSV_ABI Func_89C9237E393DA243(); int PS4_SYSV_ABI Func_EF103E845B6F0420(); void RegisterlibScePad(Core::Loader::SymbolsResolver* sym); -} // namespace Libraries::Pad \ No newline at end of file +} // namespace Libraries::Pad diff --git a/src/core/libraries/pad/pad_errors.h b/src/core/libraries/pad/pad_errors.h new file mode 100644 index 000000000..182c89219 --- /dev/null +++ b/src/core/libraries/pad/pad_errors.h @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +// Pad library +constexpr int ORBIS_PAD_ERROR_INVALID_ARG = 0x80920001; +constexpr int ORBIS_PAD_ERROR_INVALID_PORT = 0x80920002; +constexpr int ORBIS_PAD_ERROR_INVALID_HANDLE = 0x80920003; +constexpr int ORBIS_PAD_ERROR_ALREADY_OPENED = 0x80920004; +constexpr int ORBIS_PAD_ERROR_NOT_INITIALIZED = 0x80920005; +constexpr int ORBIS_PAD_ERROR_INVALID_LIGHTBAR_SETTING = 0x80920006; +constexpr int ORBIS_PAD_ERROR_DEVICE_NOT_CONNECTED = 0x80920007; +constexpr int ORBIS_PAD_ERROR_DEVICE_NO_HANDLE = 0x80920008; +constexpr int ORBIS_PAD_ERROR_FATAL = 0x809200FF; +constexpr int ORBIS_PAD_ERROR_NOT_PERMITTED = 0x80920101; +constexpr int ORBIS_PAD_ERROR_INVALID_BUFFER_LENGTH = 0x80920102; +constexpr int ORBIS_PAD_ERROR_INVALID_REPORT_LENGTH = 0x80920103; +constexpr int ORBIS_PAD_ERROR_INVALID_REPORT_ID = 0x80920104; +constexpr int ORBIS_PAD_ERROR_SEND_AGAIN = 0x80920105; diff --git a/src/core/libraries/playgo/playgo.cpp b/src/core/libraries/playgo/playgo.cpp index d4f5c6b7c..6fcf875da 100644 --- a/src/core/libraries/playgo/playgo.cpp +++ b/src/core/libraries/playgo/playgo.cpp @@ -2,7 +2,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" -#include "common/singleton.h" #include "core/file_format/playgo_chunk.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" @@ -11,6 +10,9 @@ namespace Libraries::PlayGo { +static constexpr OrbisPlayGoHandle PlaygoHandle = 1; +static std::unique_ptr playgo; + s32 PS4_SYSV_ABI sceDbgPlayGoRequestNextChunk() { LOG_ERROR(Lib_PlayGo, "(STUBBED)called"); return ORBIS_OK; @@ -24,57 +26,63 @@ s32 PS4_SYSV_ABI sceDbgPlayGoSnapshot() { s32 PS4_SYSV_ABI scePlayGoClose(OrbisPlayGoHandle handle) { LOG_INFO(Lib_PlayGo, "called"); - auto* playgo = Common::Singleton::Instance(); - - if (handle != 1) + if (handle != PlaygoHandle) { return ORBIS_PLAYGO_ERROR_BAD_HANDLE; - if (!playgo->initialized) + } + if (!playgo->initialized) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; + } + playgo.reset(); return ORBIS_OK; } s32 PS4_SYSV_ABI scePlayGoGetChunkId(OrbisPlayGoHandle handle, OrbisPlayGoChunkId* outChunkIdList, u32 numberOfEntries, u32* outEntries) { LOG_INFO(Lib_PlayGo, "called"); - auto* playgo = Common::Singleton::Instance(); - if (handle != 1) + if (handle != PlaygoHandle) { return ORBIS_PLAYGO_ERROR_BAD_HANDLE; - if (outEntries == nullptr) + } + if (outEntries == nullptr) { return ORBIS_PLAYGO_ERROR_BAD_POINTER; - if (outChunkIdList != nullptr && numberOfEntries == 0) + } + if (outChunkIdList != nullptr && numberOfEntries == 0) { return ORBIS_PLAYGO_ERROR_BAD_SIZE; + } if (playgo->GetPlaygoHeader().file_size == 0) { *outEntries = 0; - } else { - if (outChunkIdList == nullptr) { - *outEntries = playgo->chunks.size(); - } else { - if (numberOfEntries > playgo->chunks.size()) { - numberOfEntries = playgo->chunks.size(); - } - - if (numberOfEntries != 0) { - for (u32 i = 0; i < numberOfEntries; i++) { - outChunkIdList[i] = i; - } - *outEntries = numberOfEntries; - } - } + return ORBIS_OK; } + + if (outChunkIdList == nullptr) { + *outEntries = playgo->chunks.size(); + return ORBIS_OK; + } + + if (numberOfEntries > playgo->chunks.size()) { + numberOfEntries = playgo->chunks.size(); + } + + for (u32 i = 0; i < numberOfEntries; i++) { + outChunkIdList[i] = i; + } + *outEntries = numberOfEntries; return ORBIS_OK; } s32 PS4_SYSV_ABI scePlayGoGetEta(OrbisPlayGoHandle handle, const OrbisPlayGoChunkId* chunkIds, u32 numberOfEntries, OrbisPlayGoEta* outEta) { LOG_INFO(Lib_PlayGo, "called"); - if (handle != 1) + if (handle != PlaygoHandle) { return ORBIS_PLAYGO_ERROR_BAD_HANDLE; - if (chunkIds == nullptr || outEta == nullptr) + } + if (chunkIds == nullptr || outEta == nullptr) { return ORBIS_PLAYGO_ERROR_BAD_POINTER; - if (numberOfEntries == 0) + } + if (numberOfEntries == 0) { return ORBIS_PLAYGO_ERROR_BAD_SIZE; + } *outEta = 0; // all is loaded return ORBIS_OK; @@ -84,22 +92,23 @@ s32 PS4_SYSV_ABI scePlayGoGetInstallSpeed(OrbisPlayGoHandle handle, OrbisPlayGoInstallSpeed* outSpeed) { LOG_INFO(Lib_PlayGo, "called"); - auto* playgo = Common::Singleton::Instance(); - - if (handle != 1) + if (handle != PlaygoHandle) { return ORBIS_PLAYGO_ERROR_BAD_HANDLE; - if (outSpeed == nullptr) + } + if (outSpeed == nullptr) { return ORBIS_PLAYGO_ERROR_BAD_POINTER; - if (!playgo->initialized) + } + if (!playgo->initialized) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; + } std::scoped_lock lk{playgo->GetSpeedMutex()}; - if (playgo->speed == 0) { + if (playgo->speed == OrbisPlayGoInstallSpeed::Suspended) { using namespace std::chrono; if ((duration_cast(steady_clock::now().time_since_epoch()).count() - playgo->speed_tick) > 30 * 1000) { // 30sec - playgo->speed = ORBIS_PLAYGO_INSTALL_SPEED_TRICKLE; + playgo->speed = OrbisPlayGoInstallSpeed::Trickle; } } *outSpeed = playgo->speed; @@ -111,14 +120,15 @@ s32 PS4_SYSV_ABI scePlayGoGetLanguageMask(OrbisPlayGoHandle handle, OrbisPlayGoLanguageMask* outLanguageMask) { LOG_INFO(Lib_PlayGo, "called"); - auto* playgo = Common::Singleton::Instance(); - - if (handle != 1) + if (handle != PlaygoHandle) { return ORBIS_PLAYGO_ERROR_BAD_HANDLE; - if (outLanguageMask == nullptr) + } + if (outLanguageMask == nullptr) { return ORBIS_PLAYGO_ERROR_BAD_POINTER; - if (!playgo->initialized) + } + if (!playgo->initialized) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; + } *outLanguageMask = playgo->langMask; return ORBIS_OK; @@ -129,24 +139,27 @@ s32 PS4_SYSV_ABI scePlayGoGetLocus(OrbisPlayGoHandle handle, const OrbisPlayGoCh LOG_INFO(Lib_PlayGo, "called handle = {}, chunkIds = {}, numberOfEntries = {}", handle, *chunkIds, numberOfEntries); - auto* playgo = Common::Singleton::Instance(); - - if (handle != 1) + if (handle != PlaygoHandle) { return ORBIS_PLAYGO_ERROR_BAD_HANDLE; - if (chunkIds == nullptr || outLoci == nullptr) + } + if (chunkIds == nullptr || outLoci == nullptr) { return ORBIS_PLAYGO_ERROR_BAD_POINTER; - if (numberOfEntries == 0) + } + if (numberOfEntries == 0) { return ORBIS_PLAYGO_ERROR_BAD_SIZE; - if (!playgo->initialized) + } + if (!playgo->initialized) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; - if (playgo->GetPlaygoHeader().file_size == 0) + } + if (playgo->GetPlaygoHeader().file_size == 0) { return ORBIS_PLAYGO_ERROR_NOT_SUPPORT_PLAYGO; + } - for (uint32_t i = 0; i < numberOfEntries; i++) { + for (int i = 0; i < numberOfEntries; i++) { if (chunkIds[i] <= playgo->chunks.size()) { - outLoci[i] = OrbisPlayGoLocusValue::ORBIS_PLAYGO_LOCUS_LOCAL_FAST; + outLoci[i] = OrbisPlayGoLocus::LocalFast; } else { - outLoci[i] = ORBIS_PLAYGO_LOCUS_NOT_DOWNLOADED; + outLoci[i] = OrbisPlayGoLocus::NotDownloaded; return ORBIS_PLAYGO_ERROR_BAD_CHUNK_ID; } } @@ -158,18 +171,21 @@ s32 PS4_SYSV_ABI scePlayGoGetProgress(OrbisPlayGoHandle handle, const OrbisPlayG LOG_INFO(Lib_PlayGo, "called handle = {}, chunkIds = {}, numberOfEntries = {}", handle, *chunkIds, numberOfEntries); - auto* playgo = Common::Singleton::Instance(); - - if (handle != 1) + if (handle != PlaygoHandle) { return ORBIS_PLAYGO_ERROR_BAD_HANDLE; - if (chunkIds == nullptr || outProgress == nullptr) + } + if (chunkIds == nullptr || outProgress == nullptr) { return ORBIS_PLAYGO_ERROR_BAD_POINTER; - if (numberOfEntries == 0) + } + if (numberOfEntries == 0) { return ORBIS_PLAYGO_ERROR_BAD_SIZE; - if (!playgo->initialized) + } + if (!playgo->initialized) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; - if (playgo->GetPlaygoHeader().file_size == 0) + } + if (playgo->GetPlaygoHeader().file_size == 0) { return ORBIS_PLAYGO_ERROR_BAD_CHUNK_ID; + } outProgress->progressSize = 0; outProgress->totalSize = 0; @@ -194,16 +210,18 @@ s32 PS4_SYSV_ABI scePlayGoGetToDoList(OrbisPlayGoHandle handle, OrbisPlayGoToDo* u32 numberOfEntries, u32* outEntries) { LOG_INFO(Lib_PlayGo, "called handle = {} numberOfEntries = {}", handle, numberOfEntries); - auto* playgo = Common::Singleton::Instance(); - - if (handle != 1) + if (handle != PlaygoHandle) { return ORBIS_PLAYGO_ERROR_BAD_HANDLE; - if (outTodoList == nullptr || outEntries == nullptr) + } + if (outTodoList == nullptr || outEntries == nullptr) { return ORBIS_PLAYGO_ERROR_BAD_POINTER; - if (numberOfEntries == 0) + } + if (numberOfEntries == 0) { return ORBIS_PLAYGO_ERROR_BAD_SIZE; - if (!playgo->initialized) + } + if (!playgo->initialized) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; + } *outEntries = 0; // nothing to do return ORBIS_OK; } @@ -218,19 +236,19 @@ int scePlayGoConvertLanguage(int systemLang) { s32 PS4_SYSV_ABI scePlayGoInitialize(OrbisPlayGoInitParams* param) { LOG_INFO(Lib_PlayGo, "called, bufSize = {}", param->bufSize); - if (param->bufAddr == nullptr) + if (param->bufAddr == nullptr) { return ORBIS_PLAYGO_ERROR_BAD_POINTER; - if (param->bufSize < 0x200000) + } + if (param->bufSize < 0x200000) { return ORBIS_PLAYGO_ERROR_BAD_SIZE; + } - auto* playgo = Common::Singleton::Instance(); - + playgo = std::make_unique(); if (!playgo->initialized) { using namespace SystemService; - // get system lang - int systemLang = 0; - sceSystemServiceParamGetInt(ORBIS_SYSTEM_SERVICE_PARAM_ID_LANG, &systemLang); - playgo->langMask = scePlayGoConvertLanguage(systemLang); + s32 system_lang = 0; + sceSystemServiceParamGetInt(OrbisSystemServiceParamId::Lang, &system_lang); + playgo->langMask = scePlayGoConvertLanguage(system_lang); playgo->initialized = true; } else { return ORBIS_PLAYGO_ERROR_ALREADY_INITIALIZED; @@ -241,18 +259,20 @@ s32 PS4_SYSV_ABI scePlayGoInitialize(OrbisPlayGoInitParams* param) { s32 PS4_SYSV_ABI scePlayGoOpen(OrbisPlayGoHandle* outHandle, const void* param) { LOG_INFO(Lib_PlayGo, "called"); - auto* playgo = Common::Singleton::Instance(); - - if (outHandle == nullptr) + if (outHandle == nullptr) { return ORBIS_PLAYGO_ERROR_BAD_POINTER; - if (param) + } + if (param) { return ORBIS_PLAYGO_ERROR_INVALID_ARGUMENT; - if (!playgo->initialized) + } + if (!playgo->initialized) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; - if (playgo->GetPlaygoHeader().file_size == 0) + } + if (playgo->GetPlaygoHeader().file_size == 0) { return ORBIS_PLAYGO_ERROR_NOT_SUPPORT_PLAYGO; + } - playgo->handle = *outHandle = 1; + playgo->handle = *outHandle = PlaygoHandle; return ORBIS_OK; } @@ -260,21 +280,23 @@ s32 PS4_SYSV_ABI scePlayGoPrefetch(OrbisPlayGoHandle handle, const OrbisPlayGoCh u32 numberOfEntries, OrbisPlayGoLocus minimumLocus) { LOG_INFO(Lib_PlayGo, "called"); - auto* playgo = Common::Singleton::Instance(); - - if (handle != 1) + if (handle != PlaygoHandle) { return ORBIS_PLAYGO_ERROR_BAD_HANDLE; - if (chunkIds == nullptr) + } + if (chunkIds == nullptr) { return ORBIS_PLAYGO_ERROR_BAD_POINTER; - if (numberOfEntries == 0) + } + if (numberOfEntries == 0) { return ORBIS_PLAYGO_ERROR_BAD_SIZE; - if (!playgo->initialized) + } + if (!playgo->initialized) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; + } switch (minimumLocus) { - case ORBIS_PLAYGO_LOCUS_NOT_DOWNLOADED: - case ORBIS_PLAYGO_LOCUS_LOCAL_SLOW: - case ORBIS_PLAYGO_LOCUS_LOCAL_FAST: + case OrbisPlayGoLocus::NotDownloaded: + case OrbisPlayGoLocus::LocalSlow: + case OrbisPlayGoLocus::LocalFast: break; default: return ORBIS_PLAYGO_ERROR_BAD_LOCUS; @@ -285,24 +307,23 @@ s32 PS4_SYSV_ABI scePlayGoPrefetch(OrbisPlayGoHandle handle, const OrbisPlayGoCh s32 PS4_SYSV_ABI scePlayGoSetInstallSpeed(OrbisPlayGoHandle handle, OrbisPlayGoInstallSpeed speed) { LOG_INFO(Lib_PlayGo, "called"); - auto* playgo = Common::Singleton::Instance(); - - if (handle != 1) + if (handle != PlaygoHandle) { return ORBIS_PLAYGO_ERROR_BAD_HANDLE; - if (!playgo->initialized) + } + if (!playgo->initialized) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; + } switch (speed) { - case ORBIS_PLAYGO_INSTALL_SPEED_SUSPENDED: - case ORBIS_PLAYGO_INSTALL_SPEED_TRICKLE: - case ORBIS_PLAYGO_INSTALL_SPEED_FULL: + case OrbisPlayGoInstallSpeed::Suspended: + case OrbisPlayGoInstallSpeed::Trickle: + case OrbisPlayGoInstallSpeed::Full: break; default: return ORBIS_PLAYGO_ERROR_INVALID_ARGUMENT; } std::scoped_lock lk{playgo->GetSpeedMutex()}; - using namespace std::chrono; playgo->speed = speed; playgo->speed_tick = @@ -314,12 +335,13 @@ s32 PS4_SYSV_ABI scePlayGoSetInstallSpeed(OrbisPlayGoHandle handle, OrbisPlayGoI s32 PS4_SYSV_ABI scePlayGoSetLanguageMask(OrbisPlayGoHandle handle, OrbisPlayGoLanguageMask languageMask) { LOG_INFO(Lib_PlayGo, "called"); - auto* playgo = Common::Singleton::Instance(); - if (handle != 1) + if (handle != 1) { return ORBIS_PLAYGO_ERROR_BAD_HANDLE; - if (!playgo->initialized) + } + if (!playgo->initialized) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; + } playgo->langMask = languageMask; return ORBIS_OK; @@ -329,23 +351,24 @@ s32 PS4_SYSV_ABI scePlayGoSetToDoList(OrbisPlayGoHandle handle, const OrbisPlayG uint32_t numberOfEntries) { LOG_INFO(Lib_PlayGo, "called"); - auto* playgo = Common::Singleton::Instance(); - - if (handle != 1) + if (handle != PlaygoHandle) { return ORBIS_PLAYGO_ERROR_BAD_HANDLE; - if (todoList == nullptr) + } + if (todoList == nullptr) { return ORBIS_PLAYGO_ERROR_BAD_POINTER; - if (numberOfEntries == 0) + } + if (numberOfEntries == 0) { return ORBIS_PLAYGO_ERROR_BAD_SIZE; - if (!playgo->initialized) + } + if (!playgo->initialized) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; + } return ORBIS_OK; } s32 PS4_SYSV_ABI scePlayGoTerminate() { LOG_INFO(Lib_PlayGo, "called"); - auto* playgo = Common::Singleton::Instance(); if (playgo->initialized) { playgo->initialized = false; } else { diff --git a/src/core/libraries/playgo/playgo_types.h b/src/core/libraries/playgo/playgo_types.h index 62dea4f4e..885eee035 100644 --- a/src/core/libraries/playgo/playgo_types.h +++ b/src/core/libraries/playgo/playgo_types.h @@ -5,41 +5,39 @@ #include "common/types.h" -typedef u32 OrbisPlayGoHandle; -typedef u16 OrbisPlayGoChunkId; -typedef s8 OrbisPlayGoLocus; -typedef s32 OrbisPlayGoInstallSpeed; -typedef s64 OrbisPlayGoEta; -typedef u64 OrbisPlayGoLanguageMask; +using OrbisPlayGoHandle = u32; +using OrbisPlayGoChunkId = u16; +using OrbisPlayGoEta = s64; +using OrbisPlayGoLanguageMask = u64; -typedef struct OrbisPlayGoInitParams { +enum class OrbisPlayGoLocus : s8 { + NotDownloaded = 0, + LocalSlow = 2, + LocalFast = 3, +}; + +enum class OrbisPlayGoInstallSpeed : s32 { + Suspended = 0, + Trickle = 1, + Full = 2, +}; + +struct OrbisPlayGoInitParams { const void* bufAddr; u32 bufSize; u32 reserved; -} OrbisPlayGoInitParams; +}; -typedef struct OrbisPlayGoToDo { +struct OrbisPlayGoToDo { OrbisPlayGoChunkId chunkId; OrbisPlayGoLocus locus; s8 reserved; -} OrbisPlayGoToDo; +}; -typedef struct OrbisPlayGoProgress { - uint64_t progressSize; - uint64_t totalSize; -} OrbisPlayGoProgress; - -typedef enum OrbisPlayGoLocusValue { - ORBIS_PLAYGO_LOCUS_NOT_DOWNLOADED = 0, - ORBIS_PLAYGO_LOCUS_LOCAL_SLOW = 2, - ORBIS_PLAYGO_LOCUS_LOCAL_FAST = 3 -} OrbisPlayGoLocusValue; - -typedef enum OrbisPlayGoInstallSpeedValue { - ORBIS_PLAYGO_INSTALL_SPEED_SUSPENDED = 0, - ORBIS_PLAYGO_INSTALL_SPEED_TRICKLE = 1, - ORBIS_PLAYGO_INSTALL_SPEED_FULL = 2 -} OrbisPlayGoInstallSpeedValue; +struct OrbisPlayGoProgress { + u64 progressSize; + u64 totalSize; +}; constexpr int ORBIS_PLAYGO_ERROR_UNKNOWN = -2135818239; /* 0x80B20001 */ constexpr int ORBIS_PLAYGO_ERROR_FATAL = -2135818238; /* 0x80B20002 */ diff --git a/src/core/libraries/random/random.h b/src/core/libraries/random/random.h index b483cf6ed..172494106 100644 --- a/src/core/libraries/random/random.h +++ b/src/core/libraries/random/random.h @@ -10,9 +10,11 @@ class SymbolsResolver; } namespace Libraries::Random { -constexpr int32_t SCE_RANDOM_MAX_SIZE = 64; + +constexpr s32 SCE_RANDOM_MAX_SIZE = 64; s32 PS4_SYSV_ABI sceRandomGetRandomNumber(u8* buf, std::size_t size); void RegisterlibSceRandom(Core::Loader::SymbolsResolver* sym); -} // namespace Libraries::Random \ No newline at end of file + +} // namespace Libraries::Random diff --git a/src/core/libraries/rtc/rtc.cpp b/src/core/libraries/rtc/rtc.cpp index 1b8802970..f94c00134 100644 --- a/src/core/libraries/rtc/rtc.cpp +++ b/src/core/libraries/rtc/rtc.cpp @@ -4,7 +4,6 @@ #include #include "common/logging/log.h" -#include "core/libraries/error_codes.h" #include "core/libraries/kernel/process.h" #include "core/libraries/kernel/time.h" #include "core/libraries/libs.h" @@ -49,7 +48,7 @@ int PS4_SYSV_ABI sceRtcCheckValid(OrbisRtcDateTime* pTime) { if (pTime->microsecond >= 1000000) return ORBIS_RTC_ERROR_INVALID_MICROSECOND; - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcCompareTick(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2) { @@ -102,7 +101,7 @@ int PS4_SYSV_ABI sceRtcConvertUtcToLocalTime(OrbisRtcTick* pTickUtc, OrbisRtcTic } int PS4_SYSV_ABI sceRtcEnd() { - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcFormatRFC2822(char* pszDateTime, const OrbisRtcTick* pTickUtc, @@ -253,7 +252,7 @@ int PS4_SYSV_ABI sceRtcFormatRFC2822(char* pszDateTime, const OrbisRtcTick* pTic } } - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcFormatRFC2822LocalTime(char* pszDateTime, const OrbisRtcTick* pTickUtc) { @@ -374,7 +373,7 @@ int PS4_SYSV_ABI sceRtcFormatRFC3339Precise(char* pszDateTime, const OrbisRtcTic pszDateTime[i] = formattedString.c_str()[i]; } - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcFormatRFC3339PreciseLocalTime(char* pszDateTime, @@ -397,13 +396,13 @@ int PS4_SYSV_ABI sceRtcGetCurrentAdNetworkTick(OrbisRtcTick* pTick) { Kernel::OrbisKernelTimespec clocktime; int returnValue = Kernel::sceKernelClockGettime(Kernel::ORBIS_CLOCK_REALTIME, &clocktime); - if (returnValue == SCE_OK) { + if (returnValue == ORBIS_OK) { pTick->tick = clocktime.tv_nsec / 1000 + clocktime.tv_sec * 1000000 + UNIX_EPOCH_TICKS; } else { return ORBIS_RTC_ERROR_NOT_INITIALIZED; } - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcGetCurrentClock(OrbisRtcDateTime* pTime, int timeZone) { @@ -415,7 +414,7 @@ int PS4_SYSV_ABI sceRtcGetCurrentClock(OrbisRtcDateTime* pTime, int timeZone) { Kernel::OrbisKernelTimespec clocktime; int returnValue = Kernel::sceKernelClockGettime(Kernel::ORBIS_CLOCK_REALTIME, &clocktime); - if (returnValue == SCE_OK) { + if (returnValue == ORBIS_OK) { OrbisRtcTick clockTick; clockTick.tick = clocktime.tv_nsec / 1000 + clocktime.tv_sec * 1000000 + UNIX_EPOCH_TICKS; @@ -461,13 +460,13 @@ int PS4_SYSV_ABI sceRtcGetCurrentDebugNetworkTick(OrbisRtcTick* pTick) { Kernel::OrbisKernelTimespec clocktime; int returnValue = Kernel::sceKernelClockGettime(Kernel::ORBIS_CLOCK_REALTIME, &clocktime); - if (returnValue == SCE_OK) { + if (returnValue == ORBIS_OK) { pTick->tick = clocktime.tv_nsec / 1000 + clocktime.tv_sec * 1000000 + UNIX_EPOCH_TICKS; } else { return ORBIS_RTC_ERROR_NOT_INITIALIZED; } - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcGetCurrentNetworkTick(OrbisRtcTick* pTick) { @@ -479,13 +478,13 @@ int PS4_SYSV_ABI sceRtcGetCurrentNetworkTick(OrbisRtcTick* pTick) { Kernel::OrbisKernelTimespec clocktime; int returnValue = Kernel::sceKernelClockGettime(Kernel::ORBIS_CLOCK_REALTIME, &clocktime); - if (returnValue == SCE_OK) { + if (returnValue == ORBIS_OK) { pTick->tick = clocktime.tv_nsec / 1000 + clocktime.tv_sec * 1000000 + UNIX_EPOCH_TICKS; } else { return ORBIS_RTC_ERROR_NOT_INITIALIZED; } - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcGetCurrentRawNetworkTick(OrbisRtcTick* pTick) { @@ -497,13 +496,13 @@ int PS4_SYSV_ABI sceRtcGetCurrentRawNetworkTick(OrbisRtcTick* pTick) { Kernel::OrbisKernelTimespec clocktime; int returnValue = Kernel::sceKernelClockGettime(Kernel::ORBIS_CLOCK_REALTIME, &clocktime); - if (returnValue == SCE_OK) { + if (returnValue == ORBIS_OK) { pTick->tick = clocktime.tv_nsec / 1000 + clocktime.tv_sec * 1000000 + UNIX_EPOCH_TICKS; } else { return ORBIS_RTC_ERROR_NOT_INITIALIZED; } - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcGetCurrentTick(OrbisRtcTick* pTick) { @@ -519,7 +518,7 @@ int PS4_SYSV_ABI sceRtcGetCurrentTick(OrbisRtcTick* pTick) { pTick->tick = clocktime.tv_nsec / 1000 + clocktime.tv_sec * 1000000 + UNIX_EPOCH_TICKS; } - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcGetDayOfWeek(int year, int month, int day) { @@ -576,14 +575,14 @@ int PS4_SYSV_ABI sceRtcGetDaysInMonth(int year, int month) { return lastDay; } -int PS4_SYSV_ABI sceRtcGetDosTime(OrbisRtcDateTime* pTime, unsigned int* dosTime) { +int PS4_SYSV_ABI sceRtcGetDosTime(OrbisRtcDateTime* pTime, u32* dosTime) { LOG_TRACE(Lib_Rtc, "called"); if (pTime == nullptr || dosTime == nullptr) return ORBIS_RTC_ERROR_INVALID_POINTER; int isValid = sceRtcCheckValid(pTime); - if (isValid != SCE_OK) { + if (isValid != ORBIS_OK) { return isValid; } @@ -594,7 +593,7 @@ int PS4_SYSV_ABI sceRtcGetDosTime(OrbisRtcDateTime* pTime, unsigned int* dosTime *dosTime |= (pTime->month & 0x0F) << 21; *dosTime |= ((pTime->year - 1980) & 0x7F) << 25; - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcGetTick(OrbisRtcDateTime* pTime, OrbisRtcTick* pTick) { @@ -629,10 +628,10 @@ int PS4_SYSV_ABI sceRtcGetTick(OrbisRtcDateTime* pTime, OrbisRtcTick* pTick) { pTick->tick = days + msec; - return SCE_OK; + return ORBIS_OK; } -unsigned int PS4_SYSV_ABI sceRtcGetTickResolution() { +u32 PS4_SYSV_ABI sceRtcGetTickResolution() { LOG_TRACE(Lib_Rtc, "called"); return 1000000; @@ -645,7 +644,7 @@ int PS4_SYSV_ABI sceRtcGetTime_t(OrbisRtcDateTime* pTime, time_t* llTime) { return ORBIS_RTC_ERROR_INVALID_POINTER; int isValid = sceRtcCheckValid(pTime); - if (isValid != SCE_OK) { + if (isValid != ORBIS_OK) { return isValid; } @@ -658,7 +657,7 @@ int PS4_SYSV_ABI sceRtcGetTime_t(OrbisRtcDateTime* pTime, time_t* llTime) { *llTime = (timeTick.tick - UNIX_EPOCH_TICKS) / 1000000; } - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcGetWin32FileTime(OrbisRtcDateTime* pTime, uint64_t* ulWin32Time) { @@ -668,7 +667,7 @@ int PS4_SYSV_ABI sceRtcGetWin32FileTime(OrbisRtcDateTime* pTime, uint64_t* ulWin return ORBIS_RTC_ERROR_INVALID_POINTER; int isValid = sceRtcCheckValid(pTime); - if (isValid != SCE_OK) { + if (isValid != ORBIS_OK) { return isValid; } @@ -681,11 +680,11 @@ int PS4_SYSV_ABI sceRtcGetWin32FileTime(OrbisRtcDateTime* pTime, uint64_t* ulWin *ulWin32Time = (timeTick.tick - WIN32_FILETIME_EPOCH_TICKS) * 10; } - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcInit() { - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcIsLeapYear(int yearInt) { @@ -790,7 +789,7 @@ int PS4_SYSV_ABI sceRtcParseDateTime(OrbisRtcTick* pTickUtc, const char* pszDate sceRtcGetTick(&dateTime, pTickUtc); } - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcParseRFC3339(OrbisRtcTick* pTickUtc, const char* pszDateTime) { @@ -825,7 +824,7 @@ int PS4_SYSV_ABI sceRtcParseRFC3339(OrbisRtcTick* pTickUtc, const char* pszDateT } } - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcSetConf() { @@ -869,7 +868,7 @@ int PS4_SYSV_ABI sceRtcSetDosTime(OrbisRtcDateTime* pTime, u32 dosTime) { pTime->day = days & 0x1f; pTime->month = (days >> 5) & 0x0f; pTime->year = (days >> 9) + 1980; - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcSetTick(OrbisRtcDateTime* pTime, OrbisRtcTick* pTick) { @@ -918,7 +917,7 @@ int PS4_SYSV_ABI sceRtcSetTick(OrbisRtcDateTime* pTime, OrbisRtcTick* pTick) { msec %= 1000000; pTime->microsecond = msec; - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcSetTime_t(OrbisRtcDateTime* pTime, time_t llTime) { @@ -946,7 +945,7 @@ int PS4_SYSV_ABI sceRtcSetTime_t(OrbisRtcDateTime* pTime, time_t llTime) { newTick.tick += UNIX_EPOCH_TICKS; sceRtcSetTick(pTime, &newTick); - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcSetWin32FileTime(OrbisRtcDateTime* pTime, int64_t ulWin32Time) { @@ -962,7 +961,7 @@ int PS4_SYSV_ABI sceRtcSetWin32FileTime(OrbisRtcDateTime* pTime, int64_t ulWin32 sceRtcSetTick(pTime, &convertedTick); - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcTickAddDays(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, int32_t lAdd) { @@ -973,7 +972,7 @@ int PS4_SYSV_ABI sceRtcTickAddDays(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, i pTick1->tick = (lAdd * 86400000000) + pTick2->tick; - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcTickAddHours(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, int32_t lAdd) { @@ -984,7 +983,7 @@ int PS4_SYSV_ABI sceRtcTickAddHours(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, pTick1->tick = (lAdd * 3600000000) + pTick2->tick; - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcTickAddMicroseconds(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, @@ -996,7 +995,7 @@ int PS4_SYSV_ABI sceRtcTickAddMicroseconds(OrbisRtcTick* pTick1, OrbisRtcTick* p pTick1->tick = lAdd + pTick2->tick; - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcTickAddMinutes(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, int64_t lAdd) { @@ -1007,7 +1006,7 @@ int PS4_SYSV_ABI sceRtcTickAddMinutes(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2 pTick1->tick = (lAdd * 60000000) + pTick2->tick; - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcTickAddMonths(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, int32_t lAdd) { @@ -1018,7 +1017,7 @@ int PS4_SYSV_ABI sceRtcTickAddMonths(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, if (lAdd == 0) { pTick1->tick = pTick2->tick; - return SCE_OK; + return ORBIS_OK; } OrbisRtcDateTime time; @@ -1054,13 +1053,13 @@ int PS4_SYSV_ABI sceRtcTickAddMonths(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, } int timeIsValid = sceRtcCheckValid(&time); - if (timeIsValid == SCE_OK) { + if (timeIsValid == ORBIS_OK) { sceRtcGetTick(&time, pTick1); } else { return timeIsValid; } - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcTickAddSeconds(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, int64_t lAdd) { @@ -1071,7 +1070,7 @@ int PS4_SYSV_ABI sceRtcTickAddSeconds(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2 pTick1->tick = (lAdd * 1000000) + pTick2->tick; - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcTickAddTicks(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, int64_t lAdd) { @@ -1082,7 +1081,7 @@ int PS4_SYSV_ABI sceRtcTickAddTicks(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, pTick1->tick = lAdd + pTick2->tick; - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcTickAddWeeks(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, int32_t lAdd) { @@ -1093,7 +1092,7 @@ int PS4_SYSV_ABI sceRtcTickAddWeeks(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, pTick1->tick = (lAdd * 604800000000) + pTick2->tick; - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcTickAddYears(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, int32_t lAdd) { @@ -1106,7 +1105,7 @@ int PS4_SYSV_ABI sceRtcTickAddYears(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, if (lAdd == 0) { pTick1->tick = pTick2->tick; - return SCE_OK; + return ORBIS_OK; } sceRtcSetTick(&time, pTick1); @@ -1114,13 +1113,13 @@ int PS4_SYSV_ABI sceRtcTickAddYears(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, time.year += lAdd; int timeIsValid = sceRtcCheckValid(&time); - if (timeIsValid == SCE_OK) { + if (timeIsValid == ORBIS_OK) { sceRtcGetTick(&time, pTick1); } else { return timeIsValid; } - return SCE_OK; + return ORBIS_OK; } void RegisterlibSceRtc(Core::Loader::SymbolsResolver* sym) { diff --git a/src/core/libraries/rtc/rtc.h b/src/core/libraries/rtc/rtc.h index c41040863..eb7afa00c 100644 --- a/src/core/libraries/rtc/rtc.h +++ b/src/core/libraries/rtc/rtc.h @@ -3,6 +3,7 @@ #pragma once +#include #include "common/types.h" namespace Core::Loader { @@ -19,21 +20,21 @@ constexpr int ORBIS_RTC_DAYOFWEEK_THURSDAY = 4; constexpr int ORBIS_RTC_DAYOFWEEK_FRIDAY = 5; constexpr int ORBIS_RTC_DAYOFWEEK_SATURDAY = 6; -constexpr int64_t UNIX_EPOCH_TICKS = 0xdcbffeff2bc000; -constexpr int64_t WIN32_FILETIME_EPOCH_TICKS = 0xb36168b6a58000; +constexpr s64 UNIX_EPOCH_TICKS = 0xdcbffeff2bc000; +constexpr s64 WIN32_FILETIME_EPOCH_TICKS = 0xb36168b6a58000; struct OrbisRtcTick { - uint64_t tick; + u64 tick; }; struct OrbisRtcDateTime { - uint16_t year; - uint16_t month; - uint16_t day; - uint16_t hour; - uint16_t minute; - uint16_t second; - uint32_t microsecond; + u16 year; + u16 month; + u16 day; + u16 hour; + u16 minute; + u16 second; + u32 microsecond; }; int PS4_SYSV_ABI sceRtcCheckValid(OrbisRtcDateTime* pTime); @@ -58,9 +59,9 @@ int PS4_SYSV_ABI sceRtcGetCurrentRawNetworkTick(OrbisRtcTick* pTick); int PS4_SYSV_ABI sceRtcGetCurrentTick(OrbisRtcTick* pTick); int PS4_SYSV_ABI sceRtcGetDayOfWeek(int year, int month, int day); int PS4_SYSV_ABI sceRtcGetDaysInMonth(int year, int month); -int PS4_SYSV_ABI sceRtcGetDosTime(OrbisRtcDateTime* pTime, unsigned int* dosTime); +int PS4_SYSV_ABI sceRtcGetDosTime(OrbisRtcDateTime* pTime, u32* dosTime); int PS4_SYSV_ABI sceRtcGetTick(OrbisRtcDateTime* pTime, OrbisRtcTick* pTick); -unsigned int PS4_SYSV_ABI sceRtcGetTickResolution(); +u32 PS4_SYSV_ABI sceRtcGetTickResolution(); int PS4_SYSV_ABI sceRtcGetTime_t(OrbisRtcDateTime* pTime, time_t* llTime); int PS4_SYSV_ABI sceRtcGetWin32FileTime(OrbisRtcDateTime* pTime, uint64_t* ulWin32Time); int PS4_SYSV_ABI sceRtcInit(); @@ -88,4 +89,4 @@ int PS4_SYSV_ABI sceRtcTickAddWeeks(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, int PS4_SYSV_ABI sceRtcTickAddYears(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, int32_t lAdd); void RegisterlibSceRtc(Core::Loader::SymbolsResolver* sym); -} // namespace Libraries::Rtc \ No newline at end of file +} // namespace Libraries::Rtc diff --git a/src/core/libraries/rtc/rtc_error.h b/src/core/libraries/rtc/rtc_error.h index 3af5a68fd..406c6fb56 100644 --- a/src/core/libraries/rtc/rtc_error.h +++ b/src/core/libraries/rtc/rtc_error.h @@ -3,6 +3,8 @@ #pragma once +#include "core/libraries/error_codes.h" + constexpr int ORBIS_RTC_ERROR_DATETIME_UNINITIALIZED = 0x7FFEF9FE; constexpr int ORBIS_RTC_ERROR_INVALID_PARAMETER = 0x80010602; constexpr int ORBIS_RTC_ERROR_INVALID_TICK_PARAMETER = 0x80010603; @@ -29,4 +31,4 @@ constexpr int ORBIS_RTC_ERROR_INVALID_DAY = 0x80B5000A; constexpr int ORBIS_RTC_ERROR_INVALID_HOUR = 0x80B5000B; constexpr int ORBIS_RTC_ERROR_INVALID_MINUTE = 0x80B5000C; constexpr int ORBIS_RTC_ERROR_INVALID_SECOND = 0x80B5000D; -constexpr int ORBIS_RTC_ERROR_INVALID_MICROSECOND = 0x80B5000E; \ No newline at end of file +constexpr int ORBIS_RTC_ERROR_INVALID_MICROSECOND = 0x80B5000E; diff --git a/src/core/libraries/system/systemservice.cpp b/src/core/libraries/system/systemservice.cpp index 4e7f9257a..ebb9f4392 100644 --- a/src/core/libraries/system/systemservice.cpp +++ b/src/core/libraries/system/systemservice.cpp @@ -3,9 +3,9 @@ #include "common/config.h" #include "common/logging/log.h" -#include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "core/libraries/system/systemservice.h" +#include "core/libraries/system/systemservice_error.h" namespace Libraries::SystemService { @@ -1772,14 +1772,12 @@ s32 PS4_SYSV_ABI sceSystemServiceGetStatus(OrbisSystemServiceStatus* status) { LOG_ERROR(Lib_SystemService, "OrbisSystemServiceStatus is null"); return ORBIS_SYSTEM_SERVICE_ERROR_PARAMETER; } - OrbisSystemServiceStatus st = {}; - st.eventNum = 0; - st.isSystemUiOverlaid = false; - st.isInBackgroundExecution = false; - st.isCpuMode7CpuNormal = true; - st.isGameLiveStreamingOnAir = false; - st.isOutOfVrPlayArea = false; - *status = st; + status->event_num = 0; + status->is_system_ui_overlaid = false; + status->is_in_background_execution = false; + status->is_cpu_mode7_cpu_normal = true; + status->is_game_live_streaming_on_air = false; + status->is_out_of_vr_play_area = false; return ORBIS_OK; } @@ -1889,39 +1887,38 @@ int PS4_SYSV_ABI sceSystemServiceNavigateToGoHome() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceSystemServiceParamGetInt(int param_id, int* value) { +s32 PS4_SYSV_ABI sceSystemServiceParamGetInt(OrbisSystemServiceParamId param_id, int* value) { // TODO this probably should be stored in config for UI configuration - LOG_INFO(Lib_SystemService, "called param_id {}", param_id); + LOG_INFO(Lib_SystemService, "called param_id {}", u32(param_id)); if (value == nullptr) { LOG_ERROR(Lib_SystemService, "value is null"); return ORBIS_SYSTEM_SERVICE_ERROR_PARAMETER; } switch (param_id) { - case ORBIS_SYSTEM_SERVICE_PARAM_ID_LANG: + case OrbisSystemServiceParamId::Lang: *value = Config::GetLanguage(); break; - case ORBIS_SYSTEM_SERVICE_PARAM_ID_DATE_FORMAT: - *value = ORBIS_SYSTEM_PARAM_DATE_FORMAT_DDMMYYYY; + case OrbisSystemServiceParamId::DateFormat: + *value = u32(OrbisSystemParamDateFormat::FmtDDMMYYYY); break; - case ORBIS_SYSTEM_SERVICE_PARAM_ID_TIME_FORMAT: - *value = ORBIS_SYSTEM_PARAM_TIME_FORMAT_24HOUR; + case OrbisSystemServiceParamId::TimeFormat: + *value = u32(OrbisSystemParamTimeFormat::Fmt24Hour); break; - case ORBIS_SYSTEM_SERVICE_PARAM_ID_TIME_ZONE: + case OrbisSystemServiceParamId::TimeZone: *value = +120; break; - case ORBIS_SYSTEM_SERVICE_PARAM_ID_SUMMERTIME: + case OrbisSystemServiceParamId::Summertime: *value = 1; break; - case ORBIS_SYSTEM_SERVICE_PARAM_ID_GAME_PARENTAL_LEVEL: - *value = ORBIS_SYSTEM_PARAM_GAME_PARENTAL_OFF; + case OrbisSystemServiceParamId::GameParentalLevel: + *value = u32(OrbisSystemParamGameParentalLevel::Off); break; - case ORBIS_SYSTEM_SERVICE_PARAM_ID_ENTER_BUTTON_ASSIGN: - *value = ORBIS_SYSTEM_PARAM_ENTER_BUTTON_ASSIGN_CROSS; + case OrbisSystemServiceParamId::EnterButtonAssign: + *value = u32(OrbisSystemParamEnterButtonAssign::Cross); break; default: - LOG_ERROR(Lib_SystemService, "param_id {} unsupported!", - param_id); // shouldn't go there but log it - *value = 0; // return a dummy value + LOG_ERROR(Lib_SystemService, "param_id {} unsupported!", u32(param_id)); + *value = 0; } return ORBIS_OK; diff --git a/src/core/libraries/system/systemservice.h b/src/core/libraries/system/systemservice.h index 56583c97e..cdd3c15e8 100644 --- a/src/core/libraries/system/systemservice.h +++ b/src/core/libraries/system/systemservice.h @@ -12,114 +12,81 @@ class SymbolsResolver; namespace Libraries::SystemService { -enum OrbisSystemServiceParamId { - ORBIS_SYSTEM_SERVICE_PARAM_ID_LANG = 1, - ORBIS_SYSTEM_SERVICE_PARAM_ID_DATE_FORMAT = 2, - ORBIS_SYSTEM_SERVICE_PARAM_ID_TIME_FORMAT = 3, - ORBIS_SYSTEM_SERVICE_PARAM_ID_TIME_ZONE = 4, - ORBIS_SYSTEM_SERVICE_PARAM_ID_SUMMERTIME = 5, - ORBIS_SYSTEM_SERVICE_PARAM_ID_SYSTEM_NAME = 6, - ORBIS_SYSTEM_SERVICE_PARAM_ID_GAME_PARENTAL_LEVEL = 7, - ORBIS_SYSTEM_SERVICE_PARAM_ID_ENTER_BUTTON_ASSIGN = 1000 +enum class OrbisSystemServiceParamId { + Lang = 1, + DateFormat = 2, + TimeFormat = 3, + TimeZone = 4, + Summertime = 5, + SystemName = 6, + GameParentalLevel = 7, + EnterButtonAssign = 1000, }; -enum OrbisSystemParamDateFormat { - ORBIS_SYSTEM_PARAM_DATE_FORMAT_YYYYMMDD = 0, - ORBIS_SYSTEM_PARAM_DATE_FORMAT_DDMMYYYY = 1, - ORBIS_SYSTEM_PARAM_DATE_FORMAT_MMDDYYYY = 2 +enum class OrbisSystemParamDateFormat { + FmtYYYYMMDD = 0, + FmtDDMMYYYY = 1, + FmtMMDDYYYY = 2, }; -enum OrbisSystemParamTimeFormat { - ORBIS_SYSTEM_PARAM_TIME_FORMAT_12HOUR = 0, - ORBIS_SYSTEM_PARAM_TIME_FORMAT_24HOUR = 1 +enum class OrbisSystemParamTimeFormat { + Fmt12Hour = 0, + Fmt24Hour = 1, }; -enum OrbisSystemParamGameParentalLevel { - ORBIS_SYSTEM_PARAM_GAME_PARENTAL_OFF = 0, - ORBIS_SYSTEM_PARAM_GAME_PARENTAL_LEVEL01 = 1, - ORBIS_SYSTEM_PARAM_GAME_PARENTAL_LEVEL02 = 2, - ORBIS_SYSTEM_PARAM_GAME_PARENTAL_LEVEL03 = 3, - ORBIS_SYSTEM_PARAM_GAME_PARENTAL_LEVEL04 = 4, - ORBIS_SYSTEM_PARAM_GAME_PARENTAL_LEVEL05 = 5, - ORBIS_SYSTEM_PARAM_GAME_PARENTAL_LEVEL06 = 6, - ORBIS_SYSTEM_PARAM_GAME_PARENTAL_LEVEL07 = 7, - ORBIS_SYSTEM_PARAM_GAME_PARENTAL_LEVEL08 = 8, - ORBIS_SYSTEM_PARAM_GAME_PARENTAL_LEVEL09 = 9, - ORBIS_SYSTEM_PARAM_GAME_PARENTAL_LEVEL10 = 10, - ORBIS_SYSTEM_PARAM_GAME_PARENTAL_LEVEL11 = 11 +enum class OrbisSystemParamGameParentalLevel { + Off = 0, + Level01 = 1, + Level02 = 2, + Level03 = 3, + Level04 = 4, + Level05 = 5, + Level06 = 6, + Level07 = 7, + Level08 = 8, + Level09 = 9, + Level10 = 10, + Level11 = 11, }; -enum OrbisSystemParamEnterButtonAssign { - ORBIS_SYSTEM_PARAM_ENTER_BUTTON_ASSIGN_CIRCLE = 0, - ORBIS_SYSTEM_PARAM_ENTER_BUTTON_ASSIGN_CROSS = 1 +enum class OrbisSystemParamEnterButtonAssign { + Circle = 0, + Cross = 1, }; -enum OrbisSystemParamLanguage { - ORBIS_SYSTEM_PARAM_LANG_JAPANESE = 0, - ORBIS_SYSTEM_PARAM_LANG_ENGLISH_US = 1, - ORBIS_SYSTEM_PARAM_LANG_FRENCH = 2, - ORBIS_SYSTEM_PARAM_LANG_SPANISH = 3, - ORBIS_SYSTEM_PARAM_LANG_GERMAN = 4, - ORBIS_SYSTEM_PARAM_LANG_ITALIAN = 5, - ORBIS_SYSTEM_PARAM_LANG_DUTCH = 6, - ORBIS_SYSTEM_PARAM_LANG_PORTUGUESE_PT = 7, - ORBIS_SYSTEM_PARAM_LANG_RUSSIAN = 8, - ORBIS_SYSTEM_PARAM_LANG_KOREAN = 9, - ORBIS_SYSTEM_PARAM_LANG_CHINESE_T = 10, - ORBIS_SYSTEM_PARAM_LANG_CHINESE_S = 11, - ORBIS_SYSTEM_PARAM_LANG_FINNISH = 12, - ORBIS_SYSTEM_PARAM_LANG_SWEDISH = 13, - ORBIS_SYSTEM_PARAM_LANG_DANISH = 14, - ORBIS_SYSTEM_PARAM_LANG_NORWEGIAN = 15, - ORBIS_SYSTEM_PARAM_LANG_POLISH = 16, - ORBIS_SYSTEM_PARAM_LANG_PORTUGUESE_BR = 17, - ORBIS_SYSTEM_PARAM_LANG_ENGLISH_GB = 18, - ORBIS_SYSTEM_PARAM_LANG_TURKISH = 19, - ORBIS_SYSTEM_PARAM_LANG_SPANISH_LA = 20, - ORBIS_SYSTEM_PARAM_LANG_ARABIC = 21, - ORBIS_SYSTEM_PARAM_LANG_FRENCH_CA = 22, - ORBIS_SYSTEM_PARAM_LANG_CZECH = 23, - ORBIS_SYSTEM_PARAM_LANG_HUNGARIAN = 24, - ORBIS_SYSTEM_PARAM_LANG_GREEK = 25, - ORBIS_SYSTEM_PARAM_LANG_ROMANIAN = 26, - ORBIS_SYSTEM_PARAM_LANG_THAI = 27, - ORBIS_SYSTEM_PARAM_LANG_VIETNAMESE = 28, - ORBIS_SYSTEM_PARAM_LANG_INDONESIAN = 29 -}; - -enum OrbisSystemServiceEventType { - ORBIS_SYSTEM_SERVICE_EVENT_INVALID = -1, - ORBIS_SYSTEM_SERVICE_EVENT_ON_RESUME = 0x10000000, - ORBIS_SYSTEM_SERVICE_EVENT_GAME_LIVE_STREAMING_STATUS_UPDATE = 0x10000001, - ORBIS_SYSTEM_SERVICE_EVENT_SESSION_INVITATION = 0x10000002, - ORBIS_SYSTEM_SERVICE_EVENT_ENTITLEMENT_UPDATE = 0x10000003, - ORBIS_SYSTEM_SERVICE_EVENT_GAME_CUSTOM_DATA = 0x10000004, - ORBIS_SYSTEM_SERVICE_EVENT_DISPLAY_SAFE_AREA_UPDATE = 0x10000005, - ORBIS_SYSTEM_SERVICE_EVENT_URL_OPEN = 0x10000006, - ORBIS_SYSTEM_SERVICE_EVENT_LAUNCH_APP = 0x10000007, - ORBIS_SYSTEM_SERVICE_EVENT_APP_LAUNCH_LINK = 0x10000008, - ORBIS_SYSTEM_SERVICE_EVENT_ADDCONTENT_INSTALL = 0x10000009, - ORBIS_SYSTEM_SERVICE_EVENT_RESET_VR_POSITION = 0x1000000a, - ORBIS_SYSTEM_SERVICE_EVENT_JOIN_EVENT = 0x1000000b, - ORBIS_SYSTEM_SERVICE_EVENT_PLAYGO_LOCUS_UPDATE = 0x1000000c, - ORBIS_SYSTEM_SERVICE_EVENT_PLAY_TOGETHER_HOST = 0x1000000d, - ORBIS_SYSTEM_SERVICE_EVENT_SERVICE_ENTITLEMENT_UPDATE = 0x1000000e, - ORBIS_SYSTEM_SERVICE_EVENT_EYE_TO_EYE_DISTANCE_UPDATE = 0x1000000f, - ORBIS_SYSTEM_SERVICE_EVENT_JOIN_MATCH_EVENT = 0x10000010, - ORBIS_SYSTEM_SERVICE_EVENT_PLAY_TOGETHER_HOST_A = 0x10000011, - ORBIS_SYSTEM_SERVICE_EVENT_WEBBROWSER_CLOSED = 0x10000012, - ORBIS_SYSTEM_SERVICE_EVENT_CONTROLLER_SETTINGS_CLOSED = 0x10000013, - ORBIS_SYSTEM_SERVICE_EVENT_JOIN_TEAM_ON_TEAM_MATCH_EVENT = 0x10000014, - ORBIS_SYSTEM_SERVICE_EVENT_OPEN_SHARE_MENU = 0x30000000 +enum class OrbisSystemServiceEventType { + Invalid = -1, + OnResume = 0x10000000, + GameLiveStreamingStatusUpdate = 0x10000001, + SessionInvitation = 0x10000002, + EntitlementUpdate = 0x10000003, + GameCustomData = 0x10000004, + DisplaySafeAreaUpdate = 0x10000005, + UrlOpen = 0x10000006, + LaunchApp = 0x10000007, + AppLaunchLink = 0x10000008, + AddcontentInstall = 0x10000009, + ResetVrPosition = 0x1000000a, + JoinEvent = 0x1000000b, + PlaygoLocusUpdate = 0x1000000c, + PlayTogetherHost = 0x1000000d, + ServiceEntitlementUpdate = 0x1000000e, + EyeToEyeDistanceUpdate = 0x1000000f, + JoinMatchEvent = 0x10000010, + PlayTogetherHostA = 0x10000011, + WebBrowserClosed = 0x10000012, + ControllerSettingsClosed = 0x10000013, + JoinTeamOnTeamMatchEvent = 0x10000014, + OpenShareMenu = 0x30000000 }; struct OrbisSystemServiceStatus { - s32 eventNum; - bool isSystemUiOverlaid; - bool isInBackgroundExecution; - bool isCpuMode7CpuNormal; - bool isGameLiveStreamingOnAir; - bool isOutOfVrPlayArea; + s32 event_num; + bool is_system_ui_overlaid; + bool is_in_background_execution; + bool is_cpu_mode7_cpu_normal; + bool is_game_live_streaming_on_air; + bool is_out_of_vr_play_area; u8 reserved[]; }; @@ -129,36 +96,36 @@ struct OrbisSystemServiceDisplaySafeAreaInfo { }; struct OrbisSystemServiceEvent { - OrbisSystemServiceEventType eventType; + OrbisSystemServiceEventType event_type; union { char param[8192]; struct { char source[1024]; char url[4096]; - } urlOpen; + } url_open; struct { u32 size; u8 arg[8188]; - } launchApp; + } launch_app; struct { u32 size; u8 arg[2020]; - } appLaunchLink; + } app_launch_link; struct { - s32 userId; - char eventId[37]; - char bootArgument[7169]; - } joinEvent; + s32 user_id; + char event_id[37]; + char boot_argument[7169]; + } join_event; struct { - s32 userId; - u32 npServiceLabel; + s32 user_id; + u32 np_service_label; u8 reserved[8184]; - } serviceEntitlementUpdate; + } service_entitlement_update; struct { - s32 userId; - u32 npServiceLabel; + s32 user_id; + u32 np_service_label; u8 reserved[8184]; - } unifiedEntitlementUpdate; + } unified_entitlement_update; u8 reserved[8192]; }; }; @@ -537,7 +504,7 @@ int PS4_SYSV_ABI sceSystemServiceNavigateToAnotherApp(); int PS4_SYSV_ABI sceSystemServiceNavigateToGoBack(); int PS4_SYSV_ABI sceSystemServiceNavigateToGoBackWithValue(); int PS4_SYSV_ABI sceSystemServiceNavigateToGoHome(); -s32 PS4_SYSV_ABI sceSystemServiceParamGetInt(int param_id, int* value); +s32 PS4_SYSV_ABI sceSystemServiceParamGetInt(OrbisSystemServiceParamId param_id, int* value); int PS4_SYSV_ABI sceSystemServiceParamGetString(); int PS4_SYSV_ABI sceSystemServicePowerTick(); int PS4_SYSV_ABI sceSystemServiceRaiseExceptionLocalProcess(); diff --git a/src/core/libraries/system/systemservice_error.h b/src/core/libraries/system/systemservice_error.h new file mode 100644 index 000000000..ca59fab65 --- /dev/null +++ b/src/core/libraries/system/systemservice_error.h @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +// SystemService library +constexpr int ORBIS_SYSTEM_SERVICE_ERROR_PARAMETER = 0x80A10003; +constexpr int ORBIS_SYSTEM_SERVICE_ERROR_NO_EVENT = 0x80A10004; diff --git a/src/core/libraries/system/userservice.cpp b/src/core/libraries/system/userservice.cpp index 140ca8921..e1f9f75e8 100644 --- a/src/core/libraries/system/userservice.cpp +++ b/src/core/libraries/system/userservice.cpp @@ -4,9 +4,9 @@ #include "common/config.h" #include "common/logging/log.h" -#include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "core/libraries/system/userservice.h" +#include "core/libraries/system/userservice_error.h" namespace Libraries::UserService { @@ -112,7 +112,7 @@ s32 PS4_SYSV_ABI sceUserServiceGetEvent(OrbisUserServiceEvent* event) { if (!logged_in) { logged_in = true; - event->event = SCE_USER_SERVICE_EVENT_TYPE_LOGIN; + event->event = OrbisUserServiceEventType::Login; event->userId = 1; return ORBIS_OK; } @@ -1041,14 +1041,14 @@ int PS4_SYSV_ABI sceUserServiceGetTraditionalChineseInputType() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceUserServiceGetUserColor(int user_id, int* color) { +s32 PS4_SYSV_ABI sceUserServiceGetUserColor(int user_id, OrbisUserServiceUserColor* color) { // TODO fix me better LOG_INFO(Lib_UserService, "called user_id = {}", user_id); if (color == nullptr) { LOG_ERROR(Lib_UserService, "color is null"); return ORBIS_USER_SERVICE_ERROR_INVALID_ARGUMENT; } - *color = ORBIS_USER_SERVICE_USER_COLOR_BLUE; + *color = OrbisUserServiceUserColor::Blue; return ORBIS_OK; } diff --git a/src/core/libraries/system/userservice.h b/src/core/libraries/system/userservice.h index 5bb1fd043..66ac2b69d 100644 --- a/src/core/libraries/system/userservice.h +++ b/src/core/libraries/system/userservice.h @@ -40,16 +40,16 @@ struct OrbisUserServiceRegisteredUserIdList { OrbisUserServiceUserId userId[ORBIS_USER_SERVICE_MAX_REGISTER_USERS]; }; -enum OrbisUserServiceUserColor { - ORBIS_USER_SERVICE_USER_COLOR_BLUE = 0, - ORBIS_USER_SERVICE_USER_COLOR_RED = 1, - ORBIS_USER_SERVICE_USER_COLOR_GREEN = 2, - ORBIS_USER_SERVICE_USER_COLOR_PINK = 3, +enum class OrbisUserServiceUserColor { + Blue = 0, + Red = 1, + Green = 2, + Pink = 3, }; -enum OrbisUserServiceEventType { - SCE_USER_SERVICE_EVENT_TYPE_LOGIN = 0, // Login event - SCE_USER_SERVICE_EVENT_TYPE_LOGOUT = 1, // Logout event +enum class OrbisUserServiceEventType { + Login = 0, // Login event + Logout = 1, // Logout event }; struct OrbisUserServiceEvent { @@ -258,7 +258,7 @@ int PS4_SYSV_ABI sceUserServiceGetTopMenuLimitItem(); int PS4_SYSV_ABI sceUserServiceGetTopMenuNotificationFlag(); int PS4_SYSV_ABI sceUserServiceGetTopMenuTutorialFlag(); int PS4_SYSV_ABI sceUserServiceGetTraditionalChineseInputType(); -s32 PS4_SYSV_ABI sceUserServiceGetUserColor(int user_id, int* color); +s32 PS4_SYSV_ABI sceUserServiceGetUserColor(int user_id, OrbisUserServiceUserColor* color); int PS4_SYSV_ABI sceUserServiceGetUserGroupName(); int PS4_SYSV_ABI sceUserServiceGetUserGroupNameList(); int PS4_SYSV_ABI sceUserServiceGetUserGroupNum(); diff --git a/src/core/libraries/system/userservice_error.h b/src/core/libraries/system/userservice_error.h new file mode 100644 index 000000000..40921df9f --- /dev/null +++ b/src/core/libraries/system/userservice_error.h @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +// UserService library +constexpr int ORBIS_USER_SERVICE_ERROR_INTERNAL = 0x80960001; +constexpr int ORBIS_USER_SERVICE_ERROR_NOT_INITIALIZED = 0x80960002; +constexpr int ORBIS_USER_SERVICE_ERROR_ALREADY_INITIALIZED = 0x80960003; +constexpr int ORBIS_USER_SERVICE_ERROR_NO_MEMORY = 0x80960004; +constexpr int ORBIS_USER_SERVICE_ERROR_INVALID_ARGUMENT = 0x80960005; +constexpr int ORBIS_USER_SERVICE_ERROR_OPERATION_NOT_SUPPORTED = 0x80960006; +constexpr int ORBIS_USER_SERVICE_ERROR_NO_EVENT = 0x80960007; +constexpr int ORBIS_USER_SERVICE_ERROR_NOT_LOGGED_IN = 0x80960009; +constexpr int ORBIS_USER_SERVICE_ERROR_BUFFER_TOO_SHORT = 0x8096000A; diff --git a/src/core/libraries/videodec/videodec.cpp b/src/core/libraries/videodec/videodec.cpp index 8d6ea4988..6c9a9b8c0 100644 --- a/src/core/libraries/videodec/videodec.cpp +++ b/src/core/libraries/videodec/videodec.cpp @@ -1,12 +1,11 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "videodec.h" - #include "common/logging/log.h" -#include "core/libraries/error_codes.h" #include "core/libraries/libs.h" -#include "videodec_impl.h" +#include "core/libraries/videodec/videodec.h" +#include "core/libraries/videodec/videodec_error.h" +#include "core/libraries/videodec/videodec_impl.h" namespace Libraries::Videodec { @@ -134,4 +133,4 @@ void RegisterlibSceVideodec(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("f8AgDv-1X8A", "libSceVideodec", 1, "libSceVideodec", 1, 1, sceVideodecReset); }; -} // namespace Libraries::Videodec \ No newline at end of file +} // namespace Libraries::Videodec diff --git a/src/core/libraries/videodec/videodec2.cpp b/src/core/libraries/videodec/videodec2.cpp index fe2b82bea..0bd68afbd 100644 --- a/src/core/libraries/videodec/videodec2.cpp +++ b/src/core/libraries/videodec/videodec2.cpp @@ -1,13 +1,12 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "videodec2.h" -#include "videodec2_impl.h" - #include "common/assert.h" #include "common/logging/log.h" -#include "core/libraries/error_codes.h" #include "core/libraries/libs.h" +#include "core/libraries/videodec/videodec2.h" +#include "core/libraries/videodec/videodec2_impl.h" +#include "core/libraries/videodec/videodec_error.h" namespace Libraries::Vdec2 { @@ -197,4 +196,4 @@ void RegisterlibSceVdec2(Core::Loader::SymbolsResolver* sym) { sceVideodec2GetPictureInfo); } -} // namespace Libraries::Vdec2 \ No newline at end of file +} // namespace Libraries::Vdec2 diff --git a/src/core/libraries/videodec/videodec2.h b/src/core/libraries/videodec/videodec2.h index 9b230cc54..4617e1c20 100644 --- a/src/core/libraries/videodec/videodec2.h +++ b/src/core/libraries/videodec/videodec2.h @@ -15,7 +15,7 @@ namespace Libraries::Vdec2 { class VdecDecoder; using OrbisVideodec2Decoder = VdecDecoder*; -typedef void* OrbisVideodec2ComputeQueue; +using OrbisVideodec2ComputeQueue = void*; struct OrbisVideodec2DecoderConfigInfo { u64 thisSize; diff --git a/src/core/libraries/videodec/videodec2_impl.cpp b/src/core/libraries/videodec/videodec2_impl.cpp index 37270fa8c..8daa48828 100644 --- a/src/core/libraries/videodec/videodec2_impl.cpp +++ b/src/core/libraries/videodec/videodec2_impl.cpp @@ -3,10 +3,9 @@ #include "videodec2_impl.h" -#include "common/alignment.h" #include "common/assert.h" #include "common/logging/log.h" -#include "core/libraries/error_codes.h" +#include "core/libraries/videodec/videodec_error.h" // The av_err2str macro in libavutil/error.h does not play nice with C++ #ifdef av_err2str diff --git a/src/core/libraries/videodec/videodec_error.h b/src/core/libraries/videodec/videodec_error.h new file mode 100644 index 000000000..4e0066e5e --- /dev/null +++ b/src/core/libraries/videodec/videodec_error.h @@ -0,0 +1,68 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +// Videodec library +constexpr int ORBIS_VIDEODEC_ERROR_API_FAIL = 0x80C10000; +constexpr int ORBIS_VIDEODEC_ERROR_CODEC_TYPE = 0x80C10001; +constexpr int ORBIS_VIDEODEC_ERROR_STRUCT_SIZE = 0x80C10002; +constexpr int ORBIS_VIDEODEC_ERROR_HANDLE = 0x80C10003; +constexpr int ORBIS_VIDEODEC_ERROR_CPU_MEMORY_SIZE = 0x80C10004; +constexpr int ORBIS_VIDEODEC_ERROR_CPU_MEMORY_POINTER = 0x80C10005; +constexpr int ORBIS_VIDEODEC_ERROR_CPU_GPU_MEMORY_SIZE = 0x80C10006; +constexpr int ORBIS_VIDEODEC_ERROR_CPU_GPU_MEMORY_POINTER = 0x80C10007; +constexpr int ORBIS_VIDEODEC_ERROR_SHADER_CONTEXT_POINTER = 0x80C10008; +constexpr int ORBIS_VIDEODEC_ERROR_AU_SIZE = 0x80C10009; +constexpr int ORBIS_VIDEODEC_ERROR_AU_POINTER = 0x80C1000A; +constexpr int ORBIS_VIDEODEC_ERROR_FRAME_BUFFER_SIZE = 0x80C1000B; +constexpr int ORBIS_VIDEODEC_ERROR_FRAME_BUFFER_POINTER = 0x80C1000C; +constexpr int ORBIS_VIDEODEC_ERROR_FRAME_BUFFER_ALIGNMENT = 0x80C1000D; +constexpr int ORBIS_VIDEODEC_ERROR_CONFIG_INFO = 0x80C1000E; +constexpr int ORBIS_VIDEODEC_ERROR_ARGUMENT_POINTER = 0x80C1000F; +constexpr int ORBIS_VIDEODEC_ERROR_NEW_SEQUENCE = 0x80C10010; +constexpr int ORBIS_VIDEODEC_ERROR_DECODE_AU = 0x80C10011; +constexpr int ORBIS_VIDEODEC_ERROR_MISMATCH_SPEC = 0x80C10012; +constexpr int ORBIS_VIDEODEC_ERROR_INVALID_SEQUENCE = 0x80C10013; +constexpr int ORBIS_VIDEODEC_ERROR_FATAL_STREAM = 0x80C10014; +constexpr int ORBIS_VIDEODEC_ERROR_FATAL_STATE = 0x80C10015; + +// Videodec2 library +constexpr int ORBIS_VIDEODEC2_ERROR_API_FAIL = 0x811D0100; +constexpr int ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE = 0x811D0101; +constexpr int ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER = 0x811D0102; +constexpr int ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE = 0x811D0103; +constexpr int ORBIS_VIDEODEC2_ERROR_MEMORY_SIZE = 0x811D0104; +constexpr int ORBIS_VIDEODEC2_ERROR_MEMORY_POINTER = 0x811D0105; +constexpr int ORBIS_VIDEODEC2_ERROR_FRAME_BUFFER_SIZE = 0x811D0106; +constexpr int ORBIS_VIDEODEC2_ERROR_FRAME_BUFFER_POINTER = 0x811D0107; +constexpr int ORBIS_VIDEODEC2_ERROR_FRAME_BUFFER_ALIGNMENT = 0x811D0108; +constexpr int ORBIS_VIDEODEC2_ERROR_NOT_ONION_MEMORY = 0x811D0109; +constexpr int ORBIS_VIDEODEC2_ERROR_NOT_GARLIC_MEMORY = 0x811D010A; +constexpr int ORBIS_VIDEODEC2_ERROR_NOT_DIRECT_MEMORY = 0x811D010B; +constexpr int ORBIS_VIDEODEC2_ERROR_MEMORY_INFO = 0x811D010C; +constexpr int ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_SIZE = 0x811D010D; +constexpr int ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_POINTER = 0x811D010E; +constexpr int ORBIS_VIDEODEC2_ERROR_OUTPUT_INFO = 0x811D010F; +constexpr int ORBIS_VIDEODEC2_ERROR_COMPUTE_QUEUE = 0x811D0110; +constexpr int ORBIS_VIDEODEC2_ERROR_FATAL_STATE = 0x811D0111; +constexpr int ORBIS_VIDEODEC2_ERROR_PRESET_VALUE = 0x811D0112; +constexpr int ORBIS_VIDEODEC2_ERROR_CONFIG_INFO = 0x811D0200; +constexpr int ORBIS_VIDEODEC2_ERROR_COMPUTE_PIPE_ID = 0x811D0201; +constexpr int ORBIS_VIDEODEC2_ERROR_COMPUTE_QUEUE_ID = 0x811D0202; +constexpr int ORBIS_VIDEODEC2_ERROR_RESOURCE_TYPE = 0x811D0203; +constexpr int ORBIS_VIDEODEC2_ERROR_CODEC_TYPE = 0x811D0204; +constexpr int ORBIS_VIDEODEC2_ERROR_PROFILE_LEVEL = 0x811D0205; +constexpr int ORBIS_VIDEODEC2_ERROR_PIPELINE_DEPTH = 0x811D0206; +constexpr int ORBIS_VIDEODEC2_ERROR_AFFINITY_MASK = 0x811D0207; +constexpr int ORBIS_VIDEODEC2_ERROR_THREAD_PRIORITY = 0x811D0208; +constexpr int ORBIS_VIDEODEC2_ERROR_DPB_FRAME_COUNT = 0x811D0209; +constexpr int ORBIS_VIDEODEC2_ERROR_FRAME_WIDTH_HEIGHT = 0x811D020A; +constexpr int ORBIS_VIDEODEC2_ERROR_EXTRA_CONFIG_INFO = 0x811D020B; +constexpr int ORBIS_VIDEODEC2_ERROR_NEW_SEQUENCE = 0x811D0300; +constexpr int ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT = 0x811D0301; +constexpr int ORBIS_VIDEODEC2_ERROR_OVERSIZE_DECODE = 0x811D0302; +constexpr int ORBIS_VIDEODEC2_ERROR_INVALID_SEQUENCE = 0x811D0303; +constexpr int ORBIS_VIDEODEC2_ERROR_FATAL_STREAM = 0x811D0304; diff --git a/src/core/libraries/videodec/videodec_impl.cpp b/src/core/libraries/videodec/videodec_impl.cpp index a244c4a61..cf4846971 100644 --- a/src/core/libraries/videodec/videodec_impl.cpp +++ b/src/core/libraries/videodec/videodec_impl.cpp @@ -6,7 +6,7 @@ #include "common/alignment.h" #include "common/assert.h" #include "common/logging/log.h" -#include "core/libraries/error_codes.h" +#include "core/libraries/videodec/videodec_error.h" // The av_err2str macro in libavutil/error.h does not play nice with C++ #ifdef av_err2str diff --git a/src/core/libraries/videoout/driver.cpp b/src/core/libraries/videoout/driver.cpp index d8013614c..f6c25afe3 100644 --- a/src/core/libraries/videoout/driver.cpp +++ b/src/core/libraries/videoout/driver.cpp @@ -8,10 +8,9 @@ #include "common/debug.h" #include "common/thread.h" #include "core/debug_state.h" -#include "core/libraries/error_codes.h" #include "core/libraries/kernel/time.h" #include "core/libraries/videoout/driver.h" -#include "core/platform.h" +#include "core/libraries/videoout/videoout_error.h" #include "video_core/renderer_vulkan/vk_presenter.h" extern std::unique_ptr presenter; @@ -42,10 +41,10 @@ constexpr u32 PixelFormatBpp(PixelFormat pixel_format) { } VideoOutDriver::VideoOutDriver(u32 width, u32 height) { - main_port.resolution.fullWidth = width; - main_port.resolution.fullHeight = height; - main_port.resolution.paneWidth = width; - main_port.resolution.paneHeight = height; + main_port.resolution.full_width = width; + main_port.resolution.full_height = height; + main_port.resolution.pane_width = width; + main_port.resolution.pane_height = height; present_thread = std::jthread([&](std::stop_token token) { PresentThread(token); }); } @@ -174,20 +173,21 @@ void VideoOutDriver::Flip(const Request& req) { std::unique_lock lock{port->port_mutex}; auto& flip_status = port->flip_status; flip_status.count++; - flip_status.processTime = Libraries::Kernel::sceKernelGetProcessTime(); + flip_status.process_time = Libraries::Kernel::sceKernelGetProcessTime(); flip_status.tsc = Libraries::Kernel::sceKernelReadTsc(); - flip_status.flipArg = req.flip_arg; - flip_status.currentBuffer = req.index; + flip_status.flip_arg = req.flip_arg; + flip_status.current_buffer = req.index; if (req.eop) { - --flip_status.gcQueueNum; + --flip_status.gc_queue_num; } - --flip_status.flipPendingNum; + --flip_status.flip_pending_num; } // Trigger flip events for the port. for (auto& event : port->flip_events) { if (event != nullptr) { - event->TriggerEvent(SCE_VIDEO_OUT_EVENT_FLIP, Kernel::SceKernelEvent::Filter::VideoOut, + event->TriggerEvent(u64(OrbisVideoOutEventId::Flip), + Kernel::SceKernelEvent::Filter::VideoOut, reinterpret_cast(req.flip_arg)); } } @@ -211,16 +211,16 @@ bool VideoOutDriver::SubmitFlip(VideoOutPort* port, s32 index, s64 flip_arg, bool is_eop /*= false*/) { { std::unique_lock lock{port->port_mutex}; - if (index != -1 && port->flip_status.flipPendingNum >= port->NumRegisteredBuffers()) { + if (index != -1 && port->flip_status.flip_pending_num >= port->NumRegisteredBuffers()) { LOG_ERROR(Lib_VideoOut, "Flip queue is full"); return false; } if (is_eop) { - ++port->flip_status.gcQueueNum; + ++port->flip_status.gc_queue_num; } - ++port->flip_status.flipPendingNum; // integral GPU and CPU pending flips counter - port->flip_status.submitTsc = Libraries::Kernel::sceKernelReadTsc(); + ++port->flip_status.flip_pending_num; // integral GPU and CPU pending flips counter + port->flip_status.submit_tsc = Libraries::Kernel::sceKernelReadTsc(); } if (!is_eop) { @@ -298,9 +298,9 @@ void VideoOutDriver::PresentThread(std::stop_token token) { { // Needs lock here as can be concurrently read by `sceVideoOutGetVblankStatus` - std::unique_lock lock{main_port.vo_mutex}; + std::scoped_lock lock{main_port.vo_mutex}; vblank_status.count++; - vblank_status.processTime = Libraries::Kernel::sceKernelGetProcessTime(); + vblank_status.process_time = Libraries::Kernel::sceKernelGetProcessTime(); vblank_status.tsc = Libraries::Kernel::sceKernelReadTsc(); main_port.vblank_cv.notify_all(); } @@ -308,7 +308,7 @@ void VideoOutDriver::PresentThread(std::stop_token token) { // Trigger flip events for the port. for (auto& event : main_port.vblank_events) { if (event != nullptr) { - event->TriggerEvent(SCE_VIDEO_OUT_EVENT_VBLANK, + event->TriggerEvent(u64(OrbisVideoOutEventId::Vblank), Kernel::SceKernelEvent::Filter::VideoOut, nullptr); } } diff --git a/src/core/libraries/videoout/video_out.cpp b/src/core/libraries/videoout/video_out.cpp index 0258074d3..ee4a89345 100644 --- a/src/core/libraries/videoout/video_out.cpp +++ b/src/core/libraries/videoout/video_out.cpp @@ -4,12 +4,11 @@ #include "common/assert.h" #include "common/config.h" #include "common/logging/log.h" -#include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "core/libraries/system/userservice.h" #include "core/libraries/videoout/driver.h" #include "core/libraries/videoout/video_out.h" -#include "core/loader/symbols_resolver.h" +#include "core/libraries/videoout/videoout_error.h" #include "core/platform.h" #include "video_core/renderer_vulkan/vk_presenter.h" @@ -51,7 +50,7 @@ s32 PS4_SYSV_ABI sceVideoOutAddFlipEvent(Kernel::SceKernelEqueue eq, s32 handle, } Kernel::EqueueEvent event{}; - event.event.ident = SCE_VIDEO_OUT_EVENT_FLIP; + event.event.ident = u64(OrbisVideoOutEventId::Flip); event.event.filter = Kernel::SceKernelEvent::Filter::VideoOut; // The library only sets EV_ADD but kernel driver forces EV_CLEAR event.event.flags = Kernel::SceKernelEvent::Flags::Clear; @@ -78,7 +77,7 @@ s32 PS4_SYSV_ABI sceVideoOutAddVblankEvent(Kernel::SceKernelEqueue eq, s32 handl } Kernel::EqueueEvent event{}; - event.event.ident = SCE_VIDEO_OUT_EVENT_VBLANK; + event.event.ident = u64(OrbisVideoOutEventId::Vblank); event.event.filter = Kernel::SceKernelEvent::Filter::VideoOut; // The library only sets EV_ADD but kernel driver forces EV_CLEAR event.event.flags = Kernel::SceKernelEvent::Flags::Clear; @@ -118,7 +117,7 @@ s32 PS4_SYSV_ABI sceVideoOutIsFlipPending(s32 handle) { LOG_TRACE(Lib_VideoOut, "called"); auto* port = driver->GetPort(handle); std::unique_lock lock{port->port_mutex}; - s32 pending = port->flip_status.flipPendingNum; + s32 pending = port->flip_status.flip_pending_num; return pending; } @@ -156,7 +155,7 @@ s32 PS4_SYSV_ABI sceVideoOutSubmitFlip(s32 handle, s32 bufferIndex, s32 flipMode int PS4_SYSV_ABI sceVideoOutGetEventId(const Kernel::SceKernelEvent* ev) { if (ev == nullptr) { - return SCE_VIDEO_OUT_ERROR_INVALID_ADDRESS; + return ORBIS_VIDEO_OUT_ERROR_INVALID_ADDRESS; } if (ev->filter != Kernel::SceKernelEvent::Filter::VideoOut) { return ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE; @@ -166,7 +165,7 @@ int PS4_SYSV_ABI sceVideoOutGetEventId(const Kernel::SceKernelEvent* ev) { int PS4_SYSV_ABI sceVideoOutGetEventData(const Kernel::SceKernelEvent* ev, int64_t* data) { if (ev == nullptr || data == nullptr) { - return SCE_VIDEO_OUT_ERROR_INVALID_ADDRESS; + return ORBIS_VIDEO_OUT_ERROR_INVALID_ADDRESS; } if (ev->filter != Kernel::SceKernelEvent::Filter::VideoOut) { return ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE; @@ -204,7 +203,7 @@ s32 PS4_SYSV_ABI sceVideoOutGetFlipStatus(s32 handle, FlipStatus* status) { s32 PS4_SYSV_ABI sceVideoOutGetVblankStatus(int handle, SceVideoOutVblankStatus* status) { if (status == nullptr) { - return SCE_VIDEO_OUT_ERROR_INVALID_ADDRESS; + return ORBIS_VIDEO_OUT_ERROR_INVALID_ADDRESS; } auto* port = driver->GetPort(handle); diff --git a/src/core/libraries/videoout/video_out.h b/src/core/libraries/videoout/video_out.h index 0680a8491..5af9d550d 100644 --- a/src/core/libraries/videoout/video_out.h +++ b/src/core/libraries/videoout/video_out.h @@ -40,36 +40,32 @@ constexpr int SCE_VIDEO_OUT_BUFFER_ATTRIBUTE_OPTION_NONE = 0; constexpr int SCE_VIDEO_OUT_BUFFER_ATTRIBUTE_OPTION_VR = 7; constexpr int SCE_VIDEO_OUT_BUFFER_ATTRIBUTE_OPTION_STRICT_COLORIMETRY = 8; -enum SceVideoOutEventId : s16 { - SCE_VIDEO_OUT_EVENT_FLIP = 0, - SCE_VIDEO_OUT_EVENT_VBLANK = 1, - SCE_VIDEO_OUT_EVENT_PRE_VBLANK_START = 2 -}; +enum class OrbisVideoOutEventId : s16 { Flip = 0, Vblank = 1, PreVblankStart = 2 }; -enum AspectRatioMode : s32 { - SCE_VIDEO_OUT_ASPECT_RATIO_16_9 = 0, +enum class AspectRatioMode : s32 { + Ratio16_9 = 0, }; struct FlipStatus { u64 count = 0; - u64 processTime = 0; + u64 process_time = 0; u64 tsc = 0; - s64 flipArg = -1; - u64 submitTsc = 0; + s64 flip_arg = -1; + u64 submit_tsc = 0; u64 reserved0 = 0; - s32 gcQueueNum = 0; - s32 flipPendingNum = 0; - s32 currentBuffer = -1; + s32 gc_queue_num = 0; + s32 flip_pending_num = 0; + s32 current_buffer = -1; u32 reserved1 = 0; }; struct SceVideoOutResolutionStatus { - s32 fullWidth = 1280; - s32 fullHeight = 720; - s32 paneWidth = 1280; - s32 paneHeight = 720; - u64 refreshRate = SCE_VIDEO_OUT_REFRESH_RATE_59_94HZ; - float screenSizeInInch = 50; + s32 full_width = 1280; + s32 full_height = 720; + s32 pane_width = 1280; + s32 pane_height = 720; + u64 refresh_rate = SCE_VIDEO_OUT_REFRESH_RATE_59_94HZ; + float screen_size_in_inch = 50; u16 flags = 0; u16 reserved0 = 0; u32 reserved1[3] = {0}; @@ -77,7 +73,7 @@ struct SceVideoOutResolutionStatus { struct SceVideoOutVblankStatus { u64 count = 0; - u64 processTime = 0; + u64 process_time = 0; u64 tsc = 0; u64 reserved[1] = {0}; u8 flags = 0; diff --git a/src/core/libraries/videoout/videoout_error.h b/src/core/libraries/videoout/videoout_error.h new file mode 100644 index 000000000..b1ed18c92 --- /dev/null +++ b/src/core/libraries/videoout/videoout_error.h @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +// VideoOut library +constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_VALUE = 0x80290001; +constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_ADDRESS = 0x80290002; +constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_PIXEL_FORMAT = 0x80290003; +constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_PITCH = 0x80290004; +constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_RESOLUTION = 0x80290005; +constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_FLIP_MODE = 0x80290006; +constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_TILING_MODE = 0x80290007; +constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_ASPECT_RATIO = 0x80290008; +constexpr int ORBIS_VIDEO_OUT_ERROR_RESOURCE_BUSY = 0x80290009; +constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_INDEX = 0x8029000A; +constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE = 0x8029000B; +constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE = 0x8029000C; +constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT = 0x8029000D; +constexpr int ORBIS_VIDEO_OUT_ERROR_NO_EMPTY_SLOT = 0x8029000F; +constexpr int ORBIS_VIDEO_OUT_ERROR_SLOT_OCCUPIED = 0x80290010; +constexpr int ORBIS_VIDEO_OUT_ERROR_FLIP_QUEUE_FULL = 0x80290012; +constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_MEMORY = 0x80290013; +constexpr int ORBIS_VIDEO_OUT_ERROR_MEMORY_NOT_PHYSICALLY_CONTIGUOUS = 0x80290014; +constexpr int ORBIS_VIDEO_OUT_ERROR_MEMORY_INVALID_ALIGNMENT = 0x80290015; +constexpr int ORBIS_VIDEO_OUT_ERROR_UNSUPPORTED_OUTPUT_MODE = 0x80290016; +constexpr int ORBIS_VIDEO_OUT_ERROR_OVERFLOW = 0x80290017; +constexpr int ORBIS_VIDEO_OUT_ERROR_NO_DEVICE = 0x80290018; +constexpr int ORBIS_VIDEO_OUT_ERROR_UNAVAILABLE_OUTPUT_MODE = 0x80290019; +constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_OPTION = 0x8029001A; +constexpr int ORBIS_VIDEO_OUT_ERROR_UNKNOWN = 0x802900FE; +constexpr int ORBIS_VIDEO_OUT_ERROR_FATAL = 0x802900FF; +constexpr int ORBIS_VIDEO_OUT_ERROR_ENOMEM = 0x8029100C; diff --git a/src/core/linker.cpp b/src/core/linker.cpp index 9592bee0f..9cf4198ae 100644 --- a/src/core/linker.cpp +++ b/src/core/linker.cpp @@ -16,7 +16,6 @@ #include "core/linker.h" #include "core/memory.h" #include "core/tls.h" -#include "core/virtual_memory.h" namespace Core { @@ -211,7 +210,7 @@ void Linker::Relocate(Module* module) { } if (rel_is_resolved) { - VirtualMemory::memory_patch(rel_virtual_addr, rel_value); + std::memcpy(reinterpret_cast(rel_virtual_addr), &rel_value, sizeof(rel_value)); } else { LOG_INFO(Core_Linker, "Function not patched! {}", rel_name); } @@ -297,8 +296,6 @@ void* Linker::TlsGetAddr(u64 module_index, u64 offset) { u8* addr = dtv_table[module_index + 1].pointer; Module* module = m_modules[module_index - 1].get(); - // LOG_INFO(Core_Linker, "Got DTV addr {} from module index {} with name {}", - // fmt::ptr(addr), module_index, module->file.filename().string()); if (!addr) { // Module was just loaded by above code. Allocate TLS block for it. const u32 init_image_size = module->tls.init_image_size; diff --git a/src/core/memory.cpp b/src/core/memory.cpp index f638e5e1a..15fde2a57 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -5,10 +5,9 @@ #include "common/assert.h" #include "common/config.h" #include "common/debug.h" -#include "core/libraries/error_codes.h" #include "core/libraries/kernel/memory.h" +#include "core/libraries/kernel/orbis_error.h" #include "core/memory.h" -#include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_rasterizer.h" namespace Core { @@ -268,7 +267,7 @@ int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, M // Certain games perform flexible mappings on loop to determine // the available flexible memory size. Questionable but we need to handle this. if (type == VMAType::Flexible && flexible_usage + size > total_flexible_size) { - return SCE_KERNEL_ERROR_ENOMEM; + return ORBIS_KERNEL_ERROR_ENOMEM; } // When virtual addr is zero, force it to virtual_base. The guest cannot pass Fixed diff --git a/src/core/virtual_memory.cpp b/src/core/virtual_memory.cpp deleted file mode 100644 index 8907622a4..000000000 --- a/src/core/virtual_memory.cpp +++ /dev/null @@ -1,180 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/assert.h" -#include "common/error.h" -#include "common/logging/log.h" -#include "core/virtual_memory.h" - -#ifdef _WIN64 -#include -#else -#include -#endif - -#if !defined(_WIN64) -enum PosixPageProtection { - PAGE_NOACCESS = 0, - PAGE_READONLY = PROT_READ, - PAGE_READWRITE = PROT_READ | PROT_WRITE, - PAGE_EXECUTE = PROT_EXEC, - PAGE_EXECUTE_READ = PROT_EXEC | PROT_READ, - PAGE_EXECUTE_READWRITE = PROT_EXEC | PROT_READ | PROT_WRITE -}; -#endif - -namespace VirtualMemory { -static u32 convertMemoryMode(MemoryMode mode) { - switch (mode) { - case MemoryMode::Read: - return PAGE_READONLY; - case MemoryMode::Write: - case MemoryMode::ReadWrite: - return PAGE_READWRITE; - - case MemoryMode::Execute: - return PAGE_EXECUTE; - case MemoryMode::ExecuteRead: - return PAGE_EXECUTE_READ; - case MemoryMode::ExecuteWrite: - case MemoryMode::ExecuteReadWrite: - return PAGE_EXECUTE_READWRITE; - - case MemoryMode::NoAccess: - return PAGE_NOACCESS; - default: - return PAGE_NOACCESS; - } -} -static MemoryMode convertMemoryMode(u32 mode) { - switch (mode) { - case PAGE_NOACCESS: - return MemoryMode::NoAccess; - case PAGE_READONLY: - return MemoryMode::Read; - case PAGE_READWRITE: - return MemoryMode::ReadWrite; - case PAGE_EXECUTE: - return MemoryMode::Execute; - case PAGE_EXECUTE_READ: - return MemoryMode::ExecuteRead; - case PAGE_EXECUTE_READWRITE: - return MemoryMode::ExecuteReadWrite; - default: - return MemoryMode::NoAccess; - } -} - -u64 memory_alloc(u64 address, u64 size, MemoryMode mode) { -#ifdef _WIN64 - auto ptr = reinterpret_cast(VirtualAlloc( - reinterpret_cast(static_cast(address)), size, - static_cast(MEM_COMMIT) | static_cast(MEM_RESERVE), convertMemoryMode(mode))); - - if (ptr == 0) { - auto err = static_cast(GetLastError()); - LOG_ERROR(Common_Memory, "VirtualAlloc() failed: 0x{:X}", err); - } -#else - auto ptr = reinterpret_cast( - mmap(reinterpret_cast(static_cast(address)), size, - PROT_EXEC | PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)); - - if (ptr == reinterpret_cast MAP_FAILED) { - LOG_ERROR(Common_Memory, "mmap() failed: {}", std::strerror(errno)); - } -#endif - return ptr; -} -bool memory_protect(u64 address, u64 size, MemoryMode mode, MemoryMode* old_mode) { -#ifdef _WIN64 - DWORD old_protect = 0; - if (VirtualProtect(reinterpret_cast(static_cast(address)), size, - convertMemoryMode(mode), &old_protect) == 0) { - auto err = static_cast(GetLastError()); - LOG_ERROR(Common_Memory, "VirtualProtect() failed: 0x{:X}", err); - return false; - } - if (old_mode != nullptr) { - *old_mode = convertMemoryMode(old_protect); - } - return true; -#else - int ret = mprotect(reinterpret_cast(address), size, convertMemoryMode(mode)); - if (ret != 0) { - const auto error = Common::GetLastErrorMsg(); - ASSERT(false); - } - return true; -#endif -} - -bool memory_flush(u64 address, u64 size) { -#ifdef _WIN64 - if (::FlushInstructionCache(GetCurrentProcess(), - reinterpret_cast(static_cast(address)), - size) == 0) { - auto err = static_cast(GetLastError()); - LOG_ERROR(Common_Memory, "FlushInstructionCache() failed: 0x{:X}", err); - return false; - } - return true; -#else // linux probably doesn't have something similar - return true; -#endif -} -bool memory_patch(u64 vaddr, u64 value) { - MemoryMode old_mode{}; - // memory_protect(vaddr, 8, MemoryMode::ReadWrite, &old_mode); - - auto* ptr = reinterpret_cast(vaddr); - - bool ret = (*ptr != value); - - *ptr = value; - - // memory_protect(vaddr, 8, old_mode, nullptr); - - // if mode is executable flush it so insure that cpu finds it - if (containsExecuteMode(old_mode)) { - memory_flush(vaddr, 8); - } - - return ret; -} -static u64 AlignUp(u64 pos, u64 align) { - return (align != 0 ? (pos + (align - 1)) & ~(align - 1) : pos); -} - -u64 memory_alloc_aligned(u64 address, u64 size, MemoryMode mode, u64 alignment) { -#ifdef _WIN64 - // try allocate aligned address inside user area - MEM_ADDRESS_REQUIREMENTS req{}; - MEM_EXTENDED_PARAMETER param{}; - req.LowestStartingAddress = - (address == 0 ? reinterpret_cast(USER_MIN) - : reinterpret_cast(AlignUp(address, alignment))); - req.HighestEndingAddress = reinterpret_cast(USER_MAX); - req.Alignment = alignment; - param.Type = MemExtendedParameterAddressRequirements; - param.Pointer = &req; - - auto ptr = reinterpret_cast( - VirtualAlloc2(GetCurrentProcess(), nullptr, size, - static_cast(MEM_COMMIT) | static_cast(MEM_RESERVE), - convertMemoryMode(mode), ¶m, 1)); - - if (ptr == 0) { - auto err = static_cast(GetLastError()); - LOG_ERROR(Common_Memory, "VirtualAlloc2() failed: 0x{:X}", err); - } - return ptr; -#else - void* hint_address = address == 0 ? reinterpret_cast(USER_MIN) - : reinterpret_cast(AlignUp(address, alignment)); - void* ptr = mmap(hint_address, size, convertMemoryMode(mode), MAP_ANON | MAP_PRIVATE, -1, 0); - ASSERT(ptr != MAP_FAILED); - return reinterpret_cast(ptr); -#endif -} -} // namespace VirtualMemory diff --git a/src/core/virtual_memory.h b/src/core/virtual_memory.h deleted file mode 100644 index 953f35f79..000000000 --- a/src/core/virtual_memory.h +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/types.h" - -constexpr u64 SYSTEM_RESERVED = 0x800000000u; -constexpr u64 CODE_BASE_OFFSET = 0x100000000u; -constexpr u64 SYSTEM_MANAGED_MIN = 0x0000040000u; -constexpr u64 SYSTEM_MANAGED_MAX = 0x07FFFFBFFFu; -constexpr u64 USER_MIN = 0x1000000000u; -constexpr u64 USER_MAX = 0xFBFFFFFFFFu; - -namespace VirtualMemory { -enum class MemoryMode : u32 { - NoAccess = 0, - Read = 1, - Write = 2, - ReadWrite = 3, - Execute = 4, - ExecuteRead = 5, - ExecuteWrite = 6, - ExecuteReadWrite = 7, -}; -u64 memory_alloc(u64 address, u64 size, MemoryMode mode); -u64 memory_alloc_aligned(u64 address, u64 size, MemoryMode mode, u64 alignment); -bool memory_protect(u64 address, u64 size, MemoryMode mode, MemoryMode* old_mode); -bool memory_flush(u64 address, u64 size); -bool memory_patch(u64 vaddr, u64 value); - -inline bool containsExecuteMode(MemoryMode mode) { - switch (mode) { - case MemoryMode::Execute: - return true; - case MemoryMode::ExecuteRead: - return true; - case MemoryMode::ExecuteWrite: - return true; - case MemoryMode::ExecuteReadWrite: - return true; - default: - return false; - } -} - -} // namespace VirtualMemory diff --git a/src/emulator.cpp b/src/emulator.cpp index a01eb816b..4ab535a2c 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -257,9 +257,9 @@ void Emulator::Run(const std::filesystem::path& file) { linker->Execute(); - window->initTimers(); - while (window->isOpen()) { - window->waitEvent(); + window->InitTimers(); + while (window->IsOpen()) { + window->WaitEvent(); } #ifdef ENABLE_QT_GUI diff --git a/src/imgui/renderer/imgui_core.cpp b/src/imgui/renderer/imgui_core.cpp index 47f60e8e4..46391faef 100644 --- a/src/imgui/renderer/imgui_core.cpp +++ b/src/imgui/renderer/imgui_core.cpp @@ -49,7 +49,7 @@ void Initialize(const ::Vulkan::Instance& instance, const Frontend::WindowSDL& w io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; - io.DisplaySize = ImVec2((float)window.getWidth(), (float)window.getHeight()); + io.DisplaySize = ImVec2((float)window.GetWidth(), (float)window.GetHeight()); PushStyleVar(ImGuiStyleVar_WindowRounding, 6.0f); // Makes the window edges rounded auto path = config_path.u8string(); @@ -83,7 +83,7 @@ void Initialize(const ::Vulkan::Instance& instance, const Frontend::WindowSDL& w StyleColorsDark(); ::Core::Devtools::Layer::SetupSettings(); - Sdl::Init(window.GetSdlWindow()); + Sdl::Init(window.GetSDLWindow()); const Vulkan::InitInfo vk_info{ .instance = instance.GetInstance(), @@ -108,7 +108,7 @@ void Initialize(const ::Vulkan::Instance& instance, const Frontend::WindowSDL& w ImFormatString(label, IM_ARRAYSIZE(label), "WindowOverViewport_%08X", GetMainViewport()->ID); dock_id = ImHashStr(label); - if (const auto dpi = SDL_GetWindowDisplayScale(window.GetSdlWindow()); dpi > 0.0f) { + if (const auto dpi = SDL_GetWindowDisplayScale(window.GetSDLWindow()); dpi > 0.0f) { GetIO().FontGlobalScale = dpi; } } diff --git a/src/input/controller.cpp b/src/input/controller.cpp index cd7d3d8af..3927b096f 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -1,13 +1,10 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "controller.h" - -#include "common/assert.h" +#include #include "core/libraries/kernel/time.h" #include "core/libraries/pad/pad.h" - -#include +#include "input/controller.h" namespace Input { @@ -59,9 +56,7 @@ State GameController::GetLastState() const { if (m_states_num == 0) { return m_last_state; } - - auto last = (m_first_state + m_states_num - 1) % MAX_STATES; - + const u32 last = (m_first_state + m_states_num - 1) % MAX_STATES; return m_states[last]; } @@ -71,19 +66,19 @@ void GameController::AddState(const State& state) { m_first_state = (m_first_state + 1) % MAX_STATES; } - auto index = (m_first_state + m_states_num) % MAX_STATES; - + const u32 index = (m_first_state + m_states_num) % MAX_STATES; m_states[index] = state; m_last_state = state; m_private[index].obtained = false; m_states_num++; } -void GameController::CheckButton(int id, u32 button, bool isPressed) { +void GameController::CheckButton(int id, Libraries::Pad::OrbisPadButtonDataOffset button, + bool is_pressed) { std::scoped_lock lock{m_mutex}; auto state = GetLastState(); state.time = Libraries::Kernel::sceKernelGetProcessTime(); - if (isPressed) { + if (is_pressed) { state.buttonsState |= button; } else { state.buttonsState &= ~button; @@ -93,28 +88,28 @@ void GameController::CheckButton(int id, u32 button, bool isPressed) { } void GameController::Axis(int id, Input::Axis axis, int value) { + using Libraries::Pad::OrbisPadButtonDataOffset; + std::scoped_lock lock{m_mutex}; auto state = GetLastState(); state.time = Libraries::Kernel::sceKernelGetProcessTime(); - int axis_id = static_cast(axis); - state.axes[axis_id] = value; if (axis == Input::Axis::TriggerLeft) { if (value > 0) { - state.buttonsState |= Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L2; + state.buttonsState |= OrbisPadButtonDataOffset::L2; } else { - state.buttonsState &= ~Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L2; + state.buttonsState &= ~OrbisPadButtonDataOffset::L2; } } if (axis == Input::Axis::TriggerRight) { if (value > 0) { - state.buttonsState |= Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R2; + state.buttonsState |= OrbisPadButtonDataOffset::R2; } else { - state.buttonsState &= ~Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R2; + state.buttonsState &= ~OrbisPadButtonDataOffset::R2; } } diff --git a/src/input/controller.h b/src/input/controller.h index 01ea21c0c..d425fb46c 100644 --- a/src/input/controller.h +++ b/src/input/controller.h @@ -5,6 +5,7 @@ #include #include "common/types.h" +#include "core/libraries/pad/pad.h" struct SDL_Gamepad; @@ -28,7 +29,7 @@ struct TouchpadEntry { }; struct State { - u32 buttonsState = 0; + Libraries::Pad::OrbisPadButtonDataOffset buttonsState{}; u64 time = 0; int axes[static_cast(Axis::AxisMax)] = {128, 128, 128, 128, 0, 0}; TouchpadEntry touchpad[2] = {{false, 0, 0}, {false, 0, 0}}; @@ -49,7 +50,7 @@ public: void ReadState(State* state, bool* isConnected, int* connectedCount); int ReadStates(State* states, int states_num, bool* isConnected, int* connectedCount); State GetLastState() const; - void CheckButton(int id, u32 button, bool isPressed); + void CheckButton(int id, Libraries::Pad::OrbisPadButtonDataOffset button, bool isPressed); void AddState(const State& state); void Axis(int id, Input::Axis axis, int value); void SetLightBarRGB(u8 r, u8 g, u8 b); diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index ad7d1b4a6..d95e8d634 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -6,9 +6,9 @@ #include #include #include + #include "common/assert.h" #include "common/config.h" -#include "common/version.h" #include "core/libraries/pad/pad.h" #include "imgui/renderer/imgui_core.h" #include "input/controller.h" @@ -21,6 +21,45 @@ namespace Frontend { +using namespace Libraries::Pad; + +static OrbisPadButtonDataOffset SDLGamepadToOrbisButton(u8 button) { + switch (button) { + case SDL_GAMEPAD_BUTTON_DPAD_DOWN: + return OrbisPadButtonDataOffset::Down; + case SDL_GAMEPAD_BUTTON_DPAD_UP: + return OrbisPadButtonDataOffset::Up; + case SDL_GAMEPAD_BUTTON_DPAD_LEFT: + return OrbisPadButtonDataOffset::Left; + case SDL_GAMEPAD_BUTTON_DPAD_RIGHT: + return OrbisPadButtonDataOffset::Right; + case SDL_GAMEPAD_BUTTON_SOUTH: + return OrbisPadButtonDataOffset::Cross; + case SDL_GAMEPAD_BUTTON_NORTH: + return OrbisPadButtonDataOffset::Triangle; + case SDL_GAMEPAD_BUTTON_WEST: + return OrbisPadButtonDataOffset::Square; + case SDL_GAMEPAD_BUTTON_EAST: + return OrbisPadButtonDataOffset::Circle; + case SDL_GAMEPAD_BUTTON_START: + return OrbisPadButtonDataOffset::Options; + case SDL_GAMEPAD_BUTTON_TOUCHPAD: + return OrbisPadButtonDataOffset::TouchPad; + case SDL_GAMEPAD_BUTTON_BACK: + return OrbisPadButtonDataOffset::TouchPad; + case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER: + return OrbisPadButtonDataOffset::L1; + case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER: + return OrbisPadButtonDataOffset::R1; + case SDL_GAMEPAD_BUTTON_LEFT_STICK: + return OrbisPadButtonDataOffset::L3; + case SDL_GAMEPAD_BUTTON_RIGHT_STICK: + return OrbisPadButtonDataOffset::R3; + default: + return OrbisPadButtonDataOffset::None; + } +} + static Uint32 SDLCALL PollController(void* userdata, SDL_TimerID timer_id, Uint32 interval) { auto* controller = reinterpret_cast(userdata); return controller->Poll(); @@ -80,7 +119,7 @@ WindowSDL::WindowSDL(s32 width_, s32 height_, Input::GameController* controller_ WindowSDL::~WindowSDL() = default; -void WindowSDL::waitEvent() { +void WindowSDL::WaitEvent() { // Called on main thread SDL_Event event; @@ -96,16 +135,16 @@ void WindowSDL::waitEvent() { case SDL_EVENT_WINDOW_RESIZED: case SDL_EVENT_WINDOW_MAXIMIZED: case SDL_EVENT_WINDOW_RESTORED: - onResize(); + OnResize(); break; case SDL_EVENT_WINDOW_MINIMIZED: case SDL_EVENT_WINDOW_EXPOSED: is_shown = event.type == SDL_EVENT_WINDOW_EXPOSED; - onResize(); + OnResize(); break; case SDL_EVENT_KEY_DOWN: case SDL_EVENT_KEY_UP: - onKeyPress(&event); + OnKeyPress(&event); break; case SDL_EVENT_GAMEPAD_BUTTON_DOWN: case SDL_EVENT_GAMEPAD_BUTTON_UP: @@ -115,7 +154,7 @@ void WindowSDL::waitEvent() { case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN: case SDL_EVENT_GAMEPAD_TOUCHPAD_UP: case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION: - onGamepadEvent(&event); + OnGamepadEvent(&event); break; case SDL_EVENT_QUIT: is_open = false; @@ -125,18 +164,16 @@ void WindowSDL::waitEvent() { } } -void WindowSDL::initTimers() { +void WindowSDL::InitTimers() { SDL_AddTimer(100, &PollController, controller); } -void WindowSDL::onResize() { +void WindowSDL::OnResize() { SDL_GetWindowSizeInPixels(window, &width, &height); ImGui::Core::OnResize(); } -void WindowSDL::onKeyPress(const SDL_Event* event) { - using Libraries::Pad::OrbisPadButtonDataOffset; - +void WindowSDL::OnKeyPress(const SDL_Event* event) { #ifdef __APPLE__ // Use keys that are more friendly for keyboards without a keypad. // Once there are key binding options this won't be necessary. @@ -151,38 +188,38 @@ void WindowSDL::onKeyPress(const SDL_Event* event) { constexpr SDL_Keycode TriangleKey = SDLK_KP_8; #endif - u32 button = 0; + auto button = OrbisPadButtonDataOffset::None; Input::Axis axis = Input::Axis::AxisMax; int axisvalue = 0; int ax = 0; std::string backButtonBehavior = Config::getBackButtonBehavior(); switch (event->key.key) { case SDLK_UP: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_UP; + button = OrbisPadButtonDataOffset::Up; break; case SDLK_DOWN: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_DOWN; + button = OrbisPadButtonDataOffset::Down; break; case SDLK_LEFT: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_LEFT; + button = OrbisPadButtonDataOffset::Left; break; case SDLK_RIGHT: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_RIGHT; + button = OrbisPadButtonDataOffset::Right; break; case TriangleKey: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TRIANGLE; + button = OrbisPadButtonDataOffset::Triangle; break; case CircleKey: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_CIRCLE; + button = OrbisPadButtonDataOffset::Circle; break; case CrossKey: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_CROSS; + button = OrbisPadButtonDataOffset::Cross; break; case SquareKey: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_SQUARE; + button = OrbisPadButtonDataOffset::Square; break; case SDLK_RETURN: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_OPTIONS; + button = OrbisPadButtonDataOffset::Options; break; case SDLK_A: axis = Input::Axis::LeftX; @@ -257,19 +294,19 @@ void WindowSDL::onKeyPress(const SDL_Event* event) { ax = Input::GetAxis(-0x80, 0x80, axisvalue); break; case SDLK_X: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L3; + button = OrbisPadButtonDataOffset::L3; break; case SDLK_M: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R3; + button = OrbisPadButtonDataOffset::R3; break; case SDLK_Q: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L1; + button = OrbisPadButtonDataOffset::L1; break; case SDLK_U: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R1; + button = OrbisPadButtonDataOffset::R1; break; case SDLK_E: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L2; + button = OrbisPadButtonDataOffset::L2; axis = Input::Axis::TriggerLeft; if (event->type == SDL_EVENT_KEY_DOWN) { axisvalue += 255; @@ -279,7 +316,7 @@ void WindowSDL::onKeyPress(const SDL_Event* event) { ax = Input::GetAxis(0, 0x80, axisvalue); break; case SDLK_O: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R2; + button = OrbisPadButtonDataOffset::R2; axis = Input::Axis::TriggerRight; if (event->type == SDL_EVENT_KEY_DOWN) { axisvalue += 255; @@ -294,9 +331,9 @@ void WindowSDL::onKeyPress(const SDL_Event* event) { : (backButtonBehavior == "right" ? 0.75f : 0.5f); // trigger a touchpad event so that the touchpad emulation for back button works controller->SetTouchpadState(0, true, x, 0.5f); - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TOUCH_PAD; + button = OrbisPadButtonDataOffset::TouchPad; } else { - button = 0; + button = {}; } break; case SDLK_F11: @@ -317,7 +354,7 @@ void WindowSDL::onKeyPress(const SDL_Event* event) { default: break; } - if (button != 0) { + if (button != OrbisPadButtonDataOffset::None) { controller->CheckButton(0, button, event->type == SDL_EVENT_KEY_DOWN); } if (axis != Input::Axis::AxisMax) { @@ -325,10 +362,8 @@ void WindowSDL::onKeyPress(const SDL_Event* event) { } } -void WindowSDL::onGamepadEvent(const SDL_Event* event) { - using Libraries::Pad::OrbisPadButtonDataOffset; - - u32 button = 0; +void WindowSDL::OnGamepadEvent(const SDL_Event* event) { + auto button = OrbisPadButtonDataOffset::None; Input::Axis axis = Input::Axis::AxisMax; switch (event->type) { case SDL_EVENT_GAMEPAD_ADDED: @@ -343,25 +378,25 @@ void WindowSDL::onGamepadEvent(const SDL_Event* event) { event->gtouchpad.x, event->gtouchpad.y); break; case SDL_EVENT_GAMEPAD_BUTTON_DOWN: - case SDL_EVENT_GAMEPAD_BUTTON_UP: - button = sdlGamepadToOrbisButton(event->gbutton.button); - if (button != 0) { - if (event->gbutton.button == SDL_GAMEPAD_BUTTON_BACK) { - std::string backButtonBehavior = Config::getBackButtonBehavior(); - if (backButtonBehavior != "none") { - float x = backButtonBehavior == "left" - ? 0.25f - : (backButtonBehavior == "right" ? 0.75f : 0.5f); - // trigger a touchpad event so that the touchpad emulation for back button works - controller->SetTouchpadState(0, true, x, 0.5f); - controller->CheckButton(0, button, - event->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN); - } - } else { - controller->CheckButton(0, button, event->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN); - } + case SDL_EVENT_GAMEPAD_BUTTON_UP: { + button = SDLGamepadToOrbisButton(event->gbutton.button); + if (button == OrbisPadButtonDataOffset::None) { + break; + } + if (event->gbutton.button != SDL_GAMEPAD_BUTTON_BACK) { + controller->CheckButton(0, button, event->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN); + break; + } + const auto backButtonBehavior = Config::getBackButtonBehavior(); + if (backButtonBehavior != "none") { + float x = backButtonBehavior == "left" ? 0.25f + : (backButtonBehavior == "right" ? 0.75f : 0.5f); + // trigger a touchpad event so that the touchpad emulation for back button works + controller->SetTouchpadState(0, true, x, 0.5f); + controller->CheckButton(0, button, event->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN); } break; + } case SDL_EVENT_GAMEPAD_AXIS_MOTION: axis = event->gaxis.axis == SDL_GAMEPAD_AXIS_LEFTX ? Input::Axis::LeftX : event->gaxis.axis == SDL_GAMEPAD_AXIS_LEFTY ? Input::Axis::LeftY @@ -383,43 +418,4 @@ void WindowSDL::onGamepadEvent(const SDL_Event* event) { } } -int WindowSDL::sdlGamepadToOrbisButton(u8 button) { - using Libraries::Pad::OrbisPadButtonDataOffset; - - switch (button) { - case SDL_GAMEPAD_BUTTON_DPAD_DOWN: - return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_DOWN; - case SDL_GAMEPAD_BUTTON_DPAD_UP: - return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_UP; - case SDL_GAMEPAD_BUTTON_DPAD_LEFT: - return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_LEFT; - case SDL_GAMEPAD_BUTTON_DPAD_RIGHT: - return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_RIGHT; - case SDL_GAMEPAD_BUTTON_SOUTH: - return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_CROSS; - case SDL_GAMEPAD_BUTTON_NORTH: - return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TRIANGLE; - case SDL_GAMEPAD_BUTTON_WEST: - return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_SQUARE; - case SDL_GAMEPAD_BUTTON_EAST: - return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_CIRCLE; - case SDL_GAMEPAD_BUTTON_START: - return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_OPTIONS; - case SDL_GAMEPAD_BUTTON_TOUCHPAD: - return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TOUCH_PAD; - case SDL_GAMEPAD_BUTTON_BACK: - return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TOUCH_PAD; - case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER: - return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L1; - case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER: - return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R1; - case SDL_GAMEPAD_BUTTON_LEFT_STICK: - return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L3; - case SDL_GAMEPAD_BUTTON_RIGHT_STICK: - return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R3; - default: - return 0; - } -} - } // namespace Frontend diff --git a/src/sdl_window.h b/src/sdl_window.h index ec8de354b..78d0e582f 100644 --- a/src/sdl_window.h +++ b/src/sdl_window.h @@ -46,36 +46,33 @@ public: std::string_view window_title); ~WindowSDL(); - s32 getWidth() const { + s32 GetWidth() const { return width; } - s32 getHeight() const { + s32 GetHeight() const { return height; } - bool isOpen() const { + bool IsOpen() const { return is_open; } - [[nodiscard]] SDL_Window* GetSdlWindow() const { + [[nodiscard]] SDL_Window* GetSDLWindow() const { return window; } - WindowSystemInfo getWindowInfo() const { + WindowSystemInfo GetWindowInfo() const { return window_info; } - void waitEvent(); - - void initTimers(); + void WaitEvent(); + void InitTimers(); private: - void onResize(); - void onKeyPress(const SDL_Event* event); - void onGamepadEvent(const SDL_Event* event); - - int sdlGamepadToOrbisButton(u8 button); + void OnResize(); + void OnKeyPress(const SDL_Event* event); + void OnGamepadEvent(const SDL_Event* event); private: s32 width; diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index a16f35ed7..580458e7e 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -95,7 +95,7 @@ Instance::Instance(bool enable_validation, bool enable_crash_diagnostic) Instance::Instance(Frontend::WindowSDL& window, s32 physical_device_index, bool enable_validation /*= false*/, bool enable_crash_diagnostic /*= false*/) - : instance{CreateInstance(window.getWindowInfo().type, enable_validation, + : instance{CreateInstance(window.GetWindowInfo().type, enable_validation, enable_crash_diagnostic)}, physical_devices{EnumeratePhysicalDevices(instance)} { if (enable_validation) { diff --git a/src/video_core/renderer_vulkan/vk_platform.cpp b/src/video_core/renderer_vulkan/vk_platform.cpp index 0eb7e0759..b2a50cd44 100644 --- a/src/video_core/renderer_vulkan/vk_platform.cpp +++ b/src/video_core/renderer_vulkan/vk_platform.cpp @@ -73,7 +73,7 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL DebugUtilsCallback( } vk::SurfaceKHR CreateSurface(vk::Instance instance, const Frontend::WindowSDL& emu_window) { - const auto& window_info = emu_window.getWindowInfo(); + const auto& window_info = emu_window.GetWindowInfo(); vk::SurfaceKHR surface{}; #if defined(VK_USE_PLATFORM_WIN32_KHR) diff --git a/src/video_core/renderer_vulkan/vk_presenter.cpp b/src/video_core/renderer_vulkan/vk_presenter.cpp index 23d2981f5..b7d829316 100644 --- a/src/video_core/renderer_vulkan/vk_presenter.cpp +++ b/src/video_core/renderer_vulkan/vk_presenter.cpp @@ -629,9 +629,9 @@ Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop) void Presenter::Present(Frame* frame) { // Recreate the swapchain if the window was resized. - if (window.getWidth() != swapchain.GetExtent().width || - window.getHeight() != swapchain.GetExtent().height) { - swapchain.Recreate(window.getWidth(), window.getHeight()); + if (window.GetWidth() != swapchain.GetExtent().width || + window.GetHeight() != swapchain.GetExtent().height) { + swapchain.Recreate(window.GetWidth(), window.GetHeight()); } ImGui::Core::NewFrame(); @@ -776,8 +776,8 @@ Frame* Presenter::GetRenderFrame() { device.resetFences(frame->present_done); // If the window dimensions changed, recreate this frame - if (frame->width != window.getWidth() || frame->height != window.getHeight()) { - RecreateFrame(frame, window.getWidth(), window.getHeight()); + if (frame->width != window.GetWidth() || frame->height != window.GetHeight()) { + RecreateFrame(frame, window.GetWidth(), window.GetHeight()); } return frame; diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index 86d7d5063..d0bc7ebdc 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -14,7 +14,7 @@ namespace Vulkan { Swapchain::Swapchain(const Instance& instance_, const Frontend::WindowSDL& window) : instance{instance_}, surface{CreateSurface(instance.GetInstance(), window)} { FindPresentFormat(); - Create(window.getWidth(), window.getHeight(), surface); + Create(window.GetWidth(), window.GetHeight(), surface); } Swapchain::~Swapchain() { From b0860d6e8c95cf585f28a5096135878b15e47931 Mon Sep 17 00:00:00 2001 From: Jamie Tong Date: Sun, 1 Dec 2024 04:39:11 +0800 Subject: [PATCH 084/549] implement DS_AND_B32, DS_OR_B32, DS_XOR_B32 (#1593) * implement DS_OR_B32 * implement DS_AND_B32, DS_XOR_B32 --- .../backend/spirv/emit_spirv_atomic.cpp | 12 +++++ .../backend/spirv/emit_spirv_instructions.h | 3 ++ .../frontend/translate/data_share.cpp | 49 +++++++++++++++++++ .../frontend/translate/translate.h | 3 ++ src/shader_recompiler/ir/ir_emitter.cpp | 12 +++++ src/shader_recompiler/ir/ir_emitter.h | 3 ++ src/shader_recompiler/ir/microinstruction.cpp | 3 ++ src/shader_recompiler/ir/opcodes.inc | 3 ++ 8 files changed, 88 insertions(+) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp index a58b2778f..ce65a5ccb 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp @@ -60,6 +60,18 @@ Id EmitSharedAtomicSMin32(EmitContext& ctx, Id offset, Id value) { return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicSMin); } +Id EmitSharedAtomicAnd32(EmitContext& ctx, Id offset, Id value) { + return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicAnd); +} + +Id EmitSharedAtomicOr32(EmitContext& ctx, Id offset, Id value) { + return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicOr); +} + +Id EmitSharedAtomicXor32(EmitContext& ctx, Id offset, Id value) { + return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicXor); +} + Id EmitBufferAtomicIAdd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) { return BufferAtomicU32(ctx, inst, handle, address, value, &Sirit::Module::OpAtomicIAdd); } diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index 12361991a..cc3db880c 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h @@ -112,6 +112,9 @@ Id EmitSharedAtomicUMax32(EmitContext& ctx, Id offset, Id value); Id EmitSharedAtomicSMax32(EmitContext& ctx, Id offset, Id value); Id EmitSharedAtomicUMin32(EmitContext& ctx, Id offset, Id value); Id EmitSharedAtomicSMin32(EmitContext& ctx, Id offset, Id value); +Id EmitSharedAtomicAnd32(EmitContext& ctx, Id offset, Id value); +Id EmitSharedAtomicOr32(EmitContext& ctx, Id offset, Id value); +Id EmitSharedAtomicXor32(EmitContext& ctx, Id offset, Id value); Id EmitCompositeConstructU32x2(EmitContext& ctx, Id e1, Id e2); Id EmitCompositeConstructU32x3(EmitContext& ctx, Id e1, Id e2, Id e3); Id EmitCompositeConstructU32x4(EmitContext& ctx, Id e1, Id e2, Id e3, Id e4); diff --git a/src/shader_recompiler/frontend/translate/data_share.cpp b/src/shader_recompiler/frontend/translate/data_share.cpp index 2f5932f80..5914f9fe3 100644 --- a/src/shader_recompiler/frontend/translate/data_share.cpp +++ b/src/shader_recompiler/frontend/translate/data_share.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "shader_recompiler/frontend/translate/translate.h" +#include "shader_recompiler/ir/reg.h" namespace Shader::Gcn { @@ -18,6 +19,12 @@ void Translator::EmitDataShare(const GcnInst& inst) { return DS_MIN_U32(inst, false, false); case Opcode::DS_MAX_U32: return DS_MAX_U32(inst, false, false); + case Opcode::DS_AND_B32: + return DS_AND_B32(inst, false); + case Opcode::DS_OR_B32: + return DS_OR_B32(inst, false); + case Opcode::DS_XOR_B32: + return DS_XOR_B32(inst, false); case Opcode::DS_WRITE_B32: return DS_WRITE(32, false, false, false, inst); case Opcode::DS_WRITE2_B32: @@ -30,6 +37,12 @@ void Translator::EmitDataShare(const GcnInst& inst) { return DS_MIN_U32(inst, false, true); case Opcode::DS_MAX_RTN_U32: return DS_MAX_U32(inst, false, true); + case Opcode::DS_AND_RTN_B32: + return DS_AND_B32(inst, true); + case Opcode::DS_OR_RTN_B32: + return DS_OR_B32(inst, true); + case Opcode::DS_XOR_RTN_B32: + return DS_XOR_B32(inst, true); case Opcode::DS_SWIZZLE_B32: return DS_SWIZZLE_B32(inst); case Opcode::DS_READ_B32: @@ -119,6 +132,42 @@ void Translator::DS_MAX_U32(const GcnInst& inst, bool is_signed, bool rtn) { } } +void Translator::DS_AND_B32(const GcnInst& inst, bool rtn) { + const IR::U32 addr{GetSrc(inst.src[0])}; + const IR::U32 data{GetSrc(inst.src[1])}; + const IR::U32 offset = + ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0)); + const IR::U32 addr_offset = ir.IAdd(addr, offset); + const IR::Value original_val = ir.SharedAtomicAnd(addr_offset, data); + if (rtn) { + SetDst(inst.dst[0], IR::U32{original_val}); + } +} + +void Translator::DS_OR_B32(const GcnInst& inst, bool rtn) { + const IR::U32 addr{GetSrc(inst.src[0])}; + const IR::U32 data{GetSrc(inst.src[1])}; + const IR::U32 offset = + ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0)); + const IR::U32 addr_offset = ir.IAdd(addr, offset); + const IR::Value original_val = ir.SharedAtomicOr(addr_offset, data); + if (rtn) { + SetDst(inst.dst[0], IR::U32{original_val}); + } +} + +void Translator::DS_XOR_B32(const GcnInst& inst, bool rtn) { + const IR::U32 addr{GetSrc(inst.src[0])}; + const IR::U32 data{GetSrc(inst.src[1])}; + const IR::U32 offset = + ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0)); + const IR::U32 addr_offset = ir.IAdd(addr, offset); + const IR::Value original_val = ir.SharedAtomicXor(addr_offset, data); + if (rtn) { + SetDst(inst.dst[0], IR::U32{original_val}); + } +} + void Translator::DS_WRITE(int bit_size, bool is_signed, bool is_pair, bool stride64, const GcnInst& inst) { const IR::U32 addr{ir.GetVectorReg(IR::VectorReg(inst.src[0].code))}; diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index f04038909..3b89372bd 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -250,6 +250,9 @@ public: void DS_MAX_U32(const GcnInst& inst, bool is_signed, bool rtn); void DS_WRITE(int bit_size, bool is_signed, bool is_pair, bool stride64, const GcnInst& inst); void DS_SWIZZLE_B32(const GcnInst& inst); + void DS_AND_B32(const GcnInst& inst, bool rtn); + void DS_OR_B32(const GcnInst& inst, bool rtn); + void DS_XOR_B32(const GcnInst& inst, bool rtn); void DS_READ(int bit_size, bool is_signed, bool is_pair, bool stride64, const GcnInst& inst); void DS_APPEND(const GcnInst& inst); void DS_CONSUME(const GcnInst& inst); diff --git a/src/shader_recompiler/ir/ir_emitter.cpp b/src/shader_recompiler/ir/ir_emitter.cpp index 73b33432b..78e7f2289 100644 --- a/src/shader_recompiler/ir/ir_emitter.cpp +++ b/src/shader_recompiler/ir/ir_emitter.cpp @@ -326,6 +326,18 @@ U32 IREmitter::SharedAtomicIMax(const U32& address, const U32& data, bool is_sig : Inst(Opcode::SharedAtomicUMax32, address, data); } +U32 IREmitter::SharedAtomicAnd(const U32& address, const U32& data) { + return Inst(Opcode::SharedAtomicAnd32, address, data); +} + +U32 IREmitter::SharedAtomicOr(const U32& address, const U32& data) { + return Inst(Opcode::SharedAtomicOr32, address, data); +} + +U32 IREmitter::SharedAtomicXor(const U32& address, const U32& data) { + return Inst(Opcode::SharedAtomicXor32, address, data); +} + U32 IREmitter::ReadConst(const Value& base, const U32& offset) { return Inst(Opcode::ReadConst, base, offset); } diff --git a/src/shader_recompiler/ir/ir_emitter.h b/src/shader_recompiler/ir/ir_emitter.h index b3f513085..cbd3780de 100644 --- a/src/shader_recompiler/ir/ir_emitter.h +++ b/src/shader_recompiler/ir/ir_emitter.h @@ -90,6 +90,9 @@ public: [[nodiscard]] U32F32 SharedAtomicIAdd(const U32& address, const U32F32& data); [[nodiscard]] U32 SharedAtomicIMin(const U32& address, const U32& data, bool is_signed); [[nodiscard]] U32 SharedAtomicIMax(const U32& address, const U32& data, bool is_signed); + [[nodiscard]] U32 SharedAtomicAnd(const U32& address, const U32& data); + [[nodiscard]] U32 SharedAtomicOr(const U32& address, const U32& data); + [[nodiscard]] U32 SharedAtomicXor(const U32& address, const U32& data); [[nodiscard]] U32 ReadConst(const Value& base, const U32& offset); [[nodiscard]] U32 ReadConstBuffer(const Value& handle, const U32& index); diff --git a/src/shader_recompiler/ir/microinstruction.cpp b/src/shader_recompiler/ir/microinstruction.cpp index f0b4882b3..abd31a728 100644 --- a/src/shader_recompiler/ir/microinstruction.cpp +++ b/src/shader_recompiler/ir/microinstruction.cpp @@ -77,6 +77,9 @@ bool Inst::MayHaveSideEffects() const noexcept { case Opcode::SharedAtomicUMin32: case Opcode::SharedAtomicSMax32: case Opcode::SharedAtomicUMax32: + case Opcode::SharedAtomicAnd32: + case Opcode::SharedAtomicOr32: + case Opcode::SharedAtomicXor32: case Opcode::ImageWrite: case Opcode::ImageAtomicIAdd32: case Opcode::ImageAtomicSMin32: diff --git a/src/shader_recompiler/ir/opcodes.inc b/src/shader_recompiler/ir/opcodes.inc index 51e10fb38..0283ccd0f 100644 --- a/src/shader_recompiler/ir/opcodes.inc +++ b/src/shader_recompiler/ir/opcodes.inc @@ -43,6 +43,9 @@ OPCODE(SharedAtomicSMin32, U32, U32, OPCODE(SharedAtomicUMin32, U32, U32, U32, ) OPCODE(SharedAtomicSMax32, U32, U32, U32, ) OPCODE(SharedAtomicUMax32, U32, U32, U32, ) +OPCODE(SharedAtomicAnd32, U32, U32, U32, ) +OPCODE(SharedAtomicOr32, U32, U32, U32, ) +OPCODE(SharedAtomicXor32, U32, U32, U32, ) // Context getters/setters OPCODE(GetUserData, U32, ScalarReg, ) From 5f4d03172f0a56aee48566164e030980a39c3a50 Mon Sep 17 00:00:00 2001 From: Alexandre Bouvier Date: Sat, 30 Nov 2024 20:39:51 +0000 Subject: [PATCH 085/549] cmake: unbundle libpng (#1576) --- CMakeLists.txt | 5 +++-- cmake/Findzlib-ng.cmake | 15 --------------- externals/CMakeLists.txt | 31 ++++++++++++++++++------------- 3 files changed, 21 insertions(+), 30 deletions(-) delete mode 100644 cmake/Findzlib-ng.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index a8c3275f7..4ac1a6025 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -113,6 +113,7 @@ find_package(fmt 10.2.0 CONFIG) find_package(glslang 14.2.0 CONFIG) find_package(half 1.12.0 MODULE) find_package(magic_enum 0.9.6 CONFIG) +find_package(PNG 1.6 MODULE) find_package(RenderDoc 1.6.0 MODULE) find_package(SDL3 3.1.2 CONFIG) find_package(toml11 4.2.0 CONFIG) @@ -121,7 +122,7 @@ find_package(VulkanHeaders 1.3.289 CONFIG) find_package(VulkanMemoryAllocator 3.1.0 CONFIG) find_package(xbyak 7.07 CONFIG) find_package(xxHash 0.8.2 MODULE) -find_package(zlib-ng 2.1.7 MODULE) +find_package(ZLIB 1.3 MODULE) find_package(Zydis 5.0.0 CONFIG) find_package(pugixml 1.14 CONFIG) @@ -851,7 +852,7 @@ endif() create_target_directory_groups(shadps4) -target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half zlib-ng::zlib png_static) +target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG) target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::SPIRV glslang::glslang SDL3::SDL3 pugixml::pugixml) target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h") diff --git a/cmake/Findzlib-ng.cmake b/cmake/Findzlib-ng.cmake deleted file mode 100644 index ec6f14b4a..000000000 --- a/cmake/Findzlib-ng.cmake +++ /dev/null @@ -1,15 +0,0 @@ -# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -# SPDX-License-Identifier: GPL-2.0-or-later - -find_package(PkgConfig QUIET) -pkg_search_module(ZLIB_NG QUIET IMPORTED_TARGET zlib-ng) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(zlib-ng - REQUIRED_VARS ZLIB_NG_LINK_LIBRARIES - VERSION_VAR ZLIB_NG_VERSION -) - -if (zlib-ng_FOUND AND NOT TARGET zlib-ng::zlib) - add_library(zlib-ng::zlib ALIAS PkgConfig::ZLIB_NG) -endif() diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 8d080acc3..53802276e 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -52,15 +52,20 @@ file(GLOB LIBATRAC9_SOURCES LibAtrac9/C/src/*.c) add_library(LibAtrac9 STATIC ${LIBATRAC9_SOURCES}) target_include_directories(LibAtrac9 INTERFACE LibAtrac9/C/src) -# Zlib-Ng -if (NOT TARGET zlib-ng::zlib) +# zlib +if (NOT TARGET ZLIB::ZLIB) set(ZLIB_ENABLE_TESTS OFF) set(WITH_GTEST OFF) set(WITH_NEW_STRATEGIES ON) set(WITH_NATIVE_INSTRUCTIONS ON) set(ZLIB_COMPAT ON CACHE BOOL "" FORCE) - add_subdirectory(zlib-ng) - add_library(zlib-ng::zlib ALIAS zlib) + include(FetchContent) + FetchContent_Declare( + ZLIB + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/zlib-ng" + OVERRIDE_FIND_PACKAGE + ) + FetchContent_MakeAvailable(ZLIB) add_library(ZLIB::ZLIB ALIAS zlib) endif() @@ -156,15 +161,15 @@ if (NOT TARGET half::half) endif() # libpng -set(PNG_SHARED OFF CACHE BOOL "" FORCE) -set(PNG_STATIC ON CACHE BOOL "" FORCE) -set(PNG_TESTS OFF CACHE BOOL "" FORCE) -set(PNG_TOOLS OFF CACHE BOOL "" FORCE) -set(SKIP_INSTALL_ALL OFF CACHE BOOL "" FORCE) -set(ZLIB_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/zlib-ng" CACHE STRING "" FORCE) -set(ZLIB_LIBRARY "${CMAKE_CURRENT_BINARY_DIR}/zlib-ng/zlibstatic-ngd" CACHE STRING "" FORCE) -file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/libpng/zlib.h" "#include \"../zlib-ng/zlib.h\"") -add_subdirectory(libpng) +if (NOT TARGET PNG::PNG) + set(PNG_SHARED OFF CACHE BOOL "" FORCE) + set(PNG_STATIC ON CACHE BOOL "" FORCE) + set(PNG_TESTS OFF CACHE BOOL "" FORCE) + set(PNG_TOOLS OFF CACHE BOOL "" FORCE) + set(SKIP_INSTALL_ALL OFF CACHE BOOL "" FORCE) + add_subdirectory(libpng) + add_library(PNG::PNG ALIAS png_static) +endif() if (APPLE) # date From 4ebb90c774cacacd89e24886e4bef595c5ca9e10 Mon Sep 17 00:00:00 2001 From: Alexandre Bouvier Date: Sun, 1 Dec 2024 09:16:01 +0000 Subject: [PATCH 086/549] cmake: hot fixes (#1638) --- CMakeLists.txt | 5 +++-- externals/CMakeLists.txt | 5 ++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ac1a6025..82b85f2d2 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,8 @@ # SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project # SPDX-License-Identifier: GPL-2.0-or-later -cmake_minimum_required(VERSION 3.16.3) +# Version 3.24 needed for FetchContent OVERRIDE_FIND_PACKAGE +cmake_minimum_required(VERSION 3.24) set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED True) @@ -110,7 +111,7 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/common/scm_rev.cpp.in" "${CMAKE_ find_package(Boost 1.84.0 CONFIG) find_package(FFmpeg 5.1.2 MODULE) find_package(fmt 10.2.0 CONFIG) -find_package(glslang 14.2.0 CONFIG) +find_package(glslang 15 CONFIG) find_package(half 1.12.0 MODULE) find_package(magic_enum 0.9.6 CONFIG) find_package(PNG 1.6 MODULE) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 53802276e..50eda0288 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -38,7 +38,8 @@ else() set(CRYPTOPP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cryptopp/) add_subdirectory(cryptopp-cmake) file(COPY cryptopp DESTINATION cryptopp FILES_MATCHING PATTERN "*.h") - target_include_directories(cryptopp INTERFACE "${CMAKE_CURRENT_BINARY_DIR}/cryptopp") + # remove externals/cryptopp from include directories because it contains a conflicting zlib.h file + set_target_properties(cryptopp PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/cryptopp") endif() endif() @@ -67,6 +68,8 @@ if (NOT TARGET ZLIB::ZLIB) ) FetchContent_MakeAvailable(ZLIB) add_library(ZLIB::ZLIB ALIAS zlib) + # libpng expects this variable to exist after its find_package(ZLIB) + get_target_property(ZLIB_INCLUDE_DIRS zlib INTERFACE_INCLUDE_DIRECTORIES) endif() # SDL3 From 394a14626b4d691328e372e0cc9279597e7fbda8 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sun, 1 Dec 2024 03:44:15 -0800 Subject: [PATCH 087/549] libraries: Add stubs for libScePlayGoDialog (#1635) --- CMakeLists.txt | 2 + src/common/logging/filter.cpp | 1 + src/common/logging/types.h | 1 + src/core/libraries/libs.cpp | 2 + src/core/libraries/playgo/playgo_dialog.cpp | 78 +++++++++++++++++++++ src/core/libraries/playgo/playgo_dialog.h | 38 ++++++++++ 6 files changed, 122 insertions(+) create mode 100644 src/core/libraries/playgo/playgo_dialog.cpp create mode 100644 src/core/libraries/playgo/playgo_dialog.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 82b85f2d2..ed366a0b5 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -374,6 +374,8 @@ set(JPEG_LIB src/core/libraries/jpeg/jpeg_error.h set(PLAYGO_LIB src/core/libraries/playgo/playgo.cpp src/core/libraries/playgo/playgo.h + src/core/libraries/playgo/playgo_dialog.cpp + src/core/libraries/playgo/playgo_dialog.h src/core/libraries/playgo/playgo_types.h ) diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index 5f44658f0..632b2b329 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -107,6 +107,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Lib, Png) \ SUB(Lib, Jpeg) \ SUB(Lib, PlayGo) \ + SUB(Lib, PlayGoDialog) \ SUB(Lib, Random) \ SUB(Lib, Usbd) \ SUB(Lib, Ajm) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index a373e8f1b..e7e91882a 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -74,6 +74,7 @@ enum class Class : u8 { Lib_Png, ///< The LibScePng implementation. Lib_Jpeg, ///< The LibSceJpeg implementation. Lib_PlayGo, ///< The LibScePlayGo implementation. + Lib_PlayGoDialog, ///< The LibScePlayGoDialog implementation. Lib_Random, ///< The libSceRandom implementation. Lib_Usbd, ///< The LibSceUsbd implementation. Lib_Ajm, ///< The LibSceAjm implementation. diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index 53b67b395..66cdd5b87 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -27,6 +27,7 @@ #include "core/libraries/np_trophy/np_trophy.h" #include "core/libraries/pad/pad.h" #include "core/libraries/playgo/playgo.h" +#include "core/libraries/playgo/playgo_dialog.h" #include "core/libraries/random/random.h" #include "core/libraries/razor_cpu/razor_cpu.h" #include "core/libraries/remote_play/remoteplay.h" @@ -74,6 +75,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::AppContent::RegisterlibSceAppContent(sym); Libraries::PngDec::RegisterlibScePngDec(sym); Libraries::PlayGo::RegisterlibScePlayGo(sym); + Libraries::PlayGo::Dialog::RegisterlibScePlayGoDialog(sym); Libraries::Random::RegisterlibSceRandom(sym); Libraries::Usbd::RegisterlibSceUsbd(sym); Libraries::Pad::RegisterlibScePad(sym); diff --git a/src/core/libraries/playgo/playgo_dialog.cpp b/src/core/libraries/playgo/playgo_dialog.cpp new file mode 100644 index 000000000..16f7aa05d --- /dev/null +++ b/src/core/libraries/playgo/playgo_dialog.cpp @@ -0,0 +1,78 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/libraries/libs.h" +#include "core/libraries/playgo/playgo_dialog.h" +#include "core/libraries/system/commondialog.h" + +namespace Libraries::PlayGo::Dialog { + +using CommonDialog::Error; +using CommonDialog::Result; +using CommonDialog::Status; + +Error PS4_SYSV_ABI scePlayGoDialogClose() { + LOG_ERROR(Lib_PlayGoDialog, "(DUMMY) called"); + return Error::OK; +} + +Error PS4_SYSV_ABI scePlayGoDialogGetResult(OrbisPlayGoDialogResult* result) { + LOG_ERROR(Lib_PlayGoDialog, "(DUMMY) called"); + if (result == nullptr) { + return Error::ARG_NULL; + } + // Result value 3 allows games to proceed. + result->result = static_cast(3); + return Error::OK; +} + +Status PS4_SYSV_ABI scePlayGoDialogGetStatus() { + LOG_ERROR(Lib_PlayGoDialog, "(DUMMY) called"); + return Status::FINISHED; +} + +Error PS4_SYSV_ABI scePlayGoDialogInitialize() { + LOG_ERROR(Lib_PlayGoDialog, "(DUMMY) called"); + return Error::OK; +} + +Error PS4_SYSV_ABI scePlayGoDialogOpen(const OrbisPlayGoDialogParam* param) { + LOG_ERROR(Lib_PlayGoDialog, "(DUMMY) called"); + if (param == nullptr) { + return Error::ARG_NULL; + } + ASSERT(param->size == sizeof(OrbisPlayGoDialogParam)); + ASSERT(param->baseParam.size == sizeof(CommonDialog::BaseParam)); + return Error::OK; +} + +Error PS4_SYSV_ABI scePlayGoDialogTerminate() { + LOG_ERROR(Lib_PlayGoDialog, "(DUMMY) called"); + return Error::OK; +} + +Status PS4_SYSV_ABI scePlayGoDialogUpdateStatus() { + LOG_ERROR(Lib_PlayGoDialog, "(DUMMY) called"); + return Status::FINISHED; +} + +void RegisterlibScePlayGoDialog(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("fbigNQiZpm0", "libScePlayGoDialog", 1, "libScePlayGoDialog", 1, 1, + scePlayGoDialogClose); + LIB_FUNCTION("wx9TDplJKB4", "libScePlayGoDialog", 1, "libScePlayGoDialog", 1, 1, + scePlayGoDialogGetResult); + LIB_FUNCTION("NOAMxY2EGS0", "libScePlayGoDialog", 1, "libScePlayGoDialog", 1, 1, + scePlayGoDialogGetStatus); + LIB_FUNCTION("fECamTJKpsM", "libScePlayGoDialog", 1, "libScePlayGoDialog", 1, 1, + scePlayGoDialogInitialize); + LIB_FUNCTION("kHd72ukqbxw", "libScePlayGoDialog", 1, "libScePlayGoDialog", 1, 1, + scePlayGoDialogOpen); + LIB_FUNCTION("okgIGdr5Iz0", "libScePlayGoDialog", 1, "libScePlayGoDialog", 1, 1, + scePlayGoDialogTerminate); + LIB_FUNCTION("Yb60K7BST48", "libScePlayGoDialog", 1, "libScePlayGoDialog", 1, 1, + scePlayGoDialogUpdateStatus); +}; + +} // namespace Libraries::PlayGo::Dialog diff --git a/src/core/libraries/playgo/playgo_dialog.h b/src/core/libraries/playgo/playgo_dialog.h new file mode 100644 index 000000000..fa9c64680 --- /dev/null +++ b/src/core/libraries/playgo/playgo_dialog.h @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" +#include "core/libraries/system/commondialog.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::PlayGo::Dialog { + +struct OrbisPlayGoDialogParam { + CommonDialog::BaseParam baseParam; + s32 size; + u8 unk[0x30]; +}; +static_assert(sizeof(OrbisPlayGoDialogParam) == 0x68); + +struct OrbisPlayGoDialogResult { + u8 unk1[0x4]; + CommonDialog::Result result; + u8 unk2[0x20]; +}; +static_assert(sizeof(OrbisPlayGoDialogResult) == 0x28); + +CommonDialog::Error PS4_SYSV_ABI scePlayGoDialogClose(); +CommonDialog::Error PS4_SYSV_ABI scePlayGoDialogGetResult(OrbisPlayGoDialogResult* result); +CommonDialog::Status PS4_SYSV_ABI scePlayGoDialogGetStatus(); +CommonDialog::Error PS4_SYSV_ABI scePlayGoDialogInitialize(); +CommonDialog::Error PS4_SYSV_ABI scePlayGoDialogOpen(const OrbisPlayGoDialogParam* param); +CommonDialog::Error PS4_SYSV_ABI scePlayGoDialogTerminate(); +CommonDialog::Status PS4_SYSV_ABI scePlayGoDialogUpdateStatus(); + +void RegisterlibScePlayGoDialog(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::PlayGo::Dialog From b00a321b5e671007af936572223db3da374cbbf5 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sun, 1 Dec 2024 04:12:04 -0800 Subject: [PATCH 088/549] playgo: Fix loading PlayGo file. (#1639) --- src/core/file_format/playgo_chunk.h | 1 - src/core/libraries/playgo/playgo.cpp | 55 ++++++++++++++++------------ src/emulator.cpp | 7 ---- 3 files changed, 31 insertions(+), 32 deletions(-) diff --git a/src/core/file_format/playgo_chunk.h b/src/core/file_format/playgo_chunk.h index 2a234f5fe..12d8f022e 100644 --- a/src/core/file_format/playgo_chunk.h +++ b/src/core/file_format/playgo_chunk.h @@ -97,7 +97,6 @@ struct PlaygoChunk { class PlaygoFile { public: - bool initialized = false; OrbisPlayGoHandle handle = 0; OrbisPlayGoChunkId id = 0; OrbisPlayGoLocus locus = OrbisPlayGoLocus::NotDownloaded; diff --git a/src/core/libraries/playgo/playgo.cpp b/src/core/libraries/playgo/playgo.cpp index 6fcf875da..13d6f991f 100644 --- a/src/core/libraries/playgo/playgo.cpp +++ b/src/core/libraries/playgo/playgo.cpp @@ -2,7 +2,9 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" +#include "common/singleton.h" #include "core/file_format/playgo_chunk.h" +#include "core/file_sys/fs.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "core/libraries/system/systemservice.h" @@ -29,10 +31,9 @@ s32 PS4_SYSV_ABI scePlayGoClose(OrbisPlayGoHandle handle) { if (handle != PlaygoHandle) { return ORBIS_PLAYGO_ERROR_BAD_HANDLE; } - if (!playgo->initialized) { + if (!playgo) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; } - playgo.reset(); return ORBIS_OK; } @@ -98,7 +99,7 @@ s32 PS4_SYSV_ABI scePlayGoGetInstallSpeed(OrbisPlayGoHandle handle, if (outSpeed == nullptr) { return ORBIS_PLAYGO_ERROR_BAD_POINTER; } - if (!playgo->initialized) { + if (!playgo) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; } @@ -126,7 +127,7 @@ s32 PS4_SYSV_ABI scePlayGoGetLanguageMask(OrbisPlayGoHandle handle, if (outLanguageMask == nullptr) { return ORBIS_PLAYGO_ERROR_BAD_POINTER; } - if (!playgo->initialized) { + if (!playgo) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; } @@ -148,7 +149,7 @@ s32 PS4_SYSV_ABI scePlayGoGetLocus(OrbisPlayGoHandle handle, const OrbisPlayGoCh if (numberOfEntries == 0) { return ORBIS_PLAYGO_ERROR_BAD_SIZE; } - if (!playgo->initialized) { + if (!playgo) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; } if (playgo->GetPlaygoHeader().file_size == 0) { @@ -180,7 +181,7 @@ s32 PS4_SYSV_ABI scePlayGoGetProgress(OrbisPlayGoHandle handle, const OrbisPlayG if (numberOfEntries == 0) { return ORBIS_PLAYGO_ERROR_BAD_SIZE; } - if (!playgo->initialized) { + if (!playgo) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; } if (playgo->GetPlaygoHeader().file_size == 0) { @@ -219,7 +220,7 @@ s32 PS4_SYSV_ABI scePlayGoGetToDoList(OrbisPlayGoHandle handle, OrbisPlayGoToDo* if (numberOfEntries == 0) { return ORBIS_PLAYGO_ERROR_BAD_SIZE; } - if (!playgo->initialized) { + if (!playgo) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; } *outEntries = 0; // nothing to do @@ -242,17 +243,24 @@ s32 PS4_SYSV_ABI scePlayGoInitialize(OrbisPlayGoInitParams* param) { if (param->bufSize < 0x200000) { return ORBIS_PLAYGO_ERROR_BAD_SIZE; } - - playgo = std::make_unique(); - if (!playgo->initialized) { - using namespace SystemService; - s32 system_lang = 0; - sceSystemServiceParamGetInt(OrbisSystemServiceParamId::Lang, &system_lang); - playgo->langMask = scePlayGoConvertLanguage(system_lang); - playgo->initialized = true; - } else { + if (playgo) { return ORBIS_PLAYGO_ERROR_ALREADY_INITIALIZED; } + + using namespace SystemService; + + playgo = std::make_unique(); + + auto* mnt = Common::Singleton::Instance(); + const auto file_path = mnt->GetHostPath("/app0/sce_sys/playgo-chunk.dat"); + if (!playgo->Open(file_path)) { + LOG_WARNING(Lib_PlayGo, "Could not open PlayGo file"); + } + + s32 system_lang = 0; + sceSystemServiceParamGetInt(OrbisSystemServiceParamId::Lang, &system_lang); + playgo->langMask = scePlayGoConvertLanguage(system_lang); + return ORBIS_OK; } @@ -265,7 +273,7 @@ s32 PS4_SYSV_ABI scePlayGoOpen(OrbisPlayGoHandle* outHandle, const void* param) if (param) { return ORBIS_PLAYGO_ERROR_INVALID_ARGUMENT; } - if (!playgo->initialized) { + if (!playgo) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; } if (playgo->GetPlaygoHeader().file_size == 0) { @@ -289,7 +297,7 @@ s32 PS4_SYSV_ABI scePlayGoPrefetch(OrbisPlayGoHandle handle, const OrbisPlayGoCh if (numberOfEntries == 0) { return ORBIS_PLAYGO_ERROR_BAD_SIZE; } - if (!playgo->initialized) { + if (!playgo) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; } @@ -310,7 +318,7 @@ s32 PS4_SYSV_ABI scePlayGoSetInstallSpeed(OrbisPlayGoHandle handle, OrbisPlayGoI if (handle != PlaygoHandle) { return ORBIS_PLAYGO_ERROR_BAD_HANDLE; } - if (!playgo->initialized) { + if (!playgo) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; } @@ -339,7 +347,7 @@ s32 PS4_SYSV_ABI scePlayGoSetLanguageMask(OrbisPlayGoHandle handle, if (handle != 1) { return ORBIS_PLAYGO_ERROR_BAD_HANDLE; } - if (!playgo->initialized) { + if (!playgo) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; } @@ -360,7 +368,7 @@ s32 PS4_SYSV_ABI scePlayGoSetToDoList(OrbisPlayGoHandle handle, const OrbisPlayG if (numberOfEntries == 0) { return ORBIS_PLAYGO_ERROR_BAD_SIZE; } - if (!playgo->initialized) { + if (!playgo) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; } return ORBIS_OK; @@ -369,11 +377,10 @@ s32 PS4_SYSV_ABI scePlayGoSetToDoList(OrbisPlayGoHandle handle, const OrbisPlayG s32 PS4_SYSV_ABI scePlayGoTerminate() { LOG_INFO(Lib_PlayGo, "called"); - if (playgo->initialized) { - playgo->initialized = false; - } else { + if (!playgo) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; } + playgo.reset(); return ORBIS_OK; } diff --git a/src/emulator.cpp b/src/emulator.cpp index 4ab535a2c..1d2542d2b 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -22,7 +22,6 @@ #include "common/scm_rev.h" #include "common/singleton.h" #include "common/version.h" -#include "core/file_format/playgo_chunk.h" #include "core/file_format/psf.h" #include "core/file_format/splash.h" #include "core/file_format/trp.h" @@ -157,12 +156,6 @@ void Emulator::Run(const std::filesystem::path& file) { fw_version = param_sfo->GetInteger("SYSTEM_VER").value_or(0x4700000); app_version = param_sfo->GetString("APP_VER").value_or("Unknown version"); LOG_INFO(Loader, "Fw: {:#x} App Version: {}", fw_version, app_version); - } else if (entry.path().filename() == "playgo-chunk.dat") { - auto* playgo = Common::Singleton::Instance(); - auto filepath = sce_sys_folder / "playgo-chunk.dat"; - if (!playgo->Open(filepath)) { - LOG_ERROR(Loader, "PlayGo: unable to open file"); - } } else if (entry.path().filename() == "pic1.png") { auto* splash = Common::Singleton::Instance(); if (splash->IsLoaded()) { From 4a5850628eed4be35ad3f91aeea4ee27ccfa6c4f Mon Sep 17 00:00:00 2001 From: psucien Date: Sun, 1 Dec 2024 15:46:08 +0100 Subject: [PATCH 089/549] hot-fix: debug build fixed --- src/core/libraries/libpng/pngdec.cpp | 8 ++++---- src/core/libraries/videoout/video_out.cpp | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/core/libraries/libpng/pngdec.cpp b/src/core/libraries/libpng/pngdec.cpp index d6f291a62..d9a324fc5 100644 --- a/src/core/libraries/libpng/pngdec.cpp +++ b/src/core/libraries/libpng/pngdec.cpp @@ -94,8 +94,8 @@ s32 PS4_SYSV_ABI scePngDecDecode(OrbisPngDecHandle handle, const OrbisPngDecDeco LOG_TRACE(Lib_Png, "pngMemSize = {} , imageMemSize = {} , pixelFormat = {} , alphaValue = {} , " "imagePitch = {}", - param->pngMemSize, param->imageMemSize, param->pixelFormat, param->alphaValue, - param->imagePitch); + param->png_mem_size, param->image_mem_size, int(param->pixel_format), + param->alpha_value, param->image_pitch); auto pngh = (PngHandler*)handle; @@ -240,8 +240,8 @@ s32 PS4_SYSV_ABI scePngDecParseHeader(const OrbisPngDecParseParam* param, LOG_TRACE( Lib_Png, "imageWidth = {} , imageHeight = {} , colorSpace = {} , bitDepth = {} , imageFlag = {}", - imageInfo->imageWidth, imageInfo->imageHeight, imageInfo->colorSpace, imageInfo->bitDepth, - imageInfo->imageFlag); + imageInfo->image_width, imageInfo->image_height, int(imageInfo->color_space), + imageInfo->bit_depth, int(imageInfo->image_flag)); return ORBIS_OK; } diff --git a/src/core/libraries/videoout/video_out.cpp b/src/core/libraries/videoout/video_out.cpp index ee4a89345..f36de6ade 100644 --- a/src/core/libraries/videoout/video_out.cpp +++ b/src/core/libraries/videoout/video_out.cpp @@ -195,8 +195,9 @@ s32 PS4_SYSV_ABI sceVideoOutGetFlipStatus(s32 handle, FlipStatus* status) { LOG_TRACE(Lib_VideoOut, "count = {}, processTime = {}, tsc = {}, submitTsc = {}, flipArg = {}, gcQueueNum = " "{}, flipPendingNum = {}, currentBuffer = {}", - status->count, status->processTime, status->tsc, status->submitTsc, status->flipArg, - status->gcQueueNum, status->flipPendingNum, status->currentBuffer); + status->count, status->process_time, status->tsc, status->submit_tsc, + status->flip_arg, status->gc_queue_num, status->flip_pending_num, + status->current_buffer); return ORBIS_OK; } From 0835dc71b3caf1f24352d19bd597bc56595675af Mon Sep 17 00:00:00 2001 From: Vinicius Rangel Date: Sun, 1 Dec 2024 15:34:29 -0300 Subject: [PATCH 090/549] More devtools stuff (#1637) * devtools: memory map viewer * devtools: batch highlight only for non-group viewer * devtools: fix not showing entire user data * devtools: shader debug viewer * devtools: add more reg naming --- CMakeLists.txt | 4 + src/common/config.cpp | 12 ++ src/common/config.h | 2 + src/core/debug_state.cpp | 11 +- src/core/debug_state.h | 48 ++++++- src/core/devtools/gcn/gcn_context_regs.cpp | 10 ++ src/core/devtools/layer.cpp | 45 +++++- src/core/devtools/options.cpp | 11 +- src/core/devtools/options.h | 3 +- src/core/devtools/widget/cmd_list.cpp | 4 +- src/core/devtools/widget/common.h | 57 ++++++++ src/core/devtools/widget/memory_map.cpp | 134 ++++++++++++++++++ src/core/devtools/widget/memory_map.h | 33 +++++ src/core/devtools/widget/reg_view.cpp | 49 +------ src/core/devtools/widget/shader_list.cpp | 95 +++++++++++++ src/core/devtools/widget/shader_list.h | 28 ++++ src/core/memory.h | 6 + .../renderer_vulkan/vk_pipeline_cache.cpp | 4 + 18 files changed, 491 insertions(+), 65 deletions(-) create mode 100644 src/core/devtools/widget/memory_map.cpp create mode 100644 src/core/devtools/widget/memory_map.h create mode 100644 src/core/devtools/widget/shader_list.cpp create mode 100644 src/core/devtools/widget/shader_list.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ed366a0b5..2f503832c 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -435,10 +435,14 @@ set(DEV_TOOLS src/core/devtools/layer.cpp src/core/devtools/widget/frame_graph.cpp src/core/devtools/widget/frame_graph.h src/core/devtools/widget/imgui_memory_editor.h + src/core/devtools/widget/memory_map.cpp + src/core/devtools/widget/memory_map.h src/core/devtools/widget/reg_popup.cpp src/core/devtools/widget/reg_popup.h src/core/devtools/widget/reg_view.cpp src/core/devtools/widget/reg_view.h + src/core/devtools/widget/shader_list.cpp + src/core/devtools/widget/shader_list.h src/core/devtools/widget/text_editor.cpp src/core/devtools/widget/text_editor.h ) diff --git a/src/common/config.cpp b/src/common/config.cpp index 5c2c8cda6..eae8897c8 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -47,6 +47,7 @@ static std::string backButtonBehavior = "left"; static bool useSpecialPad = false; static int specialPadClass = 1; static bool isDebugDump = false; +static bool isShaderDebug = false; static bool isShowSplash = false; static bool isAutoUpdate = false; static bool isNullGpu = false; @@ -159,6 +160,10 @@ bool debugDump() { return isDebugDump; } +bool collectShadersForDebug() { + return isShaderDebug; +} + bool showSplash() { return isShowSplash; } @@ -235,6 +240,10 @@ void setDebugDump(bool enable) { isDebugDump = enable; } +void setCollectShaderForDebug(bool enable) { + isShaderDebug = enable; +} + void setShowSplash(bool enable) { isShowSplash = enable; } @@ -571,6 +580,7 @@ void load(const std::filesystem::path& path) { const toml::value& debug = data.at("Debug"); isDebugDump = toml::find_or(debug, "DebugDump", false); + isShaderDebug = toml::find_or(debug, "CollectShader", false); } if (data.contains("GUI")) { @@ -662,6 +672,7 @@ void save(const std::filesystem::path& path) { data["Vulkan"]["rdocMarkersEnable"] = vkMarkers; data["Vulkan"]["crashDiagnostic"] = vkCrashDiagnostic; data["Debug"]["DebugDump"] = isDebugDump; + data["Debug"]["CollectShader"] = isShaderDebug; data["GUI"]["theme"] = mw_themes; data["GUI"]["iconSize"] = m_icon_size; data["GUI"]["sliderPos"] = m_slider_pos; @@ -717,6 +728,7 @@ void setDefaultValues() { useSpecialPad = false; specialPadClass = 1; isDebugDump = false; + isShaderDebug = false; isShowSplash = false; isAutoUpdate = false; isNullGpu = false; diff --git a/src/common/config.h b/src/common/config.h index 400115745..d98c94480 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -37,6 +37,7 @@ u32 getScreenHeight(); s32 getGpuId(); bool debugDump(); +bool collectShadersForDebug(); bool showSplash(); bool autoUpdate(); bool nullGpu(); @@ -47,6 +48,7 @@ bool isRdocEnabled(); u32 vblankDiv(); void setDebugDump(bool enable); +void setCollectShaderForDebug(bool enable); void setShowSplash(bool enable); void setAutoUpdate(bool enable); void setNullGpu(bool enable); diff --git a/src/core/debug_state.cpp b/src/core/debug_state.cpp index 1dc4297c3..562cb62e8 100644 --- a/src/core/debug_state.cpp +++ b/src/core/debug_state.cpp @@ -157,7 +157,7 @@ void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr, if (is_compute) { dump.is_compute = true; const auto& cs = dump.regs.cs_program; - dump.cs_data = ComputerShaderDump{ + dump.cs_data = PipelineComputerProgramDump{ .cs_program = cs, .code = std::vector{cs.Code().begin(), cs.Code().end()}, }; @@ -167,7 +167,7 @@ void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr, auto stage = regs.ProgramForStage(i); if (stage->address_lo != 0) { auto code = stage->Code(); - dump.stages[i] = ShaderDump{ + dump.stages[i] = PipelineShaderProgramDump{ .user_data = *stage, .code = std::vector{code.begin(), code.end()}, }; @@ -176,3 +176,10 @@ void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr, } } } + +void DebugStateImpl::CollectShader(const std::string& name, std::span spv, + std::span raw_code) { + shader_dump_list.emplace_back(name, std::vector{spv.begin(), spv.end()}, + std::vector{raw_code.begin(), raw_code.end()}); + std::ranges::sort(shader_dump_list, {}, &ShaderDump::name); +} diff --git a/src/core/debug_state.h b/src/core/debug_state.h index cd1c6aa93..759755b52 100644 --- a/src/core/debug_state.h +++ b/src/core/debug_state.h @@ -30,7 +30,8 @@ namespace Core::Devtools { class Layer; namespace Widget { class FrameGraph; -} +class ShaderList; +} // namespace Widget } // namespace Core::Devtools namespace DebugStateType { @@ -49,12 +50,12 @@ struct QueueDump { uintptr_t base_addr; }; -struct ShaderDump { +struct PipelineShaderProgramDump { Vulkan::Liverpool::ShaderProgram user_data{}; std::vector code{}; }; -struct ComputerShaderDump { +struct PipelineComputerProgramDump { Vulkan::Liverpool::ComputeProgram cs_program{}; std::vector code{}; }; @@ -63,8 +64,8 @@ struct RegDump { bool is_compute{false}; static constexpr size_t MaxShaderStages = 5; Vulkan::Liverpool::Regs regs{}; - std::array stages{}; - ComputerShaderDump cs_data{}; + std::array stages{}; + PipelineComputerProgramDump cs_data{}; }; struct FrameDump { @@ -73,9 +74,41 @@ struct FrameDump { std::unordered_map regs; // address -> reg dump }; +struct ShaderDump { + std::string name; + std::vector spv; + std::vector raw_code; + + std::string cache_spv_disasm{}; + std::string cache_raw_disasm{}; + + ShaderDump(std::string name, std::vector spv, std::vector raw_code) + : name(std::move(name)), spv(std::move(spv)), raw_code(std::move(raw_code)) {} + + ShaderDump(const ShaderDump& other) = delete; + ShaderDump(ShaderDump&& other) noexcept + : name{std::move(other.name)}, spv{std::move(other.spv)}, + raw_code{std::move(other.raw_code)}, cache_spv_disasm{std::move(other.cache_spv_disasm)}, + cache_raw_disasm{std::move(other.cache_raw_disasm)} {} + ShaderDump& operator=(const ShaderDump& other) = delete; + ShaderDump& operator=(ShaderDump&& other) noexcept { + if (this == &other) + return *this; + name = std::move(other.name); + spv = std::move(other.spv); + raw_code = std::move(other.raw_code); + cache_spv_disasm = std::move(other.cache_spv_disasm); + cache_raw_disasm = std::move(other.cache_raw_disasm); + return *this; + } +}; + class DebugStateImpl { friend class Core::Devtools::Layer; friend class Core::Devtools::Widget::FrameGraph; + friend class Core::Devtools::Widget::ShaderList; + + std::queue debug_message_popup; std::mutex guest_threads_mutex{}; std::vector guest_threads{}; @@ -94,7 +127,7 @@ class DebugStateImpl { std::shared_mutex frame_dump_list_mutex; std::vector frame_dump_list{}; - std::queue debug_message_popup; + std::vector shader_dump_list{}; public: void ShowDebugMessage(std::string message) { @@ -152,6 +185,9 @@ public: void PushRegsDump(uintptr_t base_addr, uintptr_t header_addr, const AmdGpu::Liverpool::Regs& regs, bool is_compute = false); + + void CollectShader(const std::string& name, std::span spv, + std::span raw_code); }; } // namespace DebugStateType diff --git a/src/core/devtools/gcn/gcn_context_regs.cpp b/src/core/devtools/gcn/gcn_context_regs.cpp index 843ba9e65..5a591111e 100644 --- a/src/core/devtools/gcn/gcn_context_regs.cpp +++ b/src/core/devtools/gcn/gcn_context_regs.cpp @@ -289,6 +289,16 @@ const char* GetContextRegName(u32 reg_offset) { return "mmSPI_PS_INPUT_CNTL_2"; case mmSPI_PS_INPUT_CNTL_3: return "mmSPI_PS_INPUT_CNTL_3"; + case mmPA_SU_POLY_OFFSET_FRONT_SCALE: + return "mmPA_SU_POLY_OFFSET_FRONT_SCALE"; + case mmPA_SU_POLY_OFFSET_FRONT_OFFSET: + return "mmPA_SU_POLY_OFFSET_FRONT_OFFSET"; + case mmPA_SU_POLY_OFFSET_BACK_SCALE: + return "mmPA_SU_POLY_OFFSET_BACK_SCALE"; + case mmPA_SU_POLY_OFFSET_BACK_OFFSET: + return "mmPA_SU_POLY_OFFSET_BACK_OFFSET"; + case mmPA_SU_POLY_OFFSET_CLAMP: + return "mmPA_SU_POLY_OFFSET_CLAMP"; default: break; } diff --git a/src/core/devtools/layer.cpp b/src/core/devtools/layer.cpp index 2c4ce20b6..2c2099f4d 100644 --- a/src/core/devtools/layer.cpp +++ b/src/core/devtools/layer.cpp @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "layer.h" + #include #include "common/config.h" @@ -9,11 +11,12 @@ #include "core/debug_state.h" #include "imgui/imgui_std.h" #include "imgui_internal.h" -#include "layer.h" #include "options.h" #include "video_core/renderer_vulkan/vk_presenter.h" #include "widget/frame_dump.h" #include "widget/frame_graph.h" +#include "widget/memory_map.h" +#include "widget/shader_list.h" extern std::unique_ptr presenter; @@ -35,6 +38,9 @@ static float debug_popup_timing = 3.0f; static bool just_opened_options = false; +static Widget::MemoryMapViewer memory_map; +static Widget::ShaderList shader_list; + // clang-format off static std::string help_text = #include "help.txt" @@ -63,6 +69,7 @@ void L::DrawMenuBar() { } if (BeginMenu("GPU Tools")) { MenuItem("Show frame info", nullptr, &frame_graph.is_open); + MenuItem("Show loaded shaders", nullptr, &shader_list.open); if (BeginMenu("Dump frames")) { SliderInt("Count", &dump_frame_count, 1, 5); if (MenuItem("Dump", "Ctrl+Alt+F9", nullptr, !DebugState.DumpingCurrentFrame())) { @@ -81,6 +88,12 @@ void L::DrawMenuBar() { } ImGui::EndMenu(); } + if (BeginMenu("Debug")) { + if (MenuItem("Memory map")) { + memory_map.open = true; + } + ImGui::EndMenu(); + } EndMainMenuBar(); } @@ -175,19 +188,29 @@ void L::DrawAdvanced() { bool close_popup_options = true; if (BeginPopupModal("GPU Tools Options", &close_popup_options, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings)) { - static char disassembly_cli[512]; + static char disassembler_cli_isa[512]; + static char disassembler_cli_spv[512]; static bool frame_dump_render_on_collapse; if (just_opened_options) { just_opened_options = false; - auto s = Options.disassembly_cli.copy(disassembly_cli, sizeof(disassembly_cli) - 1); - disassembly_cli[s] = '\0'; + auto s = Options.disassembler_cli_isa.copy(disassembler_cli_isa, + sizeof(disassembler_cli_isa) - 1); + disassembler_cli_isa[s] = '\0'; + s = Options.disassembler_cli_spv.copy(disassembler_cli_spv, + sizeof(disassembler_cli_spv) - 1); + disassembler_cli_spv[s] = '\0'; frame_dump_render_on_collapse = Options.frame_dump_render_on_collapse; } - InputText("Shader disassembler: ", disassembly_cli, sizeof(disassembly_cli)); + InputText("Shader isa disassembler: ", disassembler_cli_isa, sizeof(disassembler_cli_isa)); if (IsItemHovered()) { - SetTooltip(R"(Command to disassemble shaders. Example "dis.exe" --raw "{src}")"); + SetTooltip(R"(Command to disassemble shaders. Example: dis.exe --raw "{src}")"); + } + InputText("Shader SPIRV disassembler: ", disassembler_cli_spv, + sizeof(disassembler_cli_spv)); + if (IsItemHovered()) { + SetTooltip(R"(Command to disassemble shaders. Example: spirv-cross -V "{src}")"); } Checkbox("Show frame dump popups even when collapsed", &frame_dump_render_on_collapse); if (IsItemHovered()) { @@ -196,7 +219,8 @@ void L::DrawAdvanced() { } if (Button("Save")) { - Options.disassembly_cli = disassembly_cli; + Options.disassembler_cli_isa = disassembler_cli_isa; + Options.disassembler_cli_spv = disassembler_cli_spv; Options.frame_dump_render_on_collapse = frame_dump_render_on_collapse; SaveIniSettingsToDisk(io.IniFilename); CloseCurrentPopup(); @@ -219,6 +243,13 @@ void L::DrawAdvanced() { EndPopup(); } + + if (memory_map.open) { + memory_map.Draw(); + } + if (shader_list.open) { + shader_list.Draw(); + } } void L::DrawSimple() { diff --git a/src/core/devtools/options.cpp b/src/core/devtools/options.cpp index 1b49da76b..2def42071 100644 --- a/src/core/devtools/options.cpp +++ b/src/core/devtools/options.cpp @@ -12,8 +12,12 @@ TOptions Options; void LoadOptionsConfig(const char* line) { char str[512]; int i; - if (sscanf(line, "disassembly_cli=%511[^\n]", str) == 1) { - Options.disassembly_cli = str; + if (sscanf(line, "disassembler_cli_isa=%511[^\n]", str) == 1) { + Options.disassembler_cli_isa = str; + return; + } + if (sscanf(line, "disassembler_cli_spv=%511[^\n]", str) == 1) { + Options.disassembler_cli_spv = str; return; } if (sscanf(line, "frame_dump_render_on_collapse=%d", &i) == 1) { @@ -23,7 +27,8 @@ void LoadOptionsConfig(const char* line) { } void SerializeOptionsConfig(ImGuiTextBuffer* buf) { - buf->appendf("disassembly_cli=%s\n", Options.disassembly_cli.c_str()); + buf->appendf("disassembler_cli_isa=%s\n", Options.disassembler_cli_isa.c_str()); + buf->appendf("disassembler_cli_spv=%s\n", Options.disassembler_cli_spv.c_str()); buf->appendf("frame_dump_render_on_collapse=%d\n", Options.frame_dump_render_on_collapse); } diff --git a/src/core/devtools/options.h b/src/core/devtools/options.h index c3a8aaf31..70e1d137b 100644 --- a/src/core/devtools/options.h +++ b/src/core/devtools/options.h @@ -10,7 +10,8 @@ struct ImGuiTextBuffer; namespace Core::Devtools { struct TOptions { - std::string disassembly_cli{}; + std::string disassembler_cli_isa{"clrxdisasm --raw \"{src}\""}; + std::string disassembler_cli_spv{"spirv-cross -V \"{src}\""}; bool frame_dump_render_on_collapse{false}; }; diff --git a/src/core/devtools/widget/cmd_list.cpp b/src/core/devtools/widget/cmd_list.cpp index 219d25d6a..7c550cf2e 100644 --- a/src/core/devtools/widget/cmd_list.cpp +++ b/src/core/devtools/widget/cmd_list.cpp @@ -1306,7 +1306,7 @@ void CmdListViewer::Draw(bool only_batches_view) { if (batch.id == batch_bp) { // highlight batch at breakpoint PushStyleColor(ImGuiCol_Header, ImVec4{1.0f, 0.5f, 0.5f, 0.5f}); } - if (batch.id == highlight_batch) { + if (batch.id == highlight_batch && !group_batches) { PushStyleColor(ImGuiCol_Text, ImVec4{1.0f, 0.7f, 0.7f, 1.0f}); } @@ -1459,7 +1459,7 @@ void CmdListViewer::Draw(bool only_batches_view) { } } - if (batch.id == highlight_batch) { + if (batch.id == highlight_batch && !group_batches) { PopStyleColor(); } diff --git a/src/core/devtools/widget/common.h b/src/core/devtools/widget/common.h index e650f5fc7..4429f5581 100644 --- a/src/core/devtools/widget/common.h +++ b/src/core/devtools/widget/common.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include #include @@ -10,9 +11,16 @@ #include #include "common/bit_field.h" +#include "common/io_file.h" #include "common/types.h" +#include "core/debug_state.h" #include "video_core/amdgpu/pm4_opcodes.h" +#if defined(_WIN32) +#define popen _popen +#define pclose _pclose +#endif + namespace Core::Devtools::Widget { /* * Generic PM4 header @@ -106,4 +114,53 @@ static bool IsDrawCall(AmdGpu::PM4ItOpcode opcode) { } } +inline std::optional exec_cli(const char* cli) { + std::array buffer{}; + std::string output; + const auto f = popen(cli, "r"); + if (!f) { + pclose(f); + return {}; + } + while (fgets(buffer.data(), buffer.size(), f)) { + output += buffer.data(); + } + pclose(f); + return output; +} + +inline std::string RunDisassembler(const std::string& disassembler_cli, + const std::vector& shader_code) { + std::string shader_dis; + + if (disassembler_cli.empty()) { + shader_dis = "No disassembler set"; + } else { + auto bin_path = std::filesystem::temp_directory_path() / "shadps4_tmp_shader.bin"; + + constexpr std::string_view src_arg = "{src}"; + std::string cli = disassembler_cli; + const auto pos = cli.find(src_arg); + if (pos == std::string::npos) { + DebugState.ShowDebugMessage("Disassembler CLI does not contain {src} argument\n" + + disassembler_cli); + } else { + cli.replace(pos, src_arg.size(), "\"" + bin_path.string() + "\""); + Common::FS::IOFile file(bin_path, Common::FS::FileAccessMode::Write); + file.Write(shader_code); + file.Close(); + + auto result = exec_cli(cli.c_str()); + shader_dis = result.value_or("Could not disassemble shader"); + if (shader_dis.empty()) { + shader_dis = "Disassembly empty or failed"; + } + + std::filesystem::remove(bin_path); + } + } + + return shader_dis; +} + } // namespace Core::Devtools::Widget \ No newline at end of file diff --git a/src/core/devtools/widget/memory_map.cpp b/src/core/devtools/widget/memory_map.cpp new file mode 100644 index 000000000..afafd2853 --- /dev/null +++ b/src/core/devtools/widget/memory_map.cpp @@ -0,0 +1,134 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include + +#include "core/debug_state.h" +#include "core/memory.h" +#include "memory_map.h" + +using namespace ImGui; + +namespace Core::Devtools::Widget { + +bool MemoryMapViewer::Iterator::DrawLine() { + if (is_vma) { + if (vma.it == vma.end) { + return false; + } + auto m = vma.it->second; + if (m.type == VMAType::Free) { + ++vma.it; + return DrawLine(); + } + TableNextColumn(); + Text("%zX", m.base); + TableNextColumn(); + Text("%zX", m.size); + TableNextColumn(); + Text("%s", magic_enum::enum_name(m.type).data()); + TableNextColumn(); + Text("%s", magic_enum::enum_name(m.prot).data()); + TableNextColumn(); + if (m.is_exec) { + Text("X"); + } + TableNextColumn(); + Text("%s", m.name.c_str()); + ++vma.it; + return true; + } + if (dmem.it == dmem.end) { + return false; + } + auto m = dmem.it->second; + if (m.is_free) { + ++dmem.it; + return DrawLine(); + } + TableNextColumn(); + Text("%llX", m.base); + TableNextColumn(); + Text("%llX", m.size); + TableNextColumn(); + auto type = static_cast<::Libraries::Kernel::MemoryTypes>(m.memory_type); + Text("%s", magic_enum::enum_name(type).data()); + TableNextColumn(); + Text("%d", m.is_pooled); + ++dmem.it; + return true; +} + +void MemoryMapViewer::Draw() { + SetNextWindowSize({600.0f, 500.0f}, ImGuiCond_FirstUseEver); + if (!Begin("Memory map", &open)) { + End(); + return; + } + + auto mem = Memory::Instance(); + std::scoped_lock lck{mem->mutex}; + + { + bool next_showing_vma = showing_vma; + if (showing_vma) { + PushStyleColor(ImGuiCol_Button, ImVec4{1.0f, 0.7f, 0.7f, 1.0f}); + } + if (Button("VMem")) { + next_showing_vma = true; + } + if (showing_vma) { + PopStyleColor(); + } + SameLine(); + if (!showing_vma) { + PushStyleColor(ImGuiCol_Button, ImVec4{1.0f, 0.7f, 0.7f, 1.0f}); + } + if (Button("DMem")) { + next_showing_vma = false; + } + if (!showing_vma) { + PopStyleColor(); + } + showing_vma = next_showing_vma; + } + + Iterator it{}; + if (showing_vma) { + it.is_vma = true; + it.vma.it = mem->vma_map.begin(); + it.vma.end = mem->vma_map.end(); + } else { + it.is_vma = false; + it.dmem.it = mem->dmem_map.begin(); + it.dmem.end = mem->dmem_map.end(); + } + + if (BeginTable("memory_view_table", showing_vma ? 6 : 4, + ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | + ImGuiTableFlags_SizingFixedFit)) { + if (showing_vma) { + TableSetupColumn("Address"); + TableSetupColumn("Size"); + TableSetupColumn("Type"); + TableSetupColumn("Prot"); + TableSetupColumn("Is Exec"); + TableSetupColumn("Name"); + } else { + TableSetupColumn("Address"); + TableSetupColumn("Size"); + TableSetupColumn("Type"); + TableSetupColumn("Pooled"); + } + TableHeadersRow(); + + while (it.DrawLine()) + ; + EndTable(); + } + + End(); +} + +} // namespace Core::Devtools::Widget \ No newline at end of file diff --git a/src/core/devtools/widget/memory_map.h b/src/core/devtools/widget/memory_map.h new file mode 100644 index 000000000..cc7697c8c --- /dev/null +++ b/src/core/devtools/widget/memory_map.h @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/memory.h" + +namespace Core::Devtools::Widget { + +class MemoryMapViewer { + struct Iterator { + bool is_vma; + struct { + MemoryManager::DMemMap::iterator it; + MemoryManager::DMemMap::iterator end; + } dmem; + struct { + MemoryManager::VMAMap::iterator it; + MemoryManager::VMAMap::iterator end; + } vma; + + bool DrawLine(); + }; + + bool showing_vma = true; + +public: + bool open = false; + + void Draw(); +}; + +} // namespace Core::Devtools::Widget diff --git a/src/core/devtools/widget/reg_view.cpp b/src/core/devtools/widget/reg_view.cpp index 10cc88085..a60090a8c 100644 --- a/src/core/devtools/widget/reg_view.cpp +++ b/src/core/devtools/widget/reg_view.cpp @@ -25,21 +25,6 @@ using magic_enum::enum_name; constexpr auto depth_id = 0xF3; -static std::optional exec_cli(const char* cli) { - std::array buffer{}; - std::string output; - const auto f = popen(cli, "r"); - if (!f) { - pclose(f); - return {}; - } - while (fgets(buffer.data(), buffer.size(), f)) { - output += buffer.data(); - } - pclose(f); - return output; -} - namespace Core::Devtools::Widget { void RegView::ProcessShader(int shader_id) { @@ -54,38 +39,12 @@ void RegView::ProcessShader(int shader_id) { user_data = s.user_data.user_data; } - std::string shader_dis; - - if (Options.disassembly_cli.empty()) { - shader_dis = "No disassembler set"; - } else { - auto bin_path = std::filesystem::temp_directory_path() / "shadps4_tmp_shader.bin"; - - constexpr std::string_view src_arg = "{src}"; - std::string cli = Options.disassembly_cli; - const auto pos = cli.find(src_arg); - if (pos == std::string::npos) { - DebugState.ShowDebugMessage("Disassembler CLI does not contain {src} argument"); - } else { - cli.replace(pos, src_arg.size(), "\"" + bin_path.string() + "\""); - Common::FS::IOFile file(bin_path, Common::FS::FileAccessMode::Write); - file.Write(shader_code); - file.Close(); - - auto result = exec_cli(cli.c_str()); - shader_dis = result.value_or("Could not disassemble shader"); - if (shader_dis.empty()) { - shader_dis = "Disassembly empty or failed"; - } - - std::filesystem::remove(bin_path); - } - } + std::string shader_dis = RunDisassembler(Options.disassembler_cli_isa, shader_code); MemoryEditor hex_view; hex_view.Open = true; hex_view.ReadOnly = true; - hex_view.Cols = 8; + hex_view.Cols = 16; hex_view.OptShowAscii = false; hex_view.OptShowOptions = false; @@ -376,7 +335,9 @@ void RegView::Draw() { if (!shader) { Text("Stage not selected"); } else { - shader->hex_view.DrawContents(shader->user_data.data(), shader->user_data.size()); + shader->hex_view.DrawContents(shader->user_data.data(), + shader->user_data.size() * + sizeof(Vulkan::Liverpool::UserData::value_type)); } } End(); diff --git a/src/core/devtools/widget/shader_list.cpp b/src/core/devtools/widget/shader_list.cpp new file mode 100644 index 000000000..b056880dd --- /dev/null +++ b/src/core/devtools/widget/shader_list.cpp @@ -0,0 +1,95 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "shader_list.h" + +#include + +#include "common.h" +#include "common/config.h" +#include "core/debug_state.h" +#include "core/devtools/options.h" +#include "imgui/imgui_std.h" + +using namespace ImGui; + +namespace Core::Devtools::Widget { + +void ShaderList::DrawShader(DebugStateType::ShaderDump& value) { + if (!loaded_data) { + loaded_data = true; + if (value.cache_raw_disasm.empty()) { + value.cache_raw_disasm = RunDisassembler(Options.disassembler_cli_isa, value.raw_code); + } + isa_editor.SetText(value.cache_raw_disasm); + + if (value.cache_spv_disasm.empty()) { + value.cache_spv_disasm = RunDisassembler(Options.disassembler_cli_spv, value.spv); + } + spv_editor.SetText(value.cache_spv_disasm); + } + + if (SmallButton("<-")) { + selected_shader = -1; + } + SameLine(); + Text("%s", value.name.c_str()); + SameLine(0.0f, 7.0f); + if (BeginCombo("Shader type", showing_isa ? "ISA" : "SPIRV", ImGuiComboFlags_WidthFitPreview)) { + if (Selectable("SPIRV")) { + showing_isa = false; + } + if (Selectable("ISA")) { + showing_isa = true; + } + EndCombo(); + } + + if (showing_isa) { + isa_editor.Render("ISA", GetContentRegionAvail()); + } else { + spv_editor.Render("SPIRV", GetContentRegionAvail()); + } +} + +ShaderList::ShaderList() { + isa_editor.SetPalette(TextEditor::GetDarkPalette()); + isa_editor.SetReadOnly(true); + spv_editor.SetPalette(TextEditor::GetDarkPalette()); + spv_editor.SetReadOnly(true); + spv_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::GLSL()); +} + +void ShaderList::Draw() { + SetNextWindowSize({500.0f, 600.0f}, ImGuiCond_FirstUseEver); + if (!Begin("Shader list", &open)) { + End(); + return; + } + + if (!Config::collectShadersForDebug()) { + DrawCenteredText("Enable 'CollectShader' in config to see shaders"); + End(); + return; + } + + if (selected_shader >= 0) { + DrawShader(DebugState.shader_dump_list[selected_shader]); + End(); + return; + } + + auto width = GetContentRegionAvail().x; + int i = 0; + for (const auto& shader : DebugState.shader_dump_list) { + if (ButtonEx(shader.name.c_str(), {width, 20.0f}, ImGuiButtonFlags_NoHoveredOnFocus)) { + selected_shader = i; + loaded_data = false; + } + i++; + } + + End(); +} + +} // namespace Core::Devtools::Widget \ No newline at end of file diff --git a/src/core/devtools/widget/shader_list.h b/src/core/devtools/widget/shader_list.h new file mode 100644 index 000000000..5a47f656d --- /dev/null +++ b/src/core/devtools/widget/shader_list.h @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/debug_state.h" +#include "text_editor.h" + +namespace Core::Devtools::Widget { + +class ShaderList { + int selected_shader = -1; + TextEditor isa_editor{}; + TextEditor spv_editor{}; + bool loaded_data = false; + bool showing_isa = false; + + void DrawShader(DebugStateType::ShaderDump& value); + +public: + ShaderList(); + + bool open = false; + + void Draw(); +}; + +} // namespace Core::Devtools::Widget \ No newline at end of file diff --git a/src/core/memory.h b/src/core/memory.h index a9a42e1c2..2efa02763 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -20,6 +20,10 @@ namespace Libraries::Kernel { struct OrbisQueryInfo; } +namespace Core::Devtools::Widget { +class MemoryMapViewer; +} + namespace Core { enum class MemoryProt : u32 { @@ -257,6 +261,8 @@ private: size_t total_flexible_size{}; size_t flexible_usage{}; Vulkan::Rasterizer* rasterizer{}; + + friend class ::Core::Devtools::Widget::MemoryMapViewer; }; using Memory = Common::Singleton; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index a4c213ca8..612e950bb 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -7,6 +7,7 @@ #include "common/hash.h" #include "common/io_file.h" #include "common/path_util.h" +#include "core/debug_state.h" #include "shader_recompiler/backend/spirv/emit_spirv.h" #include "shader_recompiler/info.h" #include "shader_recompiler/recompiler.h" @@ -416,6 +417,9 @@ vk::ShaderModule PipelineCache::CompileModule(Shader::Info& info, const auto module = CompileSPV(spv, instance.GetDevice()); const auto name = fmt::format("{}_{:#x}_{}", info.stage, info.pgm_hash, perm_idx); Vulkan::SetObjectName(instance.GetDevice(), module, name); + if (Config::collectShadersForDebug()) { + DebugState.CollectShader(name, spv, code); + } return module; } From 3dc0f6d831093b931ad5fac38ba714ed413db7f1 Mon Sep 17 00:00:00 2001 From: Alexandre Bouvier Date: Mon, 2 Dec 2024 03:04:44 +0000 Subject: [PATCH 091/549] cmake: fix build (#1645) --- externals/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 50eda0288..bc2d41bda 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -69,7 +69,7 @@ if (NOT TARGET ZLIB::ZLIB) FetchContent_MakeAvailable(ZLIB) add_library(ZLIB::ZLIB ALIAS zlib) # libpng expects this variable to exist after its find_package(ZLIB) - get_target_property(ZLIB_INCLUDE_DIRS zlib INTERFACE_INCLUDE_DIRECTORIES) + set(ZLIB_INCLUDE_DIRS "${FETCHCONTENT_BASE_DIR}/zlib-build") endif() # SDL3 From fda4f06518de074c8e8d1fbc99caec890b55875f Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Mon, 2 Dec 2024 11:46:51 -0800 Subject: [PATCH 092/549] devtools: More warning fixes (#1652) --- src/core/devtools/widget/memory_map.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/core/devtools/widget/memory_map.cpp b/src/core/devtools/widget/memory_map.cpp index afafd2853..dc8f5c2e9 100644 --- a/src/core/devtools/widget/memory_map.cpp +++ b/src/core/devtools/widget/memory_map.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include #include @@ -23,7 +24,7 @@ bool MemoryMapViewer::Iterator::DrawLine() { return DrawLine(); } TableNextColumn(); - Text("%zX", m.base); + Text("%" PRIXPTR, m.base); TableNextColumn(); Text("%zX", m.size); TableNextColumn(); @@ -48,9 +49,9 @@ bool MemoryMapViewer::Iterator::DrawLine() { return DrawLine(); } TableNextColumn(); - Text("%llX", m.base); + Text("%" PRIXPTR, m.base); TableNextColumn(); - Text("%llX", m.size); + Text("%zX", m.size); TableNextColumn(); auto type = static_cast<::Libraries::Kernel::MemoryTypes>(m.memory_type); Text("%s", magic_enum::enum_name(type).data()); From eb844b9b63bfddc70ef40665d5bffacd01f31a10 Mon Sep 17 00:00:00 2001 From: TheTurtle Date: Mon, 2 Dec 2024 23:20:54 +0200 Subject: [PATCH 093/549] shader_recompiler: Implement manual barycentric interpolation path (#1644) * shader_recompiler: Implement manual barycentric interpolation path * clang format * emit_spirv: Fix typo * emit_spirv: Simplify variable definition * spirv_emit: clang format --- .../backend/spirv/emit_spirv.cpp | 8 +- .../spirv/emit_spirv_context_get_set.cpp | 85 ++++++++++--------- .../backend/spirv/emit_spirv_special.cpp | 3 + .../backend/spirv/spirv_emit_context.cpp | 69 ++++++++++++--- .../backend/spirv/spirv_emit_context.h | 7 +- src/shader_recompiler/profile.h | 1 + .../renderer_vulkan/vk_instance.cpp | 7 ++ src/video_core/renderer_vulkan/vk_instance.h | 5 ++ .../renderer_vulkan/vk_pipeline_cache.cpp | 2 + 9 files changed, 129 insertions(+), 58 deletions(-) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index e84908a57..1e7032f10 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -206,7 +206,7 @@ Id DefineMain(EmitContext& ctx, const IR::Program& program) { return main; } -void SetupCapabilities(const Info& info, EmitContext& ctx) { +void SetupCapabilities(const Info& info, const Profile& profile, EmitContext& ctx) { ctx.AddCapability(spv::Capability::Image1D); ctx.AddCapability(spv::Capability::Sampled1D); ctx.AddCapability(spv::Capability::ImageQuery); @@ -251,6 +251,10 @@ void SetupCapabilities(const Info& info, EmitContext& ctx) { if (info.stage == Stage::Geometry) { ctx.AddCapability(spv::Capability::Geometry); } + if (info.stage == Stage::Fragment && profile.needs_manual_interpolation) { + ctx.AddExtension("SPV_KHR_fragment_shader_barycentric"); + ctx.AddCapability(spv::Capability::FragmentBarycentricKHR); + } } void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) { @@ -342,7 +346,7 @@ std::vector EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_in EmitContext ctx{profile, runtime_info, program.info, binding}; const Id main{DefineMain(ctx, program)}; DefineEntryPoint(program, ctx, main); - SetupCapabilities(program.info, ctx); + SetupCapabilities(program.info, profile, ctx); SetupFloatMode(ctx, profile, runtime_info, main); PatchPhiNodes(program, ctx); binding.user_data += program.info.ud_mask.NumRegs(); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp index 064200d99..d8c0a17bd 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp @@ -171,54 +171,38 @@ Id EmitReadStepRate(EmitContext& ctx, int rate_idx) { rate_idx == 0 ? ctx.u32_zero_value : ctx.u32_one_value)); } +Id EmitGetAttributeForGeometry(EmitContext& ctx, IR::Attribute attr, u32 comp, u32 index) { + if (IR::IsPosition(attr)) { + ASSERT(attr == IR::Attribute::Position0); + const auto position_arr_ptr = ctx.TypePointer(spv::StorageClass::Input, ctx.F32[4]); + const auto pointer{ + ctx.OpAccessChain(position_arr_ptr, ctx.gl_in, ctx.ConstU32(index), ctx.ConstU32(0u))}; + const auto position_comp_ptr = ctx.TypePointer(spv::StorageClass::Input, ctx.F32[1]); + return ctx.OpLoad(ctx.F32[1], + ctx.OpAccessChain(position_comp_ptr, pointer, ctx.ConstU32(comp))); + } + + if (IR::IsParam(attr)) { + const u32 param_id{u32(attr) - u32(IR::Attribute::Param0)}; + const auto param = ctx.input_params.at(param_id).id; + const auto param_arr_ptr = ctx.TypePointer(spv::StorageClass::Input, ctx.F32[4]); + const auto pointer{ctx.OpAccessChain(param_arr_ptr, param, ctx.ConstU32(index))}; + const auto position_comp_ptr = ctx.TypePointer(spv::StorageClass::Input, ctx.F32[1]); + return ctx.OpLoad(ctx.F32[1], + ctx.OpAccessChain(position_comp_ptr, pointer, ctx.ConstU32(comp))); + } + UNREACHABLE(); +} + Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, u32 comp, u32 index) { if (ctx.info.stage == Stage::Geometry) { - if (IR::IsPosition(attr)) { - ASSERT(attr == IR::Attribute::Position0); - const auto position_arr_ptr = ctx.TypePointer(spv::StorageClass::Input, ctx.F32[4]); - const auto pointer{ctx.OpAccessChain(position_arr_ptr, ctx.gl_in, ctx.ConstU32(index), - ctx.ConstU32(0u))}; - const auto position_comp_ptr = ctx.TypePointer(spv::StorageClass::Input, ctx.F32[1]); - return ctx.OpLoad(ctx.F32[1], - ctx.OpAccessChain(position_comp_ptr, pointer, ctx.ConstU32(comp))); - } - - if (IR::IsParam(attr)) { - const u32 param_id{u32(attr) - u32(IR::Attribute::Param0)}; - const auto param = ctx.input_params.at(param_id).id; - const auto param_arr_ptr = ctx.TypePointer(spv::StorageClass::Input, ctx.F32[4]); - const auto pointer{ctx.OpAccessChain(param_arr_ptr, param, ctx.ConstU32(index))}; - const auto position_comp_ptr = ctx.TypePointer(spv::StorageClass::Input, ctx.F32[1]); - return ctx.OpLoad(ctx.F32[1], - ctx.OpAccessChain(position_comp_ptr, pointer, ctx.ConstU32(comp))); - } - UNREACHABLE(); + return EmitGetAttributeForGeometry(ctx, attr, comp, index); } if (IR::IsParam(attr)) { const u32 index{u32(attr) - u32(IR::Attribute::Param0)}; const auto& param{ctx.input_params.at(index)}; - if (param.buffer_handle < 0) { - if (!ValidId(param.id)) { - // Attribute is disabled or varying component is not written - return ctx.ConstF32(comp == 3 ? 1.0f : 0.0f); - } - - Id result; - if (param.is_default) { - result = ctx.OpCompositeExtract(param.component_type, param.id, comp); - } else if (param.num_components > 1) { - const Id pointer{ - ctx.OpAccessChain(param.pointer_type, param.id, ctx.ConstU32(comp))}; - result = ctx.OpLoad(param.component_type, pointer); - } else { - result = ctx.OpLoad(param.component_type, param.id); - } - if (param.is_integer) { - result = ctx.OpBitcast(ctx.F32[1], result); - } - return result; - } else { + if (param.buffer_handle >= 0) { const auto step_rate = EmitReadStepRate(ctx, param.id.value); const auto offset = ctx.OpIAdd( ctx.U32[1], @@ -229,7 +213,26 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, u32 comp, u32 index) { ctx.ConstU32(comp)); return EmitReadConstBuffer(ctx, param.buffer_handle, offset); } + + Id result; + if (param.is_loaded) { + // Attribute is either default or manually interpolated. The id points to an already + // loaded vector. + result = ctx.OpCompositeExtract(param.component_type, param.id, comp); + } else if (param.num_components > 1) { + // Attribute is a vector and we need to access a specific component. + const Id pointer{ctx.OpAccessChain(param.pointer_type, param.id, ctx.ConstU32(comp))}; + result = ctx.OpLoad(param.component_type, pointer); + } else { + // Attribute is a single float or interger, simply load it. + result = ctx.OpLoad(param.component_type, param.id); + } + if (param.is_integer) { + result = ctx.OpBitcast(ctx.F32[1], result); + } + return result; } + switch (attr) { case IR::Attribute::FragCoord: { const Id coord = ctx.OpLoad( diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp index e9ffdcce8..4a22ba09f 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp @@ -8,6 +8,9 @@ namespace Shader::Backend::SPIRV { void EmitPrologue(EmitContext& ctx) { + if (ctx.stage == Stage::Fragment) { + ctx.DefineInterpolatedAttribs(); + } ctx.DefineBufferOffsets(); } diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index dc404b121..6c8eb1236 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -222,6 +222,36 @@ void EmitContext::DefineBufferOffsets() { } } +void EmitContext::DefineInterpolatedAttribs() { + if (!profile.needs_manual_interpolation) { + return; + } + // Iterate all input attributes, load them and manually interpolate with barycentric + // coordinates. + for (s32 i = 0; i < runtime_info.fs_info.num_inputs; i++) { + const auto& input = runtime_info.fs_info.inputs[i]; + const u32 semantic = input.param_index; + auto& params = input_params[semantic]; + if (input.is_flat || params.is_loaded) { + continue; + } + const Id p_array{OpLoad(TypeArray(F32[4], ConstU32(3U)), params.id)}; + const Id p0{OpCompositeExtract(F32[4], p_array, 0U)}; + const Id p1{OpCompositeExtract(F32[4], p_array, 1U)}; + const Id p2{OpCompositeExtract(F32[4], p_array, 2U)}; + const Id p10{OpFSub(F32[4], p1, p0)}; + const Id p20{OpFSub(F32[4], p2, p0)}; + const Id bary_coord{OpLoad(F32[3], gl_bary_coord_id)}; + const Id bary_coord_y{OpCompositeExtract(F32[1], bary_coord, 1)}; + const Id bary_coord_z{OpCompositeExtract(F32[1], bary_coord, 2)}; + const Id p10_y{OpVectorTimesScalar(F32[4], p10, bary_coord_y)}; + const Id p20_z{OpVectorTimesScalar(F32[4], p20, bary_coord_z)}; + params.id = OpFAdd(F32[4], p0, OpFAdd(F32[4], p10_y, p20_z)); + Name(params.id, fmt::format("fs_in_attr{}", semantic)); + params.is_loaded = true; + } +} + Id MakeDefaultValue(EmitContext& ctx, u32 default_value) { switch (default_value) { case 0: @@ -260,14 +290,14 @@ void EmitContext::DefineInputs() { input.instance_step_rate == Info::VsInput::InstanceIdType::OverStepRate0 ? 0 : 1; // Note that we pass index rather than Id - input_params[input.binding] = { - rate_idx, - input_u32, - U32[1], - input.num_components, - true, - false, - input.instance_data_buf, + input_params[input.binding] = SpirvAttribute{ + .id = rate_idx, + .pointer_type = input_u32, + .component_type = U32[1], + .num_components = input.num_components, + .is_integer = true, + .is_loaded = false, + .buffer_handle = input.instance_data_buf, }; } else { Id id{DefineInput(type, input.binding)}; @@ -286,6 +316,10 @@ void EmitContext::DefineInputs() { frag_coord = DefineVariable(F32[4], spv::BuiltIn::FragCoord, spv::StorageClass::Input); frag_depth = DefineVariable(F32[1], spv::BuiltIn::FragDepth, spv::StorageClass::Output); front_facing = DefineVariable(U1[1], spv::BuiltIn::FrontFacing, spv::StorageClass::Input); + if (profile.needs_manual_interpolation) { + gl_bary_coord_id = + DefineVariable(F32[3], spv::BuiltIn::BaryCoordKHR, spv::StorageClass::Input); + } for (s32 i = 0; i < runtime_info.fs_info.num_inputs; i++) { const auto& input = runtime_info.fs_info.inputs[i]; const u32 semantic = input.param_index; @@ -299,14 +333,21 @@ void EmitContext::DefineInputs() { const IR::Attribute param{IR::Attribute::Param0 + input.param_index}; const u32 num_components = info.loads.NumComponents(param); const Id type{F32[num_components]}; - const Id id{DefineInput(type, semantic)}; - if (input.is_flat) { - Decorate(id, spv::Decoration::Flat); + Id attr_id{}; + if (profile.needs_manual_interpolation && !input.is_flat) { + attr_id = DefineInput(TypeArray(type, ConstU32(3U)), semantic); + Decorate(attr_id, spv::Decoration::PerVertexKHR); + Name(attr_id, fmt::format("fs_in_attr{}_p", semantic)); + } else { + attr_id = DefineInput(type, semantic); + Name(attr_id, fmt::format("fs_in_attr{}", semantic)); + } + if (input.is_flat) { + Decorate(attr_id, spv::Decoration::Flat); } - Name(id, fmt::format("fs_in_attr{}", semantic)); input_params[semantic] = - GetAttributeInfo(AmdGpu::NumberFormat::Float, id, num_components, false); - interfaces.push_back(id); + GetAttributeInfo(AmdGpu::NumberFormat::Float, attr_id, num_components, false); + interfaces.push_back(attr_id); } break; case Stage::Compute: diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index fb30a5dd6..1c5da946d 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -42,7 +42,9 @@ public: ~EmitContext(); Id Def(const IR::Value& value); + void DefineBufferOffsets(); + void DefineInterpolatedAttribs(); [[nodiscard]] Id DefineInput(Id type, u32 location) { const Id input_id{DefineVar(type, spv::StorageClass::Input)}; @@ -197,6 +199,9 @@ public: Id shared_memory_u32_type{}; + Id interpolate_func{}; + Id gl_bary_coord_id{}; + struct TextureDefinition { const VectorIds* data_types; Id id; @@ -241,7 +246,7 @@ public: Id component_type; u32 num_components; bool is_integer{}; - bool is_default{}; + bool is_loaded{}; s32 buffer_handle{-1}; }; std::array input_params{}; diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index bbda731e0..a868ab76c 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h @@ -24,6 +24,7 @@ struct Profile { bool support_explicit_workgroup_layout{}; bool has_broken_spirv_clamp{}; bool lower_left_origin_mode{}; + bool needs_manual_interpolation{}; u64 min_ssbo_alignment{}; }; diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 580458e7e..1c150ce28 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -256,6 +256,7 @@ bool Instance::CreateDevice() { workgroup_memory_explicit_layout = add_extension(VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME); vertex_input_dynamic_state = add_extension(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); + fragment_shader_barycentric = add_extension(VK_KHR_FRAGMENT_SHADER_BARYCENTRIC_EXTENSION_NAME); // The next two extensions are required to be available together in order to support write masks color_write_en = add_extension(VK_EXT_COLOR_WRITE_ENABLE_EXTENSION_NAME); @@ -399,6 +400,9 @@ bool Instance::CreateDevice() { vk::PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT{ .primitiveTopologyListRestart = true, }, + vk::PhysicalDeviceFragmentShaderBarycentricFeaturesKHR{ + .fragmentShaderBarycentric = true, + }, #ifdef __APPLE__ feature_chain.get(), #endif @@ -438,6 +442,9 @@ bool Instance::CreateDevice() { if (!vertex_input_dynamic_state) { device_chain.unlink(); } + if (!fragment_shader_barycentric) { + device_chain.unlink(); + } auto [device_result, dev] = physical_device.createDeviceUnique(device_chain.get()); if (device_result != vk::Result::eSuccess) { diff --git a/src/video_core/renderer_vulkan/vk_instance.h b/src/video_core/renderer_vulkan/vk_instance.h index 51c2c57c5..5a46ef6fe 100644 --- a/src/video_core/renderer_vulkan/vk_instance.h +++ b/src/video_core/renderer_vulkan/vk_instance.h @@ -143,6 +143,11 @@ public: return maintenance5; } + /// Returns true when VK_KHR_fragment_shader_barycentric is supported. + bool IsFragmentShaderBarycentricSupported() const { + return fragment_shader_barycentric; + } + bool IsListRestartSupported() const { return list_restart; } diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 612e950bb..a1ed7edac 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -169,6 +169,8 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_, .support_fp32_denorm_preserve = bool(vk12_props.shaderDenormPreserveFloat32), .support_fp32_denorm_flush = bool(vk12_props.shaderDenormFlushToZeroFloat32), .support_explicit_workgroup_layout = true, + .needs_manual_interpolation = instance.IsFragmentShaderBarycentricSupported() && + instance.GetDriverID() == vk::DriverId::eNvidiaProprietary, }; auto [cache_result, cache] = instance.GetDevice().createPipelineCacheUnique({}); ASSERT_MSG(cache_result == vk::Result::eSuccess, "Failed to create pipeline cache: {}", From f0b75289c8c810a53294c975453c7faba4f49ef2 Mon Sep 17 00:00:00 2001 From: psucien Date: Mon, 2 Dec 2024 22:24:54 +0100 Subject: [PATCH 094/549] video_core: few detiler formats added --- src/video_core/texture_cache/tile_manager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/video_core/texture_cache/tile_manager.cpp b/src/video_core/texture_cache/tile_manager.cpp index c4f24420d..7430168d0 100644 --- a/src/video_core/texture_cache/tile_manager.cpp +++ b/src/video_core/texture_cache/tile_manager.cpp @@ -182,12 +182,15 @@ vk::Format DemoteImageFormatForDetiling(vk::Format format) { case vk::Format::eB8G8R8A8Srgb: case vk::Format::eB8G8R8A8Unorm: case vk::Format::eR8G8B8A8Unorm: + case vk::Format::eR8G8B8A8Snorm: case vk::Format::eR8G8B8A8Uint: case vk::Format::eR32Sfloat: case vk::Format::eR32Uint: case vk::Format::eR16G16Sfloat: case vk::Format::eR16G16Unorm: + case vk::Format::eR16G16Snorm: case vk::Format::eB10G11R11UfloatPack32: + case vk::Format::eA2B10G10R10UnormPack32: return vk::Format::eR32Uint; case vk::Format::eBc1RgbaSrgbBlock: case vk::Format::eBc1RgbaUnormBlock: From c66db95378fb7ae9b68ea3980eae248da55d2250 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Tue, 3 Dec 2024 10:05:51 +0200 Subject: [PATCH 095/549] Misc Ime fixes continue (#1655) * core/libraries: Misc. Ime fixes * fixed issues --------- Co-authored-by: Daniel R <47796739+polybiusproxy@users.noreply.github.com> --- src/core/libraries/ime/ime.cpp | 95 +++++++++++++++++++---------- src/core/libraries/ime/ime.h | 26 ++++++-- src/core/libraries/ime/ime_common.h | 2 +- src/core/libraries/ime/ime_ui.cpp | 81 ++++++++++++------------ src/core/libraries/ime/ime_ui.h | 6 +- 5 files changed, 131 insertions(+), 79 deletions(-) diff --git a/src/core/libraries/ime/ime.cpp b/src/core/libraries/ime/ime.cpp index 700585ff3..dfd659db8 100644 --- a/src/core/libraries/ime/ime.cpp +++ b/src/core/libraries/ime/ime.cpp @@ -44,10 +44,14 @@ public: openEvent.param.rect.y = m_param.ime.posy; } else { openEvent.param.resource_id_array.userId = 1; - openEvent.param.resource_id_array.resource_id[0] = 1; + openEvent.param.resource_id_array.resourceId[0] = 1; } - Execute(nullptr, &openEvent, true); + // Are we supposed to call the event handler on init with + // ADD_OSK? + if (!ime_mode && False(m_param.key.option & OrbisImeKeyboardOption::AddOsk)) { + Execute(nullptr, &openEvent, true); + } if (ime_mode) { g_ime_state = ImeState(&m_param.ime); @@ -56,6 +60,11 @@ public: } s32 Update(OrbisImeEventHandler handler) { + if (!m_ime_mode) { + /* We don't handle any events for ImeKeyboard */ + return ORBIS_OK; + } + std::unique_lock lock{g_ime_state.queue_mutex}; while (!g_ime_state.event_queue.empty()) { @@ -85,6 +94,16 @@ public: } } + s32 SetText(const char16_t* text, u32 length) { + g_ime_state.SetText(text, length); + return ORBIS_OK; + } + + s32 SetCaret(const OrbisImeCaret* caret) { + g_ime_state.SetCaret(caret->index); + return ORBIS_OK; + } + bool IsIme() { return m_ime_mode; } @@ -98,6 +117,7 @@ private: }; static std::unique_ptr g_ime_handler; +static std::unique_ptr g_keyboard_handler; int PS4_SYSV_ABI FinalizeImeModule() { LOG_ERROR(Lib_Ime, "(STUBBED) called"); @@ -130,9 +150,6 @@ s32 PS4_SYSV_ABI sceImeClose() { if (!g_ime_handler) { return ORBIS_IME_ERROR_NOT_OPENED; } - if (!g_ime_handler->IsIme()) { - return ORBIS_IME_ERROR_NOT_OPENED; - } g_ime_handler.release(); g_ime_ui = ImeUi(); @@ -233,14 +250,11 @@ s32 PS4_SYSV_ABI sceImeGetPanelSize(const OrbisImeParam* param, u32* width, u32* s32 PS4_SYSV_ABI sceImeKeyboardClose(s32 userId) { LOG_INFO(Lib_Ime, "(STUBBED) called"); - if (!g_ime_handler) { - return ORBIS_IME_ERROR_NOT_OPENED; - } - if (g_ime_handler->IsIme()) { + if (!g_keyboard_handler) { return ORBIS_IME_ERROR_NOT_OPENED; } - g_ime_handler.release(); + g_keyboard_handler.release(); return ORBIS_OK; } @@ -255,18 +269,17 @@ int PS4_SYSV_ABI sceImeKeyboardGetResourceId() { } s32 PS4_SYSV_ABI sceImeKeyboardOpen(s32 userId, const OrbisImeKeyboardParam* param) { - LOG_ERROR(Lib_Ime, "(STUBBED) called"); + LOG_INFO(Lib_Ime, "called"); if (!param) { return ORBIS_IME_ERROR_INVALID_ADDRESS; } - if (g_ime_handler) { + if (g_keyboard_handler) { return ORBIS_IME_ERROR_BUSY; } - // g_ime_handler = std::make_unique(param); - // return ORBIS_OK; - return ORBIS_IME_ERROR_CONNECTION_FAILED; // Fixup + g_keyboard_handler = std::make_unique(param); + return ORBIS_OK; } int PS4_SYSV_ABI sceImeKeyboardOpenInternal() { @@ -287,16 +300,14 @@ int PS4_SYSV_ABI sceImeKeyboardUpdate() { s32 PS4_SYSV_ABI sceImeOpen(const OrbisImeParam* param, const void* extended) { LOG_INFO(Lib_Ime, "called"); - if (!g_ime_handler) { - g_ime_handler = std::make_unique(param); - } else { - if (g_ime_handler->IsIme()) { - return ORBIS_IME_ERROR_BUSY; - } - - g_ime_handler->Init((void*)param, true); + if (!param) { + return ORBIS_IME_ERROR_INVALID_ADDRESS; + } + if (g_ime_handler) { + return ORBIS_IME_ERROR_BUSY; } + g_ime_handler = std::make_unique(param); return ORBIS_OK; } @@ -322,13 +333,29 @@ int PS4_SYSV_ABI sceImeSetCandidateIndex() { } int PS4_SYSV_ABI sceImeSetCaret(const OrbisImeCaret* caret) { - LOG_ERROR(Lib_Ime, "(STUBBED) called"); - return ORBIS_OK; + LOG_TRACE(Lib_Ime, "called"); + + if (!g_ime_handler) { + return ORBIS_IME_ERROR_NOT_OPENED; + } + if (!caret) { + return ORBIS_IME_ERROR_INVALID_ADDRESS; + } + + return g_ime_handler->SetCaret(caret); } -int PS4_SYSV_ABI sceImeSetText() { - LOG_ERROR(Lib_Ime, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceImeSetText(const char16_t* text, u32 length) { + LOG_TRACE(Lib_Ime, "called"); + + if (!g_ime_handler) { + return ORBIS_IME_ERROR_NOT_OPENED; + } + if (!text) { + return ORBIS_IME_ERROR_INVALID_ADDRESS; + } + + return g_ime_handler->SetText(text, length); } int PS4_SYSV_ABI sceImeSetTextGeometry() { @@ -337,13 +364,19 @@ int PS4_SYSV_ABI sceImeSetTextGeometry() { } s32 PS4_SYSV_ABI sceImeUpdate(OrbisImeEventHandler handler) { - LOG_TRACE(Lib_Ime, "called"); + if (g_ime_handler) { + g_ime_handler->Update(handler); + } - if (!g_ime_handler) { + if (g_keyboard_handler) { + g_keyboard_handler->Update(handler); + } + + if (!g_ime_handler || !g_keyboard_handler) { return ORBIS_IME_ERROR_NOT_OPENED; } - return g_ime_handler->Update(handler); + return ORBIS_OK; } int PS4_SYSV_ABI sceImeVshClearPreedit() { diff --git a/src/core/libraries/ime/ime.h b/src/core/libraries/ime/ime.h index 2915b70da..448ee6896 100644 --- a/src/core/libraries/ime/ime.h +++ b/src/core/libraries/ime/ime.h @@ -26,6 +26,24 @@ enum class OrbisImeKeyboardOption : u32 { }; DECLARE_ENUM_FLAG_OPERATORS(OrbisImeKeyboardOption) +enum class OrbisImeOption : u32 { + DEFAULT = 0, + MULTILINE = 1, + NO_AUTO_CAPITALIZATION = 2, + PASSWORD = 4, + LANGUAGES_FORCED = 8, + EXT_KEYBOARD = 16, + NO_LEARNING = 32, + FIXED_POSITION = 64, + DISABLE_RESUME = 256, + DISABLE_AUTO_SPACE = 512, + DISABLE_POSITION_ADJUSTMENT = 2048, + EXPANDED_PREEDIT_BUFFER = 4096, + USE_JAPANESE_EISUU_KEY_AS_CAPSLOCK = 8192, + USE_2K_COORDINATES = 16384, +}; +DECLARE_ENUM_FLAG_OPERATORS(OrbisImeOption) + struct OrbisImeKeyboardParam { OrbisImeKeyboardOption option; s8 reserved1[4]; @@ -41,9 +59,9 @@ struct OrbisImeParam { OrbisImeEnterLabel enter_label; OrbisImeInputMethod input_method; OrbisImeTextFilter filter; - u32 option; - u32 max_text_length; - char16_t* input_text_buffer; + OrbisImeOption option; + u32 maxTextLength; + char16_t* inputTextBuffer; float posx; float posy; OrbisImeHorizontalAlignment horizontal_alignment; @@ -93,7 +111,7 @@ int PS4_SYSV_ABI sceImeOpenInternal(); void PS4_SYSV_ABI sceImeParamInit(OrbisImeParam* param); int PS4_SYSV_ABI sceImeSetCandidateIndex(); s32 PS4_SYSV_ABI sceImeSetCaret(const OrbisImeCaret* caret); -int PS4_SYSV_ABI sceImeSetText(); +s32 PS4_SYSV_ABI sceImeSetText(const char16_t* text, u32 length); int PS4_SYSV_ABI sceImeSetTextGeometry(); s32 PS4_SYSV_ABI sceImeUpdate(OrbisImeEventHandler handler); int PS4_SYSV_ABI sceImeVshClearPreedit(); diff --git a/src/core/libraries/ime/ime_common.h b/src/core/libraries/ime/ime_common.h index 77f23d91d..6d4afd81d 100644 --- a/src/core/libraries/ime/ime_common.h +++ b/src/core/libraries/ime/ime_common.h @@ -142,7 +142,7 @@ struct OrbisImeKeycode { struct OrbisImeKeyboardResourceIdArray { s32 userId; - u32 resource_id[6]; + u32 resourceId[5]; }; enum class OrbisImeCaretMovementDirection : u32 { diff --git a/src/core/libraries/ime/ime_ui.cpp b/src/core/libraries/ime/ime_ui.cpp index c5f41c5e8..8eaa48178 100644 --- a/src/core/libraries/ime/ime_ui.cpp +++ b/src/core/libraries/ime/ime_ui.cpp @@ -16,7 +16,7 @@ ImeState::ImeState(const OrbisImeParam* param) { } work_buffer = param->work; - text_buffer = param->input_text_buffer; + text_buffer = param->inputTextBuffer; std::size_t text_len = std::char_traits::length(text_buffer); if (!ConvertOrbisToUTF8(text_buffer, text_len, current_text.begin(), @@ -26,15 +26,13 @@ ImeState::ImeState(const OrbisImeParam* param) { } ImeState::ImeState(ImeState&& other) noexcept - : input_changed(other.input_changed), work_buffer(other.work_buffer), - text_buffer(other.text_buffer), current_text(std::move(other.current_text)), - event_queue(std::move(other.event_queue)) { + : work_buffer(other.work_buffer), text_buffer(other.text_buffer), + current_text(std::move(other.current_text)), event_queue(std::move(other.event_queue)) { other.text_buffer = nullptr; } ImeState& ImeState::operator=(ImeState&& other) noexcept { if (this != &other) { - input_changed = other.input_changed; work_buffer = other.work_buffer; text_buffer = other.text_buffer; current_text = std::move(other.current_text); @@ -63,6 +61,10 @@ void ImeState::SendCloseEvent() { SendEvent(&closeEvent); } +void ImeState::SetText(const char16_t* text, u32 length) {} + +void ImeState::SetCaret(u32 position) {} + bool ImeState::ConvertOrbisToUTF8(const char16_t* orbis_text, std::size_t orbis_text_len, char* utf8_text, std::size_t utf8_text_len) { std::fill(utf8_text, utf8_text + utf8_text_len, '\0'); @@ -180,9 +182,8 @@ void ImeUi::DrawInputText() { if (first_render) { SetKeyboardFocusHere(); } - if (InputTextEx("##ImeInput", nullptr, state->current_text.begin(), ime_param->max_text_length, + if (InputTextEx("##ImeInput", nullptr, state->current_text.begin(), ime_param->maxTextLength, input_size, ImGuiInputTextFlags_CallbackAlways, InputTextCallback, this)) { - state->input_changed = true; } } @@ -190,6 +191,39 @@ int ImeUi::InputTextCallback(ImGuiInputTextCallbackData* data) { ImeUi* ui = static_cast(data->UserData); ASSERT(ui); + static std::string lastText; + std::string currentText(data->Buf, data->BufTextLen); + if (currentText != lastText) { + OrbisImeEditText eventParam{}; + eventParam.str = reinterpret_cast(ui->ime_param->work); + eventParam.caret_index = data->CursorPos; + eventParam.area_num = 1; + + eventParam.text_area[0].mode = 1; // Edit mode + eventParam.text_area[0].index = data->CursorPos; + eventParam.text_area[0].length = data->BufTextLen; + + if (!ui->state->ConvertUTF8ToOrbis(data->Buf, data->BufTextLen, eventParam.str, + ui->ime_param->maxTextLength)) { + LOG_ERROR(Lib_ImeDialog, "Failed to convert Orbis char to UTF-8"); + return 0; + } + + if (!ui->state->ConvertUTF8ToOrbis(data->Buf, data->BufTextLen, + ui->ime_param->inputTextBuffer, + ui->ime_param->maxTextLength)) { + LOG_ERROR(Lib_ImeDialog, "Failed to convert Orbis char to UTF-8"); + return 0; + } + + OrbisImeEvent event{}; + event.id = OrbisImeEventId::UpdateText; + event.param.text = eventParam; + + lastText = currentText; + ui->state->SendEvent(&event); + } + static int lastCaretPos = -1; if (lastCaretPos == -1) { lastCaretPos = data->CursorPos; @@ -209,39 +243,6 @@ int ImeUi::InputTextCallback(ImGuiInputTextCallbackData* data) { ui->state->SendEvent(&event); } - static std::string lastText; - std::string currentText(data->Buf, data->BufTextLen); - if (currentText != lastText) { - OrbisImeEditText eventParam{}; - eventParam.str = reinterpret_cast(ui->ime_param->work); - eventParam.caret_index = data->CursorPos; - eventParam.area_num = 1; - - eventParam.text_area[0].mode = 1; // Edit mode - eventParam.text_area[0].index = data->CursorPos; - eventParam.text_area[0].length = data->BufTextLen; - - if (!ui->state->ConvertUTF8ToOrbis(data->Buf, data->BufTextLen, eventParam.str, - ui->ime_param->max_text_length)) { - LOG_ERROR(Lib_ImeDialog, "Failed to convert Orbis char to UTF-8"); - return 0; - } - - if (!ui->state->ConvertUTF8ToOrbis(data->Buf, data->BufTextLen, - ui->ime_param->input_text_buffer, - ui->ime_param->max_text_length)) { - LOG_ERROR(Lib_ImeDialog, "Failed to convert Orbis char to UTF-8"); - return 0; - } - - OrbisImeEvent event{}; - event.id = OrbisImeEventId::UpdateText; - event.param.text = eventParam; - - lastText = currentText; - ui->state->SendEvent(&event); - } - return 0; } diff --git a/src/core/libraries/ime/ime_ui.h b/src/core/libraries/ime/ime_ui.h index ebd70a7c8..a2a806bb9 100644 --- a/src/core/libraries/ime/ime_ui.h +++ b/src/core/libraries/ime/ime_ui.h @@ -22,10 +22,7 @@ class ImeState { friend class ImeHandler; friend class ImeUi; - bool input_changed = false; - void* work_buffer{}; - char16_t* text_buffer{}; // A character can hold up to 4 bytes in UTF-8 @@ -43,6 +40,9 @@ public: void SendEnterEvent(); void SendCloseEvent(); + void SetText(const char16_t* text, u32 length); + void SetCaret(u32 position); + private: bool ConvertOrbisToUTF8(const char16_t* orbis_text, std::size_t orbis_text_len, char* utf8_text, std::size_t native_text_len); From 6bf93071cf43f19b428fb1f4f34039a71cb315ee Mon Sep 17 00:00:00 2001 From: TheTurtle Date: Tue, 3 Dec 2024 14:15:08 +0200 Subject: [PATCH 096/549] hot-fix: Correct getpagesize Tested on my PS4 pro, returns 16KB instead of 4KB --- src/core/libraries/kernel/kernel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/libraries/kernel/kernel.cpp b/src/core/libraries/kernel/kernel.cpp index b310c7be9..4028116ef 100644 --- a/src/core/libraries/kernel/kernel.cpp +++ b/src/core/libraries/kernel/kernel.cpp @@ -203,7 +203,7 @@ int PS4_SYSV_ABI _sigprocmask() { } int PS4_SYSV_ABI posix_getpagesize() { - return 4096; + return 16_KB; } void RegisterKernel(Core::Loader::SymbolsResolver* sym) { From 74b091fd0818894b19949a1a12d183e152f712ea Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Wed, 4 Dec 2024 01:15:58 -0800 Subject: [PATCH 097/549] renderer_vulkan: Add support for indexed QuadList draw. (#1661) --- src/video_core/buffer_cache/buffer_cache.cpp | 28 ++++++++++++++++++- .../renderer_vulkan/liverpool_to_vk.cpp | 13 --------- .../renderer_vulkan/liverpool_to_vk.h | 28 ++++++++++++++++++- 3 files changed, 54 insertions(+), 15 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index 77b353c2f..1f05b16c8 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -236,7 +236,7 @@ bool BufferCache::BindVertexBuffers(const Shader::Info& vs_info) { u32 BufferCache::BindIndexBuffer(bool& is_indexed, u32 index_offset) { // Emulate QuadList primitive type with CPU made index buffer. const auto& regs = liverpool->regs; - if (regs.primitive_type == AmdGpu::PrimitiveType::QuadList) { + if (regs.primitive_type == AmdGpu::PrimitiveType::QuadList && !is_indexed) { is_indexed = true; // Emit indices. @@ -262,6 +262,32 @@ u32 BufferCache::BindIndexBuffer(bool& is_indexed, u32 index_offset) { VAddr index_address = regs.index_base_address.Address(); index_address += index_offset * index_size; + if (regs.primitive_type == AmdGpu::PrimitiveType::QuadList) { + // Convert indices. + const u32 new_index_size = regs.num_indices * index_size * 6 / 4; + const auto [data, offset] = stream_buffer.Map(new_index_size); + const auto index_ptr = reinterpret_cast(index_address); + switch (index_type) { + case vk::IndexType::eUint16: + Vulkan::LiverpoolToVK::ConvertQuadToTriangleListIndices(data, index_ptr, + regs.num_indices); + break; + case vk::IndexType::eUint32: + Vulkan::LiverpoolToVK::ConvertQuadToTriangleListIndices(data, index_ptr, + regs.num_indices); + break; + default: + UNREACHABLE_MSG("Unsupported QuadList index type {}", vk::to_string(index_type)); + break; + } + stream_buffer.Commit(); + + // Bind index buffer. + const auto cmdbuf = scheduler.CommandBuffer(); + cmdbuf.bindIndexBuffer(stream_buffer.Handle(), offset, index_type); + return new_index_size / index_size; + } + // Bind index buffer. const u32 index_buffer_size = regs.num_indices * index_size; const auto [vk_buffer, offset] = ObtainBuffer(index_address, index_buffer_size, false); diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp index 258e7f391..2262a429a 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp @@ -726,19 +726,6 @@ vk::Format DepthFormat(DepthBuffer::ZFormat z_format, DepthBuffer::StencilFormat return format->vk_format; } -void EmitQuadToTriangleListIndices(u8* out_ptr, u32 num_vertices) { - static constexpr u16 NumVerticesPerQuad = 4; - u16* out_data = reinterpret_cast(out_ptr); - for (u16 i = 0; i < num_vertices; i += NumVerticesPerQuad) { - *out_data++ = i; - *out_data++ = i + 1; - *out_data++ = i + 2; - *out_data++ = i; - *out_data++ = i + 2; - *out_data++ = i + 3; - } -} - vk::ClearValue ColorBufferClearValue(const AmdGpu::Liverpool::ColorBuffer& color_buffer) { const auto comp_swap = color_buffer.info.comp_swap.Value(); const auto format = color_buffer.info.format.Value(); diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.h b/src/video_core/renderer_vulkan/liverpool_to_vk.h index 70e707fad..287ba691e 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.h +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.h @@ -68,7 +68,33 @@ vk::ClearValue ColorBufferClearValue(const AmdGpu::Liverpool::ColorBuffer& color vk::SampleCountFlagBits NumSamples(u32 num_samples, vk::SampleCountFlags supported_flags); -void EmitQuadToTriangleListIndices(u8* out_indices, u32 num_vertices); +static constexpr u16 NumVerticesPerQuad = 4; + +inline void EmitQuadToTriangleListIndices(u8* out_ptr, u32 num_vertices) { + u16* out_data = reinterpret_cast(out_ptr); + for (u16 i = 0; i < num_vertices; i += NumVerticesPerQuad) { + *out_data++ = i; + *out_data++ = i + 1; + *out_data++ = i + 2; + *out_data++ = i; + *out_data++ = i + 2; + *out_data++ = i + 3; + } +} + +template +void ConvertQuadToTriangleListIndices(u8* out_ptr, const u8* in_ptr, u32 num_vertices) { + T* out_data = reinterpret_cast(out_ptr); + const T* in_data = reinterpret_cast(in_ptr); + for (u16 i = 0; i < num_vertices; i += NumVerticesPerQuad) { + *out_data++ = in_data[i]; + *out_data++ = in_data[i + 1]; + *out_data++ = in_data[i + 2]; + *out_data++ = in_data[i]; + *out_data++ = in_data[i + 2]; + *out_data++ = in_data[i + 3]; + } +} static inline vk::Format PromoteFormatToDepth(vk::Format fmt) { if (fmt == vk::Format::eR32Sfloat) { From 920acb8d8b7524f32eafefccd5396f7c07f8c158 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Wed, 4 Dec 2024 03:03:47 -0800 Subject: [PATCH 098/549] renderer_vulkan: Parse fetch shader per-pipeline (#1656) * shader_recompiler: Read image format info directly from sharps instead of storing in shader info. * renderer_vulkan: Parse fetch shader per-pipeline * Few minor fixes. * shader_recompiler: Specialize on vertex attribute number types. * shader_recompiler: Move GetDrawOffsets to fetch shader --- .../backend/spirv/emit_spirv_image.cpp | 3 +- .../backend/spirv/spirv_emit_context.cpp | 91 ++++++++++--------- .../frontend/fetch_shader.cpp | 12 ++- src/shader_recompiler/frontend/fetch_shader.h | 56 +++++++++++- .../frontend/translate/translate.cpp | 36 ++------ src/shader_recompiler/info.h | 47 ++-------- .../ir/passes/resource_tracking_pass.cpp | 4 - src/shader_recompiler/profile.h | 1 + src/shader_recompiler/specialization.h | 45 ++++++++- src/video_core/amdgpu/pixel_format.h | 19 +++- src/video_core/amdgpu/resource.h | 4 + src/video_core/buffer_cache/buffer_cache.cpp | 21 +++-- src/video_core/buffer_cache/buffer_cache.h | 8 +- .../renderer_vulkan/vk_graphics_pipeline.cpp | 32 ++++--- .../renderer_vulkan/vk_graphics_pipeline.h | 7 ++ .../renderer_vulkan/vk_instance.cpp | 7 ++ src/video_core/renderer_vulkan/vk_instance.h | 7 ++ .../renderer_vulkan/vk_pipeline_cache.cpp | 40 ++++---- .../renderer_vulkan/vk_pipeline_cache.h | 7 +- .../renderer_vulkan/vk_rasterizer.cpp | 14 +-- src/video_core/texture_cache/image_view.cpp | 7 +- 21 files changed, 286 insertions(+), 182 deletions(-) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index 40e5ea8b9..fe2660705 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp @@ -187,7 +187,8 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, const Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, u32 handle, Id lod, bool has_mips) { const auto& texture = ctx.images[handle & 0xFFFF]; const Id image = ctx.OpLoad(texture.image_type, texture.id); - const auto type = ctx.info.images[handle & 0xFFFF].type; + const auto sharp = ctx.info.images[handle & 0xFFFF].GetSharp(ctx.info); + const auto type = sharp.GetBoundType(); const Id zero = ctx.u32_zero_value; const auto mips{[&] { return has_mips ? ctx.OpImageQueryLevels(ctx.U32[1], image) : zero; }}; const bool uses_lod{type != AmdGpu::ImageType::Color2DMsaa && !texture.is_storage}; diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 6c8eb1236..4ce9f4221 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -4,6 +4,7 @@ #include "common/assert.h" #include "common/div_ceil.h" #include "shader_recompiler/backend/spirv/spirv_emit_context.h" +#include "shader_recompiler/frontend/fetch_shader.h" #include "shader_recompiler/ir/passes/srt.h" #include "video_core/amdgpu/types.h" @@ -155,18 +156,12 @@ void EmitContext::DefineInterfaces() { } const VectorIds& GetAttributeType(EmitContext& ctx, AmdGpu::NumberFormat fmt) { - switch (fmt) { - case AmdGpu::NumberFormat::Float: - case AmdGpu::NumberFormat::Unorm: - case AmdGpu::NumberFormat::Snorm: - case AmdGpu::NumberFormat::SnormNz: - case AmdGpu::NumberFormat::Sscaled: - case AmdGpu::NumberFormat::Uscaled: - case AmdGpu::NumberFormat::Srgb: + switch (GetNumberClass(fmt)) { + case AmdGpu::NumberClass::Float: return ctx.F32; - case AmdGpu::NumberFormat::Sint: + case AmdGpu::NumberClass::Sint: return ctx.S32; - case AmdGpu::NumberFormat::Uint: + case AmdGpu::NumberClass::Uint: return ctx.U32; default: break; @@ -176,18 +171,12 @@ const VectorIds& GetAttributeType(EmitContext& ctx, AmdGpu::NumberFormat fmt) { EmitContext::SpirvAttribute EmitContext::GetAttributeInfo(AmdGpu::NumberFormat fmt, Id id, u32 num_components, bool output) { - switch (fmt) { - case AmdGpu::NumberFormat::Float: - case AmdGpu::NumberFormat::Unorm: - case AmdGpu::NumberFormat::Snorm: - case AmdGpu::NumberFormat::SnormNz: - case AmdGpu::NumberFormat::Sscaled: - case AmdGpu::NumberFormat::Uscaled: - case AmdGpu::NumberFormat::Srgb: + switch (GetNumberClass(fmt)) { + case AmdGpu::NumberClass::Float: return {id, output ? output_f32 : input_f32, F32[1], num_components, false}; - case AmdGpu::NumberFormat::Uint: + case AmdGpu::NumberClass::Uint: return {id, output ? output_u32 : input_u32, U32[1], num_components, true}; - case AmdGpu::NumberFormat::Sint: + case AmdGpu::NumberClass::Sint: return {id, output ? output_s32 : input_s32, S32[1], num_components, true}; default: break; @@ -280,33 +269,42 @@ void EmitContext::DefineInputs() { base_vertex = DefineVariable(U32[1], spv::BuiltIn::BaseVertex, spv::StorageClass::Input); instance_id = DefineVariable(U32[1], spv::BuiltIn::InstanceIndex, spv::StorageClass::Input); - for (const auto& input : info.vs_inputs) { - ASSERT(input.binding < IR::NumParams); - const Id type{GetAttributeType(*this, input.fmt)[4]}; - if (input.instance_step_rate == Info::VsInput::InstanceIdType::OverStepRate0 || - input.instance_step_rate == Info::VsInput::InstanceIdType::OverStepRate1) { - + const auto fetch_shader = Gcn::ParseFetchShader(info); + if (!fetch_shader) { + break; + } + for (const auto& attrib : fetch_shader->attributes) { + ASSERT(attrib.semantic < IR::NumParams); + const auto sharp = attrib.GetSharp(info); + const Id type{GetAttributeType(*this, sharp.GetNumberFmt())[4]}; + if (attrib.UsesStepRates()) { const u32 rate_idx = - input.instance_step_rate == Info::VsInput::InstanceIdType::OverStepRate0 ? 0 - : 1; + attrib.GetStepRate() == Gcn::VertexAttribute::InstanceIdType::OverStepRate0 ? 0 + : 1; + const u32 num_components = AmdGpu::NumComponents(sharp.GetDataFmt()); + const auto buffer = + std::ranges::find_if(info.buffers, [&attrib](const auto& buffer) { + return buffer.instance_attrib == attrib.semantic; + }); // Note that we pass index rather than Id - input_params[input.binding] = SpirvAttribute{ + input_params[attrib.semantic] = SpirvAttribute{ .id = rate_idx, .pointer_type = input_u32, .component_type = U32[1], - .num_components = input.num_components, + .num_components = std::min(attrib.num_elements, num_components), .is_integer = true, .is_loaded = false, - .buffer_handle = input.instance_data_buf, + .buffer_handle = int(buffer - info.buffers.begin()), }; } else { - Id id{DefineInput(type, input.binding)}; - if (input.instance_step_rate == Info::VsInput::InstanceIdType::Plain) { - Name(id, fmt::format("vs_instance_attr{}", input.binding)); + Id id{DefineInput(type, attrib.semantic)}; + if (attrib.GetStepRate() == Gcn::VertexAttribute::InstanceIdType::Plain) { + Name(id, fmt::format("vs_instance_attr{}", attrib.semantic)); } else { - Name(id, fmt::format("vs_in_attr{}", input.binding)); + Name(id, fmt::format("vs_in_attr{}", attrib.semantic)); } - input_params[input.binding] = GetAttributeInfo(input.fmt, id, 4, false); + input_params[attrib.semantic] = + GetAttributeInfo(sharp.GetNumberFmt(), id, 4, false); interfaces.push_back(id); } } @@ -553,9 +551,10 @@ void EmitContext::DefineBuffers() { void EmitContext::DefineTextureBuffers() { for (const auto& desc : info.texture_buffers) { - const bool is_integer = - desc.nfmt == AmdGpu::NumberFormat::Uint || desc.nfmt == AmdGpu::NumberFormat::Sint; - const VectorIds& sampled_type{GetAttributeType(*this, desc.nfmt)}; + const auto sharp = desc.GetSharp(info); + const auto nfmt = sharp.GetNumberFmt(); + const bool is_integer = AmdGpu::IsInteger(nfmt); + const VectorIds& sampled_type{GetAttributeType(*this, nfmt)}; const u32 sampled = desc.is_written ? 2 : 1; const Id image_type{TypeImage(sampled_type[1], spv::Dim::Buffer, false, false, false, sampled, spv::ImageFormat::Unknown)}; @@ -650,10 +649,11 @@ spv::ImageFormat GetFormat(const AmdGpu::Image& image) { } Id ImageType(EmitContext& ctx, const ImageResource& desc, Id sampled_type) { - const auto image = ctx.info.ReadUdSharp(desc.sharp_idx); + const auto image = desc.GetSharp(ctx.info); const auto format = desc.is_atomic ? GetFormat(image) : spv::ImageFormat::Unknown; + const auto type = image.GetBoundType(); const u32 sampled = desc.is_storage ? 2 : 1; - switch (desc.type) { + switch (type) { case AmdGpu::ImageType::Color1D: return ctx.TypeImage(sampled_type, spv::Dim::Dim1D, false, false, false, sampled, format); case AmdGpu::ImageType::Color1DArray: @@ -672,14 +672,15 @@ Id ImageType(EmitContext& ctx, const ImageResource& desc, Id sampled_type) { default: break; } - throw InvalidArgument("Invalid texture type {}", desc.type); + throw InvalidArgument("Invalid texture type {}", type); } void EmitContext::DefineImagesAndSamplers() { for (const auto& image_desc : info.images) { - const bool is_integer = image_desc.nfmt == AmdGpu::NumberFormat::Uint || - image_desc.nfmt == AmdGpu::NumberFormat::Sint; - const VectorIds& data_types = GetAttributeType(*this, image_desc.nfmt); + const auto sharp = image_desc.GetSharp(info); + const auto nfmt = sharp.GetNumberFmt(); + const bool is_integer = AmdGpu::IsInteger(nfmt); + const VectorIds& data_types = GetAttributeType(*this, nfmt); const Id sampled_type = data_types[1]; const Id image_type{ImageType(*this, image_desc, sampled_type)}; const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)}; diff --git a/src/shader_recompiler/frontend/fetch_shader.cpp b/src/shader_recompiler/frontend/fetch_shader.cpp index 16938410c..8ae664d79 100644 --- a/src/shader_recompiler/frontend/fetch_shader.cpp +++ b/src/shader_recompiler/frontend/fetch_shader.cpp @@ -34,8 +34,14 @@ namespace Shader::Gcn { * We take the reverse way, extract the original input semantics from these instructions. **/ -FetchShaderData ParseFetchShader(const u32* code, u32* out_size) { - FetchShaderData data{}; +std::optional ParseFetchShader(const Shader::Info& info) { + if (!info.has_fetch_shader) { + return std::nullopt; + } + const u32* code; + std::memcpy(&code, &info.user_data[info.fetch_shader_sgpr_base], sizeof(code)); + + FetchShaderData data{.code = code}; GcnCodeSlice code_slice(code, code + std::numeric_limits::max()); GcnDecodeContext decoder; @@ -49,7 +55,7 @@ FetchShaderData ParseFetchShader(const u32* code, u32* out_size) { u32 semantic_index = 0; while (!code_slice.atEnd()) { const auto inst = decoder.decodeInstruction(code_slice); - *out_size += inst.length; + data.size += inst.length; if (inst.opcode == Opcode::S_SETPC_B64) { break; diff --git a/src/shader_recompiler/frontend/fetch_shader.h b/src/shader_recompiler/frontend/fetch_shader.h index 0e5d15419..ee9f5c805 100644 --- a/src/shader_recompiler/frontend/fetch_shader.h +++ b/src/shader_recompiler/frontend/fetch_shader.h @@ -3,26 +3,80 @@ #pragma once +#include #include #include "common/types.h" +#include "shader_recompiler/info.h" namespace Shader::Gcn { struct VertexAttribute { + enum InstanceIdType : u8 { + None = 0, + OverStepRate0 = 1, + OverStepRate1 = 2, + Plain = 3, + }; + u8 semantic; ///< Semantic index of the attribute u8 dest_vgpr; ///< Destination VGPR to load first component. u8 num_elements; ///< Number of components to load u8 sgpr_base; ///< SGPR that contains the pointer to the list of vertex V# u8 dword_offset; ///< The dword offset of the V# that describes this attribute. u8 instance_data; ///< Indicates that the buffer will be accessed in instance rate + + [[nodiscard]] InstanceIdType GetStepRate() const { + return static_cast(instance_data); + } + + [[nodiscard]] bool UsesStepRates() const { + const auto step_rate = GetStepRate(); + return step_rate == OverStepRate0 || step_rate == OverStepRate1; + } + + [[nodiscard]] constexpr AmdGpu::Buffer GetSharp(const Shader::Info& info) const noexcept { + return info.ReadUdReg(sgpr_base, dword_offset); + } + + bool operator==(const VertexAttribute& other) const { + return semantic == other.semantic && dest_vgpr == other.dest_vgpr && + num_elements == other.num_elements && sgpr_base == other.sgpr_base && + dword_offset == other.dword_offset && instance_data == other.instance_data; + } }; struct FetchShaderData { + const u32* code; + u32 size = 0; std::vector attributes; s8 vertex_offset_sgpr = -1; ///< SGPR of vertex offset from VADDR s8 instance_offset_sgpr = -1; ///< SGPR of instance offset from VADDR + + [[nodiscard]] bool UsesStepRates() const { + return std::ranges::find_if(attributes, [](const VertexAttribute& attribute) { + return attribute.UsesStepRates(); + }) != attributes.end(); + } + + [[nodiscard]] std::pair GetDrawOffsets(const AmdGpu::Liverpool::Regs& regs, + const Info& info) const { + u32 vertex_offset = regs.index_offset; + u32 instance_offset = 0; + if (vertex_offset == 0 && vertex_offset_sgpr != -1) { + vertex_offset = info.user_data[vertex_offset_sgpr]; + } + if (instance_offset_sgpr != -1) { + instance_offset = info.user_data[instance_offset_sgpr]; + } + return {vertex_offset, instance_offset}; + } + + bool operator==(const FetchShaderData& other) const { + return attributes == other.attributes && vertex_offset_sgpr == other.vertex_offset_sgpr && + instance_offset_sgpr == other.instance_offset_sgpr; + } }; -FetchShaderData ParseFetchShader(const u32* code, u32* out_size); +std::optional ParseFetchShader(const Shader::Info& info); } // namespace Shader::Gcn diff --git a/src/shader_recompiler/frontend/translate/translate.cpp b/src/shader_recompiler/frontend/translate/translate.cpp index 005c4a7ff..68625a12b 100644 --- a/src/shader_recompiler/frontend/translate/translate.cpp +++ b/src/shader_recompiler/frontend/translate/translate.cpp @@ -368,13 +368,11 @@ void Translator::SetDst64(const InstOperand& operand, const IR::U64F64& value_ra void Translator::EmitFetch(const GcnInst& inst) { // Read the pointer to the fetch shader assembly. - const u32 sgpr_base = inst.src[0].code; - const u32* code; - std::memcpy(&code, &info.user_data[sgpr_base], sizeof(code)); + info.has_fetch_shader = true; + info.fetch_shader_sgpr_base = inst.src[0].code; - // Parse the assembly to generate a list of attributes. - u32 fetch_size{}; - const auto fetch_data = ParseFetchShader(code, &fetch_size); + const auto fetch_data = ParseFetchShader(info); + ASSERT(fetch_data.has_value()); if (Config::dumpShaders()) { using namespace Common::FS; @@ -384,13 +382,10 @@ void Translator::EmitFetch(const GcnInst& inst) { } const auto filename = fmt::format("vs_{:#018x}.fetch.bin", info.pgm_hash); const auto file = IOFile{dump_dir / filename, FileAccessMode::Write}; - file.WriteRaw(code, fetch_size); + file.WriteRaw(fetch_data->code, fetch_data->size); } - info.vertex_offset_sgpr = fetch_data.vertex_offset_sgpr; - info.instance_offset_sgpr = fetch_data.instance_offset_sgpr; - - for (const auto& attrib : fetch_data.attributes) { + for (const auto& attrib : fetch_data->attributes) { const IR::Attribute attr{IR::Attribute::Param0 + attrib.semantic}; IR::VectorReg dst_reg{attrib.dest_vgpr}; @@ -420,29 +415,14 @@ void Translator::EmitFetch(const GcnInst& inst) { // In case of programmable step rates we need to fallback to instance data pulling in // shader, so VBs should be bound as regular data buffers - s32 instance_buf_handle = -1; - const auto step_rate = static_cast(attrib.instance_data); - if (step_rate == Info::VsInput::OverStepRate0 || - step_rate == Info::VsInput::OverStepRate1) { + if (attrib.UsesStepRates()) { info.buffers.push_back({ .sharp_idx = info.srt_info.ReserveSharp(attrib.sgpr_base, attrib.dword_offset, 4), .used_types = IR::Type::F32, .is_instance_data = true, + .instance_attrib = attrib.semantic, }); - instance_buf_handle = s32(info.buffers.size() - 1); - info.uses_step_rates = true; } - - const u32 num_components = AmdGpu::NumComponents(buffer.GetDataFmt()); - info.vs_inputs.push_back({ - .fmt = buffer.GetNumberFmt(), - .binding = attrib.semantic, - .num_components = std::min(attrib.num_elements, num_components), - .sgpr_base = attrib.sgpr_base, - .dword_offset = attrib.dword_offset, - .instance_step_rate = step_rate, - .instance_data_buf = instance_buf_handle, - }); } } diff --git a/src/shader_recompiler/info.h b/src/shader_recompiler/info.h index c7ae2a1e5..d382d0e7c 100644 --- a/src/shader_recompiler/info.h +++ b/src/shader_recompiler/info.h @@ -45,6 +45,7 @@ struct BufferResource { AmdGpu::Buffer inline_cbuf; bool is_gds_buffer{}; bool is_instance_data{}; + u8 instance_attrib{}; bool is_written{}; bool IsStorage(AmdGpu::Buffer buffer) const noexcept { @@ -57,7 +58,6 @@ using BufferResourceList = boost::container::small_vector; struct TextureBufferResource { u32 sharp_idx; - AmdGpu::NumberFormat nfmt; bool is_written{}; constexpr AmdGpu::Buffer GetSharp(const Info& info) const noexcept; @@ -66,8 +66,6 @@ using TextureBufferResourceList = boost::container::small_vector vs_inputs{}; - struct AttributeFlags { bool Get(IR::Attribute attrib, u32 comp = 0) const { return flags[Index(attrib)] & (1 << comp); @@ -179,9 +159,6 @@ struct Info { CopyShaderData gs_copy_data; - s8 vertex_offset_sgpr = -1; - s8 instance_offset_sgpr = -1; - BufferResourceList buffers; TextureBufferResourceList texture_buffers; ImageResourceList images; @@ -208,10 +185,11 @@ struct Info { bool uses_shared{}; bool uses_fp16{}; bool uses_fp64{}; - bool uses_step_rates{}; bool translation_failed{}; // indicates that shader has unsupported instructions bool has_readconst{}; u8 mrt_mask{0u}; + bool has_fetch_shader{false}; + u32 fetch_shader_sgpr_base{0u}; explicit Info(Stage stage_, ShaderParams params) : stage{stage_}, pgm_hash{params.hash}, pgm_base{params.Base()}, @@ -252,18 +230,6 @@ struct Info { bnd.user_data += ud_mask.NumRegs(); } - [[nodiscard]] std::pair GetDrawOffsets(const AmdGpu::Liverpool::Regs& regs) const { - u32 vertex_offset = regs.index_offset; - u32 instance_offset = 0; - if (vertex_offset == 0 && vertex_offset_sgpr != -1) { - vertex_offset = user_data[vertex_offset_sgpr]; - } - if (instance_offset_sgpr != -1) { - instance_offset = user_data[instance_offset_sgpr]; - } - return {vertex_offset, instance_offset}; - } - void RefreshFlatBuf() { flattened_ud_buf.resize(srt_info.flattened_bufsize_dw); ASSERT(user_data.size() <= NumUserDataRegs); @@ -284,7 +250,12 @@ constexpr AmdGpu::Buffer TextureBufferResource::GetSharp(const Info& info) const } constexpr AmdGpu::Image ImageResource::GetSharp(const Info& info) const noexcept { - return info.ReadUdSharp(sharp_idx); + const auto image = info.ReadUdSharp(sharp_idx); + if (!image.Valid()) { + // Fall back to null image if unbound. + return AmdGpu::Image::Null(); + } + return image; } constexpr AmdGpu::Sampler SamplerResource::GetSharp(const Info& info) const noexcept { diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index 7d29c845d..c1ff3d2f2 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -381,7 +381,6 @@ void PatchTextureBufferInstruction(IR::Block& block, IR::Inst& inst, Info& info, const auto buffer = info.ReadUdSharp(sharp); const s32 binding = descriptors.Add(TextureBufferResource{ .sharp_idx = sharp, - .nfmt = buffer.GetNumberFmt(), .is_written = inst.GetOpcode() == IR::Opcode::StoreBufferFormatF32, }); @@ -660,11 +659,8 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip } } - const auto type = image.IsPartialCubemap() ? AmdGpu::ImageType::Color2DArray : image.GetType(); u32 image_binding = descriptors.Add(ImageResource{ .sharp_idx = tsharp, - .type = type, - .nfmt = image.GetNumberFmt(), .is_storage = is_storage, .is_depth = bool(inst_info.is_depth), .is_atomic = IsImageAtomicInstruction(inst), diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index a868ab76c..96c458d44 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h @@ -22,6 +22,7 @@ struct Profile { bool support_fp32_denorm_preserve{}; bool support_fp32_denorm_flush{}; bool support_explicit_workgroup_layout{}; + bool support_legacy_vertex_attributes{}; bool has_broken_spirv_clamp{}; bool lower_left_origin_mode{}; bool needs_manual_interpolation{}; diff --git a/src/shader_recompiler/specialization.h b/src/shader_recompiler/specialization.h index 225b164b5..740b89dda 100644 --- a/src/shader_recompiler/specialization.h +++ b/src/shader_recompiler/specialization.h @@ -6,12 +6,19 @@ #include #include "common/types.h" +#include "frontend/fetch_shader.h" #include "shader_recompiler/backend/bindings.h" #include "shader_recompiler/info.h" #include "shader_recompiler/ir/passes/srt.h" namespace Shader { +struct VsAttribSpecialization { + AmdGpu::NumberClass num_class{}; + + auto operator<=>(const VsAttribSpecialization&) const = default; +}; + struct BufferSpecialization { u16 stride : 14; u16 is_storage : 1; @@ -50,6 +57,8 @@ struct StageSpecialization { const Shader::Info* info; RuntimeInfo runtime_info; + Gcn::FetchShaderData fetch_shader_data{}; + boost::container::small_vector vs_attribs; std::bitset bitset{}; boost::container::small_vector buffers; boost::container::small_vector tex_buffers; @@ -57,9 +66,19 @@ struct StageSpecialization { boost::container::small_vector fmasks; Backend::Bindings start{}; - explicit StageSpecialization(const Shader::Info& info_, RuntimeInfo runtime_info_, - Backend::Bindings start_) + explicit StageSpecialization(const Info& info_, RuntimeInfo runtime_info_, + const Profile& profile_, Backend::Bindings start_) : info{&info_}, runtime_info{runtime_info_}, start{start_} { + if (const auto fetch_shader = Gcn::ParseFetchShader(info_)) { + fetch_shader_data = *fetch_shader; + if (info_.stage == Stage::Vertex && !profile_.support_legacy_vertex_attributes) { + // Specialize shader on VS input number types to follow spec. + ForEachSharp(vs_attribs, fetch_shader_data.attributes, + [](auto& spec, const auto& desc, AmdGpu::Buffer sharp) { + spec.num_class = AmdGpu::GetNumberClass(sharp.GetNumberFmt()); + }); + } + } u32 binding{}; if (info->has_readconst) { binding++; @@ -75,8 +94,7 @@ struct StageSpecialization { }); ForEachSharp(binding, images, info->images, [](auto& spec, const auto& desc, AmdGpu::Image sharp) { - spec.type = sharp.IsPartialCubemap() ? AmdGpu::ImageType::Color2DArray - : sharp.GetType(); + spec.type = sharp.GetBoundType(); spec.is_integer = AmdGpu::IsInteger(sharp.GetNumberFmt()); }); ForEachSharp(binding, fmasks, info->fmasks, @@ -86,6 +104,17 @@ struct StageSpecialization { }); } + void ForEachSharp(auto& spec_list, auto& desc_list, auto&& func) { + for (const auto& desc : desc_list) { + auto& spec = spec_list.emplace_back(); + const auto sharp = desc.GetSharp(*info); + if (!sharp) { + continue; + } + func(spec, desc, sharp); + } + } + void ForEachSharp(u32& binding, auto& spec_list, auto& desc_list, auto&& func) { for (const auto& desc : desc_list) { auto& spec = spec_list.emplace_back(); @@ -106,6 +135,14 @@ struct StageSpecialization { if (runtime_info != other.runtime_info) { return false; } + if (fetch_shader_data != other.fetch_shader_data) { + return false; + } + for (u32 i = 0; i < vs_attribs.size(); i++) { + if (vs_attribs[i] != other.vs_attribs[i]) { + return false; + } + } u32 binding{}; if (info->has_readconst != other.info->has_readconst) { return false; diff --git a/src/video_core/amdgpu/pixel_format.h b/src/video_core/amdgpu/pixel_format.h index e83313ea4..38c81ba5f 100644 --- a/src/video_core/amdgpu/pixel_format.h +++ b/src/video_core/amdgpu/pixel_format.h @@ -10,7 +10,24 @@ namespace AmdGpu { -[[nodiscard]] constexpr bool IsInteger(NumberFormat nfmt) { +enum NumberClass { + Float, + Sint, + Uint, +}; + +[[nodiscard]] constexpr NumberClass GetNumberClass(const NumberFormat nfmt) { + switch (nfmt) { + case NumberFormat::Sint: + return Sint; + case NumberFormat::Uint: + return Uint; + default: + return Float; + } +} + +[[nodiscard]] constexpr bool IsInteger(const NumberFormat nfmt) { return nfmt == AmdGpu::NumberFormat::Sint || nfmt == AmdGpu::NumberFormat::Uint; } diff --git a/src/video_core/amdgpu/resource.h b/src/video_core/amdgpu/resource.h index f43fc9800..a78a68391 100644 --- a/src/video_core/amdgpu/resource.h +++ b/src/video_core/amdgpu/resource.h @@ -304,6 +304,10 @@ struct Image { const auto viewed_slice = last_array - base_array + 1; return GetType() == ImageType::Cube && viewed_slice < 6; } + + ImageType GetBoundType() const noexcept { + return IsPartialCubemap() ? ImageType::Color2DArray : GetType(); + } }; static_assert(sizeof(Image) == 32); // 256bits diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index 1f05b16c8..1abdb230b 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -5,6 +5,7 @@ #include "common/alignment.h" #include "common/scope_exit.h" #include "common/types.h" +#include "shader_recompiler/frontend/fetch_shader.h" #include "shader_recompiler/info.h" #include "video_core/amdgpu/liverpool.h" #include "video_core/buffer_cache/buffer_cache.h" @@ -107,7 +108,8 @@ void BufferCache::DownloadBufferMemory(Buffer& buffer, VAddr device_addr, u64 si } } -bool BufferCache::BindVertexBuffers(const Shader::Info& vs_info) { +bool BufferCache::BindVertexBuffers( + const Shader::Info& vs_info, const std::optional& fetch_shader) { boost::container::small_vector attributes; boost::container::small_vector bindings; SCOPE_EXIT { @@ -126,7 +128,7 @@ bool BufferCache::BindVertexBuffers(const Shader::Info& vs_info) { } }; - if (vs_info.vs_inputs.empty()) { + if (!fetch_shader || fetch_shader->attributes.empty()) { return false; } @@ -150,30 +152,29 @@ bool BufferCache::BindVertexBuffers(const Shader::Info& vs_info) { // Calculate buffers memory overlaps bool has_step_rate = false; boost::container::static_vector ranges{}; - for (const auto& input : vs_info.vs_inputs) { - if (input.instance_step_rate == Shader::Info::VsInput::InstanceIdType::OverStepRate0 || - input.instance_step_rate == Shader::Info::VsInput::InstanceIdType::OverStepRate1) { + for (const auto& attrib : fetch_shader->attributes) { + if (attrib.UsesStepRates()) { has_step_rate = true; continue; } - const auto& buffer = vs_info.ReadUdReg(input.sgpr_base, input.dword_offset); + const auto& buffer = attrib.GetSharp(vs_info); if (buffer.GetSize() == 0) { continue; } guest_buffers.emplace_back(buffer); ranges.emplace_back(buffer.base_address, buffer.base_address + buffer.GetSize()); attributes.push_back({ - .location = input.binding, - .binding = input.binding, + .location = attrib.semantic, + .binding = attrib.semantic, .format = Vulkan::LiverpoolToVK::SurfaceFormat(buffer.GetDataFmt(), buffer.GetNumberFmt()), .offset = 0, }); bindings.push_back({ - .binding = input.binding, + .binding = attrib.semantic, .stride = buffer.GetStride(), - .inputRate = input.instance_step_rate == Shader::Info::VsInput::None + .inputRate = attrib.GetStepRate() == Shader::Gcn::VertexAttribute::InstanceIdType::None ? vk::VertexInputRate::eVertex : vk::VertexInputRate::eInstance, .divisor = 1, diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index e2519e942..b1bf77f8a 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -20,8 +20,11 @@ struct Liverpool; } namespace Shader { -struct Info; +namespace Gcn { +struct FetchShaderData; } +struct Info; +} // namespace Shader namespace VideoCore { @@ -76,7 +79,8 @@ public: void InvalidateMemory(VAddr device_addr, u64 size); /// Binds host vertex buffers for the current draw. - bool BindVertexBuffers(const Shader::Info& vs_info); + bool BindVertexBuffers(const Shader::Info& vs_info, + const std::optional& fetch_shader); /// Bind host index buffer for the current draw. u32 BindIndexBuffer(bool& is_indexed, u32 index_offset); diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index d0d16ac75..d53204c77 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include #include @@ -10,6 +11,8 @@ #include "video_core/amdgpu/resource.h" #include "video_core/buffer_cache/buffer_cache.h" #include "video_core/renderer_vulkan/vk_graphics_pipeline.h" + +#include "shader_recompiler/frontend/fetch_shader.h" #include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/texture_cache/texture_cache.h" @@ -20,8 +23,10 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul DescriptorHeap& desc_heap_, const GraphicsPipelineKey& key_, vk::PipelineCache pipeline_cache, std::span infos, + std::optional fetch_shader_, std::span modules) - : Pipeline{instance_, scheduler_, desc_heap_, pipeline_cache}, key{key_} { + : Pipeline{instance_, scheduler_, desc_heap_, pipeline_cache}, key{key_}, + fetch_shader{std::move(fetch_shader_)} { const vk::Device device = instance.GetDevice(); std::ranges::copy(infos, stages.begin()); BuildDescSetLayout(); @@ -46,32 +51,31 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul boost::container::static_vector vertex_bindings; boost::container::static_vector vertex_attributes; - if (!instance.IsVertexInputDynamicState()) { - const auto& vs_info = stages[u32(Shader::Stage::Vertex)]; - for (const auto& input : vs_info->vs_inputs) { - if (input.instance_step_rate == Shader::Info::VsInput::InstanceIdType::OverStepRate0 || - input.instance_step_rate == Shader::Info::VsInput::InstanceIdType::OverStepRate1) { + if (fetch_shader && !instance.IsVertexInputDynamicState()) { + const auto& vs_info = GetStage(Shader::Stage::Vertex); + for (const auto& attrib : fetch_shader->attributes) { + if (attrib.UsesStepRates()) { // Skip attribute binding as the data will be pulled by shader continue; } - const auto buffer = - vs_info->ReadUdReg(input.sgpr_base, input.dword_offset); + const auto buffer = attrib.GetSharp(vs_info); if (buffer.GetSize() == 0) { continue; } vertex_attributes.push_back({ - .location = input.binding, - .binding = input.binding, + .location = attrib.semantic, + .binding = attrib.semantic, .format = LiverpoolToVK::SurfaceFormat(buffer.GetDataFmt(), buffer.GetNumberFmt()), .offset = 0, }); vertex_bindings.push_back({ - .binding = input.binding, + .binding = attrib.semantic, .stride = buffer.GetStride(), - .inputRate = input.instance_step_rate == Shader::Info::VsInput::None - ? vk::VertexInputRate::eVertex - : vk::VertexInputRate::eInstance, + .inputRate = + attrib.GetStepRate() == Shader::Gcn::VertexAttribute::InstanceIdType::None + ? vk::VertexInputRate::eVertex + : vk::VertexInputRate::eInstance, }); } } diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index 4f4abfd16..91ffe4ea4 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -4,6 +4,7 @@ #include #include "common/types.h" +#include "shader_recompiler/frontend/fetch_shader.h" #include "video_core/renderer_vulkan/liverpool_to_vk.h" #include "video_core/renderer_vulkan/vk_common.h" #include "video_core/renderer_vulkan/vk_pipeline_common.h" @@ -59,9 +60,14 @@ public: GraphicsPipeline(const Instance& instance, Scheduler& scheduler, DescriptorHeap& desc_heap, const GraphicsPipelineKey& key, vk::PipelineCache pipeline_cache, std::span stages, + std::optional fetch_shader, std::span modules); ~GraphicsPipeline(); + const std::optional& GetFetchShader() const noexcept { + return fetch_shader; + } + bool IsEmbeddedVs() const noexcept { static constexpr size_t EmbeddedVsHash = 0x9b2da5cf47f8c29f; return key.stage_hashes[u32(Shader::Stage::Vertex)] == EmbeddedVsHash; @@ -94,6 +100,7 @@ private: private: GraphicsPipelineKey key; + std::optional fetch_shader{}; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 1c150ce28..49e4987db 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -265,6 +265,7 @@ bool Instance::CreateDevice() { const bool robustness = add_extension(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME); list_restart = add_extension(VK_EXT_PRIMITIVE_TOPOLOGY_LIST_RESTART_EXTENSION_NAME); maintenance5 = add_extension(VK_KHR_MAINTENANCE_5_EXTENSION_NAME); + legacy_vertex_attributes = add_extension(VK_EXT_LEGACY_VERTEX_ATTRIBUTES_EXTENSION_NAME); // These extensions are promoted by Vulkan 1.3, but for greater compatibility we use Vulkan 1.2 // with extensions. @@ -403,6 +404,9 @@ bool Instance::CreateDevice() { vk::PhysicalDeviceFragmentShaderBarycentricFeaturesKHR{ .fragmentShaderBarycentric = true, }, + vk::PhysicalDeviceLegacyVertexAttributesFeaturesEXT{ + .legacyVertexAttributes = true, + }, #ifdef __APPLE__ feature_chain.get(), #endif @@ -445,6 +449,9 @@ bool Instance::CreateDevice() { if (!fragment_shader_barycentric) { device_chain.unlink(); } + if (!legacy_vertex_attributes) { + device_chain.unlink(); + } auto [device_result, dev] = physical_device.createDeviceUnique(device_chain.get()); if (device_result != vk::Result::eSuccess) { diff --git a/src/video_core/renderer_vulkan/vk_instance.h b/src/video_core/renderer_vulkan/vk_instance.h index 5a46ef6fe..81303c9cc 100644 --- a/src/video_core/renderer_vulkan/vk_instance.h +++ b/src/video_core/renderer_vulkan/vk_instance.h @@ -148,10 +148,16 @@ public: return fragment_shader_barycentric; } + /// Returns true when VK_EXT_primitive_topology_list_restart is supported. bool IsListRestartSupported() const { return list_restart; } + /// Returns true when VK_EXT_legacy_vertex_attributes is supported. + bool IsLegacyVertexAttributesSupported() const { + return legacy_vertex_attributes; + } + /// Returns true when geometry shaders are supported by the device bool IsGeometryStageSupported() const { return features.geometryShader; @@ -320,6 +326,7 @@ private: bool null_descriptor{}; bool maintenance5{}; bool list_restart{}; + bool legacy_vertex_attributes{}; u64 min_imported_host_pointer_alignment{}; u32 subgroup_size{}; bool tooling_info{}; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index a1ed7edac..47713f0ff 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -169,6 +169,7 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_, .support_fp32_denorm_preserve = bool(vk12_props.shaderDenormPreserveFloat32), .support_fp32_denorm_flush = bool(vk12_props.shaderDenormFlushToZeroFloat32), .support_explicit_workgroup_layout = true, + .support_legacy_vertex_attributes = instance_.IsLegacyVertexAttributesSupported(), .needs_manual_interpolation = instance.IsFragmentShaderBarycentricSupported() && instance.GetDriverID() == vk::DriverId::eNvidiaProprietary, }; @@ -187,7 +188,7 @@ const GraphicsPipeline* PipelineCache::GetGraphicsPipeline() { const auto [it, is_new] = graphics_pipelines.try_emplace(graphics_key); if (is_new) { it.value() = graphics_pipeline_pool.Create(instance, scheduler, desc_heap, graphics_key, - *pipeline_cache, infos, modules); + *pipeline_cache, infos, fetch_shader, modules); } return it->second; } @@ -304,8 +305,12 @@ bool PipelineCache::RefreshGraphicsKey() { } auto params = Liverpool::GetParams(*pgm); - std::tie(infos[stage_out_idx], modules[stage_out_idx], key.stage_hashes[stage_out_idx]) = - GetProgram(stage_in, params, binding); + std::optional fetch_shader_; + std::tie(infos[stage_out_idx], modules[stage_out_idx], fetch_shader_, + key.stage_hashes[stage_out_idx]) = GetProgram(stage_in, params, binding); + if (fetch_shader_) { + fetch_shader = fetch_shader_; + } return true; }; @@ -341,16 +346,14 @@ bool PipelineCache::RefreshGraphicsKey() { } } - const auto* vs_info = infos[static_cast(Shader::Stage::Vertex)]; - if (vs_info && !instance.IsVertexInputDynamicState()) { + const auto vs_info = infos[static_cast(Shader::Stage::Vertex)]; + if (vs_info && fetch_shader && !instance.IsVertexInputDynamicState()) { u32 vertex_binding = 0; - for (const auto& input : vs_info->vs_inputs) { - if (input.instance_step_rate == Shader::Info::VsInput::InstanceIdType::OverStepRate0 || - input.instance_step_rate == Shader::Info::VsInput::InstanceIdType::OverStepRate1) { + for (const auto& attrib : fetch_shader->attributes) { + if (attrib.UsesStepRates()) { continue; } - const auto& buffer = - vs_info->ReadUdReg(input.sgpr_base, input.dword_offset); + const auto& buffer = attrib.GetSharp(*vs_info); if (buffer.GetSize() == 0) { continue; } @@ -394,7 +397,7 @@ bool PipelineCache::RefreshComputeKey() { Shader::Backend::Bindings binding{}; const auto* cs_pgm = &liverpool->regs.cs_program; const auto cs_params = Liverpool::GetParams(*cs_pgm); - std::tie(infos[0], modules[0], compute_key) = + std::tie(infos[0], modules[0], fetch_shader, compute_key) = GetProgram(Shader::Stage::Compute, cs_params, binding); return true; } @@ -425,24 +428,26 @@ vk::ShaderModule PipelineCache::CompileModule(Shader::Info& info, return module; } -std::tuple PipelineCache::GetProgram( - Shader::Stage stage, Shader::ShaderParams params, Shader::Backend::Bindings& binding) { +std::tuple, u64> +PipelineCache::GetProgram(Shader::Stage stage, Shader::ShaderParams params, + Shader::Backend::Bindings& binding) { const auto runtime_info = BuildRuntimeInfo(stage); auto [it_pgm, new_program] = program_cache.try_emplace(params.hash); if (new_program) { Program* program = program_pool.Create(stage, params); auto start = binding; const auto module = CompileModule(program->info, runtime_info, params.code, 0, binding); - const auto spec = Shader::StageSpecialization(program->info, runtime_info, start); + const auto spec = Shader::StageSpecialization(program->info, runtime_info, profile, start); program->AddPermut(module, std::move(spec)); it_pgm.value() = program; - return std::make_tuple(&program->info, module, HashCombine(params.hash, 0)); + return std::make_tuple(&program->info, module, spec.fetch_shader_data, + HashCombine(params.hash, 0)); } Program* program = it_pgm->second; auto& info = program->info; info.RefreshFlatBuf(); - const auto spec = Shader::StageSpecialization(info, runtime_info, binding); + const auto spec = Shader::StageSpecialization(info, runtime_info, profile, binding); size_t perm_idx = program->modules.size(); vk::ShaderModule module{}; @@ -456,7 +461,8 @@ std::tuple PipelineCache::GetProgram module = it->module; perm_idx = std::distance(program->modules.begin(), it); } - return std::make_tuple(&info, module, HashCombine(params.hash, perm_idx)); + return std::make_tuple(&info, module, spec.fetch_shader_data, + HashCombine(params.hash, perm_idx)); } void PipelineCache::DumpShader(std::span code, u64 hash, Shader::Stage stage, diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index 662bcbd80..e4a8abd4f 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h @@ -47,8 +47,10 @@ public: const ComputePipeline* GetComputePipeline(); - std::tuple GetProgram( - Shader::Stage stage, Shader::ShaderParams params, Shader::Backend::Bindings& binding); + std::tuple, + u64> + GetProgram(Shader::Stage stage, Shader::ShaderParams params, + Shader::Backend::Bindings& binding); private: bool RefreshGraphicsKey(); @@ -80,6 +82,7 @@ private: tsl::robin_map graphics_pipelines; std::array infos{}; std::array modules{}; + std::optional fetch_shader{}; GraphicsPipelineKey graphics_key{}; u64 compute_key{}; }; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index ff5e88141..084b7c345 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -187,13 +187,14 @@ void Rasterizer::Draw(bool is_indexed, u32 index_offset) { } const auto& vs_info = pipeline->GetStage(Shader::Stage::Vertex); - buffer_cache.BindVertexBuffers(vs_info); + const auto& fetch_shader = pipeline->GetFetchShader(); + buffer_cache.BindVertexBuffers(vs_info, fetch_shader); const u32 num_indices = buffer_cache.BindIndexBuffer(is_indexed, index_offset); BeginRendering(*pipeline, state); UpdateDynamicState(*pipeline); - const auto [vertex_offset, instance_offset] = vs_info.GetDrawOffsets(regs); + const auto [vertex_offset, instance_offset] = fetch_shader->GetDrawOffsets(regs, vs_info); const auto cmdbuf = scheduler.CommandBuffer(); cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline->Handle()); @@ -243,7 +244,8 @@ void Rasterizer::DrawIndirect(bool is_indexed, VAddr arg_address, u32 offset, u3 } const auto& vs_info = pipeline->GetStage(Shader::Stage::Vertex); - buffer_cache.BindVertexBuffers(vs_info); + const auto& fetch_shader = pipeline->GetFetchShader(); + buffer_cache.BindVertexBuffers(vs_info, fetch_shader); buffer_cache.BindIndexBuffer(is_indexed, 0); const auto& [buffer, base] = @@ -397,10 +399,8 @@ bool Rasterizer::BindResources(const Pipeline* pipeline) { if (!stage) { continue; } - if (stage->uses_step_rates) { - push_data.step0 = regs.vgt_instance_step_rate_0; - push_data.step1 = regs.vgt_instance_step_rate_1; - } + push_data.step0 = regs.vgt_instance_step_rate_0; + push_data.step1 = regs.vgt_instance_step_rate_1; stage->PushUd(binding, push_data); BindBuffers(*stage, binding, push_data, set_writes, buffer_barriers); diff --git a/src/video_core/texture_cache/image_view.cpp b/src/video_core/texture_cache/image_view.cpp index 488d44a7f..61cabdf11 100644 --- a/src/video_core/texture_cache/image_view.cpp +++ b/src/video_core/texture_cache/image_view.cpp @@ -87,12 +87,9 @@ ImageViewInfo::ImageViewInfo(const AmdGpu::Image& image, const Shader::ImageReso range.extent.levels = image.last_level - image.base_level + 1; } range.extent.layers = image.last_array - image.base_array + 1; - type = ConvertImageViewType(image.GetType()); + type = ConvertImageViewType(image.GetBoundType()); - // Adjust view type for partial cubemaps and arrays - if (image.IsPartialCubemap()) { - type = vk::ImageViewType::e2DArray; - } + // Adjust view type for arrays if (type == vk::ImageViewType::eCube) { if (desc.is_array) { type = vk::ImageViewType::eCubeArray; From c019b54fecff141bade82ef6efbbd1fdbc7d231a Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Wed, 4 Dec 2024 10:21:03 -0800 Subject: [PATCH 099/549] thread: Configure stack and guard on POSIX hosts. (#1664) --- src/core/libraries/kernel/threads/pthread.cpp | 2 +- src/core/thread.cpp | 4 +++- src/core/thread.h | 6 +++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/core/libraries/kernel/threads/pthread.cpp b/src/core/libraries/kernel/threads/pthread.cpp index 4629980c9..793ddd1fe 100644 --- a/src/core/libraries/kernel/threads/pthread.cpp +++ b/src/core/libraries/kernel/threads/pthread.cpp @@ -281,7 +281,7 @@ int PS4_SYSV_ABI posix_pthread_create_name_np(PthreadT* thread, const PthreadAtt /* Create thread */ new_thread->native_thr = Core::Thread(); - int ret = new_thread->native_thr.Create(RunThread, new_thread); + int ret = new_thread->native_thr.Create(RunThread, new_thread, &new_thread->attr); ASSERT_MSG(ret == 0, "Failed to create thread with error {}", ret); if (ret) { *thread = nullptr; diff --git a/src/core/thread.cpp b/src/core/thread.cpp index e9c46b522..a93f16c8d 100644 --- a/src/core/thread.cpp +++ b/src/core/thread.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "libraries/kernel/threads/pthread.h" #include "thread.h" #ifdef _WIN64 @@ -15,7 +16,7 @@ Thread::Thread() : native_handle{0} {} Thread::~Thread() {} -int Thread::Create(ThreadFunc func, void* arg) { +int Thread::Create(ThreadFunc func, void* arg, const ::Libraries::Kernel::PthreadAttr* attr) { #ifdef _WIN64 native_handle = CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)func, arg, 0, nullptr); return native_handle ? 0 : -1; @@ -23,6 +24,7 @@ int Thread::Create(ThreadFunc func, void* arg) { pthread_t* pthr = reinterpret_cast(&native_handle); pthread_attr_t pattr; pthread_attr_init(&pattr); + pthread_attr_setstack(&pattr, attr->stackaddr_attr, attr->stacksize_attr); return pthread_create(pthr, &pattr, (PthreadFunc)func, arg); #endif } diff --git a/src/core/thread.h b/src/core/thread.h index 8665100af..cfb8b8309 100644 --- a/src/core/thread.h +++ b/src/core/thread.h @@ -5,6 +5,10 @@ #include "common/types.h" +namespace Libraries::Kernel { +struct PthreadAttr; +} // namespace Libraries::Kernel + namespace Core { class Thread { @@ -15,7 +19,7 @@ public: Thread(); ~Thread(); - int Create(ThreadFunc func, void* arg); + int Create(ThreadFunc func, void* arg, const ::Libraries::Kernel::PthreadAttr* attr); void Exit(); uintptr_t GetHandle() { From 2380f2f9c9340fcf3b35cf9ff9ff24e3726e4d89 Mon Sep 17 00:00:00 2001 From: Vinicius Rangel Date: Thu, 5 Dec 2024 13:00:17 -0300 Subject: [PATCH 100/549] Virtual device abstraction (#1577) * IOFile: removes seek limit checks when file is writable * add virtual devices scaffold * add stdin/out/err as virtual devices * fixed some merging issues * clang-fix --------- Co-authored-by: georgemoralis --- CMakeLists.txt | 7 + src/common/io_file.cpp | 22 +-- src/common/io_file.h | 2 + src/common/logging/filter.cpp | 1 + src/common/logging/types.h | 1 + src/common/va_ctx.h | 111 +++++++++++++ src/core/devices/base_device.cpp | 12 ++ src/core/devices/base_device.h | 72 +++++++++ src/core/devices/ioccom.h | 67 ++++++++ src/core/devices/logger.cpp | 64 ++++++++ src/core/devices/logger.h | 37 +++++ src/core/devices/nop_device.h | 55 +++++++ src/core/file_sys/fs.cpp | 31 +++- src/core/file_sys/fs.h | 12 +- src/core/libraries/kernel/file_system.cpp | 186 ++++++++++++++++++---- src/core/libraries/kernel/kernel.cpp | 79 +++++---- src/core/libraries/kernel/process.cpp | 2 +- src/emulator.cpp | 3 + 18 files changed, 687 insertions(+), 77 deletions(-) create mode 100644 src/common/va_ctx.h create mode 100644 src/core/devices/base_device.cpp create mode 100644 src/core/devices/base_device.h create mode 100644 src/core/devices/ioccom.h create mode 100644 src/core/devices/logger.cpp create mode 100644 src/core/devices/logger.h create mode 100644 src/core/devices/nop_device.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f503832c..56760be37 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -502,6 +502,7 @@ set(COMMON src/common/logging/backend.cpp src/common/types.h src/common/uint128.h src/common/unique_function.h + src/common/va_ctx.h src/common/version.h src/common/ntapi.h src/common/ntapi.cpp @@ -526,6 +527,12 @@ set(CORE src/core/aerolib/stubs.cpp src/core/crypto/crypto.cpp src/core/crypto/crypto.h src/core/crypto/keys.h + src/core/devices/base_device.cpp + src/core/devices/base_device.h + src/core/devices/ioccom.h + src/core/devices/logger.cpp + src/core/devices/logger.h + src/core/devices/nop_device.h src/core/file_format/pfs.h src/core/file_format/pkg.cpp src/core/file_format/pkg.h diff --git a/src/common/io_file.cpp b/src/common/io_file.cpp index dd3a40cae..067010a26 100644 --- a/src/common/io_file.cpp +++ b/src/common/io_file.cpp @@ -377,16 +377,18 @@ bool IOFile::Seek(s64 offset, SeekOrigin origin) const { return false; } - u64 size = GetSize(); - if (origin == SeekOrigin::CurrentPosition && Tell() + offset > size) { - LOG_ERROR(Common_Filesystem, "Seeking past the end of the file"); - return false; - } else if (origin == SeekOrigin::SetOrigin && (u64)offset > size) { - LOG_ERROR(Common_Filesystem, "Seeking past the end of the file"); - return false; - } else if (origin == SeekOrigin::End && offset > 0) { - LOG_ERROR(Common_Filesystem, "Seeking past the end of the file"); - return false; + if (False(file_access_mode & (FileAccessMode::Write | FileAccessMode::Append))) { + u64 size = GetSize(); + if (origin == SeekOrigin::CurrentPosition && Tell() + offset > size) { + LOG_ERROR(Common_Filesystem, "Seeking past the end of the file"); + return false; + } else if (origin == SeekOrigin::SetOrigin && (u64)offset > size) { + LOG_ERROR(Common_Filesystem, "Seeking past the end of the file"); + return false; + } else if (origin == SeekOrigin::End && offset > 0) { + LOG_ERROR(Common_Filesystem, "Seeking past the end of the file"); + return false; + } } errno = 0; diff --git a/src/common/io_file.h b/src/common/io_file.h index 8fed4981f..feb2110ac 100644 --- a/src/common/io_file.h +++ b/src/common/io_file.h @@ -10,6 +10,7 @@ #include "common/concepts.h" #include "common/types.h" +#include "enum.h" namespace Common::FS { @@ -42,6 +43,7 @@ enum class FileAccessMode { */ ReadAppend = Read | Append, }; +DECLARE_ENUM_FLAG_OPERATORS(FileAccessMode); enum class FileType { BinaryFile, diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index 632b2b329..75c61a188 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -69,6 +69,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Common, Memory) \ CLS(Core) \ SUB(Core, Linker) \ + SUB(Core, Devices) \ CLS(Config) \ CLS(Debug) \ CLS(Kernel) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index e7e91882a..a0e7d021f 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -35,6 +35,7 @@ enum class Class : u8 { Common_Memory, ///< Memory mapping and management functions Core, ///< LLE emulation core Core_Linker, ///< The module linker + Core_Devices, ///< Devices emulation Config, ///< Emulator configuration (including commandline) Debug, ///< Debugging tools Kernel, ///< The HLE implementation of the PS4 kernel. diff --git a/src/common/va_ctx.h b/src/common/va_ctx.h new file mode 100644 index 000000000..e0b8c0bab --- /dev/null +++ b/src/common/va_ctx.h @@ -0,0 +1,111 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + +#include +#include "common/types.h" + +#define VA_ARGS \ + uint64_t rdi, uint64_t rsi, uint64_t rdx, uint64_t rcx, uint64_t r8, uint64_t r9, \ + uint64_t overflow_arg_area, __m128 xmm0, __m128 xmm1, __m128 xmm2, __m128 xmm3, \ + __m128 xmm4, __m128 xmm5, __m128 xmm6, __m128 xmm7, ... + +#define VA_CTX(ctx) \ + alignas(16)::Common::VaCtx ctx{}; \ + (ctx).reg_save_area.gp[0] = rdi; \ + (ctx).reg_save_area.gp[1] = rsi; \ + (ctx).reg_save_area.gp[2] = rdx; \ + (ctx).reg_save_area.gp[3] = rcx; \ + (ctx).reg_save_area.gp[4] = r8; \ + (ctx).reg_save_area.gp[5] = r9; \ + (ctx).reg_save_area.fp[0] = xmm0; \ + (ctx).reg_save_area.fp[1] = xmm1; \ + (ctx).reg_save_area.fp[2] = xmm2; \ + (ctx).reg_save_area.fp[3] = xmm3; \ + (ctx).reg_save_area.fp[4] = xmm4; \ + (ctx).reg_save_area.fp[5] = xmm5; \ + (ctx).reg_save_area.fp[6] = xmm6; \ + (ctx).reg_save_area.fp[7] = xmm7; \ + (ctx).va_list.reg_save_area = &(ctx).reg_save_area; \ + (ctx).va_list.gp_offset = offsetof(::Common::VaRegSave, gp); \ + (ctx).va_list.fp_offset = offsetof(::Common::VaRegSave, fp); \ + (ctx).va_list.overflow_arg_area = &overflow_arg_area; + +namespace Common { + +// https://stackoverflow.com/questions/4958384/what-is-the-format-of-the-x86-64-va-list-structure + +struct VaList { + u32 gp_offset; + u32 fp_offset; + void* overflow_arg_area; + void* reg_save_area; +}; + +struct VaRegSave { + u64 gp[6]; + __m128 fp[8]; +}; + +struct VaCtx { + VaRegSave reg_save_area; + VaList va_list; +}; + +template +T vaArgRegSaveAreaGp(VaList* l) { + auto* addr = reinterpret_cast(static_cast(l->reg_save_area) + l->gp_offset); + l->gp_offset += Size; + return *addr; +} +template +T vaArgOverflowArgArea(VaList* l) { + auto ptr = ((reinterpret_cast(l->overflow_arg_area) + (Align - 1)) & ~(Align - 1)); + auto* addr = reinterpret_cast(ptr); + l->overflow_arg_area = reinterpret_cast(ptr + Size); + return *addr; +} + +template +T vaArgRegSaveAreaFp(VaList* l) { + auto* addr = reinterpret_cast(static_cast(l->reg_save_area) + l->fp_offset); + l->fp_offset += Size; + return *addr; +} + +inline int vaArgInteger(VaList* l) { + if (l->gp_offset <= 40) { + return vaArgRegSaveAreaGp(l); + } + return vaArgOverflowArgArea(l); +} + +inline long long vaArgLongLong(VaList* l) { + if (l->gp_offset <= 40) { + return vaArgRegSaveAreaGp(l); + } + return vaArgOverflowArgArea(l); +} +inline long vaArgLong(VaList* l) { + if (l->gp_offset <= 40) { + return vaArgRegSaveAreaGp(l); + } + return vaArgOverflowArgArea(l); +} + +inline double vaArgDouble(VaList* l) { + if (l->fp_offset <= 160) { + return vaArgRegSaveAreaFp(l); + } + return vaArgOverflowArgArea(l); +} + +template +T* vaArgPtr(VaList* l) { + if (l->gp_offset <= 40) { + return vaArgRegSaveAreaGp(l); + } + return vaArgOverflowArgArea(l); +} + +} // namespace Common diff --git a/src/core/devices/base_device.cpp b/src/core/devices/base_device.cpp new file mode 100644 index 000000000..4f91c81c7 --- /dev/null +++ b/src/core/devices/base_device.cpp @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "base_device.h" + +namespace Core::Devices { + +BaseDevice::BaseDevice() = default; + +BaseDevice::~BaseDevice() = default; + +} // namespace Core::Devices \ No newline at end of file diff --git a/src/core/devices/base_device.h b/src/core/devices/base_device.h new file mode 100644 index 000000000..351af82b4 --- /dev/null +++ b/src/core/devices/base_device.h @@ -0,0 +1,72 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "common/types.h" +#include "common/va_ctx.h" + +namespace Libraries::Kernel { +struct OrbisKernelStat; +struct SceKernelIovec; +} // namespace Libraries::Kernel + +namespace Core::Devices { + +class BaseDevice { +public: + explicit BaseDevice(); + + virtual ~BaseDevice() = 0; + + virtual int ioctl(u64 cmd, Common::VaCtx* args) { + return ORBIS_KERNEL_ERROR_ENOTTY; + } + + virtual s64 write(const void* buf, size_t nbytes) { + return ORBIS_KERNEL_ERROR_EBADF; + } + + virtual size_t readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { + return ORBIS_KERNEL_ERROR_EBADF; + } + + virtual size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { + return ORBIS_KERNEL_ERROR_EBADF; + } + + virtual s64 preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) { + return ORBIS_KERNEL_ERROR_EBADF; + } + + virtual s64 lseek(s64 offset, int whence) { + return ORBIS_KERNEL_ERROR_EBADF; + } + + virtual s64 read(void* buf, size_t nbytes) { + return ORBIS_KERNEL_ERROR_EBADF; + } + + virtual int fstat(Libraries::Kernel::OrbisKernelStat* sb) { + return ORBIS_KERNEL_ERROR_EBADF; + } + + virtual s32 fsync() { + return ORBIS_KERNEL_ERROR_EBADF; + } + + virtual int ftruncate(s64 length) { + return ORBIS_KERNEL_ERROR_EBADF; + } + + virtual int getdents(void* buf, u32 nbytes, s64* basep) { + return ORBIS_KERNEL_ERROR_EBADF; + } + + virtual s64 pwrite(const void* buf, size_t nbytes, u64 offset) { + return ORBIS_KERNEL_ERROR_EBADF; + } +}; + +} // namespace Core::Devices diff --git a/src/core/devices/ioccom.h b/src/core/devices/ioccom.h new file mode 100644 index 000000000..671ee33d4 --- /dev/null +++ b/src/core/devices/ioccom.h @@ -0,0 +1,67 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +/*- + * Copyright (c) 1982, 1986, 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ioccom.h 8.2 (Berkeley) 3/28/94 + * $FreeBSD$ + */ + +#define IOCPARM_SHIFT 13 /* number of bits for ioctl size */ +#define IOCPARM_MASK ((1 << IOCPARM_SHIFT) - 1) /* parameter length mask */ +#define IOCPARM_LEN(x) (((x) >> 16) & IOCPARM_MASK) +#define IOCBASECMD(x) ((x) & ~(IOCPARM_MASK << 16)) +#define IOCGROUP(x) (((x) >> 8) & 0xff) + +#define IOCPARM_MAX (1 << IOCPARM_SHIFT) /* max size of ioctl */ +#define IOC_VOID 0x20000000 /* no parameters */ +#define IOC_OUT 0x40000000 /* copy out parameters */ +#define IOC_IN 0x80000000 /* copy in parameters */ +#define IOC_INOUT (IOC_IN | IOC_OUT) +#define IOC_DIRMASK (IOC_VOID | IOC_OUT | IOC_IN) + +#define _IOC(inout, group, num, len) \ + ((unsigned long)((inout) | (((len) & IOCPARM_MASK) << 16) | ((group) << 8) | (num))) +#define _IO(g, n) _IOC(IOC_VOID, (g), (n), 0) +#define _IOWINT(g, n) _IOC(IOC_VOID, (g), (n), sizeof(int)) +#define _IOR(g, n, t) _IOC(IOC_OUT, (g), (n), sizeof(t)) +#define _IOW(g, n, t) _IOC(IOC_IN, (g), (n), sizeof(t)) +/* this should be _IORW, but stdio got there first */ +#define _IOWR(g, n, t) _IOC(IOC_INOUT, (g), (n), sizeof(t)) + +/* +# Simple parse of ioctl cmd +def parse(v): + print('inout', (v >> 24 & 0xFF)) + print('len', hex(v >> 16 & 0xFF)) + print('group', chr(v >> 8 & 0xFF)) + print('num', hex(v & 0xFF)) +*/ diff --git a/src/core/devices/logger.cpp b/src/core/devices/logger.cpp new file mode 100644 index 000000000..bf5a28382 --- /dev/null +++ b/src/core/devices/logger.cpp @@ -0,0 +1,64 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/libraries/kernel/file_system.h" +#include "logger.h" + +namespace Core::Devices { + +Logger::Logger(std::string prefix, bool is_err) : prefix(std::move(prefix)), is_err(is_err) {} + +Logger::~Logger() = default; + +s64 Logger::write(const void* buf, size_t nbytes) { + log(static_cast(buf), nbytes); + return nbytes; +} +size_t Logger::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { + for (int i = 0; i < iovcnt; i++) { + log(static_cast(iov[i].iov_base), iov[i].iov_len); + } + return iovcnt; +} + +s64 Logger::pwrite(const void* buf, size_t nbytes, u64 offset) { + log(static_cast(buf), nbytes); + return nbytes; +} + +s32 Logger::fsync() { + log_flush(); + return 0; +} + +void Logger::log(const char* buf, size_t nbytes) { + std::scoped_lock lock{mtx}; + const char* end = buf + nbytes; + for (const char* it = buf; it < end; ++it) { + char c = *it; + if (c == '\r') { + continue; + } + if (c == '\n') { + log_flush(); + continue; + } + buffer.push_back(c); + } +} + +void Logger::log_flush() { + std::scoped_lock lock{mtx}; + if (buffer.empty()) { + return; + } + if (is_err) { + LOG_ERROR(Tty, "[{}] {}", prefix, std::string_view{buffer}); + } else { + LOG_INFO(Tty, "[{}] {}", prefix, std::string_view{buffer}); + } + buffer.clear(); +} + +} // namespace Core::Devices \ No newline at end of file diff --git a/src/core/devices/logger.h b/src/core/devices/logger.h new file mode 100644 index 000000000..bfb07f337 --- /dev/null +++ b/src/core/devices/logger.h @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "base_device.h" + +#include +#include +#include + +namespace Core::Devices { + +class Logger final : BaseDevice { + std::string prefix; + bool is_err; + + std::recursive_mutex mtx; + std::vector buffer; + +public: + explicit Logger(std::string prefix, bool is_err); + + ~Logger() override; + + s64 write(const void* buf, size_t nbytes) override; + size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override; + s64 pwrite(const void* buf, size_t nbytes, u64 offset) override; + + s32 fsync() override; + +private: + void log(const char* buf, size_t nbytes); + void log_flush(); +}; + +} // namespace Core::Devices diff --git a/src/core/devices/nop_device.h b/src/core/devices/nop_device.h new file mode 100644 index 000000000..a75b92f1b --- /dev/null +++ b/src/core/devices/nop_device.h @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once +#include "base_device.h" + +namespace Core::Devices { + +class NopDevice final : BaseDevice { + u32 handle; + +public: + explicit NopDevice(u32 handle) : handle(handle) {} + + ~NopDevice() override = default; + + int ioctl(u64 cmd, Common::VaCtx* args) override { + return 0; + } + s64 write(const void* buf, size_t nbytes) override { + return 0; + } + size_t readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override { + return 0; + } + size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override { + return 0; + } + s64 preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) override { + return 0; + } + s64 lseek(s64 offset, int whence) override { + return 0; + } + s64 read(void* buf, size_t nbytes) override { + return 0; + } + int fstat(Libraries::Kernel::OrbisKernelStat* sb) override { + return 0; + } + s32 fsync() override { + return 0; + } + int ftruncate(s64 length) override { + return 0; + } + int getdents(void* buf, u32 nbytes, s64* basep) override { + return 0; + } + s64 pwrite(const void* buf, size_t nbytes, u64 offset) override { + return 0; + } +}; + +} // namespace Core::Devices diff --git a/src/core/file_sys/fs.cpp b/src/core/file_sys/fs.cpp index 769940cf0..0fdbb2783 100644 --- a/src/core/file_sys/fs.cpp +++ b/src/core/file_sys/fs.cpp @@ -4,12 +4,12 @@ #include #include "common/config.h" #include "common/string_util.h" +#include "core/devices/logger.h" +#include "core/devices/nop_device.h" #include "core/file_sys/fs.h" namespace Core::FileSys { -constexpr int RESERVED_HANDLES = 3; // First 3 handles are stdin,stdout,stderr - void MntPoints::Mount(const std::filesystem::path& host_folder, const std::string& guest_folder, bool read_only) { std::scoped_lock lock{m_mutex}; @@ -135,7 +135,6 @@ int HandleTable::CreateHandle() { std::scoped_lock lock{m_mutex}; auto* file = new File{}; - file->is_directory = false; file->is_opened = false; int existingFilesNum = m_files.size(); @@ -143,23 +142,23 @@ int HandleTable::CreateHandle() { for (int index = 0; index < existingFilesNum; index++) { if (m_files.at(index) == nullptr) { m_files[index] = file; - return index + RESERVED_HANDLES; + return index; } } m_files.push_back(file); - return m_files.size() + RESERVED_HANDLES - 1; + return m_files.size() - 1; } void HandleTable::DeleteHandle(int d) { std::scoped_lock lock{m_mutex}; - delete m_files.at(d - RESERVED_HANDLES); - m_files[d - RESERVED_HANDLES] = nullptr; + delete m_files.at(d); + m_files[d] = nullptr; } File* HandleTable::GetFile(int d) { std::scoped_lock lock{m_mutex}; - return m_files.at(d - RESERVED_HANDLES); + return m_files.at(d); } File* HandleTable::GetFile(const std::filesystem::path& host_name) { @@ -171,4 +170,20 @@ File* HandleTable::GetFile(const std::filesystem::path& host_name) { return nullptr; } +void HandleTable::CreateStdHandles() { + auto setup = [this](const char* path, auto* device) { + int fd = CreateHandle(); + auto* file = GetFile(fd); + file->is_opened = true; + file->type = FileType::Device; + file->m_guest_name = path; + file->device = + std::shared_ptr{reinterpret_cast(device)}; + }; + // order matters + setup("/dev/stdin", new Devices::NopDevice(0)); // stdin + setup("/dev/stdout", new Devices::Logger("stdout", false)); // stdout + setup("/dev/stderr", new Devices::Logger("stderr", true)); // stderr +} + } // namespace Core::FileSys diff --git a/src/core/file_sys/fs.h b/src/core/file_sys/fs.h index eeaeaf781..b0153c162 100644 --- a/src/core/file_sys/fs.h +++ b/src/core/file_sys/fs.h @@ -9,6 +9,7 @@ #include #include #include "common/io_file.h" +#include "core/devices/base_device.h" namespace Core::FileSys { @@ -55,15 +56,22 @@ struct DirEntry { bool isFile; }; +enum class FileType { + Regular, // standard file + Directory, + Device, +}; + struct File { std::atomic_bool is_opened{}; - std::atomic_bool is_directory{}; + std::atomic type{FileType::Regular}; std::filesystem::path m_host_name; std::string m_guest_name; Common::FS::IOFile f; std::vector dirents; u32 dirents_index; std::mutex m_mutex; + std::shared_ptr device; // only valid for type == Device }; class HandleTable { @@ -76,6 +84,8 @@ public: File* GetFile(int d); File* GetFile(const std::filesystem::path& host_name); + void CreateStdHandles(); + private: std::vector m_files; std::mutex m_mutex; diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index 1b95e5270..447467cb7 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -11,6 +11,39 @@ #include "core/libraries/libs.h" #include "kernel.h" +#include +#include + +#include "core/devices/logger.h" +#include "core/devices/nop_device.h" + +namespace D = Core::Devices; +using FactoryDevice = std::function(u32, const char*, int, u16)>; + +#define GET_DEVICE_FD(fd) \ + [](u32, const char*, int, u16) { \ + return Common::Singleton::Instance()->GetFile(fd)->device; \ + } + +// prefix path, only dev devices +static std::map available_device = { + // clang-format off + {"/dev/stdin", GET_DEVICE_FD(0)}, + {"/dev/stdout", GET_DEVICE_FD(1)}, + {"/dev/stderr", GET_DEVICE_FD(2)}, + + {"/dev/fd/0", GET_DEVICE_FD(0)}, + {"/dev/fd/1", GET_DEVICE_FD(1)}, + {"/dev/fd/2", GET_DEVICE_FD(2)}, + + {"/dev/deci_stdin", GET_DEVICE_FD(0)}, + {"/dev/deci_stdout", GET_DEVICE_FD(1)}, + {"/dev/deci_stderr", GET_DEVICE_FD(2)}, + + {"/dev/null", GET_DEVICE_FD(0)}, // fd0 (stdin) is a nop device + // clang-format on +}; + namespace Libraries::Kernel { auto GetDirectoryEntries(const std::filesystem::path& path) { @@ -24,8 +57,8 @@ auto GetDirectoryEntries(const std::filesystem::path& path) { return files; } -int PS4_SYSV_ABI sceKernelOpen(const char* path, int flags, u16 mode) { - LOG_INFO(Kernel_Fs, "path = {} flags = {:#x} mode = {}", path, flags, mode); +int PS4_SYSV_ABI sceKernelOpen(const char* raw_path, int flags, u16 mode) { + LOG_INFO(Kernel_Fs, "path = {} flags = {:#x} mode = {}", raw_path, flags, mode); auto* h = Common::Singleton::Instance(); auto* mnt = Common::Singleton::Instance(); @@ -44,22 +77,35 @@ int PS4_SYSV_ABI sceKernelOpen(const char* path, int flags, u16 mode) { bool direct = (flags & ORBIS_KERNEL_O_DIRECT) != 0; bool directory = (flags & ORBIS_KERNEL_O_DIRECTORY) != 0; - if (std::string_view{path} == "/dev/console") { + std::string_view path{raw_path}; + + if (path == "/dev/console") { return 2000; } - if (std::string_view{path} == "/dev/deci_tty6") { + if (path == "/dev/deci_tty6") { return 2001; } - if (std::string_view{path} == "/dev/stdout") { - return 2002; - } - if (std::string_view{path} == "/dev/urandom") { + if (path == "/dev/urandom") { return 2003; } + u32 handle = h->CreateHandle(); auto* file = h->GetFile(handle); + + if (path.starts_with("/dev/")) { + for (const auto& [prefix, factory] : available_device) { + if (path.starts_with(prefix)) { + file->is_opened = true; + file->type = Core::FileSys::FileType::Device; + file->m_guest_name = path; + file->device = factory(handle, path.data(), flags, mode); + return handle; + } + } + } + if (directory) { - file->is_directory = true; + file->type = Core::FileSys::FileType::Directory; file->m_guest_name = path; file->m_host_name = mnt->GetHostPath(file->m_guest_name); if (!std::filesystem::is_directory(file->m_host_name)) { // directory doesn't exist @@ -135,11 +181,12 @@ int PS4_SYSV_ABI sceKernelClose(int d) { if (file == nullptr) { return ORBIS_KERNEL_ERROR_EBADF; } - if (!file->is_directory) { + if (file->type == Core::FileSys::FileType::Regular) { file->f.Close(); } file->is_opened = false; LOG_INFO(Kernel_Fs, "Closing {}", file->m_guest_name); + // FIXME: Lock file mutex before deleting it? h->DeleteHandle(d); return ORBIS_OK; } @@ -155,14 +202,6 @@ int PS4_SYSV_ABI posix_close(int d) { } size_t PS4_SYSV_ABI sceKernelWrite(int d, const void* buf, size_t nbytes) { - if (d <= 2) { // stdin,stdout,stderr - char* str = strdup((const char*)buf); - if (str[nbytes - 1] == '\n') - str[nbytes - 1] = 0; - LOG_INFO(Tty, "{}", str); - free(str); - return nbytes; - } auto* h = Common::Singleton::Instance(); auto* file = h->GetFile(d); if (file == nullptr) { @@ -170,6 +209,9 @@ size_t PS4_SYSV_ABI sceKernelWrite(int d, const void* buf, size_t nbytes) { } std::scoped_lock lk{file->m_mutex}; + if (file->type == Core::FileSys::FileType::Device) { + return file->device->write(buf, nbytes); + } return file->f.WriteRaw(buf, nbytes); } @@ -207,17 +249,63 @@ int PS4_SYSV_ABI sceKernelUnlink(const char* path) { size_t PS4_SYSV_ABI _readv(int d, const SceKernelIovec* iov, int iovcnt) { auto* h = Common::Singleton::Instance(); auto* file = h->GetFile(d); - size_t total_read = 0; + if (file == nullptr) { + return ORBIS_KERNEL_ERROR_EBADF; + } + std::scoped_lock lk{file->m_mutex}; + if (file->type == Core::FileSys::FileType::Device) { + int r = file->device->readv(iov, iovcnt); + if (r < 0) { + ErrSceToPosix(r); + return -1; + } + return r; + } + size_t total_read = 0; for (int i = 0; i < iovcnt; i++) { total_read += file->f.ReadRaw(iov[i].iov_base, iov[i].iov_len); } return total_read; } +size_t PS4_SYSV_ABI _writev(int fd, const SceKernelIovec* iov, int iovcn) { + if (fd == 1) { + size_t total_written = 0; + for (int i = 0; i < iovcn; i++) { + total_written += ::fwrite(iov[i].iov_base, 1, iov[i].iov_len, stdout); + } + return total_written; + } + auto* h = Common::Singleton::Instance(); + auto* file = h->GetFile(fd); + if (file == nullptr) { + return ORBIS_KERNEL_ERROR_EBADF; + } + + std::scoped_lock lk{file->m_mutex}; + + if (file->type == Core::FileSys::FileType::Device) { + return file->device->writev(iov, iovcn); + } + size_t total_written = 0; + for (int i = 0; i < iovcn; i++) { + total_written += file->f.WriteRaw(iov[i].iov_base, iov[i].iov_len); + } + return total_written; +} + s64 PS4_SYSV_ABI sceKernelLseek(int d, s64 offset, int whence) { auto* h = Common::Singleton::Instance(); auto* file = h->GetFile(d); + if (file == nullptr) { + return ORBIS_KERNEL_ERROR_EBADF; + } + + std::scoped_lock lk{file->m_mutex}; + if (file->type == Core::FileSys::FileType::Device) { + return file->device->lseek(offset, whence); + } Common::FS::SeekOrigin origin{}; if (whence == 0) { @@ -228,7 +316,6 @@ s64 PS4_SYSV_ABI sceKernelLseek(int d, s64 offset, int whence) { origin = Common::FS::SeekOrigin::End; } - std::scoped_lock lk{file->m_mutex}; if (!file->f.Seek(offset, origin)) { LOG_CRITICAL(Kernel_Fs, "sceKernelLseek: failed to seek"); return ORBIS_KERNEL_ERROR_EINVAL; @@ -261,6 +348,9 @@ s64 PS4_SYSV_ABI sceKernelRead(int d, void* buf, size_t nbytes) { } std::scoped_lock lk{file->m_mutex}; + if (file->type == Core::FileSys::FileType::Device) { + return file->device->read(buf, nbytes); + } return file->f.ReadRaw(buf, nbytes); } @@ -409,7 +499,13 @@ int PS4_SYSV_ABI posix_stat(const char* path, OrbisKernelStat* sb) { int PS4_SYSV_ABI sceKernelCheckReachability(const char* path) { auto* mnt = Common::Singleton::Instance(); - const auto path_name = mnt->GetHostPath(path); + std::string_view guest_path{path}; + for (const auto& prefix : available_device | std::views::keys) { + if (guest_path.starts_with(prefix)) { + return ORBIS_OK; + } + } + const auto path_name = mnt->GetHostPath(guest_path); if (!std::filesystem::exists(path_name)) { return ORBIS_KERNEL_ERROR_ENOENT; } @@ -431,6 +527,10 @@ s64 PS4_SYSV_ABI sceKernelPreadv(int d, SceKernelIovec* iov, int iovcnt, s64 off } std::scoped_lock lk{file->m_mutex}; + if (file->type == Core::FileSys::FileType::Device) { + return file->device->preadv(iov, iovcnt, offset); + } + const s64 pos = file->f.Tell(); SCOPE_EXIT { file->f.Seek(pos); @@ -466,18 +566,25 @@ int PS4_SYSV_ABI sceKernelFStat(int fd, OrbisKernelStat* sb) { } std::memset(sb, 0, sizeof(OrbisKernelStat)); - if (file->is_directory) { - sb->st_mode = 0000777u | 0040000u; - sb->st_size = 0; - sb->st_blksize = 512; - sb->st_blocks = 0; - // TODO incomplete - } else { + switch (file->type) { + case Core::FileSys::FileType::Device: + return file->device->fstat(sb); + case Core::FileSys::FileType::Regular: sb->st_mode = 0000777u | 0100000u; sb->st_size = file->f.GetSize(); sb->st_blksize = 512; sb->st_blocks = (sb->st_size + 511) / 512; // TODO incomplete + break; + case Core::FileSys::FileType::Directory: + sb->st_mode = 0000777u | 0040000u; + sb->st_size = 0; + sb->st_blksize = 512; + sb->st_blocks = 0; + // TODO incomplete + break; + default: + UNREACHABLE(); } return ORBIS_OK; } @@ -495,6 +602,13 @@ int PS4_SYSV_ABI posix_fstat(int fd, OrbisKernelStat* sb) { s32 PS4_SYSV_ABI sceKernelFsync(int fd) { auto* h = Common::Singleton::Instance(); auto* file = h->GetFile(fd); + if (file == nullptr) { + return ORBIS_KERNEL_ERROR_EBADF; + } + + if (file->type == Core::FileSys::FileType::Device) { + return file->device->fsync(); + } file->f.Flush(); return ORBIS_OK; } @@ -517,6 +631,10 @@ int PS4_SYSV_ABI sceKernelFtruncate(int fd, s64 length) { return ORBIS_KERNEL_ERROR_EBADF; } + if (file->type == Core::FileSys::FileType::Device) { + return file->device->ftruncate(length); + } + if (file->m_host_name.empty()) { return ORBIS_KERNEL_ERROR_EACCES; } @@ -538,10 +656,15 @@ static int GetDents(int fd, char* buf, int nbytes, s64* basep) { if (file == nullptr) { return ORBIS_KERNEL_ERROR_EBADF; } + if (file->type != Core::FileSys::FileType::Device) { + return file->device->getdents(buf, nbytes, basep); + } + if (file->dirents_index == file->dirents.size()) { return ORBIS_OK; } - if (!file->is_directory || nbytes < 512 || file->dirents_index > file->dirents.size()) { + if (file->type != Core::FileSys::FileType::Directory || nbytes < 512 || + file->dirents_index > file->dirents.size()) { return ORBIS_KERNEL_ERROR_EINVAL; } const auto& entry = file->dirents.at(file->dirents_index++); @@ -586,6 +709,10 @@ s64 PS4_SYSV_ABI sceKernelPwrite(int d, void* buf, size_t nbytes, s64 offset) { } std::scoped_lock lk{file->m_mutex}; + + if (file->type == Core::FileSys::FileType::Device) { + return file->device->pwrite(buf, nbytes, offset); + } const s64 pos = file->f.Tell(); SCOPE_EXIT { file->f.Seek(pos); @@ -637,6 +764,7 @@ void RegisterFileSystem(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("4wSze92BhLI", "libkernel", 1, "libkernel", 1, 1, sceKernelWrite); LIB_FUNCTION("+WRlkKjZvag", "libkernel", 1, "libkernel", 1, 1, _readv); + LIB_FUNCTION("YSHRBRLn2pI", "libkernel", 1, "libkernel", 1, 1, _writev); LIB_FUNCTION("Oy6IpwgtYOk", "libkernel", 1, "libkernel", 1, 1, posix_lseek); LIB_FUNCTION("Oy6IpwgtYOk", "libScePosix", 1, "libkernel", 1, 1, posix_lseek); LIB_FUNCTION("oib76F-12fk", "libkernel", 1, "libkernel", 1, 1, sceKernelLseek); diff --git a/src/core/libraries/kernel/kernel.cpp b/src/core/libraries/kernel/kernel.cpp index 4028116ef..0b4e89fc7 100644 --- a/src/core/libraries/kernel/kernel.cpp +++ b/src/core/libraries/kernel/kernel.cpp @@ -9,6 +9,9 @@ #include "common/logging/log.h" #include "common/polyfill_thread.h" #include "common/thread.h" +#include "common/va_ctx.h" +#include "core/file_sys/fs.h" +#include "core/libraries/error_codes.h" #include "core/libraries/kernel/equeue.h" #include "core/libraries/kernel/file_system.h" #include "core/libraries/kernel/kernel.h" @@ -24,6 +27,7 @@ #ifdef _WIN64 #include #endif +#include namespace Libraries::Kernel { @@ -65,19 +69,6 @@ static PS4_SYSV_ABI void stack_chk_fail() { UNREACHABLE(); } -struct iovec { - void* iov_base; /* Base address. */ - size_t iov_len; /* Length. */ -}; - -size_t PS4_SYSV_ABI _writev(int fd, const struct iovec* iov, int iovcn) { - size_t total_written = 0; - for (int i = 0; i < iovcn; i++) { - total_written += ::fwrite(iov[i].iov_base, 1, iov[i].iov_len, stdout); - } - return total_written; -} - static thread_local int g_posix_errno = 0; int* PS4_SYSV_ABI __Error() { @@ -142,24 +133,33 @@ void PS4_SYSV_ABI sceLibcHeapGetTraceInfo(HeapInfoInfo* info) { } s64 PS4_SYSV_ABI ps4__write(int d, const char* buf, std::size_t nbytes) { - if (d <= 2) { // stdin,stdout,stderr - std::string_view str{buf}; - if (str[nbytes - 1] == '\n') { - str = str.substr(0, nbytes - 1); - } - LOG_INFO(Tty, "{}", str); - return nbytes; + auto* h = Common::Singleton::Instance(); + auto* file = h->GetFile(d); + if (file == nullptr) { + return ORBIS_KERNEL_ERROR_EBADF; } - LOG_ERROR(Kernel, "(STUBBED) called d = {} nbytes = {} ", d, nbytes); - UNREACHABLE(); - return ORBIS_OK; + std::scoped_lock lk{file->m_mutex}; + if (file->type == Core::FileSys::FileType::Device) { + return file->device->write(buf, nbytes); + } + return file->f.WriteRaw(buf, nbytes); } s64 PS4_SYSV_ABI ps4__read(int d, void* buf, u64 nbytes) { - ASSERT_MSG(d == 0, "d is not 0!"); - - return static_cast( - strlen(std::fgets(static_cast(buf), static_cast(nbytes), stdin))); + if (d == 0) { + return static_cast( + strlen(std::fgets(static_cast(buf), static_cast(nbytes), stdin))); + } + auto* h = Common::Singleton::Instance(); + auto* file = h->GetFile(d); + if (file == nullptr) { + return ORBIS_KERNEL_ERROR_EBADF; + } + std::scoped_lock lk{file->m_mutex}; + if (file->type == Core::FileSys::FileType::Device) { + return file->device->read(buf, nbytes); + } + return file->f.ReadRaw(buf, nbytes); } struct OrbisKernelUuid { @@ -189,6 +189,29 @@ int PS4_SYSV_ABI sceKernelUuidCreate(OrbisKernelUuid* orbisUuid) { return 0; } +int PS4_SYSV_ABI kernel_ioctl(int fd, u64 cmd, VA_ARGS) { + auto* h = Common::Singleton::Instance(); + auto* file = h->GetFile(fd); + if (file == nullptr) { + LOG_INFO(Lib_Kernel, "ioctl: fd = {:X} cmd = {:X} file == nullptr", fd, cmd); + g_posix_errno = POSIX_EBADF; + return -1; + } + if (file->type != Core::FileSys::FileType::Device) { + LOG_WARNING(Lib_Kernel, "ioctl: fd = {:X} cmd = {:X} file->type != Device", fd, cmd); + g_posix_errno = ENOTTY; + return -1; + } + VA_CTX(ctx); + int result = file->device->ioctl(cmd, &ctx); + LOG_TRACE(Lib_Kernel, "ioctl: fd = {:X} cmd = {:X} result = {}", fd, cmd, result); + if (result < 0) { + ErrSceToPosix(result); + return -1; + } + return result; +} + const char* PS4_SYSV_ABI sceKernelGetFsSandboxRandomWord() { const char* path = "sys"; return path; @@ -219,13 +242,13 @@ void RegisterKernel(Core::Loader::SymbolsResolver* sym) { Libraries::Kernel::RegisterException(sym); LIB_OBJ("f7uOxY9mM1U", "libkernel", 1, "libkernel", 1, 1, &g_stack_chk_guard); + LIB_FUNCTION("PfccT7qURYE", "libkernel", 1, "libkernel", 1, 1, kernel_ioctl); LIB_FUNCTION("JGfTMBOdUJo", "libkernel", 1, "libkernel", 1, 1, sceKernelGetFsSandboxRandomWord); LIB_FUNCTION("XVL8So3QJUk", "libkernel", 1, "libkernel", 1, 1, posix_connect); LIB_FUNCTION("6xVpy0Fdq+I", "libkernel", 1, "libkernel", 1, 1, _sigprocmask); LIB_FUNCTION("Xjoosiw+XPI", "libkernel", 1, "libkernel", 1, 1, sceKernelUuidCreate); LIB_FUNCTION("Ou3iL1abvng", "libkernel", 1, "libkernel", 1, 1, stack_chk_fail); LIB_FUNCTION("9BcDykPmo1I", "libkernel", 1, "libkernel", 1, 1, __Error); - LIB_FUNCTION("YSHRBRLn2pI", "libkernel", 1, "libkernel", 1, 1, _writev); LIB_FUNCTION("DRuBt2pvICk", "libkernel", 1, "libkernel", 1, 1, ps4__read); LIB_FUNCTION("k+AXqu2-eBc", "libkernel", 1, "libkernel", 1, 1, posix_getpagesize); LIB_FUNCTION("k+AXqu2-eBc", "libScePosix", 1, "libkernel", 1, 1, posix_getpagesize); diff --git a/src/core/libraries/kernel/process.cpp b/src/core/libraries/kernel/process.cpp index 15e4ff820..a657ddf98 100644 --- a/src/core/libraries/kernel/process.cpp +++ b/src/core/libraries/kernel/process.cpp @@ -20,7 +20,7 @@ int PS4_SYSV_ABI sceKernelIsNeoMode() { int PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(int* ver) { int version = Common::ElfInfo::Instance().RawFirmwareVer(); *ver = version; - return (version > 0) ? ORBIS_OK : ORBIS_KERNEL_ERROR_EINVAL; + return (version >= 0) ? ORBIS_OK : ORBIS_KERNEL_ERROR_EINVAL; } int PS4_SYSV_ABI sceKernelGetCpumode() { diff --git a/src/emulator.cpp b/src/emulator.cpp index 1d2542d2b..60d6e18d7 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -75,6 +75,9 @@ Emulator::Emulator() { LOG_INFO(Config, "Vulkan rdocMarkersEnable: {}", Config::vkMarkersEnabled()); LOG_INFO(Config, "Vulkan crashDiagnostics: {}", Config::vkCrashDiagnosticEnabled()); + // Create stdin/stdout/stderr + Common::Singleton::Instance()->CreateStdHandles(); + // Defer until after logging is initialized. memory = Core::Memory::Instance(); controller = Common::Singleton::Instance(); From 98f0cb65d757a6fb5877ec66573a48ed0bc3b994 Mon Sep 17 00:00:00 2001 From: "Daniel R." <47796739+polybiusproxy@users.noreply.github.com> Date: Thu, 5 Dec 2024 17:21:35 +0100 Subject: [PATCH 101/549] The way to Unity, pt.1 (#1659) --- src/common/ntapi.cpp | 6 + src/common/ntapi.h | 417 +++++++++++++++++- src/core/libraries/kernel/threads/pthread.cpp | 31 +- src/core/libraries/kernel/threads/pthread.h | 2 +- src/core/memory.cpp | 10 +- src/core/thread.cpp | 104 ++++- src/core/thread.h | 21 +- src/video_core/amdgpu/liverpool.cpp | 2 +- 8 files changed, 564 insertions(+), 29 deletions(-) diff --git a/src/common/ntapi.cpp b/src/common/ntapi.cpp index 0fe797e09..ffdedb17f 100644 --- a/src/common/ntapi.cpp +++ b/src/common/ntapi.cpp @@ -5,8 +5,11 @@ #include "ntapi.h" +NtClose_t NtClose = nullptr; NtDelayExecution_t NtDelayExecution = nullptr; NtSetInformationFile_t NtSetInformationFile = nullptr; +NtCreateThread_t NtCreateThread = nullptr; +NtTerminateThread_t NtTerminateThread = nullptr; namespace Common::NtApi { @@ -14,9 +17,12 @@ void Initialize() { HMODULE nt_handle = GetModuleHandleA("ntdll.dll"); // http://stackoverflow.com/a/31411628/4725495 + NtClose = (NtClose_t)GetProcAddress(nt_handle, "NtClose"); NtDelayExecution = (NtDelayExecution_t)GetProcAddress(nt_handle, "NtDelayExecution"); NtSetInformationFile = (NtSetInformationFile_t)GetProcAddress(nt_handle, "NtSetInformationFile"); + NtCreateThread = (NtCreateThread_t)GetProcAddress(nt_handle, "NtCreateThread"); + NtTerminateThread = (NtTerminateThread_t)GetProcAddress(nt_handle, "NtTerminateThread"); } } // namespace Common::NtApi diff --git a/src/common/ntapi.h b/src/common/ntapi.h index 17d353403..743174061 100644 --- a/src/common/ntapi.h +++ b/src/common/ntapi.h @@ -108,14 +108,427 @@ typedef struct _FILE_DISPOSITION_INFORMATION { BOOLEAN DeleteFile; } FILE_DISPOSITION_INFORMATION, *PFILE_DISPOSITION_INFORMATION; -typedef u32(__stdcall* NtDelayExecution_t)(BOOL Alertable, PLARGE_INTEGER DelayInterval); +typedef struct _UNICODE_STRING { + USHORT Length; + USHORT MaximumLength; + PWCH Buffer; +} UNICODE_STRING, *PUNICODE_STRING; -typedef u32(__stdcall* NtSetInformationFile_t)(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, +typedef const UNICODE_STRING* PCUNICODE_STRING; + +typedef struct _OBJECT_ATTRIBUTES { + ULONG Length; + HANDLE RootDirectory; + PCUNICODE_STRING ObjectName; + ULONG Attributes; + PVOID SecurityDescriptor; // PSECURITY_DESCRIPTOR; + PVOID SecurityQualityOfService; // PSECURITY_QUALITY_OF_SERVICE +} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES; + +typedef const OBJECT_ATTRIBUTES* PCOBJECT_ATTRIBUTES; + +typedef struct _CLIENT_ID { + HANDLE UniqueProcess; + HANDLE UniqueThread; +} CLIENT_ID, *PCLIENT_ID; + +typedef struct _INITIAL_TEB { + struct { + PVOID OldStackBase; + PVOID OldStackLimit; + } OldInitialTeb; + PVOID StackBase; + PVOID StackLimit; + PVOID StackAllocationBase; +} INITIAL_TEB, *PINITIAL_TEB; + +typedef struct _PEB_LDR_DATA { + ULONG Length; + BOOLEAN Initialized; + PVOID SsHandle; + LIST_ENTRY InLoadOrderModuleList; + LIST_ENTRY InMemoryOrderModuleList; + LIST_ENTRY InInitializationOrderModuleList; + PVOID EntryInProgress; + BOOLEAN ShutdownInProgress; + HANDLE ShutdownThreadId; +} PEB_LDR_DATA, *PPEB_LDR_DATA; + +typedef struct _CURDIR { + UNICODE_STRING DosPath; + PVOID Handle; +} CURDIR, *PCURDIR; + +typedef struct RTL_DRIVE_LETTER_CURDIR { + USHORT Flags; + USHORT Length; + ULONG TimeStamp; + UNICODE_STRING DosPath; +} RTL_DRIVE_LETTER_CURDIR, *PRTL_DRIVE_LETTER_CURDIR; + +typedef struct _RTL_USER_PROCESS_PARAMETERS { + ULONG AllocationSize; + ULONG Size; + ULONG Flags; + ULONG DebugFlags; + HANDLE ConsoleHandle; + ULONG ConsoleFlags; + HANDLE hStdInput; + HANDLE hStdOutput; + HANDLE hStdError; + CURDIR CurrentDirectory; + UNICODE_STRING DllPath; + UNICODE_STRING ImagePathName; + UNICODE_STRING CommandLine; + PWSTR Environment; + ULONG dwX; + ULONG dwY; + ULONG dwXSize; + ULONG dwYSize; + ULONG dwXCountChars; + ULONG dwYCountChars; + ULONG dwFillAttribute; + ULONG dwFlags; + ULONG wShowWindow; + UNICODE_STRING WindowTitle; + UNICODE_STRING Desktop; + UNICODE_STRING ShellInfo; + UNICODE_STRING RuntimeInfo; + RTL_DRIVE_LETTER_CURDIR DLCurrentDirectory[0x20]; + ULONG_PTR EnvironmentSize; + ULONG_PTR EnvironmentVersion; + PVOID PackageDependencyData; + ULONG ProcessGroupId; + ULONG LoaderThreads; +} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS; + +typedef struct tagRTL_BITMAP { + ULONG SizeOfBitMap; + PULONG Buffer; +} RTL_BITMAP, *PRTL_BITMAP; + +typedef struct { + UINT next; + UINT id; + ULONGLONG addr; + ULONGLONG size; + UINT args[4]; +} CROSS_PROCESS_WORK_ENTRY; + +typedef union { + struct { + UINT first; + UINT counter; + }; + volatile LONGLONG hdr; +} CROSS_PROCESS_WORK_HDR; + +typedef struct { + CROSS_PROCESS_WORK_HDR free_list; + CROSS_PROCESS_WORK_HDR work_list; + ULONGLONG unknown[4]; + CROSS_PROCESS_WORK_ENTRY entries[1]; +} CROSS_PROCESS_WORK_LIST; + +typedef struct _CHPEV2_PROCESS_INFO { + ULONG Wow64ExecuteFlags; /* 000 */ + USHORT NativeMachineType; /* 004 */ + USHORT EmulatedMachineType; /* 006 */ + HANDLE SectionHandle; /* 008 */ + CROSS_PROCESS_WORK_LIST* CrossProcessWorkList; /* 010 */ + void* unknown; /* 018 */ +} CHPEV2_PROCESS_INFO, *PCHPEV2_PROCESS_INFO; + +typedef u64(__stdcall* KERNEL_CALLBACK_PROC)(void*, ULONG); + +typedef struct _PEB { /* win32/win64 */ + BOOLEAN InheritedAddressSpace; /* 000/000 */ + BOOLEAN ReadImageFileExecOptions; /* 001/001 */ + BOOLEAN BeingDebugged; /* 002/002 */ + UCHAR ImageUsedLargePages : 1; /* 003/003 */ + UCHAR IsProtectedProcess : 1; + UCHAR IsImageDynamicallyRelocated : 1; + UCHAR SkipPatchingUser32Forwarders : 1; + UCHAR IsPackagedProcess : 1; + UCHAR IsAppContainer : 1; + UCHAR IsProtectedProcessLight : 1; + UCHAR IsLongPathAwareProcess : 1; + HANDLE Mutant; /* 004/008 */ + HMODULE ImageBaseAddress; /* 008/010 */ + PPEB_LDR_DATA LdrData; /* 00c/018 */ + RTL_USER_PROCESS_PARAMETERS* ProcessParameters; /* 010/020 */ + PVOID SubSystemData; /* 014/028 */ + HANDLE ProcessHeap; /* 018/030 */ + PRTL_CRITICAL_SECTION FastPebLock; /* 01c/038 */ + PVOID AtlThunkSListPtr; /* 020/040 */ + PVOID IFEOKey; /* 024/048 */ + ULONG ProcessInJob : 1; /* 028/050 */ + ULONG ProcessInitializing : 1; + ULONG ProcessUsingVEH : 1; + ULONG ProcessUsingVCH : 1; + ULONG ProcessUsingFTH : 1; + ULONG ProcessPreviouslyThrottled : 1; + ULONG ProcessCurrentlyThrottled : 1; + ULONG ProcessImagesHotPatched : 1; + ULONG ReservedBits0 : 24; + KERNEL_CALLBACK_PROC* KernelCallbackTable; /* 02c/058 */ + ULONG Reserved; /* 030/060 */ + ULONG AtlThunkSListPtr32; /* 034/064 */ + PVOID ApiSetMap; /* 038/068 */ + ULONG TlsExpansionCounter; /* 03c/070 */ + PRTL_BITMAP TlsBitmap; /* 040/078 */ + ULONG TlsBitmapBits[2]; /* 044/080 */ + PVOID ReadOnlySharedMemoryBase; /* 04c/088 */ + PVOID SharedData; /* 050/090 */ + PVOID* ReadOnlyStaticServerData; /* 054/098 */ + PVOID AnsiCodePageData; /* 058/0a0 */ + PVOID OemCodePageData; /* 05c/0a8 */ + PVOID UnicodeCaseTableData; /* 060/0b0 */ + ULONG NumberOfProcessors; /* 064/0b8 */ + ULONG NtGlobalFlag; /* 068/0bc */ + LARGE_INTEGER CriticalSectionTimeout; /* 070/0c0 */ + SIZE_T HeapSegmentReserve; /* 078/0c8 */ + SIZE_T HeapSegmentCommit; /* 07c/0d0 */ + SIZE_T HeapDeCommitTotalFreeThreshold; /* 080/0d8 */ + SIZE_T HeapDeCommitFreeBlockThreshold; /* 084/0e0 */ + ULONG NumberOfHeaps; /* 088/0e8 */ + ULONG MaximumNumberOfHeaps; /* 08c/0ec */ + PVOID* ProcessHeaps; /* 090/0f0 */ + PVOID GdiSharedHandleTable; /* 094/0f8 */ + PVOID ProcessStarterHelper; /* 098/100 */ + PVOID GdiDCAttributeList; /* 09c/108 */ + PVOID LoaderLock; /* 0a0/110 */ + ULONG OSMajorVersion; /* 0a4/118 */ + ULONG OSMinorVersion; /* 0a8/11c */ + ULONG OSBuildNumber; /* 0ac/120 */ + ULONG OSPlatformId; /* 0b0/124 */ + ULONG ImageSubSystem; /* 0b4/128 */ + ULONG ImageSubSystemMajorVersion; /* 0b8/12c */ + ULONG ImageSubSystemMinorVersion; /* 0bc/130 */ + KAFFINITY ActiveProcessAffinityMask; /* 0c0/138 */ +#ifdef _WIN64 + ULONG GdiHandleBuffer[60]; /* /140 */ +#else + ULONG GdiHandleBuffer[34]; /* 0c4/ */ +#endif + PVOID PostProcessInitRoutine; /* 14c/230 */ + PRTL_BITMAP TlsExpansionBitmap; /* 150/238 */ + ULONG TlsExpansionBitmapBits[32]; /* 154/240 */ + ULONG SessionId; /* 1d4/2c0 */ + ULARGE_INTEGER AppCompatFlags; /* 1d8/2c8 */ + ULARGE_INTEGER AppCompatFlagsUser; /* 1e0/2d0 */ + PVOID ShimData; /* 1e8/2d8 */ + PVOID AppCompatInfo; /* 1ec/2e0 */ + UNICODE_STRING CSDVersion; /* 1f0/2e8 */ + PVOID ActivationContextData; /* 1f8/2f8 */ + PVOID ProcessAssemblyStorageMap; /* 1fc/300 */ + PVOID SystemDefaultActivationData; /* 200/308 */ + PVOID SystemAssemblyStorageMap; /* 204/310 */ + SIZE_T MinimumStackCommit; /* 208/318 */ + PVOID* FlsCallback; /* 20c/320 */ + LIST_ENTRY FlsListHead; /* 210/328 */ + union { + PRTL_BITMAP FlsBitmap; /* 218/338 */ +#ifdef _WIN64 + CHPEV2_PROCESS_INFO* ChpeV2ProcessInfo; /* /338 */ +#endif + }; + ULONG FlsBitmapBits[4]; /* 21c/340 */ + ULONG FlsHighIndex; /* 22c/350 */ + PVOID WerRegistrationData; /* 230/358 */ + PVOID WerShipAssertPtr; /* 234/360 */ + PVOID EcCodeBitMap; /* 238/368 */ + PVOID pImageHeaderHash; /* 23c/370 */ + ULONG HeapTracingEnabled : 1; /* 240/378 */ + ULONG CritSecTracingEnabled : 1; + ULONG LibLoaderTracingEnabled : 1; + ULONG SpareTracingBits : 29; + ULONGLONG CsrServerReadOnlySharedMemoryBase; /* 248/380 */ + ULONG TppWorkerpListLock; /* 250/388 */ + LIST_ENTRY TppWorkerpList; /* 254/390 */ + PVOID WaitOnAddressHashTable[0x80]; /* 25c/3a0 */ + PVOID TelemetryCoverageHeader; /* 45c/7a0 */ + ULONG CloudFileFlags; /* 460/7a8 */ + ULONG CloudFileDiagFlags; /* 464/7ac */ + CHAR PlaceholderCompatibilityMode; /* 468/7b0 */ + CHAR PlaceholderCompatibilityModeReserved[7]; /* 469/7b1 */ + PVOID LeapSecondData; /* 470/7b8 */ + ULONG LeapSecondFlags; /* 474/7c0 */ + ULONG NtGlobalFlag2; /* 478/7c4 */ +} PEB, *PPEB; + +typedef struct _RTL_ACTIVATION_CONTEXT_STACK_FRAME { + struct _RTL_ACTIVATION_CONTEXT_STACK_FRAME* Previous; + struct _ACTIVATION_CONTEXT* ActivationContext; + ULONG Flags; +} RTL_ACTIVATION_CONTEXT_STACK_FRAME, *PRTL_ACTIVATION_CONTEXT_STACK_FRAME; + +typedef struct _ACTIVATION_CONTEXT_STACK { + RTL_ACTIVATION_CONTEXT_STACK_FRAME* ActiveFrame; + LIST_ENTRY FrameListCache; + ULONG Flags; + ULONG NextCookieSequenceNumber; + ULONG_PTR StackId; +} ACTIVATION_CONTEXT_STACK, *PACTIVATION_CONTEXT_STACK; + +typedef struct _GDI_TEB_BATCH { + ULONG Offset; + HANDLE HDC; + ULONG Buffer[0x136]; +} GDI_TEB_BATCH; + +typedef struct _TEB_ACTIVE_FRAME_CONTEXT { + ULONG Flags; + const char* FrameName; +} TEB_ACTIVE_FRAME_CONTEXT, *PTEB_ACTIVE_FRAME_CONTEXT; + +typedef struct _TEB_ACTIVE_FRAME { + ULONG Flags; + struct _TEB_ACTIVE_FRAME* Previous; + TEB_ACTIVE_FRAME_CONTEXT* Context; +} TEB_ACTIVE_FRAME, *PTEB_ACTIVE_FRAME; + +typedef struct _TEB { /* win32/win64 */ + NT_TIB Tib; /* 000/0000 */ + PVOID EnvironmentPointer; /* 01c/0038 */ + CLIENT_ID ClientId; /* 020/0040 */ + PVOID ActiveRpcHandle; /* 028/0050 */ + PVOID ThreadLocalStoragePointer; /* 02c/0058 */ + PPEB Peb; /* 030/0060 */ + ULONG LastErrorValue; /* 034/0068 */ + ULONG CountOfOwnedCriticalSections; /* 038/006c */ + PVOID CsrClientThread; /* 03c/0070 */ + PVOID Win32ThreadInfo; /* 040/0078 */ + ULONG User32Reserved[26]; /* 044/0080 */ + ULONG UserReserved[5]; /* 0ac/00e8 */ + PVOID WOW32Reserved; /* 0c0/0100 */ + ULONG CurrentLocale; /* 0c4/0108 */ + ULONG FpSoftwareStatusRegister; /* 0c8/010c */ + PVOID ReservedForDebuggerInstrumentation[16]; /* 0cc/0110 */ +#ifdef _WIN64 + PVOID SystemReserved1[30]; /* /0190 */ +#else + PVOID SystemReserved1[26]; /* 10c/ used for krnl386 private data in Wine */ +#endif + char PlaceholderCompatibilityMode; /* 174/0280 */ + BOOLEAN PlaceholderHydrationAlwaysExplicit; /* 175/0281 */ + char PlaceholderReserved[10]; /* 176/0282 */ + DWORD ProxiedProcessId; /* 180/028c */ + ACTIVATION_CONTEXT_STACK ActivationContextStack; /* 184/0290 */ + UCHAR WorkingOnBehalfOfTicket[8]; /* 19c/02b8 */ + LONG ExceptionCode; /* 1a4/02c0 */ + ACTIVATION_CONTEXT_STACK* ActivationContextStackPointer; /* 1a8/02c8 */ + ULONG_PTR InstrumentationCallbackSp; /* 1ac/02d0 */ + ULONG_PTR InstrumentationCallbackPreviousPc; /* 1b0/02d8 */ + ULONG_PTR InstrumentationCallbackPreviousSp; /* 1b4/02e0 */ +#ifdef _WIN64 + ULONG TxFsContext; /* /02e8 */ + BOOLEAN InstrumentationCallbackDisabled; /* /02ec */ + BOOLEAN UnalignedLoadStoreExceptions; /* /02ed */ +#else + BOOLEAN InstrumentationCallbackDisabled; /* 1b8/ */ + BYTE SpareBytes1[23]; /* 1b9/ */ + ULONG TxFsContext; /* 1d0/ */ +#endif + GDI_TEB_BATCH GdiTebBatch; /* 1d4/02f0 used for ntdll private data in Wine */ + CLIENT_ID RealClientId; /* 6b4/07d8 */ + HANDLE GdiCachedProcessHandle; /* 6bc/07e8 */ + ULONG GdiClientPID; /* 6c0/07f0 */ + ULONG GdiClientTID; /* 6c4/07f4 */ + PVOID GdiThreadLocaleInfo; /* 6c8/07f8 */ + ULONG_PTR Win32ClientInfo[62]; /* 6cc/0800 used for user32 private data in Wine */ + PVOID glDispatchTable[233]; /* 7c4/09f0 */ + PVOID glReserved1[29]; /* b68/1138 */ + PVOID glReserved2; /* bdc/1220 */ + PVOID glSectionInfo; /* be0/1228 */ + PVOID glSection; /* be4/1230 */ + PVOID glTable; /* be8/1238 */ + PVOID glCurrentRC; /* bec/1240 */ + PVOID glContext; /* bf0/1248 */ + ULONG LastStatusValue; /* bf4/1250 */ + UNICODE_STRING StaticUnicodeString; /* bf8/1258 */ + WCHAR StaticUnicodeBuffer[261]; /* c00/1268 */ + PVOID DeallocationStack; /* e0c/1478 */ + PVOID TlsSlots[64]; /* e10/1480 */ + LIST_ENTRY TlsLinks; /* f10/1680 */ + PVOID Vdm; /* f18/1690 */ + PVOID ReservedForNtRpc; /* f1c/1698 */ + PVOID DbgSsReserved[2]; /* f20/16a0 */ + ULONG HardErrorMode; /* f28/16b0 */ +#ifdef _WIN64 + PVOID Instrumentation[11]; /* /16b8 */ +#else + PVOID Instrumentation[9]; /* f2c/ */ +#endif + GUID ActivityId; /* f50/1710 */ + PVOID SubProcessTag; /* f60/1720 */ + PVOID PerflibData; /* f64/1728 */ + PVOID EtwTraceData; /* f68/1730 */ + PVOID WinSockData; /* f6c/1738 */ + ULONG GdiBatchCount; /* f70/1740 */ + ULONG IdealProcessorValue; /* f74/1744 */ + ULONG GuaranteedStackBytes; /* f78/1748 */ + PVOID ReservedForPerf; /* f7c/1750 */ + PVOID ReservedForOle; /* f80/1758 */ + ULONG WaitingOnLoaderLock; /* f84/1760 */ + PVOID SavedPriorityState; /* f88/1768 */ + ULONG_PTR ReservedForCodeCoverage; /* f8c/1770 */ + PVOID ThreadPoolData; /* f90/1778 */ + PVOID* TlsExpansionSlots; /* f94/1780 */ +#ifdef _WIN64 + union { + PVOID DeallocationBStore; /* /1788 */ + PVOID* ChpeV2CpuAreaInfo; /* /1788 */ + } DUMMYUNIONNAME; + PVOID BStoreLimit; /* /1790 */ +#endif + ULONG MuiGeneration; /* f98/1798 */ + ULONG IsImpersonating; /* f9c/179c */ + PVOID NlsCache; /* fa0/17a0 */ + PVOID ShimData; /* fa4/17a8 */ + ULONG HeapVirtualAffinity; /* fa8/17b0 */ + PVOID CurrentTransactionHandle; /* fac/17b8 */ + TEB_ACTIVE_FRAME* ActiveFrame; /* fb0/17c0 */ + PVOID* FlsSlots; /* fb4/17c8 */ + PVOID PreferredLanguages; /* fb8/17d0 */ + PVOID UserPrefLanguages; /* fbc/17d8 */ + PVOID MergedPrefLanguages; /* fc0/17e0 */ + ULONG MuiImpersonation; /* fc4/17e8 */ + USHORT CrossTebFlags; /* fc8/17ec */ + USHORT SameTebFlags; /* fca/17ee */ + PVOID TxnScopeEnterCallback; /* fcc/17f0 */ + PVOID TxnScopeExitCallback; /* fd0/17f8 */ + PVOID TxnScopeContext; /* fd4/1800 */ + ULONG LockCount; /* fd8/1808 */ + LONG WowTebOffset; /* fdc/180c */ + PVOID ResourceRetValue; /* fe0/1810 */ + PVOID ReservedForWdf; /* fe4/1818 */ + ULONGLONG ReservedForCrt; /* fe8/1820 */ + GUID EffectiveContainerId; /* ff0/1828 */ +} TEB, *PTEB; +static_assert(offsetof(TEB, DeallocationStack) == + 0x1478); /* The only member we care about at the moment */ + +typedef u64(__stdcall* NtClose_t)(HANDLE Handle); + +typedef u64(__stdcall* NtDelayExecution_t)(BOOL Alertable, PLARGE_INTEGER DelayInterval); + +typedef u64(__stdcall* NtSetInformationFile_t)(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length, FILE_INFORMATION_CLASS FileInformationClass); +typedef u64(__stdcall* NtCreateThread_t)(PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess, + PCOBJECT_ATTRIBUTES ObjectAttributes, HANDLE ProcessHandle, + PCLIENT_ID ClientId, PCONTEXT ThreadContext, + PINITIAL_TEB InitialTeb, BOOLEAN CreateSuspended); + +typedef u64(__stdcall* NtTerminateThread_t)(HANDLE ThreadHandle, u64 ExitStatus); + +extern NtClose_t NtClose; extern NtDelayExecution_t NtDelayExecution; extern NtSetInformationFile_t NtSetInformationFile; +extern NtCreateThread_t NtCreateThread; +extern NtTerminateThread_t NtTerminateThread; namespace Common::NtApi { void Initialize(); diff --git a/src/core/libraries/kernel/threads/pthread.cpp b/src/core/libraries/kernel/threads/pthread.cpp index 793ddd1fe..a562c51b2 100644 --- a/src/core/libraries/kernel/threads/pthread.cpp +++ b/src/core/libraries/kernel/threads/pthread.cpp @@ -206,6 +206,7 @@ static void RunThread(void* arg) { DebugState.AddCurrentThreadToGuestList(); /* Run the current thread's start routine with argument: */ + curthread->native_thr.Initialize(); void* ret = Core::ExecuteGuest(curthread->start_routine, curthread->arg); /* Remove thread from tracking */ @@ -280,7 +281,7 @@ int PS4_SYSV_ABI posix_pthread_create_name_np(PthreadT* thread, const PthreadAtt (*thread) = new_thread; /* Create thread */ - new_thread->native_thr = Core::Thread(); + new_thread->native_thr = Core::NativeThread(); int ret = new_thread->native_thr.Create(RunThread, new_thread, &new_thread->attr); ASSERT_MSG(ret == 0, "Failed to create thread with error {}", ret); if (ret) { @@ -412,6 +413,33 @@ int PS4_SYSV_ABI posix_pthread_getschedparam(PthreadT pthread, SchedPolicy* poli return 0; } +int PS4_SYSV_ABI posix_pthread_setschedparam(PthreadT pthread, SchedPolicy policy, + const SchedParam* param) { + if (pthread == nullptr || param == nullptr) { + return POSIX_EINVAL; + } + + auto* thread_state = ThrState::Instance(); + if (pthread == g_curthread) { + g_curthread->lock.lock(); + } else if (int ret = thread_state->FindThread(pthread, /*include dead*/ 0); ret != 0) { + return ret; + } + + if (pthread->attr.sched_policy == policy && + (policy == SchedPolicy::Other || pthread->attr.prio == param->sched_priority)) { + pthread->attr.prio = param->sched_priority; + pthread->lock.unlock(); + return 0; + } + + // TODO: _thr_setscheduler + pthread->attr.sched_policy = policy; + pthread->attr.prio = param->sched_priority; + pthread->lock.unlock(); + return 0; +} + int PS4_SYSV_ABI scePthreadGetprio(PthreadT thread, int* priority) { SchedParam param; SchedPolicy policy; @@ -495,6 +523,7 @@ void RegisterThread(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("lZzFeSxPl08", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setcancelstate); LIB_FUNCTION("a2P9wYGeZvc", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setprio); LIB_FUNCTION("FIs3-UQT9sg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_getschedparam); + LIB_FUNCTION("Xs9hdiD7sAA", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setschedparam); LIB_FUNCTION("6XG4B33N09g", "libScePosix", 1, "libkernel", 1, 1, sched_yield); // Posix-Kernel diff --git a/src/core/libraries/kernel/threads/pthread.h b/src/core/libraries/kernel/threads/pthread.h index b41ca2abd..9d71c75e8 100644 --- a/src/core/libraries/kernel/threads/pthread.h +++ b/src/core/libraries/kernel/threads/pthread.h @@ -259,7 +259,7 @@ struct Pthread { int refcount; PthreadEntryFunc start_routine; void* arg; - Core::Thread native_thr; + Core::NativeThread native_thr; PthreadAttr attr; bool cancel_enable; bool cancel_pending; diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 15fde2a57..3e1cd441f 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -328,7 +328,7 @@ int MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, size_t size, Mem } // Map the file. - impl.MapFile(mapped_addr, size, offset, std::bit_cast(prot), fd); + impl.MapFile(mapped_addr, size_aligned, offset, std::bit_cast(prot), fd); // Add virtual memory area auto& new_vma = CarveVMA(mapped_addr, size_aligned)->second; @@ -512,9 +512,8 @@ int MemoryManager::VirtualQuery(VAddr addr, int flags, info->is_flexible.Assign(vma.type == VMAType::Flexible); info->is_direct.Assign(vma.type == VMAType::Direct); info->is_stack.Assign(vma.type == VMAType::Stack); - info->is_pooled.Assign(vma.type == VMAType::Pooled); - info->is_committed.Assign(vma.type != VMAType::Free && vma.type != VMAType::Reserved && - vma.type != VMAType::PoolReserved); + info->is_pooled.Assign(vma.type == VMAType::PoolReserved); + info->is_committed.Assign(vma.type == VMAType::Pooled); vma.name.copy(info->name.data(), std::min(info->name.size(), vma.name.size())); if (vma.type == VMAType::Direct) { const auto dmem_it = FindDmemArea(vma.phys_base); @@ -585,6 +584,7 @@ void MemoryManager::NameVirtualRange(VAddr virtual_addr, size_t size, std::strin "Range provided is not fully contained in vma"); it->second.name = name; } + VAddr MemoryManager::SearchFree(VAddr virtual_addr, size_t size, u32 alignment) { // If the requested address is below the mapped range, start search from the lowest address auto min_search_address = impl.SystemManagedVirtualBase(); @@ -691,7 +691,7 @@ MemoryManager::DMemHandle MemoryManager::Split(DMemHandle dmem_handle, size_t of new_area.size -= offset_in_area; return dmem_map.emplace_hint(std::next(dmem_handle), new_area.base, new_area); -}; +} int MemoryManager::GetDirectMemoryType(PAddr addr, int* directMemoryTypeOut, void** directMemoryStartOut, void** directMemoryEndOut) { diff --git a/src/core/thread.cpp b/src/core/thread.cpp index a93f16c8d..f87e3c8dc 100644 --- a/src/core/thread.cpp +++ b/src/core/thread.cpp @@ -4,45 +4,125 @@ #include "libraries/kernel/threads/pthread.h" #include "thread.h" +#include "core/libraries/kernel/threads/pthread.h" + #ifdef _WIN64 #include +#include "common/ntapi.h" #else #include #endif namespace Core { -Thread::Thread() : native_handle{0} {} - -Thread::~Thread() {} - -int Thread::Create(ThreadFunc func, void* arg, const ::Libraries::Kernel::PthreadAttr* attr) { #ifdef _WIN64 - native_handle = CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)func, arg, 0, nullptr); - return native_handle ? 0 : -1; -#else +#define KGDT64_R3_DATA (0x28) +#define KGDT64_R3_CODE (0x30) +#define KGDT64_R3_CMTEB (0x50) +#define RPL_MASK (0x03) + +#define INITIAL_FPUCW (0x037f) +#define INITIAL_MXCSR_MASK (0xffbf) +#define EFLAGS_INTERRUPT_MASK (0x200) + +void InitializeTeb(INITIAL_TEB* teb, const ::Libraries::Kernel::PthreadAttr* attr) { + teb->StackBase = (void*)((u64)attr->stackaddr_attr + attr->stacksize_attr); + teb->StackLimit = nullptr; + teb->StackAllocationBase = attr->stackaddr_attr; +} + +void InitializeContext(CONTEXT* ctx, ThreadFunc func, void* arg, + const ::Libraries::Kernel::PthreadAttr* attr) { + /* Note: The stack has to be reversed */ + ctx->Rsp = (u64)attr->stackaddr_attr + attr->stacksize_attr; + ctx->Rbp = (u64)attr->stackaddr_attr + attr->stacksize_attr; + ctx->Rcx = (u64)arg; + ctx->Rip = (u64)func; + + ctx->SegGs = KGDT64_R3_DATA | RPL_MASK; + ctx->SegEs = KGDT64_R3_DATA | RPL_MASK; + ctx->SegDs = KGDT64_R3_DATA | RPL_MASK; + ctx->SegCs = KGDT64_R3_CODE | RPL_MASK; + ctx->SegSs = KGDT64_R3_DATA | RPL_MASK; + ctx->SegFs = KGDT64_R3_CMTEB | RPL_MASK; + + ctx->EFlags = 0x3000 | EFLAGS_INTERRUPT_MASK; + ctx->MxCsr = INITIAL_MXCSR; + + ctx->FltSave.ControlWord = INITIAL_FPUCW; + ctx->FltSave.MxCsr = INITIAL_MXCSR; + ctx->FltSave.MxCsr_Mask = INITIAL_MXCSR_MASK; + + ctx->ContextFlags = + CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT; +} +#endif + +NativeThread::NativeThread() : native_handle{0} {} + +NativeThread::~NativeThread() {} + +int NativeThread::Create(ThreadFunc func, void* arg, const ::Libraries::Kernel::PthreadAttr* attr) { +#ifndef _WIN64 pthread_t* pthr = reinterpret_cast(&native_handle); pthread_attr_t pattr; pthread_attr_init(&pattr); pthread_attr_setstack(&pattr, attr->stackaddr_attr, attr->stacksize_attr); return pthread_create(pthr, &pattr, (PthreadFunc)func, arg); +#else + CLIENT_ID clientId{}; + INITIAL_TEB teb{}; + CONTEXT ctx{}; + + clientId.UniqueProcess = GetCurrentProcess(); + clientId.UniqueThread = GetCurrentThread(); + + InitializeTeb(&teb, attr); + InitializeContext(&ctx, func, arg, attr); + + return NtCreateThread(&native_handle, THREAD_ALL_ACCESS, nullptr, GetCurrentProcess(), + &clientId, &ctx, &teb, false); #endif } -void Thread::Exit() { +void NativeThread::Exit() { if (!native_handle) { return; } + tid = 0; + #ifdef _WIN64 - CloseHandle(native_handle); + NtClose(native_handle); native_handle = nullptr; - // We call this assuming the thread has finished execution. - ExitThread(0); + /* The Windows kernel will free the stack + given at thread creation via INITIAL_TEB + (StackAllocationBase) upon thread termination. + + In earlier Windows versions (NT4 to Windows Server 2003), + you could get around this via disabling FreeStackOnTermination + on the TEB. This has been removed since then. + + To avoid this, we must forcefully set the TEB + deallocation stack pointer to NULL so ZwFreeVirtualMemory fails + in the kernel and our stack is not freed. + */ + auto* teb = reinterpret_cast(NtCurrentTeb()); + teb->DeallocationStack = nullptr; + + NtTerminateThread(nullptr, 0); #else pthread_exit(nullptr); #endif } +void NativeThread::Initialize() { +#if _WIN64 + tid = GetCurrentThreadId(); +#else + tid = (u64)pthread_self(); +#endif +} + } // namespace Core \ No newline at end of file diff --git a/src/core/thread.h b/src/core/thread.h index cfb8b8309..3bac0e699 100644 --- a/src/core/thread.h +++ b/src/core/thread.h @@ -11,27 +11,34 @@ struct PthreadAttr; namespace Core { -class Thread { -public: - using ThreadFunc = void (*)(void*); - using PthreadFunc = void* (*)(void*); +using ThreadFunc = void (*)(void*); +using PthreadFunc = void* (*)(void*); - Thread(); - ~Thread(); +class NativeThread { +public: + NativeThread(); + ~NativeThread(); int Create(ThreadFunc func, void* arg, const ::Libraries::Kernel::PthreadAttr* attr); void Exit(); + void Initialize(); + uintptr_t GetHandle() { return reinterpret_cast(native_handle); } + u64 GetTid() { + return tid; + } + private: -#if _WIN64 +#ifdef _WIN64 void* native_handle; #else uintptr_t native_handle; #endif + u64 tid; }; } // namespace Core \ No newline at end of file diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index f7b710edd..b81806d10 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -46,7 +46,7 @@ Liverpool::~Liverpool() { } void Liverpool::Process(std::stop_token stoken) { - Common::SetCurrentThreadName("shadPS4:GPU_CommandProcessor"); + Common::SetCurrentThreadName("shadPS4:GpuCommandProcessor"); while (!stoken.stop_requested()) { { From 15ae7a094d3a4430ac2e7358c3a85af1409de404 Mon Sep 17 00:00:00 2001 From: "Daniel R." <47796739+polybiusproxy@users.noreply.github.com> Date: Thu, 5 Dec 2024 18:45:55 +0100 Subject: [PATCH 102/549] hotfix: fix inverted operator on GetDents --- src/core/libraries/kernel/file_system.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index 447467cb7..2a65255fb 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -656,7 +656,7 @@ static int GetDents(int fd, char* buf, int nbytes, s64* basep) { if (file == nullptr) { return ORBIS_KERNEL_ERROR_EBADF; } - if (file->type != Core::FileSys::FileType::Device) { + if (file->type == Core::FileSys::FileType::Device) { return file->device->getdents(buf, nbytes, basep); } From 37f4bad2b7efd74643f2b7d579011aa942e251f2 Mon Sep 17 00:00:00 2001 From: psucien <168137814+psucien@users.noreply.github.com> Date: Thu, 5 Dec 2024 22:09:22 +0100 Subject: [PATCH 103/549] video_core: fix for targets clears and copies (#1670) --- .../renderer_vulkan/vk_rasterizer.cpp | 49 ++++++++++++++----- .../texture_cache/texture_cache.cpp | 15 +++--- src/video_core/texture_cache/texture_cache.h | 23 +++++++-- 3 files changed, 61 insertions(+), 26 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 084b7c345..620e5f103 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -102,9 +102,6 @@ RenderState Rasterizer::PrepareRenderState(u32 mrt_mask) { continue; } - const bool is_clear = texture_cache.IsMetaCleared(col_buf.CmaskAddress()); - texture_cache.TouchMeta(col_buf.CmaskAddress(), false); - const auto& hint = liverpool->last_cb_extent[col_buf_id]; auto& [image_id, desc] = cb_descs.emplace_back(std::piecewise_construct, std::tuple{}, std::tuple{col_buf, hint}); @@ -113,6 +110,10 @@ RenderState Rasterizer::PrepareRenderState(u32 mrt_mask) { auto& image = texture_cache.GetImage(image_id); image.binding.is_target = 1u; + const auto slice = image_view.info.range.base.layer; + const bool is_clear = texture_cache.IsMetaCleared(col_buf.CmaskAddress(), slice); + texture_cache.TouchMeta(col_buf.CmaskAddress(), slice, false); + const auto mip = image_view.info.range.base.level; state.width = std::min(state.width, std::max(image.info.size.width >> mip, 1u)); state.height = std::min(state.height, std::max(image.info.size.height >> mip, 1u)); @@ -134,8 +135,6 @@ RenderState Rasterizer::PrepareRenderState(u32 mrt_mask) { (regs.depth_control.stencil_enable && regs.depth_buffer.stencil_info.format != StencilFormat::Invalid))) { const auto htile_address = regs.depth_htile_data_base.GetAddress(); - const bool is_clear = regs.depth_render_control.depth_clear_enable || - texture_cache.IsMetaCleared(htile_address); const auto& hint = liverpool->last_db_extent; auto& [image_id, desc] = db_desc.emplace(std::piecewise_construct, std::tuple{}, @@ -146,6 +145,11 @@ RenderState Rasterizer::PrepareRenderState(u32 mrt_mask) { auto& image = texture_cache.GetImage(image_id); image.binding.is_target = 1u; + const auto slice = image_view.info.range.base.layer; + const bool is_clear = regs.depth_render_control.depth_clear_enable || + texture_cache.IsMetaCleared(htile_address, slice); + ASSERT(desc.view_info.range.extent.layers == 1); + state.width = std::min(state.width, image.info.size.width); state.height = std::min(state.height, image.info.size.height); state.depth_image = image.image; @@ -157,7 +161,7 @@ RenderState Rasterizer::PrepareRenderState(u32 mrt_mask) { .clearValue = vk::ClearValue{.depthStencil = {.depth = regs.depth_clear, .stencil = regs.stencil_clear}}, }; - texture_cache.TouchMeta(htile_address, false); + texture_cache.TouchMeta(htile_address, slice, false); state.has_depth = regs.depth_buffer.z_info.format != AmdGpu::Liverpool::DepthBuffer::ZFormat::Invalid; state.has_stencil = regs.depth_buffer.stencil_info.format != @@ -359,9 +363,11 @@ bool Rasterizer::BindResources(const Pipeline* pipeline) { // will need its full emulation anyways. For cases of metadata read a warning will be // logged. const auto IsMetaUpdate = [&](const auto& desc) { - const VAddr address = desc.GetSharp(info).base_address; + const auto sharp = desc.GetSharp(info); + const VAddr address = sharp.base_address; if (desc.is_written) { - if (texture_cache.TouchMeta(address, true)) { + // Assume all slices were updates + if (texture_cache.ClearMeta(address)) { LOG_TRACE(Render_Vulkan, "Metadata update skipped"); return true; } @@ -373,17 +379,36 @@ bool Rasterizer::BindResources(const Pipeline* pipeline) { return false; }; + // Assume if a shader reads and writes metas at the same time, it is a copy shader. + bool meta_read = false; for (const auto& desc : info.buffers) { if (desc.is_gds_buffer) { continue; } - if (IsMetaUpdate(desc)) { - return false; + if (!desc.is_written) { + const VAddr address = desc.GetSharp(info).base_address; + meta_read = texture_cache.IsMeta(address); } } + for (const auto& desc : info.texture_buffers) { - if (IsMetaUpdate(desc)) { - return false; + if (!desc.is_written) { + const VAddr address = desc.GetSharp(info).base_address; + meta_read = texture_cache.IsMeta(address); + } + } + + if (!meta_read) { + for (const auto& desc : info.buffers) { + if (IsMetaUpdate(desc)) { + return false; + } + } + + for (const auto& desc : info.texture_buffers) { + if (IsMetaUpdate(desc)) { + return false; + } } } } diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index 4373fdc52..1670648b3 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -398,17 +398,15 @@ ImageView& TextureCache::FindRenderTarget(BaseDesc& desc) { // Register meta data for this color buffer if (!(image.flags & ImageFlagBits::MetaRegistered)) { if (desc.info.meta_info.cmask_addr) { - surface_metas.emplace( - desc.info.meta_info.cmask_addr, - MetaDataInfo{.type = MetaDataInfo::Type::CMask, .is_cleared = true}); + surface_metas.emplace(desc.info.meta_info.cmask_addr, + MetaDataInfo{.type = MetaDataInfo::Type::CMask}); image.info.meta_info.cmask_addr = desc.info.meta_info.cmask_addr; image.flags |= ImageFlagBits::MetaRegistered; } if (desc.info.meta_info.fmask_addr) { - surface_metas.emplace( - desc.info.meta_info.fmask_addr, - MetaDataInfo{.type = MetaDataInfo::Type::FMask, .is_cleared = true}); + surface_metas.emplace(desc.info.meta_info.fmask_addr, + MetaDataInfo{.type = MetaDataInfo::Type::FMask}); image.info.meta_info.fmask_addr = desc.info.meta_info.fmask_addr; image.flags |= ImageFlagBits::MetaRegistered; } @@ -428,9 +426,8 @@ ImageView& TextureCache::FindDepthTarget(BaseDesc& desc) { // Register meta data for this depth buffer if (!(image.flags & ImageFlagBits::MetaRegistered)) { if (desc.info.meta_info.htile_addr) { - surface_metas.emplace( - desc.info.meta_info.htile_addr, - MetaDataInfo{.type = MetaDataInfo::Type::HTile, .is_cleared = true}); + surface_metas.emplace(desc.info.meta_info.htile_addr, + MetaDataInfo{.type = MetaDataInfo::Type::HTile}); image.info.meta_info.htile_addr = desc.info.meta_info.htile_addr; image.flags |= ImageFlagBits::MetaRegistered; } diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index fab4c832f..676ede777 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -156,18 +156,31 @@ public: return surface_metas.contains(address); } - bool IsMetaCleared(VAddr address) const { + bool IsMetaCleared(VAddr address, u32 slice) const { const auto& it = surface_metas.find(address); if (it != surface_metas.end()) { - return it.value().is_cleared; + return it.value().clear_mask & (1u << slice); } return false; } - bool TouchMeta(VAddr address, bool is_clear) { + bool ClearMeta(VAddr address) { auto it = surface_metas.find(address); if (it != surface_metas.end()) { - it.value().is_cleared = is_clear; + it.value().clear_mask = u32(-1); + return true; + } + return false; + } + + bool TouchMeta(VAddr address, u32 slice, bool is_clear) { + auto it = surface_metas.find(address); + if (it != surface_metas.end()) { + if (is_clear) { + it.value().clear_mask |= 1u << slice; + } else { + it.value().clear_mask &= ~(1u << slice); + } return true; } return false; @@ -280,7 +293,7 @@ private: HTile, }; Type type; - bool is_cleared; + u32 clear_mask{u32(-1)}; }; tsl::robin_map surface_metas; }; From 7fbe15de284c923187a1e06f0267ccb1cd84aea0 Mon Sep 17 00:00:00 2001 From: Richard Habitzreuter Date: Thu, 5 Dec 2024 18:09:43 -0300 Subject: [PATCH 104/549] Missing dependency on building-windows.md (#1658) * Missing dependency on building-windows.md * Update building-windows.md --- documents/building-windows.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documents/building-windows.md b/documents/building-windows.md index 48fd09c41..0da630f0b 100644 --- a/documents/building-windows.md +++ b/documents/building-windows.md @@ -79,7 +79,7 @@ Normal x86-based computers, follow: 1. Open "MSYS2 MINGW64" from your new applications 2. Run `pacman -Syu`, let it complete; -3. Run `pacman -S --needed git mingw-w64-x86_64-binutils mingw-w64-x86_64-clang mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-ffmpeg` +3. Run `pacman -S --needed git mingw-w64-x86_64-binutils mingw-w64-x86_64-clang mingw-w64-x86_64-cmake mingw-w64-x86_64-rapidjson mingw-w64-x86_64-ninja mingw-w64-x86_64-ffmpeg` 1. Optional (Qt only): run `pacman -S --needed mingw-w64-x86_64-qt6-base mingw-w64-x86_64-qt6-tools mingw-w64-x86_64-qt6-multimedia` 4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4` 5. Run `cd shadPS4` @@ -93,7 +93,7 @@ ARM64-based computers, follow: 1. Open "MSYS2 CLANGARM64" from your new applications 2. Run `pacman -Syu`, let it complete; -3. Run `pacman -S --needed git mingw-w64-clang-aarch64-binutils mingw-w64-clang-aarch64-clang mingw-w64-clang-aarch64-cmake mingw-w64-clang-aarch64-ninja mingw-w64-clang-aarch64-ffmpeg` +3. Run `pacman -S --needed git mingw-w64-clang-aarch64-binutils mingw-w64-clang-aarch64-clang mingw-w64-clang-aarch64-rapidjson mingw-w64-clang-aarch64-cmake mingw-w64-clang-aarch64-ninja mingw-w64-clang-aarch64-ffmpeg` 1. Optional (Qt only): run `pacman -S --needed mingw-w64-clang-aarch64-qt6-base mingw-w64-clang-aarch64-qt6-tools mingw-w64-clang-aarch64-qt6-multimedia` 4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4` 5. Run `cd shadPS4` From 642dedea8c9099b0b84ff4cd653d0c79fc9bb4d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?= Date: Thu, 5 Dec 2024 22:09:59 +0100 Subject: [PATCH 105/549] Handle INDIRECT_BUFFER_CONST in ProcessCeUpdate (#1613) --- src/video_core/amdgpu/liverpool.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index b81806d10..38fca6bc0 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -161,6 +161,19 @@ Liverpool::Task Liverpool::ProcessCeUpdate(std::span ccb) { } break; } + case PM4ItOpcode::IndirectBufferConst: { + const auto* indirect_buffer = reinterpret_cast(header); + auto task = ProcessCeUpdate( + {indirect_buffer->Address(), indirect_buffer->ib_size}); + while (!task.handle.done()) { + task.handle.resume(); + + TracyFiberLeave; + co_yield {}; + TracyFiberEnter(ccb_task_name); + }; + break; + } default: const u32 count = header->type3.NumWords(); UNREACHABLE_MSG("Unknown PM4 type 3 opcode {:#x} with count {}", From 874508f8c2bd721e1f97a0a877dd4686bf43ef83 Mon Sep 17 00:00:00 2001 From: Alexandre Bouvier Date: Thu, 5 Dec 2024 21:10:27 +0000 Subject: [PATCH 106/549] cmake: unbundle stb (#1601) --- CMakeLists.txt | 5 ++++- REUSE.toml | 2 +- cmake/Findstb.cmake | 19 +++++++++++++++++++ externals/CMakeLists.txt | 7 +++++++ externals/{ => stb}/stb_image.h | 0 src/common/stb.cpp | 7 +++++++ src/common/stb.h | 6 ++++++ src/core/file_format/splash.cpp | 6 +----- src/imgui/renderer/texture_manager.cpp | 3 +-- 9 files changed, 46 insertions(+), 9 deletions(-) create mode 100644 cmake/Findstb.cmake rename externals/{ => stb}/stb_image.h (100%) create mode 100644 src/common/stb.cpp create mode 100644 src/common/stb.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 56760be37..378b8f78d 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -117,6 +117,7 @@ find_package(magic_enum 0.9.6 CONFIG) find_package(PNG 1.6 MODULE) find_package(RenderDoc 1.6.0 MODULE) find_package(SDL3 3.1.2 CONFIG) +find_package(stb MODULE) find_package(toml11 4.2.0 CONFIG) find_package(tsl-robin-map 1.3.0 CONFIG) find_package(VulkanHeaders 1.3.289 CONFIG) @@ -495,6 +496,8 @@ set(COMMON src/common/logging/backend.cpp src/common/slot_vector.h src/common/spin_lock.cpp src/common/spin_lock.h + src/common/stb.cpp + src/common/stb.h src/common/string_util.cpp src/common/string_util.h src/common/thread.cpp @@ -867,7 +870,7 @@ endif() create_target_directory_groups(shadps4) target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG) -target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::SPIRV glslang::glslang SDL3::SDL3 pugixml::pugixml) +target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::SPIRV glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers) target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h") target_compile_definitions(Dear_ImGui PRIVATE IMGUI_USER_CONFIG="${PROJECT_SOURCE_DIR}/src/imgui/imgui_config.h") diff --git a/REUSE.toml b/REUSE.toml index 2d94c9292..5bd21bead 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -63,7 +63,7 @@ SPDX-FileCopyrightText = "2019-2024 Baldur Karlsson" SPDX-License-Identifier = "MIT" [[annotations]] -path = "externals/stb_image.h" +path = "externals/stb/**" precedence = "aggregate" SPDX-FileCopyrightText = "2017 Sean Barrett" SPDX-License-Identifier = "MIT" diff --git a/cmake/Findstb.cmake b/cmake/Findstb.cmake new file mode 100644 index 000000000..667911e1d --- /dev/null +++ b/cmake/Findstb.cmake @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +find_path(stb_image_INCLUDE_DIR stb_image.h PATH_SUFFIXES stb) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(stb + REQUIRED_VARS stb_image_INCLUDE_DIR +) + +if (stb_FOUND AND NOT TARGET stb::headers) + add_library(stb::headers INTERFACE IMPORTED) + set_property(TARGET stb::headers PROPERTY + INTERFACE_INCLUDE_DIRECTORIES + "${stb_image_INCLUDE_DIR}" + ) +endif() + +mark_as_advanced(stb_image_INCLUDE_DIR) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index bc2d41bda..8ccae8070 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -222,3 +222,10 @@ endif() # GCN Headers add_subdirectory(gcn) + +# stb +if (NOT TARGET stb::headers) + add_library(stb INTERFACE) + target_include_directories(stb INTERFACE stb) + add_library(stb::headers ALIAS stb) +endif() diff --git a/externals/stb_image.h b/externals/stb/stb_image.h similarity index 100% rename from externals/stb_image.h rename to externals/stb/stb_image.h diff --git a/src/common/stb.cpp b/src/common/stb.cpp new file mode 100644 index 000000000..0cd916185 --- /dev/null +++ b/src/common/stb.cpp @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#define STB_IMAGE_IMPLEMENTATION +#define STBI_ONLY_PNG +#define STBI_NO_STDIO +#include "common/stb.h" diff --git a/src/common/stb.h b/src/common/stb.h new file mode 100644 index 000000000..6f4d34483 --- /dev/null +++ b/src/common/stb.h @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include diff --git a/src/core/file_format/splash.cpp b/src/core/file_format/splash.cpp index 5e06c912d..b68702157 100644 --- a/src/core/file_format/splash.cpp +++ b/src/core/file_format/splash.cpp @@ -5,13 +5,9 @@ #include "common/assert.h" #include "common/io_file.h" +#include "common/stb.h" #include "splash.h" -#define STB_IMAGE_IMPLEMENTATION -#define STBI_ONLY_PNG -#define STBI_NO_STDIO -#include "externals/stb_image.h" - bool Splash::Open(const std::filesystem::path& filepath) { ASSERT_MSG(filepath.stem().string() != "png", "Unexpected file format passed"); diff --git a/src/imgui/renderer/texture_manager.cpp b/src/imgui/renderer/texture_manager.cpp index 7f9c69d49..dd233ee60 100644 --- a/src/imgui/renderer/texture_manager.cpp +++ b/src/imgui/renderer/texture_manager.cpp @@ -4,12 +4,11 @@ #include #include -#include - #include "common/assert.h" #include "common/config.h" #include "common/io_file.h" #include "common/polyfill_thread.h" +#include "common/stb.h" #include "imgui_impl_vulkan.h" #include "texture_manager.h" From 22a2741ea01dcd10ad207da9bb87211cda179099 Mon Sep 17 00:00:00 2001 From: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> Date: Thu, 5 Dec 2024 23:14:16 +0200 Subject: [PATCH 107/549] shader_recompilers: Improvements to SSA phi generation and lane instruction elimination (#1667) * shader_recompiler: Add use tracking for Insts * ssa_rewrite: Recursively remove phis * ssa_rewrite: Correct recursive trivial phi elimination * ir: Improve read lane folding pass * control_flow: Avoid adding unnecessary divergant blocks * clang format * externals: Update ext-boost --------- Co-authored-by: Frodo Baggins --- externals/ext-boost | 2 +- .../frontend/control_flow_graph.cpp | 43 ++++++---- src/shader_recompiler/ir/basic_block.cpp | 2 + src/shader_recompiler/ir/microinstruction.cpp | 50 +++++++----- .../ir/passes/constant_propagation_pass.cpp | 81 ++++++++++++++----- .../passes/lower_shared_mem_to_registers.cpp | 2 +- .../ir/passes/resource_tracking_pass.cpp | 2 +- .../ir/passes/ssa_rewrite_pass.cpp | 19 ++--- src/shader_recompiler/ir/value.h | 48 +++++++++-- src/video_core/amdgpu/liverpool.cpp | 2 +- 10 files changed, 175 insertions(+), 76 deletions(-) diff --git a/externals/ext-boost b/externals/ext-boost index f2474e1b5..ca6f230e6 160000 --- a/externals/ext-boost +++ b/externals/ext-boost @@ -1 +1 @@ -Subproject commit f2474e1b584fb7a3ed6f85ba875e6eacd742ec8a +Subproject commit ca6f230e67be7cc45fc919057f07b2aee64dadc1 diff --git a/src/shader_recompiler/frontend/control_flow_graph.cpp b/src/shader_recompiler/frontend/control_flow_graph.cpp index 354196d31..8c3122b28 100644 --- a/src/shader_recompiler/frontend/control_flow_graph.cpp +++ b/src/shader_recompiler/frontend/control_flow_graph.cpp @@ -47,6 +47,15 @@ static IR::Condition MakeCondition(const GcnInst& inst) { } } +static bool IgnoresExecMask(Opcode opcode) { + switch (opcode) { + case Opcode::V_WRITELANE_B32: + return true; + default: + return false; + } +} + static constexpr size_t LabelReserveSize = 32; CFG::CFG(Common::ObjectPool& block_pool_, std::span inst_list_) @@ -133,20 +142,26 @@ void CFG::EmitDivergenceLabels() { curr_begin = -1; continue; } - // Add a label to the instruction right after the open scope call. - // It is the start of a new basic block. - const auto& save_inst = inst_list[curr_begin]; - const Label label = index_to_pc[curr_begin] + save_inst.length; - AddLabel(label); - // Add a label to the close scope instruction. - // There are 3 cases where we need to close a scope. - // * Close scope instruction inside the block - // * Close scope instruction at the end of the block (cbranch or endpgm) - // * Normal instruction at the end of the block - // For the last case we must NOT add a label as that would cause - // the instruction to be separated into its own basic block. - if (is_close) { - AddLabel(index_to_pc[index]); + // If all instructions in the scope ignore exec masking, we shouldn't insert a + // scope. + const auto start = inst_list.begin() + curr_begin + 1; + if (!std::ranges::all_of(start, inst_list.begin() + index, IgnoresExecMask, + &GcnInst::opcode)) { + // Add a label to the instruction right after the open scope call. + // It is the start of a new basic block. + const auto& save_inst = inst_list[curr_begin]; + const Label label = index_to_pc[curr_begin] + save_inst.length; + AddLabel(label); + // Add a label to the close scope instruction. + // There are 3 cases where we need to close a scope. + // * Close scope instruction inside the block + // * Close scope instruction at the end of the block (cbranch or endpgm) + // * Normal instruction at the end of the block + // For the last case we must NOT add a label as that would cause + // the instruction to be separated into its own basic block. + if (is_close) { + AddLabel(index_to_pc[index]); + } } // Reset scope begin. curr_begin = -1; diff --git a/src/shader_recompiler/ir/basic_block.cpp b/src/shader_recompiler/ir/basic_block.cpp index 426acb2b8..b4d1a78c7 100644 --- a/src/shader_recompiler/ir/basic_block.cpp +++ b/src/shader_recompiler/ir/basic_block.cpp @@ -19,12 +19,14 @@ void Block::AppendNewInst(Opcode op, std::initializer_list args) { Block::iterator Block::PrependNewInst(iterator insertion_point, const Inst& base_inst) { Inst* const inst{inst_pool->Create(base_inst)}; + inst->SetParent(this); return instructions.insert(insertion_point, *inst); } Block::iterator Block::PrependNewInst(iterator insertion_point, Opcode op, std::initializer_list args, u32 flags) { Inst* const inst{inst_pool->Create(op, flags)}; + inst->SetParent(this); const auto result_it{instructions.insert(insertion_point, *inst)}; if (inst->NumArgs() != args.size()) { diff --git a/src/shader_recompiler/ir/microinstruction.cpp b/src/shader_recompiler/ir/microinstruction.cpp index abd31a728..9b4ad63d2 100644 --- a/src/shader_recompiler/ir/microinstruction.cpp +++ b/src/shader_recompiler/ir/microinstruction.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include #include "shader_recompiler/exception.h" @@ -119,10 +120,10 @@ void Inst::SetArg(size_t index, Value value) { } const IR::Value arg{Arg(index)}; if (!arg.IsImmediate()) { - UndoUse(arg); + UndoUse(arg.Inst(), index); } if (!value.IsImmediate()) { - Use(value); + Use(value.Inst(), index); } if (op == Opcode::Phi) { phi_args[index].second = value; @@ -143,7 +144,7 @@ Block* Inst::PhiBlock(size_t index) const { void Inst::AddPhiOperand(Block* predecessor, const Value& value) { if (!value.IsImmediate()) { - Use(value); + Use(value.Inst(), phi_args.size()); } phi_args.emplace_back(predecessor, value); } @@ -155,17 +156,19 @@ void Inst::Invalidate() { void Inst::ClearArgs() { if (op == Opcode::Phi) { - for (auto& pair : phi_args) { + for (auto i = 0; i < phi_args.size(); i++) { + auto& pair = phi_args[i]; IR::Value& value{pair.second}; if (!value.IsImmediate()) { - UndoUse(value); + UndoUse(value.Inst(), i); } } phi_args.clear(); } else { - for (auto& value : args) { + for (auto i = 0; i < args.size(); i++) { + auto& value = args[i]; if (!value.IsImmediate()) { - UndoUse(value); + UndoUse(value.Inst(), i); } } // Reset arguments to null @@ -174,13 +177,21 @@ void Inst::ClearArgs() { } } -void Inst::ReplaceUsesWith(Value replacement) { - Invalidate(); - ReplaceOpcode(Opcode::Identity); - if (!replacement.IsImmediate()) { - Use(replacement); +void Inst::ReplaceUsesWith(Value replacement, bool preserve) { + // Copy since user->SetArg will mutate this->uses + // Could also do temp_uses = std::move(uses) but more readable + const auto temp_uses = uses; + for (const auto& [user, operand] : temp_uses) { + DEBUG_ASSERT(user->Arg(operand).Inst() == this); + user->SetArg(operand, replacement); + } + Invalidate(); + if (preserve) { + // Still useful to have Identity for indirection. + // SSA pass would be more complicated without it + ReplaceOpcode(Opcode::Identity); + SetArg(0, replacement); } - args[0] = replacement; } void Inst::ReplaceOpcode(IR::Opcode opcode) { @@ -195,14 +206,15 @@ void Inst::ReplaceOpcode(IR::Opcode opcode) { op = opcode; } -void Inst::Use(const Value& value) { - Inst* const inst{value.Inst()}; - ++inst->use_count; +void Inst::Use(Inst* used, u32 operand) { + DEBUG_ASSERT(0 == std::count(used->uses.begin(), used->uses.end(), IR::Use(this, operand))); + used->uses.emplace_front(this, operand); } -void Inst::UndoUse(const Value& value) { - Inst* const inst{value.Inst()}; - --inst->use_count; +void Inst::UndoUse(Inst* used, u32 operand) { + IR::Use use(this, operand); + DEBUG_ASSERT(1 == std::count(used->uses.begin(), used->uses.end(), use)); + used->uses.remove(use); } } // namespace Shader::IR diff --git a/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp b/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp index a03fe051c..9624ce6a5 100644 --- a/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp +++ b/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp @@ -43,7 +43,7 @@ bool FoldCommutative(IR::Inst& inst, ImmFn&& imm_fn) { if (is_lhs_immediate && is_rhs_immediate) { const auto result{imm_fn(Arg(lhs), Arg(rhs))}; - inst.ReplaceUsesWith(IR::Value{result}); + inst.ReplaceUsesWithAndRemove(IR::Value{result}); return false; } if (is_lhs_immediate && !is_rhs_immediate) { @@ -75,7 +75,7 @@ bool FoldWhenAllImmediates(IR::Inst& inst, Func&& func) { return false; } using Indices = std::make_index_sequence::NUM_ARGS>; - inst.ReplaceUsesWith(EvalImmediates(inst, func, Indices{})); + inst.ReplaceUsesWithAndRemove(EvalImmediates(inst, func, Indices{})); return true; } @@ -83,12 +83,12 @@ template void FoldBitCast(IR::Inst& inst, IR::Opcode reverse) { const IR::Value value{inst.Arg(0)}; if (value.IsImmediate()) { - inst.ReplaceUsesWith(IR::Value{std::bit_cast(Arg(value))}); + inst.ReplaceUsesWithAndRemove(IR::Value{std::bit_cast(Arg(value))}); return; } IR::Inst* const arg_inst{value.InstRecursive()}; if (arg_inst->GetOpcode() == reverse) { - inst.ReplaceUsesWith(arg_inst->Arg(0)); + inst.ReplaceUsesWithAndRemove(arg_inst->Arg(0)); return; } } @@ -131,7 +131,7 @@ void FoldCompositeExtract(IR::Inst& inst, IR::Opcode construct, IR::Opcode inser if (!result) { return; } - inst.ReplaceUsesWith(*result); + inst.ReplaceUsesWithAndRemove(*result); } void FoldConvert(IR::Inst& inst, IR::Opcode opposite) { @@ -141,7 +141,7 @@ void FoldConvert(IR::Inst& inst, IR::Opcode opposite) { } IR::Inst* const producer{value.InstRecursive()}; if (producer->GetOpcode() == opposite) { - inst.ReplaceUsesWith(producer->Arg(0)); + inst.ReplaceUsesWithAndRemove(producer->Arg(0)); } } @@ -152,9 +152,9 @@ void FoldLogicalAnd(IR::Inst& inst) { const IR::Value rhs{inst.Arg(1)}; if (rhs.IsImmediate()) { if (rhs.U1()) { - inst.ReplaceUsesWith(inst.Arg(0)); + inst.ReplaceUsesWithAndRemove(inst.Arg(0)); } else { - inst.ReplaceUsesWith(IR::Value{false}); + inst.ReplaceUsesWithAndRemove(IR::Value{false}); } } } @@ -162,7 +162,7 @@ void FoldLogicalAnd(IR::Inst& inst) { void FoldSelect(IR::Inst& inst) { const IR::Value cond{inst.Arg(0)}; if (cond.IsImmediate()) { - inst.ReplaceUsesWith(cond.U1() ? inst.Arg(1) : inst.Arg(2)); + inst.ReplaceUsesWithAndRemove(cond.U1() ? inst.Arg(1) : inst.Arg(2)); } } @@ -173,9 +173,9 @@ void FoldLogicalOr(IR::Inst& inst) { const IR::Value rhs{inst.Arg(1)}; if (rhs.IsImmediate()) { if (rhs.U1()) { - inst.ReplaceUsesWith(IR::Value{true}); + inst.ReplaceUsesWithAndRemove(IR::Value{true}); } else { - inst.ReplaceUsesWith(inst.Arg(0)); + inst.ReplaceUsesWithAndRemove(inst.Arg(0)); } } } @@ -183,12 +183,12 @@ void FoldLogicalOr(IR::Inst& inst) { void FoldLogicalNot(IR::Inst& inst) { const IR::U1 value{inst.Arg(0)}; if (value.IsImmediate()) { - inst.ReplaceUsesWith(IR::Value{!value.U1()}); + inst.ReplaceUsesWithAndRemove(IR::Value{!value.U1()}); return; } IR::Inst* const arg{value.InstRecursive()}; if (arg->GetOpcode() == IR::Opcode::LogicalNot) { - inst.ReplaceUsesWith(arg->Arg(0)); + inst.ReplaceUsesWithAndRemove(arg->Arg(0)); } } @@ -199,7 +199,7 @@ void FoldInverseFunc(IR::Inst& inst, IR::Opcode reverse) { } IR::Inst* const arg_inst{value.InstRecursive()}; if (arg_inst->GetOpcode() == reverse) { - inst.ReplaceUsesWith(arg_inst->Arg(0)); + inst.ReplaceUsesWithAndRemove(arg_inst->Arg(0)); return; } } @@ -211,7 +211,7 @@ void FoldAdd(IR::Block& block, IR::Inst& inst) { } const IR::Value rhs{inst.Arg(1)}; if (rhs.IsImmediate() && Arg(rhs) == 0) { - inst.ReplaceUsesWith(inst.Arg(0)); + inst.ReplaceUsesWithAndRemove(inst.Arg(0)); return; } } @@ -226,21 +226,58 @@ void FoldCmpClass(IR::Block& block, IR::Inst& inst) { } else if ((class_mask & IR::FloatClassFunc::Finite) == IR::FloatClassFunc::Finite) { IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; const IR::F32 value = IR::F32{inst.Arg(0)}; - inst.ReplaceUsesWith(ir.LogicalNot(ir.LogicalOr(ir.FPIsInf(value), ir.FPIsInf(value)))); + inst.ReplaceUsesWithAndRemove( + ir.LogicalNot(ir.LogicalOr(ir.FPIsInf(value), ir.FPIsInf(value)))); } else { UNREACHABLE(); } } -void FoldReadLane(IR::Inst& inst) { +void FoldReadLane(IR::Block& block, IR::Inst& inst) { const u32 lane = inst.Arg(1).U32(); IR::Inst* prod = inst.Arg(0).InstRecursive(); - while (prod->GetOpcode() == IR::Opcode::WriteLane) { - if (prod->Arg(2).U32() == lane) { - inst.ReplaceUsesWith(prod->Arg(1)); + + const auto search_chain = [lane](const IR::Inst* prod) -> IR::Value { + while (prod->GetOpcode() == IR::Opcode::WriteLane) { + if (prod->Arg(2).U32() == lane) { + return prod->Arg(1); + } + prod = prod->Arg(0).InstRecursive(); + } + return {}; + }; + + if (prod->GetOpcode() == IR::Opcode::WriteLane) { + if (const IR::Value value = search_chain(prod); !value.IsEmpty()) { + inst.ReplaceUsesWith(value); + } + return; + } + + if (prod->GetOpcode() == IR::Opcode::Phi) { + boost::container::small_vector phi_args; + for (size_t arg_index = 0; arg_index < prod->NumArgs(); ++arg_index) { + const IR::Inst* arg{prod->Arg(arg_index).InstRecursive()}; + if (arg->GetOpcode() != IR::Opcode::WriteLane) { + return; + } + const IR::Value value = search_chain(arg); + if (value.IsEmpty()) { + continue; + } + phi_args.emplace_back(value); + } + if (std::ranges::all_of(phi_args, [&](IR::Value value) { return value == phi_args[0]; })) { + inst.ReplaceUsesWith(phi_args[0]); return; } - prod = prod->Arg(0).InstRecursive(); + const auto insert_point = IR::Block::InstructionList::s_iterator_to(*prod); + IR::Inst* const new_phi{&*block.PrependNewInst(insert_point, IR::Opcode::Phi)}; + new_phi->SetFlags(IR::Type::U32); + for (size_t arg_index = 0; arg_index < phi_args.size(); arg_index++) { + new_phi->AddPhiOperand(prod->PhiBlock(arg_index), phi_args[arg_index]); + } + inst.ReplaceUsesWith(IR::Value{new_phi}); } } @@ -290,7 +327,7 @@ void ConstantPropagation(IR::Block& block, IR::Inst& inst) { case IR::Opcode::SelectF64: return FoldSelect(inst); case IR::Opcode::ReadLane: - return FoldReadLane(inst); + return FoldReadLane(block, inst); case IR::Opcode::FPNeg32: FoldWhenAllImmediates(inst, [](f32 a) { return -a; }); return; diff --git a/src/shader_recompiler/ir/passes/lower_shared_mem_to_registers.cpp b/src/shader_recompiler/ir/passes/lower_shared_mem_to_registers.cpp index 76bfcf911..c109f3595 100644 --- a/src/shader_recompiler/ir/passes/lower_shared_mem_to_registers.cpp +++ b/src/shader_recompiler/ir/passes/lower_shared_mem_to_registers.cpp @@ -25,7 +25,7 @@ void LowerSharedMemToRegisters(IR::Program& program) { }); ASSERT(it != ds_writes.end()); // Replace data read with value written. - inst.ReplaceUsesWith((*it)->Arg(1)); + inst.ReplaceUsesWithAndRemove((*it)->Arg(1)); } } } diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index c1ff3d2f2..89c5c78a0 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -596,7 +596,7 @@ void PatchImageSampleInstruction(IR::Block& block, IR::Inst& inst, Info& info, } return ir.ImageSampleImplicitLod(handle, coords, bias, offset, inst_info); }(); - inst.ReplaceUsesWith(new_inst); + inst.ReplaceUsesWithAndRemove(new_inst); } void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descriptors& descriptors) { diff --git a/src/shader_recompiler/ir/passes/ssa_rewrite_pass.cpp b/src/shader_recompiler/ir/passes/ssa_rewrite_pass.cpp index df73c1bc8..1d252bee1 100644 --- a/src/shader_recompiler/ir/passes/ssa_rewrite_pass.cpp +++ b/src/shader_recompiler/ir/passes/ssa_rewrite_pass.cpp @@ -164,7 +164,6 @@ IR::Opcode UndefOpcode(const FlagTag) noexcept { enum class Status { Start, SetValue, - PreparePhiArgument, PushPhiArgument, }; @@ -253,12 +252,10 @@ public: IR::Inst* const phi{stack.back().phi}; phi->AddPhiOperand(*stack.back().pred_it, stack.back().result); ++stack.back().pred_it; - } - [[fallthrough]]; - case Status::PreparePhiArgument: prepare_phi_operand(); break; } + } } while (stack.size() > 1); return stack.back().result; } @@ -266,9 +263,7 @@ public: void SealBlock(IR::Block* block) { const auto it{incomplete_phis.find(block)}; if (it != incomplete_phis.end()) { - for (auto& pair : it->second) { - auto& variant{pair.first}; - auto& phi{pair.second}; + for (auto& [variant, phi] : it->second) { std::visit([&](auto& variable) { AddPhiOperands(variable, *phi, block); }, variant); } } @@ -289,7 +284,7 @@ private: const size_t num_args{phi.NumArgs()}; for (size_t arg_index = 0; arg_index < num_args; ++arg_index) { const IR::Value& op{phi.Arg(arg_index)}; - if (op.Resolve() == same.Resolve() || op == IR::Value{&phi}) { + if (op.Resolve() == same.Resolve() || op.Resolve() == IR::Value{&phi}) { // Unique value or self-reference continue; } @@ -314,9 +309,15 @@ private: ++reinsert_point; } // Reinsert the phi node and reroute all its uses to the "same" value + const auto users = phi.Uses(); list.insert(reinsert_point, phi); phi.ReplaceUsesWith(same); - // TODO: Try to recursively remove all phi users, which might have become trivial + // Try to recursively remove all phi users, which might have become trivial + for (const auto& [user, arg_index] : users) { + if (user->GetOpcode() == IR::Opcode::Phi) { + TryRemoveTrivialPhi(*user, user->GetParent(), undef_opcode); + } + } return same; } diff --git a/src/shader_recompiler/ir/value.h b/src/shader_recompiler/ir/value.h index 7e46747b9..dbe8b5cc4 100644 --- a/src/shader_recompiler/ir/value.h +++ b/src/shader_recompiler/ir/value.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -107,6 +108,16 @@ public: explicit TypedValue(IR::Inst* inst_) : TypedValue(Value(inst_)) {} }; +struct Use { + Inst* user; + u32 operand; + + Use() = default; + Use(Inst* user_, u32 operand_) : user(user_), operand(operand_) {} + Use(const Use&) = default; + bool operator==(const Use&) const noexcept = default; +}; + class Inst : public boost::intrusive::list_base_hook<> { public: explicit Inst(IR::Opcode op_, u32 flags_) noexcept; @@ -118,14 +129,22 @@ public: Inst& operator=(Inst&&) = delete; Inst(Inst&&) = delete; + IR::Block* GetParent() const { + ASSERT(parent); + return parent; + } + void SetParent(IR::Block* block) { + parent = block; + } + /// Get the number of uses this instruction has. [[nodiscard]] int UseCount() const noexcept { - return use_count; + return uses.size(); } /// Determines whether this instruction has uses or not. [[nodiscard]] bool HasUses() const noexcept { - return use_count > 0; + return uses.size() > 0; } /// Get the opcode this microinstruction represents. @@ -167,7 +186,13 @@ public: void Invalidate(); void ClearArgs(); - void ReplaceUsesWith(Value replacement); + void ReplaceUsesWithAndRemove(Value replacement) { + ReplaceUsesWith(replacement, false); + } + + void ReplaceUsesWith(Value replacement) { + ReplaceUsesWith(replacement, true); + } void ReplaceOpcode(IR::Opcode opcode); @@ -197,25 +222,32 @@ public: return std::bit_cast(definition); } + const auto Uses() const { + return uses; + } + private: struct NonTriviallyDummy { NonTriviallyDummy() noexcept {} }; - void Use(const Value& value); - void UndoUse(const Value& value); + void Use(Inst* used, u32 operand); + void UndoUse(Inst* used, u32 operand); + void ReplaceUsesWith(Value replacement, bool preserve); IR::Opcode op{}; - int use_count{}; u32 flags{}; u32 definition{}; + IR::Block* parent{}; union { NonTriviallyDummy dummy{}; boost::container::small_vector, 2> phi_args; std::array args; }; + + boost::container::list uses; }; -static_assert(sizeof(Inst) <= 128, "Inst size unintentionally increased"); +static_assert(sizeof(Inst) <= 160, "Inst size unintentionally increased"); using U1 = TypedValue; using U8 = TypedValue; @@ -373,4 +405,4 @@ template <> struct hash { std::size_t operator()(const Shader::IR::Value& v) const; }; -} // namespace std \ No newline at end of file +} // namespace std diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index 38fca6bc0..1bbd77f82 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -715,7 +715,7 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, int vqid) { false); } else if (dma_data->src_sel == DmaDataSrc::Gds && dma_data->dst_sel == DmaDataDst::Memory) { - LOG_WARNING(Render_Vulkan, "GDS memory read"); + // LOG_WARNING(Render_Vulkan, "GDS memory read"); } else if (dma_data->src_sel == DmaDataSrc::Memory && dma_data->dst_sel == DmaDataDst::Memory) { rasterizer->InlineData(dma_data->DstAddress(), From 77da8bac00ef22b8b79abd3cde284abdf3f18514 Mon Sep 17 00:00:00 2001 From: IndecisiveTurtle <47210458+raphaelthegreat@users.noreply.github.com> Date: Fri, 6 Dec 2024 00:46:34 +0200 Subject: [PATCH 108/549] core: Return proper address of eh frame/add more opcodes --- src/core/module.cpp | 4 ++-- src/emulator.cpp | 2 +- .../frontend/translate/scalar_alu.cpp | 21 +++++++++++++++++++ .../frontend/translate/translate.h | 2 ++ src/video_core/amdgpu/liverpool.cpp | 4 ++-- 5 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/core/module.cpp b/src/core/module.cpp index ef34f25c1..70afb932c 100644 --- a/src/core/module.cpp +++ b/src/core/module.cpp @@ -470,8 +470,8 @@ OrbisKernelModuleInfoEx Module::GetModuleInfoEx() const { .tls_align = tls.align, .init_proc_addr = base_virtual_addr + dynamic_info.init_virtual_addr, .fini_proc_addr = base_virtual_addr + dynamic_info.fini_virtual_addr, - .eh_frame_hdr_addr = eh_frame_hdr_addr, - .eh_frame_addr = eh_frame_addr, + .eh_frame_hdr_addr = base_virtual_addr + eh_frame_hdr_addr, + .eh_frame_addr = base_virtual_addr + eh_frame_addr, .eh_frame_hdr_size = eh_frame_hdr_size, .eh_frame_size = eh_frame_size, .segments = info.segments, diff --git a/src/emulator.cpp b/src/emulator.cpp index 60d6e18d7..8a7c04cf4 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -266,7 +266,7 @@ void Emulator::Run(const std::filesystem::path& file) { } void Emulator::LoadSystemModules(const std::filesystem::path& file, std::string game_serial) { - constexpr std::array ModulesToLoad{ + constexpr std::array ModulesToLoad{ {{"libSceNgs2.sprx", &Libraries::Ngs2::RegisterlibSceNgs2}, {"libSceFiber.sprx", &Libraries::Fiber::RegisterlibSceFiber}, {"libSceUlt.sprx", nullptr}, diff --git a/src/shader_recompiler/frontend/translate/scalar_alu.cpp b/src/shader_recompiler/frontend/translate/scalar_alu.cpp index de8b9da87..75ad957b3 100644 --- a/src/shader_recompiler/frontend/translate/scalar_alu.cpp +++ b/src/shader_recompiler/frontend/translate/scalar_alu.cpp @@ -50,6 +50,8 @@ void Translator::EmitScalarAlu(const GcnInst& inst) { return S_OR_B64(NegateMode::None, false, inst); case Opcode::S_XOR_B32: return S_XOR_B32(inst); + case Opcode::S_NOT_B32: + return S_NOT_B32(inst); case Opcode::S_XOR_B64: return S_OR_B64(NegateMode::None, true, inst); case Opcode::S_ANDN2_B32: @@ -94,6 +96,8 @@ void Translator::EmitScalarAlu(const GcnInst& inst) { return S_BREV_B32(inst); case Opcode::S_BCNT1_I32_B64: return S_BCNT1_I32_B64(inst); + case Opcode::S_FF1_I32_B64: + return S_FF1_I32_B64(inst); case Opcode::S_AND_SAVEEXEC_B64: return S_SAVEEXEC_B64(NegateMode::None, false, inst); case Opcode::S_ORN2_SAVEEXEC_B64: @@ -301,6 +305,10 @@ void Translator::S_AND_B64(NegateMode negate, const GcnInst& inst) { ASSERT_MSG(-s32(operand.code) + SignedConstIntNegMin - 1 == -1, "SignedConstIntNeg must be -1"); return ir.Imm1(true); + case OperandField::LiteralConst: + ASSERT_MSG(operand.code == 0 || operand.code == std::numeric_limits::max(), + "Unsupported literal {:#x}", operand.code); + return ir.Imm1(operand.code & 1); default: UNREACHABLE(); } @@ -382,6 +390,13 @@ void Translator::S_XOR_B32(const GcnInst& inst) { ir.SetScc(ir.INotEqual(result, ir.Imm32(0))); } +void Translator::S_NOT_B32(const GcnInst& inst) { + const IR::U32 src0{GetSrc(inst.src[0])}; + const IR::U32 result{ir.BitwiseNot(src0)}; + SetDst(inst.dst[0], result); + ir.SetScc(ir.INotEqual(result, ir.Imm32(0))); +} + void Translator::S_LSHL_B32(const GcnInst& inst) { const IR::U32 src0{GetSrc(inst.src[0])}; const IR::U32 src1{GetSrc(inst.src[1])}; @@ -560,6 +575,12 @@ void Translator::S_BCNT1_I32_B64(const GcnInst& inst) { ir.SetScc(ir.INotEqual(result, ir.Imm32(0))); } +void Translator::S_FF1_I32_B64(const GcnInst& inst) { + const IR::U32 src0{GetSrc(inst.src[0])}; + const IR::U32 result{ir.Select(ir.IEqual(src0, ir.Imm32(0U)), ir.Imm32(-1), ir.FindILsb(src0))}; + SetDst(inst.dst[0], result); +} + void Translator::S_SAVEEXEC_B64(NegateMode negate, bool is_or, const GcnInst& inst) { // This instruction normally operates on 64-bit data (EXEC, VCC, SGPRs) // However here we flatten it to 1-bit EXEC and 1-bit VCC. For the destination diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index 3b89372bd..dd379d8ea 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -96,6 +96,7 @@ public: void S_MUL_I32(const GcnInst& inst); void S_BFE_U32(const GcnInst& inst); void S_ABSDIFF_I32(const GcnInst& inst); + void S_NOT_B32(const GcnInst& inst); // SOPK void S_MOVK(const GcnInst& inst); @@ -109,6 +110,7 @@ public: void S_NOT_B64(const GcnInst& inst); void S_BREV_B32(const GcnInst& inst); void S_BCNT1_I32_B64(const GcnInst& inst); + void S_FF1_I32_B64(const GcnInst& inst); void S_GETPC_B64(u32 pc, const GcnInst& inst); void S_SAVEEXEC_B64(NegateMode negate, bool is_or, const GcnInst& inst); diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index 1bbd77f82..c0c5f1b2f 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -565,7 +565,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); - if (dma_data->dst_addr_lo == 0x3022C) { + if (dma_data->dst_addr_lo == 0x3022C || !rasterizer) { break; } if (dma_data->src_sel == DmaDataSrc::Data && dma_data->dst_sel == DmaDataDst::Gds) { @@ -700,7 +700,7 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, int vqid) { } case PM4ItOpcode::DmaData: { const auto* dma_data = reinterpret_cast(header); - if (dma_data->dst_addr_lo == 0x3022C) { + if (dma_data->dst_addr_lo == 0x3022C || !rasterizer) { break; } if (dma_data->src_sel == DmaDataSrc::Data && dma_data->dst_sel == DmaDataDst::Gds) { From 17abbcd74d5c21badda079a8e71fa9fa4c20ea30 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Thu, 5 Dec 2024 16:21:35 -0800 Subject: [PATCH 109/549] misc: Fix clang format (#1673) --- src/video_core/amdgpu/liverpool.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index c0c5f1b2f..a4eae8e7a 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -163,8 +163,8 @@ Liverpool::Task Liverpool::ProcessCeUpdate(std::span ccb) { } case PM4ItOpcode::IndirectBufferConst: { const auto* indirect_buffer = reinterpret_cast(header); - auto task = ProcessCeUpdate( - {indirect_buffer->Address(), indirect_buffer->ib_size}); + auto task = + ProcessCeUpdate({indirect_buffer->Address(), indirect_buffer->ib_size}); while (!task.handle.done()) { task.handle.resume(); From d05846a327e609ba514c981c0f28777c77914271 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 6 Dec 2024 02:59:55 -0800 Subject: [PATCH 110/549] specialization: Fix fetch shader field type (#1675) --- src/shader_recompiler/frontend/fetch_shader.h | 13 ------------- src/shader_recompiler/specialization.h | 19 +++++++++---------- .../renderer_vulkan/vk_pipeline_cache.cpp | 2 ++ .../renderer_vulkan/vk_rasterizer.cpp | 18 +++++++++++++++++- 4 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/shader_recompiler/frontend/fetch_shader.h b/src/shader_recompiler/frontend/fetch_shader.h index ee9f5c805..080b0eb22 100644 --- a/src/shader_recompiler/frontend/fetch_shader.h +++ b/src/shader_recompiler/frontend/fetch_shader.h @@ -58,19 +58,6 @@ struct FetchShaderData { }) != attributes.end(); } - [[nodiscard]] std::pair GetDrawOffsets(const AmdGpu::Liverpool::Regs& regs, - const Info& info) const { - u32 vertex_offset = regs.index_offset; - u32 instance_offset = 0; - if (vertex_offset == 0 && vertex_offset_sgpr != -1) { - vertex_offset = info.user_data[vertex_offset_sgpr]; - } - if (instance_offset_sgpr != -1) { - instance_offset = info.user_data[instance_offset_sgpr]; - } - return {vertex_offset, instance_offset}; - } - bool operator==(const FetchShaderData& other) const { return attributes == other.attributes && vertex_offset_sgpr == other.vertex_offset_sgpr && instance_offset_sgpr == other.instance_offset_sgpr; diff --git a/src/shader_recompiler/specialization.h b/src/shader_recompiler/specialization.h index 740b89dda..82c064640 100644 --- a/src/shader_recompiler/specialization.h +++ b/src/shader_recompiler/specialization.h @@ -57,7 +57,7 @@ struct StageSpecialization { const Shader::Info* info; RuntimeInfo runtime_info; - Gcn::FetchShaderData fetch_shader_data{}; + std::optional fetch_shader_data{}; boost::container::small_vector vs_attribs; std::bitset bitset{}; boost::container::small_vector buffers; @@ -69,15 +69,14 @@ struct StageSpecialization { explicit StageSpecialization(const Info& info_, RuntimeInfo runtime_info_, const Profile& profile_, Backend::Bindings start_) : info{&info_}, runtime_info{runtime_info_}, start{start_} { - if (const auto fetch_shader = Gcn::ParseFetchShader(info_)) { - fetch_shader_data = *fetch_shader; - if (info_.stage == Stage::Vertex && !profile_.support_legacy_vertex_attributes) { - // Specialize shader on VS input number types to follow spec. - ForEachSharp(vs_attribs, fetch_shader_data.attributes, - [](auto& spec, const auto& desc, AmdGpu::Buffer sharp) { - spec.num_class = AmdGpu::GetNumberClass(sharp.GetNumberFmt()); - }); - } + fetch_shader_data = Gcn::ParseFetchShader(info_); + if (info_.stage == Stage::Vertex && fetch_shader_data && + !profile_.support_legacy_vertex_attributes) { + // Specialize shader on VS input number types to follow spec. + ForEachSharp(vs_attribs, fetch_shader_data->attributes, + [](auto& spec, const auto& desc, AmdGpu::Buffer sharp) { + spec.num_class = AmdGpu::GetNumberClass(sharp.GetNumberFmt()); + }); } u32 binding{}; if (info->has_readconst) { diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 47713f0ff..82a029b95 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -279,6 +279,8 @@ bool PipelineCache::RefreshGraphicsKey() { ++remapped_cb; } + fetch_shader = std::nullopt; + Shader::Backend::Bindings binding{}; const auto& TryBindStageRemap = [&](Shader::Stage stage_in, Shader::Stage stage_out) -> bool { const auto stage_in_idx = static_cast(stage_in); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 620e5f103..e2b6d9749 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -171,6 +171,22 @@ RenderState Rasterizer::PrepareRenderState(u32 mrt_mask) { return state; } +[[nodiscard]] std::pair GetDrawOffsets( + const AmdGpu::Liverpool::Regs& regs, const Shader::Info& info, + const std::optional& fetch_shader) { + u32 vertex_offset = regs.index_offset; + u32 instance_offset = 0; + if (fetch_shader) { + if (vertex_offset == 0 && fetch_shader->vertex_offset_sgpr != -1) { + vertex_offset = info.user_data[fetch_shader->vertex_offset_sgpr]; + } + if (fetch_shader->instance_offset_sgpr != -1) { + instance_offset = info.user_data[fetch_shader->instance_offset_sgpr]; + } + } + return {vertex_offset, instance_offset}; +} + void Rasterizer::Draw(bool is_indexed, u32 index_offset) { RENDERER_TRACE; @@ -198,7 +214,7 @@ void Rasterizer::Draw(bool is_indexed, u32 index_offset) { BeginRendering(*pipeline, state); UpdateDynamicState(*pipeline); - const auto [vertex_offset, instance_offset] = fetch_shader->GetDrawOffsets(regs, vs_info); + const auto [vertex_offset, instance_offset] = GetDrawOffsets(regs, vs_info, fetch_shader); const auto cmdbuf = scheduler.CommandBuffer(); cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline->Handle()); From 9e618c0e0c14d9fd7daf040509d71392356054be Mon Sep 17 00:00:00 2001 From: TheTurtle Date: Fri, 6 Dec 2024 19:54:59 +0200 Subject: [PATCH 111/549] video_core: Add multipler to handle special cases of texture buffer stride mismatch (#1640) * page_manager: Enable userfaultfd by default * Much faster than page faults and causes less problems * shader_recompiler: Add texel buffer multiplier * Fixes format mismatch assert when vsharp stride is multiple of format stride * shader_recompiler: Specialize UBOs on size * Some games can perform manual vertex pulling and thus bind read only buffers of varying size. We only recompile when the vsharp size is larger than size in shader, in opposite case its not needed * clang format --- CMakeLists.txt | 4 ++++ .../backend/spirv/emit_spirv_context_get_set.cpp | 8 ++++++-- .../backend/spirv/spirv_emit_context.cpp | 2 ++ .../backend/spirv/spirv_emit_context.h | 1 + src/shader_recompiler/info.h | 5 +++++ src/shader_recompiler/specialization.h | 10 ++++++++-- src/video_core/page_manager.cpp | 2 +- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 5 +++-- 8 files changed, 30 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 378b8f78d..ae6d1d74e 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -875,6 +875,10 @@ target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAlloca target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h") target_compile_definitions(Dear_ImGui PRIVATE IMGUI_USER_CONFIG="${PROJECT_SOURCE_DIR}/src/imgui/imgui_config.h") +if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + target_compile_definitions(shadps4 PRIVATE ENABLE_USERFAULTFD) +endif() + if (APPLE) option(USE_SYSTEM_VULKAN_LOADER "Enables using the system Vulkan loader instead of directly linking with MoltenVK. Useful for loading validation layers." OFF) if (USE_SYSTEM_VULKAN_LOADER) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp index d8c0a17bd..b578f0c52 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp @@ -326,7 +326,9 @@ Id EmitLoadBufferU32x4(EmitContext& ctx, IR::Inst*, u32 handle, Id address) { Id EmitLoadBufferFormatF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) { const auto& buffer = ctx.texture_buffers[handle]; const Id tex_buffer = ctx.OpLoad(buffer.image_type, buffer.id); - const Id coord = ctx.OpIAdd(ctx.U32[1], address, buffer.coord_offset); + const Id coord = + ctx.OpIAdd(ctx.U32[1], ctx.OpShiftLeftLogical(ctx.U32[1], address, buffer.coord_shift), + buffer.coord_offset); Id texel = buffer.is_storage ? ctx.OpImageRead(buffer.result_type, tex_buffer, coord) : ctx.OpImageFetch(buffer.result_type, tex_buffer, coord); if (buffer.is_integer) { @@ -372,7 +374,9 @@ void EmitStoreBufferU32x4(EmitContext& ctx, IR::Inst* inst, u32 handle, Id addre void EmitStoreBufferFormatF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) { const auto& buffer = ctx.texture_buffers[handle]; const Id tex_buffer = ctx.OpLoad(buffer.image_type, buffer.id); - const Id coord = ctx.OpIAdd(ctx.U32[1], address, buffer.coord_offset); + const Id coord = + ctx.OpIAdd(ctx.U32[1], ctx.OpShiftLeftLogical(ctx.U32[1], address, buffer.coord_shift), + buffer.coord_offset); if (buffer.is_integer) { value = ctx.OpBitcast(buffer.result_type, value); } diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 4ce9f4221..5c7278c6b 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -207,6 +207,8 @@ void EmitContext::DefineBufferOffsets() { push_data_block, ConstU32(half), ConstU32(comp))}; const Id value{OpLoad(U32[1], ptr)}; tex_buffer.coord_offset = OpBitFieldUExtract(U32[1], value, ConstU32(offset), ConstU32(6U)); + tex_buffer.coord_shift = + OpBitFieldUExtract(U32[1], value, ConstU32(offset + 6U), ConstU32(2U)); Name(tex_buffer.coord_offset, fmt::format("texbuf{}_off", binding)); } } diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index 1c5da946d..4e5e7dd3b 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -223,6 +223,7 @@ public: struct TextureBufferDefinition { Id id; Id coord_offset; + Id coord_shift; u32 binding; Id image_type; Id result_type; diff --git a/src/shader_recompiler/info.h b/src/shader_recompiler/info.h index d382d0e7c..494bbb4bb 100644 --- a/src/shader_recompiler/info.h +++ b/src/shader_recompiler/info.h @@ -105,6 +105,11 @@ struct PushData { ASSERT(offset < 256 && binding < buf_offsets.size()); buf_offsets[binding] = offset; } + + void AddTexelOffset(u32 binding, u32 multiplier, u32 texel_offset) { + ASSERT(texel_offset < 64 && multiplier < 16); + buf_offsets[binding] = texel_offset | ((std::bit_width(multiplier) - 1) << 6); + } }; static_assert(sizeof(PushData) <= 128, "PushData size is greater than minimum size guaranteed by Vulkan spec"); diff --git a/src/shader_recompiler/specialization.h b/src/shader_recompiler/specialization.h index 82c064640..2a3bd62f4 100644 --- a/src/shader_recompiler/specialization.h +++ b/src/shader_recompiler/specialization.h @@ -9,7 +9,6 @@ #include "frontend/fetch_shader.h" #include "shader_recompiler/backend/bindings.h" #include "shader_recompiler/info.h" -#include "shader_recompiler/ir/passes/srt.h" namespace Shader { @@ -22,8 +21,12 @@ struct VsAttribSpecialization { struct BufferSpecialization { u16 stride : 14; u16 is_storage : 1; + u32 size = 0; - auto operator<=>(const BufferSpecialization&) const = default; + bool operator==(const BufferSpecialization& other) const { + return stride == other.stride && is_storage == other.is_storage && + (size >= other.is_storage || is_storage); + } }; struct TextureBufferSpecialization { @@ -86,6 +89,9 @@ struct StageSpecialization { [](auto& spec, const auto& desc, AmdGpu::Buffer sharp) { spec.stride = sharp.GetStride(); spec.is_storage = desc.IsStorage(sharp); + if (!spec.is_storage) { + spec.size = sharp.GetSize(); + } }); ForEachSharp(binding, tex_buffers, info->texture_buffers, [](auto& spec, const auto& desc, AmdGpu::Buffer sharp) { diff --git a/src/video_core/page_manager.cpp b/src/video_core/page_manager.cpp index d26a7067a..80b91b825 100644 --- a/src/video_core/page_manager.cpp +++ b/src/video_core/page_manager.cpp @@ -29,7 +29,7 @@ namespace VideoCore { constexpr size_t PAGESIZE = 4_KB; constexpr size_t PAGEBITS = 12; -#if ENABLE_USERFAULTFD +#ifdef ENABLE_USERFAULTFD struct PageManager::Impl { Impl(Vulkan::Rasterizer* rasterizer_) : rasterizer{rasterizer_} { uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index e2b6d9749..4e858c0d3 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -548,12 +548,13 @@ void Rasterizer::BindBuffers(const Shader::Info& stage, Shader::Backend::Binding const auto [vk_buffer, offset] = buffer_cache.ObtainBuffer( vsharp.base_address, vsharp.GetSize(), desc.is_written, true, buffer_id); const u32 fmt_stride = AmdGpu::NumBits(vsharp.GetDataFmt()) >> 3; - ASSERT_MSG(fmt_stride == vsharp.GetStride(), + const u32 buf_stride = vsharp.GetStride(); + ASSERT_MSG(buf_stride % fmt_stride == 0, "Texel buffer stride must match format stride"); const u32 offset_aligned = Common::AlignDown(offset, alignment); const u32 adjust = offset - offset_aligned; ASSERT(adjust % fmt_stride == 0); - push_data.AddOffset(binding.buffer, adjust / fmt_stride); + push_data.AddTexelOffset(binding.buffer, buf_stride / fmt_stride, adjust / fmt_stride); buffer_view = vk_buffer->View(offset_aligned, vsharp.GetSize() + adjust, desc.is_written, vsharp.GetDataFmt(), vsharp.GetNumberFmt()); From 6acfdd5e33cdb4b2484e162916242f40eaa9dcc6 Mon Sep 17 00:00:00 2001 From: IndecisiveTurtle Date: Fri, 6 Dec 2024 20:00:21 +0200 Subject: [PATCH 112/549] buffer_cache: Bump usable address space to 40bits * Fixes crashes in games that use the upper region of user area --- src/video_core/buffer_cache/buffer_cache.h | 2 +- src/video_core/buffer_cache/memory_tracker_base.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index b1bf77f8a..3dab95db7 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -42,7 +42,7 @@ public: struct Traits { using Entry = BufferId; - static constexpr size_t AddressSpaceBits = 39; + static constexpr size_t AddressSpaceBits = 40; static constexpr size_t FirstLevelBits = 14; static constexpr size_t PageBits = CACHING_PAGEBITS; }; diff --git a/src/video_core/buffer_cache/memory_tracker_base.h b/src/video_core/buffer_cache/memory_tracker_base.h index 375701c4c..a59bcfff5 100644 --- a/src/video_core/buffer_cache/memory_tracker_base.h +++ b/src/video_core/buffer_cache/memory_tracker_base.h @@ -14,7 +14,7 @@ namespace VideoCore { class MemoryTracker { public: - static constexpr size_t MAX_CPU_PAGE_BITS = 39; + static constexpr size_t MAX_CPU_PAGE_BITS = 40; static constexpr size_t HIGHER_PAGE_BITS = 22; static constexpr size_t HIGHER_PAGE_SIZE = 1ULL << HIGHER_PAGE_BITS; static constexpr size_t HIGHER_PAGE_MASK = HIGHER_PAGE_SIZE - 1ULL; From 357b7829c3eee85d59b620fcfde8562195f50ce2 Mon Sep 17 00:00:00 2001 From: IndecisiveTurtle Date: Fri, 6 Dec 2024 21:50:25 +0200 Subject: [PATCH 113/549] hot-fix: Silence depth macrotiled warning --- src/video_core/texture_cache/tile_manager.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/video_core/texture_cache/tile_manager.cpp b/src/video_core/texture_cache/tile_manager.cpp index 7430168d0..9823cb4dc 100644 --- a/src/video_core/texture_cache/tile_manager.cpp +++ b/src/video_core/texture_cache/tile_manager.cpp @@ -392,7 +392,8 @@ std::pair TileManager::TryDetile(vk::Buffer in_buffer, u32 in_o const auto* detiler = GetDetiler(image); if (!detiler) { if (image.info.tiling_mode != AmdGpu::TilingMode::Texture_MacroTiled && - image.info.tiling_mode != AmdGpu::TilingMode::Display_MacroTiled) { + image.info.tiling_mode != AmdGpu::TilingMode::Display_MacroTiled && + image.info.tiling_mode != AmdGpu::TilingMode::Depth_MacroTiled) { LOG_ERROR(Render_Vulkan, "Unsupported tiled image: {} ({})", vk::to_string(image.info.pixel_format), NameOf(image.info.tiling_mode)); } From 7ffa581d4b1aea106485ab3e6957836b5ad22f02 Mon Sep 17 00:00:00 2001 From: "Daniel R." <47796739+polybiusproxy@users.noreply.github.com> Date: Fri, 6 Dec 2024 22:04:36 +0100 Subject: [PATCH 114/549] The way to Unity, pt.2 (#1671) --- CMakeLists.txt | 5 +- src/common/ntapi.cpp | 2 - src/common/ntapi.h | 9 +- src/common/thread.cpp | 14 ++- src/common/thread.h | 2 + src/core/devices/logger.cpp | 1 + src/core/libraries/kernel/sync/mutex.cpp | 52 ++++++++ src/core/libraries/kernel/sync/mutex.h | 80 ++++++++++++ src/core/libraries/kernel/sync/semaphore.h | 117 ++++++++++++++++++ src/core/libraries/kernel/threads/condvar.cpp | 4 +- .../libraries/kernel/threads/event_flag.cpp | 1 - src/core/libraries/kernel/threads/pthread.cpp | 1 + src/core/libraries/kernel/threads/pthread.h | 8 +- .../libraries/kernel/threads/semaphore.cpp | 26 ++-- src/core/libraries/kernel/time.cpp | 17 ++- 15 files changed, 311 insertions(+), 28 deletions(-) create mode 100644 src/core/libraries/kernel/sync/mutex.cpp create mode 100644 src/core/libraries/kernel/sync/mutex.h create mode 100644 src/core/libraries/kernel/sync/semaphore.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ae6d1d74e..84146bb01 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -210,7 +210,10 @@ set(GNM_LIB src/core/libraries/gnmdriver/gnmdriver.cpp src/core/libraries/gnmdriver/gnm_error.h ) -set(KERNEL_LIB src/core/libraries/kernel/threads/condvar.cpp +set(KERNEL_LIB src/core/libraries/kernel/sync/mutex.cpp + src/core/libraries/kernel/sync/mutex.h + src/core/libraries/kernel/sync/semaphore.h + src/core/libraries/kernel/threads/condvar.cpp src/core/libraries/kernel/threads/event_flag.cpp src/core/libraries/kernel/threads/exception.cpp src/core/libraries/kernel/threads/exception.h diff --git a/src/common/ntapi.cpp b/src/common/ntapi.cpp index ffdedb17f..e0ff1cef0 100644 --- a/src/common/ntapi.cpp +++ b/src/common/ntapi.cpp @@ -6,7 +6,6 @@ #include "ntapi.h" NtClose_t NtClose = nullptr; -NtDelayExecution_t NtDelayExecution = nullptr; NtSetInformationFile_t NtSetInformationFile = nullptr; NtCreateThread_t NtCreateThread = nullptr; NtTerminateThread_t NtTerminateThread = nullptr; @@ -18,7 +17,6 @@ void Initialize() { // http://stackoverflow.com/a/31411628/4725495 NtClose = (NtClose_t)GetProcAddress(nt_handle, "NtClose"); - NtDelayExecution = (NtDelayExecution_t)GetProcAddress(nt_handle, "NtDelayExecution"); NtSetInformationFile = (NtSetInformationFile_t)GetProcAddress(nt_handle, "NtSetInformationFile"); NtCreateThread = (NtCreateThread_t)GetProcAddress(nt_handle, "NtCreateThread"); diff --git a/src/common/ntapi.h b/src/common/ntapi.h index 743174061..cb1ba7f1c 100644 --- a/src/common/ntapi.h +++ b/src/common/ntapi.h @@ -408,7 +408,7 @@ typedef struct _TEB { /* win32/win64 */ #ifdef _WIN64 PVOID SystemReserved1[30]; /* /0190 */ #else - PVOID SystemReserved1[26]; /* 10c/ used for krnl386 private data in Wine */ + PVOID SystemReserved1[26]; /* 10c/ */ #endif char PlaceholderCompatibilityMode; /* 174/0280 */ BOOLEAN PlaceholderHydrationAlwaysExplicit; /* 175/0281 */ @@ -430,13 +430,13 @@ typedef struct _TEB { /* win32/win64 */ BYTE SpareBytes1[23]; /* 1b9/ */ ULONG TxFsContext; /* 1d0/ */ #endif - GDI_TEB_BATCH GdiTebBatch; /* 1d4/02f0 used for ntdll private data in Wine */ + GDI_TEB_BATCH GdiTebBatch; /* 1d4/02f0 */ CLIENT_ID RealClientId; /* 6b4/07d8 */ HANDLE GdiCachedProcessHandle; /* 6bc/07e8 */ ULONG GdiClientPID; /* 6c0/07f0 */ ULONG GdiClientTID; /* 6c4/07f4 */ PVOID GdiThreadLocaleInfo; /* 6c8/07f8 */ - ULONG_PTR Win32ClientInfo[62]; /* 6cc/0800 used for user32 private data in Wine */ + ULONG_PTR Win32ClientInfo[62]; /* 6cc/0800 */ PVOID glDispatchTable[233]; /* 7c4/09f0 */ PVOID glReserved1[29]; /* b68/1138 */ PVOID glReserved2; /* bdc/1220 */ @@ -511,8 +511,6 @@ static_assert(offsetof(TEB, DeallocationStack) == typedef u64(__stdcall* NtClose_t)(HANDLE Handle); -typedef u64(__stdcall* NtDelayExecution_t)(BOOL Alertable, PLARGE_INTEGER DelayInterval); - typedef u64(__stdcall* NtSetInformationFile_t)(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length, FILE_INFORMATION_CLASS FileInformationClass); @@ -525,7 +523,6 @@ typedef u64(__stdcall* NtCreateThread_t)(PHANDLE ThreadHandle, ACCESS_MASK Desir typedef u64(__stdcall* NtTerminateThread_t)(HANDLE ThreadHandle, u64 ExitStatus); extern NtClose_t NtClose; -extern NtDelayExecution_t NtDelayExecution; extern NtSetInformationFile_t NtSetInformationFile; extern NtCreateThread_t NtCreateThread; extern NtTerminateThread_t NtTerminateThread; diff --git a/src/common/thread.cpp b/src/common/thread.cpp index 46df68c38..c87aea6ef 100644 --- a/src/common/thread.cpp +++ b/src/common/thread.cpp @@ -147,6 +147,10 @@ void SetCurrentThreadName(const char* name) { SetThreadDescription(GetCurrentThread(), UTF8ToUTF16W(name).data()); } +void SetThreadName(void* thread, const char* name) { + SetThreadDescription(thread, UTF8ToUTF16W(name).data()); +} + #else // !MSVC_VER, so must be POSIX threads // MinGW with the POSIX threading model does not support pthread_setname_np @@ -170,11 +174,19 @@ void SetCurrentThreadName(const char* name) { pthread_setname_np(pthread_self(), name); #endif } + +void SetThreadName(void* thread, const char* name) { + // TODO +} #endif #if defined(_WIN32) void SetCurrentThreadName(const char*) { - // Do Nothing on MingW + // Do Nothing on MinGW +} + +void SetThreadName(void* thread, const char* name) { + // Do Nothing on MinGW } #endif diff --git a/src/common/thread.h b/src/common/thread.h index fd962f8e5..175ba9445 100644 --- a/src/common/thread.h +++ b/src/common/thread.h @@ -23,6 +23,8 @@ void SetCurrentThreadPriority(ThreadPriority new_priority); void SetCurrentThreadName(const char* name); +void SetThreadName(void* thread, const char* name); + class AccurateTimer { std::chrono::nanoseconds target_interval{}; std::chrono::nanoseconds total_wait{}; diff --git a/src/core/devices/logger.cpp b/src/core/devices/logger.cpp index bf5a28382..6f104509c 100644 --- a/src/core/devices/logger.cpp +++ b/src/core/devices/logger.cpp @@ -15,6 +15,7 @@ s64 Logger::write(const void* buf, size_t nbytes) { log(static_cast(buf), nbytes); return nbytes; } + size_t Logger::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { for (int i = 0; i < iovcnt; i++) { log(static_cast(iov[i].iov_base), iov[i].iov_len); diff --git a/src/core/libraries/kernel/sync/mutex.cpp b/src/core/libraries/kernel/sync/mutex.cpp new file mode 100644 index 000000000..c5e3eba1d --- /dev/null +++ b/src/core/libraries/kernel/sync/mutex.cpp @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "mutex.h" + +#include "common/assert.h" + +namespace Libraries::Kernel { + +TimedMutex::TimedMutex() { +#ifdef _WIN64 + mtx = CreateMutex(nullptr, false, nullptr); + ASSERT(mtx); +#endif +} + +TimedMutex::~TimedMutex() { +#ifdef _WIN64 + CloseHandle(mtx); +#endif +} + +void TimedMutex::lock() { +#ifdef _WIN64 + for (;;) { + u64 res = WaitForSingleObjectEx(mtx, INFINITE, true); + if (res == WAIT_OBJECT_0) { + return; + } + } +#else + mtx.lock(); +#endif +} + +bool TimedMutex::try_lock() { +#ifdef _WIN64 + return WaitForSingleObjectEx(mtx, 0, true) == WAIT_OBJECT_0; +#else + return mtx.try_lock(); +#endif +} + +void TimedMutex::unlock() { +#ifdef _WIN64 + ReleaseMutex(mtx); +#else + mtx.unlock(); +#endif +} + +} // namespace Libraries::Kernel \ No newline at end of file diff --git a/src/core/libraries/kernel/sync/mutex.h b/src/core/libraries/kernel/sync/mutex.h new file mode 100644 index 000000000..f14a920b4 --- /dev/null +++ b/src/core/libraries/kernel/sync/mutex.h @@ -0,0 +1,80 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "common/types.h" + +#ifdef _WIN64 +#include +#else +#include +#endif + +namespace Libraries::Kernel { + +class TimedMutex { +public: + TimedMutex(); + ~TimedMutex(); + + void lock(); + bool try_lock(); + + void unlock(); + + template + bool try_lock_for(const std::chrono::duration& rel_time) { +#ifdef _WIN64 + constexpr auto zero = std::chrono::duration::zero(); + const auto now = std::chrono::steady_clock::now(); + + std::chrono::steady_clock::time_point abs_time = now; + if (rel_time > zero) { + constexpr auto max = (std::chrono::steady_clock::time_point::max)(); + if (abs_time < max - rel_time) { + abs_time += rel_time; + } else { + abs_time = max; + } + } + + return try_lock_until(abs_time); +#else + return mtx.try_lock_for(rel_time); +#endif + } + + template + bool try_lock_until(const std::chrono::time_point& abs_time) { +#ifdef _WIN64 + for (;;) { + const auto now = Clock::now(); + if (abs_time <= now) { + return false; + } + + const auto rel_ms = std::chrono::ceil(abs_time - now); + u64 res = WaitForSingleObjectEx(mtx, static_cast(rel_ms.count()), true); + if (res == WAIT_OBJECT_0) { + return true; + } else if (res == WAIT_TIMEOUT) { + return false; + } + } +#else + return mtx.try_lock_until(abs_time); +#endif + } + +private: +#ifdef _WIN64 + HANDLE mtx; +#else + std::timed_mutex mtx; +#endif +}; + +} // namespace Libraries::Kernel \ No newline at end of file diff --git a/src/core/libraries/kernel/sync/semaphore.h b/src/core/libraries/kernel/sync/semaphore.h new file mode 100644 index 000000000..a103472c8 --- /dev/null +++ b/src/core/libraries/kernel/sync/semaphore.h @@ -0,0 +1,117 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#include "common/assert.h" +#include "common/types.h" + +#ifdef _WIN64 +#include +#else +#include +#endif + +namespace Libraries::Kernel { + +template +class Semaphore { +public: + Semaphore(s32 initialCount) +#ifndef _WIN64 + : sem{initialCount} +#endif + { +#ifdef _WIN64 + sem = CreateSemaphore(nullptr, initialCount, max, nullptr); + ASSERT(sem); +#endif + } + + ~Semaphore() { +#ifdef _WIN64 + CloseHandle(sem); +#endif + } + + void release() { +#ifdef _WIN64 + ReleaseSemaphore(sem, 1, nullptr); +#else + sem.release(); +#endif + } + + void acquire() { +#ifdef _WIN64 + for (;;) { + u64 res = WaitForSingleObjectEx(sem, INFINITE, true); + if (res == WAIT_OBJECT_0) { + return; + } + } +#else + sem.acquire(); +#endif + } + + bool try_acquire() { +#ifdef _WIN64 + return WaitForSingleObjectEx(sem, 0, true) == WAIT_OBJECT_0; +#else + return sem.try_acquire(); +#endif + } + + template + bool try_acquire_for(const std::chrono::duration& rel_time) { +#ifdef _WIN64 + const auto rel_time_ms = std::chrono::ceil(rel_time); + const u64 timeout_ms = static_cast(rel_time_ms.count()); + + if (timeout_ms == 0) { + return false; + } + + return WaitForSingleObjectEx(sem, timeout_ms, true) == WAIT_OBJECT_0; +#else + return sem.try_acquire_for(rel_time); +#endif + } + + template + bool try_acquire_until(const std::chrono::time_point& abs_time) { +#ifdef _WIN64 + const auto now = Clock::now(); + if (now >= abs_time) { + return false; + } + + const auto rel_time = std::chrono::ceil(abs_time - now); + const u64 timeout_ms = static_cast(rel_time.count()); + if (timeout_ms == 0) { + return false; + } + + u64 res = WaitForSingleObjectEx(sem, static_cast(timeout_ms), true); + return res == WAIT_OBJECT_0; +#else + return sem.try_acquire_until(abs_time); +#endif + } + +private: +#ifdef _WIN64 + HANDLE sem; +#else + std::counting_semaphore sem; +#endif +}; + +using BinarySemaphore = Semaphore<1>; +using CountingSemaphore = Semaphore<0x7FFFFFFF /*ORBIS_KERNEL_SEM_VALUE_MAX*/>; + +} // namespace Libraries::Kernel \ No newline at end of file diff --git a/src/core/libraries/kernel/threads/condvar.cpp b/src/core/libraries/kernel/threads/condvar.cpp index cbe8f6ca7..2927899d9 100644 --- a/src/core/libraries/kernel/threads/condvar.cpp +++ b/src/core/libraries/kernel/threads/condvar.cpp @@ -191,7 +191,7 @@ int PthreadCond::Signal() { PthreadMutex* mp = td->mutex_obj; has_user_waiters = SleepqRemove(sq, td); - std::binary_semaphore* waddr = nullptr; + BinarySemaphore* waddr = nullptr; if (mp->m_owner == curthread) { if (curthread->nwaiter_defer >= Pthread::MaxDeferWaiters) { curthread->WakeAll(); @@ -211,7 +211,7 @@ int PthreadCond::Signal() { struct BroadcastArg { Pthread* curthread; - std::binary_semaphore* waddrs[Pthread::MaxDeferWaiters]; + BinarySemaphore* waddrs[Pthread::MaxDeferWaiters]; int count; }; diff --git a/src/core/libraries/kernel/threads/event_flag.cpp b/src/core/libraries/kernel/threads/event_flag.cpp index 39925153c..ce75bed9e 100644 --- a/src/core/libraries/kernel/threads/event_flag.cpp +++ b/src/core/libraries/kernel/threads/event_flag.cpp @@ -118,7 +118,6 @@ public: } m_bits |= bits; - m_cond_var.notify_all(); } diff --git a/src/core/libraries/kernel/threads/pthread.cpp b/src/core/libraries/kernel/threads/pthread.cpp index a562c51b2..b2fe09934 100644 --- a/src/core/libraries/kernel/threads/pthread.cpp +++ b/src/core/libraries/kernel/threads/pthread.cpp @@ -380,6 +380,7 @@ int PS4_SYSV_ABI posix_sched_get_priority_min() { int PS4_SYSV_ABI posix_pthread_rename_np(PthreadT thread, const char* name) { LOG_INFO(Kernel_Pthread, "name = {}", name); + Common::SetThreadName(reinterpret_cast(thread->native_thr.GetHandle()), name); thread->name = name; return ORBIS_OK; } diff --git a/src/core/libraries/kernel/threads/pthread.h b/src/core/libraries/kernel/threads/pthread.h index 9d71c75e8..456c2ef37 100644 --- a/src/core/libraries/kernel/threads/pthread.h +++ b/src/core/libraries/kernel/threads/pthread.h @@ -11,6 +11,8 @@ #include #include "common/enum.h" +#include "core/libraries/kernel/sync/mutex.h" +#include "core/libraries/kernel/sync/semaphore.h" #include "core/libraries/kernel/time.h" #include "core/thread.h" #include "core/tls.h" @@ -44,7 +46,7 @@ enum class PthreadMutexProt : u32 { }; struct PthreadMutex { - std::timed_mutex m_lock; + TimedMutex m_lock; PthreadMutexFlags m_flags; Pthread* m_owner; int m_count; @@ -288,14 +290,14 @@ struct Pthread { int report_events; int event_mask; std::string name; - std::binary_semaphore wake_sema{0}; + BinarySemaphore wake_sema{0}; SleepQueue* sleepqueue; void* wchan; PthreadMutex* mutex_obj; bool will_sleep; bool has_user_waiters; int nwaiter_defer; - std::binary_semaphore* defer_waiters[MaxDeferWaiters]; + BinarySemaphore* defer_waiters[MaxDeferWaiters]; bool InCritical() const noexcept { return locklevel > 0 || critical_count > 0; diff --git a/src/core/libraries/kernel/threads/semaphore.cpp b/src/core/libraries/kernel/threads/semaphore.cpp index e3c7e9092..5aa04f251 100644 --- a/src/core/libraries/kernel/threads/semaphore.cpp +++ b/src/core/libraries/kernel/threads/semaphore.cpp @@ -6,6 +6,8 @@ #include #include +#include "core/libraries/kernel/sync/semaphore.h" + #include "common/logging/log.h" #include "core/libraries/kernel/kernel.h" #include "core/libraries/kernel/orbis_error.h" @@ -21,7 +23,7 @@ constexpr int ORBIS_KERNEL_SEM_VALUE_MAX = 0x7FFFFFFF; struct PthreadSem { explicit PthreadSem(s32 value_) : semaphore{value_}, value{value_} {} - std::counting_semaphore semaphore; + CountingSemaphore semaphore; std::atomic value; }; @@ -75,7 +77,7 @@ public: it = wait_list.erase(it); token_count -= waiter->need_count; waiter->was_signaled = true; - waiter->cv.notify_one(); + waiter->sem.release(); } return true; @@ -88,7 +90,7 @@ public: } for (auto* waiter : wait_list) { waiter->was_cancled = true; - waiter->cv.notify_one(); + waiter->sem.release(); } wait_list.clear(); token_count = set_count < 0 ? init_count : set_count; @@ -99,21 +101,21 @@ public: std::scoped_lock lk{mutex}; for (auto* waiter : wait_list) { waiter->was_deleted = true; - waiter->cv.notify_one(); + waiter->sem.release(); } wait_list.clear(); } public: struct WaitingThread { - std::condition_variable cv; + BinarySemaphore sem; u32 priority; s32 need_count; bool was_signaled{}; bool was_deleted{}; bool was_cancled{}; - explicit WaitingThread(s32 need_count, bool is_fifo) : need_count{need_count} { + explicit WaitingThread(s32 need_count, bool is_fifo) : sem{0}, need_count{need_count} { // Retrieve calling thread priority for sorting into waiting threads list. if (!is_fifo) { priority = g_curthread->attr.prio; @@ -134,24 +136,26 @@ public: } int Wait(std::unique_lock& lk, u32* timeout) { + lk.unlock(); if (!timeout) { // Wait indefinitely until we are woken up. - cv.wait(lk); + sem.acquire(); + lk.lock(); return GetResult(false); } // Wait until timeout runs out, recording how much remaining time there was. const auto start = std::chrono::high_resolution_clock::now(); - const auto signaled = cv.wait_for(lk, std::chrono::microseconds(*timeout), - [this] { return was_signaled; }); + sem.try_acquire_for(std::chrono::microseconds(*timeout)); const auto end = std::chrono::high_resolution_clock::now(); const auto time = std::chrono::duration_cast(end - start).count(); - if (signaled) { + lk.lock(); + if (was_signaled) { *timeout -= time; } else { *timeout = 0; } - return GetResult(!signaled); + return GetResult(!was_signaled); } }; diff --git a/src/core/libraries/kernel/time.cpp b/src/core/libraries/kernel/time.cpp index b586431ab..2565b8078 100644 --- a/src/core/libraries/kernel/time.cpp +++ b/src/core/libraries/kernel/time.cpp @@ -52,7 +52,22 @@ u64 PS4_SYSV_ABI sceKernelReadTsc() { int PS4_SYSV_ABI sceKernelUsleep(u32 microseconds) { #ifdef _WIN64 - std::this_thread::sleep_for(std::chrono::microseconds(microseconds)); + const auto start_time = std::chrono::high_resolution_clock::now(); + auto total_wait_time = std::chrono::microseconds(microseconds); + + while (total_wait_time.count() > 0) { + auto wait_time = std::chrono::ceil(total_wait_time).count(); + u64 res = SleepEx(static_cast(wait_time), true); + if (res == WAIT_IO_COMPLETION) { + auto elapsedTime = std::chrono::high_resolution_clock::now() - start_time; + auto elapsedMicroseconds = + std::chrono::duration_cast(elapsedTime).count(); + total_wait_time = std::chrono::microseconds(microseconds - elapsedMicroseconds); + } else { + break; + } + } + return 0; #else timespec start; From e1ecfb8dd1062d3081821aa25ba619f17c887497 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 6 Dec 2024 13:46:44 -0800 Subject: [PATCH 115/549] semaphore: Add GCD semaphore implementation. (#1677) --- src/core/libraries/kernel/sync/semaphore.h | 36 +++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/core/libraries/kernel/sync/semaphore.h b/src/core/libraries/kernel/sync/semaphore.h index a103472c8..884b08968 100644 --- a/src/core/libraries/kernel/sync/semaphore.h +++ b/src/core/libraries/kernel/sync/semaphore.h @@ -11,6 +11,8 @@ #ifdef _WIN64 #include +#elif defined(__APPLE__) +#include #else #include #endif @@ -21,25 +23,32 @@ template class Semaphore { public: Semaphore(s32 initialCount) -#ifndef _WIN64 +#if !defined(_WIN64) && !defined(__APPLE__) : sem{initialCount} #endif { #ifdef _WIN64 sem = CreateSemaphore(nullptr, initialCount, max, nullptr); ASSERT(sem); +#elif defined(__APPLE__) + sem = dispatch_semaphore_create(initialCount); + ASSERT(sem); #endif } ~Semaphore() { #ifdef _WIN64 CloseHandle(sem); +#elif defined(__APPLE__) + dispatch_release(sem); #endif } void release() { #ifdef _WIN64 ReleaseSemaphore(sem, 1, nullptr); +#elif defined(__APPLE__) + dispatch_semaphore_signal(sem); #else sem.release(); #endif @@ -53,6 +62,13 @@ public: return; } } +#elif defined(__APPLE__) + for (;;) { + const auto res = dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + if (res == 0) { + return; + } + } #else sem.acquire(); #endif @@ -61,6 +77,8 @@ public: bool try_acquire() { #ifdef _WIN64 return WaitForSingleObjectEx(sem, 0, true) == WAIT_OBJECT_0; +#elif defined(__APPLE__) + return dispatch_semaphore_wait(sem, DISPATCH_TIME_NOW) == 0; #else return sem.try_acquire(); #endif @@ -77,6 +95,10 @@ public: } return WaitForSingleObjectEx(sem, timeout_ms, true) == WAIT_OBJECT_0; +#elif defined(__APPLE__) + const auto rel_time_ns = std::chrono::ceil(rel_time).count(); + const auto timeout = dispatch_time(DISPATCH_TIME_NOW, rel_time_ns); + return dispatch_semaphore_wait(sem, timeout) == 0; #else return sem.try_acquire_for(rel_time); #endif @@ -98,6 +120,16 @@ public: u64 res = WaitForSingleObjectEx(sem, static_cast(timeout_ms), true); return res == WAIT_OBJECT_0; +#elif defined(__APPLE__) + auto abs_s = std::chrono::time_point_cast(abs_time); + auto abs_ns = std::chrono::time_point_cast(abs_time) - + std::chrono::time_point_cast(abs_s); + const timespec abs_timespec = { + .tv_sec = abs_s.time_since_epoch().count(), + .tv_nsec = abs_ns.count(), + }; + const auto timeout = dispatch_walltime(&abs_timespec, 0); + return dispatch_semaphore_wait(sem, timeout) == 0; #else return sem.try_acquire_until(abs_time); #endif @@ -106,6 +138,8 @@ public: private: #ifdef _WIN64 HANDLE sem; +#elif defined(__APPLE__) + dispatch_semaphore_t sem; #else std::counting_semaphore sem; #endif From 8eacb88a865fd38d87ce1f2510ecd5e6b108ae27 Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Sat, 7 Dec 2024 02:20:09 +0300 Subject: [PATCH 116/549] recompiler: fixed fragment shader built-in attribute access (#1676) * recompiler: fixed fragment shader built-in attribute access * handle en/addr separately * handle other registers as well --- .../frontend/translate/translate.cpp | 75 +++++++++++++++++-- src/shader_recompiler/runtime_info.h | 4 + src/video_core/amdgpu/liverpool.h | 29 ++++++- .../renderer_vulkan/vk_pipeline_cache.cpp | 2 + 4 files changed, 101 insertions(+), 9 deletions(-) diff --git a/src/shader_recompiler/frontend/translate/translate.cpp b/src/shader_recompiler/frontend/translate/translate.cpp index 68625a12b..b3a47fde8 100644 --- a/src/shader_recompiler/frontend/translate/translate.cpp +++ b/src/shader_recompiler/frontend/translate/translate.cpp @@ -53,15 +53,74 @@ void Translator::EmitPrologue() { } break; case Stage::Fragment: - // https://github.com/chaotic-cx/mesa-mirror/blob/72326e15/src/amd/vulkan/radv_shader_args.c#L258 - // The first two VGPRs are used for i/j barycentric coordinates. In the vast majority of - // cases it will be only those two, but if shader is using both e.g linear and perspective - // inputs it can be more For now assume that this isn't the case. - dst_vreg = IR::VectorReg::V2; - for (u32 i = 0; i < 4; i++) { - ir.SetVectorReg(dst_vreg++, ir.GetAttribute(IR::Attribute::FragCoord, i)); + dst_vreg = IR::VectorReg::V0; + if (runtime_info.fs_info.addr_flags.persp_sample_ena) { + ++dst_vreg; // I + ++dst_vreg; // J + } + if (runtime_info.fs_info.addr_flags.persp_center_ena) { + ++dst_vreg; // I + ++dst_vreg; // J + } + if (runtime_info.fs_info.addr_flags.persp_centroid_ena) { + ++dst_vreg; // I + ++dst_vreg; // J + } + if (runtime_info.fs_info.addr_flags.persp_pull_model_ena) { + ++dst_vreg; // I/W + ++dst_vreg; // J/W + ++dst_vreg; // 1/W + } + if (runtime_info.fs_info.addr_flags.linear_sample_ena) { + ++dst_vreg; // I + ++dst_vreg; // J + } + if (runtime_info.fs_info.addr_flags.linear_center_ena) { + ++dst_vreg; // I + ++dst_vreg; // J + } + if (runtime_info.fs_info.addr_flags.linear_centroid_ena) { + ++dst_vreg; // I + ++dst_vreg; // J + } + if (runtime_info.fs_info.addr_flags.line_stipple_tex_ena) { + ++dst_vreg; + } + if (runtime_info.fs_info.addr_flags.pos_x_float_ena) { + if (runtime_info.fs_info.en_flags.pos_x_float_ena) { + ir.SetVectorReg(dst_vreg++, ir.GetAttribute(IR::Attribute::FragCoord, 0)); + } else { + ir.SetVectorReg(dst_vreg++, ir.Imm32(0.0f)); + } + } + if (runtime_info.fs_info.addr_flags.pos_y_float_ena) { + if (runtime_info.fs_info.en_flags.pos_y_float_ena) { + ir.SetVectorReg(dst_vreg++, ir.GetAttribute(IR::Attribute::FragCoord, 1)); + } else { + ir.SetVectorReg(dst_vreg++, ir.Imm32(0.0f)); + } + } + if (runtime_info.fs_info.addr_flags.pos_z_float_ena) { + if (runtime_info.fs_info.en_flags.pos_z_float_ena) { + ir.SetVectorReg(dst_vreg++, ir.GetAttribute(IR::Attribute::FragCoord, 2)); + } else { + ir.SetVectorReg(dst_vreg++, ir.Imm32(0.0f)); + } + } + if (runtime_info.fs_info.addr_flags.pos_w_float_ena) { + if (runtime_info.fs_info.en_flags.pos_w_float_ena) { + ir.SetVectorReg(dst_vreg++, ir.GetAttribute(IR::Attribute::FragCoord, 3)); + } else { + ir.SetVectorReg(dst_vreg++, ir.Imm32(0.0f)); + } + } + if (runtime_info.fs_info.addr_flags.front_face_ena) { + if (runtime_info.fs_info.en_flags.front_face_ena) { + ir.SetVectorReg(dst_vreg++, ir.GetAttributeU32(IR::Attribute::IsFrontFace)); + } else { + ir.SetVectorReg(dst_vreg++, ir.Imm32(0)); + } } - ir.SetVectorReg(dst_vreg++, ir.GetAttributeU32(IR::Attribute::IsFrontFace)); break; case Stage::Compute: ir.SetVectorReg(dst_vreg++, ir.GetAttributeU32(IR::Attribute::LocalInvocationId, 0)); diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index 4662def93..4c779a368 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -7,6 +7,7 @@ #include #include #include "common/types.h" +#include "video_core/amdgpu/liverpool.h" #include "video_core/amdgpu/types.h" namespace Shader { @@ -105,6 +106,8 @@ struct FragmentRuntimeInfo { auto operator<=>(const PsInput&) const noexcept = default; }; + AmdGpu::Liverpool::PsInput en_flags; + AmdGpu::Liverpool::PsInput addr_flags; u32 num_inputs; std::array inputs; struct PsColorBuffer { @@ -117,6 +120,7 @@ struct FragmentRuntimeInfo { bool operator==(const FragmentRuntimeInfo& other) const noexcept { return std::ranges::equal(color_buffers, other.color_buffers) && + en_flags.raw == other.en_flags.raw && addr_flags.raw == other.addr_flags.raw && num_inputs == other.num_inputs && std::ranges::equal(inputs.begin(), inputs.begin() + num_inputs, other.inputs.begin(), other.inputs.begin() + num_inputs); diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index 2b2f2c00a..ca3b01612 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -1071,6 +1071,28 @@ struct Liverpool { BitField<27, 1, u32> enable_postz_overrasterization; }; + union PsInput { + u32 raw; + struct { + u32 persp_sample_ena : 1; + u32 persp_center_ena : 1; + u32 persp_centroid_ena : 1; + u32 persp_pull_model_ena : 1; + u32 linear_sample_ena : 1; + u32 linear_center_ena : 1; + u32 linear_centroid_ena : 1; + u32 line_stipple_tex_ena : 1; + u32 pos_x_float_ena : 1; + u32 pos_y_float_ena : 1; + u32 pos_z_float_ena : 1; + u32 pos_w_float_ena : 1; + u32 front_face_ena : 1; + u32 ancillary_ena : 1; + u32 sample_coverage_ena : 1; + u32 pos_fixed_pt_ena : 1; + }; + }; + union Regs { struct { INSERT_PADDING_WORDS(0x2C08); @@ -1126,7 +1148,10 @@ struct Liverpool { INSERT_PADDING_WORDS(0xA191 - 0xA187); std::array ps_inputs; VsOutputConfig vs_output_config; - INSERT_PADDING_WORDS(4); + INSERT_PADDING_WORDS(1); + PsInput ps_input_ena; + PsInput ps_input_addr; + INSERT_PADDING_WORDS(1); BitField<0, 6, u32> num_interp; INSERT_PADDING_WORDS(0xA1C3 - 0xA1B6 - 1); ShaderPosFormat shader_pos_format; @@ -1388,6 +1413,8 @@ static_assert(GFX6_3D_REG_INDEX(viewports) == 0xA10F); static_assert(GFX6_3D_REG_INDEX(clip_user_data) == 0xA16F); static_assert(GFX6_3D_REG_INDEX(ps_inputs) == 0xA191); static_assert(GFX6_3D_REG_INDEX(vs_output_config) == 0xA1B1); +static_assert(GFX6_3D_REG_INDEX(ps_input_ena) == 0xA1B3); +static_assert(GFX6_3D_REG_INDEX(ps_input_addr) == 0xA1B4); static_assert(GFX6_3D_REG_INDEX(num_interp) == 0xA1B6); static_assert(GFX6_3D_REG_INDEX(shader_pos_format) == 0xA1C3); static_assert(GFX6_3D_REG_INDEX(z_export_format) == 0xA1C4); diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 82a029b95..53bdc79a6 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -123,6 +123,8 @@ Shader::RuntimeInfo PipelineCache::BuildRuntimeInfo(Shader::Stage stage) { } case Shader::Stage::Fragment: { BuildCommon(regs.ps_program); + info.fs_info.en_flags = regs.ps_input_ena; + info.fs_info.addr_flags = regs.ps_input_addr; const auto& ps_inputs = regs.ps_inputs; info.fs_info.num_inputs = regs.num_interp; for (u32 i = 0; i < regs.num_interp; i++) { From 6f543b5bd9ce19ec1143a7beffcd00e4be378821 Mon Sep 17 00:00:00 2001 From: "Daniel R." <47796739+polybiusproxy@users.noreply.github.com> Date: Sat, 7 Dec 2024 09:48:12 +0100 Subject: [PATCH 117/549] hotfix: enable discord RPC --- CMakeLists.txt | 4 ++++ src/emulator.cpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 84146bb01..81fae39c2 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -878,6 +878,10 @@ target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAlloca target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h") target_compile_definitions(Dear_ImGui PRIVATE IMGUI_USER_CONFIG="${PROJECT_SOURCE_DIR}/src/imgui/imgui_config.h") +if (ENABLE_DISCORD_RPC) + target_compile_definitions(shadps4 PRIVATE ENABLE_DISCORD_RPC) +endif() + if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") target_compile_definitions(shadps4 PRIVATE ENABLE_USERFAULTFD) endif() diff --git a/src/emulator.cpp b/src/emulator.cpp index 8a7c04cf4..60d6e18d7 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -266,7 +266,7 @@ void Emulator::Run(const std::filesystem::path& file) { } void Emulator::LoadSystemModules(const std::filesystem::path& file, std::string game_serial) { - constexpr std::array ModulesToLoad{ + constexpr std::array ModulesToLoad{ {{"libSceNgs2.sprx", &Libraries::Ngs2::RegisterlibSceNgs2}, {"libSceFiber.sprx", &Libraries::Fiber::RegisterlibSceFiber}, {"libSceUlt.sprx", nullptr}, From 9524f9474993885b53e963219e02e7184d33d5bb Mon Sep 17 00:00:00 2001 From: "Daniel R." <47796739+polybiusproxy@users.noreply.github.com> Date: Sat, 7 Dec 2024 10:07:14 +0100 Subject: [PATCH 118/549] hotfix: add missing include --- src/qt_gui/settings_dialog.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index abbd39edd..1fd4b6e8b 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -6,6 +6,9 @@ #include #include +#ifdef ENABLE_DISCORD_RPC +#include "common/discord_rpc_handler.h" +#endif #ifdef ENABLE_UPDATER #include "check_update.h" #endif From 941a668f78845290b4f5b28f2246c6cf00de765e Mon Sep 17 00:00:00 2001 From: psucien Date: Sat, 7 Dec 2024 10:30:36 +0100 Subject: [PATCH 119/549] hot-fix: obtain cmdbuf for dispatches after cache ops This fixes cmdbuf being in incorrect state after scheduler rotation on flush --- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 4e858c0d3..0471fdb0a 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -312,7 +312,6 @@ void Rasterizer::DrawIndirect(bool is_indexed, VAddr arg_address, u32 offset, u3 void Rasterizer::DispatchDirect() { RENDERER_TRACE; - const auto cmdbuf = scheduler.CommandBuffer(); const auto& cs_program = liverpool->regs.cs_program; const ComputePipeline* pipeline = pipeline_cache.GetComputePipeline(); if (!pipeline) { @@ -324,6 +323,8 @@ void Rasterizer::DispatchDirect() { } scheduler.EndRendering(); + + const auto cmdbuf = scheduler.CommandBuffer(); cmdbuf.bindPipeline(vk::PipelineBindPoint::eCompute, pipeline->Handle()); cmdbuf.dispatch(cs_program.dim_x, cs_program.dim_y, cs_program.dim_z); @@ -333,7 +334,6 @@ void Rasterizer::DispatchDirect() { void Rasterizer::DispatchIndirect(VAddr address, u32 offset, u32 size) { RENDERER_TRACE; - const auto cmdbuf = scheduler.CommandBuffer(); const auto& cs_program = liverpool->regs.cs_program; const ComputePipeline* pipeline = pipeline_cache.GetComputePipeline(); if (!pipeline) { @@ -345,8 +345,11 @@ void Rasterizer::DispatchIndirect(VAddr address, u32 offset, u32 size) { } scheduler.EndRendering(); - cmdbuf.bindPipeline(vk::PipelineBindPoint::eCompute, pipeline->Handle()); + const auto [buffer, base] = buffer_cache.ObtainBuffer(address + offset, size, false); + + const auto cmdbuf = scheduler.CommandBuffer(); + cmdbuf.bindPipeline(vk::PipelineBindPoint::eCompute, pipeline->Handle()); cmdbuf.dispatchIndirect(buffer->Handle(), base); ResetBindings(); From 2266622dcf77422a996460d86e13cb2926ab634b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A5IGA?= <164882787+Xphalnos@users.noreply.github.com> Date: Sat, 7 Dec 2024 18:41:41 +0100 Subject: [PATCH 120/549] Support for Vulkan 1.4 (#1665) --- .gitmodules | 2 ++ CMakeLists.txt | 2 +- externals/CMakeLists.txt | 4 ++-- externals/LibAtrac9 | 2 +- externals/date | 2 +- externals/glslang | 2 +- externals/magic_enum | 2 +- externals/pugixml | 2 +- externals/sdl3 | 2 +- externals/toml11 | 2 +- externals/vma | 2 +- externals/vulkan-headers | 2 +- externals/xbyak | 2 +- externals/xxhash | 2 +- externals/zydis | 2 +- src/core/devtools/widget/common.h | 2 +- src/core/devtools/widget/frame_dump.cpp | 2 +- src/core/devtools/widget/memory_map.cpp | 2 +- src/core/devtools/widget/reg_popup.cpp | 2 +- src/core/devtools/widget/reg_view.cpp | 2 +- src/core/libraries/ajm/ajm.cpp | 2 +- src/core/libraries/ajm/ajm_instance.cpp | 2 +- src/core/libraries/audio/audioout.cpp | 2 +- src/core/libraries/avplayer/avplayer_file_streamer.cpp | 2 +- src/core/libraries/avplayer/avplayer_source.cpp | 2 +- src/core/libraries/avplayer/avplayer_state.cpp | 2 +- src/core/libraries/ime/error_dialog.cpp | 2 +- src/core/libraries/ime/ime_dialog.cpp | 3 ++- src/core/libraries/ime/ime_dialog_ui.cpp | 2 +- src/core/libraries/jpeg/jpegenc.cpp | 3 ++- src/core/libraries/save_data/dialog/savedatadialog.cpp | 3 ++- src/core/libraries/save_data/dialog/savedatadialog_ui.cpp | 2 +- src/core/libraries/save_data/save_backup.cpp | 2 +- src/core/libraries/save_data/save_instance.cpp | 2 +- src/core/libraries/save_data/savedata.cpp | 2 +- src/core/libraries/system/msgdialog.cpp | 2 +- src/core/libraries/system/sysmodule.cpp | 2 +- src/core/platform.h | 3 ++- .../backend/spirv/emit_spirv_context_get_set.cpp | 2 +- src/shader_recompiler/frontend/decode.cpp | 2 +- src/shader_recompiler/frontend/translate/translate.cpp | 2 +- src/video_core/renderer_vulkan/liverpool_to_vk.cpp | 2 +- src/video_core/renderer_vulkan/vk_platform.cpp | 2 +- src/video_core/texture_cache/tile_manager.cpp | 2 +- 44 files changed, 50 insertions(+), 44 deletions(-) diff --git a/.gitmodules b/.gitmodules index fb859c87d..8010250a9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -102,6 +102,8 @@ [submodule "externals/LibAtrac9"] path = externals/LibAtrac9 url = https://github.com/shadps4-emu/ext-LibAtrac9.git + shallow = true [submodule "externals/libpng"] path = externals/libpng url = https://github.com/pnggroup/libpng + shallow = true \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 81fae39c2..bc811fada 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -120,7 +120,7 @@ find_package(SDL3 3.1.2 CONFIG) find_package(stb MODULE) find_package(toml11 4.2.0 CONFIG) find_package(tsl-robin-map 1.3.0 CONFIG) -find_package(VulkanHeaders 1.3.289 CONFIG) +find_package(VulkanHeaders 1.4.303 CONFIG) find_package(VulkanMemoryAllocator 3.1.0 CONFIG) find_package(xbyak 7.07 CONFIG) find_package(xxHash 0.8.2 MODULE) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 8ccae8070..082be211a 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -35,7 +35,7 @@ else() if (NOT TARGET cryptopp::cryptopp) set(CRYPTOPP_INSTALL OFF) set(CRYPTOPP_BUILD_TESTING OFF) - set(CRYPTOPP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cryptopp/) + set(CRYPTOPP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cryptopp) add_subdirectory(cryptopp-cmake) file(COPY cryptopp DESTINATION cryptopp FILES_MATCHING PATTERN "*.h") # remove externals/cryptopp from include directories because it contains a conflicting zlib.h file @@ -216,7 +216,7 @@ endif() # Discord RPC if (ENABLE_DISCORD_RPC) set(BUILD_EXAMPLES OFF) - add_subdirectory(discord-rpc/) + add_subdirectory(discord-rpc) target_include_directories(discord-rpc INTERFACE discord-rpc/include) endif() diff --git a/externals/LibAtrac9 b/externals/LibAtrac9 index 3acdcdc78..9640129dc 160000 --- a/externals/LibAtrac9 +++ b/externals/LibAtrac9 @@ -1 +1 @@ -Subproject commit 3acdcdc78f129c2e6145331ff650fa76dd88d62c +Subproject commit 9640129dc6f2afbca6ceeca3019856e8653a5fb2 diff --git a/externals/date b/externals/date index dd8affc6d..28b7b2325 160000 --- a/externals/date +++ b/externals/date @@ -1 +1 @@ -Subproject commit dd8affc6de5755e07638bf0a14382d29549d6ee9 +Subproject commit 28b7b232521ace2c8ef3f2ad4126daec3569c14f diff --git a/externals/glslang b/externals/glslang index e61d7bb30..a0995c49e 160000 --- a/externals/glslang +++ b/externals/glslang @@ -1 +1 @@ -Subproject commit e61d7bb3006f451968714e2f653412081871e1ee +Subproject commit a0995c49ebcaca2c6d3b03efbabf74f3843decdb diff --git a/externals/magic_enum b/externals/magic_enum index 126539e13..1a1824df7 160000 --- a/externals/magic_enum +++ b/externals/magic_enum @@ -1 +1 @@ -Subproject commit 126539e13cccdc2e75ce770e94f3c26403099fa5 +Subproject commit 1a1824df7ac798177a521eed952720681b0bf482 diff --git a/externals/pugixml b/externals/pugixml index 3b1718437..4bc14418d 160000 --- a/externals/pugixml +++ b/externals/pugixml @@ -1 +1 @@ -Subproject commit 3b17184379fcaaeb7f1fbe08018b7fedf2640b3b +Subproject commit 4bc14418d12d289dd9978fdce9490a45deeb653e diff --git a/externals/sdl3 b/externals/sdl3 index 54e622c2e..3a1d76d29 160000 --- a/externals/sdl3 +++ b/externals/sdl3 @@ -1 +1 @@ -Subproject commit 54e622c2e6af456bfef382fae44c17682d5ac88a +Subproject commit 3a1d76d298db023f6cf37fb08ee766f20a4e12ab diff --git a/externals/toml11 b/externals/toml11 index f925e7f28..7f6c574ff 160000 --- a/externals/toml11 +++ b/externals/toml11 @@ -1 +1 @@ -Subproject commit f925e7f287c0008813c2294798cf9ca167fd9ffd +Subproject commit 7f6c574ff5aa1053534e7e19c0a4f22bf4c6aaca diff --git a/externals/vma b/externals/vma index 1c35ba99c..5a53a1989 160000 --- a/externals/vma +++ b/externals/vma @@ -1 +1 @@ -Subproject commit 1c35ba99ce775f8342d87a83a3f0f696f99c2a39 +Subproject commit 5a53a198945ba8260fbc58fadb788745ce6aa263 diff --git a/externals/vulkan-headers b/externals/vulkan-headers index d91597a82..6a74a7d65 160000 --- a/externals/vulkan-headers +++ b/externals/vulkan-headers @@ -1 +1 @@ -Subproject commit d91597a82f881d473887b560a03a7edf2720b72c +Subproject commit 6a74a7d65cafa19e38ec116651436cce6efd5b2e diff --git a/externals/xbyak b/externals/xbyak index d067f0d3f..4e44f4614 160000 --- a/externals/xbyak +++ b/externals/xbyak @@ -1 +1 @@ -Subproject commit d067f0d3f55696ae8bc9a25ad7012ee80f221d54 +Subproject commit 4e44f4614ddbf038f2a6296f5b906d5c72691e0f diff --git a/externals/xxhash b/externals/xxhash index d4ad85e4a..2bf8313b9 160000 --- a/externals/xxhash +++ b/externals/xxhash @@ -1 +1 @@ -Subproject commit d4ad85e4afaad5c780f54db1dc967fff5a869ffd +Subproject commit 2bf8313b934633b2a5b7e8fd239645b85e10c852 diff --git a/externals/zydis b/externals/zydis index 9d298eb80..bffbb610c 160000 --- a/externals/zydis +++ b/externals/zydis @@ -1 +1 @@ -Subproject commit 9d298eb8067ff62a237203d1e1470785033e185c +Subproject commit bffbb610cfea643b98e87658b9058382f7522807 diff --git a/src/core/devtools/widget/common.h b/src/core/devtools/widget/common.h index 4429f5581..5f669eb65 100644 --- a/src/core/devtools/widget/common.h +++ b/src/core/devtools/widget/common.h @@ -8,7 +8,7 @@ #include #include -#include +#include #include "common/bit_field.h" #include "common/io_file.h" diff --git a/src/core/devtools/widget/frame_dump.cpp b/src/core/devtools/widget/frame_dump.cpp index 86ba7b86e..055ce1333 100644 --- a/src/core/devtools/widget/frame_dump.cpp +++ b/src/core/devtools/widget/frame_dump.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include "common/io_file.h" #include "core/devtools/options.h" diff --git a/src/core/devtools/widget/memory_map.cpp b/src/core/devtools/widget/memory_map.cpp index dc8f5c2e9..7edd676e9 100644 --- a/src/core/devtools/widget/memory_map.cpp +++ b/src/core/devtools/widget/memory_map.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include "core/debug_state.h" #include "core/memory.h" diff --git a/src/core/devtools/widget/reg_popup.cpp b/src/core/devtools/widget/reg_popup.cpp index 0633e76e6..2727e1745 100644 --- a/src/core/devtools/widget/reg_popup.cpp +++ b/src/core/devtools/widget/reg_popup.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include "cmd_list.h" #include "common.h" diff --git a/src/core/devtools/widget/reg_view.cpp b/src/core/devtools/widget/reg_view.cpp index a60090a8c..79b02a849 100644 --- a/src/core/devtools/widget/reg_view.cpp +++ b/src/core/devtools/widget/reg_view.cpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #include "common.h" diff --git a/src/core/libraries/ajm/ajm.cpp b/src/core/libraries/ajm/ajm.cpp index 2396669b6..a3728039e 100644 --- a/src/core/libraries/ajm/ajm.cpp +++ b/src/core/libraries/ajm/ajm.cpp @@ -9,7 +9,7 @@ #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" -#include +#include namespace Libraries::Ajm { diff --git a/src/core/libraries/ajm/ajm_instance.cpp b/src/core/libraries/ajm/ajm_instance.cpp index 4e04eea74..ea7fd5617 100644 --- a/src/core/libraries/ajm/ajm_instance.cpp +++ b/src/core/libraries/ajm/ajm_instance.cpp @@ -5,7 +5,7 @@ #include "core/libraries/ajm/ajm_instance.h" #include "core/libraries/ajm/ajm_mp3.h" -#include +#include namespace Libraries::Ajm { diff --git a/src/core/libraries/audio/audioout.cpp b/src/core/libraries/audio/audioout.cpp index b92c75a8f..78b04cc90 100644 --- a/src/core/libraries/audio/audioout.cpp +++ b/src/core/libraries/audio/audioout.cpp @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include -#include +#include #include "common/assert.h" #include "common/logging/log.h" diff --git a/src/core/libraries/avplayer/avplayer_file_streamer.cpp b/src/core/libraries/avplayer/avplayer_file_streamer.cpp index 3323ee9b6..19faeb273 100644 --- a/src/core/libraries/avplayer/avplayer_file_streamer.cpp +++ b/src/core/libraries/avplayer/avplayer_file_streamer.cpp @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include // std::max, std::min -#include +#include #include "core/libraries/avplayer/avplayer_file_streamer.h" extern "C" { diff --git a/src/core/libraries/avplayer/avplayer_source.cpp b/src/core/libraries/avplayer/avplayer_source.cpp index 8e43e7277..950951673 100644 --- a/src/core/libraries/avplayer/avplayer_source.cpp +++ b/src/core/libraries/avplayer/avplayer_source.cpp @@ -8,7 +8,7 @@ #include "core/libraries/avplayer/avplayer_file_streamer.h" #include "core/libraries/avplayer/avplayer_source.h" -#include +#include extern "C" { #include diff --git a/src/core/libraries/avplayer/avplayer_state.cpp b/src/core/libraries/avplayer/avplayer_state.cpp index c3694eec0..143df749c 100644 --- a/src/core/libraries/avplayer/avplayer_state.cpp +++ b/src/core/libraries/avplayer/avplayer_state.cpp @@ -8,7 +8,7 @@ #include "core/libraries/avplayer/avplayer_state.h" #include "core/tls.h" -#include +#include namespace Libraries::AvPlayer { diff --git a/src/core/libraries/ime/error_dialog.cpp b/src/core/libraries/ime/error_dialog.cpp index 811f2cb99..07580fe1d 100644 --- a/src/core/libraries/ime/error_dialog.cpp +++ b/src/core/libraries/ime/error_dialog.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include "common/assert.h" #include "common/logging/log.h" diff --git a/src/core/libraries/ime/ime_dialog.cpp b/src/core/libraries/ime/ime_dialog.cpp index d6d027885..9151aa64e 100644 --- a/src/core/libraries/ime/ime_dialog.cpp +++ b/src/core/libraries/ime/ime_dialog.cpp @@ -2,7 +2,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include -#include +#include + #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" diff --git a/src/core/libraries/ime/ime_dialog_ui.cpp b/src/core/libraries/ime/ime_dialog_ui.cpp index 5957606eb..51183c79b 100644 --- a/src/core/libraries/ime/ime_dialog_ui.cpp +++ b/src/core/libraries/ime/ime_dialog_ui.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include "common/assert.h" #include "common/logging/log.h" diff --git a/src/core/libraries/jpeg/jpegenc.cpp b/src/core/libraries/jpeg/jpegenc.cpp index b664a2334..b9c88d094 100644 --- a/src/core/libraries/jpeg/jpegenc.cpp +++ b/src/core/libraries/jpeg/jpegenc.cpp @@ -1,7 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include +#include + #include "common/alignment.h" #include "common/assert.h" #include "common/logging/log.h" diff --git a/src/core/libraries/save_data/dialog/savedatadialog.cpp b/src/core/libraries/save_data/dialog/savedatadialog.cpp index 0ad7d7dc0..2f0619165 100644 --- a/src/core/libraries/save_data/dialog/savedatadialog.cpp +++ b/src/core/libraries/save_data/dialog/savedatadialog.cpp @@ -1,11 +1,12 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include + #include "common/elf_info.h" #include "common/logging/log.h" #include "core/libraries/libs.h" #include "core/libraries/system/commondialog.h" -#include "magic_enum.hpp" #include "savedatadialog.h" #include "savedatadialog_ui.h" diff --git a/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp b/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp index 4e0d801a6..a6ca8744d 100644 --- a/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp +++ b/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include "common/elf_info.h" #include "common/singleton.h" diff --git a/src/core/libraries/save_data/save_backup.cpp b/src/core/libraries/save_data/save_backup.cpp index da5172b15..3f7969d69 100644 --- a/src/core/libraries/save_data/save_backup.cpp +++ b/src/core/libraries/save_data/save_backup.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include "save_backup.h" #include "save_instance.h" diff --git a/src/core/libraries/save_data/save_instance.cpp b/src/core/libraries/save_data/save_instance.cpp index 0d6c5173c..99daf83cc 100644 --- a/src/core/libraries/save_data/save_instance.cpp +++ b/src/core/libraries/save_data/save_instance.cpp @@ -3,7 +3,7 @@ #include -#include +#include #include "common/assert.h" #include "common/config.h" diff --git a/src/core/libraries/save_data/savedata.cpp b/src/core/libraries/save_data/savedata.cpp index c515ebcbf..66899fb34 100644 --- a/src/core/libraries/save_data/savedata.cpp +++ b/src/core/libraries/save_data/savedata.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include "common/assert.h" #include "common/cstring.h" diff --git a/src/core/libraries/system/msgdialog.cpp b/src/core/libraries/system/msgdialog.cpp index 7d924e4ad..8a01f429f 100644 --- a/src/core/libraries/system/msgdialog.cpp +++ b/src/core/libraries/system/msgdialog.cpp @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include -#include +#include #include "common/assert.h" #include "common/logging/log.h" diff --git a/src/core/libraries/system/sysmodule.cpp b/src/core/libraries/system/sysmodule.cpp index 9bed4ef31..350f1317b 100644 --- a/src/core/libraries/system/sysmodule.cpp +++ b/src/core/libraries/system/sysmodule.cpp @@ -3,7 +3,7 @@ #define MAGIC_ENUM_RANGE_MIN 0 #define MAGIC_ENUM_RANGE_MAX 300 -#include +#include #include "common/logging/log.h" #include "core/libraries/error_codes.h" diff --git a/src/core/platform.h b/src/core/platform.h index 03bd79e86..bdb50701b 100644 --- a/src/core/platform.h +++ b/src/core/platform.h @@ -7,7 +7,8 @@ #include "common/logging/log.h" #include "common/singleton.h" #include "common/types.h" -#include "magic_enum.hpp" + +#include #include #include diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp index b578f0c52..d005169c4 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp @@ -5,7 +5,7 @@ #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" #include "shader_recompiler/backend/spirv/spirv_emit_context.h" -#include +#include namespace Shader::Backend::SPIRV { namespace { diff --git a/src/shader_recompiler/frontend/decode.cpp b/src/shader_recompiler/frontend/decode.cpp index 796bed127..a5187aebd 100644 --- a/src/shader_recompiler/frontend/decode.cpp +++ b/src/shader_recompiler/frontend/decode.cpp @@ -5,7 +5,7 @@ #include "common/assert.h" #include "shader_recompiler/frontend/decode.h" -#include "magic_enum.hpp" +#include namespace Shader::Gcn { diff --git a/src/shader_recompiler/frontend/translate/translate.cpp b/src/shader_recompiler/frontend/translate/translate.cpp index b3a47fde8..97978ff6b 100644 --- a/src/shader_recompiler/frontend/translate/translate.cpp +++ b/src/shader_recompiler/frontend/translate/translate.cpp @@ -14,7 +14,7 @@ #define MAGIC_ENUM_RANGE_MIN 0 #define MAGIC_ENUM_RANGE_MAX 1515 -#include "magic_enum.hpp" +#include namespace Shader::Gcn { diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp index 2262a429a..f0f7d352c 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp @@ -6,7 +6,7 @@ #include "video_core/amdgpu/pixel_format.h" #include "video_core/renderer_vulkan/liverpool_to_vk.h" -#include +#include #define INVALID_NUMBER_FORMAT_COMBO \ LOG_ERROR(Render_Vulkan, "Unsupported number type {} for format {}", number_type, format); diff --git a/src/video_core/renderer_vulkan/vk_platform.cpp b/src/video_core/renderer_vulkan/vk_platform.cpp index b2a50cd44..2e717397b 100644 --- a/src/video_core/renderer_vulkan/vk_platform.cpp +++ b/src/video_core/renderer_vulkan/vk_platform.cpp @@ -22,7 +22,7 @@ #include "video_core/renderer_vulkan/vk_platform.h" #if VULKAN_HPP_ENABLE_DYNAMIC_LOADER_TOOL -static vk::DynamicLoader dl; +static vk::detail::DynamicLoader dl; #else extern "C" { VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr(VkInstance instance, diff --git a/src/video_core/texture_cache/tile_manager.cpp b/src/video_core/texture_cache/tile_manager.cpp index 9823cb4dc..2bc3bf282 100644 --- a/src/video_core/texture_cache/tile_manager.cpp +++ b/src/video_core/texture_cache/tile_manager.cpp @@ -14,7 +14,7 @@ #include "video_core/host_shaders/detile_m8x2_comp.h" #include -#include +#include #include namespace VideoCore { From 57e762468f7cff0c1cf130b1ac2197d2f6153fa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A5IGA?= <164882787+Xphalnos@users.noreply.github.com> Date: Sat, 7 Dec 2024 22:27:57 +0100 Subject: [PATCH 121/549] Fix + documentation update (#1689) --- CMakeLists.txt | 2 +- documents/building-windows.md | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bc811fada..16b49cf08 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -113,7 +113,7 @@ find_package(FFmpeg 5.1.2 MODULE) find_package(fmt 10.2.0 CONFIG) find_package(glslang 15 CONFIG) find_package(half 1.12.0 MODULE) -find_package(magic_enum 0.9.6 CONFIG) +find_package(magic_enum 0.9.7 CONFIG) find_package(PNG 1.6 MODULE) find_package(RenderDoc 1.6.0 MODULE) find_package(SDL3 3.1.2 CONFIG) diff --git a/documents/building-windows.md b/documents/building-windows.md index 0da630f0b..d01e7b81e 100644 --- a/documents/building-windows.md +++ b/documents/building-windows.md @@ -25,8 +25,8 @@ Once you are within the installer: Beware, this requires you to create a Qt account. If you do not want to do this, please follow the MSYS2/MinGW compilation method instead. -1. Under the current, non beta version of Qt (at the time of writing 6.7.2), select the option `MSVC 2019 64-bit` or similar. - If you are on Windows on ARM / Qualcomm Snapdragon Elite X, select `MSVC 2019 ARM64` instead. +1. Under the current, non beta version of Qt (at the time of writing 6.7.3), select the option `MSVC 2022 64-bit` or similar. + If you are on Windows on ARM / Qualcomm Snapdragon Elite X, select `MSVC 2022 ARM64` instead. Go through the installation normally. If you know what you are doing, you may unselect individual components that eat up too much disk space. @@ -35,7 +35,7 @@ Beware, this requires you to create a Qt account. If you do not want to do this, Once you are finished, you will have to configure Qt within Visual Studio: 1. Tools -> Options -> Qt -> Versions -2. Add a new Qt version and navigate it to the correct folder. Should look like so: `C:\Qt\6.7.2\msvc2019_64` +2. Add a new Qt version and navigate it to the correct folder. Should look like so: `C:\Qt\6.7.3\msvc2022_64` 3. Enable the default checkmark on the new version you just created. ### (Prerequisite) Download [**Git for Windows**](https://git-scm.com/download/win) @@ -55,16 +55,16 @@ Go through the Git for Windows installation as normal 3. If you want to build shadPS4 with the Qt Gui: 1. Click x64-Clang-Release and select "Manage Configurations" 2. Look for "CMake command arguments" and add to the text field - `-DENABLE_QT_GUI=ON -DCMAKE_PREFIX_PATH=C:\Qt\6.7.2\msvc2019_64` + `-DENABLE_QT_GUI=ON -DCMAKE_PREFIX_PATH=C:\Qt\6.7.3\msvc2022_64` (Change Qt path if you've installed it to non-default path) 3. Press CTRL+S to save and wait a moment for CMake generation 4. Change the project to build to shadps4.exe 5. Build -> Build All -Your shadps4.exe will be in `c:\path\to\source\Build\x64-Clang-Release\` +Your shadps4.exe will be in `C:\path\to\source\Build\x64-Clang-Release\` To automatically populate the necessary files to run shadPS4.exe, run in a command prompt or terminal: -`C:\Qt\6.7.2\msvc2019_64\bin\windeployqt.exe "c:\path\to\shadps4.exe"` +`C:\Qt\6.7.3\msvc2022_64\bin\windeployqt.exe "C:\path\to\shadps4.exe"` (Change Qt path if you've installed it to non-default path) ## Option 2: MSYS2/MinGW From c076ba69e8c66401e3655940bfc554cbcd21c2f2 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sat, 7 Dec 2024 13:28:17 -0800 Subject: [PATCH 122/549] shader_recompiler: Implement V_LSHL_B64 for immediate arguments. (#1674) --- .../frontend/translate/vector_alu.cpp | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/shader_recompiler/frontend/translate/vector_alu.cpp b/src/shader_recompiler/frontend/translate/vector_alu.cpp index eb90c256e..8149230db 100644 --- a/src/shader_recompiler/frontend/translate/vector_alu.cpp +++ b/src/shader_recompiler/frontend/translate/vector_alu.cpp @@ -1155,15 +1155,23 @@ void Translator::V_LSHL_B64(const GcnInst& inst) { const IR::U64 src0{GetSrc64(inst.src[0])}; const IR::U64 src1{GetSrc64(inst.src[1])}; const IR::VectorReg dst_reg{inst.dst[0].code}; - if (src0.IsImmediate() && src0.U64() == -1) { - ir.SetVectorReg(dst_reg, ir.Imm32(0xFFFFFFFF)); - ir.SetVectorReg(dst_reg + 1, ir.Imm32(0xFFFFFFFF)); - return; + if (src0.IsImmediate()) { + if (src0.U64() == -1) { + // If src0 is a fixed -1, the result will always be -1. + ir.SetVectorReg(dst_reg, ir.Imm32(0xFFFFFFFF)); + ir.SetVectorReg(dst_reg + 1, ir.Imm32(0xFFFFFFFF)); + return; + } + if (src1.IsImmediate()) { + // If both src0 and src1 are immediates, we can calculate the result now. + // Note that according to the manual, only bits 4:0 are used from src1. + const u64 result = src0.U64() << (src1.U64() & 0x1F); + ir.SetVectorReg(dst_reg, ir.Imm32(static_cast(result))); + ir.SetVectorReg(dst_reg + 1, ir.Imm32(static_cast(result >> 32))); + return; + } } - ASSERT_MSG(src0.IsImmediate() && src0.U64() == 0 && src1.IsImmediate() && src1.U64() == 0, - "V_LSHL_B64 with non-zero src0 or src1 is not supported"); - ir.SetVectorReg(dst_reg, ir.Imm32(0)); - ir.SetVectorReg(dst_reg + 1, ir.Imm32(0)); + UNREACHABLE_MSG("Unimplemented V_LSHL_B64 arguments"); } void Translator::V_MUL_F64(const GcnInst& inst) { From 119e03cb585bf797cf3155216d2bb5262a66aef9 Mon Sep 17 00:00:00 2001 From: psucien Date: Sat, 7 Dec 2024 22:28:11 +0100 Subject: [PATCH 123/549] hot-fix: fix for incorrect asc qid --- src/core/libraries/gnmdriver/gnmdriver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index 10d121afe..4e2db9083 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -544,7 +544,7 @@ void PS4_SYSV_ABI sceGnmDingDong(u32 gnm_vqid, u32 next_offs_dw) { .base_addr = base_addr, }); } - liverpool->SubmitAsc(vqid, acb_span); + liverpool->SubmitAsc(gnm_vqid, acb_span); *asc_queue.read_addr += acb_size; *asc_queue.read_addr %= asc_queue.ring_size_dw * 4; From cde84e4bac94cfaa4c63f38f2b0494b78c6955eb Mon Sep 17 00:00:00 2001 From: IndecisiveTurtle Date: Sat, 7 Dec 2024 23:43:52 +0200 Subject: [PATCH 124/549] shader_recompiler: Fix mistake --- src/shader_recompiler/frontend/translate/scalar_alu.cpp | 6 +++--- src/shader_recompiler/frontend/translate/translate.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/shader_recompiler/frontend/translate/scalar_alu.cpp b/src/shader_recompiler/frontend/translate/scalar_alu.cpp index 75ad957b3..c320e00a7 100644 --- a/src/shader_recompiler/frontend/translate/scalar_alu.cpp +++ b/src/shader_recompiler/frontend/translate/scalar_alu.cpp @@ -96,8 +96,8 @@ void Translator::EmitScalarAlu(const GcnInst& inst) { return S_BREV_B32(inst); case Opcode::S_BCNT1_I32_B64: return S_BCNT1_I32_B64(inst); - case Opcode::S_FF1_I32_B64: - return S_FF1_I32_B64(inst); + case Opcode::S_FF1_I32_B32: + return S_FF1_I32_B32(inst); case Opcode::S_AND_SAVEEXEC_B64: return S_SAVEEXEC_B64(NegateMode::None, false, inst); case Opcode::S_ORN2_SAVEEXEC_B64: @@ -575,7 +575,7 @@ void Translator::S_BCNT1_I32_B64(const GcnInst& inst) { ir.SetScc(ir.INotEqual(result, ir.Imm32(0))); } -void Translator::S_FF1_I32_B64(const GcnInst& inst) { +void Translator::S_FF1_I32_B32(const GcnInst& inst) { const IR::U32 src0{GetSrc(inst.src[0])}; const IR::U32 result{ir.Select(ir.IEqual(src0, ir.Imm32(0U)), ir.Imm32(-1), ir.FindILsb(src0))}; SetDst(inst.dst[0], result); diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index dd379d8ea..00cdd8d55 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -110,7 +110,7 @@ public: void S_NOT_B64(const GcnInst& inst); void S_BREV_B32(const GcnInst& inst); void S_BCNT1_I32_B64(const GcnInst& inst); - void S_FF1_I32_B64(const GcnInst& inst); + void S_FF1_I32_B32(const GcnInst& inst); void S_GETPC_B64(u32 pc, const GcnInst& inst); void S_SAVEEXEC_B64(NegateMode negate, bool is_or, const GcnInst& inst); From 8ee672fe32702b232728ae193abf57d9468b5de1 Mon Sep 17 00:00:00 2001 From: TheTurtle Date: Sun, 8 Dec 2024 00:10:20 +0200 Subject: [PATCH 125/549] hot-fix: Allow unpriviledged userfaultfd --- src/video_core/page_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_core/page_manager.cpp b/src/video_core/page_manager.cpp index 80b91b825..fefae81f4 100644 --- a/src/video_core/page_manager.cpp +++ b/src/video_core/page_manager.cpp @@ -32,7 +32,7 @@ constexpr size_t PAGEBITS = 12; #ifdef ENABLE_USERFAULTFD struct PageManager::Impl { Impl(Vulkan::Rasterizer* rasterizer_) : rasterizer{rasterizer_} { - uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); + uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY); ASSERT_MSG(uffd != -1, "{}", Common::GetLastErrorMsg()); // Request uffdio features from kernel. From dad5953e8c3cca86152d85a92b6415a3c8f8e80f Mon Sep 17 00:00:00 2001 From: Ada Ahmed Date: Sat, 7 Dec 2024 22:52:03 +0000 Subject: [PATCH 126/549] fix: fix #1457 again by moving av_err2str to a common header (#1688) --- src/common/support/avdec.h | 17 +++++++++++++++++ src/core/libraries/ajm/ajm_mp3.cpp | 2 ++ src/core/libraries/avplayer/avplayer_source.cpp | 11 +---------- src/core/libraries/videodec/videodec2_impl.cpp | 11 +---------- src/core/libraries/videodec/videodec_impl.cpp | 11 +---------- 5 files changed, 22 insertions(+), 30 deletions(-) create mode 100644 src/common/support/avdec.h diff --git a/src/common/support/avdec.h b/src/common/support/avdec.h new file mode 100644 index 000000000..fa3483dc4 --- /dev/null +++ b/src/common/support/avdec.h @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +// support header file for libav + +// The av_err2str macro in libavutil/error.h does not play nice with C++ +#ifdef av_err2str +#undef av_err2str +#include +av_always_inline std::string av_err2string(int errnum) { + char errbuf[AV_ERROR_MAX_STRING_SIZE]; + return av_make_error_string(errbuf, AV_ERROR_MAX_STRING_SIZE, errnum); +} +#define av_err2str(err) av_err2string(err).c_str() +#endif // av_err2str diff --git a/src/core/libraries/ajm/ajm_mp3.cpp b/src/core/libraries/ajm/ajm_mp3.cpp index 3b464238d..2c572a01b 100644 --- a/src/core/libraries/ajm/ajm_mp3.cpp +++ b/src/core/libraries/ajm/ajm_mp3.cpp @@ -12,6 +12,8 @@ extern "C" { #include } +#include "common/support/avdec.h" + namespace Libraries::Ajm { // Following tables have been reversed from AJM library diff --git a/src/core/libraries/avplayer/avplayer_source.cpp b/src/core/libraries/avplayer/avplayer_source.cpp index 950951673..cf783403c 100644 --- a/src/core/libraries/avplayer/avplayer_source.cpp +++ b/src/core/libraries/avplayer/avplayer_source.cpp @@ -18,16 +18,7 @@ extern "C" { #include } -// The av_err2str macro in libavutil/error.h does not play nice with C++ -#ifdef av_err2str -#undef av_err2str -#include -av_always_inline std::string av_err2string(int errnum) { - char errbuf[AV_ERROR_MAX_STRING_SIZE]; - return av_make_error_string(errbuf, AV_ERROR_MAX_STRING_SIZE, errnum); -} -#define av_err2str(err) av_err2string(err).c_str() -#endif // av_err2str +#include "common/support/avdec.h" namespace Libraries::AvPlayer { diff --git a/src/core/libraries/videodec/videodec2_impl.cpp b/src/core/libraries/videodec/videodec2_impl.cpp index 8daa48828..138d78af3 100644 --- a/src/core/libraries/videodec/videodec2_impl.cpp +++ b/src/core/libraries/videodec/videodec2_impl.cpp @@ -7,16 +7,7 @@ #include "common/logging/log.h" #include "core/libraries/videodec/videodec_error.h" -// The av_err2str macro in libavutil/error.h does not play nice with C++ -#ifdef av_err2str -#undef av_err2str -#include -av_always_inline std::string av_err2string(int errnum) { - char errbuf[AV_ERROR_MAX_STRING_SIZE]; - return av_make_error_string(errbuf, AV_ERROR_MAX_STRING_SIZE, errnum); -} -#define av_err2str(err) av_err2string(err).c_str() -#endif // av_err2str +#include "common/support/avdec.h" namespace Libraries::Vdec2 { diff --git a/src/core/libraries/videodec/videodec_impl.cpp b/src/core/libraries/videodec/videodec_impl.cpp index cf4846971..b5f72e9ce 100644 --- a/src/core/libraries/videodec/videodec_impl.cpp +++ b/src/core/libraries/videodec/videodec_impl.cpp @@ -8,16 +8,7 @@ #include "common/logging/log.h" #include "core/libraries/videodec/videodec_error.h" -// The av_err2str macro in libavutil/error.h does not play nice with C++ -#ifdef av_err2str -#undef av_err2str -#include -av_always_inline std::string av_err2string(int errnum) { - char errbuf[AV_ERROR_MAX_STRING_SIZE]; - return av_make_error_string(errbuf, AV_ERROR_MAX_STRING_SIZE, errnum); -} -#define av_err2str(err) av_err2string(err).c_str() -#endif // av_err2str +#include "common/support/avdec.h" namespace Libraries::Videodec { From 1940ac0fec81b88ea5bbb586e1e0edd35f6e98af Mon Sep 17 00:00:00 2001 From: auser1337 <154299690+auser1337@users.noreply.github.com> Date: Sun, 8 Dec 2024 00:18:12 -0800 Subject: [PATCH 127/549] ajm: support for multiple contexts (#1690) * ajm: support for multiple contexts * fix sceAjmInitialize --- src/core/libraries/ajm/ajm.cpp | 56 ++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/src/core/libraries/ajm/ajm.cpp b/src/core/libraries/ajm/ajm.cpp index a3728039e..3184fa64f 100644 --- a/src/core/libraries/ajm/ajm.cpp +++ b/src/core/libraries/ajm/ajm.cpp @@ -19,7 +19,7 @@ constexpr int ORBIS_AJM_CHANNELMASK_QUAD = 0x0033; constexpr int ORBIS_AJM_CHANNELMASK_5POINT1 = 0x060F; constexpr int ORBIS_AJM_CHANNELMASK_7POINT1 = 0x063F; -static std::unique_ptr context{}; +static std::unordered_map> contexts{}; u32 GetChannelMask(u32 num_channels) { switch (num_channels) { @@ -40,7 +40,13 @@ u32 GetChannelMask(u32 num_channels) { int PS4_SYSV_ABI sceAjmBatchCancel(const u32 context_id, const u32 batch_id) { LOG_INFO(Lib_Ajm, "called context_id = {} batch_id = {}", context_id, batch_id); - return context->BatchCancel(batch_id); + + auto it = contexts.find(context_id); + if (it == contexts.end()) { + return ORBIS_AJM_ERROR_INVALID_CONTEXT; + } + + return it->second->BatchCancel(batch_id); } int PS4_SYSV_ABI sceAjmBatchErrorDump() { @@ -90,14 +96,26 @@ int PS4_SYSV_ABI sceAjmBatchStartBuffer(u32 context_id, u8* p_batch, u32 batch_s u32* out_batch_id) { LOG_TRACE(Lib_Ajm, "called context = {}, batch_size = {:#x}, priority = {}", context_id, batch_size, priority); - return context->BatchStartBuffer(p_batch, batch_size, priority, batch_error, out_batch_id); + + auto it = contexts.find(context_id); + if (it == contexts.end()) { + return ORBIS_AJM_ERROR_INVALID_CONTEXT; + } + + return it->second->BatchStartBuffer(p_batch, batch_size, priority, batch_error, out_batch_id); } int PS4_SYSV_ABI sceAjmBatchWait(const u32 context_id, const u32 batch_id, const u32 timeout, AjmBatchError* const batch_error) { LOG_TRACE(Lib_Ajm, "called context = {}, batch_id = {}, timeout = {}", context_id, batch_id, timeout); - return context->BatchWait(batch_id, timeout, batch_error); + + auto it = contexts.find(context_id); + if (it == contexts.end()) { + return ORBIS_AJM_ERROR_INVALID_CONTEXT; + } + + return it->second->BatchWait(batch_id, timeout, batch_error); } int PS4_SYSV_ABI sceAjmDecAt9ParseConfigData() { @@ -117,12 +135,12 @@ int PS4_SYSV_ABI sceAjmFinalize() { int PS4_SYSV_ABI sceAjmInitialize(s64 reserved, u32* p_context_id) { LOG_INFO(Lib_Ajm, "called reserved = {}", reserved); - ASSERT_MSG(context == nullptr, "Multiple contexts are currently unsupported."); if (p_context_id == nullptr || reserved != 0) { return ORBIS_AJM_ERROR_INVALID_PARAMETER; } - *p_context_id = 1; - context = std::make_unique(); + u32 id = contexts.size() + 1; + *p_context_id = id; + contexts.emplace(id, std::make_unique()); return ORBIS_OK; } @@ -135,12 +153,24 @@ int PS4_SYSV_ABI sceAjmInstanceCreate(u32 context_id, AjmCodecType codec_type, AjmInstanceFlags flags, u32* out_instance) { LOG_INFO(Lib_Ajm, "called context = {}, codec_type = {}, flags = {:#x}", context_id, magic_enum::enum_name(codec_type), flags.raw); - return context->InstanceCreate(codec_type, flags, out_instance); + + auto it = contexts.find(context_id); + if (it == contexts.end()) { + return ORBIS_AJM_ERROR_INVALID_CONTEXT; + } + + return it->second->InstanceCreate(codec_type, flags, out_instance); } int PS4_SYSV_ABI sceAjmInstanceDestroy(u32 context_id, u32 instance_id) { LOG_INFO(Lib_Ajm, "called context = {}, instance = {}", context_id, instance_id); - return context->InstanceDestroy(instance_id); + + auto it = contexts.find(context_id); + if (it == contexts.end()) { + return ORBIS_AJM_ERROR_INVALID_CONTEXT; + } + + return it->second->InstanceDestroy(instance_id); } int PS4_SYSV_ABI sceAjmInstanceExtend() { @@ -168,7 +198,13 @@ int PS4_SYSV_ABI sceAjmModuleRegister(u32 context_id, AjmCodecType codec_type, s if (reserved != 0) { return ORBIS_AJM_ERROR_INVALID_PARAMETER; } - return context->ModuleRegister(codec_type); + + auto it = contexts.find(context_id); + if (it == contexts.end()) { + return ORBIS_AJM_ERROR_INVALID_CONTEXT; + } + + return it->second->ModuleRegister(codec_type); } int PS4_SYSV_ABI sceAjmModuleUnregister() { From 7d546f32d8d175476a9dfe80b53f0b4a66978dde Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sun, 8 Dec 2024 00:19:39 -0800 Subject: [PATCH 128/549] image_view: Add more BGRA storage format swizzles. (#1693) --- .../renderer_vulkan/vk_instance.cpp | 5 +++-- src/video_core/texture_cache/image_view.cpp | 21 ++++++++++++++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 49e4987db..81784eb60 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -70,8 +70,9 @@ std::unordered_map GetFormatProperties( static constexpr std::array misc_formats = { vk::Format::eA2R10G10B10UnormPack32, vk::Format::eA8B8G8R8UnormPack32, vk::Format::eA8B8G8R8SrgbPack32, vk::Format::eB8G8R8A8Unorm, - vk::Format::eB8G8R8A8Srgb, vk::Format::eR5G6B5UnormPack16, - vk::Format::eD24UnormS8Uint, + vk::Format::eB8G8R8A8Snorm, vk::Format::eB8G8R8A8Uint, + vk::Format::eB8G8R8A8Sint, vk::Format::eB8G8R8A8Srgb, + vk::Format::eR5G6B5UnormPack16, vk::Format::eD24UnormS8Uint, }; for (const auto& format : misc_formats) { if (!format_properties.contains(format)) { diff --git a/src/video_core/texture_cache/image_view.cpp b/src/video_core/texture_cache/image_view.cpp index 61cabdf11..57a58c714 100644 --- a/src/video_core/texture_cache/image_view.cpp +++ b/src/video_core/texture_cache/image_view.cpp @@ -58,11 +58,22 @@ bool IsIdentityMapping(u32 dst_sel, u32 num_components) { } vk::Format TrySwizzleFormat(vk::Format format, u32 dst_sel) { - if (format == vk::Format::eR8G8B8A8Unorm && dst_sel == 0b111100101110) { - return vk::Format::eB8G8R8A8Unorm; - } - if (format == vk::Format::eR8G8B8A8Srgb && dst_sel == 0b111100101110) { - return vk::Format::eB8G8R8A8Srgb; + // BGRA + if (dst_sel == 0b111100101110) { + switch (format) { + case vk::Format::eR8G8B8A8Unorm: + return vk::Format::eB8G8R8A8Unorm; + case vk::Format::eR8G8B8A8Snorm: + return vk::Format::eB8G8R8A8Snorm; + case vk::Format::eR8G8B8A8Uint: + return vk::Format::eB8G8R8A8Uint; + case vk::Format::eR8G8B8A8Sint: + return vk::Format::eB8G8R8A8Sint; + case vk::Format::eR8G8B8A8Srgb: + return vk::Format::eB8G8R8A8Srgb; + default: + break; + } } return format; } From 71a82199ed57245d51776e620ec21b6b4a3ea75d Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sun, 8 Dec 2024 00:20:05 -0800 Subject: [PATCH 129/549] shader_recompiler: Fix check for fragment depth store. (#1694) --- src/shader_recompiler/backend/spirv/emit_spirv.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index 1e7032f10..23800fc49 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -284,7 +284,7 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) { ctx.AddExtension("SPV_EXT_demote_to_helper_invocation"); ctx.AddCapability(spv::Capability::DemoteToHelperInvocationEXT); } - if (info.stores.Get(IR::Attribute::Depth)) { + if (info.stores.GetAny(IR::Attribute::Depth)) { ctx.AddExecutionMode(main, spv::ExecutionMode::DepthReplacing); } break; From 4fb2247196d4626bab8f2c28710b0c34cad053fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A5IGA?= <164882787+Xphalnos@users.noreply.github.com> Date: Sun, 8 Dec 2024 09:20:24 +0100 Subject: [PATCH 130/549] Better title bar for Cheats/Patches menu (#1696) --- src/qt_gui/cheats_patches.cpp | 2 +- src/qt_gui/translations/ar.ts | 4 ++-- src/qt_gui/translations/da_DK.ts | 4 ++-- src/qt_gui/translations/de.ts | 4 ++-- src/qt_gui/translations/el.ts | 4 ++-- src/qt_gui/translations/en.ts | 4 ++-- src/qt_gui/translations/es_ES.ts | 4 ++-- src/qt_gui/translations/fa_IR.ts | 4 ++-- src/qt_gui/translations/fi.ts | 4 ++-- src/qt_gui/translations/fr.ts | 34 ++++++++++++++++---------------- src/qt_gui/translations/hu_HU.ts | 4 ++-- src/qt_gui/translations/id.ts | 4 ++-- src/qt_gui/translations/it.ts | 4 ++-- src/qt_gui/translations/ja_JP.ts | 4 ++-- src/qt_gui/translations/ko_KR.ts | 4 ++-- src/qt_gui/translations/lt_LT.ts | 4 ++-- src/qt_gui/translations/nb_NO.ts | 4 ++-- src/qt_gui/translations/nl.ts | 4 ++-- src/qt_gui/translations/pl_PL.ts | 4 ++-- src/qt_gui/translations/pt_BR.ts | 4 ++-- src/qt_gui/translations/ro_RO.ts | 4 ++-- src/qt_gui/translations/ru_RU.ts | 4 ++-- src/qt_gui/translations/sq.ts | 4 ++-- src/qt_gui/translations/tr_TR.ts | 4 ++-- src/qt_gui/translations/uk_UA.ts | 4 ++-- src/qt_gui/translations/vi_VN.ts | 4 ++-- src/qt_gui/translations/zh_CN.ts | 4 ++-- src/qt_gui/translations/zh_TW.ts | 4 ++-- 28 files changed, 70 insertions(+), 70 deletions(-) diff --git a/src/qt_gui/cheats_patches.cpp b/src/qt_gui/cheats_patches.cpp index a35136f12..3e7c22451 100644 --- a/src/qt_gui/cheats_patches.cpp +++ b/src/qt_gui/cheats_patches.cpp @@ -39,7 +39,7 @@ CheatsPatches::CheatsPatches(const QString& gameName, const QString& gameSerial, m_gameSize(gameSize), m_gameImage(gameImage), manager(new QNetworkAccessManager(this)) { setupUI(); resize(500, 400); - setWindowTitle(tr("Cheats / Patches")); + setWindowTitle(tr("Cheats / Patches for ") + m_gameName); } CheatsPatches::~CheatsPatches() {} diff --git a/src/qt_gui/translations/ar.ts b/src/qt_gui/translations/ar.ts index 25e215183..45a030062 100644 --- a/src/qt_gui/translations/ar.ts +++ b/src/qt_gui/translations/ar.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - الغش / التصحيحات + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts index 14c42f1d9..fa7e9c50b 100644 --- a/src/qt_gui/translations/da_DK.ts +++ b/src/qt_gui/translations/da_DK.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Snyd / Patches + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/de.ts b/src/qt_gui/translations/de.ts index 64a6c6480..0fa534bb9 100644 --- a/src/qt_gui/translations/de.ts +++ b/src/qt_gui/translations/de.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Cheats / Patches + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/el.ts b/src/qt_gui/translations/el.ts index e064f8c26..5233454b9 100644 --- a/src/qt_gui/translations/el.ts +++ b/src/qt_gui/translations/el.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Cheats / Patches + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index 9bf7c7188..cd5a5fe8a 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Cheats / Patches + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index 5d637249e..0598e04f3 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Trucos / Parches + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index 55a2fdf53..3cd72cac3 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - چیت / پچ ها + Cheats / Patches for + Cheats / Patches for ا diff --git a/src/qt_gui/translations/fi.ts b/src/qt_gui/translations/fi.ts index 4d160bf6b..8c9518d0f 100644 --- a/src/qt_gui/translations/fi.ts +++ b/src/qt_gui/translations/fi.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Huijaukset / Korjaukset + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/fr.ts b/src/qt_gui/translations/fr.ts index 39cd11bf6..a9903d43c 100644 --- a/src/qt_gui/translations/fr.ts +++ b/src/qt_gui/translations/fr.ts @@ -62,7 +62,7 @@ Select which directory you want to install to. - Select which directory you want to install to. + Sélectionnez le répertoire où vous souhaitez effectuer l'installation. @@ -158,22 +158,22 @@ Delete... - Delete... + Supprimer... Delete Game - Delete Game + Supprimer jeu Delete Update - Delete Update + Supprimer MÀJ Delete DLC - Delete DLC + Supprimer DLC @@ -203,7 +203,7 @@ Game - Game + Jeu @@ -213,17 +213,17 @@ This game has no update to delete! - This game has no update to delete! + Ce jeu n'a pas de mise à jour à supprimer! Update - Update + Mise à jour This game has no DLC to delete! - This game has no DLC to delete! + Ce jeu n'a pas de DLC à supprimer! @@ -233,12 +233,12 @@ Delete %1 - Delete %1 + Supprime %1 Are you sure you want to delete %1's %2 directory? - Are you sure you want to delete %1's %2 directory? + Êtes vous sûr de vouloir supprimer le répertoire %1 %2 ? @@ -495,7 +495,7 @@ Enable Separate Update Folder - Enable Separate Update Folder + Dossier séparé pour les mises à jours @@ -510,7 +510,7 @@ Enable Discord Rich Presence - Activer Discord Rich Presence + Activer la présence Discord @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Cheats/Patches + Cheats / Patches for + Cheats/Patchs pour @@ -1159,7 +1159,7 @@ separateUpdatesCheckBox - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + Dossier séparé pour les mises à jours:\nInstalle les mises à jours des jeux dans un dossier séparé pour une gestion plus facile. @@ -1169,7 +1169,7 @@ ps4proCheckBox - Est-ce un PS4 Pro:\nFait en sorte que l'émulateur se comporte comme un PS4 PRO, ce qui peut activer des fonctionnalités spéciales dans les jeux qui le prennent en charge. + Mode PS4 Pro:\nFait en sorte que l'émulateur se comporte comme un PS4 PRO, ce qui peut activer des fonctionnalités spéciales dans les jeux qui le prennent en charge. diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index a43b8d371..135fc4231 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Csalások / Javítások + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/id.ts b/src/qt_gui/translations/id.ts index d616f1cf3..5c6148b86 100644 --- a/src/qt_gui/translations/id.ts +++ b/src/qt_gui/translations/id.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Cheat / Patch + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/it.ts b/src/qt_gui/translations/it.ts index c59289314..b496cc330 100644 --- a/src/qt_gui/translations/it.ts +++ b/src/qt_gui/translations/it.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Trucchi / Patch + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index f4a4b15ad..3cee79951 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - チート / パッチ + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts index 2fa3ee153..ea449ebc1 100644 --- a/src/qt_gui/translations/ko_KR.ts +++ b/src/qt_gui/translations/ko_KR.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Cheats / Patches + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts index 16aaf5d86..dd467283f 100644 --- a/src/qt_gui/translations/lt_LT.ts +++ b/src/qt_gui/translations/lt_LT.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Sukčiavimai / Pataisos + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/nb_NO.ts b/src/qt_gui/translations/nb_NO.ts index e02f24182..de0a88b73 100644 --- a/src/qt_gui/translations/nb_NO.ts +++ b/src/qt_gui/translations/nb_NO.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Juks / Programrettelse + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/nl.ts b/src/qt_gui/translations/nl.ts index b0cfaff5e..399aef8be 100644 --- a/src/qt_gui/translations/nl.ts +++ b/src/qt_gui/translations/nl.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Cheats / Patches + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index 4d11c13f6..730b37124 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Kody / poprawki + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index f1d3631d8..7ea63b9fb 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Cheats / Patches + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index fff0bcddb..d73b1be6c 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Cheats / Patches + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index 052623235..ab7e2c40e 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Читы и патчи + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index f7144a001..bc8f24162 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Mashtrime / Arna + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index 335465778..5944f980f 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Hileler / Yamalar + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/uk_UA.ts b/src/qt_gui/translations/uk_UA.ts index 31bfe9dba..b28035335 100644 --- a/src/qt_gui/translations/uk_UA.ts +++ b/src/qt_gui/translations/uk_UA.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Чити та Патчі + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index 223cb9ed0..b5ae8bd39 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Cheat / Bản vá + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index 4fe1f7c42..989e71071 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - 作弊码 / 补丁 + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index 4db00775d..b650a74ea 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - 作弊碼 / 修補檔 + Cheats / Patches for + Cheats / Patches for From 0b672a08acd6845f9f5c36c67511454be04b7fd4 Mon Sep 17 00:00:00 2001 From: "Daniel R." <47796739+polybiusproxy@users.noreply.github.com> Date: Sun, 8 Dec 2024 15:57:51 +0100 Subject: [PATCH 131/549] video_core: improve image cube heuristic --- src/video_core/texture_cache/image.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/video_core/texture_cache/image.cpp b/src/video_core/texture_cache/image.cpp index 3d5202ad6..ea298c04b 100644 --- a/src/video_core/texture_cache/image.cpp +++ b/src/video_core/texture_cache/image.cpp @@ -151,9 +151,10 @@ Image::Image(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_, // the texture cache should re-create the resource with the usage requested vk::ImageCreateFlags flags{vk::ImageCreateFlagBits::eMutableFormat | vk::ImageCreateFlagBits::eExtendedUsage}; - const bool can_be_cube = (info.type == vk::ImageType::e2D) && - (info.resources.layers % 6 == 0) && - (info.size.width == info.size.height); + const bool can_be_cube = + (info.type == vk::ImageType::e2D) && + (info.props.is_pow2 ? (info.resources.layers % 8) : (info.resources.layers % 6) == 0) && + (info.size.width == info.size.height); if (info.props.is_cube || can_be_cube) { flags |= vk::ImageCreateFlagBits::eCubeCompatible; } else if (info.props.is_volume) { From a88850fec6d051fec52adbd864f1ff1a6d58f227 Mon Sep 17 00:00:00 2001 From: "Daniel R." <47796739+polybiusproxy@users.noreply.github.com> Date: Sun, 8 Dec 2024 16:02:38 +0100 Subject: [PATCH 132/549] video_core/amdgpu: fix calculation of lod range --- src/video_core/amdgpu/resource.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video_core/amdgpu/resource.h b/src/video_core/amdgpu/resource.h index a78a68391..ba87425f2 100644 --- a/src/video_core/amdgpu/resource.h +++ b/src/video_core/amdgpu/resource.h @@ -420,11 +420,11 @@ struct Sampler { } float MinLod() const noexcept { - return static_cast(min_lod); + return static_cast(min_lod.Value()) / 256.0f; } float MaxLod() const noexcept { - return static_cast(max_lod); + return static_cast(max_lod.Value()) / 256.0f; } }; From 1793fd4df02d4f56ebe572be26a0d50b4d66118d Mon Sep 17 00:00:00 2001 From: "Daniel R." <47796739+polybiusproxy@users.noreply.github.com> Date: Sun, 8 Dec 2024 16:05:36 +0100 Subject: [PATCH 133/549] format --- src/video_core/texture_cache/image.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_core/texture_cache/image.cpp b/src/video_core/texture_cache/image.cpp index ea298c04b..e7e1ce1da 100644 --- a/src/video_core/texture_cache/image.cpp +++ b/src/video_core/texture_cache/image.cpp @@ -153,7 +153,7 @@ Image::Image(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_, vk::ImageCreateFlagBits::eExtendedUsage}; const bool can_be_cube = (info.type == vk::ImageType::e2D) && - (info.props.is_pow2 ? (info.resources.layers % 8) : (info.resources.layers % 6) == 0) && + ((info.props.is_pow2 ? (info.resources.layers % 8) : (info.resources.layers % 6)) == 0) && (info.size.width == info.size.height); if (info.props.is_cube || can_be_cube) { flags |= vk::ImageCreateFlagBits::eCubeCompatible; From fea2593ab4be8638426e4f608ebbe2314d83d9f6 Mon Sep 17 00:00:00 2001 From: "Daniel R." <47796739+polybiusproxy@users.noreply.github.com> Date: Sun, 8 Dec 2024 17:30:33 +0100 Subject: [PATCH 134/549] The way to Unity, pt.3 (#1681) --- src/common/ntapi.cpp | 2 + src/common/ntapi.h | 20 ++++ src/core/libraries/ajm/ajm_context.cpp | 2 + src/core/libraries/kernel/kernel.cpp | 3 +- src/core/libraries/kernel/memory.cpp | 3 +- src/core/libraries/kernel/process.cpp | 15 ++- src/core/libraries/kernel/sync/semaphore.h | 42 ++++--- .../libraries/kernel/threads/event_flag.cpp | 30 ++++- .../libraries/kernel/threads/exception.cpp | 50 ++++++-- src/core/libraries/kernel/threads/pthread.cpp | 2 + .../libraries/kernel/threads/semaphore.cpp | 108 ++++++++++++++++-- src/core/libraries/save_data/save_backup.cpp | 2 +- src/core/libraries/save_data/save_memory.cpp | 2 +- src/core/linker.h | 9 ++ src/core/memory.cpp | 8 +- src/core/memory.h | 4 +- src/imgui/renderer/texture_manager.cpp | 4 + 17 files changed, 256 insertions(+), 50 deletions(-) diff --git a/src/common/ntapi.cpp b/src/common/ntapi.cpp index e0ff1cef0..c76c4657e 100644 --- a/src/common/ntapi.cpp +++ b/src/common/ntapi.cpp @@ -9,6 +9,7 @@ NtClose_t NtClose = nullptr; NtSetInformationFile_t NtSetInformationFile = nullptr; NtCreateThread_t NtCreateThread = nullptr; NtTerminateThread_t NtTerminateThread = nullptr; +NtQueueApcThreadEx_t NtQueueApcThreadEx = nullptr; namespace Common::NtApi { @@ -21,6 +22,7 @@ void Initialize() { (NtSetInformationFile_t)GetProcAddress(nt_handle, "NtSetInformationFile"); NtCreateThread = (NtCreateThread_t)GetProcAddress(nt_handle, "NtCreateThread"); NtTerminateThread = (NtTerminateThread_t)GetProcAddress(nt_handle, "NtTerminateThread"); + NtQueueApcThreadEx = (NtQueueApcThreadEx_t)GetProcAddress(nt_handle, "NtQueueApcThreadEx"); } } // namespace Common::NtApi diff --git a/src/common/ntapi.h b/src/common/ntapi.h index cb1ba7f1c..daab8440d 100644 --- a/src/common/ntapi.h +++ b/src/common/ntapi.h @@ -509,6 +509,20 @@ typedef struct _TEB { /* win32/win64 */ static_assert(offsetof(TEB, DeallocationStack) == 0x1478); /* The only member we care about at the moment */ +typedef enum _QUEUE_USER_APC_FLAGS { + QueueUserApcFlagsNone, + QueueUserApcFlagsSpecialUserApc, + QueueUserApcFlagsMaxValue +} QUEUE_USER_APC_FLAGS; + +typedef union _USER_APC_OPTION { + ULONG_PTR UserApcFlags; + HANDLE MemoryReserveHandle; +} USER_APC_OPTION, *PUSER_APC_OPTION; + +using PPS_APC_ROUTINE = void (*)(PVOID ApcArgument1, PVOID ApcArgument2, PVOID ApcArgument3, + PCONTEXT Context); + typedef u64(__stdcall* NtClose_t)(HANDLE Handle); typedef u64(__stdcall* NtSetInformationFile_t)(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, @@ -522,10 +536,16 @@ typedef u64(__stdcall* NtCreateThread_t)(PHANDLE ThreadHandle, ACCESS_MASK Desir typedef u64(__stdcall* NtTerminateThread_t)(HANDLE ThreadHandle, u64 ExitStatus); +typedef u64(__stdcall* NtQueueApcThreadEx_t)(HANDLE ThreadHandle, + USER_APC_OPTION UserApcReserveHandle, + PPS_APC_ROUTINE ApcRoutine, PVOID ApcArgument1, + PVOID ApcArgument2, PVOID ApcArgument3); + extern NtClose_t NtClose; extern NtSetInformationFile_t NtSetInformationFile; extern NtCreateThread_t NtCreateThread; extern NtTerminateThread_t NtTerminateThread; +extern NtQueueApcThreadEx_t NtQueueApcThreadEx; namespace Common::NtApi { void Initialize(); diff --git a/src/core/libraries/ajm/ajm_context.cpp b/src/core/libraries/ajm/ajm_context.cpp index e30e1c478..09255110c 100644 --- a/src/core/libraries/ajm/ajm_context.cpp +++ b/src/core/libraries/ajm/ajm_context.cpp @@ -3,6 +3,7 @@ #include "common/assert.h" #include "common/logging/log.h" +#include "common/thread.h" #include "core/libraries/ajm/ajm.h" #include "core/libraries/ajm/ajm_at9.h" #include "core/libraries/ajm/ajm_context.h" @@ -53,6 +54,7 @@ s32 AjmContext::ModuleRegister(AjmCodecType type) { } void AjmContext::WorkerThread(std::stop_token stop) { + Common::SetCurrentThreadName("shadPS4:AjmWorker"); while (!stop.stop_requested()) { auto batch = batch_queue.PopWait(stop); if (batch != nullptr) { diff --git a/src/core/libraries/kernel/kernel.cpp b/src/core/libraries/kernel/kernel.cpp index 0b4e89fc7..bda446257 100644 --- a/src/core/libraries/kernel/kernel.cpp +++ b/src/core/libraries/kernel/kernel.cpp @@ -46,7 +46,7 @@ void KernelSignalRequest() { } static void KernelServiceThread(std::stop_token stoken) { - Common::SetCurrentThreadName("shadPS4:Kernel_ServiceThread"); + Common::SetCurrentThreadName("shadPS4:KernelServiceThread"); while (!stoken.stop_requested()) { HLE_TRACE; @@ -255,6 +255,7 @@ void RegisterKernel(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("NWtTN10cJzE", "libSceLibcInternalExt", 1, "libSceLibcInternal", 1, 1, sceLibcHeapGetTraceInfo); LIB_FUNCTION("FxVZqBAA7ks", "libkernel", 1, "libkernel", 1, 1, ps4__write); + LIB_FUNCTION("FN4gaPmuFV8", "libScePosix", 1, "libkernel", 1, 1, ps4__write); } } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/memory.cpp b/src/core/libraries/kernel/memory.cpp index 606c5c185..7d326cbbf 100644 --- a/src/core/libraries/kernel/memory.cpp +++ b/src/core/libraries/kernel/memory.cpp @@ -492,8 +492,7 @@ int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len) { return ORBIS_OK; } auto* memory = Core::Memory::Instance(); - memory->UnmapMemory(std::bit_cast(addr), len); - return ORBIS_OK; + return memory->UnmapMemory(std::bit_cast(addr), len); } int PS4_SYSV_ABI posix_munmap(void* addr, size_t len) { diff --git a/src/core/libraries/kernel/process.cpp b/src/core/libraries/kernel/process.cpp index a657ddf98..6c29d9305 100644 --- a/src/core/libraries/kernel/process.cpp +++ b/src/core/libraries/kernel/process.cpp @@ -45,10 +45,11 @@ s32 PS4_SYSV_ABI sceKernelLoadStartModule(const char* moduleFileName, size_t arg // Load PRX module and relocate any modules that import it. auto* linker = Common::Singleton::Instance(); - u32 handle = linker->LoadModule(path, true); - if (handle == -1) { - return ORBIS_KERNEL_ERROR_EINVAL; + u32 handle = linker->FindByName(path); + if (handle != -1) { + return handle; } + handle = linker->LoadModule(path, true); auto* module = linker->GetModule(handle); linker->RelocateAnyImports(module); @@ -60,7 +61,10 @@ s32 PS4_SYSV_ABI sceKernelLoadStartModule(const char* moduleFileName, size_t arg // Retrieve and verify proc param according to libkernel. u64* param = module->GetProcParam(); ASSERT_MSG(!param || param[0] >= 0x18, "Invalid module param size: {}", param[0]); - module->Start(args, argp, param); + s32 ret = module->Start(args, argp, param); + if (pRes) { + *pRes = ret; + } return handle; } @@ -104,6 +108,9 @@ s32 PS4_SYSV_ABI sceKernelGetModuleInfoForUnwind(VAddr addr, int flags, LOG_INFO(Lib_Kernel, "called addr = {:#x}, flags = {:#x}", addr, flags); auto* linker = Common::Singleton::Instance(); auto* module = linker->FindByAddress(addr); + if (!module) { + return ORBIS_KERNEL_ERROR_EFAULT; + } const auto mod_info = module->GetModuleInfoEx(); // Fill in module info. diff --git a/src/core/libraries/kernel/sync/semaphore.h b/src/core/libraries/kernel/sync/semaphore.h index 884b08968..48a5dc0d8 100644 --- a/src/core/libraries/kernel/sync/semaphore.h +++ b/src/core/libraries/kernel/sync/semaphore.h @@ -87,14 +87,23 @@ public: template bool try_acquire_for(const std::chrono::duration& rel_time) { #ifdef _WIN64 - const auto rel_time_ms = std::chrono::ceil(rel_time); - const u64 timeout_ms = static_cast(rel_time_ms.count()); + const auto start_time = std::chrono::high_resolution_clock::now(); + auto rel_time_ms = std::chrono::ceil(rel_time); - if (timeout_ms == 0) { - return false; + while (rel_time_ms.count() > 0) { + u64 timeout_ms = static_cast(rel_time_ms.count()); + u64 res = WaitForSingleObjectEx(sem, timeout_ms, true); + if (res == WAIT_OBJECT_0) { + return true; + } else if (res == WAIT_IO_COMPLETION) { + auto elapsed_time = std::chrono::high_resolution_clock::now() - start_time; + rel_time_ms -= std::chrono::duration_cast(elapsed_time); + } else { + return false; + } } - return WaitForSingleObjectEx(sem, timeout_ms, true) == WAIT_OBJECT_0; + return false; #elif defined(__APPLE__) const auto rel_time_ns = std::chrono::ceil(rel_time).count(); const auto timeout = dispatch_time(DISPATCH_TIME_NOW, rel_time_ns); @@ -107,19 +116,26 @@ public: template bool try_acquire_until(const std::chrono::time_point& abs_time) { #ifdef _WIN64 - const auto now = Clock::now(); - if (now >= abs_time) { + const auto start_time = Clock::now(); + if (start_time >= abs_time) { return false; } - const auto rel_time = std::chrono::ceil(abs_time - now); - const u64 timeout_ms = static_cast(rel_time.count()); - if (timeout_ms == 0) { - return false; + auto rel_time = std::chrono::ceil(abs_time - start_time); + while (rel_time.count() > 0) { + u64 timeout_ms = static_cast(rel_time.count()); + u64 res = WaitForSingleObjectEx(sem, timeout_ms, true); + if (res == WAIT_OBJECT_0) { + return true; + } else if (res == WAIT_IO_COMPLETION) { + auto elapsed_time = Clock::now() - start_time; + rel_time -= std::chrono::duration_cast(elapsed_time); + } else { + return false; + } } - u64 res = WaitForSingleObjectEx(sem, static_cast(timeout_ms), true); - return res == WAIT_OBJECT_0; + return false; #elif defined(__APPLE__) auto abs_s = std::chrono::time_point_cast(abs_time); auto abs_ns = std::chrono::time_point_cast(abs_time) - diff --git a/src/core/libraries/kernel/threads/event_flag.cpp b/src/core/libraries/kernel/threads/event_flag.cpp index ce75bed9e..24ddcb927 100644 --- a/src/core/libraries/kernel/threads/event_flag.cpp +++ b/src/core/libraries/kernel/threads/event_flag.cpp @@ -132,6 +132,33 @@ public: m_bits &= bits; } + void Cancel(u64 setPattern, int* numWaitThreads) { + std::unique_lock lock{m_mutex}; + + while (m_status != Status::Set) { + m_mutex.unlock(); + std::this_thread::sleep_for(std::chrono::microseconds(10)); + m_mutex.lock(); + } + + if (numWaitThreads) { + *numWaitThreads = m_waiting_threads; + } + + m_status = Status::Canceled; + m_bits = setPattern; + + m_cond_var.notify_all(); + + while (m_waiting_threads > 0) { + m_mutex.unlock(); + std::this_thread::sleep_for(std::chrono::microseconds(10)); + m_mutex.lock(); + } + + m_status = Status::Set; + } + private: enum class Status { Set, Canceled, Deleted }; @@ -232,7 +259,8 @@ int PS4_SYSV_ABI sceKernelClearEventFlag(OrbisKernelEventFlag ef, u64 bitPattern int PS4_SYSV_ABI sceKernelCancelEventFlag(OrbisKernelEventFlag ef, u64 setPattern, int* pNumWaitThreads) { - LOG_ERROR(Kernel_Event, "(STUBBED) called"); + LOG_DEBUG(Kernel_Event, "called"); + ef->Cancel(setPattern, pNumWaitThreads); return ORBIS_OK; } diff --git a/src/core/libraries/kernel/threads/exception.cpp b/src/core/libraries/kernel/threads/exception.cpp index b6d89aae4..017984e0d 100644 --- a/src/core/libraries/kernel/threads/exception.cpp +++ b/src/core/libraries/kernel/threads/exception.cpp @@ -7,6 +7,7 @@ #include "core/libraries/libs.h" #ifdef _WIN64 +#include "common/ntapi.h" #else #include #endif @@ -64,6 +65,34 @@ void SigactionHandler(int signum, siginfo_t* inf, ucontext_t* raw_context) { handler(POSIX_SIGUSR1, &ctx); } } +#else +void ExceptionHandler(void* arg1, void* arg2, void* arg3, PCONTEXT context) { + const char* thrName = (char*)arg1; + LOG_INFO(Lib_Kernel, "Exception raised successfully on thread '{}'", thrName); + const auto handler = Handlers[POSIX_SIGUSR1]; + if (handler) { + auto ctx = Ucontext{}; + ctx.uc_mcontext.mc_r8 = context->R8; + ctx.uc_mcontext.mc_r9 = context->R9; + ctx.uc_mcontext.mc_r10 = context->R10; + ctx.uc_mcontext.mc_r11 = context->R11; + ctx.uc_mcontext.mc_r12 = context->R12; + ctx.uc_mcontext.mc_r13 = context->R13; + ctx.uc_mcontext.mc_r14 = context->R14; + ctx.uc_mcontext.mc_r15 = context->R15; + ctx.uc_mcontext.mc_rdi = context->Rdi; + ctx.uc_mcontext.mc_rsi = context->Rsi; + ctx.uc_mcontext.mc_rbp = context->Rbp; + ctx.uc_mcontext.mc_rbx = context->Rbx; + ctx.uc_mcontext.mc_rdx = context->Rdx; + ctx.uc_mcontext.mc_rax = context->Rax; + ctx.uc_mcontext.mc_rcx = context->Rcx; + ctx.uc_mcontext.mc_rsp = context->Rsp; + ctx.uc_mcontext.mc_fs = context->SegFs; + ctx.uc_mcontext.mc_gs = context->SegGs; + handler(POSIX_SIGUSR1, &ctx); + } +} #endif int PS4_SYSV_ABI sceKernelInstallExceptionHandler(s32 signum, SceKernelExceptionHandler handler) { @@ -73,9 +102,7 @@ int PS4_SYSV_ABI sceKernelInstallExceptionHandler(s32 signum, SceKernelException } ASSERT_MSG(!Handlers[POSIX_SIGUSR1], "Invalid parameters"); Handlers[POSIX_SIGUSR1] = handler; -#ifdef _WIN64 - UNREACHABLE_MSG("Missing exception implementation"); -#else +#ifndef _WIN64 struct sigaction act = {}; act.sa_flags = SA_SIGINFO | SA_RESTART; act.sa_sigaction = reinterpret_cast(SigactionHandler); @@ -91,9 +118,7 @@ int PS4_SYSV_ABI sceKernelRemoveExceptionHandler(s32 signum) { } ASSERT_MSG(Handlers[POSIX_SIGUSR1], "Invalid parameters"); Handlers[POSIX_SIGUSR1] = nullptr; -#ifdef _WIN64 - UNREACHABLE_MSG("Missing exception implementation"); -#else +#ifndef _WIN64 struct sigaction act = {}; act.sa_flags = SA_SIGINFO | SA_RESTART; act.sa_sigaction = nullptr; @@ -103,13 +128,18 @@ int PS4_SYSV_ABI sceKernelRemoveExceptionHandler(s32 signum) { } int PS4_SYSV_ABI sceKernelRaiseException(PthreadT thread, int signum) { - LOG_ERROR(Lib_Kernel, "Raising exception"); + LOG_WARNING(Lib_Kernel, "Raising exception on thread '{}'", thread->name); ASSERT_MSG(signum == POSIX_SIGUSR1, "Attempting to raise non user defined signal!"); -#ifdef _WIN64 - UNREACHABLE_MSG("Missing exception implementation"); -#else +#ifndef _WIN64 pthread_t pthr = *reinterpret_cast(thread->native_thr.GetHandle()); pthread_kill(pthr, SIGUSR2); +#else + USER_APC_OPTION option; + option.UserApcFlags = QueueUserApcFlagsSpecialUserApc; + + u64 res = NtQueueApcThreadEx(reinterpret_cast(thread->native_thr.GetHandle()), option, + ExceptionHandler, (void*)thread->name.c_str(), nullptr, nullptr); + ASSERT(res == 0); #endif return 0; } diff --git a/src/core/libraries/kernel/threads/pthread.cpp b/src/core/libraries/kernel/threads/pthread.cpp index b2fe09934..c83af86d0 100644 --- a/src/core/libraries/kernel/threads/pthread.cpp +++ b/src/core/libraries/kernel/threads/pthread.cpp @@ -540,6 +540,8 @@ void RegisterThread(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("onNY9Byn-W8", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_join)); LIB_FUNCTION("P41kTWUS3EI", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_getschedparam)); + LIB_FUNCTION("oIRFTjoILbg", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_setschedparam)); LIB_FUNCTION("How7B8Oet6k", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_getname_np)); LIB_FUNCTION("3kg7rT0NQIs", "libkernel", 1, "libkernel", 1, 1, posix_pthread_exit); LIB_FUNCTION("aI+OeCz8xrQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self); diff --git a/src/core/libraries/kernel/threads/semaphore.cpp b/src/core/libraries/kernel/threads/semaphore.cpp index 5aa04f251..39c1a0233 100644 --- a/src/core/libraries/kernel/threads/semaphore.cpp +++ b/src/core/libraries/kernel/threads/semaphore.cpp @@ -111,15 +111,19 @@ public: BinarySemaphore sem; u32 priority; s32 need_count; + std::string thr_name; bool was_signaled{}; bool was_deleted{}; bool was_cancled{}; - explicit WaitingThread(s32 need_count, bool is_fifo) : sem{0}, need_count{need_count} { + explicit WaitingThread(s32 need_count, bool is_fifo) + : sem{0}, priority{0}, need_count{need_count} { // Retrieve calling thread priority for sorting into waiting threads list. if (!is_fifo) { priority = g_curthread->attr.prio; } + + thr_name = g_curthread->name; } int GetResult(bool timed_out) { @@ -232,6 +236,7 @@ int PS4_SYSV_ABI sceKernelDeleteSema(OrbisKernelSema sem) { return ORBIS_KERNEL_ERROR_ESRCH; } sem->Delete(); + delete sem; return ORBIS_OK; } @@ -246,6 +251,16 @@ int PS4_SYSV_ABI posix_sem_init(PthreadSem** sem, int pshared, u32 value) { return 0; } +int PS4_SYSV_ABI posix_sem_destroy(PthreadSem** sem) { + if (sem == nullptr || *sem == nullptr) { + *__Error() = POSIX_EINVAL; + return -1; + } + delete *sem; + *sem = nullptr; + return 0; +} + int PS4_SYSV_ABI posix_sem_wait(PthreadSem** sem) { if (sem == nullptr || *sem == nullptr) { *__Error() = POSIX_EINVAL; @@ -296,16 +311,6 @@ int PS4_SYSV_ABI posix_sem_post(PthreadSem** sem) { return 0; } -int PS4_SYSV_ABI posix_sem_destroy(PthreadSem** sem) { - if (sem == nullptr || *sem == nullptr) { - *__Error() = POSIX_EINVAL; - return -1; - } - delete *sem; - *sem = nullptr; - return 0; -} - int PS4_SYSV_ABI posix_sem_getvalue(PthreadSem** sem, int* sval) { if (sem == nullptr || *sem == nullptr) { *__Error() = POSIX_EINVAL; @@ -317,6 +322,77 @@ int PS4_SYSV_ABI posix_sem_getvalue(PthreadSem** sem, int* sval) { return 0; } +s32 PS4_SYSV_ABI scePthreadSemInit(PthreadSem** sem, int flag, u32 value, const char* name) { + if (flag != 0) { + return ORBIS_KERNEL_ERROR_EINVAL; + } + + s32 ret = posix_sem_init(sem, 0, value); + if (ret != 0) { + return ErrnoToSceKernelError(*__Error()); + } + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI scePthreadSemDestroy(PthreadSem** sem) { + s32 ret = posix_sem_destroy(sem); + if (ret != 0) { + return ErrnoToSceKernelError(*__Error()); + } + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI scePthreadSemWait(PthreadSem** sem) { + s32 ret = posix_sem_wait(sem); + if (ret != 0) { + return ErrnoToSceKernelError(*__Error()); + } + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI scePthreadSemTrywait(PthreadSem** sem) { + s32 ret = posix_sem_trywait(sem); + if (ret != 0) { + return ErrnoToSceKernelError(*__Error()); + } + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI scePthreadSemTimedwait(PthreadSem** sem, u32 usec) { + OrbisKernelTimespec time{}; + time.tv_sec = usec / 1000000; + time.tv_nsec = (usec % 1000000) * 1000; + + s32 ret = posix_sem_timedwait(sem, &time); + if (ret != 0) { + return ErrnoToSceKernelError(*__Error()); + } + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI scePthreadSemPost(PthreadSem** sem) { + s32 ret = posix_sem_post(sem); + if (ret != 0) { + return ErrnoToSceKernelError(*__Error()); + } + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI scePthreadSemGetvalue(PthreadSem** sem, int* sval) { + s32 ret = posix_sem_getvalue(sem, sval); + if (ret != 0) { + return ErrnoToSceKernelError(*__Error()); + } + + return ORBIS_OK; +} + void RegisterSemaphore(Core::Loader::SymbolsResolver* sym) { // Orbis LIB_FUNCTION("188x57JYp0g", "libkernel", 1, "libkernel", 1, 1, sceKernelCreateSema); @@ -328,12 +404,20 @@ void RegisterSemaphore(Core::Loader::SymbolsResolver* sym) { // Posix LIB_FUNCTION("pDuPEf3m4fI", "libScePosix", 1, "libkernel", 1, 1, posix_sem_init); + LIB_FUNCTION("cDW233RAwWo", "libScePosix", 1, "libkernel", 1, 1, posix_sem_destroy); LIB_FUNCTION("YCV5dGGBcCo", "libScePosix", 1, "libkernel", 1, 1, posix_sem_wait); LIB_FUNCTION("WBWzsRifCEA", "libScePosix", 1, "libkernel", 1, 1, posix_sem_trywait); LIB_FUNCTION("w5IHyvahg-o", "libScePosix", 1, "libkernel", 1, 1, posix_sem_timedwait); LIB_FUNCTION("IKP8typ0QUk", "libScePosix", 1, "libkernel", 1, 1, posix_sem_post); - LIB_FUNCTION("cDW233RAwWo", "libScePosix", 1, "libkernel", 1, 1, posix_sem_destroy); LIB_FUNCTION("Bq+LRV-N6Hk", "libScePosix", 1, "libkernel", 1, 1, posix_sem_getvalue); + + LIB_FUNCTION("GEnUkDZoUwY", "libkernel", 1, "libkernel", 1, 1, scePthreadSemInit); + LIB_FUNCTION("Vwc+L05e6oE", "libkernel", 1, "libkernel", 1, 1, scePthreadSemDestroy); + LIB_FUNCTION("C36iRE0F5sE", "libkernel", 1, "libkernel", 1, 1, scePthreadSemWait); + LIB_FUNCTION("H2a+IN9TP0E", "libkernel", 1, "libkernel", 1, 1, scePthreadSemTrywait); + LIB_FUNCTION("fjN6NQHhK8k", "libkernel", 1, "libkernel", 1, 1, scePthreadSemTimedwait); + LIB_FUNCTION("aishVAiFaYM", "libkernel", 1, "libkernel", 1, 1, scePthreadSemPost); + LIB_FUNCTION("DjpBvGlaWbQ", "libkernel", 1, "libkernel", 1, 1, scePthreadSemGetvalue); } } // namespace Libraries::Kernel diff --git a/src/core/libraries/save_data/save_backup.cpp b/src/core/libraries/save_data/save_backup.cpp index 3f7969d69..5261cdb11 100644 --- a/src/core/libraries/save_data/save_backup.cpp +++ b/src/core/libraries/save_data/save_backup.cpp @@ -79,7 +79,7 @@ static void backup(const std::filesystem::path& dir_name) { } static void BackupThreadBody() { - Common::SetCurrentThreadName("shadPS4:SaveData_BackupThread"); + Common::SetCurrentThreadName("shadPS4:SaveData:BackupThread"); while (g_backup_status != WorkerStatus::Stopping) { g_backup_status = WorkerStatus::Waiting; diff --git a/src/core/libraries/save_data/save_memory.cpp b/src/core/libraries/save_data/save_memory.cpp index e9ef53761..84179bc27 100644 --- a/src/core/libraries/save_data/save_memory.cpp +++ b/src/core/libraries/save_data/save_memory.cpp @@ -66,7 +66,7 @@ static void SaveFileSafe(void* buf, size_t count, const std::filesystem::path& p } [[noreturn]] void SaveThreadLoop() { - Common::SetCurrentThreadName("shadPS4:SaveData_SaveDataMemoryThread"); + Common::SetCurrentThreadName("shadPS4:SaveData:SaveDataMemoryThread"); std::mutex mtx; while (true) { { diff --git a/src/core/linker.h b/src/core/linker.h index 3a1aeb960..d6b5d648a 100644 --- a/src/core/linker.h +++ b/src/core/linker.h @@ -85,6 +85,15 @@ public: return m_modules.at(index).get(); } + u32 FindByName(const std::filesystem::path& name) const { + for (u32 i = 0; i < m_modules.size(); i++) { + if (name == m_modules[i]->file) { + return i; + } + } + return -1; + } + u32 MaxTlsIndex() const { return max_tls_index; } diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 3e1cd441f..82e4b7ad3 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -375,12 +375,12 @@ void MemoryManager::PoolDecommit(VAddr virtual_addr, size_t size) { TRACK_FREE(virtual_addr, "VMEM"); } -void MemoryManager::UnmapMemory(VAddr virtual_addr, size_t size) { +s32 MemoryManager::UnmapMemory(VAddr virtual_addr, size_t size) { std::scoped_lock lk{mutex}; - UnmapMemoryImpl(virtual_addr, size); + return UnmapMemoryImpl(virtual_addr, size); } -void MemoryManager::UnmapMemoryImpl(VAddr virtual_addr, size_t size) { +s32 MemoryManager::UnmapMemoryImpl(VAddr virtual_addr, size_t size) { const auto it = FindVMA(virtual_addr); const auto& vma_base = it->second; ASSERT_MSG(vma_base.Contains(virtual_addr, size), @@ -415,6 +415,8 @@ void MemoryManager::UnmapMemoryImpl(VAddr virtual_addr, size_t size) { impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + size, phys_base, is_exec, has_backing, readonly_file); TRACK_FREE(virtual_addr, "VMEM"); + + return ORBIS_OK; } int MemoryManager::QueryProtection(VAddr addr, void** start, void** end, u32* prot) { diff --git a/src/core/memory.h b/src/core/memory.h index 2efa02763..364609451 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -192,7 +192,7 @@ public: void PoolDecommit(VAddr virtual_addr, size_t size); - void UnmapMemory(VAddr virtual_addr, size_t size); + s32 UnmapMemory(VAddr virtual_addr, size_t size); int QueryProtection(VAddr addr, void** start, void** end, u32* prot); @@ -250,7 +250,7 @@ private: DMemHandle Split(DMemHandle dmem_handle, size_t offset_in_area); - void UnmapMemoryImpl(VAddr virtual_addr, size_t size); + s32 UnmapMemoryImpl(VAddr virtual_addr, size_t size); private: AddressSpace impl; diff --git a/src/imgui/renderer/texture_manager.cpp b/src/imgui/renderer/texture_manager.cpp index dd233ee60..f13c995be 100644 --- a/src/imgui/renderer/texture_manager.cpp +++ b/src/imgui/renderer/texture_manager.cpp @@ -9,6 +9,7 @@ #include "common/io_file.h" #include "common/polyfill_thread.h" #include "common/stb.h" +#include "common/thread.h" #include "imgui_impl_vulkan.h" #include "texture_manager.h" @@ -81,6 +82,7 @@ RefCountedTexture::~RefCountedTexture() { } } } + RefCountedTexture::Image RefCountedTexture::GetTexture() const { if (inner == nullptr) { return {}; @@ -91,6 +93,7 @@ RefCountedTexture::Image RefCountedTexture::GetTexture() const { .height = inner->height, }; } + RefCountedTexture::operator bool() const { return inner != nullptr && inner->texture_id != nullptr; } @@ -130,6 +133,7 @@ Inner::~Inner() { } void WorkerLoop() { + Common::SetCurrentThreadName("shadPS4:ImGuiTextureManager"); std::mutex mtx; while (g_is_worker_running) { std::unique_lock lk{mtx}; From f938829f12eb81aa2ae3eeb0c7d2dca43984e718 Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Sun, 8 Dec 2024 14:04:33 -0600 Subject: [PATCH 135/549] Implement sceGnmDingDongForWorkload (#1707) Seen in Final Fantasy XV. --- src/core/libraries/gnmdriver/gnmdriver.cpp | 6 +++--- src/core/libraries/gnmdriver/gnmdriver.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index 4e2db9083..18035e6ce 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -550,9 +550,9 @@ void PS4_SYSV_ABI sceGnmDingDong(u32 gnm_vqid, u32 next_offs_dw) { *asc_queue.read_addr %= asc_queue.ring_size_dw * 4; } -int PS4_SYSV_ABI sceGnmDingDongForWorkload() { - LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); - return ORBIS_OK; +void PS4_SYSV_ABI sceGnmDingDongForWorkload(u32 gnm_vqid, u32 next_offs_dw, u64 workload_id) { + LOG_DEBUG(Lib_GnmDriver, "called, redirecting to sceGnmDingDong"); + sceGnmDingDong(gnm_vqid, next_offs_dw); } int PS4_SYSV_ABI sceGnmDisableMipStatsReport() { diff --git a/src/core/libraries/gnmdriver/gnmdriver.h b/src/core/libraries/gnmdriver/gnmdriver.h index 5307b3baa..017dbe3ad 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.h +++ b/src/core/libraries/gnmdriver/gnmdriver.h @@ -34,7 +34,7 @@ int PS4_SYSV_ABI sceGnmDebugHardwareStatus(); s32 PS4_SYSV_ABI sceGnmDeleteEqEvent(SceKernelEqueue eq, u64 id); int PS4_SYSV_ABI sceGnmDestroyWorkloadStream(); void PS4_SYSV_ABI sceGnmDingDong(u32 gnm_vqid, u32 next_offs_dw); -int PS4_SYSV_ABI sceGnmDingDongForWorkload(); +void PS4_SYSV_ABI sceGnmDingDongForWorkload(u32 gnm_vqid, u32 next_offs_dw, u64 workload_id); int PS4_SYSV_ABI sceGnmDisableMipStatsReport(); s32 PS4_SYSV_ABI sceGnmDispatchDirect(u32* cmdbuf, u32 size, u32 threads_x, u32 threads_y, u32 threads_z, u32 flags); From f347d3df1897af39f393c7bbfe38b8152fcef58e Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sun, 8 Dec 2024 12:53:29 -0800 Subject: [PATCH 136/549] image_view: Correct view format for D16Unorm images as well. (#1708) --- src/video_core/texture_cache/image_view.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/video_core/texture_cache/image_view.cpp b/src/video_core/texture_cache/image_view.cpp index 57a58c714..61f1aaafe 100644 --- a/src/video_core/texture_cache/image_view.cpp +++ b/src/video_core/texture_cache/image_view.cpp @@ -161,11 +161,12 @@ ImageView::ImageView(const Vulkan::Instance& instance, const ImageViewInfo& info if (!info.is_storage) { usage_ci.usage &= ~vk::ImageUsageFlagBits::eStorage; } - // When sampling D32 texture from shader, the T# specifies R32 Float format so adjust it. + // When sampling D32/D16 texture from shader, the T# specifies R32/R16 format so adjust it. vk::Format format = info.format; vk::ImageAspectFlags aspect = image.aspect_mask; if (image.aspect_mask & vk::ImageAspectFlagBits::eDepth && - (format == vk::Format::eR32Sfloat || format == vk::Format::eD32Sfloat)) { + (format == vk::Format::eR32Sfloat || format == vk::Format::eD32Sfloat || + format == vk::Format::eR16Unorm || format == vk::Format::eD16Unorm)) { format = image.info.pixel_format; aspect = vk::ImageAspectFlagBits::eDepth; } From 0b59ebb22fd64bd4c60d2fe409966900b617428e Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Mon, 9 Dec 2024 03:12:33 -0800 Subject: [PATCH 137/549] shader_recompiler: Implement S_ABS_I32 (#1713) --- src/shader_recompiler/frontend/translate/scalar_alu.cpp | 8 ++++++++ src/shader_recompiler/frontend/translate/translate.h | 1 + 2 files changed, 9 insertions(+) diff --git a/src/shader_recompiler/frontend/translate/scalar_alu.cpp b/src/shader_recompiler/frontend/translate/scalar_alu.cpp index c320e00a7..5b411d83e 100644 --- a/src/shader_recompiler/frontend/translate/scalar_alu.cpp +++ b/src/shader_recompiler/frontend/translate/scalar_alu.cpp @@ -102,6 +102,8 @@ void Translator::EmitScalarAlu(const GcnInst& inst) { return S_SAVEEXEC_B64(NegateMode::None, false, inst); case Opcode::S_ORN2_SAVEEXEC_B64: return S_SAVEEXEC_B64(NegateMode::Src1, true, inst); + case Opcode::S_ABS_I32: + return S_ABS_I32(inst); default: LogMissingOpcode(inst); } @@ -620,6 +622,12 @@ void Translator::S_SAVEEXEC_B64(NegateMode negate, bool is_or, const GcnInst& in ir.SetScc(result); } +void Translator::S_ABS_I32(const GcnInst& inst) { + const auto result = ir.IAbs(GetSrc(inst.src[0])); + SetDst(inst.dst[0], result); + ir.SetScc(ir.INotEqual(result, ir.Imm32(0))); +} + // SOPC void Translator::S_CMP(ConditionOp cond, bool is_signed, const GcnInst& inst) { diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index 00cdd8d55..43f3ccef2 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -113,6 +113,7 @@ public: void S_FF1_I32_B32(const GcnInst& inst); void S_GETPC_B64(u32 pc, const GcnInst& inst); void S_SAVEEXEC_B64(NegateMode negate, bool is_or, const GcnInst& inst); + void S_ABS_I32(const GcnInst& inst); // SOPC void S_CMP(ConditionOp cond, bool is_signed, const GcnInst& inst); From 07f451650f75b01a90e48bf7d979c47da1aec26e Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Mon, 9 Dec 2024 13:47:26 -0300 Subject: [PATCH 138/549] Help - improvement (#1522) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Help - improvement * Adding shadow below icons * Adding keys icon + Update changelog * color according to the selected theme * submenu 'Keys and Shortcuts' * clang * + * remove keys_shortcuts --------- Co-authored-by: ¥IGA <164882787+Xphalnos@users.noreply.github.com> --- CMakeLists.txt | 2 +- REUSE.toml | 7 +- documents/{changelog.txt => changelog.md} | 44 ++++++ src/images/discord.png | Bin 0 -> 68549 bytes src/images/github.png | Bin 0 -> 116428 bytes src/images/ko-fi.png | Bin 0 -> 39500 bytes src/images/website.png | Bin 0 -> 90853 bytes src/images/youtube.png | Bin 0 -> 39404 bytes src/qt_gui/about_dialog.cpp | 181 ++++++++++++++++++++++ src/qt_gui/about_dialog.h | 17 +- src/qt_gui/about_dialog.ui | 145 +++++++++++++++-- src/shadps4.qrc | 5 + 12 files changed, 388 insertions(+), 13 deletions(-) rename documents/{changelog.txt => changelog.md} (50%) create mode 100644 src/images/discord.png create mode 100644 src/images/github.png create mode 100644 src/images/ko-fi.png create mode 100644 src/images/website.png create mode 100644 src/images/youtube.png diff --git a/CMakeLists.txt b/CMakeLists.txt index 16b49cf08..7de79a43d 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1014,4 +1014,4 @@ if (ENABLE_QT_GUI AND CMAKE_SYSTEM_NAME STREQUAL "Linux") install(FILES "dist/net.shadps4.shadPS4.metainfo.xml" DESTINATION "share/metainfo") install(FILES ".github/shadps4.png" DESTINATION "share/icons/hicolor/512x512/apps" RENAME "net.shadps4.shadPS4.png") install(FILES "src/images/net.shadps4.shadPS4.svg" DESTINATION "share/icons/hicolor/scalable/apps") -endif() +endif() \ No newline at end of file diff --git a/REUSE.toml b/REUSE.toml index 5bd21bead..747679c8b 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -12,12 +12,13 @@ path = [ "dist/net.shadps4.shadPS4_metadata.pot", "dist/net.shadps4.shadPS4.metainfo.xml", "dist/net.shadps4.shadPS4.releases.xml", - "documents/changelog.txt", + "documents/changelog.md", "documents/Quickstart/2.png", "documents/Screenshots/*", "scripts/ps4_names.txt", "src/images/about_icon.png", "src/images/controller_icon.png", + "src/images/discord.png", "src/images/dump_icon.png", "src/images/exit_icon.png", "src/images/file_icon.png", @@ -28,8 +29,10 @@ path = [ "src/images/flag_us.png", "src/images/flag_world.png", "src/images/folder_icon.png", + "src/images/github.png", "src/images/grid_icon.png", "src/images/iconsize_icon.png", + "src/images/ko-fi.png", "src/images/list_icon.png", "src/images/list_mode_icon.png", "src/images/pause_icon.png", @@ -43,6 +46,8 @@ path = [ "src/images/net.shadps4.shadPS4.svg", "src/images/themes_icon.png", "src/images/update_icon.png", + "src/images/youtube.png", + "src/images/website.png", "src/shadps4.qrc", "src/shadps4.rc", ] diff --git a/documents/changelog.txt b/documents/changelog.md similarity index 50% rename from documents/changelog.txt rename to documents/changelog.md index 6df09472d..766e1a09f 100644 --- a/documents/changelog.txt +++ b/documents/changelog.md @@ -1,3 +1,47 @@ +v0.4.0 31/10/2024 - codename divicius +================= + +- Shader recompiler fixes +- Emulated support for cpus that doesn't have SSE4.2a (intel cpus) +- Frame graph + Precise 60 fps timing +- Save data: fix nullptr & concurrent file write +- Auto Update +- Error dialog implementation +- Swapchain recreation and window resizing +- Add playback of background/title music in game list +- Kernel: Quiet sceKernelWaitEventFlag error log on timeout +- Improve keyboard navigation in game list +- core/memory: Pooled memory implementation +- Fix PKG loading +- replace trophy xml assert with error +- Refactor audio handling with range checks, buffer threshold, and lock +- audio_core: Fix return value types and shift some error handling to library +- Devtools: PM4 Explorer +- Initial support of Geometry shaders +- Working touchpad support +- net: Stub sceNetErrnoLoc +- Add support to click touchpad using back button on non PS4/5 controllers +- Multiple Install Folders +- Using a more standard data directory for linux +- video_core: Implement sceGnmInsertPushColorMarker +- ime_dialog: Initial implementation +- Network libs fixes +- Use GetSystemTimePreciseAsFileTime to fix fps timing issues +- Added adaptive mutex initializer +- Small Np + trophy fixes +- Separate Updates from Game Folder +- Minor Fixes for Separate Update Folder +- AvPlayer: Do not align w/h to 16 with vdec2 +- Improve sceSystemServiceReceiveEvent stub +- renderer_vulkan: Commize and adjust buffer bindings +- Add poll interval to libScePad +- Add more surface format mappings. +- vulkan: Report only missing format feature flags. +- IME implementation +- Videodec2 implementation +- path_util: Make sure macOS has current directory set and clean up path code. +- Load LLE modules from sys_modules/GAMEID folder + v0.3.0 23/09/2024 - codename broamic ================= diff --git a/src/images/discord.png b/src/images/discord.png new file mode 100644 index 0000000000000000000000000000000000000000..2fa455fd10a5cd8ae4df56f2f35e642b4c0d8b38 GIT binary patch literal 68549 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 z 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 literal 0 HcmV?d00001 diff --git a/src/images/ko-fi.png b/src/images/ko-fi.png new file mode 100644 index 0000000000000000000000000000000000000000..d19991b5f3a895e1e3ee4b23d08ec2716024fd9d GIT binary patch literal 39500 zcmeFa2Ut`|w=PT*n;>0-G-c_r1y|rqEcU3cyy4tD-NDh+V;NTok zS5wl%!GXkKf1rfmPEfT(JNSdxN$rd)4h|^|_6LG%o1Z`>mbW9UtY}tv$c}tI4!Ovq~)ZDIcKZp%PW zdGR~Aa()x?Lyi*0742f{{fSvtDA$#QUD2ip65pVtMay@4EDzhehb6!bzl z2?`4c3I48=(@$^rw@%;z=p_2vPFCo>ft=i3?7z;(3N47S$6UZTxVeIU!oTeY*1&$t-35&S&42squ5QX6VDY~z(>??#dw})$tth8+j&6>w z=N$hyr*p) ztQD*q_q6E{<02Xp9RIYpm>Hy1?crI{?dwff-_4usZ-4D*^035~89A!N2wR zNs<*>+S<|O0?G|(djVyG5p;5}K?weJ^Y1O)Y~AcJKP?bgrmyzyxV@X;Pq)7c__Zwp z2t{Mp($VForavU;W1Rnf_s0W!Tcn09_MNcy5B%k?7s9sqdiU>rzYhM-fv|c2%#XCT zE|{e&Quymq|8(Ooxj$N}pyL(C zZ~Y~952~Nu{3lUAz}B?~8(bT80W8g5eX*~EUG~2(Fm_Vk9{_z&^hP=SUO9ehqy-=Y z9`=9P?hMA>-qG`a*-XJ61R}q0_3iH8Uw99K-%j!e0{WgPH}pAG7e{xe{~`waRD`dv z!LY-rO50$E!NQGH!dRo+?cHGChKGF}|LY6?+~{vh@((#!^+SF$d*3^Kz4L3Q{{~?H zf(dpGT3XV27zZm`Y;2@xk8*WII=MJHT1x{v_x1Jv`TVc<`QBg25$%o*S=5z~K%jt? zt(7!fT*OLPL`;fbR1A&cx0VtU=7)=+toSiPR^lj8AyElQVeud0f9n1Z(drJaZYT#d z=DTQHtY|SyF;SE#h984MiSvtzi(&Xt!f*^f8YL=ag+Ym-gsd%pi2kYjKSb-e*a9Pu zvj1s#cXuFPNZB`hq35=H-z{~Hyvqll=0(9aR% zH@ScB`0oRt&!HS_Fjh#xA9MS0Te_`)| zh!OaGH;^JAt+BQp<>X{K~Ky!!`W>skYzp*z-;pkTiZn{Qpt?+;7x`b-Di~v%1Gyz@^1R zrA6RBc#HqGe*PPq@=f6XOTO@bu9$zRAJ&HcM5_Fs)J3ar!TgU6@!fg+YOMcqz(4Jn zJw#w%-NpZv)B7JspTbfiViMLUG(TKQ!irx)0wgF>!V(yMYe{iSQ4wJglms^K`?i9A zwi-4N`M)6S++RlhKUuV2c-?QJ{u?R7{}uM}U#TfTmHabZMC=s-q!k_Q9bEvQjX`30 zLfU@~}hWeLc{TC^j-?78L5rzS&2wDOz#E%ve z7U35Yv&QgCS)#@HMWis+mX;D$;9BbUA?vrhe}Cbh(fCi(MPQ2}(&|`#{j{x>8{k(# zW{VK~_3lqF{`E)`<6r}Nh+(tef871Ghk~t}t1iYxA9ca$w>{;WOH+%YIYkubqB<^4p$g{C+3ugxF0_Mq6$#UHr#gVJ7`KXC0qv7d`SaP0@B zy*7W~+Jj<07k}W|4@!G&{=l^d#eOdSz_lNg_S*b`YY&S3T>OD+KPc_B`2*J;6#KdO z1J`~~+H3O%u01IBbMXhR{h+kh<_}zZQ0(X84_x~}X|K&6xb~pf&&40O_Jh)1n?G>v zL9w5UKXB~_rM)(P;M#*?KNo-C+7C*5ZT`Tu2gQCa{=l^#l=j;Efol(n{apNkYd;tyQ=L20keAGr3Q*w4iu zxb}n6UYkE~?Lo1hi$8Gf2c^9>f8g4KVm}xE5iXLye#0L4)fBQEp5XWA1?^JY2ETg` zhE~(l!ol(32EV=U5)RJiUGV2L4vxDJ4$eml931KEI5-TB3FZwdI5;O{)Rm4Kc=dm( z^ma2?-QL}`5P5r8<_vKNxBS&pNAE@$+2u*hF$bL@k;T<6=DojX!PAPK(a zu9L{g$LosFKuV|B0Mja!YBr)&V*DZ3X?bX~oB6BY%L1{QumfuDyF7Vqo%3IO%esl( zaHwt#`M(>U=p+g@gpsk}oE>zXo%?LP?)nr`h&%r*gK7J1yg=+!M(Bdr@f-49sZ&~V zGfo2%m*P@{H7*ZU3_jnQAg)ok!-vhG~?Pb6!NM^!d+DaR?9%%@53VWPXX* z?Zp*))&${!znJrlj9lQ_w!$3^wi9ZQ&5~TZwOIw_C5Q^%k$>X*Ui!GY$3>y$?UaDI zSDW$aA;}Mxg>;ENxz|#znyi&iuHhb->w-af zpUV%;*(D-q*&$Q`W$AipI5}`Z%b;bBuu|1Xn)th(@jNAI^e3s_jw@xe_Tk%m#PS=l z-ll6S36V;zU;twwjtBCQiQ_Xij#en=ZMss0(+sqL!J6%56?(1w40t`HP;W^-xtU9r zO+Jx%P6c{c9%kg$W+W+6T8j`cQ(>hKhziS5dPk$xG2f(MvZ^gF0iU;eN|StH&X=7F zmUTK}O+QDba7@$U^0L5*!ED+IzKRaWWXx+Bog6|Vf{N(S@+?+OQ4)7z;t06b#TwmP zxQx^IXjoD;ZyohW-bM-a(fG(DJV~j-oTNJM#PdA7^O+w7z-)(dUV78u6`M3VS-3Gc zDx&f>Ry5bp?Dw^5&fjYFKC)_BK@Y|sldHHq)fao*==3^1pXRBk2^o?i{L+ZPv&CF~ zVvY|XM#L54h--LrU5EA6<&`WGW4WuFAT}16dCxlrnmw{IXSjqTJz6ED3v(J}CEyg8 zv}L0Zd?hYQWh1j}THeV8p;{3bkw(Z&Kc(rkn^_Z*?`>6wH}^bh^#0j`n;L5JN-WfA z3dT@P+z!_)VQ8CsPMcB|^CLL>+shLtl5iYRQi)T#YG5`<9wt(CEu2Qn%)HT#W!@U; z#!&dFTB8R!gmwl6&gm_Be4lP3BJq_r;vg(@By2>QZnY;zDyU+X7-ZESkp@U6ah|!j znyjRZ$=Bq9Pz@hGq~S+_hr~&Z4$TX*oOr6RLN`!c(Oh0*IdXyL@MiHsa$FFU;^gJf zybfnp)zD@6hwm>$Fm{dNqqCP!7)o_oF`X#YkXLF~Z**ar=rf8h8OnZ598PJ*F0FuJ z3a>MMGna{dn-%sOMt47IS{!vkCcWdWZFLG=IC`M3Dq%fHGAPvO`Z4L1lK=ui5qwg8 zt$OLk?98Jv^i%;^O!?hsDGCUZ@OC{|Bf*|w<%-~LRCR(&At5u07HhX>|AeJ(_q0+I!TC%6NvjCNZIp&6*l-f4UY2hFNcMHfto3 zLi9P^MUYcXKC(PzJa0+7-aFJwctZu3LtB$R{Mfz2<>B<=kxJQ2`Tb|f_-cl-+*v-vjjd$=QUA=b8!X~#$I4jN!v#8 z!6N@E#jP%0>NtE(f|NL4VfqJU-QI^9e6&K~0!(xMB7_6xmuTXx?cJi{g6g+(^GJhI zg7SyfyKbEFkqv<_${s80wZ7#`i( zQnQ0fH1aSFr1nK{_lM$h2?(t0^wHPY1q}pIJR>#JcL{~dUX*8*Aay8R)|O6<;FaWn zEXtyTm!=>HcK*&rv;ti#FBK#)T?z1VIgXbhj?Po!d*D2DRlyI z%b@v1Wh8ba?TKeXB8b@;h5TR$wkeaF zS7qlXR$~iDI8ygT+M(XCoj!BPgndg%T6YAwaT`^xN`A}xEOne27ajAn9pOoL1Ndp9 zG4_yZs0BLYM3KlQhdpdu!rm2Z78z@8Q? zZK_1e98D|6=-Qj`;`iPU>lGtP+3O;kC=Z-<7Fkeu17{{7>1sx64jtUVQ zoOGnNba2NUraq1mHF@n-nic}1z$Q$M7vJLHkO=dHVn4Mi-|DBR+f^SK;)C3b5e`j< zr^uCKf)%((j`MX>uBsdinyonwwt)+WNb&HMl;XH+VBC660#Y;v{UThw<}i%!m74q& zWhN&n!#lpguIN{~=v$PLK7pD|M+Jy1CMwI*R5`d{;ZV5Fxb%pCeUt+|J;gOIX#DjR z*YzN`g3EzIJ?Bb?BGqbjR<8wJ_qyEpMaJ4`? zQ5<>~LrV@8ITSgj_P?tSqidZKn9)ISn4i}^aaz}cx)BvpI)N@}A zsvajl98w!@r8<-zZe`6RdT1iqQhZ1O+LgF|-Lk;O$~-GVjRUH7XBpwpxR$Je2%)Sz zE@7B?EowsP3Dx9iffqv@pEs*Q1d=|UgNLK*V?ilhycc~OnLT4xy&{x~RELWozrFu> zYSKnyeo&4}slmF4eOL@7u+UwpY6;6qPiVqlge~&Ij@00jOA?;|@{6hC&t<5|(sxAV z7xOG0hCwV$7#11t*%?8p6kUo_)#UiDKE4D$Si|Uu>vEX>Nf{Gw@8e@33pn2LN9b$& zKOm@xBbpdP?%%2;V9c~LgpLYR9eJ)`#2lG;+hCCcrd)GCTfTAXES-W^oZtF64-OZ3 zTxws1nm09p=yKUdor=)WQzSz~!$UQMe#4w&o)C3{2N2|y7#oU@UuYG)t_NrwVMKU+HkosuFFSS z3-hfjk(n(^~gmC78IqpSDh5B+L>e{HVi$;w3 z^sx`>S04sluBTTt4dYIJJ;NTNM%z8)UwFVM+&y1IUOR!>LfYxj6UGxjMb)@)<>~1= zq;U+!pYKo#lBkg^P!+sW9Df^18L1h_!)08wL#d-Rw|X-u2UkDXuGsoHi>aJK1pc}b zu91`@XHVW76P5bX^~^9!y3=gWj}~Q3*egKeBa~#ims6bzh}P8QcM>^9sO?+l;04sj zM~|^9alE34Zhv1oziu(gr*n19M_s;y$l7vKR^+`ml)avvDpDZxMAKTHE?BIT2adrSkltgmzp6NoMgLrSUO z4EJx-DMZl36R^}vzxFJfr_qxqlv&&mlDMbj4U_@C=r7Yi+(c*zcM?v*P52ZtE@44- zuasILA~BQgiQiFX<4eZMmwgm1xnX%V1U1)cxNOMSLnwI!IlPT7jcQxIwqQI4InqEq znQVFBQXa1#cL?PWd&Fuu3r-L2fk+taOkZijOb~@dq+4V-rGi^V+%n>c<$!QL%ZN&MYBrn=Hw-Ln} zx*A@XeqlM-(@?7QbGIlY8P{$HUA15)JeJ0jwfYFy5>YO+zAQOTTk!TG7fh;#sMz~7 znu`@Y$W}V`(22F#@Cz(+_%yDIgwruY|7&-8&qCRKLfZv69zJ^-c*fi<&;~MIeBjbE zAOBFwtls9j4TGn{x_*Sg z*HNULH*CAe`Z)z;;+z3?a*uCa-MqZaJ9okeNa>r6=6T)?Mfc(iOpmTU ztz?I77pKQkIF*WaGa#0lIZXmSPh7ovm69YBicjlJQ%jn2=T5@SnV7J=L2mz4YS z`r8(xdHT6aZwdphEqBW8`ZL_6tj!ocp{R(ry1Hurt}2b+_(_q|s6x8mQcH+9u@Hq9 zzL6${_RQ?`(E|3Gi5g{jD&*aYvYjt8pLf=)J3mim=$t=)Z`fa9Bh^AkO+|&Up`n3{ z4Vg+zbJ*!Q0fW<9OtP?LJ+!~S|L* zwifDAog0~adWor;LKN5uDOlU3CR6K9oi=R%Kg!O|!qN2nCqiD$hbxD3)DW&Oqz2LA zu{~c)W$+%#qNAhRS_qf3dVTk#k0)_F)UzvKTTNU0@UdgjnTp{e4ukLq)jK{N(ldNu z3HqM7+8{Cl9?~QnXeCJ`To&N1N#iP%ZSjVTQ zKk$sFyqJ~6aai+u&C2vECh_yMqznRFEaXiCws$PAJuMGfQF7XWrerFUwV+&Jpk5TP-auG0OUgUAuOTj1_Uc*UXncUAvg=iJU)05iWf8x~UEr zxkn(7sV8{O*19wO1sJBKx%s6G=x)G{)pq?mnxnO(f;q*-@%QfCE0vNhxKTvdS8waU z|0z=Y*=XOL=a~UT!;frV@g66CGqePanGaz=%Z9vRG&vDdc@!SEnaQBkF%Va!I4&QXclbX*R9)mnEr^lHYF=JY!o zS8>t7&o^$_I<;RHzG=rDG9{6hm#29{B$SF%Wg(ctFUWVkvbVRaZ{pG}bB&>PF~^J> zB2LWSzQw!m!|_g0w<%rs6jFBW8vsS*Vx&&dxh{^wI_aM#^^dr%&$&v)Jc)iq7q?i$ zv|)9|KwF!&6Nwrj-%Og=kK3PUHBFz|qY0gy)2tcmC3zXAA zEQYXA$PEqEbs|rxju;SF!RY0fRpvj@PSZ(COS?6Yvin?ZZ`X9F`fM$i^o4LpcFV*< z*PNsoRxqxA7*Q0Z;ylgh+t5F77BSUSrwkP z<}Ua3JHm4&h7R5mD9Xq+@e zJE*Br&L$8-IYtX~du* zF~9II4^LBccyi3OC31MY3cmIjQChIpuvS+acmY{OZ*Qqs*|qBo6#Q?7mXD`ksw zr|YNk4U65T^~hP^@+vBgw%ZjzlM@mX!LEk^OE)-Z0!#)kFE30|QgZH7a$4F8Q_rD> z3h!lv=efS&T#k_;eX6(s`CxrmXkiHD?s0v68o`PMg+lI-w$@fgQEM``jT$2O=BJV) z{QTU4g0W!ZEiI2{=UcR1JpwFSo1O0){UR2%s#g~~=1A1&lhlqJPXS?d(}KKN;P$H7 z=4W?z_t$UURL;|U$x%-%ohzFueV?F#6>Mv3i;oL=*I$&A6O!Us_0|qVO>^6~`wqr# zt8S_`J@l!`>x7mGGblsjy&gS%x;{N`{LqS+0N31&0&-l zD^p#AUgI%lGLIlea%#j)Qzs}`n*5of8frvV>8TW0Wh*X06{qfULNCCEY*OuR-zHB%&B*o7N@N{!6cW+!HC!ehc&=^fxiLcjX_v&P1@ zj*hwa@6WzWk)woCLh-SFNJ{F4h0xSy>C$3^633&*kGpz$RD6A9h$vWdDl5~}xD-@X zo7l^hX5$hPsC(DordblpxUQ2On7P4cezSJw=nw5T%+jS++1`Kn;IjJWv}oXtpWq?G zKt76u?n4k_)LT#YjLkR@)Eqo`@SLqJYIkS*(@=SGnvCZ|&v`STE>3Dnz(SD^@g^oF zs_W?R@bky4thl+jxHPx6a`5nkzkY4d-Q5ihRmjQBO}u&YBeRT{q~xo?K|eM5_4#)? zAUxPypO>?WPD-K&u6$~0%F7%fCo79p+lM73)9KZ)a$(>}vG2Nr$83MY+)(-3Vl>gfQ^(xQ%+V0Jl7fQE z!FWWhU!*~dq7riuSTzD}Zf^WbXXD3}0p@k7Qmz|yY&$i#lg&<8-&uFGK{s8QNX*#c zqCjrkI!%7$mF;n2T~`s@gvf@9FqZ@kWu;G{*V_;w)*2r5oS5i2N&B&}?kp7e@QN9vlIYF)U%FD@=_aOzYs zsxC~?(uZRd!Ftk{&#Yo2u30-BaRY=`LLjK=KrCxEUOB|<${dmHT6aKlp`J#^Y59eu z?~ZtDd%G3bwr0{f2Dt=^L7qW)Jk)W1c93D`$J1lucxc~GS^HUjpi)(n6=#gvdVY%QN+_>pJ+^5 z(&~BWt;dhAfsnSy;yEr*s?xEjW7wS!pRZVEgge~|slS8`jDYgYRV-_FdOo5!C>F7_ z|c~Xx4tLu%hd1F+W_HlamvdlA>W~XqiKia`UFw=ZR$h zu5e+;VMG}Dp%co=p}EUmX=!Oe~e5zdUF7Q}t%y=-mf zx2XJeuxRtGi$mp}O(yL<4@}#D4el$kp#UZv>=+kU*XiEENlDmzU-2d7X!^zSciUIk zb~l=~Rtp1To*lpe@#Z1J0;A)`#>{8VoB>|+ggl~g@R=)#jD@`GZ)3NLEQhf&+mcx?;7;?+V{XL^C{WMTDNIG)6B{NWkeq{R9~ zrpa+$T4{rZH~I>XQFVl70iQ9ffm_Z2n*;BB6yRkK!Cs-~Gs2Mv`|a7YXE{0b^Sn~k z#84xb?W|}4%f2F1I$azHu`~oL-`}3`Ry_m{uS6s`|d-YO_4a0U97{@BjgI3@YPDzu1Y1FqVxjOec8#XlNsTR*zv zD(4G3W>_$?*u*y3rOLMYLfEmdD46b;VcfNAAaPhcGEn*K*$ogt$276}UN+zT(px39 z3$Q7}6iBz?-J0|(gPxSWf+e7!d&)eI-kC7Z6 z832ov`7$me!h_BRgAsNfQxa$MiBM(pzgp6XOc`*j9!ji4u#y(9diLv*eR4`~*=%*w zA2dIKK-Bl#H}?LRm%Gsw8}NR>_1uLE6RsJXEUq8gQ6N3#+1~9R82Aj-`uXn8{O%V$ zL|Ifbh$~;2@fXwQHr7$dfn8^Z@V381c);@G>pWl!5V=(02|lI-TyM59R|JkZ7J>#+OjZAE(87B&N+6cc2o$g4PmU@4jkkxE8EZHmfJ-UyIafleMRi-J&(V%C6`LyG6)pM7~329wB&0@5-x)*Jxrjs<`~NqiZ1jP2xLK%NdKHmYjV3@?}kDz?$d| z7~qiS+4Ee~z^onE`eY-we1fc06+69p^PMGzwYi~u(m}MD+No35CPQRpWKxRJO+H%k zB(W30Txi9T*DB0tkowy7KEQOON)?z@N8?0$0Pkd~Gf zpHWeb-7x{*%Tlf%gdB$}vOg#SjJ;>>KOI{eOcpfn00H5rYr=H3izAmPe3rx)jT+81 z+bWHm!%es}i=*BuZ4+N{XL^P84q*l}`s;ZE4vW2|Mlbtp#3EX1h;?r5{ zP;3Y?(RwZYUNcFk0vQYZ<*{%Yr-LV_#Kz-{tWfL)0cDphRe?{)KV5hw`r(^I)KHmw z#pe$xpgUEhlF?I#q0Ezui`rlx0X?#6x^k#&LAKu5v9jU$B_df@1jyLlPfq%_C~c=2 zS}6mrh(Eq{ypNegS&$}XcNc7~6L*)*s{B4@hH4!<{Xp}st2szBnI#Id^gC-VwpFNE1g3>AKA!M6yNHB*j{RcR^%TTDCBQO*&6lh@IRxO{-F~;b z2>cIMDiLmx+jO`7Sy?`xd7r_DnCM&tW$dgAg(Z6d=%cW zUZr!C$#GGgDYguqJQ_%y20Yeb?bNBr`=RRU>ShBDH!g-2Jakb=nwNu>lhBpgJrXFv^AggF&+Dxvh8&WX1H_WJM0na_OGWgn2Bt`V z9amRT&^(cy+DIOFSpmo*6hv@~udg#v_`nWy;6>gg(O`{;5C(hkQ(v)SNijkJ&T=O^ zTQLkS-Nm9t9!GoaiB$o$23pgyrpkZQATqHAOFe;bwH1#l0QNXK2p?A-Un9q}RigOw zBTfj_fddE92cweXJeyw|BqUrVM-r+VQK+3akY5Vc%RTz=EZeK@ZUrn?$uB$K(TSe*|Rro>c?w> z@ytX|%JG?})8e!iA66h%BU3wZLa!m5l66_WqPwEEt7}vl5^3nmMGzF+f!8Hb%Saq? z6sZJyKft5VJ#$9g%q-pJN-l_t8XHv#ZyYHL5t8f=O@E-lBY}VExK4wyvb~zR`l9B9 z4u@#KmX}T?IUox`LeOhx9rfTb|7CK0gN>sqfMg?%fYFj1xjGHOQeK_!?qOi-B@lvz4{=33Wg4Tycy zeFY<74oWCC@UpedyBvQD#PeO%fq~pz==SzI1Mf^Tc_vklQS3mu2I?I<`>~w~jo{EN_29QfzZhc z=x;iyIf%tio;o#wc}0+Y?1J)T0xJ*&U1kzhX;hOBBV$qk=@vIkcnq6VzFw&3HGb-V z0Zz$G!nFD>5r-wi8J^*+?fNYU;?{kjbzp00q|;LE$C<^4ztcc7HUfFm$LFME+RJR-iH%; zbdrRrL!zQc06jU<@-f3}GP!z-JMpmA(x=5)kU0i!&zM(HQiAB1gYs5IMe1^gj83B( zCDzf09bmpgzJmOa8P1AjkyZ*qMgOXFfg;qhihz~_BqY0>oO$Dv z;#~D^05$;t@vp4!LAD>{dSf!)|H>v zKDjVZF}w@xdKa%J1r<;S84tc>Q;!>$sdBOWfo3}5ZQ={N#b!kc*?NZR4P~86<4n;+VkID+U1FYm}$w4gP}M8GI6+pE2z9-oYuvXX(KkG{3Zm;kiGqSkHWQ>W;3 zWLK}6YYqzRzKy=e3U%r^VrQl2nHlrKUMu7MWm}B8pIG}d~kXxe^q9CB{?077VwbF?7LDvB3_tv+Bp<*~h zT(Ds-KqJKltYiZ}v($%gxMj z!ykva54M6xnI5F8+M0@lS-yTBL^DPowh2TJ#B&3C1NiGpV`G+pxlpyX{$NXtIdOs{ zSUyOXSINUe0^~@bOrxu-D<2RLVA8*M|LO49XgJ&2EB2={5ZlPA?wids&VVF&2H@KO z5pUZi3MB8PjUW&PT?3lYQNmg0dlBSQ;m8$s(9SUZ=W^cMkU;t~_Df zz2xpqFo+OPD6F+(svFn5DHd5iR~8NC*v2?^7ew2Df^)AXX+~O{#$m)Id)YQk(E%SP z!>%A86)|M7Sneekxq9TV*3I_=_h4O{uX}r!L0~x$8_xQir0^ha&EZ>(vY7#tu{30a zQZof5rGR=9TN}#uTPw+!SZWjGk&!WOq*T2PQYjE-Hb|leVmV-_TinEbWS?+JhB%Ly znLlzKZxp`JFJ>Mi0Y3=HelkX(pv#xZ4DX-Cax$w|R-hotPsS2!mxw5mgzJU90htB< z8fH#WO<+V?Z~(^<*AX&M+?TWfdHeRQiO=-yK~N+R2~)i;<$73h$SqOuPE}MNND9bV z{Z~%PWhhWWfhU1A$jYV1feagB%_W$0gZpYzNR2DJw3j?|Xz^>>2k`iNr>N-tt*L!j92Je8t{C*JMu zNM`J;dE~KD`xDV-p3e^;fl&+x0tT z5;^|RW#U8?$OD?{^28vzVZKbc|yU!h-ekJ$T;BL~noVac(o>^@5A~PhY&(}*jq@&-K*totB?`>wR z)C4p$=v&z8-k=`YG#)G0A=-(cp~!tj{AhGdjiBh^5UvF!P(?dX;sJJk43Jnk2M_qh z1HEST8i4>E;bt8x1)=6Luy;!}cog+*N8BxlSAtHjS8e_kr!9_g^QY0R*%M~lq&N537P7F8lsNct*^;lEVxY(o{TM7(&NHym z-IN?*U})&FyS)x<0Q;p>LJHXxN>S_fh?%ZWSvH8+hG>T67M%bQv-LG4**H8?@Ka-! zE5LE3`NfNg1PuXp>d1xrj{ebrDQn}+g|UKwAO{q`8Yy{bV`Bri#tV#}3Yh7X4B0k7 zOvv&1Lf!vA>)0g% z)QBsf`I5r0{I$Y8^ui0rfJBhDnu1kn z1LWH0z}@*>Z0y9{dgQ4EXW^P^2Grwo8+oqK{W7t!cQnsF-pg0h(V3Ka_xZ{Rzw(8b ziWJV1X`UirOEM|4TyuhJ`uod*ZM42!)tg-|CKD6Eq9xy7WEQ7l%CV^9;2;24y(5{r z5o}u{MC}=Ja+$O7iHUW)Tb;X3ue2mVz?Ma-#G;S*(3#PRM}eqWnrNe$U11I$a+_xO zbQFf-lX|F=tZo?xv;=EM6A}`d+uPq`l~qj!1eJ@|3jIR!3#-n8*|cD}nsK(>W}vsX zmBgPTbE%qW<7SPd3e_lqa)Nw|la?etA)yx3$1`*%&4EY7jws%10_uQO0?@?_&lmoi zMkUq|q#DTF|FA+=)gY2JetvH$m=x?o5o2e_j}{njZq6S5G{= z__i}33F{5Pau{G~LBRjCNmhT>VyZ*Lr8OA)GV&Ah)QR%2nmBgF(upKv ztlEJ02l;LP`lL80)y$QSlLO1BbN-%3F1IvD0RU-RWZ6Ihh$?2mV>Z5*E_LLclU@?M zav0`Y3{UpB#05V|)dH$IzV9~E3(?QbH~V|910TbwdWDOT>|=wLMc29W=Pd#Kz-Lk# z2jbtar!Rzf=;M5@=vlZw^6VDii+^y%?fA))RHsed>43-ITFI4LHLXK|QwQdsTThn) zx`avEjW;;xL&#xJu?s0HlO7J(ya3dZZC#l|R+Cg>_xySbwAsOe(=h(j#ly>o5x@+D zm-TGFc7j{CGa|RAbzFh3vt#ka^*IO05 zAq6J+gm4|{Y<@6#vybf1q$blB#x0U@yRAXeg%lsqkH!Qx8~Qaj-Nzm=VA zuyZsmy9#n9tc~CqYmR4qW0^}J&Vtd~EWY@BwZ@RomIFc+53E4}4g`WK7rRQtP&8hiEVF{K z+^`octOZ8A%u&49>V7Ib9@LURg#UT%Y4vQL$UB+2(n}zkbpeM#?snf3M5c$;Vdo8& z^ksiPNQlqIZVg_!LJp!zKo+wgyyBibkpPncj*S9PC|J@15VcL4YjQeM;ywPugNGne zQ;0a(8mG6qZrqemA&}pakdT0)=LnJ9MlesS-+cKz61i3!D-51+w{1owB_%1b2{F!p z@;FO5cO~eNrFYz+hZ%5sz|mrJ09poyuR?v=#4Z4cl_fx!gM1>~4sOD#N`712+#vMzRlz|q9xEv6sF zhzDi_QXCN_zlUsI6!|sMeTN{C7Lkbu=4RC1>aj$(x3??4(Iy}uKrGhN_%HeL=%lv; z^gn|Ly+D=~n-=RD7;q`p^9%&7vIxsBTH$luL#7M`vXXGMrH^?@CPdQkB;2^6?(FPb z7+YNh0_bNTnl!MTfB@rX6|#{XvwnKK+Y~Zgf3*1H5Jh58#5g!T)C$f)scUF(f|4Ik zx>wfedsf(Z(xH6ziQ*dzjwj$iQ8RQ=^-I6zE`bI|0AS`k1Hzwx+=VHKopMhAMN$gx48K2U8*Ol2_qgk<$s{}bzMw9qfsrX86 z;h?B3P_O4$&vwf7?5qvK@dFNBS%JoabN|66d=uQUfgV$efq8gPz=POw90f@6nzx-&cmwj z-SF|rDNYYYYE9B*83S67@W8=(;I@^3U62FDTnA3~xU}?oXW-5!J&avW;Yy31g;Bv5=TJ5x;c)0H=f}cIAeuPe;G`if<2N*yVgH>tbcK5 zLGPUMB;dNp-PK#phNPV0k%`vlW+W2<%8sr5gR>6cEK3eJafaPc0j<1>v^91@RE1o{Ko zjhjU=VsfA7EZ;IHY53->D8%QO@cEQ8)1e8Zk`8TzGTMj4sK{VpLQL} z9uh&ZX1N_;EOmT&B##7Y6h0=+mpUOM=5aP69qjcfe>ygM=k+y*3q&&yfxP)44}sM8$mDB$FREqIwK(JA_QBgQygo$rA~weoMmV z$=dvUn3l2#zfHydEIK8l;Pv33CMYfmFtGbXfW+kEr5l~*3Vem8ijoPFxtuTyjG0|A zhBGao_%$dNg93h;Rmc=OD3XALS0qGkx<2gvgWJrv%VoJ?j?y|gLxMg1&s+g745Wd| zJ~(6n>dnc*Tk7TI;k)I91XdJ9v?(tK#u)7Gn^oTYU_G%DI&}o{792`@x>gR==^Deg z0%v;GZv;I;M<&PQeu+jmfMMOF2NL5;)wM0^*I$c6173`zr|LQUdj~mSu%lOg$$p^|B;o}FeBbECO_ z^K+4lxy)(Ik2)!mj4Ql^!Rf=YkPvv9j4oF#;Mh;q&giT?^IdCvwS8Qc9X1b%E>fX2 zU-*#Xc$9*p-d5M#yb~0~B$;Y6UgH#^Q;5(4FyA7Hjn$2uoOMvaq5%gmUt+lGkLVd2 zw`Ip7k1B4&V)qShN)59s1QMNnzh>-wdSUa~rw-W40yslqnw&^E#6=xh1gh>#wSjJB zzAGnu&Sm-N5QB_ZxF|&bdM-6-z?WAUKw`eei3kV1Q>Pk0waX4P7Ay5#vGn1!8Pdn? z4#TV@(QY_J|XfHZ11JXPg{9cwcCP& z9b1dBfv<0K*Q;aE(ASZ@-EGPyePu4af^uN;HF{-c6iQimq(Sz4O?XQcFUjzF^eke&i(m~LTTSarSo0gvtYKiA zG523Rb@b>_?5V})7P`r4l-dXo{F*O$_ z$KL7mWXO050A>jgR3RWB`?5NdtD#q4*-LdOVFRQW%7lxO%*EY9)sV_3#|86}b+Q_c zpyHPm^tM+>D!@urfPfnm9j~%0hD{~VI%|N_KZR9aj$+gOPUL0**_h+O;A)&sH0OZt zg_;YFq-s2do~Z{eyC{t1$1X8T*pC3V@%E!fR{`fmLQ3lH9aYNCLaq;#_?3>_i$k7+ znUL(AT$(ojE%KRdcwb62Umk__-51;G3Vc`Tmd0~Sunw#OjD|hSVPJrN%f)=uZnW>Q z9Rv0)%)fLeZ!`!7uxHm3l^4H!b4JFNx2!~v&xUs6L>peXxhzOV=QHEd6C zNEb^8ZEg91Ga_nQT5oRiAwVHU6gVAh$^Z`gV5t)9K`l@@<`}QPp&z;GgCO0CK%UL- zO2$nfG$9)#@x$kh%suT05}~hWu>cJQPLqOA2~FCz^#L7WRnho8Vv$8MDM?z=(9e zdPNM*LgnO4i7-?u0L}p>OCcOaA0wV^(+BmTjydb7_)9)-;smHV?A zUOSa#rwEQYwjG@GZ*RJH7OeulAklS^`;tqr=)!1`GB}~n=>e&3Qc1FpUVYBxUC--V zoZ2iuW~H43&an!`^cB_H1l=}+-{PWRn#3_YqZ<|aFhN((6v{4Ld^G=Q+GH`b?UI^& z*~`lf%Tcy4jc-3qgnbR3k*#AGO#+{(KKPbO zCaV{n7sMzme2F!HD$c{cQG=2cgDD+9@Ufp-q5jByBXbkF1tK1f7&F;Eu2|+0@J*@{ zV@`n)_)1W!LN^QVScan3{1;}(J}x#!H#Wl?r@$BfMBHP*cYCgy)*wdI`R2Q(CoZjC z3UZUCdK-Jdl;z5uH4^r**u(aSP5l}fGX{!ksM^*0MrKy9B!lD#DEpW?f*!A$(5#N+ z{|gZd?(~Q}kDml2x&ye)35fC`LWp=E3@pLQbv>Ol_p^Y2mGpC9L`?;-{1Fqku$! z?auA%j}2bT0Frf03L$(#3s49A3+wlI6OT3v9uL3f5ZbWW_(|B9lOnXqPzd24!tNgS zjQiT=$CF6%@e6PetIX<$CDaG|{CtEEi9s9ih7*s!;*sT0i6$Umam0=81sURmL_-&a z5MCt$EC+sub(^>P`DEhZ6MGxBT)qPsc;8#vO_WAu};w9wMjD@pOv6)!~ zx=w`<9-|8QKj1k$w3b!^Vl)Cf-8q$EV^4$-!V7G~7DGJ;ycaJg6>mxBJ=h5NB&m-EU~^18v|c}aw0;A zo}(6cbU06B z8@5kmphQFn(M|FAJvQHJ8FAX@AptqDi0r^tNfu#czCs9bJk~m|<9FD2lD0%OM51r- z>%_uY+pzBS{#b>UmQo2Jj-=fQ#_zBu!BxcBY%2-Ksj(ZV!M20tJ8wj`5kf>ch>aw9 z%85s{Oro1)R~oQJpq*H^dbDFbh~-Iy5Q#-6mKblqdOLoOmFw2YG!l@b(da%|G&|d5 z0$EN(gb<16b<_c`V_~Zou!dwI0XgnPv(a5Bc}_&qunu-1L}Ji{H3+@|{05j$g7tVv zKu(=DtZO~`X(Pvpi98V@;-6Mv9Wal>qe+@&770jqMWhA`YqdKsq!37!X@rPdTCfr9 zuV77r3rXNIT?xqPvjZDfZ~)7P6k?;{g%I&YBk*r5-|=}Gnj?_oV7?N}%)~o$- zAy)1ygzyJDu>OuY&ikl#?`>tipObp1~GCHOMp)kXRDka=68b30Q6< z8#NvZ;RV7jTJRe1H1KzqFF*cg&`-JcVpqg(=#m_l*^Fby~l7?15?S6EAkZrnSuxpXUmH-Wzb3lh!< z5|D%ykpaMDpb`sj6+0o(z7ZjgO`8*romhG9%h(9^tt8+aD+x$q(;qk!I19MYc_xEp z79oyF4Yp?XbzlXSBv07B4he_=TbKJm_c#wrvghjc5dt{8lV>#+u3G2>B;gwZB_JY7 z1{T6P2e{mQ9gGcV6e3aCk4?&b0}EFzb;6+?S=WYBG>*?4p(?~!>_ejBV zA)|n)?lA&eV5QKOh$k9=T~0g}Vhw?7u%&GWWgZELICX-+Fee@pu>8mbtn^nNiSRSc zSV`^*Y(K{`U;~!-(BMM}i0EMk0~6h23{ZhWTf(2hQG<1jN5fVB#s-iI35e)LGO&eJ zXJWl0=VN11icn}vc$)@n0o!^kLH;i%AdT2oPay#jeMB-A+A7C-M;75^Q4bM)y#TLjodVOaV47{v7wU0vjEljmD#h*wDe@K92QRBjDRuo@1NLBLNZK zA{8tDEyKp6Ou_Ob&~8a_ekhtP_>tPFV8Mnq6^vgq>(ag`x%Y8(GAWtScE%rK5*zH6i+LKZ1yo_yFI4T;N zYL&?(AQBMaCo-KkQs4w5(|uo#jh@eT&*yQt!Yk-RNZ~KL={LBqP44@>UBsi>c^OUa zxn1t7_J~VBBp@O#NpW6Cp%as0=baQ_m0jshbOyVB6AgO>yL=6D^e|Y&UE4_>J@*cG zjL~C_xaXo_sP-;j4`2;@+uZkiuu|J-dF^)ReW+SW0wMts2}eICCMg^)2ab9lMObB5 zk@HS++}B*^sbp|?7-X9BY&x(|O~CzqmV3S@cA|8;pWo?j`khCdN2JRm`dn9mOL8Kx z8SC!uy`&1s}aGqtT6QT4Yge1v5qTbAjIOuNl(VRl`X5QvRtHu3&tGlrWy8P^9cig+N zHLjbw2vSr;qC*Iyza#qdBoaabBE)GB=<|3Ch^$liO8u0%xk-WjQkLK&4kvsVbYN;Zj>l#EbhB$4c`B_k_4l^GGr z_Iti~-}n8#eeS#8=XZR+$MO3o$I+43b-vE^IG^+JJgiP(R zKY{)sCWKFP9(I0#zewD*OuP_;j1K*SL6Yt>BZ%C6M?+(8V;yZdYd4oemNsrywuk&& z+~I8mQBd}Cx3qS$_2#y+wRdz?#C|WY#&SE_C}NEybwqUB)odLcwJv(v8eG&hw7%$M zEo*~SRw7mKlY;?VY`rbH{al<~z2y89vD@Ry!SCqD!dUL@SG=7Rv8w2V+{QX5xYgV| zZMh{6NeWquh>3Db%N`Pyl#mq_733BZ5fv8}6%iJb6%rMdlaQ7ZmE!*E2dhL1zbSay z*vaXuYyLGIJSk!wyuID!goS;5eGmDHA9C}w7Z#P3l@%5d6BZK_f;WV`{9U~*{e)b- z_U#Pvk8#v(y{tVQ-Mt;%T)EM4Ev?*qycMxnbfSO${IxF^_kT|0>h%|PfTFOUrMs}` zArayK)XB#BpMBhYJe{|fW@9aE>ul>{>+0)LJo9REux|JY02&DsZ*XjNBhTQ4{VufLrSoYJ<= z9=5dq_fP-P;OwXbhjPfq(MC?pMp9B-LR4JH+Qv#+NYYM1T1ZyJQc_4vN<>=L##%x~ z##&547}Zqh^skkmBW-{B?{oZp1HW4#dhj|raz|agye(k^N7c92;|S}q6SK4u7niXX z5|fm$5wet)uojXLv$GSD5Eqx0wXzcxm9~`JmWF@t{;!q&HS|Ab|7&P;tFlsdwzlFD zB0_c|vNA%VuxlY%Yb#M98(VQ%Yiki(X$dPhft{88t^5BNTF=uFthJ@{-`@ELn*U#> z_z%PUkEygA(X;j61w48h2DTpm{^`HYcH6GHTY7rgqEAXIt@{9sdu`z{~Q&|Je!r?^Njj>;(SJuKxdW z0w~ry6I(l2y4u^?C<*^VZ2$4W|L)Lsj`H92=^tj`f9jtC>PY24cR`LUw{=ETLHK`s z_OF?D-Zw^d(Aj7EeQ7Zf_<@KEe*WtE_vYPQO@80?@6Efr{;TIHS4VFpu;bec-SzR` zdZFHa#~&F$U}~!*1-=2S?e33v_c-C`XX|XN?g;+K3$-HX*8b|XyXil6HTmyd|JL+Z z*PY|~TNt@*26sM%AQj?c;eQ6m|HDH5(|-Nm{N-PJ`oB5quCe}1vI~&kKmLL1_xS8; z{R7u7Kz{%D2d>}av#a$FT)P1I{o^0Fevi+t)<1CV0_69Pf8hE(KD%1~z_kmI-#`9= z>-YHVYW)M(EWYkAL9$JwCfy|G>2i zkl#Q4f$R79>}vf3*DgSQ|M&;4-{Z5Z^$%RT0QvpnAGm&x&#u-#aP0!*_m6+z`aM3o zTK~Yc3y|MG{(}av#a$FT)P1I{o^0Fevi+t)<1CV z0_69PzlDqRUtc-3b%pPl`ob4V8yuRF;LE7o)>`^H2y$^hg5W|BWMvEfo~1qV9KgM#(CoXS84ICUggR7HB?lsd`N66oOR@O(EFg^)*{a2 z!kO}J$LX7uIx6bw>e6p%aI(15NDJ}}xr%vOT06`}p4wAfZL~gHK!OM%7ZGB~Ikz%o zWDg>a@K&!m@gmw79z3n`TIK#d+zcu$#T5vS0f*-*0E4;M*U?kc&}?Y>B9sVgzT`kX zV!Sz3P>zXs`EyIs+(|b%UvjibkV1uE)AvRlWsNMtU%eW@idpv-vd==CG35Jimjr0Ko()+$6-U7bfbz~Mo+DKZh5_p*e5ny6;Xluqf3QEUl; zLDm7C>hs@z;XTGXL1aNCp*J**xnKG7R~Pcw?#*ATh@WjW!O!0)B*PIqRQb;O=aIbF zL;XsEB~#GBETsU?ojpm9>KKN-q9m5*ir#SK&eGd;Jg$tfxp5&O`I6^CFnd%N_9)Et zy?mRHoi61NI(BMxz=-C zzFxe3RUmEU_6DAW-f$dJN;;x|i`J&1p(#}5!(#86s4S=t5G5IP2H;0ru`BT&cT&yp z{@OE&NE9@uVBA;xQto^Xrt72m>;1}kLTdLsl3z_*RdiH#=U)+LsuuAKC?P9I0>;LC z%?tb2G9+)eRYVPKn5qrYC7Et3;TXF1ylkQqgcd}&x5!U?t#G>wE2dfDJDAX<-j%HUUYu1yguB%C z?wPK7*)6h`Cv$6QcdyS9Gk5=9Mn4lgLK4LNO< zM`iEbk%%|rds3&1l_|rrkd1;3 zk3AA}E0!JKP8z)ZQKrQ29Y@4=(FLjNp zLzS&?h#jNYMewU-rEwF&w&5?JseMp;)Qyfcs_x9(pUtt)mE7j{-;(|H&(fS<}HA)dc* z#uMA|f)SBNz97aVE#Kx^{xM9k7hW4djWJ??kuhJ}iiogjBX0Sy$Ud-jT5!pn`e!0HFJH+N9dXFUS8gB(LmoXTWySe^6^S6XAIxg3utGSL{qAd||bQIm}76{kQS7E{~n^`)jrCzIdfs4FGSjRY+o%`Md^J03E7U+-?#F!#A>90vL$o63tkvVQ0 z;(k;Do389dP$Py!B9xfNmdAGFZ1CCv>TjY1%D6+T3MPz9J&R$NNQpy zxv^sByO3*0A{jT03LP9aD%9R@XH5;1=xcc3{Rr0)dt?}YnO#K)Fh*OSAKX(@k7(i( z+sj2nJf=8=$dHWKB1W)7)OvddTvSf)cQ%=PZcND zSToSjIJWB{xABbY4XIJxO;;WxNTtFBs}vcs($K``-^V;nwCJjJiMgIp8i*cSE}4Lt`l>+eL| z*I^0R$WFXvOieI`9bG(NoPA0ep~CaSWkVamG9Cz6_+8TNjht^Mt1)P+c;niHj(X)e zNT$U;iQ#Oz?FDH}QI41(H&R!I?ngvuz1Bx6$Ss&83hH)RCzpuksBvqFq+pZM9=7FX zR^5aNVE;T%%*|}8$#DAtOLYNX<|LoaYE0X!oqZ&zQX#EK1d&B8I%E9N(6O#xT$oPr zoqdp}3FDeE11MfJpG0uKQtzGs#|vAS)F78Ac&0~afu1$ZMw0--g^_83*8rLhTkFj| zxByHdfT6A){FtG}6G?e?k`G-=hE551Wxoh?>t&8lFoKBt)tyy(KdHbdkhgd`a-eXh zpE*qgqUDS5z)HOL*=b{F264i;HB2tP2q}&XWJcZlj3DBTnUh9}k!Kh~u#JmOfmd|Z ztECxg`jI|y2AEs!2F8K{Cp*GAwS6j=>=h)O?owF^fY)9eBbF$rbR%ec=S__s_UCSn z8s${~uV`FBZY?e0^w`Z_02S-_c}J>|VT4yz{J1>0JC-2<2eW|ccZS=&mb{3lsYdoA zo2cBydkdtQB+Zy3QJ|@xYfT0sW8Tsu2QdR=HSv?M--HAP9B11*Dtxfz@O5|fsb;UiIV`RbSrtQ3)XWseg(JOS z=Ta~8??v@3Oq2lhKwwWHz@xqmnA0{sTTtAf*M50SKeIG&SLfD>8=Yh7?rN@e{(P_>Yj|2#{3~bmC6U z*T@6Hr*YZb30%m?|LHZ#!rEx;`Aq^@EJeAkC4Wb4%TL76hN4HhM8l>*&^%!7}%e^P7Y@NRERsXiqN(^X-S2o-A&tY+Xji{ z@+`uOIS^`hR0=hEJfTN0xr8IzHYT3Nqp~~W9x7^){CmrM)(Mj*eqP1*``i}^IuP+H zk%Pcp8MmNK(4O~+&O7~#G)>Z@u}akBVX@0<3oaK4aC9K>V1$C6?wO-Pj$>p*Pz2R` zKH@Qx)!^5Ny5q^F->ZYoxa#U5Tu6j1ic-X8CjkT>?4a>|VW4?^v#Jhej#PF#Z3*%c zPx_7Vd%@C!rRX3eLfp*Bj-z@L@wi10A^~br3qG7Cgd`Ml?Ris#7IYGHbs+dO?Rk@U z7CgAK2os;*L%`9N8~)hZnuqoaVQ0@6{#SoiP{#xjvV<^lLR4_ zr2QWwCRpE(>Mowy+orvzCw3eBj%;lQ-cN%qhSZKAob6lMn5X0_I~tYY^}g-h4>RJC zJ=>Brac9Hg5H1uMK#eLETPaE)0+IHRv^YmY)74j{q4>t+BQ)F6@XDeLpP6EW2d9t~ z62nlf%4gk8RKtqw$aOruC!&C~Bu%PohU>MxHde&5;DC@3l_D+HYsf674UhX%A6H}y zsPK)#Li{;a+&Jh5{AADUp{6?2s0sRM_?ie@q~^A2t4F!BCS~?s6K4Q%*HRos1PR(6 zZ0}9~rW_d9;Tps1ee-PIVuW${#sr7bG~o<0)EV00t1QJ+%2%mD+>?nYi&smH% z(RG3m_U!?`NYW(jmtEhHrmklpV>++Tf*A9mGJ2N}BN$?br9ro(FKvK)MoRIQ!K%Zl z{s0gDwuiD46*C)!i6>>A?MninZOFUy@3)XF8XVZBIpP7<7BpdCu%0yy; zk2|pqVFYqEbgbC-(jDEX2BU$a+3AS$o;I+TFju-0JLcV0yX3_^Ss|dKlSAalJ;YBG zwT|kG@?*&Co)ddUn75bjZ7+pu9MPWHZW@PB32#o(K=oLyTia3ocbLFEBP2VMxz_n2 zS%?D&K8Wi~(Uq})h4^i~gup2X;Y95H_+Wi68VPG&AWhqTN!^=`G|fMuDkYoSD^g)hV5x2>)e`G)vPX@<7R^wqC}8#AB-7I{A?nA1U`2S-z12|MA#<5hzruav$)Ax zYM>t|MNom62sXnPiE~5&!YW4NQxjWIf&PV%M*`yHW<(<4J5Bg2mv^e>ip24H7 zGDq1b@3Befy${%Sj_N%8Tn@Z(<;2`KCb@&aD0T2LA@^!~`cQkN#d02akhy&ZAg@Pt z6foZi+wN{3E0(|!5n-%3ly$|sZc)zm*8iz2u(bBwaNxN_+6nKNh1x7N^h*Zh0@ib?nGois8E zUtL|DM*oIIb#`~BXJrv~b#*;{{8;7GDTb-(>62&9M8A1+?B=aoY3b>B`}glZAS9%r zp+O*U@E|m;g-eq|%%r6#LG|?Kwlps~vg0U5uDYvdSdER1xqEt+ak{#?y8HNq-nql1 zps4ui^JjcFx9`)jKZPk)g2<^2w7hb{4zF{o|=llJm_p5 z9`j4p1u%O{OUpoMgjP!uMmx;zqa}Eu3@=~2UScI#K5S;n9vy9TPm%*f3AR2NFtV_sa4n``qdLd#KS)fiD0MU8^D&cBpDgm^2$o>@Gutu zNlZbZap%xEYIb&VW##JWg47#g#CuPiIu#lfMf&B-7j7XTibr}WO`&8=CrnMFo){Hi z`S`+t+KPXdgc`k2)Ldui6wZS{gAa>EY&U95n3gF|dbZou`@! zXXmA)&4b@OxP3e9-8=2GrS2Ghe*RMLNm@E}b+K%G5lqdF`bzn!E}Yl32a8Nz+t^5e zE|Zc{)6kH<%x5}mWW>TRAfUdv`AG4N!uA$NM@Q95my~||_;DSE&B)-nuN1@zA0FW6 zS2ZzVxuLEe)`7I#Sme4(3a;u!{bb+e3}Mjm)zR_s@axy9pSuln4wm~K(bguftE;oH zxAzSaa!)UjngN8hbaXVVtROW>xc=b5wc~dV?S;*gV6j2x3EHS?PNJsfL!LXqQwkL~ zSgUvAn>VBw3}&J`kFCu2`!z}qIc|U=K0aQXa`N3vuW|RWcPwXLdJ^#I-ml3vcy#^N ztP2TVb?@4BfgVd-bkNKWUN0{%@FLtYGRzxmzmB9R1))L*^2i%b z&MPm^_VMFKnZRWcN-m|Cw|9j1?cX2jR6-Yj=>8SEA72lni!(S=ir>2@RT;M1=kO{d zB&5B-Tp1lvPL8#`y&YAeND@6Yo)oYcppbj{`AJ^Cel6oWJFt|MOd(I>_VmN<9X1^Fs@m&z)^z?LU7M4iM`mi*&;i|b`zsBi2 z_4n`HgAVlN70vqM2R*H$M{5DB6KBr6{X*TICo$=P2qdCMS^bL@_YxLV9Ga5CMM%XN z)7xw8{e6f7Hum)Sa~rEy6B1HVSlH6hqeo@@=D2^(4n_|QoaGIR=sPcqyQ_^4eoty# zz0(ft88I1|+Hh5n=*2|`bxlnyH#aVF?`rb5Z_f7>0~pV|aMd_`7*F58;G}^;eFJrS z%3b`C;qCs2A7RZJtYPE9!-xA;m!`vA2g@vbpIS}~S1ae{<|Z?DmI<%B1uhfBB9iT&A=+sOHp$hd>=Bmw4~wfEundhJ(5%LVk&^5 zO&WijT`7 ziD8#Tbr3pV%go9eZRQGYXjR^dsSl@m`1o%7F_$5$0Uf6gr_$v{NOUNbvbUg**<^`2PIW*{fcWZwp2 zPz!^E><8xbB)SL>PtU_PHaw%BKa1MDqP`j)USC&tC30_qx`6@R`xNE4luN55(>(>v zpbyI}a`7Glc;M@6~4;+lp?I`kwPnwsjmZ+ofI@}JVt zp#W{XDs-k~r2KmoYBg^hwqd`3$6PD%RF%(XmAuAU8$bS{th~H+4Gxp2;4cpp3QBl$R-(p`8KQAf4ii*;p+s;2%j+z1Dy@||NA5)SM&aBI@^ZF3vwit!_)acYy zRB5Tq`ixs}J?#I}=xALAHA{9!hyJ(z7gSG9=hY2W2An*7x+#M?yHy-fvSEc_=a~kg zg^bh~QfI4yiPVhaj9p&#Q`hhiCB&}g89dUpoarm>eP&Dc{=RJeRqE`w!6px($PU%A4Z`T7^nb}N1~^v2lBOPsH%zqi3=-s>al8lpa_A4jebPuURo8> z4>g3zHyi0|2#{Jha!-GMf6Mjd`O;rQ0W@@U_{WbQhwiS&tgZLa($N(Lu6VA`)l(ff zaKJ)8;-(Nq%?Sjk4>oW8Yp0OSCJAbO0}z)i?YmbTKVyhCOlD(;=K|Cl>7&k3*h zVK{P{aXb~PkF!JO5h*bShyy59kt(8^gF)CxpNTe%xZS&&cX!3x)6|G~uSR5CSovWa zxbpRmaq)SE{If6Xu2R3wV^@*E1)|EsGQ^8~zDtOe)lgR-i9f-_Aa9 zNJ-VGBpf4X2Qze32KXv0wyBklKM0W7Cr^{DYiqmzbe?JG{^Pf~`S~MZWv%6inFT8L zmnevyGUCJyRC&TdsYcN~KX?2vGBx$)Ds_C$t(!M3mZrLlEBr)tQIMKE!_7Y@W)Z~NCT~O)in#AKc@!=+zeJWW^vJ*_v)*;LwFM;sZrY<7nE@J ziGe~1PAmp~JH$#d_555&d7SK^4Ag=rNv8d4f)8QB{% zEFjW0&`B)*eX>8C%e8jyw%iOXl-e4Z{Jo3?*5Zo@ERK{D{C9PqcL6# z=>i#}U=4&=2)Y=;)&kMO7cZJ?F5`>34#*fA8;`&|s1dM=Ab+TcV?@^OlG={Yd4HY2 zj37``x}sGLset?U%3q_|j~*QM^OK=VCxXx)!qoc7505-XYA`%}e4pRkI#}xYm9(Ow z;;HMP967Uy%6!X;b(~@t#A58qmj*_p19P4{X)m(ZJ6q;W63r-72P&RBu|7Lo&1I0K zt!inxx9^2pTxu$}_jES}8W;8CpMxZz{+{zi2-L2`DXkJ8Je{j@?i>d=31)F57ex1Y zJ-G@YQfncy$Y$LXLjz`LdH zP(kI-B;z8xSebyIxeg8vBi9~VqqFWia_!5@&{`2~L;MJvGaBA&?3lpj5Ymve;fcZW z7sizrB?}(=95{IJICvY?cMlZL4ET>H*VWg5)^qBp3JUP}R7aq(1qx?3-$19gKITio zDxsA`7}S=kTFufwytV1uJ}{6wasN?PR@8-=zKzL}ualGfVAJ^@T%6)B^!Th9pPbyn zVHkco|1AH-#fe%kijcG|Ej(op-ZGbhd5b!W31KG$3m5Q znTVE9?U$vSoA85xS=mPE%VJWQ=9gA76D6H)M_jwcC?zE&eeT{h4*4jr@%Q|j{I1T< z-zb7pzEQ{_q82qsd{R=*1nYe`w%bXnkfXMPQ4w|O7Mx$|F+aFz7wQaaZv@@t`}Xad z-ytiJMW(o-7HpaSOqxmgbs`!A*Q0L z&g&~JEscO!>YyAS#LHL#fhnrshqSET2>f~s@(u9q2b*8pM@fr9^^M_NmkvM-)PR4P5!}yV2y=h1H(E?69l|HJ>DTC#N|{fswU{`B zj(uO&OC8j#Ak3{kl8&!o;H8(w*|Yb-#&@2NWoKt^2%})5Pfj^keu2cusHb{8@7Vb^ z46(Gd^gJ9Wf0-0mRXT92rh|5Qxw#E(X=;rX{tFGSZ}9o!N+}2k2u6#0%s%wR(i4Wv zhtKa{R+MWuj~cJNQrk8&{q0z=PS16Uf%tt#8laOpxTw(R=;-%Ti;Ya?2jq+(0;28i z?si$tJLUv)vazwv&%78GDANR&-~I9D^wgBV#ignJLxBrA@z<}{^o+QDUbuer2Jid; zY*p%k<(kG~a~a;_&zE7gO8`L>!@<)LV1mt-hBDx1x}d;m?dVGEC|nXU@UFToXo7v$ z($c>kaH}^JpBmK8>f>RTx!ICV}QX7ZUZaK?T=KiX?R%39#<2H84OLr3OujljUbduAWHPgkr? zJ&AhX4?e>EL$ygZFdFlp!np*>?1U>96NXCMjEszExVVI?mPg6ZOpDXO^{}q)HNVx# zN1=D`vb4I8hyDC4ff$uot5+9aCn6$LD{rq34lcBArZoFla?;T;8?{Mjpaw}(%Fy%@ zc=CgC!CDEogR~fb?=P>eu1=Ko$}%p$Ak<@0O`^QH{BWYDfUDI5 z=CQ`_7fs)~`#JnSeX1dWw4Zs81^Q`H!s zpKs=>{yLl=M#zC%27i=4!8+tS97Nc^`S9VxM)6jKeYCW+>Rw*rRNBF5&z|X5Z2as8 zHY#>p0d_Jk&@YCq!B>%`A|?;JVAT*ob6@o(g_yW_qmg;ge$5zW%elJ=eA_+<^-ZP)s4zjQ%c~ENN>HQ!)j_8aMo7l zg&Y0Dn_0IM9_N9{wR@MC)~l!O>FKGir4?bg_J+fEV6W1~q5*J1HTZp8sQ+P>zaZEe ziDE|9FCnJ+uyjG3`NdY}`VBp$Ptx~-M<5^`eL4PsPE}R4qw75oE9{CF4deaua&l;W z2Yv2zd!{wPc}2v<-5K>Ai~^TC?Xz^HL=#f6Ml#`w)ZU;YJxNJP4G{jkdo&5tjB=JZ z*=P?S_s6fbS`1v&exP6ip8dX2hO`4`Pl35GN>rRnMEMu=F8 zlc&F*MOnPP~3#$iG+-%@Y2Rb-7tTAaI#h%8Alb2#F$?&`5Oj$i64edgRSvEK3qJbtMs4Ym4B@3XUCoh`v){;@ z@}$LJ4HZ-Cx8R_ngEtpa(u3B1Mm_!nc9Z_@I3yvW-V-O1U3$*Dwt393O+mb?(R~TB zuc+P^sB=Y~n#X7Z2`6*#{=Iuu$=PMDkDnm?+-=R-FRfpMINTN#G;LG&=t<|1t zbsrze0;y1HAt9mBM=6(RAt|7qot=%!{!FK*koGE&iGd*!QpV&VKmC9ZN)>VrIYW9T zrh}erexs3m^hGKDetr=FvoDW)8>&3>{Q2`a9RJnF`T2~fJpqgUMfO&0X>n#x*3;O1 z#d5XD_iIaEM#RL>pDA(v#4uL;J;nE1?_NkL^GeBDz=B8j^_gmn1ujms%LR)!FoVal zf`lu*s>;lxAN7=pG9KyaKV=~U6t#QDTo~p z%goHQdY!O8-M;lfRDNc0aWO+uFDx$4&2fO%;o(ZI*6w&MTt=$T)dtXv17$ zfkcaf94F%0GqHzC-DVYjCu7*8uZ^1#KAbs8$sIGp!9RFx_8obXd3b1OXrjDtKDJt~ z>vm3#K%wW?V<@Y4s?)pn3PIEp)#Jyb6Gg4VhK%?`P_0FO$MT7-jf7Ac7RLSjK5U+A zTKwiQ-$?G0Cv;w8?*K?f1tvf!IuFtQI2x}XOVHN9akqAP0r)+X`jYG;YXGx}dbMZ_ zKH=l6EYzrvPPKJ*X8al(=wuYk&{!!Ty{|xo_NK`4lICC)P{QNxj;K8>J!lc!Z@1xG@RF`+>%(m8x4`!>)Ba$dHzj61Xthq#OlgU!h$K>qA9Odpo-7whDX1ShezQ=U-6TvpPxmon<8fVU$D*0ue3-H(?iIqV1%xhXeCFU|MlR2BTI}j z+=e06(B;gcKuyx>;$$6=+8~KuUUbcITt;VgHo?QOO1L?a%5s+>Y8b_ zv$GooBR~V}xOm^a$H&f{j;n36R(M+yce94EX8-I{@}^kpfl?2k+M6d1{nXdjZ}=Ri zoZt5d{&xvGtItibQBU47nFm_V4HT+wnI|SCMHN(UntJrN0|OZfYZ3V6f<{K*+oNyZ zq<<*y3K?HC!~j1AS}fN67p>QhJUi3E0lGLZI0K<< z8$DkH>4K%J>wCg;>4Y11tQ&s~2N%5RN$;wN3-I@k1niE2p(K`a#wCg>(M7!V4E zLQ14de+j>9d6H64+0?IJkkm)nzP%lpmBsr|F00_o3*9|?_RQS}%P7P6l!Aw41@F~Y z$~PA4QWt;C3PFzVV#wqP&V==5}~M%Or)c}SqtlGvZAR(n9@AR6NF z#!68-PEJ?)waurN^)asg?|>$YOiW~c(2p*A2`MY$;Q1KXn0%vl+2viLsM^gtci6fb z?};j?uI;~{oZJYekm1zBBKua9Bh3Esif|)tpNPl_k6|S)`3u@Wd9bv2Sb#)m`p}i+ z=4xx-lZqYpBmBJ8%z(@~lN@FI{L(;-d5a9}q{jQEz$d^RH6ARez@9yKE;g$PjWfT# zxfPZ30iweUr}rOIU*sLFcK>uGy`=c-TUdznTd0VjQ8b;ct*xflKrp0dSD`%Exk=FP{2D>EL;eH|N%+T2(>e)qW4 zL@)>Ny@msi=!GlhuC#M7VfXE8oa%ZKJGNX6GSH=mys;Rtp~su3`FWk(g}_&nP>pV| z%*S9r`r!)!QPGn?pRm~`AAu~Bm5q(T;0Xl5D0Az$@S3pF1*O_FIQBtI)&SK4$D7R& zz~)DR=VWVqiAEk+Ik}`BguH2Ml(=6_EaUY62`n2%M7jDd^BCZ9B2eX&fg*@)^2Yi) zMD1be=YK-_tCegZHQ^EmPSNyazuCo$Wk8-wzOfF4P||Oy%SZ#F-KkbG_`ja4-rVG` zV6+g~WTGn4;Zbwn;~pu`9fFzvR@52=WL1~>)u3O4zL2}UahP_tx90;|FZFR!<=Sj{ z$M`LZ)s{xc@EM9JfPpaY?dxNm@z^IPXBt1#LdaxHS<={cUDskh13EWZIMjP1@fc{oLu2LWawP(%Fo zo87+J$#SPWZb+M)Ia8!+%mx3@rSOoK}wEOKW*2 zg^HUk=1%#FNt|)uRUXl87U?4n0bln$%Wo$UZrsei$-eS| zkIYT%YGLFwMg?Z4W;9NzsfGA`j+MR&C6no(nWJC9_t6_v$Hb7g1H+;5HIZU9?})K+ z;;YxMPrm#4(y8Z=rLAqFH5oUmcxT)yGaobTbv=FILdob)enK^(!Wft&K^Z4E>p*TD z3q%tdqjZ^RbI5wCH#9aDc2`G1hG8VS^aP@w5F)D6XRm!s9X2CplhTe_FYfAht8TG= z`+e%p_3Np93&(VHVoTxNO`O6Cn~{?EjDu%NLquaFANY^4pv=k}vd1vGsN0YNuoJDO zTLNfo@>mYdVWQrNvn}?=5pnvdob1mj%#YhV@@Zs*8%;&Kh7i~D=V`%j(mS=&Fp3oCvxbTL}5C06ie$s_D_Sl$6g3pRE`EZJ7?8# z6>?%@`!LaF_L3VJI^BrpOKZ^>Az)n_KnZf*kYxg|Fb^!y9le&V)Y)UJvwf&!zjFYTdnoGV{%{E$=SK>x9h+i@=q zVCaFtLDUeO^1tj20jnsKHM&=*Mj;e1(iWnlrM;S$C&cD0HPK^!O9>d};q?=$t|@uoY$)kwmZH>@xe@~GabpmG7>nA?ZW2|xkgWL(*i@s`*V zPcyRzsPh%@1dkIy15TQhl$5@ZqNk?^4Ls(07l=N6{ks3Q(3z|^UhQ$po3FfR>W!@x z`j3~LYks*;G=_hDecf<{!U9ACauxg7Egqok8ycgT7*4iAq=c5Ex(4ViL-IG=?mo%M zp)V9lQ4Thfk(Xa!UgYxt3-H9#zZD2N_d|GLqD}tDduP$f(;GMBY3dWPf`ZyWDCyqP z6Oi+nYGB>c5HzbW7L}~r+FU!e)QtxKH={IV;>-$gAYVl%gt0t4>JS>M`HVjTHxbp= zcA`7c(z)=83-4eQB@*=#Uo>X_>d%>j5H@Dykf8 zaD-b5qGdo}`|@(g9snDMQd-?FC}`;Ec|5(m7*6)&oq0h@M@J_$;oR0w52h*VuHHU* z`7?0UVIi?ItaLiRoW7Xc=8O$QR}gD;-?7y8h?wCWjNu$`WNt#MMRbjgjX7m)Ce@cJ z9fqqEr~8`-?ST4;dW=e~2G|<`zA*@``q@rQPEJPkHdSwJ26PSLI!=MrIEm7mr^fTa zLwCK)tF?G8YWHj=qvYvTI}&78g1oC4(lkhXigOORoB4lb=>Exza?1MZ=MD-87_NCo zQj}ftJZoZ-9pSR^nlI%`;Uq8W4h_0wb8~Wzfe79&wq-tUgSgwS9S#0!IV(tR9l>vq z6#?qeb+!{q9A?^yBA1IRBPTu8TnGJP z$|4gb924Lwz^Rs%+^npy6bBGBB`X-27f~Vcr4Q@-oL@ex|BhDOr7&pi!uu-$H!NLT zgdlZz6tQ{Y39?RgRRT zEgspdPrhnN-tv;X1y>Td{3#qxJF2+ywcm8!3;L78U|~_)LzqlacDk;;{Uj9I1+4m0 zq}@2;4~c0`sRd-TSAprVJLk3+bh;6=F0;S`oDH+I>;03RS1HOi zt?RCmq-SRzrSWnqEiYf_^&xA|J0l%`^JYerml>M1$a?Wkzc~?*f%nw%__69l^3+@M zglc&Zh?8rajVWma#lQOEh4l2rXtda&(Ftjpxl~fpQ&nm0iXU$dMy^{tI@KzrxXg#`B97XT7T^YHWd>OHJw-e58H1u1u_xU87CA zPe*fBbe5dFxM_L*^Qrk3sEEGw8@Wtm{Gp%N&CN|iP|bU?WB(`M?G1}9LtINyx(x29 zMEP|MzI_|r{H#ABob#fV;px)?n}NNMDndfpngl~1|M-CQ1ver)K*Jp9d8mMl=}^RF ze4=MrFIbKlo8d=SPup^ID$wHV3^Mb9zLP3pS~O&=T^OO z+RxALDJ&E!Dx*MicJu`Fm$=mR^(9frv3ZAFh0GAmj2G9A9y=EGw5aHF;fz%M+qcJa zO^P(<$6t?*T2)y#o$!jWDnEZ#8)_sJDQP7@J*GhU;MBTCGq`ObZEbC11EGc^TtPfR zmEvMzSAo)O?~b4-W2d2}<_2CcbJ!d*Q$^1H=VVvVim0nU4KboP;!BE%=5yd?7 zqfEc``DTWBlC~870|%&q{IOMJIUn=d`AlrHs_PuNqy|uNSTpNQabN@PFKR|Z$&F9< zfdYTf;IQ9kiiZUZY~C+Nfw;bXsT$QAsQY~?)IE+8y8;3N^g)!c2!`6)+FU+0my^U0 zEIe7&8EJZd!T^ot!J7!I)wm8)lz0_%Sz;bXHIJt!YQ>lrL(8|h6wDhpJ)(Z88 zx7Nvw525%PwcPNj71$|F7~tsx^5Vv`B?VJ@>A<7nhbAwu$G(-=iL_}2# z+Bu)dMl`ZilfNN1ulS}h3lNKoix*y)G1VCUx&^WM^2H*`QE(DBtA0XR&?0Yn&pDcT zNa?kzl|8I<1_kwALIkamVhvTqgW5VeCVF8>>1_QbODjy$&S9ojD?h$QRk@?K*Z2sB zI%Mn`s;ZYei(B8l`!apUxs73orM>HkQRdJixPYc!eqqK~nB!{49m_Rv<|aqNlB^{A zO?*7?PQ0VImllRbz4Sq~a~;Qp3$MvVNji0Fom&H?LshpsilQuin@r2h)R+pI1_8It z^QMQ}1<%r3U!LU;NlSOw)0XU#cd?>2HVMW;B`_#$?wxq+Qy2@po0j<&WDF_ARw4PX zmM0#@WeitCJ+8rY^1y?jpC%e90bIfB9^LV2w~gPHoaRW!NO*OX_fLD1xxdVJARcUPC?RA)BS(fG--_Z3vF z3LHLsID^9+uI8Z8wV|QmQ{xORNNCaW2;3j#n_*T{Q=6Z9Qt-@J?itjt2Ovp9W7UCP zC@Y~Aak$^}?0UxSp=ZcAII&}JrtgN!KIb2&Rx<;OP~v)8TZ zGp-Hf`@@0m*V5HJ{UuZda!qDA?*r41>w&+Zhr1NFF3Q6di&A*90kEE6;qDjM3%-n@ z@eALF&;T980`<0yw+vN}424((X) znZ8pV)~)Z~H$iqUzfuj{PeNGHkP>_e;L!J zU2tQ-f`a8(+zmvq_lLj{2^^zqDS2iSR7V*6>#Ct{3S_bk#S%4muA7@(7l4*R1zn*2 z{d?Wj);P2X(tV7o{iT#)Z5n|&R5F;3p785mfB)mz*tvqnjF zsx>q;Xeo-GwYGjZ@!=7bQzPNIF0|;IeP=KyFYoJBkBd zi;c}?RaS{4tcfeoibt=I5|A+-a2)^#jK&-ZH*T1yD=z=4-lA}Fa&m0`)qaF)@7}$a zL$Rmi-A53m)rozM&Gulf;Ktgs6Bpg#{?5DmvUI5XW?KX2bM^7#1B~%E1;}fL6A4%5 zCuP;*p|G5DB_$+A&`TI_0XAAgGYy0k+{Wz-nRsw@yaL=hs?~*yBc0g> zXl)2CxG_l1!ZredHbG^W=4hlGerYsCS%n#5zp#(sA{Z`_^PLAYVpr~DcET+SV--bz zC@)ZRavqvl241frbLrK@HxN0vBYW#YAY6I%TWmjKazGK>+ny}FlxuKtSm68skP1y` z3ag@@6MT5^ZTP{{5;(g#Nvg0qls=6L;K+yY9ZlLVU5tTiz9-L~6T!2|yzr zQ+(YPYiML-4VTp53IY|`<0C+r%0YIR!7*IDxg<treC4HDPw<9OZ zv`-28$+GQNV*P<2_4^dcWUQcXW$wrK!NS-MIgRs;W+sh-+ec^-RAcaHF#3W#{#EoY zn*WcC=lv*!8VcVoK+Q{xd1z5l5!lw-7pfI6d`lFOmbT9$r$$Ru>F+n-nhsj2&E-&Y zEKCr3X6b)<_<=Y*J%{`SdbrOYC8t`w@LoQb!#sLD80g1FxNl$rU(tzSgZl8kj4GP9 zt+&XTzE`c~r{`nfs=NkNEluRZ64FUjq)T3hE#OHX4%0_r{szp5DBo9DE?T56x4-^sfm?OEbAut?)*x zFY*mRX!vD({J3lhq>k2*gbF5HR#=^Q2-mUiTQvoYC;M6rRrm|C^S@R{6oC^M_Cn6S z+=!<|FPgS0{<@#uG*D=LMB&$fmx#EyHHN}{UT~o$zQ;V61F{i8`u3DpEiH{u-OuD0 z2J=kA%>3%` z5@*7_s}%HRvr~4naf$QY=l%7Nj4bEuGxx9`Xn3R(PRWsQAsA{`qSG18%pw-JWhe|V z1Sp;WQN42Iit+KV1gv&sY`%UdPpg{Dm<)x)z3Mzh(!WT?T4#2tLrk9+$}9FYts|-W2{IQ>-W^K%w3~a!=P~mspL^@ZF-dG zbn#&Z19iJ(6PQ^Brv{M7GZEmakCmGjm=;G#cFcF=K;&o8Ra^r~^QuqmCT%A2tygJ! z$idZ_d_Q`{1uZ0>cnE%l*xcN_+vIKg3paB|`=3Gr0<>r#PsirnB!m{|A;h?SA#WOP z?-c2pQo_5aY@IU0hXTWbaqS8HirnXRMv(=j)#TXME32z$DG~1cocQ`Z;Kyq|^cJ#q ziV_Ey6v5Kl3R~+79aVD6WB1&!LPD_`*fIhpVY3EcNcVs36%-O;kdDlL2RV{R(5g2a z_>p4qukt(b_}q^lR<%b-9$-dlB_`f8&mBSmfpmn_y3l9(bbID;rtS?X>*cvm9o@?i z#?&=6h55kOSesm~CMPFr9XpnAL77X;hIrchUOU_fMYHXPvRlIDRrmiNs=hmr>bC#? z7?C1mlu<^qlT9Vr9iv2uLPBIGr$UO5%rZjANQcrON=6YCWtC2a%8D}DLPgQ{aV*yPuaB|@+I$V-|$V{4L%#`{;SiF`yG^}n%M)Juz!#p0$LMc zAh|V6y@la_hs79Y{D4VbxpvJhCDz{%@asz-(@DA68$m~oG^1m=3HTRUcE*7dzc&Bs zjmlE@eEh+1r~)CL%-+jHHhCcFuo)N4M3552v>Vel8VsL7YdP1djr`au_ovK9M@6xaIsX}g zN`RhXe22d1G+RnhnOvD}dkWtk_4;1P8S}o)&DPeIWA@*f)td>({qW0swte2-@+anJ zJmo%R7{;)BPHGGM4pT@)&p8n9pjqjvbCjn^mg zzh4KyHC{aZ8@e9pFM)%A6IDOH{wWsno;MKHX5P&JYqV#v2?@jV9myM1Dca>7^@sav zcvalF;6Mw6oggnM;2}{ZVarQA^k>7t!H9DjgsS2f@FC za?_doNCgw{dvwb`In>5BHFKOosz#WQW|_ah;Sg2U*J0mI2R;@f2tka12+|&(B0wKt zbK8;LrY7URlllMn!C8HX+qD&R^5b27LaL{4-+I?N8-JH8LI&#_JxpfgLniO))lZGa zV(;JI0`1XR)Fp%Uf4)ZDxN)QQtIpHxMF82yn=4(LmOxc?PDDI~NlHq}1<#U^)b{8R zD+CVKwxxc4enc_mh6hdt7Kn^$rjJx3qv+e$(s`IxGDY}E4MdL*I<1O{Ce6 zR{XBAy=UQ}kv;LC5Cxzh)Ci2EPk23qWT`3V721Ing7lL!O0Df36KBK!8a}UCwK-J% zj(=bL^rvn&$dr3~rk=ptihht72GaB^RG7Y7QW$kt<=mjkbJ3|1O6`ECECRfUGB4Y` zhBsJcyGorBuwyFt|0UB5MG9SH7rZH?WaUbvGUtcSZy=zxNH_kf6T$z($SHCJn$)f=)mp7pwH}dhh_VQ@q z{rTBX1Bo+O4fx11;uh_LZ)qwtPB5~tNYuY0fr1rUb$KUexOpVOuUF?!a%{{mFc;0s z%aa5bS368xa+oK#^bavm4Nst+PJk~rZ}7yl=1ag~zjAbLIM!HyRH?dsJC>9(!Sm&xsU^RBM zhUvgq(EIFl0Yn6P#DCE0_5{^ZS+@=m7AiW`+Mth~jQ*)G5H^`K_Oj#!SHLpDqJAvT z2fPeJ(4j4!PXtf+w*B*5thG4CVgl8v*>P8pY0FW4%)J;mIB@(|9JW{I#=mBeC}5K2 zu2gZ2R@TsXHLeMVG!b8jJ1tXktQLn9y*mGvzGqZv1e~e@@YSV$)vgHtcLE#x z)aAe4o7_%xp;+SD_q zI`=00-=6`^xr~Si3c=jQEg3P@jph5>1OvOBj&eXak?Xq^%|XVGsx7Fr|MyIG-23~Z z&3d&t)W#QY<>WxU=GvCAuCTm3MdtcIopAku=Y^V<6+ba-sB_1#C>b3*sQfD^#vQ=)iW*97yKv{O_%tIocnxwRYGH)BvdPxotO+Rl&ZJv9$f~&lMrZAuuMyjNP48GBPrxq$>%Z zL`znaujU?a+T2@kzm+RO!tnR(tXbc6Ft{JzKiHt7Sjm{dP`*hml%<#ESd;PM*rU759fC^CO;@?w57YJpT*YQ6>nB-`j zQ)joY2!fl|Gs1A%{7J z*TdKr*X?|48!S3wVSfS-9^^qW$QUUE?`>pkjDhoH`FdfusQNSHT2NA^&A#7^BC>8| zjkL6h@9TYpaBWfLm2($v@y zwR&j-s}Y;~{{5%0M-b&{>%@NOO85So&1{_i1EY)q=zHg|31d8vz$mK%Qk$USwqQ+( zCj}_*42x)HG^FpascgkRh0AjP%|Noeq1nn%-HPH4I%6dO2{nz$oW(-sQT|LW%%8T! zXo8TR`qXXtW}*)N7`9&Jv6a~GC!k?-0X}G7AGTrlo;@}1WS;=-dLYFUI3Ue}JdP$b zz)6rXxni6ln?YqP`2XP6oH9ISR}^}^2dQkEna1W!^CX0)l$1L^F&o{7O6hUJ`qoXq z9(LW>Qyx0)?bV!yrWT{e6?y_tojh9Ni^%_>IC*HqQ__h-G3EUECq}X`F0wBec7Y5N z(89x!_@{xb6?MFY1d?^rrcM5O?g#pq64hhajw}8S-6YflTIeV%J3BBqvT72)GXu!P*HlPCEG4C%yiT|cN z4+$9mz~ldi^r6)0NQ&pYv4v7s|McQFw#~wNJfAFLrcfLIr%5bgWgWbDdUfiNFeuMcejdKHB^5GB zNaiTm%pT(AYf^;lk(q*mzs-n8=p@j9i`CWVm`y2n|aAF=1$6ciL5`jpib$Lo|i){si42|#fY${hdTP#cJPVKc*MF>!x& z)fP;`KFKNBwQCm<*c}7QqjS2wavbs{Lu2Dp=iErs!6Pi(%5|2rg-dU}hUVDLf-I>s zPsHcZP&Kr)n6{Q5M}a^XTJJ+n5KR-qD;9@lAkkvS-<2gBo#yxq0)QIzG1|Ln2 z9MQ}RpAR8^cd_GBDOaUUw;@vlZXLxBJy0BzJ}pW_TaJW_JURO8G5p07;0Gr5n6A17=Qm!qg++Dh+7uOK6_u0t9ZYjs zoSSpoNIwjTm+kyh$n{ocuHTz+$QLCOnwa;^)EM<;hy}Uo)#=7G<>J(`!=od65&Y?L zkAVZ}yn0rhi^vt&z?VTe+K8GNnKVsF*6b+F-EB!dI$KF0xt3TMVwH&P6J|OJ9vMtG>^@JbrdIRKIqogM%Cvnl(h#N%{Z+F}X!X6EpmRIrt00 zHcU8$)nheMl;J%fX&gkdx|aOR)vH&7BVJs@=ahrZCdTYbZ@Zv4Xu`NJx^9kQ9NyAV zm6Q9P^b}`|^e$f$P5`@)g+mHJ zgc2IP;6&^uY)%h=AT9?JDXFWAZJ=qz&`?u?Z|iiyojZ1f=PSf#nbgg3!GMjmjOuU+ zV)Gv>8(>(o+U8#k zlj#=@O1|9OR>h${!I+Cf=u|R)EIWvK1oMhVlK;W(698JQb+)NX1A#*Xht+koOA8Wf zG~H0A1m?Wwqs1(Hh-qcCb!!4~J(7PRy6H$F`x=&es&xpjAb^X1+WiEeDqaI`2t4ES zNZI{**WpRu!qeg5@)11!z7ofma&R<)@ppk~xvz(476}!h$PinpeNY?#xGoX-F}8ob z>;Z+tlP6Ee=0u`g(3`!3@9luUzsDh3gk@(^6mNu zfymn5Kd~7?xMrD=a95N~!E@8yMIyVfL*QqWz9df4LyIV(EYzLeQLEWq6sqSp2sNMr zi^pVltPMm@%>j9jv8gFX!qf`#Es#2ML&JCOhuR`H?;c2mq97F|F*Rs;)X;-PfSZ&) z&9!lk2`d~5$$da@V&mftE^p86L?^HLp3TO}>LjX6VUmA<`#+z2fAHPAlMpG=#ya(D z%a22*LZFZYqZrji4WUJvW8YYUV>G0W`#>@Zuje4_>3uz9XAA=bp~%>#`4RP;_}AJ` zN^(Blz98%Zr$5@lQY}ovXTTZ$Ux37EuG;4)AR^D6<%W;_BpzoWp5@}jiz5)vmA0l) z#Rt!gDBhFLoqH7B8~ys0=kTSox~cw-(1#G^0Q6xFQmWCFlwzg>w!5(%cf3E?XK%>R za@gCp@xQo?$d4br1;+&;262OOgk(|-A}=GFDupZ@l9LoP!U=EA!JnTU5LFNt2W7Dt z6c+Hvui1F?Ya-qtxMCr!NQTtK_s7y&P!^U021HpeT2fMC@njvc0v^>L|I>LRCoFT4 z@FFNAA4{f4*f1?--BEg*ZS%4Q|E{xBu(d74fH&T;Bbnxwqq=_yV7Ooqazz7=f?DCsDQT02|@kbmX&33Jp6b z2MU14xHJ_I-3TgCm0Lpi88Yy3X(;JoJkU2YTSCH$0JhlJ+9Gv4s`SMqNNP2xC6P7A zw0G~or?MMIk2v8I#bCW}dRDm;qV@-td6p11gxY&LVEVW?yA~6@x-a|{D1a${VvFq+$ zf#|NAH*XF;y$sAn104Z7lyvdH_YD^}(#MkFBCm%~J8|{q-L>Vom2IuNzMD?(E(lq9 ze1{y@tO7;bhh;pyt`0nX&&d;Wv!)uQP*oEfJjvKfpl_Xboo zY2x7_O{U6+Rsnti0WwHQ1_<7N4}hkvJ9hA(i|qt^9UK};#wRvH@`r%LkjM5o*Yq9M z0aE*4cdiqL&1j#&)B(Vnun}y+c1IWPo}c@pf}%~=SjNzvd60Ll`q&+crlFdtXi7gZ zAGdP!V6%@x^P00rH5tO3eee%gL(zbUiq_~WHv^yHjXOK9hAA)^6)>DA&?pkFsXBC8 z-rL8gDDV6kuF$veA(N;-ct8(ymqL9q7JzV$Mg??{w|QkDDsT&S)D{)(p z95+0C$kN~6PX<4DPNd8XA|Dc(aImVAFZkEkSzAY<6Lx_w2Gyh3d7d~?EG)nzb7=u= zXK-2AA3u0<(*rUnlIfiV++EM_XO3}ih75zz*Kwnyq!EIzM?=&Dpr{D{i~Kre7@gto z<03JCi769PQ_+_%k8=In4QLA$-wrFQiJZ%yu`-j}+PClBG+RB&GVJ8&18U~q*F)L> zDKrvd^mguiu~%~qenVn_4(LM5Vr6apBQrc76#<4hh=`Cmli12i4HTAxgM$>3)p_^s z-e+Z(FUz2e-{?c)1MU|GAT(1XNG~vrMTl_pFp!$Y9lOF;YQ61|H*kZ; ztB)ljIO>5BC7<~~SJ(X=Z>v7%GC8s~yzQrJ0saGp8=aQsxZa|UC};G~LPb~BARwT)eEfwlWp~bb z{kOaFSnS0ZmpS;PsofX4U%h$m0w5gai}NhC14CEe1f1UrK!lbG2bJ+nhh;%ysVZHfUOjPN$^#C30t<>HbMkP`Ext02eV*8GN)e)JG5aH3l$ zd7+TdhU_xo(m^zSDc&08_*oB6xago!K3>J|;cZ zPX@Eg1qEZU>*CF#o2k%Vg6`t+rf)_@p~_*u4@RwDPpt|d&n3wV4s#@*?hmBq*RpiB znq0oYwt1x3P8x?yGc2@}8PA7^@A&kM=rTyL zItlF--^GJSYG{P(u}F9)+)By_p1>3Fx~Vzq;|FaIg{@*WeLch=nf?~2(6^!z!-FN6 zOK<@h_M(i- zZ%^FnwTaQ^sU+G#GEp(|dU|5fpjT+i55gY!%6JH+J|Q_sa#k=@Q$+2vMnNIrlw@HJ zj4P*5)VzZqfW&{0qJhY7vnMdexe@QHcy0;lGqHwYB>s5c0wsw{Aa!po^Ub>4@WwAs zTX~l?oINal9O^a-+71loI{^b{P*0K@0v>=?!CWm5XpAJD74|X^C!&H%jtaMlb;fQO~erLoO+mKpttHy}_;CH|w;&qybQc`hz3 z&4$7YkcMkuoXyqx`qhANw2ypV2`n}e+GZ%=xKPge&5rilCN%H zkAR6Oqoc+Qo0aKw=pQWmZ+b7V=)tc5l8wAPpp%X0CXUZeHDYlhW;PHEjc?!PoRYS# zMn)^?T0x~e!cQijKOfv?9wxYAg&PVekd(#z_tybE#4gLYQM65~47(#yAYRq~_48*l z2s{$Sh>N?po)S)}Qh)L7i~86XO0@Dnx(`q((jj1v0~QPT&|Wkh1ny!K1$3cjXdnYV z<(FN#(tus%CQ_d$ddT4;O$+KJgw_=R^a)#)M`glROLRk@`|c8|NIQI92OwYN&u{-w zBGA1$SBZ)e8L;MQQK!3%k-`QqJN5m6T%sl(A#s0%67Xl zkw{Av`U#haJv7Zj5pA9}>`iw6av`94jQ)}&u#iUvMR#}p&8-L`Mqnh8#(Q~UIj^Ho zU7_Ttht7*6^`dp-7Zk)~F_8IJhY7X@N?8P8TmPNk^tGA=wu8Z#0)pEjY?ozq=$M38 zvpNc6ya0&}8?qKj9v@?Osj3&5I?=vq{NWX=O9=8__v$>lfQJvRM3LP7&Qmqn7=v!`FyvAEW>Wp-@oh zisLIFEhTzfJS84}epW55dSzqUUDCy5W#K>dCAVw#k4kYU*Ug=^q6(q14^9&2mC5$_g?%ZrRwE|I9 z1o=XjMJ5Q^Ieib0k#&}ziF*D<-x;FeNpgh>^TeCdNS{7E|%yaa1MKw zQinHFgoo+_Fr-Q5A5wKWDc|$zg=wXzS2h6bi!swEM%-_Ovix!Zfdw?anZaO0!c|}z zxB&uK1duJm`#bo#M}@>9QPc-25Qk6RE}|p!vFRhE44ga#(1m{({feIs8dHdpjnEK7 z2owvbaS+c^R$kt^Dd6w;<$PkdXdwNY8;OlZ>G!(e+LLSzAG&uZVxvin@P`>R6><2cxQGpwbxz)^fGQk0kHM$KON|N5Of7 zl^goq&)lk)sYL}DImI2gmV#}S9py+W^*lB$QziNMqtzihk{#(*j z%%(nI)uLX`-M1Pj+``1p1J_VkTf4h*22mZx5S1a7vg}_tXjcdr5!KFus2n2Gf?J-K zB>(^XX@WaIqgPX&+LfM>LGT|a`pFw1YZAFWz>;VzN$@@7qR-!7LZb=LE4f%-4CM;y zWg;6)vL%b%O@JvS(sN0Wl%}4bbK~5}kkU!EI(CZghN&#Tg5W4*f5AVPg zV-+Ri^#1*S(*b>by}ba|nFx}Bi^d~H^=45Vi3FxKTegT{*&|~CgC0aJfyhxfV?}Tb z=zJYLPzw}za5O)hphrb3v7v>nrb`M*=-y3 zmAk{2X0&m$92hzC!_O;FN(h=51CYZ}D-s>SP=&V^9=I5?UO~ao!Qu7v+QAbY=-MGh zBF`A=X3heKxLWw4q)Eh*iKp zjBbgKkkVNCZfVoaw|9m~mfm7JPVqnPrCF#MmynR=tvLz^=!%EjDh4*c)9PA)C|c>W zKX=|BBNTuHjA0`AoE;sAap1xgBl8DQCR}Lh$>|>uHckKg^6}#mc*J_%z9rxUR9qmy zp!oeV9p1mv^AhTd`+37q5WvunDK7j!3>35rxfcws)YRp8aeMzw;@^OLD?m9*gw*z2 zvS_!_^uwN~`r95r4q*vD_tvDHvWg?w=Djt1K!cO{havZETo`X@x}3~6x9{Q#T|bgZ zYXLe5-D9S=1=>Yj^p46}nZwR*SX;XPr0WF*O-{#DfTBZL7C!TgTsu(3r%#`D=NvwE z>=?9rh?C&Owu$|0o*bU?_s4x)732e4q-<9znEdgh6Pp39ncz8h?g(Of5~v4N5!gB_ zppn_x!sM2(Uj3+L1!fvZk8PP*fgScpV-pj$!b_L9bOjL^40~%jS8c2BK)q8o+bRlD z%g7Q@pKDm##hY!~bxKJX%AoBVUtP8ooBI5E=hcLC2okU-xdOES25F|2hq;Z=!Msye z1$%{O@2F4S_W(M!nV4NbH3CX!CRF3xX3(TW`iTOBU4j*WS{b|0X_QeUt0*3kWT&Mx~mY-<_8aMT%oi&>E>1DkUgt zzE4iNS=rEG%S29ZhKCM1d(uSjqjrlj(Owuw^{=9=$X%N|v)@6$0Vnyq1AXVd&qi8V znMYSw*E+EXr81uBeyhX_n7P5H{XdX!8UE5~9@FZ_C7hC((t@ z`W_7)|CyDYZ8k0c7EAN*xedIgMQC=gU8H)Debl5zH8Q*9XG|fay*Avj)JmPVZ;n+} z>Q}*>bT3sYNpypCb~#l0u23~7#yZ}qMxVd!`q;e0jb~@y6h2ntjc7^d) z^nOH5+!0K$d&~eqll`GTKXYJ8$u%EBUbmN0L|p8Nb!*nJ0OYhzY-(&|f{fkO$_8Aa z$<^*&2m^_JKrn{ZBB-~*Pjf7nVy{w0S3r)Pb-OF)f%>GO_Xj`nlXcgg9N`ejlX4qe zO7LgtT*Ut7)9qcCcUn+^PPz!5qOId^*t`Ae^9;5=R;_Y>k>?$UXtZE?D5|HW*Mi_46EPl5*aEtIXB^0Vw+*pa-;v^tI z+*=9z8|AV+dxWPTo%wy57CMx^Kco^4K2qNqTU`V=&0SgNJBf+5drJ)E{s3NZI5VU2~psn}tTrfEm#qa;Z)XBKM-oj|glZ;dc z{|*>>lG4m_f!)9%iy?<%0&S(I6*Y5XpMR7;d&AifDJM+#v+8a$7(zJQ=xO1V9T)_t z)?}LOVesDJ;LuXYlQM-`kzNz%2UWu>>n!?aNO+ndTS95*K0B3a)56lw`PQrR8wIDD zt?~k+kJ_zB$P)SwC;%N+3VeKJo_9)?80>i@tqIs=swS8bW@S5c z%8ManPPtR{0w?%cKvV)}8GWTWf~3)bJt(|lgH#Rr%B?68P=!9m=yxSNEjTSKgkWnF zW6fYO;RpC*ow!z3c5!+6-v^nrtKC3M470W{1hTraEzl-GmS)c{fJ89^(CGYba)|K( zPUygo>%!b9tgg1t9^R1ed-Vm<3F)B^fsgqsE*ZD(#_dI&-@;e~J$Q+;?WVBbH`Uc% zr5?d48`Q~S6YOdzgBLyp`?FV9$uhh(f_)Ymz_x5%*Rm4lp@7xE1Sr}}`LoBX(~0t3 zmb#G_^(P!IR24x9R*;BIxJw8GF^1_Y6D$S}E$5Z{(zAaddb7#BR`3&2(bU^+rOJme zAH1K=*2fj0FuApsl-piiY8}s>Jw{7v&!3*^C24@O3F$_v>-6PcsN~-MgkFpICR`P< z%R6?RrM0ZmJB@2tgVQrgiG(w|oUmU&(lC}@zj)D)~VQIP+63DdAO zV?>g!4~h74&Db6veCVQPwo`uhn?*exxj-ZC>8otVp>=X}-d03h^&zWN0k~=b>P{(j zu8T)rF9x@uARAU-d=yTpBYl}yGan#v*fH^NK4jz-Y*h=OF9kWxXusNrj2VYY+xi;_ zFQ%$w0RADT78y%OHp$7$KOn&xD-$v^SD>iGioX#u4APmeGzVpj+TNa@kv@jI+@Quh z?%|VoCS&A%D3;+9E3Uh$Zj**2V}qA7?^?-P!c_9A< z3X@BNnQqo+vGw7&-AR*L9=t^WoLJB2|AxR*c~e$??F)AU%mxqrSZcZZ#u$u;o8t1e zVBw*UGglGBl0o8mj3u1LSsF$?gs1cF6bg!!uWE6BL`i|e|g+b5EQjv675LIl7gT37&}U@u<0kVGS zvhoB#!bmbz$v~j7(ub`^@kan?IU-0nzMQm>ki9F$n*sS>jG_oL2Vxn-DeSYN?qch+ zh|lvp2LYhrp?Zn($SWm9l0F9zM#LH-DWiFDCH0Cp) z-g*gBw@58*oJ@4+w$#gNa?2lmEF=7uLs!(4cL_dyG;}{m=VDcaNCB@pH!Q@$3xQkGne4iX%k>#BZD^JPNXBoW5SrnjK&2kRX0Nn)x-K{J6a9s^9e z$sWT7;7kLaG>&G%Vv406C37c^$3W$9Q6OR~KJ(ajfuoF*fONf@ADG^KiPeQ0XE~rk z0!O8|n71TG@v%C3Lhz%v#N|m)c85KNhCL5s(N4<4w~PYr2C?v*?KE?IFx{aTt$^9p zC|$37phW6!HPC!4H88qLQWZMv-5t!*Xx#-M+ssumkoNI3M6cF*AOi>9%CKwVL1MN6 zUhs0xk?dQ{dCpBZ32H3#v^T|#=)!1Wz@MkMDps^>7}WZ7?$7G%2JrsrVp=JU&1J@u zt9En5;*q%9>rkfvhsDmIrnWPCQw9AviY~@=LDFmDz(nl6Hp2u*0dM812(6^D#)n(d zl~=D^Ngi3Jj;;sTf4MAuDbCNGzpj6j!t+!o<<8-cFFAnH;E|xxR*a3up<(8etaDIT zG8~^Q=k+3|i(ze-YO;r{L8jxQGF=44db+D9IBX(I)Ti#+{qMSd8PkqN(UdzaKX&hc zQ~>Ee`M&3-nen7-Kh^lSTLFph1 zem9P#OQXOdCK$Z&h#6Q{=@A;WEbrTr6g%j&0U&1LL_o|`W2Pg*71y3Y7QyZvRa%n+;qoHP(7+Ed?Nr-cXuWh&ar=_e}6ad8|62mn_=&hl}i~eGRH9VTFsp7|AcnyE(#UJ#p4F!c(_IF++I0~ zKGvYyB{pr!!3RK)0Q4QZ5@tVovvOHH4z$S(9ewO>r*WoXLZK_5S*$3@WJozCWzz~p zC~rkHP)G;wiM`#gd>HwcD0J(?vIbbMT}VqyQ!mTk@9usuPD9eV2xz01bmP?b?}NL? z35l4W*GIr2zgx=w>1Edc zG%lcqM-hTW=+ss6oZFXrL9buNKMZ)Nk<>;!>1iM2%%JeX$Rjs%I4UnQ^Re5O@h1H3 z4jiZFv&Z^YGk#Q{eQC)w-2pmr;D3TfO{5~*;DrH*g)AQ1->a+WtVdp`z=cP0b!6o; z^7GF=cwiuh#gfBxYjf`v{ZV!Sx-A^C48@T2oSZh9Z23_HHg_25qwhDy$tDRHwXb@6 z6+2pGL1>0`IDNqSOn==EogQ*Z$|AA5`@byU_H?G^+^eg@!B%UU?4eb~hV`uQrhaz< ziYe?`HkKJ~xj3CgJt#TZAGnz5tvVoPra{NeuUa3!#Fncjj9JXaW;WS60i_EbK+wU1 z$s;_M(474B!scUk@qxrr=Y=YF(*Zl%Kii`EpWI8UsGDp&HVa$^+Uyq2i_&}^y54*ZD7Flg@DE)x_OxsFy+(0{G^?mqbkx8>y>^0mh7_U%lf>uaf~qzpx-b8X~>B}wVw5;=?|hGS6jSHQLAL7N|c zNus?h(E3}eU&fBEBz^Tg)ciflLZ(qlZHpADv?NtC*y`x?=urye?N8}8YoCgHYt62@ zGBr&9;;MCVrtZNjhnoajBV=?ltbL209I8;1mmd-yGjiug@l#YI(IS)maTj!C*g)i( zah3@S=>t#QZV}b{w^wVdp3;7P1w#-t)%|;o!2w+fj z{&X}=K}%~_d?3-hJ%66ENW#x&_Ha~xLs;@15gJfSXPsncj8Rjys4`AIXyJUir{_qv2E?_UTHvj!EQA`>8kBCLqMTSUSCzH1O(4 z;iIJ5Qg>mzMCIh<@LP7G1=}#n#4D5z@-Y*vdrTbpPH^f2&Qh zW=q9tFNoJLG955$u%sihik}O%Y`zo~bRVfhIzHH8J3gw=H=*ZbD1MP?t5hm6Q;Qhc zD@3K%^i@Mjy!kv}ws_BLY&1*oNyps)y%u-iYcZYSx#h;w4)9rRroG8(1E9f13 zbP!DZE4V6p_pj|s-S3KPR9)P-A~p6&wJ=S{;}zTckevAc##hh{A}4$!o~yjbg{2{~ zMBEQztjwW^S7_0MDwCoW%d%R2WXtfT=mM2*`+hKjR)*uR0qnDhokqZW)dq$`RR06@ zWKEtS2k`aS`c1ZGxaA{{h~L~l)n8!6ic3XJ-=D33aA9y{q&f1h z*s))$oNd!9nf`A3R(D$JT&JNyfW(wDeTA{!=QFDWo@Y($WH-JUA+U&-m9;3A`b~o; zR2<>9(HVKYQ}2sC!^2sH9y4?FRy;$hO-PrT+L@txpWHj!U+2&|&TwrUzW`-5$%4^6 zgGX0?$d?()U+5-k9trc7>uarsHdEJFivU@&>(^_|YU7{Q+!h;uNC~18wX=+;R#jE; z4`Z6842i5&&1K8gijI9^BO;y0{bUDOV?qp5aUMN)SUb~gm(|oH_G?g+5FNU>ZzmVQ zq{^!KQcUF)70(T&yP*UCl#gOu=7p#sju7RR?_lEGznYqP{rY=*eqzZ1{h+uQnY#$O zK4o`pKL*u%^soT%yT*ngvOMR(*ulsLaEU(zAOWBQwdGS$N|~(bWlVZNFQAn~Xnf*r zTBk|x*e-8wad;3!L@iI}s#0RmD8TUjA8)M=1>+NI1;22_1D=jTNgpXucw6Py7F_?ooeoNJY9$A&=fZTnq{H^T&`j zvIPY>3+{Lbo}60#SwQ^RwmppX4tKQhQdVo3b}QBxr)+=S(?boyR-%%!NQJ4QHi32jJ6SR1m`C_7Yox(vo@e^2NNd5-+?#QCG+&5@P(KmE@SB^1?68I-&apH-D zj*cjnIG9{VMOWLb$D4xA=H#_&*T7=|S;rQHGW%iBeY0nCcuu52FV@G{8BLO46g7KL z>KA=y4brd0so=5b6fffV6caIX&@mtZXFjIHTQaahkp-o=8L#Wgl`DgqTc_=8Z5Lt8 zc-h^@A4J$)`_TT#CIBA})p3f4NGVDy?8^a9ul?@je%aBp{$Y(Kj)O)&PAKXG!&+;1 zj)KGxai?+szavt9`}Q#r#PSrUNpXUWr2j+rc!K+fWLtjp^zmtPlODgYA!45e%XmD< zRnHx3!D&mrf>LJ4(~q9XRwwsDuhtbYSi9E5A-R=0jp3$kDmq@IKf2q7u7(GrvjZJ8 zZ;B~KnzHfifzCMEn}SJ!qvT_70ysInx*hSrY5RdQ^QL?Rg!QAt3V(6v79<%J((ry5 z(dSKCd*N8eVc#_q8e-%phbq@&Jf~7Jv!S7plyv^H9DhmvOHy^^i8rPgsJIyX>M7vs z%R*a~rbj+VT7Pk)6c2uGg=Ymh#zt~yc(?HNC4-7zaYgjz?lAJxPi$bu zQ02g%kl*1K*T(0ozW@JMuy;~|eA0hczyVfGnS*@QBcHvJ0P+@o#Wc!{kx#V)AS<=- zJ*IeXF3a(8EeT~atM0b)tAY= z#p?1{Jz0s{rb2xTqY;(+$(jw9ckj3a^qx@0aOh>Lz|eUS7x7l~)gF8T0>m&o)^w;i zUVB9{@|!M(iY1_`aq*OS_Q4$s5ccp8MLP)zR!RdYWM}PcYzSgv9<`nRB4L}uG8-JL z$pldh=^05muPu*Cjs?HbnR%ZU3|n!cZXQPdyDk4_xIq!Sc(oWR^yR`Kb9?tXKvt2E zGz--=%E9}K*jjR`Hx}Z7dLHT8G(Z(SuV24Irk)A(eo4A{IR9xi=nF$|7nmUS;sToF zE^!_e|2$4j6h5wl2Obu=z1E}S8kI4#$a$eXk4s82#);kug@@3=Vzq@giHOjb8gTu5 zD`nX1--%N!4*n;mtdM=_VyQC`%|8rH(<9__p>Fi)tu99o1Qs1{YpFpkE$Z8R@^&Ce z08^>)<+uI&vdQ9iZ|LjjC_Cyu!D!+37cX|k?`p+EgfXuratF|X8aQ0`#+NVDOVR}p z&sry9xj!GYKBaWkek{eHRieOWqY?nQ=Z8VR{(a$?J_x8r%30%j8%X9CL3EY~yUS#wz~RzTro%T{#TT&j8@EcT9iK)sUT({-(zVXIBFHU@6qeo zj%#5=OSSd+;478NAF%bgKw4RSM4dNF3T2~LJ;w+sEV)cGs(w?CbItC@ATJ^CXCR2R ziF7wUw3E_Jta}jN6l;-=I*5OuV5!9K_`4i1T|pUr=b0cQ75i>cfz8$Z$ZJ zIm)+<57Genrd7V=Yn&&fBvq#-EMr85(D+=q-DY%0%b{NXSzDVMezbzDk{3)a>BBVq z{4qIZrel?z^}j{f0JGH7qiTty_<&{mcmF&U`y?440$vPfGtS?Nx7`W#Lvnup zv&iI9hb$IZmIk@?rk*J2>A#ot;aDZ$bf_0vX!_D^*DH*Sii1D9MfnTE!9zcPt`hM7 zr-C%+^DME8k{fK2O9|YnW+j?mkYj?zj)MWli(K%Uw(>GGceko@D3E6W<^`ha&}$mN z6}pbnh#y0bct#3`n~C4VncduoRo$)i_#-Q8@*w;ESYM>y8 z%|W+hu;PS~dpz*zT)rb&Y!3c0ZHI24IbTxnzKSik?MBLWq;;4DJ^!z#x}Z1^L8e-J z;;&6{PiUmGCaqIB2__boA0;ms%_{ng__Y-CAc`?XNmYz)4w#9dZt-^+jslUW(h|xL z#+b&-Rr9`@FZD4j2}%oJw#t9oa(0ScJNtO5$)yxm4}y~E>T59pHA69}F-s;M>RR3`eqg0o z@Pv+wVpZ!rppF+w-~$wYW5YPj+7RU$ruq?nJ(YnbsXC1@)%IdBfx;1uRa{?WOFP-V^2jFf3UK^^EB)O}gSh zbILpvL$|O2sxl+67tP%by9AWW1bCX6$LMj)o(Z7ZevbI}wEqM}Z*7o4t@Vx_;vH2R z!Xisg;6|+Pl@py0qF8}!Sj2!Vm3%P z5&p0vM+=A<0{mdjYAL*8kMtiL_6K=Zxww(Ls(aQb+5FnBmt4TY2Q5$0$~IW(AZ2<4 zdMSOrUcYKlL(?%+PwY}81+2+lSj%6rL&1tZzf=voFnaFqfG$Yv0f1q&8|1H>?4c4Y zI)5RI91Zce~HTF^^C8tq-yhLB?&+({bgk?YpsV?Wy#TEfWdj^RW7f-?pxyBp>hoENwX7)ucjgD?xC;EbUY75Dr{aF{MxbK!YC ztsZb?BCdE=BcR_gJ`m?^o8GeKEisdesK66DmW7`LsOsgs$)7>zK*?#(FVKco%r#OR z(F5DfsQ-O^d_X7aaA@h;@c41gRkpg9lQN7d`_f z&qsI;-k7welUy6`bJ%GC{!sVtMem9HSqz@D#uCTX$E^Y$Tcr)*9z8jF_d#xt|-q(VlNZCa4tF4<^~)ka5>JuG)6VZ z2vg4TmJPFh`F4gcB>0>UA_9=zOf8KUaZem8V(t6yt=NVGBmon zwoPUGrRSKG0wAsp!L(p-0%pmaTywnU#AUX=s2K?-b4$z2k*`Of#vlj_NJPM3cz{>} z!4~Y|5|^~CxOp?#Bd!t%wO2;a+CC6YUqfX`cocHi;aze4`z7ay0~85|KqP?c@1KjMrZ24E z%YCp#Ld~>2qiQu4n4;b3u{MwYZs!W+sD3Ftu8-p`)XOev_`TsLEKR%_u+WP)cbq9v z*B)TvElI%F{vf!PpE$!>iw@t-akSOlOZ2BY~eXgC`s}Wl$WafgNXt->C@@ zGTq_OW3u66z)wt1gNQ_>;pn=p2{|p$LI1a>1b%s=A$V#^Vwv8DKH;fL6z?nr##)JJ zTnc6z$*RYtc&Q6}`LiK5i4$^?1#U7SAwk`zz!L}h=gR@M_TWN1;DRD6~eq2n!-e@mQPgg0_m!0y1B$*MlmH zhL?e;YY>cudB`60cVArPeV@)}EY&SxDW#~X;aNcwME=<%*Q)V6#WXh1J_YdJ6K;Y? z?a`+NUze$a$;A+fkto~20(6KoGF>Sl)6hIkb;o^m!1fAXgX{veKI6Xg16QqL+ia!Z z+YIuslRs1rBcaF3?zBW7dNCEyIj8|hRPK$G`d*{HhZCV1Ln-Ym#&@Gvh291t(%K_m zS~0W!A5q@{&GrAb|B+}>W*J2!qg1wrjEu}kOQo_>Mr4H|L_%b=C_9y;p`nP7T_mEC z85L4S5t;w%_WeEQf6jBx?>T*o&*wev*L`2tbzhgh{(J?22=KG)?3bU?Q2=^A7#Z?fm$K3<zBI1HrH%!c=oqK8P)RKg&B$K zPym5zq9&j80LW+i(%-+X)C{)F!Gg+85LH0n5v=MvHt_kfVNpP^LN^OORWbZZU}^;V z3EWzHM+X!dCitL`9zuNsJFKovc;%ydym0KyOc>f{A@@%M;{(MrQaT_lIpm~}rqTA= zCt4NK3;oAe(vp&d!2nmKreBhNpqiVLlY$8vms{!}eE4qWWUYB)WLB3C?i?!wTd(zg zz*-5|_jP*gcJHd0=6(-%hlo=1+RarM$bfqk;X=XhT>%%|xh)YFy!7HXGx4A(+YLr_ z`B0W&7>BR`rStw`^)dJ7BJ5*Ej8n1lb?2b_uqzXN!zGk<3W#r=bZsn{hEFhj@)L&cEcW-+6c`d4s-06 zRg;2;R8;Si9TWs!p2#$XSQkbCxR0D$+QES1Hock0;K#RNS{~=U!WVIQ5Re7qHZNTG zy|;W7Ci8R9?L!-ws=WYSD33rbqp8$5+K_RX$GQ@4IZThCEQ5$g9ZRDqyny5&s@}^} zcU7A4js4iW8c3)X;Q@Jm;@34E>#Xc-of#$O7HSF&b@^cT0uw!GX4A~z;(Q6E8e4XD zyMshp=FgiK0C0uJItTw!f%|RDs>x#yd~dUEj>P9o&&YTML?Mme7+3}cLcdBkadga7 zu(m`0gwrFe;fdW2$VK{V8Y=7RxL^PRu_L-9Xv1wSbr0QdH3$@{5lzQvjkCB)qM z^C5oKy~4}P$Pv#M5jFiSD=jV_%(6WN&4qh^>w#;7SmUD`ugIwDzo}6JlwpjdAQ(IG zO2>(diL~Upbh45(k!S5{iH(oDaKcsb7_m< zYox2I8)d5kH4G373&_q9@pkQ_NYfL$Vq>Wg-R;B){F_n%`IynmTE`AOKdIF`S_=DX z^deU=9qqrZv5qCGOeN+e{8YDG%OV#O>;pl(jkO=WYoQ&`=)JptU=zeQJn#vajmq?& z02>3@7Ce_FP~$16h*waEL~8BiaiiPgPzy9*woBsn?1Wz2o|6816K@6kgmv5|_L z_p7!lKt$`LS88rl;Y!?m9z@EF*s&}JGD=QtT_N%lHw4N97Z^q1=Htnr{Q}4zvEq#f zZ*lOD)DHYiBSu5j${s=p7YeCJR(sLF(V(?5O%Ouxy7=Rp;5P${Jm^it@}ZWp6QGnR zRgFC<2lG#gAbb4UAhtvO-;74TY#IJDi0>!${LRh*t&vWsgE4|b-vm(t0giZdNT2rl zbGBPI{zy%w^bNDaH?5tGQyLfyN(1- z4UeSo%^yB}df0Mj8XEnKi}wxN+@W3lV#W(&ftmhZ`@dR^l_6AB^5uLLrJA7KQrti8 z^p1tmx=l16S9Zp~Ab0r}hrS;x-YYGJzY@$eNg0jiXJx~)qKPSUUs4wDYy1kc4iG5P zY(4KA#wLuu4^I@Yz0s$)G5R2OT}`fi`+)1c;rfgDE5*cgA4lL^z-TK(TJksV-*aOl z2nQj$FVlRxt+^BjY$t96Pn=T74bdY!)2iP*@j)-}^g?x6H~lfkcZdJ!@G*2QEUj#8 z-t`@pMHEnEB^@E4*x}i&CH53I0kR1_w~1}~`Y326SR{k<0p1*3-Ss}BG7B`v(pWh5 zClzG9WIw6&WALR)ZT@S}OwePXltLs7$5U)0WA{Hyp(z#^0SpC-P{*ENdqArCeKj@c(RG(Y zaVQ~$1=|mek5;PPSS~6B;x-wB33@h!-x?gc6OQ;X?465Od%@s^R_*ZsuMt$w7uU4c z4HPQQi&0%EEZ1zu`O*0o(edG_$zecfGfkk0)*zZ zM-^zA;IW3^Z@WX^Yz8U%I;t<=Wh8WHc@UJS82Yw_Wr;~hXx^wr%Ohu1GWSlai3+S^ z@jbA>a@&x-K>}dIVneWTgHe>B^9U)|!7QSIgKWbBKesOY zc1Fg0xCGc(>b8Y3>c{sd1Rvyt2hW{zdd?69hC-*C0xh~}xCBaNAS?T;|5pC#0V9_| zlo}zSg&3jIZr^?^Gym()pKJgz$leKP92N+{@8(Sw`~p+r;IIH#4sCt?|BW?J`$VFE z!~+s1mALzJQ+5qs4s8^;u-<5KIqQMV*ct$OI~yqcjmVYDmoq?tz{Cngi3Qy~t>j@@ z4cDDk^3I(r|0U6a{k-Wv?u}Tl!S|ki?*$@z^~Ocy1@-r@(}3EZy?g(uMfiZ+q%Q@b zHp=Hw1$sPSEiiHd0D{Ji+GMO`VfH4f^rGoJtOqS$z8uPC;3msqE1m|h8eIFhY7V==Y^?tN=XDZ=qZY7;)WI)L zo;;yc>4#XGU#wGui%(mTz%2=s_-_-HoW$Ukfj$JRn;n*T{m;t8%5L6rT*b}U<&TM&6itt zw`U3n@V1OW+HIyKC$^sbv;0}th}6Gh&1Wr~$0&|-sJjg(l=ibr4&opo)*W$mE?;IX zkuwRz^89iMi8(;VLGr^!4&jS$y%$cs3xTK${eq8rByL%&VIxpgUWi~J$$BnoXW zYgl+W9wAyIA0D23KfP!7ZdTw~Q1h9;^x?;_rh)KX^5?@`-SoyY%VqijOq_1O&3r~V zeX%#`t}ZtAXdmCMY(-Fe*I^q;3FqN=Cmr4~wM+j{b?w^fou&-f=oNIRX>mR8@e;zl z35J+lWcQ0mAb%$2aMhWuh@Xw`#RT1Pz>{zd3#c%lBI5wSYOE+i(3P){luSR^rocVP z3#lzgg`(o(PU8pfy6~bZD)N#)V?~8J7G$xAf>kVk2sLRic>48r3JiH_wrobb1nA{! za3&W%LIBQpg-$(P-9qg3nDNO4t!CH{w^)HJC0X-%+wcd<9z= zT^0(U&<>nx($0eIZAQUUdU3IW2tY0bi~b@g#^fmgM{zvU#X9k9udyKvIV?=2CX))L z#y@`T?C7B2Bp}UrY};Xxli~Ck=v*z-_GtZ%r0H0R3< zfeH716vm~DMlkVhfK|?7fdV0HI$?uYv6x30I5^JCmh#deK+et(lJNT?o5NiL?XC;9 zbQ->XgwZ2iS!@*n%niWUcLPs35SOoITTPX@P%Dq(eAkH-@*RKBmgmh4)bqJ}4;N+v z4naXOUjgND=le5HY>vRE0BsLWE&6rJx|hi_Xyb{W=uk0%Q9j$^U^WpJ59g%be2wyZ zo0ty7y486GCSusZ<{xZXU`M3Xy^0KX2$TCT{`AqKEqGIS8k50>7DJAVj%o`&CoBho z@Gmg*R9(@;J9T}ml#~=8*-`}B^!L}Whje>lVtbdlS6%_g9ip^k{BtFWumVsBqYo@L z1;SjU`MHZ`?#+bL`;E5TvX;M74ExqJGudy-Tjow6OGBEC|FwvwQg;s{mC4!)7XoAF~O`P6;+JfVLWY*Mj)Wd2o*qRW>Lge7OG;W@;9$q zH6KnwM_ej6Oqojf9%($#XmRMnOY(r8L|6gd5g5WWl!60^5&>{mf$w0NRfj|?jjsl6 zC3|b~ruFNS@xjUC=YI?K3U+ltZ5OzE0^kh+l``#Tk{unI96)DeYD~7JfF!=4hlQF# zaWre|ViXyAUz8W~(hs7Jy&)ENOl(p@B27TTMEU%SA^~^C`i1Sh-xG(9!Py!kSRm=e ztgmhKSS9Gr3%dr2K*eFS$>RrgIq05xezn0gVLqS-G*H~YQ8Y1&;`Jqol@o^+t1TY4 z>|;%-XrUQY_rzAN6j4Fxp1lBTjv%rK<*z!l1r}bBG?xy@KTAGa5$=vEC#co1tJ51+i%4-Cx+P&GAF?Y#aXa% z(i84Zf{>AXlZ|sft$JO?^2|=_F!=D`ub$TocCEQ+*K8bkdYL+xe`1wd&kl^kbY0;{ z@#9ArE|@j^`=p~@0sLU26)6>sY?KppHGMBMJY8Hg;CA(Y&P~`2MdxRm70OcvUj)2l z+Ncvu43Or}u6k^Oj)uIO@Rp2^dCv5!-x{#WiIu#{S9c4s?1Ve9YUZAnyoyROpA+Tv zVESRB@5K3PhBWH{n}hn;_s-5h#F))sm9i+33o#7iE?WoiK$A8fb5&iH+i`fHBX({! z2*Kg0BbfZjcmzcbk(ONeCpxfQy}EgYX%<9KIb2cQ z!RvUt$OlFH{tDyi!wwGTsqwMi>U}HYHTrcNp;*y0TP_zQ%w(AV(Qov$Q{4P|--#(6 zhTDgLA&8U~kb4_i@SFTQ&{U8Y5v*I{SWF)RqsCma?HX%7hK-{wxr)dl@+~i@K^4aH zLN1oIz$>WPQZ#1f?712d!A=zs-vsyLMrI}(P>^!CE|L*8AfH*F7d$8A(KfBv#qEJV zQ|ekJ13x}81kaHD^(iZ`dISH^@e^v(1CuY`yU$mj$9TI|>m<73jdujx(K*6RHG(~N zKKXjiNSYpa@7ufA5|)F0jmc;g$!&;lgR2^3s~kfc-`>EY+R;7B0r4j8zrj zfXm3A8&kyePz~GC^&I3b8D0QvntY%|c;CV`0gvT5G?_a)_rX;i51(mh?zvARFNJ$7 zI0hM?A1+RF1_whuF)*{W)v*{8aUqgksQwbmfx4jsSOi+z7%LSNd^i@*Vc-%G07=pC zbt#Rj{Bcqql+?``Cp^r_n#`;cR&#M5m>CM4#{R%licJ-+9Nv!)1yc} z+8`mC;0mWk^8SX4DMsS~KEsXgaqzGStD0xXe(UO@d_(Dl{B_aelg#e5$%kf}l@u1Zjo`YH`itJmS$#4<av|^lwZ_X2@7kMJ4_dkKWP-7M07rBFckorguvmp}hKOUy7V@=xE7)gq zw2LA3x<2{imaUOPp+6kp{{}W7#@>*5&cYJ(^kz$0E)JG0;*8#hZuq>O$wQ4m))ZK! znnv8gR5~*vR=jgp(zuedaLzFI=(3Ic?_i;FGrqnT4xBbFJU_`<4Ol*TnjS%UzUWl9 z$37NGJNmh@3ICb?Tewt2WiXar%F;02+VZShixpXK=fAzVYM^`{zL3c;Rr0u-1WmHac;cidU4Li6np?$^s&Vw7oexWz+jZni)&(yiqg!$i7i+ba@~u zLox2hO~H>~pWoftA+cE35+1KO5CYJ;%kjf%RS9i=SyKK$2Pi-Tlngl*lSQd% z`avv_!D~8z4C|rC2c|?$U=Ue?G6I9S-NeKn!ab7xgMme7^B>4TKHD(qjzDN$AvoRi zf}5!3@K!9u7)NeX|3g{;7{L`0P*M=&EtpwAxUhnVCzJ^%9RU*vfk7oepNSQXqKLpB zum$0vzy`1|6e$gff{fJshql!MJpx1MNnil9D_427-ta0Wg>wt9F8x@(>G>fhuPaYQ zHTUJX-gK`%j){hqjnAK3;(1{}CRKVo`3#!S2s90PH?7c00F<<}7SI74+gPr-fS*gi z0Y+r>^3b5sx5wrJgcy)ylG=pCe|ReCtHXP{vtTv)Q7XA$vA6rV;U>@<2HU|+Q@%JX zP&~;I3<@`b&i*%w{s*d&&IvlP|JL)Ep*bKps_eG0T*EC6OUJY(=3bS{`PT#Lwbx4vmV;ySD2ua48^-uh&9j*mwRxH z-*L;(GC)k;^3`m^YYOu)eq8RWULY3_?LiY-i~wjY$N~)JGSt@y4&Wl>zm#h%@be>g zr7($ZcmdY;dKMLI_MN^LFam4|%q{wmcDOpxxDAmWiY|!CW?iG5xR#Wwz%Hmn5xj;W zHi%CRA-ieCTey7ae&>*nctFLw#90IP)=sNKYZHK(!0X{b8(p;d#i)s}Ax3qh3rY5C z>N-$m1)ykJ;bEuH4enG|Tz(+uYj!5%aW3RlkLm^VHK)F2T`*wh~< zYcFg^DBHgUgsHH)a`$m|cXc)6 z95-inSB8U5@DtZ>`aLsiV|hb$2BzE6tXA!NYJYaHv%E8_Tu2jWZyd*EDLik0Q3(Bk z0UH>o4(xwFTd{fbvVHeHq_eMAY}dd2hzqv~pw?KEbw+;v8)2rHKgz8FSH@5HCu zw~M9(3(Y@-vsD5f1tY2B<@J1LCoX3(rXLsBHZHF8r@KQzw;0^8X1rjtJ}on1wl;rp zf*phTE8G|I&qHDCfHfD)5V!wy?&NlFnFntZ;7&A3LYJ8gyHc>*>}UtrAlrj|nClVK z^#Hw+PLIt4pRZ|SBgSpjZruQtLE0&|Zy4?PRDIS1d`0Za6X)$CVJ}3<&*Cj}`->ws zCV1c|Tool0ScNQ-y(!8<_oR)TT_~7|FQF2Ng{aE^#_rC-4v{FH7YK+zC=`QD0b;S> zKadmbIbQO+eW_R3bEgP8f=?d#{sEgue@tZrVV{7QW>7+-6WX2t!h*ppb|N0K6ra=U zbb~w^Prd~OyAPP=XjAqYLh^8-$)U>o&v(<8Wd?q}OE&<4p+K`kr~d&yWHAg&yl;tdOuXee zgfYbTLJw$N!Fb1~e);~ih3~;qAuLM;?}h|?rNNbU9PCj}j&QpvL(d`-WRc=t0+a}D z$A##&YHu1W5!bSU3l~gl2^u*5`>P&0Gcvs-PFPRx%V`bPk*3c>cu^@Df`OxKDbU?# z1rCeg{fUs=T8Man`U?0;sf@`(nMcGgF&6SQDuJ~BfGK>e$(t?x3yRkOa#@kGhs^L` zOapJceHFU=PbVcWVmwTGx#`pF=E5j8h-*+62 zXXZt}gKxL9as6U|OR=$hfH9b%b47<@b>{Rr<9FZwHdXyLx~4!V7(D6}}- zV(6m9GWR{5k4{<(pe?s+lFg*g0BAnA#*#;^nV4L#6yw9{d(i0~U#hLcA_*FzoxAJ zat4b&z*h?q|Gk+a{QDqR!ni*VYytiv@|`Ip%r;tp?4BvQSO&)jKy5h@9Wp?NK@9Ay z+EJ~^_ZhzxghBP!hZ@Rzi7bKK>Hj9avu9z6%fR7acp(}F0v5nOM`5o4HnUL>qz<0!S=m$O0Tl;^BqHE+ zJxKwf^1EV1tg7$EkBUm7C>_v8f}bPCn!J^fihKtB%uN0iJ6aLyhtO$j1MhHBbLleD z=|ssbL*JE!@(d|d@VuM_pi)S6@Y z#-u#O&%!Wr(Y2)sIj(}!Si&(&+oT{Jx%n?2LP{8zGsQ~^5iLswpG)B{1qmB)Y;AXU z1qoN{z-=Q%c=`cCXn}r3?|cd&2ZCsh=|s9{ZGrV&@}<6llOFx2oQPj1B_mP>ZOd~I zT_|L8@I4>v+OT;ua+#l}eia^MXB1iRtysKfpO_77V8r%iJ5~xziXgpqHB75lJlS_| zdGVe5_ZK6FjRS>wF~|TO0Da($LYvZpMrLRL{)65V=eC1Y@f&u~ z!AHX?e3XOdh%wjMKkYzm;sB!aUFqLB4E(fh75&JGvfdGGW;>DA|Glw_!G6*hRNPjZ z<6sgBN%mweaHKSh*9-ZCW?~F-J%`oxIn4dPLvxtsiX_(_1%ZdNep@^7diF;jG#9}rr;)R zm?y>m{xO3`E$$M#|IXH9mVB#UBGy={pMmmyZQ=k1nx^w;u>OSR1hjqzpU#EHo)>kE z1NCAtR1YdTP@W@Kn-Bj#y=#;lY6ad|6sAvr-3HZd{Uv@t$V^vM{J%&%Y}@MXy#Y7= z`0}~IvMZMO+ly0P&cAe6G| zYtBi9-02lEQp#sjZY?h#KyBoFo97|2%L;Z`jvt0Ip&f!_wjr)g!`Ey@96sc_FV$;m zW>!(d%#Yjz`;)vD91C8{*plIL-NCskxSirV&H+TCAX&QthaF&=R^ZT-5IXf!mTaN= zrYRZtE?MVGQ?eQbWJh1TSj?Q1h+gqLYOc-`hmUVc$nmH2S0A$Wf6n@EAc z++pHI*FrFqgy94mR8NS^HEf-=~ zfm0w0!4fFvVU_Q=@o44!e!ZF?VCtJ77`pS@^CcR$qpf*4m#hK)E(6c;TsB7bm`00o zHxekBEQ;^xBveSMj{lU$0|CcJ#1-DNcZJRC$#E-NR9ytiwX+`eKZbqCk9bn6#Jcwa zyQBPgwB!^eX?mZ-?EwsU245{$r@J8=)S3@cpPLz#pR#AAZ{HtynnzvS!Jv!_>~3CFnWGsQ81(QSw9cK~@DdRLUyM{6LUM4Wh*nmkdCgQuhpl0-gYP#pF+saWY5; z-ZgOxdG^BFwz+QGh8Vy@D?~&B1cmr_to7d!Ja&~I=s3&6gyEIeV-$iHC)MSf3YvnF zufnQ-g>bGQvwCD5av}Y-qs-OL|C1 za!Lq{isrAy$@2v}4fu9Z7-Xe#H|z`q6G8(#(j?Ayv@UgXb$wMZHN4KQd<41R%VE1E z$j{$&P;=@H8@RhUh56y(KXAg8(FV@a{e)(o`s=9uRTlPt!2Y%Z3dL5EY7q;^PF(HqyV7UD6f@)IB;?g(c@RaP)R)TyQ? z^V!}ba<{--`M_+m?t(L%U+WU3nP6oggm{frFp~7g^sGBxeeKRQ9AvNoh@C6SjeI75 zboR2GT<|r2g|q26-s+jzp@4S@4t+}U{ygZ5ABnKO?>gFLC)m{QToVLi5S07$Ye=|l zWz5RDwI8u6;19-z0|qp85yudWO<<(z)TlKwirqA0On`PvoEMejgpVcF)wsFyu=28$ z&Ra651wv<_tO};p2O*4gt?I;zzITfWU_7jUH*IR|&ah)FeB5ed2si+tL@7@?1<{{V zw*@Oplo7i1YH;MoNLtCR>q;l%MRl85i9Wye#5G)rgu z1t=|CKHgqXjBQu8&xK`ilR?zmIC<*gb(R^ctNYg*ybywCA)uu%!J2aGnVIkTI{idT zE?V@lxv~O4%;1Op&M9zKeLV;lE6^CKGM@|l@H?o20FIc0_s&UX{;QQAL+YKuQb1E- zE6P*S(7=l)ryMbo;bT{t&#){2ced{E1*{be_)U-aksyRsQ*N}D4;N0c`v}uS)wPRR zfQLA!UY+p4B`C*J%|6!u^TRzsU}qOl1obxWO_07Itabqq9uJOY(+CHQT-4kz&`R5a zvnE#Va2RqtAb-ONm~`zpjTm-j|{R7yt$8k zqP)q!O|P(+*xwWZR{XiwVCoaNj;!WJ|J$jGZ1Pr_(idEb>El8Pf{fyBin+aG+)kOp z2mPi23u~c!dT^;vbmdBI>3vUh!Lv>qZ}9mZhrs9u5QL1l%%r>XKw?3ECAYJBhws0-wCYG)97vREbO9u)X4*;q>fiXcy_d5`?&51B4 zPQY)&uX|7R=S#HoqtWCA1n|F)qd}6^e__87U%0>Dtp?F!r3hSo{jsedv3~}6gJpul zsw2&7Ag|Fv2z`CQKR~-3xEoSs*Ifhq=C^Yx&kAOvr6XD~7@p?{0&Qe+(<#Jok-Tno zashB8k(U#{4JgP9?inB2mAP=)(#DN2{eDx$3LD56E4)E<%MO8O)xvBqIJ61yCOj{L z%<{alAQ$@@+T~0NOf>gxBPagUZWY#>+=NbCgcd|`NkV&{o!oix?^5O2!P2r&k=!x1 zLa9iF7yZak6P7ef^1T0O0XTYaa_SYHxe~O>zU3c3^#g}nY=tlx(J*B=5wpVohdb2U-$7(PdI5~L*k}7KEfNa0Hd%f*WW+qR;)Yx4< zwbOYZw%+&rwT4CkbE0^Qv+Btq$)wcB8VDq9zuiUhV1Kw5Z^5&L6%~FYy!@ z1u{|FH%$d2Zz7MmrV&;4dTNMS7Uh%S}a zZx!;^axQ3ufD4qT7UBgGxnEDxLX+1}?biO-f)qqE{8ReapN~(~T)_XR)G_f?m8BKXkfg1b5)~{O_Ju)&N+o!t^|KFf5htB za!-s?PXs7Zf8L)9_0~gB+yE~%%H0N}2Gv8BVE!eCd^l&EzRlI9Mpxj8OM878g6M;% z{~d;{&ha@CF{F%5Afma z>4`42o128WQu=zlMNpl`r*_}iCKf@i?--*jM8h*de5g$hakDNG3MYE)N7;lj>OLf0 z58XQjRJ>#$D=v!&)7?VhebZCFtM}mP6VcMq(N8KsW7J83c|ee9l*GP@>kYX_n2nd# z*47%%uI%35$SP)y&G_~`POHVPN2-p^1zKVl6ivyact46X7i|v0St4cIyk)KrT5Zz{ z$8Z)+EKbms!+n^^FkjmA@sJ<8$R+t*pwDhogdN%yR*Y)j$_|T>SP8|xN-HYdib*R= z1AA@^HMdhDjS%gA$>~begc;05OLZcgP?ac<6#`3x=mJZS5+PwReWQ^yM0J#eHdlm* z$>C>{-Xqrq43Z^ghp?(1v_~>%w890I7vhJs>)5J;hWA3tVRU+yT1*UMK$fvaaA>S? z_VUCg_DXR%xtIsJf)!9d3|6d&v_O{Rr#$b+seT5qj(~!2)wfRveTR2ph&mp)BE}z1 z95YJo^1pb>l3_?t`>!R>Tb?L99CfRH7)h@j$4wyMwiE%uy8EQ_ho3hZl~h3qlI z^%smM=^Y~NY@*k&n3B?N=xkk34FuX=f1Z8apgGxDr{&3rmT4sjOr)ZgX?2@{5=6NS zLOKrhTp8DWpF!}ANB^s#Zp&*^1GcQdn+)uQfS1kf@(nMA;<%kau_J#Ha=c3 z^R_8lsemvy6%qt)8iaoK$DcOfWt8IhCn$3TDD@2^4#8DfTfeh@GXKpwDbadk`QCgq zrZ{yEi}n@cstopTM2s%1;Y>75YhK3hH)q=q-12_PR4_?e&hi3F5 z2zV#mhD6K@V?4ZGT?wACb7LA+l)6XyX8E@?2kYu!_mci$>XrEDQm_sK6g-Y-xKEFd zMCwic1+0YRIk5dt?HlG#kCv-Fcj4kMdR(J=9#<3mKHwz_M8S*iA`O-W{+$MrBCZTLNfacb09*M`dL_Q;6Or!ha(1=q6GwZ^oAuJ0P z4{Z9u8ms1~imF$x6;-vENzz83FH+Heb^9|ew$Osrs@}iF)YiW^kK<4SKw@GO+j8uU z-b~mqijQg)G0MKN*gwmrl1nO*1uD5ExVo9O_xj)ajeePvSkW)KYLz}#4cx9Y+e}Q98IEZSguK6U;#(p@a8U8veoAMje}hxI5HDg}Q~|7IBD)e#E!=nSO6Bsn zW9lt|hm^WE7TOKy*@Lz$gVG=_qzzk?B|r`?5Z(Zo+pt1-k^S90;7>JkUC@r2*P=z`&czQ zFjLa9;MYw~TSEF@j&GxB{l#;Gs_V+eug5@Ng&}M%O9%UBDdl|Yk0+C&;y0g+XP^0p z?zOep{iK-0!Ju_P_>I?*c-e8HC~EVEhY26@7c;_gTx4z3EMZB0{iog-xDYE;pXS9s z_(5|-pE!j`gGi9^HVL1S<%c)`cM{BK+Bc3PL{w5T#3SPfg55u-b*1PT7;poa>&z}R z*%H28|GmTgWY$SJ|0M6EGij~5NO{0?Zs%ICNnV~C&EewUh06$}b5gPi%;k?h8x9?m~ zO+(HvS*M><1G(}J+K$~e!iO2!3N>EbZR*fdd*0r+Ci*I=X~7@OE!oGE80GjbHY1-M z9((~0n=usuEodyXm(V;%;0HbG9_a=gfH&gKCF=$3?4@|DU*ns+g9fh_<08HZI&#R3 zAL@=sA{rH4*eisdhXY!^GxkgzECnF9Bei<-6d3FGXC+3=?w=*g!vQ>F2})r`p2JJ@ z+gRWz-q|2M>o06zs6Ezu|4zfBJ9lm&o!p|FiuAw1Xe;zKWJ6?#1d|Keau0D%U|B&+ zwzDP6dd}2jzu)8IX<`M8^=}mT&Moq{A6Uz&DN+lf864of^cUYz?nNAqwDT!D#Xo7% zgtHgfVDy$qbcdhR!UKcbY&2Iy$g7>{LuFwW;1}%!%r=-Kz^pz#fsfDYe?i~r_~nV& z%hLCIO>M;kdlP$ks~}t=Q_RwEZ`9XVbP?x|sN=x{fv_oUDYgIvTm7be&GzkkWTNFs zYp27H>`6}oQe&?q^H`VKbSP+BDcd|~N5BlRLRnwb5gn7s%u%htw})?(Cn4n_e(Hq| zTP!v@EOt0j2sJ)ok#$as==h}Zi)$EZ?=#%kdE-qOF5cb+6d*Rs8hQC|t+hK^!U$wO*4O>gf-pWYH zQaraYUbAJNr&btzqr#x%khEX36JR&xo&hVdW@4u-XKXOUJ?T6JwvLjv7l(Cw((_M) z{pZ0o=Fd6s{7YYA3g?rZ)#f|03}zQR^53B*GK`aB!CNK0T*_#cKYU8QJz}V{KQl7L z@;VqE6ZY zP=?aUi5OYi|J0~mJ>i2%yAF=Q=3tag1%#W) zTcDxbLnaQu>93qnivacrq%QMv;tdE7>3h3${eg6I9?+=|QAN%*E0b?YbgQ@Q7^0K( z(k;w?7>6XGx450|GKYnQCK6LMSyOU1s!G_j=kuU9_$@GnVPkJ&bg!bh9-JEQFNXWo zXW1z{_Zkqeoxi!2zAat`gm)yTyClK%1MmQ)DD2fTOpv~-@mqfK?nwgpa`wIxjBLD{W#vh`%( z$S*?mfR^^P=#NVDFEFic*)Lv~IM?!r6);rXzK4shV_^S|KsK|jn}aZ1tv@-GUOzmInQHH9rS6Oi z>t2p-`QbYN9?o3Jz?jtgc=?1+Q z8*Ea=Yfil8%p!*FE-baup<%XvzriN5Tke1oLi);u%|q`r&A`fH`G!~dFUBCl`1PC= zt|X$@^I_I#oSjMbLPkc>3nR9(<+Pe~+R9A=m#72LWwy&amf``va{3IdKxXYmuehg) zed5v+aGjZ#FLa_J@apfv6o|I3dZS!D*RoHo8O5iXWBKhvZYQ1IC)tQ;;O)jA>p?EB z^r&{Qx7R|)JafrIpk>zE?C^qP3)MaPVG+u$CPO(R(OZQqm+{+&4m&zD6tnmT3o2@| z5BB>_@2_1lpPJ>~^kPC{F0)9nOnh*Nt1~I6t8Z&=^u?%k2oJn_Vq|YRax@r@EacA3kM2L=aVXWGrxV<1;--| zJu97>*`5YLlZ@8rTm~N6XjOUqWD=Y3&j&pj+Q^GdI%7rhBL#I(>Rn zd3hfd+}jBNUwm79#lD{9&|j{^4EUhi?FSeg+L-RGG})2Hblg_(u9J06ihTWFcL{NE zo!TWlaF|HNC(7*d4aQq8s_GlN!A-okl=T4T;Am&0K>={F_r^Y#_s*%sb`5k)x4PYW zU_Lb}&Y~GHxVCA}dydM8gp^xk4;OO&{Enfs$k~mKRj}jg^-ijNbl3o`(;AO^&`S_C zvUi&^#8+URYT#u*;46*t@Pj zLM(SGfocu_QUc?BAgh3)iE+x5LO)l0ru7^u3~|t=k+s;G1*%|`rE5=AZ+!gMm%xIK zi%`y9=J)^-=q)kYL9V^?r?ak+QR#|El{1*y9w(cJ_%I|?44VW_H}1SYZ#QKwfoO&wOF_Fzo|T#ZnP>$zsn2=$P;-RN4Nja% zhmH8{!*4k5H8Pg*H+7m|drCo~K{_|KPiU8;7_2WI_tgR|t!-(UGUOqoa1`#@xHzQ` zEsN9IoH6|ns_vEfA~No?^0CipVc%6SQYaoySJa99ebHveWw!PuXds_%fR2M2NF15k z(-%lgjLQrG-Qn&1MFhbCn$%T$i}!~W>R)L-J<_aUT!Ou-CHwB}9yBHenkXc&<=0v@ ziFp9aYjCI&<>vQ{oQPB3E8`>SJSjT85r@XKmU7k&+`xzTzOq%s#S~fl%Wa z)Jlg97;y%I_gMAUZ+i*7iOMZS89>Y6Onp+q+>&?S z$%mQQK6?3QAr$tZ9*1MM3|Vi|_dBfWxiI*%HBe(y1?Jp|wJq?Be0pyyA(m)`wS6L; z^c}_@X((@lE`BR#`75C0ZXf=#F72%Uou9+8g_Kq(OpmkKc7$wp)a>b>J*(o|wpTJ| z#SgGQZk)2kN+|7dW**y|ihG3BzAa@5c21kD|oY^aSeoG>9u6$CXvV( zs@nqlw+;~gT0!ZWxu%#Wl}}x!$t3rpM9vKvLy@vb;dP+OcDc#AL1#(y&xhSBALrt^ zz%xv@>Z1X=CJoNc}X-(;LC=)ic9^B6x6IOB4> zra{-JRfQ+<_bozPW-*Xo~x(Sjpi&sGz+JQvZQ6V493Cfb@IYbdU3iA+@&DL(T~i;B z=4j*F1F0lA!@QfZ(GyW3F9?$$Id(&ITBAaW+kW6|1(cUuK1#mSpW28;{^3vQ#r^5DtcRg(SE zvY1uq`jj{XPrF9+(J_EKJ@3?#n^nfPP_|I z25QGC5TV2AF@d(9ZjtkGI;hb*t+o=ZdHtFT`_d{(liHAhhX8KN=$mi{WIWT7#ah}A zayA*}3LkmSEo&%+BUszUChf=CGwA-Qx|wwrgq#yPsVnQ@Gg!K0=4YGaP5gcn=G}R2 z_4Wx5QicM3xzm^Dj^zk~ss!W>*Kjes9+sw!*9L6jDy~QBxe<_tUFo$D+2R}*?wvSC zfSKrzJ-c?T#M4m-F8pNv%zFzF1ZUG8?(bQ5@PSBy z2M_AFr!RT2OwA2WCoH`U^wLhZfH$lM)Rg%E)Z7g@mIF=(Pv-!i`|9|Fgi_pTDHNQY z-hKZ-o*@B{#i3fJRC3O{uwyHG>fD;M_-+Z1UN~qUq;vXMEsgSc$%`5FrIkJHuo5ji zEr-pS^w?jc0CK-&b$KR<#pAC&?Ya&^^16p@tZQENiI^cvLC>|pcghy5%Ucdz#ZK>9 zFl*v?H_H2&nBd7H0<7dl>}OwKLsY`2jc2rKP5l@q&!Ug16!yfGEd{I%d;hpqHL^Ie zCOU%ZzO;hXpL@Sv-8ly1aC0R-FKQx10cZH;x{M9kkpko51Gl3`)9HmYY_WYC zUrDM4#&VL7mO4HoQR@^Z2e?2ExK3^V<$V+4pKxKd#E%?oJ0e>hSLh|Fe%)3k^9^1Q z&uBs9SLseb6{FoZB7*=;>wWZwJcdd+r{bG8Z@4M$I?$Ho#5M>_!+fk@Lm1 zA=$E~rv225%9h8SLetJpIH1Fv;5+4>j>39vKbf>#j#$UKK5=X`ZD^Y}UxYL+e49@` z!SBFp5O(Jy-WA?Wk2eXrw^V%suI^Fl_(0fiB1=zrKj~93yzcZ}h)ilOsP6M@En-jF zFwM0Y*A0Hht-i){W3bdYbh4v^<-)ss19o9FBtjn8tYeH~c2*YjR>Hdt^8t^FE?u?^ zeideceLsIn!(@;5ae@$lSPU}Pzsi!AsTF{tjD(a)WfG>%7h%&mpi-F~J({s8(6SKi zGdF!ClNV^=h`(Kc<%o-ST`(X(q>yE056WnjQn&brTo3QLQL zzJK?+prAJ-t%KgQu$P(j#@Z8)4~S{HF&&TFBpk>zh{tr5?c~q#tYb||w8wGrZa;dy z)bx$M0AL*78Kr4V=B`{9`j`z(HS&3;?c zayE$T2*5ZGbgLn034qR0ObOm=3M~d2PQ;Yh_XV+)`5ij9d;F_Q0qB5mFFf04@v!OD ze`svn3yTG*TdElBtRR!aaQZpfI8IJ``(r<0wbSPC(Tc0OZ44Tj7xRH)s9O;_K`qd? z-t2`I6UwQdgkrKb%&PeEw!)&GMh@DP3c6#4=>=zueCX#BKkD_cYqRQ>5*CWXj;?P^ zR+gso4a0lHQ zLZ&Ls(=axNqM@o0TU&qW@8&VI?zA2CDfw=ihI8Yio9Mj1d3JHN#wI8`C#H&D$~#0g z0_4os4M|0#ZC(k-L@K|Y>bt~NHcdM%XLWAK#x2MO89Io2mGSbv{yt$$??m#Vv_tij zr?CiFBFuBcKK~^sq(OL*kl%z$bX1guT=_pBcd|~+>k~Wq!_~??7>SPa^u(tabvbTw ze150bjN!n4bqShgBkbhpHnhd|fgj(l*!a}p>XTkBoMoqQc|#%eTWx>631eMGMRB#GVyXp3(&uE&p;FP~Jr{A-$d9U=Lm*!B|$JNXF;#njFU*)zupWm{i z$LwNplO+k4ibmZ42ZsR;B0t58x#{nZDs)Uw^-l-U@R*bg5^Yt65Y&nmf%hM>P6FH7 zS+BGk3+=e2$+?r8!7NO)O;09)2bH;F?FBxCvPNm*g(L&Z~`&-23ZY`?VGi(HQ!mYGt8&~RzBSwNhZigY z6GF6#Wd;d=&_S<&2#@!W0k-dmNG@$?$1%AAiy zi9I&#SEcu+K94^Ht5XWtVb6^g7=oNP45h#Hm@_O3&01TdzJ_e$imV_nOwqba} z{%$0DZh#+Ni~@j5yPE$#g1H=amhV0ZKkVdw_vYYv3|TLZF4>GN%A zw%}KttPH_y*EF2~#*^ks{_tSzXg~D=GG};lR!-a9J9p^JUJTMFYKpfS=)h{Za;z^b zhCIRX-7pXShg4r-%}Ur+5gS(R!cq#cbS~(CNqotCEB*RhCqM?~P4Pv~pS_zETcUKK z{-<4EH(ObL&F`fE4DME|<=JmK@^&ei8rk*IR^BSPXY@-U?sa_Uv6M8vKxxC8#fdbu z8+i=gS6b+Dxi!9wmNF|&NWkx*K?0h%&WkB(&%OWLq87Wm z4GvQ$KBj;Nod@?H2pJsTUS1!N%(2DiPqngX-+p46F&l9M+*09~+J^3nMiuB~m}`)? zlV5Jf`0LnG!Q;iyZQoX7(R_;5HfRYfZ~o+Y@m`3nc(I^;<;Nxum>Xnad?`Ks%pN!f z>!fNiva+)N=+UT%na^O@_aFXQDZKxJ5Q!4dzX-Ex3HC?4?%T-pt$WRQV%?;3%VxHx z4{PVtk@?e5ux*eZY$I}#C^?8t<9LQ$Jw2DeZX~Sg8>H}Q&-(WmrD!~Zm*&s9P)Jyn zKl#k|JP$inNSU1nzJG0ll_3vwJ~;}*Rx&(7?;Gz&OPvhdq!#EhEo)yd$;r)KeD{3; zHkwwD*eZCDu+xxU3{_e58@zGlkR5b?^Vs`Txf|{+gcXHl{=?xMemd|CN)5)1nT0D@ zKMK8@Dube~6vBP)Z8kFQ+)bZuMc;A73hEpte3@c|7WsfP^tpC=30{znhCPQ6Tw~=2 z1;xn_FooLRok{suT^+%GlKHQ}&9@&CSWPJ3=q!OJws(}=?+f)7oKUSqN7N(oGDw@H^7{L4s z$EX2eED!>%dQ=9S-U?4p+ ztGr^K6`0a=z8Rx*C>)~>7PS(mi0!;gFyh(Wt@$rBBwn2aNyu~k87ANEOuaqUfqk8w zH$Htg2hr<0v_0_G9Ehw_PmT0hVIU!kqM1FyTQ6|7asV4ttdI*~3l1&a@AxKUs@Fh> z2VYDFFIg#+Mc#`0jUjTk!g6;9dh8`w7iPT^ZTe9cWgm-zbK!KQ)a^GHTkqNhrN8ki z3H;>!iwb1!`1y$*%qj*LJB=Y`=0s=Gwc$e1JKu?_6XD%{aZKw=1DOL;#=qqs^!mc$ zg0BByqY`map!`;sK$9YgNRFX>D{kJ8MvOWkOlZ*^-#Kfe1<^7$kGJa00Y;wh)C-Wh zBjubB7kXL_5Dwo1ZZSa%C{7}T#E*PzlUi&H&unWgzs141igUb^Eo6oss?=hLd-HK? zzXpELC?5AxY_<1X!$Poz|N@#mg zyDd)!&iMi)J=J||E*6;bN^+J={4u`Tw(C{QEP*KnH05B|;rp)s%}@w&VG!fQ4_-pe zJE*LU3Cb40R>Pd?;2&o5i2WpBnW+O*_pYQa0|4Q$?@B2NT?{hz&=euWXM@RKm&G?fo{tVHv3tDx}FBqkwcscwB z4X&c>OSUK>vf=HtfE)fep=}j8PI59biGa_$``!@#n=yK`Yl3zA;QXWr>UW<{wX+L= z$DpP0Q3Zs^L74o>unfL9Zyd4R2&HnWE^-) zXzjHS80THpd}SZM+_@nmAEFF-4H4SHWw{4=ic0w|$ z;SQo$%ZC=bK3IMaGr5ChyMuf?iVx+4U_!TlG7vv$zai(*_b|=QTwJglQdNCb1}0zr zSfdu6D;jhxMp_eyU6Zc)H~s`ax5y z!+_||R@AtqMdtc`X%(YChN1)h6IAhEFLPpD;zv7g8=Xd0wlCOUWgY`;T7Mv|oLJVd z3q3PU-~hOTSi~LJCsfax2Bs-PEJB`gk@K!TGGvGB@yb<>0SaTwkTOe;v&O1;U|~=u z%y6Ljw*Zw*rs>r*>Pwx^z zaf*PPi`pl3WO%DvZHHAIGxWLS;A49NjCVpo$&f&doO580vQQw~9V17fMsfPrKxEMN zpbHaI+Czv|wj8H{UV``csAR1--E(6+V|)}jb|}sYVf} zv}O-6a(_gz;K9yT=d{lylKBOD&}sR}ZZduyK4@%ZrGZ%KbPD7^OAWzp-4&m$fFBuR zDITtN7C{x<-y5Djs;Th`2v8;{pRuKIHWW0U?-z#jUD%5ba6x@RTAZPoSt`%a@*IpM+S-YBflw1r(Y~9uxwcJZ!aM$ z3gW9v;iW~bv~t($>uN~Mr8POUbX2?St3uN)CV*=fAXG)&#iu;|kyViZk3{>`#(;R* z(%WeV6!rZG@VBDU>(<$&=V%6CKKp*VAsNb&d)KPAZEf^8kx#)Mx9M;rMxprA0_DO; z&9g9c$~UED7r>IDV?=%i#%MQ@nRT1CR&F04B0YCjdJLR*@dyN~1AR|MFOuVP13VY- zvg(kM4iqY5A@_6+dm?5?Z|I?5x#XQ)@PHzA>);_RiO3P4(jqte= zCfQ7aEj0l3scgpRy*4|Y3GN?+f>@dk26Vp1c+QUkUef2FN&_MoXW;F!v3yIx>MX_C z9o!@GB$V)P`?2poLHLxWPNFdA5OSCn6>H8`%{DTWfo)82#ez^&K4sdpV4uXpK}Lc< z-C1b)Tfdmtt<_CDjM%6$7`zC!jzzeHR!CZ!I0dtT?nsX>YU1~!6bC8($9RT5nUhIQ zpl(xc#_J0C-e2k+@@;9!EGioJgQMJr(WYR?x#{NVc@MaNmyl`-?-%yx!9U8Pc=}DB z1S?WtIo9bx10)j7hoDh3)aPWeIZtU~%`igOO~^PyB|HA>U72#OzQCsdy>?CF=XM&U zZhGv;HUXV#(cEg38BinwTI%YPcxC}aAH}7l>0+-+!Vb8c3dCGLD+>0I(i=>CIqMqP z&XnAG=%l!Q?b`iVA{sNH*gbGW;b@kfLV^sXTf~%U!7S}in9nYB{6!E&53ETE&z>Pl zDm3sp00_Onx7E^6Ic@v1k6kxv9XmKH?#K7_@I{TK-8B{_HoV(OR$Gk4*1nh^e<>xz zz()&Ar|(mv3QpEt)SLhpsVbkHJq7q6G<6Ww-!k;WTdzYBUE>fY_pkepzKm>yq9W(&b-*-IK7yFC23)p|NXg1rn7`!}!=33R_QSb}ES9~bDY7ou z>o$TF`XXYf@BuJ^7eO0h;iq0ufoWG>RLrE%mjD>ST`@hRiN&8%j|fV`LtW7f;$p7h zR6vu;IvR^6LP< zLr`bF5QsA7al=s*cL$-x{U|(9bL1u=KLp{ZATYkBe?rtyUFU^BmOIz|CWwn=LhqjLg zf-8(aN`7k+e>rn>#>Sa+HQaqw$PlAxrUp=BuE}TD_1qjQv8U(8a`P3h5|??Zb)x{rL*s543M&oFK^i;0RPtfOHg6CB zD0Dk;3?rZkqsD3Bxo4IZPJPSX$e5o9!9--psJvuV!Jfn)hi=}SPm#V5%L|0j3sbJCS4Z=2%Bw{)SK25L6j8V2fczR-76Gg_qXUV$o#O=zqL{6SX0^N5PZfB( zvs9a>!qlQDpm0>eTjdG|LLGWH86~KHf<6o6lv|La#enptFx!(R#U~l&4Q+mWfGHWL zj6>F4trof1rf-dDVn-Z$r5T{c1pX+RApEiRMb&LkG}-}W-Z$i|vPmm5{;+a=dQ+}%d?~Amk!$0-0L>}^ z90ss2xO`8-bfxkY%#%HC+%4m-d@cBlP`u7T>$XHBE>O2fVp0;#7e`KIwgKnq>3WjD zs17nk*Yjp2En>sy3pP!x_M(l8Ad-O>{dRjhHSH(@A}aqB@^(@F0;_<&5m_RWFm1S# zUC{@!($JXWcutnBEaB4mevBI+P4c<=7@`>cEMOu(U*J(|qdje~=TN{9MG#P|azw-xIGpUm3~eR8 z3LP)+$S#Jpj6ew$iHE^T*=)7|s|sD>Bs>a8O3+)8#r3-Tl%Qc)}in=!+(RsbEusfIM~jhe2N0;=eG~uUZdp1!};Sy ztTRJ?hp~enADNTNOFUza6$D@c62*|60)HlTK187f0qVgh06hv%0ObW3lI~Lr{U%ApNP8`%{-hoLi1q?%-m(s zw%AdZ@NEXT#}j{{AfCVfUN&NSQ`#}*3sxHS<+PC^MTEp4U0v_J8Vv~xqmc%9YG23s zgl*cSgg=hYn7RxSfTD}~`9Z-M{5-``RwdCK*BfWHnVQ^t+x*0YmXMB#9y#K@B5NClbcXMjGjz zkmdcCpY|NdxPr#%$TVc6o{8t(2a2Yg+t9swzlihosEhyOAePph$ z!@S?wB5uQyg9~89I0Y-74O_*!G8fX8d;BvI=XGmLq;KI^ox2hF`C;Qr-ui)qHk6Fb zrqT_^>)cWwc$IYZ+M(lil4mV%;x|SXQ{YR~z%0{WHoPf&zILm}ynDtS`JRr9VMS2_ zcpol}GsIj@w54Eg-yS@4V*9D?XD6W0(ebHwmo&b7aV+DFzqxGw`uil0>?6JF=h0hk zd(JZ4WiDkD8pO6u?GB#uhJ0gfxaWuVh^py({YVrM1>lXTMWq=hgIOIci8dGyE=6xe z{`jL?X&>{$X=OYs57<4pxYuVAm%T+-GZI@UC*zY9^Gnb{kY9T?8D$s6=;&*X6j4dw`T)6<<%czqzb`o@*DZ& z)nDjrUl329OX-*eg^?o0 z8H{J_yt>``&dr;8nn{zT^h5dU!&p&iAqLCCv#o^Qs~axN3HkpbOz?ZXvF61wHN9ve)CN8JC)fRIK!}Yfbp9PXu;sK?D-kPM_@TlSO`(ybid*RyH5PPOJ z>;|l8U2L_Sxq?WwU~lkU+$y&uzFz%H=HZTN-A!wmT=}D0h5G9wj3=LATv8So3og3L z(|cATBXWwoov8uF;5KI?qpLKMum5-(eWUmM^Q6}tt?423In6c-209{h^6SsXMp8eJ z;iDl2lUunF`F@`n!($Axq@wc(nmo-sc zjbS`wT+(!43%sNYUY$RgrA-VVY0+Ffa8VQAthimCMAmIyovJ`!k>_WI#zsovk5ew-^ z(l@>%S>pG{8WKQxx@)OaHW56R&MW=wN=B`o$f95`6eSZ!LqR0zV)U(%j>E5YCP~|q zQ3Z10IKMnlX58smdXt_Tlb5!nh&5%csoMo>-uUc;43c@W`|1ul2%kfiq~5#apXupm zBZSypd9VJnLpVktF*l1fq%Ux*N5S5rVWEl1g63&GnUWnJ=_X%(#p25dHc1(z&uP$kBKlT?;sr^O;i80a7mag#4GKM6A3}q|k&08sdykqy|lUo$7t^K*g zZ>Bjm555n5oJ656lYYF3Cz;G}C}+5~t(z@K%a+Tt2W)FzWxrad(IJ~BQW9=1`HB$N zp+|G;S|r>EryZFJd^4g)=1Yf}7ya)ffdW~BZ-YkqPmL2zXt;3HjtH!L7h=u)W=tmU zSy8Retu$02SDq$qAkmC}WZ8G5q}=Sy5m=mw)=P&fqV{p+Jb~4jay{FukCeF$E(W@@ zj7S5!GIn0N-7{h>XCUVg7M@9WFt23;sbQLH2{Jz#(B+wOgmlSB!K&hO<(1Pn*|@|6HjI}-;{n+=hbrJ%Gkz~+@|A6Pby_foXH8s z8ITt4-fx>H_>(MRjFD2j+q()@FS|2fvoS~CJV-IIZ;iSjjLed*p4(bQVynzo^Ied? zVmKhv&HfsrgYd1SOvtoctRs4V%*`GZWp1P5lilU#9i*hDZmK6bP3$m|z5^|of6L^C z`MiFYX5UCkQIqHHE0b5Q1nU{RyXTsL77)RaV9y=&2LMNU=v2LW1QOu zWiRRD;ATn>3~!^2;6T{OX})wGg-d4(uAV8M+N>`ylkSRe3hDK1xUf0r(Ka@A#ZtqV zbdg#a2PNsLPYE^d%=zo7XgCflzhyJS9B1YqwR9(l#`M>6Bud4>Q@Tvu^;S5?P1SJ2Kr|-3wNJk&Cn32k zGPPhIzT90^awd9XoUqV9h}3g66W3+r1gitwk#fpaq}Nj52pe^nF8faYmjpoA#9HwCEuj zY!N=}9x$debIFi>!_5d+N@&%BHpVp9=H@SK;^D41X-Ba|tL zFS}X~iC;ZySVm$&Apc(^si$2jRV#eLCrbYpoqkglLKRE_@w!YFILUF@3Qc;}99paWD~dziz>s)h}p9etm4q#5BjwQ#DyU zK`=rNF?wB_2kAEEPg5Whn7ukMMbgiLe{kw86+|n^1u2LAIGH&xPNdjyQ`ObU6dH$9 z!Q=^%S8l!`wG#w0>0WY=a~d8!up28fBug<*Jnmy`Ca5Q~S-qroyg;L#{${_`ndsxO z&n|3*pNw76=HYal+)(73(gP7ZYlsmuhOhOLh&GH7EGL6f0V}5_%Vz-pwWbTR%4Tu~ z<4uSisXa^u*wdq%$a6-Np2K9EaLl=*XMmOUi$tGnq4b{qSe+52Byu4i@fRhp!GC3Y zxq=U5z?`0z{_|rZ)&7}I!uNo)fJ<#F`io?xl{M1UIujVj?48?LF~^t#M&dNDPD6Bs ztRv=9Rq4mCW}TUfR-}}@TarW>Li!us|BeuM!hc8!WF#$>*aZi9NH_P7UCaS_!AB{Z zJ#?zO@cTFrFMXtVn@h89X9mNc9^BwH(-{NCg5%@?&Mp0(Wk*8UvKC|JrF9TyZto7U z1~f&#g_~`a-V}DqN)*UU%GP~OW{}Kg`~$Ai86_rAlRMuZvV$%@2$-{{rX1_uu!Ll%` zt_5^Gc}_}vC4nb77Wng>*@`wK49aD{5xuM9ttmj`d&g#OxRZ45-0!R`B3bowFLb=@ zI=5){kUwg+4ckZW_%?IL@&geAK8utzr#$84G7ULLIq46Kn3tz8Ba)0b3JPK~F>92J zGSlYQZ{Va)$u*o_%9h>dkbc>8ulkGqOs46k5EDt&*GbGw^7q5?n2W)dpKHd?+F=~F zwSRd9&$NSQ*_ALU^>fcXbys2V%rb=!dRuEInjYe6^l^7ap7u<8(s#v_cUfViDrWXa_C6z zKkQ1#=pB1QWidWJJISh4oV6nE+O1z z`aMP`dd*P|Pq-{{PMqLRv00a=22VN^-&YLz*2UtxzfYVd2|BfXD23kwo7n#v2ChSxW)9_tN#D! z)J%8TkqqnGS=TEr`3q~U?sQ1gCCUeEHq{1Igi) z#gVbH&$WKux3f_kYGohWzv>|O{u`J3+eX15bOw?>oM!srvAHaYj~H_ zb8dYW&;2*Kc$~`5mvh5j2l{&No?({|i>Ei0sx-D9EX-Q%%azh-=x@;yub^$ObbNRA zbnOy;2tQ>Z_j%mwl<4HibWMb2L8_T;{y{dwhT_h_%Xz%_HzLJjKP}UK^?b&?%Ho0z z(jjE)e4Df7fc^7Q{T~7j?|sUOi2wR-Fqi30{lKGy6P&POw#GMBgst7inl(MQt_Q!5 z)>P4(b7A$t%0P_?CVx>X0oCcBzNb*%PJ;e<^)!!nt;{t-@5g^apE$joDL;Sl{y=<+ zPFK$?m-1Nca?Qn;UtAE|r(9`2m$LkAiMn*1xI63K_%64vEsg4q3QaqnZ1iHo6K$>1 zRr_oTeBw?Q|Mp|s7wJ(FHU^!H$Q-e5A2ZZ#9kuICMU-&#ikwQDQIO{MC8e6XEw%)B z{#@JO?e}uT{w2+)+bZUKGyAK?v7~)M`|E-Dfh)z4BUO1)dgHEmc}=W2Fh51{t;iEy7Y^EYf%(`*T{=Ci|Af+XTFFN@BFqy??~Ov&4VKW^+g%k z?;~tOR=&Qb+pqgoJi6ue*&OLPv|Qh1snMsxXL#IQk(&KkUc>WRu5oX4(Ei~${%N1S zsCS$G|97j+EUs1e1g>0E@_6)d$&9}ijjTvX{v>St_r{lr_UBSxJ#Ey!a(Civm%ru< zu9jAwkrFKLO;hFad+tx<^`?GS>NV`L{`1?-gCPOCLQPwrXqWt5bVBjFM*WW9$v>96 zOZSj_v{BhVbfsT^T&)7eczlM`s>awZC9^~JNr*Xl}7{(HrGd= zD_~YUx3>8?K%q{jzWCSm)^$ymqglm&|L~ph{q@2izt`J+EndH$eAn*i;|%eGZwp@s zp5I>Cab(8#^6T$>{+ zD7(1%ZE;r)^`S#=g!BwT=`OC@+=FDcxO;f{u8`{~tB{lNa$6zyhn9t^1>MNq(`)t4 z0C$_6mbR`tx4G)L$r-Zy z6UsPP&}5AK0^DV^l(m#xRn^sGv~`r#v@~_p)RxMqtEy?JsHv)`>nN$I>1t~0sx6cG z{wJqTf?s+8Zd-M&jZME#2c;EqoPpc2| z+WI$h{sj_l`LDRUhkE^&VYr1ej1UD5hxA~rx<&!+EZ&hOQq|B^Rs9DLPR}VY zEd1QOwub!&80s3jAdvrn@sFNUVSwtmxdgfVkD&as??2$Ux$1893-ECX()aRl@o-n6 z`+Df9{9XBvo<9Ui*T`?XUjU%xuCJk|@}H{zF^Ui}-8H^}K`y?o?rV(op+ecq%T3o+ zQ%6(XMQy8+raEP-(pHKlMajj*O-IQMw0o-?#f1X@6GXJI_~-C{18wZ*8jKa?J7_lm zt?s6!rJ<>&q2%heMO#U0tERS+j;f26lKL`LZ5=mPO$x2^>Fk?3E{{LG0SHpHM!W_|E0s`H!vaOK&OWS_f zuOH?Kk0F#+*ToeZGJRL93+`@uD*saZH(EbNejn!b?+pB(QJDk(AH)Cq93h_WzCTka z4k)76e+Lriw>2olCBS{92gt<#hnjN${_BXTko8os&EM|ze}M;qE<67BIPgDHq5nM& z{EIpIf5L&kW!Kfy#n;2#O<(0NvHh#!f5tQpEB{rW{xSpqu77%X|Ir2A1vzpdbVf~2 z<=^W5o|)6%0qfxQV4}aax+?sEqXK{5hyJs9>d^H+ANtScsYCxBY3J(|qz}f8Ky*s= zKStqAl4Hhgz@Ky1Uj_zKOHXBL_0$nGuTb~x4#r+!Zv(LvQPa?(d>=Kn=|2u#|KErH zqv`ul4!i#0eGw|isRiE-+)9-ne&xSu=MVe!fAYuQYx+M4bqcIslT0DV&x^mv^>g}6 zY5hg6DFpd>@fW#%PM;~QzsNO(AU`kuBG=F9Go|$xxuy{0=fz*-`Z;~3wEiO36oUM` z_={XWr_YquU*wuXke?TSk?ZI5nbP`;TvG`0^WraZ{hU5iT7Qvi3PFBe{6(&x(`QQS zFLF&G$j^(v$n|sjOlkc^t|%rnLSd*A#;My!eY;Kc~->)?ehBLXe*qf066w^qJE7i(FF(^7G;^a{Zh>Q(AwK zYYIVrUi?L_pVMbb>o0OmA;`~*zsU7-`b=s4MXo6X`FZgdxqeQcDXqWAHH9EQFa9Fe z&*?Lz^%uFO5aj2@pOTC8_e)K8UwEDw0`D;SuZ8Y|H=HuAtF0{%+PM%R#%_eZO~Suj z2nDMm^uZY+-DHGB{f=#}HbrRj@ioRPZ9`vveADeO`QrMbmuu1mTZ?`d?W()j6sLMr z^TI>rRdst7yg4Sz_eNScM}EJMUjNnkuEz2?*UjWAX9`P>^*%Te!iA{aj~{OC{GdEG zpd+?mOH^x`^a@RcUIu&cAXL4!&;Q8!Ep_V;sOby2hM4jonsTKAqDETZl-}{x)F!rd zcT0=7AfoE?FcG@9rq4r0QQ)0MPi*dTS1y#C#wH_Y357^Olf1fXiE5E6)SU%7uW_T- z=5!LGpC!kvtJjQko{QL9Vr&p2Dsxy%pvcsUzh+p=;f?rA#DL+RR-{(>)NS!mMo-l= z?YJ0me3o2{4VlB*GDY@6O}lor=A7|Is7NH08)dGQm5EK|v*lta;!f5VgpU|rtjX^e zLS$2zuS34NqB2`!b;hQb7ZLS3Ak2EX!(j5F3CVcurR5v(87O}qtS$e-n}<=PHPLJ1 zWzBifyhRjV^!`}YjTNeCYI`msRy-bO3*!`i=WPzq)d&_4P3K0wzeBgnMq*165U}wx{#${O} ze1RLuQg|8mpQ^rHK%HVnM@YzGb}Bbj5@WiKL_e*@a}hQ20GuaYLj!3s){L;y=VWAS z1|Wi08$``C<7!^GnwMb)6i9b0qgyIa8*1{Kg-|tyqJyE>k!Xv2Y?1svpl<(l{yb}j zL_$Ua39X+4JvLcWE|JqsUS^{s;xI|BpXi26G`20?RWjIvjPmE>I#a>E2RaTrk~~gw(|`lns8<1S z7*mMNCo^`$%Np}C3}C5jc_^be%Jhrp*P%vT=#lpd*SuX|%$x275H=L@i-vJC)(W_s zkY?1t2v3s(uGcihd6A=!KxYPbBOi>>THE^C8YLgJi$poV_jJ>;`~yO0Z9%;dH5eB5 zL@w4Y967uav;W4Gy@yR^JpNRrsE<186b+CG5RZ1w$)S1%!#X}AmPCz)0f}<4{42Pq zU4=9*`8eqR`jcT98F_2O%~M3DV8D?}PM%5JRDNi$I|l8k7btO=cjZyKj;m(|_Yo05 zN#}X&4v-lYC0k^_n8NF66mcU4 zi3-CvBpEuBKu^?QyakY{P$8B@23>MB{&+j~0!mpx;q_dL5nvU?38!;wg8DSZk-5Qw zT;!v1Nyt!mvo{jWjsRdT0C++Ga8}^9Wsm^-563Y4{CQl8R1;Wl@M=7JT`ZcL&K+aJ zD998}=0+VKWaUg(%XKwr&~ykOD&PV(rhVylhPIdlx9>fkFsninlo+#1^n0FKH22xvCIik;+0Yr=};wQ>p z0^Or%5`;`DlQKOEvJ65qlC?lK7{7p;dGhti<|e98sYh`&EGkmXTJ)e`!*50|+zi;3!~1qI>XAYc{ou z!LEyfB^GSq<5D~#n_wfeN&zn?ofo6#X4X!wOTcgny#zkl$SjDlkr7omlmQiu*Y^sa zPM?BO8JaIloL(iyRi8mVDYS!uhkP<`ow$Y2e!1%`U;x$w#41884eP zyJnx5A%kQN%4ej`z>hqK9$ z|Ld61`64ax(@J9PBG~o{H!tx6pK3qo)8||oVJYp9#jyI_f~8CLL;Fcl*(mX82pzdP z3Ya_I&C#b{B)=_!F7>X^Wgm2zT_%jl;a&jkurm7HgYgydKnwZj5e{!=G`QJG51Sp> zH_>STb>6Uw;lvt+-c#h-tgc(@fG#J#Ejbih@JOC9D z?O`^eN`)sXv0&Vgv$rc{hz#3z?2TJ-Pho0CX}qjntoKaIj7^x|40ne&dtn#I&{xQP z4}@N75lTAoLW0Y7A!oQC7~=G061-1Vwv#_xJ-F|!7+__u6&GlgL5i`--aP>Fjr!H% zddUYCQ)2sPg7BjLj@LHw&ujS79SGVXodFF=vUxkXKC#Lq9fq4_gpqh>ry7P?V_8RL z8W@_q{b0sY4l5JozlF`Jhc&;WFPjCl|5nqe7c0kSnIS-6d;=Dw?qMh0f6~ws7ED`U z(7p1#rlss=h14im0d4sMo4O~>d@^e}vnvfRIaGc9k-UtG1oKE5A>@qa_||jlj?DE) zHQdAzIL~$c12P>Z?d1MeXZ-aTJmXS6OIaa2BLYYCY}E%IX1+ewR@4*5Cz=rRl+sRa zK2D&(svWz}x8yM;HuwVlPi^SY`A9EzE}x}K7zYJM(@+xsD+#WpiUd&^_r~Yea=-Q@ z=L;m*I9QShpvB^t(_4X2?iUDwR!vab!&F^fsc`?HoiLCo`K4HuP4Wpg`TatyBF@UL zd{!>YVr-HRV?HxV6M(JjVb1vk$|u!2V7?ZU zj+HXM4R#&y)?>Q}R@6nqAQ?)m7pJtpm+V`p#$B?8gW|WoYl?W!x||d_AuR&S>@XW3 zUxvZm9E@qxTNNB(=TBNoC~^AIDw~ocY_c+kNYqn%m~^g6g;b7UG74r|wVKD+P^>t> zgMq~2Jj~uAf$t@B9nc(m#hzX4rLOgYJ`bzg4+{i z#+g{D%4Rr$O^!T4aEE$&53?8i&57^DS5Q38CLiK}k}(V=iO5o7`3NqIS%#Vo@GuLN z2}Xgxtd|My@&=Bip)`@RkC#>|FWxQ7WFq1nMmG2j+*G!Hei zS!POg)x`@!4DW9fXjM1?#U(^>sVfxcLU947NVI|pf{hb%7;PKVoA4HR zV%$!)dJILDEl76e?2QZ3XLAUfe|?lPZ_xC7dXNP3i414e0sX>NHl)Rr%pgvmBMVv; zz@fJ;;IO9dRBz%IpLj;hpeaB^>vtu%_RTzS-v7YjO;B|9O<|H(wI|LdOhNk6>!u2a zY7{a>u#Kjks-Y~i%HWe=Cc23dVk&P(5)I3ekzj@fz|zr4&A9gpyw4@Lcvo{Mv*C5^ z)%)5KygE&kg&a$aXy;+GJf>Eupu-U}M#@a9yC)AC9liFJ4?v`QUuiXL#G9>EDim2S z-E7?e9VrevN{2oySRP{&aE+fJSK*@A-o*RaE8Ex1hQ4*xVzvCHl^?E~QWtTABRzOX z&YxV(z6!6n6_^ zfozRKUtP6gY?=!=GxnB}t8DBGw7BheJ|30Edjx8rOV^$leTtX9QGVEx=JRNp~|}o6~G>_mP0j^b<{Al zp8L8fW3dKwPFEfBcYGn85K~H-6QjcsRy5#$Rer0hU78MuiTCTY?2Ka%4CqB(|WK`u+)!%kHA4zWWv_B_ui20*Mne)Qllc}8*0B6Xv&QM_3%kS}DR;keWk|Xl+8%e8#1G1Jc zU>(O=)0n(>pIAclLq1ELTA>9{ffN_$y|b|&{C0rYmT3z*&-5!)Dx|%L4aDlr(9NWY zwa>91?CZc&72dDc@0p$2FVcyn9_0z2?VoXJMhrXYD!1IBB z%|*We+`D{-w8$uU*km7ijn~FaMz8Q9+4K4fO#wnJ;RzyCT=3u(-^?({V}9Py%DAg% ze8*EDxQXJkdr1YRn0c-LK0)Es5mSs4JK%u#iB}RUpIS>jT!zhp^M)aRz3G9P2XuAX z3w;gZp$a+MH!ROfv>V@9DuyRyjpcs)qdq6{jJ3m~8Blhpig0*w<(S>L?s`u7)u=VglYSa(1%+x*_qDN1P^$a;6*lD67aZ0E=&DlI73ta>rz zMed)NLmdz5BQrED@4fpCoLq+Q|c1B{oS;$^I6e+nO0#YAE(a(MCZNR8K7j+8VWddt^;O)KW(1==Ib zRL{E+Qrfpyov+(#H$GM(gLy_h?G`M4H76~JaG&Aa|8BS@e)ZLXwip_;BA>vFY;(`l z_qUg0sY4|++eMC2ib4vG!bDm0Kz*FuxE_byY^5cmORnWql%F9Ca<3#5nmQZLH72>=$bCw?@Ghg zyL|K-x7VO9$1ovLdPa7vSE};E<#67Gojyl9#@#2ud7XmPpO~$zcc0r!R_IGqtLJ`D zxyPG<`;^4$`N>K!@4Ya&v$+OWumlS&MWrQ8t!Kou6r7a1XmVU(=ScZ`LfHzO^QN0#R!iq4YR$x2v z>=EVGg9gGh)!F*-JZqxdO85@DN%z*Vnyx|4DyZ)ZEw5bg-?@UrMYHtaoxBN6-6e|R z7<6W%?yVU1mw@joFTau$)?CfQOgi3(HIpWBsyX($Dg7u}nS>RC8uqz8^w#-D@(t~6 zw4^Y>qOBN4{X!wV<96c%-#1a(^)n)=tE$5FITL!z%+cFj)AjvWgR*Zo@Rcue(m%|> zRqH5!cc&3(f{A$El_A#Dq^|)imixBXpc;)!`P_QEx^+b*5&0s~!(`CF4C+00ig(l1A zrOR6sTW%Dx49V~z(TffnS}9qAY)copYPoRvE;GHI*VMe*#>a*sG*>i-SgQAF?)A%gGhbc+V+mI!yrl!x z%}ZjhD;yDD2}Z+jrge0@)>B;{CA@6=5Z&`nKCFO{f{s&Az4%4yqMCi7p6?Dq>v(IQ zU~Vhq^pI7|zrL3;J^Sf41a$HRP?#p<|md2}%Z zml>}?NW|h+^3P4acSa>YTtRNrij&b7A@~n^sATIr-AJnV=B1~G^bu^Y*^44tmcvB> zWLJqmUjYndF4YM+>R3s;?NfuW(HzRj>a|B#Tydc699q9>ew z_qJ(0lc;AIJ#_Qt%{#D|m0-u`o;`bZyDwMT#m{#$YHBWCvxwfDR$7EZs?Mbheq2_&H?6)31d>Dpq zeA2?rbS=3^FNxsL3viuK*fRWhchi|#>Ci`D;X{8Da=ZBFLE{tU zIX&lS?CN>22k>nhZM?8$p!S#~^KSdBmxQ->r8`>gli97-h1!-*WYWzN*rrHdhKI#Q z$KT4N|7NoF2n4O0afpzXmXwtA=Zyxay?95`$(w#-AawT#K6a*WsYE7Q;A-8WxfY(=wR05K-9!xU^vz2b5BqE=g$rG`nD5!^RXjtUoWVvM0pciqhNb) z`2#r1e0?}=bX>nYYSLGXcBMx|?PP%K+U1FZT0Bge5^+Fz^{H7n-M6~qXyzIJ6us-| zmJM1!Ai1h}5>Hf41Q@N|Dd`~!vA$#YsKc1kdB$+O<-JH>$V~0R3wGnu^Z%eRZ?`(b z_RLb5FD)I*q+O9yziVwjc)WJble-cS!0SC*-XqBMp=e2~DakVWX^>MVKGCsnr-|-i z(t3(RXKEKD+C^*|p~>Pi3+s>Ek48oF<+j}aHvMEH%7~kR0&oHGf-B`@Kt=RI zO^~!4ecQ}#UBbKbXwr@KepBjSd^JnxL|9muKi+TYM?dY@#z#xbeKr`l`Dsho7}bgJ z8%yF&L}Xjnf2(=-mQn=${jvYW-eCVV!|cWT<2u@wHhdNtif?77BQ$2tHMTXWTd?e2 zxaV1s47r!UWL93GWrnV+)I;3|mJJCu849*{mfr^7H%*BVa>3E>xl_%wU4tdjJBf3H z%_H5|4+2M9>mx6N#P)Fy_8!8@>>p9?lAS^V4V9yMYg5a1d#~d`DmKSzRK|gObg_ ztInU-Jwi)RQ+IgwFwZkV#17UWhCtO@70e8gyd41+Mn?rAcbw z!>{+#p&J`%_lgG|r_r0LL}!NfaX~G8f9CEcw+`A24_+uP{EHfQt>A7SLMcEEH1E2n z*wrAqcd@8<=t*o!5wHIfG1~=7-|j+0p=4~m;zA?nhMJF@95G%yowq;+m@7^ug z`*!9oTd2RE4S-PgSq$l*W4QZ84a=w!gN#`X+JKtbyHuO(`j4gWYw+C;V_uMx2Z*rd z&|(KU%9~Q@%7b7a7)67S;v9Rp`D&nB=Ag32(qm+Y8k>)yP; zP|K1_1-OYA)|sXHy;*l81))F}6x|o|;Q~~oitc?`eLr};vkrC{(UU6PbLon%#(MYr zqB{j(JuT_;W5D>O!%cE_!&33i8b`dGlV5J}uFp14Y2V#2H}&)D&1@6Q4HoxfDQR0w zULf##?b;boytxXh5C#LDl=*$lshN>rJ zO)%ho9(IaAvT?fZap~V~D72cws*$+@SOFFjuCJ^HZw(nj4MXl@)|m6@R6i*wfMW-@ z#!&fQZ^a66?k66Pi(XhXXwPy;o7RM@=e@BQ>x&dqc=C=;5d(NMj(Demcr%BoHp@V~ zMKKs>6R#Yhj(B{oPG1wzb7y6CqnpPpOYzj5*8vQOQexH!Rf5Uddvg>Yb;$A-eJhvg zydlCN49QFdeVmsA;REYu!ExwBmIUIsiB5F+jib|2zMwX+!jVn6TsN&a6wZG1`DDe-^#jMJee1ux1ezPw z6EM~&-Smx8RM@rmi?M2<IThxv`L9QG**2KX!H9g>AHK+vGU{W5m{0 zp2d6fQn!=~$R;9Rsrah$_D%1asO05Ox;Zp!+(Vdz;!EnZ?^A8133fORYNZF;4@MhMS}?NN_p6_6f4Q`84ep1`fPnblLK&z2z1o41bX;JVUsyW< zZV3L()`lDesLBckt!u0+9$gp}s?j+EL#jK&CZ`-#{&pMpxZFE7CiUvmvah4JTtMPt zb3aT&ui1TlYTy^}W&=m!-jqhsHEL(@)^D}oWvhc*0xj*|zP*4QEPXaefix`lnRuM+ z^;dalE<{}ceU$uMRRFRckc`#%_tD7njaS5EU*97<41@Way{pY992Y;%-m^q*-kD8hrDjVe+sa6V(! z_r)v24J%3yxUCBLC5SPUd3a(sMu6)t1aN-h;fdQGm0b<5AsMrvrH5FkIL28bNO$td z2)F__tw8-6^En{pZd}&j@wA$J6g9k*GPs=!H8MCH@N~j592GtfYyxO{-FGfw$`3!_ z@ZNLH&K_-ExNRRP*XBWTZ7tu)0NF!bw_3*JDQ`}JwE>?T&uRgQBZ$5BJM9}u*cx~& z&+Fl*L4M5Yp|^e{z_Fz6JArKFZVjE5=4D{~H|dFv>+H)SqjTgu*er*R`yNo>syxMQqJA%P9azkG$^J1b%m=H$2Z zxR$-qorFXC#*dbhc%w>JB;<_kZDnN@hm7xB0jZ*ZnW}s>7{R3o(uqYneFGCc#k5F* z96Ut3^W2-`IQ|BNjnR>o{YX#%}9w^m>p24KEBPX}8H zMiY~n6il+*bso%hm*8zc^1Fppy;e+^0ERkVj0JHe0Bf6GitCIL|Ss?fQm5QdBJ4V_dg~hax@XS3MOL(;K^u5h{;r?F%o3Yik8|P zWx>ElmO0Uy&k@c0#Ks=GtBkOy$#A_7yM;fAhK5b zXFkm}SX9D4d?NNB&se-L$~^@tw$g>fq8;7Drek(I#fw0H_ycWd8W<+Mh@jjPNI`|X zpzWN^D2H$a7X@-G4Grf4Bh^xlrICkh67XoMIW_}l$N?U?gt18folwODVvd6x!KDe{ z!THHeBNOfI=V6D(DbffXgs>P;fsu;^k?}N@v!it29CMWWnPc(-h|^X9*gIP|Id%~1 za68|91IJ5X3}+-n@KjV(`uh8~h#~5{i6jy5FSdOc)yFc*jp`0yWF2tIXdT`Qk%KKn zori8+GyiMqY6yh!K}oKJ0?n#r5FDga;FQSN;+bmLp1Nc@x_`0zMrc@=8XrohNZ`eG zSVmujBN#T(Fydl)k53U~`4h`Jj`Lo%uwh2pT{o0R%0pTggQ3ED$@T_3xouFo8F+Yb z0hHS_N|Bmn2o)O$s4pPymy{&B8;l(jyH?>{YjUme(Z4kGaG~$=!d6qBp5Omj;eSX$I9vqaI6f8 za4Al(aa@C8DL(!&72MdsRNS1YR1++WKjKqa{ZoP)Xwg=19T4g1kEsQ)V9}B0sLyY@`kmRQS#X5#LHcYG-OPnR^wrwBYaTq44#7tDet8v~M(NYF!$5a=*9Mw6| z;8&GeUK)XXXT;KkafUXG00;w1a%+BmLir76nfea(_s@2E2IQxzBkb5+ZbKSc`1C4I!LCJ6`MFSWU!3wb`C_yU=52nkP4 z&jOJ}as(AQ;*BEvuKVB60QegMI1P>1U+SJ2?s%npAL@FtrdiUpspc$n6wP_K?DHU-QgRK9b-7oe|Unk ztqni6j9Q59<%WR2-!ou$dXO|knnWH+f{))xkAqo@9#MF=NUbtLY{ehzrWN$vY6+@Q z5C)|bMkT9%ARrOnGyeYl;|kcx%HE!J#jH!;n#Rji^aPjbe9tb>0rVgrPE+7HE7>5= zYE^+aBp7io>Weg%cSgu9Crpp#@Me6Gn<=;Efet#_-=lTDWEGPXkD(L=3oF z7%9hM)|k?FeloxGs|v8Rytoq^P{iK6nU}e1LCi9^jg;i3x^jB5W?)BH^F2X;8o{*j zRhR&tcU~2RgRKtS%pm|M?hBwlD@@9DdMXwa+aG2dwCVG;$v+`T!65yRG`3l%rsWJ* zZX|#ERiJq+NGW{T==;yo_2r@>Af%Y{;(!z57pa{c*gKH~$BmAQgl&aX?ez=S zK_<1q*|ao4E}3a?(bY=FMx1p3o2^r!WjJg9{#LzCq4iJUrjY?JC7dHhZ>_yaT~HG^ z(fB?_h>{G(wQ!{T@aB=m22YZAhVJCe!FU@c5imkJ5`N$A;mA}s#9ctT5aZYz z+mV}EaD+C15-n%^t-6x!_M7iiwh@y*fK2VDH5(`T$(3DfIDNsNH|!@jw~5_cjFBUE zzjt;$zzzz+qtQYMK!*|X#^Q^Aq}akIo!^6lMMR*Bc{=dnMf<^3L%dIFWxJp^^->ne zGII${wi4_JqNV#2!94h>*@clyw>+*1Pc)zm8H6VHURr*^yGj%fMpef-9NP7&O?#>3t$Ogp?@y+P-X!83ta65Ze$)K0u}gmrqhpC*(M z^?p#f7aUtD$1hppsl_F5qqQHLx)Ccg(J)=N-BCL9Gy6#f79_-y+Ip+zSd9Yxt;Qqv zPCuxjF8@4lSjr$?`~#gLB>*$aza*}YYI9$_-x{q#wZL(4Bwt^{#|YFh{tbTD-TCPL zU?Gl9W5?d%v^t-ydnysB$2~K}J12P7;&u5vy%5-i-tPJ+@&M4IAQASl>QmEuEiaTU*-1lZg{r0s+0;= z8e)z9T~?vRRidTvf+9lDJun)4v~)6Y@fvQV3Adc3X&K?O>}NpVd78@OGqv-=GgyUK z^ciVR4BmvbQB_4THV-$@My{O*1XIl5hS)dhlA}hKZ?($s-chOg_o*OqG9eHes5GDju;#Eb-%Vp#cD7I9OZ3Yir+7;xaWp$)p8}?(}9IieIZ^U zOML-pC=LVDx2ubD)A9)$4oGAb?(}iQ&OEX;DV>&+zY#9-)Wz?xucCJn(_5-wu)gC# zuKLMk4Xviz1*Rp(?0yCkXpjbXfs&o{1w2fK=pyK(mD^=BcZbgFeHNzEu6FbDUb_ri zUk58iKXKA0^`77hF_P-sJRlqE;X+nkOHJu(;YtNIFHB40h1=*@CuME7<1d^)ju=)F zG}u;?*ih4NU4{>^wY2 z{u$?z&Uqcd?d@vLdSPSyde<1W^z-M>=el)w0~;0 zi5I<28_q58Ic?ezQRCI2M`4G*<(-aM*wPc5Mcb>^DH-wy3vF&MDG(x?V+ti7xDNNb z-P;3fSZKu@LtX-kg6C6b>=hR(?m&mvLI54qZ5OVk%yn6IYxn;4^=uMd8%HQHDuT}%VZ`&`gv^y}9 z*KAiA7@=FWqKThIhk76jhZziF67E&eQr5Wi$-9cRr5Pcyf;&W;2Qws;$$)uyHE%|` zqKPBpdW`J_0`s6X;;n3(1Ly^sXx9(S4B)ttgtrokBagGUxuXu zQNyvG$?*z7?hk6FD?3k{CO)IA5EReeQzQvXS)r`R|5V}g-pMk<8RNHI1wTNl5a;}l zghfd6XcyZ8>U=79Th8~R?d@r)sD1Os3C@K87@ydrfezETf+2gL=dte>r^;#1bYtsr z&;+7}=`P^=o493I3aw-Jojx5iLx1P}l}@?sphmACqxY%A+MACS!hNVkCI3ROU9%66 zX+4KWGT-)=Rn72ol2+{4vJNJh2N@B0&&T|2hS_#a>4ptj+=y{~(#~nq@P@GdRO1^g-`C%u0Z!BCPB_^UdGiFwM>z=H}__n}WACnN{{?I);$H#|SIEhl=R?oD^ z5O*~ma~cu#|KrhbkUSwsuLR>O54kld)?Z9AEWJsVn~l23DKd}{%_u0@!KKMOaNpnN z(Qj>X>l*obY^Ncr4or~ht$qV0s4w34JZ!t6B&5^Khmb4IzJVMqDwtj)LmSV>MoOFO zvI>@)c9SInywC2$X(Zon*|+!>Z@Of(E2sXwH$S9Zr8Sj9dnrPaWfYQM#9_ z#pe52Rm)eyI8A}wq8^~u1z-%X+{O5^44IY?kBw%QLwr{&xe*FN!f9ni}kc7{ziL0_M}VX5cbZ~LrR zemgSsKDWqAP#o1F5Do}%M^B@>;F2ojPUW(L* z0~-({yzSdJ9nd!xdAN7g$CQeavgw~$81-7#DtzO>x{~{Dnn#ZA#@Cwk85^F>(0Bg? zG&*GYYW*hZX&%}7fo0n|4h0>RXD@~1)RrNMY;YAJ{jbDuu(U1f=yL0BGG&zae8_XS z{u>cx+gP~ygb2RL*uZ2}YvRkFqj3uK^Lstn=RTf}`%Hs%eTp?PjcIMp7W7-v-; zOSBA=;Z|UYtO6}sO#&=EpXGu=%jg5CDHlb!?I0kM+Wcw3o(t6Alpk%=;j<&iH^lph zDc|boMe&d5uiy$~CupB$*QffZJv8{ni4R4hwf-x$Da989wAuuRziF&1Z97*QDu5J&&I7 zFyuT>mmASX)WA_;E86hfNP7JGL2c0=@)h5hzi};upoDlvhU4!TQTPjPS~kvLe+k69 zAy`s#{o&{Yk?{KRIk;GKEF!}2+!&;q;ga|7l4E;nx}N>sgG*0L-w*O$><`16F@%Qq zJNEKRFguU3z!yO@{`Jqqf4|pBY{@8nx_YICXX;t2+2BkeYL}a%$1&y2VDX^DICgj1 zL&(4v->(NHlzGg@iCPt|!wN$J2F9FBGaI@KhrW;2fBftG2cs4Lij9SNf z(Q7$Tw>VGUs9f_wX4D{q|DrZV<4Bp>o~%FMEkFWitvXH1aPq$o2j#k_VunqZyi(s< zaxjtdI{lW~{$&S;5*=FwGP*3_NdvG3Kv69_BDEh=&eZJ2M-g;nrsU%}NA#>Z;q(mt zDi~k&Q!PBudjZ4=!Ib)_=j>md#soou5-X)6V$LJVnUluSP+}C7%zC(ohuI_%HG$6u zjD-oZNhygRKI?sdEW*DNr1t_m<#2H)`e(+=YMx20=?dqd?GSUlOW?Yx>glq5t7~99 zd{HNE-|%5IbF22CO;==zM>@{>{kKk0pxl6cB6#^*z!YC|p_6by5bFszEL1)02SvXj2gm`ThC-?r(dXUOAr<<=M)KTI&I$Qu_M zwBj0r{3V#(R|~L+uyou)mo!qG4s+PH`KWB3@rGN<9HG!xkK;GYGi?uYdP`8Ay`bRy zTzE`l*V6WU#KIKg&ysZIv;4!|&OnIs@{6I}u>;=vq|NZK!?V%Pd8KgO4c2#*$J}A4 zC4MZBp*%Qv0>^0>*H_$8({p#X9~?LrhYzHUmqCVJG*TWM=Cm)n`$7Gje~L7L4C8Cv z9`yy)4Um5bU8&1!=qs~7@>veRBPb{%A1DgITd7gJks-znL0QcQ$7;OZ=6=M|`NTp$ zNg<)>rVbgLElBxYc1Yv8R!nZh!S=FQ_$wQ57o;Bd*s<9*`kpf(3F*qj(<_zTAy_WZ z2ia}~2E5D)=U)OBln7~ok*-LPjZ&<1|K`GjYfN_Sa}70n53jq160jUlPsItR%rj~) z)FV7jL_4dN*mgjUqI8_IwgQJoD)DyX0Tqw%X!+f9$GN>yPu{D?ArI&YB{aZ~cXz$n?;$Wu#27L}LVaINj4mh0RfV~M|Q{mlRIgxTzcN^5M z%`3I}HV5u}H{Z|3>aN5J;TheRB`$Uq<$mS>6g8{GH5N+5*tDeL(raIV%>&x^7*{9i0wAFFfq;$omzXCxTfbQey6F=u}5Q-YJ1`4Reh%%ZW^w}^PPpKv{>%Yq=eDkc;VFk zEBKosP^L5=*(aRZwjZ;RZG1I8YI_1hyS%#3+n*3SMooN%ZH;7zDQo#ABb>F13S9*{ zQ~D(<6{I@dH%Y=M698Cno~)X-*N*e5O~wZB^tK;7)i#q4+GMni_$*2*oB2u}0p*i8 zls`qNRrE%Oryhz8%dr{-sKyDdU*v3;c9Eii^{lSana6x3{lMZyP)MJ5z{%W{UY}S) zi^aTeZrwsNudmKXepw9fpP|Bey=#dWc}}3e{QEY10>Yn0$>+K!ko!SH)Z+k@(OUY0 zxm#u7aX3zgq$yS_;b+&SnUu?)u&SA_@vP6)v^)hg7RMaS0^6B*YNlnM8|Fo#o4`V) zd4Su(TQ&Z{*iNuF^e@$G%zn&~waXojHuC_S)>$^RA?1ad?(1S~PA-FQKR~hMzObRq zyzB7R9Lu^fN7l<#P47fWPFovTqp&T2SOQza^|_<_LU^OTh}drQ<;u3e<92tSSvxQe zZ_j(x=kPE)oQao%Y(bMH_RP=b%GOfPu;pM}pJ{P7rbeMu%!n7`w?P~5HwO`n4?pP2 zuL~a(0}tIpAX8C&i(Q)3Ee>KE)S=UTKFhl&2s~fPo|jt=uj&7o6$2A8cBrGeL+r#< zK~oru>}*oyHhMR9C|CRY;0dx1HYVnJr5-C&v&HkLF9RmuO@%KEtcI_OK)aTh(V7F? zfPf(&(3lpFrQ`r6s-b79b;>hBXQ=yYgj1{`*t2jpRw3$oToe=Cd(Qas4a{YC@p#5# z=&t?%drEa_++>r-luWm`dC-}0bEbvDNtkb~|8_$E!bDQ6H7gagRuh{wBbCoW0lwwn zts1vv0Pv$KgORr8O0e-hW;lSE(ySR5Fz1ADs`Y935CD`Nz<}B)0&yhLF068e3PyF* ze9;u^xiAEttA52AqE+;1td1X7H&O+H7N~02I zJf*ppDz`V&-)yZWob%yJXe2Yum2|UhL~r_v421{6sgLDa@kEZ6o@W26iE9stDf{DR zriR?LsHP;8+QOipN>P;Nmz2_bs!fSeT0QJm?9z|udW7Djl8n@*#v>ZL$h#|g{IF#E zd9KG)XgwR&qmuf4&TaqLKhL@Me4po>d+s@(^N}f%yfPJ+Xxw(`7XJ>anXrPky8}IA zf?b6tSSt#_nSsaq@R-tJ%pdUlla5bpv0BWF)y#reeMMaW!^?Hk@i8;kR%${9ir?!O z*ZFOfbp`)j)GMwVjiT)DqHb{=R?C_T0|B}}&Ms{=m_VW=yyXA+#4x5{0_nhoe|U2M z|M9ay-%(w`XV!mYnIr`iBpsf-O{D;3H~^z`eHU-B^xo#m#PJs;3&*F?j>p;#@o5F) z)~SU9ild=8Oe2< zS>q(+ng@+8_W+%;Zi=?jdMY2nm80gwFu&?5>U#Mwcz-{%OmYoua#!ai4aKi5QHS80 zy=2u!N+`oOsG$uq!R*xKLMdr7th-L@6_zJ4SLgEsg^M<63vCh@np4&qK6Z&FWs)j+ zytXPMf!-4P%-{A^JG;t4(;FmJo%T6j2SH9Um}4empWTEvLy^^L?ymx<=n4OA;n0_x zHcs<3Gfi!L`&-sh>Zgw}?7)&Mt#?0YGhk^9m=0x4I2hpi7f{C!UA(bjyJ@zp>05@5 zH}<~sbeuJ{jEaie(JWmR!#()_T*Om?K*zI}(*Rg8FAwZLn%IRC0dCI2g z1BvmjkDG0W#58aIM>tO9G>eUK!umSt4Mr~aloE2qFu%dskJK_B-ZDjWR#eOTJps~L zcD`j15?;Bcz|4GA19^SZA#K&VuR5p&DbM&HfadX_EUPN^UAH6Xfz+R52Hg8k@!Hks#Wetx?zG zOXq+3;tgkP^1RU6*KJ7MWM|@XcWRcPi;{RPsX-Pmm2%1g>Wkh{OzbdsD4$i%WTzwfkKRYRL8HW>;w|&S-vZ^r0B*7-C0rOnL0}*_kC<0X{!6lB^7D(e#%Fc zTZpJsoZIr12kL4@w}ZUx+LcjCF3{xMDb)O6*L7MLn$aLp4SJ18tM#JB%xM7~Sq0D# zp_dSY_VF|ZnlzgnD;k3-zz0@!w>74rqp>%fE`#)o!gN)Y3E-FmC?ggt%c;k`HyD73 zt!ZvX`#q#n`iDu~f=cP2>F#7{WLEng$3^1+A3p1%uj8{}IgXeez4*3Pqs$|h4VC(8 zE2Y;LU7`)K-pVO^9wV0ifVww<;Kw7UUkzB|DSnzgu0n*vOy}DP0}!+2@p6Gsvg}ZM&j7J@Wb${TNh>KbJ(? zo63zLqP(VvIu@?@#jRzAhu~VnSS2yZ#V&Vh{M#;lYsXfxJfQ6@rmT5RecIbWYsb}x z)BREF8pB){LGR-k9z^4t{aA&XU90+r^*#(B3jycoI-mEn5536fmgK4izY69GVak#3 zP6H-P+;2wKiPfO65nKp~qR4EU;~_SBwD0D^)Gr0eQs9t2IrVC*rsd8Nyy~?U1bLC#@!^f04{%&q&#Fi!`O!-dqyG*cqCv)U-(%GRyaCR;l!vFDj&_qdbv$lJuF zQ!TIdtWQFOBVrM-W`kC7sh_0~yQ#yecmEoqBe|8iLGAg=G0$Ymw3RM8^80p1^>}sdjF&jCIV{IY3CYd6Cdt z>L4nbpUwj6IT3QqAxFW>2|sS(LZ5r!c{u-kM`3i`aYqK5%SvJa%ec9jaz?sYMjlba zSjQ94-0S8bQm>9H#J;rkFNPPz>~N$drGf^dVP$&7ZvlY)wTjvBThK4<_wDGk2lFs0 z))t%^Y(W0Zl`~ufB$@;WeiXUv`mK)Crw9F#nQoZ@(4X-3tXf@{3$W?1B>(E|F-lX@sx9;b-2a zhVRaZ**SVBpE1t$6mT(N-n)c}7~kalM4L2*oSzoKNN2#OH9=8~0a~0SVWwlAjTR~9{Onwzt^-c?)$~G-)`&KbAw0tv1M2?Bc zb%^Q;PT$6GPGJwi8Bz;p)GIB`Cz1A8-T4Bw47NWw+E5-y-XESjMa1rbv6>=dT?4i~ zo<29jbEj%|H<8*Q>~bJUF2)Ia^-tYqxAq<&%Ma2PyATe!v!zL)Q0{CM32tk52NC;y z{)U{DqXqEvWPguTpDqX0WX}cTJyNHq$&oRzaH4yGT!Pl(B)jDa^Y-D6K&w zqdDnk0|Yr-kc13HoLHNP_41K{VD!!xa;11)bm{5fHD(5CN2}YN0yzWst|=A$r%Vmh z@m8+5Z-enmyfz21nTBOzkYzC>rK-f`$VaX6cwH|6-@`ep7u~XXqBR{h6Nq{|1ZcMD zz5K$xJu#(af4VemBrL zHRB-X9Of2COVjS>rH`5-c>nUJ2A&l&z{Do16o(KJz zyLAs?k0OrJPA_j|-44b2p0z)XA<}C2C=dFetli_Ec1M4nfGtDRxaD4tJc4A0G;6Ky z%$gTobyth%W7;4`z1)JIwXDc4Pk$ajSPNLd9l@KyFZPwx!5={5qxSPVx? zQ!Q(24Y>lr%h+xQc2SUrHff%UdN%KKTU@ox#~(ZT7k_ENvgl6#AQGjVWHKf%AqZbT O;yKsX?Wn7K +#include +#include +#include +#include +#include +#include #include "about_dialog.h" +#include "main_window_themes.h" #include "ui_about_dialog.h" AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent), ui(new Ui::AboutDialog) { ui->setupUi(this); + preloadImages(); + + ui->image_1->setAttribute(Qt::WA_Hover, true); + ui->image_2->setAttribute(Qt::WA_Hover, true); + ui->image_3->setAttribute(Qt::WA_Hover, true); + ui->image_4->setAttribute(Qt::WA_Hover, true); + ui->image_5->setAttribute(Qt::WA_Hover, true); + + ui->image_1->installEventFilter(this); + ui->image_2->installEventFilter(this); + ui->image_3->installEventFilter(this); + ui->image_4->installEventFilter(this); + ui->image_5->installEventFilter(this); } AboutDialog::~AboutDialog() { delete ui; } + +void AboutDialog::preloadImages() { + originalImages[0] = ui->image_1->pixmap().copy(); + originalImages[1] = ui->image_2->pixmap().copy(); + originalImages[2] = ui->image_3->pixmap().copy(); + originalImages[3] = ui->image_4->pixmap().copy(); + originalImages[4] = ui->image_5->pixmap().copy(); + + for (int i = 0; i < 5; ++i) { + QImage image = originalImages[i].toImage(); + for (int y = 0; y < image.height(); ++y) { + for (int x = 0; x < image.width(); ++x) { + QColor color = image.pixelColor(x, y); + color.setRed(255 - color.red()); + color.setGreen(255 - color.green()); + color.setBlue(255 - color.blue()); + image.setPixelColor(x, y, color); + } + } + invertedImages[i] = QPixmap::fromImage(image); + } + updateImagesForCurrentTheme(); +} + +void AboutDialog::updateImagesForCurrentTheme() { + Theme currentTheme = static_cast(Config::getMainWindowTheme()); + bool isDarkTheme = (currentTheme == Theme::Dark || currentTheme == Theme::Green || + currentTheme == Theme::Blue || currentTheme == Theme::Violet); + if (isDarkTheme) { + ui->image_1->setPixmap(invertedImages[0]); + ui->image_2->setPixmap(invertedImages[1]); + ui->image_3->setPixmap(invertedImages[2]); + ui->image_4->setPixmap(invertedImages[3]); + ui->image_5->setPixmap(invertedImages[4]); + } else { + ui->image_1->setPixmap(originalImages[0]); + ui->image_2->setPixmap(originalImages[1]); + ui->image_3->setPixmap(originalImages[2]); + ui->image_4->setPixmap(originalImages[3]); + ui->image_5->setPixmap(originalImages[4]); + } +} + +bool AboutDialog::eventFilter(QObject* obj, QEvent* event) { + if (event->type() == QEvent::Enter) { + if (obj == ui->image_1) { + if (isDarkTheme()) { + ui->image_1->setPixmap(originalImages[0]); + } else { + ui->image_1->setPixmap(invertedImages[0]); + } + applyHoverEffect(ui->image_1); + } else if (obj == ui->image_2) { + if (isDarkTheme()) { + ui->image_2->setPixmap(originalImages[1]); + } else { + ui->image_2->setPixmap(invertedImages[1]); + } + applyHoverEffect(ui->image_2); + } else if (obj == ui->image_3) { + if (isDarkTheme()) { + ui->image_3->setPixmap(originalImages[2]); + } else { + ui->image_3->setPixmap(invertedImages[2]); + } + applyHoverEffect(ui->image_3); + } else if (obj == ui->image_4) { + if (isDarkTheme()) { + ui->image_4->setPixmap(originalImages[3]); + } else { + ui->image_4->setPixmap(invertedImages[3]); + } + applyHoverEffect(ui->image_4); + } else if (obj == ui->image_5) { + if (isDarkTheme()) { + ui->image_5->setPixmap(originalImages[4]); + } else { + ui->image_5->setPixmap(invertedImages[4]); + } + applyHoverEffect(ui->image_5); + } + } else if (event->type() == QEvent::Leave) { + if (obj == ui->image_1) { + if (isDarkTheme()) { + ui->image_1->setPixmap(invertedImages[0]); + } else { + ui->image_1->setPixmap(originalImages[0]); + } + removeHoverEffect(ui->image_1); + } else if (obj == ui->image_2) { + if (isDarkTheme()) { + ui->image_2->setPixmap(invertedImages[1]); + } else { + ui->image_2->setPixmap(originalImages[1]); + } + removeHoverEffect(ui->image_2); + } else if (obj == ui->image_3) { + if (isDarkTheme()) { + ui->image_3->setPixmap(invertedImages[2]); + } else { + ui->image_3->setPixmap(originalImages[2]); + } + removeHoverEffect(ui->image_3); + } else if (obj == ui->image_4) { + if (isDarkTheme()) { + ui->image_4->setPixmap(invertedImages[3]); + } else { + ui->image_4->setPixmap(originalImages[3]); + } + removeHoverEffect(ui->image_4); + } else if (obj == ui->image_5) { + if (isDarkTheme()) { + ui->image_5->setPixmap(invertedImages[4]); + } else { + ui->image_5->setPixmap(originalImages[4]); + } + removeHoverEffect(ui->image_5); + } + } else if (event->type() == QEvent::MouseButtonPress) { + if (obj == ui->image_1) { + QDesktopServices::openUrl(QUrl("https://github.com/shadps4-emu/shadPS4")); + } else if (obj == ui->image_2) { + QDesktopServices::openUrl(QUrl("https://discord.gg/bFJxfftGW6")); + } else if (obj == ui->image_3) { + QDesktopServices::openUrl(QUrl("https://www.youtube.com/@shadPS4/videos")); + } else if (obj == ui->image_4) { + QDesktopServices::openUrl(QUrl("https://ko-fi.com/shadps4")); + } else if (obj == ui->image_5) { + QDesktopServices::openUrl(QUrl("https://shadps4.net")); + } + return true; + } + return QDialog::eventFilter(obj, event); +} + +void AboutDialog::applyHoverEffect(QLabel* label) { + QColor shadowColor = isDarkTheme() ? QColor(0, 0, 0) : QColor(169, 169, 169); + QGraphicsDropShadowEffect* shadow = new QGraphicsDropShadowEffect; + shadow->setBlurRadius(5); + shadow->setXOffset(2); + shadow->setYOffset(2); + shadow->setColor(shadowColor); + label->setGraphicsEffect(shadow); +} + +void AboutDialog::removeHoverEffect(QLabel* label) { + QColor shadowColor = isDarkTheme() ? QColor(50, 50, 50) : QColor(169, 169, 169); + QGraphicsDropShadowEffect* shadow = new QGraphicsDropShadowEffect; + shadow->setBlurRadius(3); + shadow->setXOffset(0); + shadow->setYOffset(0); + shadow->setColor(shadowColor); + label->setGraphicsEffect(shadow); +} + +bool AboutDialog::isDarkTheme() const { + Theme currentTheme = static_cast(Config::getMainWindowTheme()); + return currentTheme == Theme::Dark || currentTheme == Theme::Green || + currentTheme == Theme::Blue || currentTheme == Theme::Violet; +} diff --git a/src/qt_gui/about_dialog.h b/src/qt_gui/about_dialog.h index 8c802221b..42e8d557a 100644 --- a/src/qt_gui/about_dialog.h +++ b/src/qt_gui/about_dialog.h @@ -3,7 +3,11 @@ #pragma once +#include #include +#include +#include +#include namespace Ui { class AboutDialog; @@ -15,7 +19,18 @@ class AboutDialog : public QDialog { public: explicit AboutDialog(QWidget* parent = nullptr); ~AboutDialog(); + bool eventFilter(QObject* obj, QEvent* event); private: Ui::AboutDialog* ui; -}; \ No newline at end of file + + void preloadImages(); + void updateImagesForCurrentTheme(); + void applyHoverEffect(QLabel* label); + void removeHoverEffect(QLabel* label); + + bool isDarkTheme() const; + + QPixmap originalImages[5]; + QPixmap invertedImages[5]; +}; diff --git a/src/qt_gui/about_dialog.ui b/src/qt_gui/about_dialog.ui index e2e76f4c4..19840e452 100644 --- a/src/qt_gui/about_dialog.ui +++ b/src/qt_gui/about_dialog.ui @@ -9,7 +9,7 @@ 0 0 780 - 320 + 310 @@ -22,14 +22,14 @@ - 10 - 30 + 15 + 15 271 - 261 + 271 - QFrame::Shape::NoFrame + QFrame::NoFrame @@ -45,7 +45,7 @@ 310 - 40 + 15 171 41 @@ -64,9 +64,9 @@ 310 - 90 + 60 451 - 101 + 70 @@ -85,9 +85,9 @@ 310 - 180 + 130 451 - 101 + 70 @@ -102,6 +102,131 @@ true + + + + 310 + 210 + 80 + 80 + + + + ArrowCursor + + + QFrame::NoFrame + + + + + + :/images/github.png + + + true + + + + + + 400 + 210 + 80 + 80 + + + + ArrowCursor + + + QFrame::NoFrame + + + + + + :/images/discord.png + + + true + + + + + + 490 + 210 + 80 + 80 + + + + ArrowCursor + + + QFrame::NoFrame + + + + + + :/images/youtube.png + + + true + + + + + + 580 + 210 + 80 + 80 + + + + ArrowCursor + + + QFrame::NoFrame + + + + + + :/images/ko-fi.png + + + true + + + + + + 670 + 210 + 80 + 80 + + + + ArrowCursor + + + QFrame::NoFrame + + + + + + :/images/website.png + + + true + + diff --git a/src/shadps4.qrc b/src/shadps4.qrc index e328f2c42..30f234ed8 100644 --- a/src/shadps4.qrc +++ b/src/shadps4.qrc @@ -25,5 +25,10 @@ images/flag_us.png images/flag_world.png images/flag_china.png + images/github.png + images/discord.png + images/ko-fi.png + images/youtube.png + images/website.png From f623613d120d6b0dd66dcdb5584ca569d4ff2b0a Mon Sep 17 00:00:00 2001 From: Martin <67326368+Martini-141@users.noreply.github.com> Date: Mon, 9 Dec 2024 17:53:25 +0100 Subject: [PATCH 139/549] Update nb translations (#1712) * update nb_NO.ts * small grammar changes * revert to nb.ts --- src/qt_gui/translations/{nb_NO.ts => nb.ts} | 72 ++++++++++----------- 1 file changed, 36 insertions(+), 36 deletions(-) rename src/qt_gui/translations/{nb_NO.ts => nb.ts} (94%) diff --git a/src/qt_gui/translations/nb_NO.ts b/src/qt_gui/translations/nb.ts similarity index 94% rename from src/qt_gui/translations/nb_NO.ts rename to src/qt_gui/translations/nb.ts index de0a88b73..303baadaf 100644 --- a/src/qt_gui/translations/nb_NO.ts +++ b/src/qt_gui/translations/nb.ts @@ -90,7 +90,7 @@ The value for location to install games is not valid. - Verdien for mappen for å installere spill er ikke gyldig. + Stien for å installere spillet er ikke gyldig. @@ -123,7 +123,7 @@ Open Game Folder - Åpne Spillmappe + Åpne Spillmappen @@ -208,7 +208,7 @@ requiresEnableSeparateUpdateFolder_MSG - Denne funksjonen krever 'Aktiver seperat oppdateringsmappe' konfigurasjonsalternativet. Hvis du vil bruke denne funksjonen, vennligst aktiver den. + Denne funksjonen krever 'Aktiver seperat oppdateringsmappe' konfigurasjonsalternativet. Hvis du vil bruke denne funksjonen, må du aktiver den. @@ -261,7 +261,7 @@ Check for Updates - Sjekk etter oppdateringer + Se etter oppdateringer @@ -500,7 +500,7 @@ Show Splash - Vis Velkomst + Vis Velkomstbilde @@ -655,7 +655,7 @@ Check for Updates at Startup - Sjekk etter oppdateringer ved oppstart + Se etter oppdateringer ved oppstart @@ -665,7 +665,7 @@ Check for Updates - Sjekk for oppdateringer + Se etter oppdateringer @@ -718,7 +718,7 @@ Patches Downloaded Successfully! - Programrettelser lastet ned vellykket! + Programrettelser ble lastet ned! @@ -828,7 +828,7 @@ Game successfully installed at %1 - Spillet ble installert vellykket på %1 + Spillet ble installert i %1 @@ -841,12 +841,12 @@ Cheats / Patches for - Cheats / Patches for + Juks / Programrettelser for defaultTextEdit_MSG - Juks/programrettelse er eksperimentelle.\nBruk med forsiktighet.\n\nLast ned juks individuelt ved å velge pakkebrønn og klikke på nedlastingsknappen.\nPå fanen programrettelse kan du laste ned alle programrettelser samtidig, velge hvilke du ønsker å bruke, og lagre valget ditt.\n\nSiden vi ikke utvikler Juksene/Programrettelsene,\nvær vennlig å rapportere problemer til juks-utvikleren.\n\nHar du laget en ny juks? Besøk:\nhttps://github.com/shadps4-emu/ps4_cheats + Juks/programrettelse er eksperimentelle.\nBruk med forsiktighet.\n\nLast ned juks individuelt ved å velge pakkebrønn og klikke på nedlastingsknappen.\nPå fanen programrettelse kan du laste ned alle programrettelser samtidig, velge hvilke du ønsker å bruke, og lagre valget ditt.\n\nSiden vi ikke utvikler Juksene/Programrettelsene,\nvær vennlig å rapportere problemer til jukse/programrettelse utvikleren.\n\nHar du laget en ny juks? Besøk:\nhttps://github.com/shadps4-emu/ps4_cheats @@ -871,7 +871,7 @@ Select Cheat File: - Velg juksfil: + Velg juksefil: @@ -896,7 +896,7 @@ You can delete the cheats you don't want after downloading them. - Du kan slette jukser du ikke ønsker etter å ha lastet dem ned. + Du kan slette juksene du ikke ønsker etter å ha lastet dem ned. @@ -971,12 +971,12 @@ Options saved successfully. - Alternativer lagret vellykket. + Alternativer ble lagret. Invalid Source - Ugyldig kilde + Ugyldig Kilde @@ -986,7 +986,7 @@ File Exists - Filen eksisterer + Filen Eksisterer @@ -996,17 +996,17 @@ Failed to save file: - Kunne ikke lagre fil: + Kunne ikke lagre filen: Failed to download file: - Kunne ikke laste ned fil: + Kunne ikke laste ned filen: Cheats Not Found - Jukser ikke funnet + Fant ikke juksene @@ -1016,12 +1016,12 @@ Cheats Downloaded Successfully - Jukser lastet ned vellykket + Juksene ble lastet ned CheatsDownloadedSuccessfully_MSG - Du har lastet ned jukser vellykket for denne versjonen av spillet fra den valgte pakkebrønnen. Du kan prøve å laste ned fra en annen pakkebrønn, hvis det er tilgjengelig, vil det også være mulig å bruke det ved å velge filen fra listen. + Du har lastet ned jukser for denne versjonen av spillet fra den valgte pakkebrønnen. Du kan prøve å laste ned fra en annen pakkebrønn, hvis det er tilgjengelig, vil det også være mulig å bruke det ved å velge filen fra listen. @@ -1041,7 +1041,7 @@ DownloadComplete_MSG - Oppdateringer lastet ned vellykket! Alle programrettelsene tilgjengelige for alle spill har blitt lastet ned, det er ikke nødvendig å laste dem ned individuelt for hvert spill som skjer med jukser. Hvis programrettelsen ikke vises, kan det hende at den ikke finnes for den spesifikke serienummeret og versjonen av spillet. + Programrettelser ble lastet ned! Alle programrettelsene tilgjengelige for alle spill har blitt lastet ned, det er ikke nødvendig å laste dem ned individuelt for hvert spill som skjer med jukser. Hvis programrettelsen ikke vises, kan det hende at den ikke finnes for den spesifikke serienummeret og versjonen av spillet. @@ -1076,7 +1076,7 @@ Failed to open file: - Kunne ikke åpne fil: + Kunne ikke åpne filen: @@ -1111,7 +1111,7 @@ Can't apply cheats before the game is started - Kan ikke bruke juksetriks før spillet er startet. + Kan ikke bruke juksene før spillet er startet. @@ -1154,7 +1154,7 @@ fullscreenCheckBox - Aktiver fullskjerm:\nSetter automatisk spillvinduet i fullskjermmodus.\nDette kan slås av ved å trykke på F11-tasten. + Aktiver fullskjerm:\nSetter spillvinduet automatisk i fullskjermmodus.\nDette kan slås av ved å trykke på F11-tasten. @@ -1164,12 +1164,12 @@ showSplashCheckBox - Vis startskjerm:\nViser spillets startskjerm (et spesialbilde) når spillet starter. + Vis Velkomstbilde:\nViser spillets velkomstbilde (et spesialbilde) når spillet starter. ps4proCheckBox - Er PS4 Pro:\nFår emulatoren til å fungere som en PS4 PRO, noe som kan aktivere spesielle funksjoner i spill som støtter dette. + Er PS4 Pro:\nFår etterligneren til å fungere som en PS4 PRO, noe som kan aktivere spesielle funksjoner i spill som støtter dette. @@ -1199,7 +1199,7 @@ GUIgroupBox - Spille tittelmusikk:\nHvis et spill støtter det, aktiverer spesiell musikk når du velger spillet i menyen. + Spille tittelmusikk:\nHvis et spill støtter det, så aktiveres det spesiell musikk når du velger spillet i menyen. @@ -1254,7 +1254,7 @@ graphicsAdapterGroupBox - Grafikkenhet:\nI systemer med flere GPU-er, velg GPU-en etterligneren skal bruke fra rullegardinlisten,\neller velg "Auto Select" for å bestemme det automatisk. + Grafikkenhet:\nI systemer med flere GPU-er, velg GPU-en etterligneren skal bruke fra rullegardinlisten,\neller velg "Auto Select" for å bestemme den automatisk. @@ -1264,7 +1264,7 @@ heightDivider - Vblank Skillelinje:\nBildehastigheten som etterligneren oppdaterer ved, multipliseres med dette tallet. Endring av dette kan ha negative effekter, som å øke hastigheten på spillet, eller ødelegge kritisk spillfunksjonalitet som ikke forventer at dette endres! + Vblank Skillelinje:\nBildehastigheten som etterligneren oppdaterer ved, multipliseres med dette tallet. Endring av dette kan ha negative effekter, som å øke hastigheten av spillet, eller ødelegge kritisk spillfunksjonalitet som ikke forventer at dette endres! @@ -1274,12 +1274,12 @@ nullGpuCheckBox - Aktiver Null GPU:\nFor teknisk feilsøking deaktiverer spillgjengivelse som om det ikke var noe grafikkort. + Aktiver Null GPU:\nFor teknisk feilsøking deaktiverer spillets-gjengivelse som om det ikke var noe grafikkort. gameFoldersBox - Spillmapper:\nListen over mapper for å sjekke installerte spill. + Spillmapper:\nListen over mapper som brukes for å se etter installerte spill. @@ -1299,12 +1299,12 @@ vkValidationCheckBox - Aktiver Vulkan valideringslag:\nAktiverer et system som validerer tilstanden til Vulkan-gjengiveren og logger informasjon om dens indre tilstand. Dette vil redusere ytelsen og sannsynligvis endre etterlignerens oppførsel. + Aktiver Vulkan valideringslag:\nAktiverer et system som validerer tilstanden til Vulkan-gjengiveren og logger informasjon om dens indre tilstand. Dette vil redusere ytelsen og sannsynligvis endre etterlignerens atferd. vkSyncValidationCheckBox - Aktiver Vulkan synkronisering validering:\nAktiverer et system som validerer frekvens tiden av Vulkan-gjengivelsensoppgaver. Dette vil redusere ytelsen og sannsynligvis endre etterlignerens oppførsel. + Aktiver Vulkan synkronisering validering:\nAktiverer et system som validerer frekvens tiden av Vulkan-gjengivelsensoppgaver. Dette vil redusere ytelsen og sannsynligvis endre etterlignerens atferd. @@ -1365,7 +1365,7 @@ Auto Updater - Automatisk oppdaterer + Automatisk oppdaterering @@ -1435,7 +1435,7 @@ Check for Updates at Startup - Sjekk etter oppdateringer ved oppstart + Se etter oppdateringer ved oppstart From f1b23c616e266fdc726b7e352faa5b0ec897361a Mon Sep 17 00:00:00 2001 From: Vinicius Rangel Date: Mon, 9 Dec 2024 17:11:11 -0300 Subject: [PATCH 140/549] Devtools - Shader editing (#1705) * devtools: shader editing and compiling * devtools: patch shader at runtime * devtools: shader editing load patch even with config disabled --- src/common/string_util.cpp | 4 + src/common/string_util.h | 2 + src/core/debug_state.cpp | 11 +- src/core/debug_state.h | 42 +++- src/core/devtools/options.h | 4 +- src/core/devtools/widget/common.h | 31 ++- src/core/devtools/widget/shader_list.cpp | 230 +++++++++++++++--- src/core/devtools/widget/shader_list.h | 28 ++- src/core/devtools/widget/text_editor.cpp | 49 +++- src/core/devtools/widget/text_editor.h | 1 + src/sdl_window.cpp | 15 ++ src/sdl_window.h | 5 + .../renderer_vulkan/vk_compute_pipeline.cpp | 2 +- .../renderer_vulkan/vk_compute_pipeline.h | 24 +- .../renderer_vulkan/vk_graphics_pipeline.h | 2 + .../renderer_vulkan/vk_pipeline_cache.cpp | 80 ++++-- .../renderer_vulkan/vk_pipeline_cache.h | 27 +- src/video_core/renderer_vulkan/vk_presenter.h | 8 + .../renderer_vulkan/vk_rasterizer.h | 4 + 19 files changed, 466 insertions(+), 103 deletions(-) diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index 6d5a254cd..4658d0ef4 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp @@ -37,6 +37,10 @@ std::vector SplitString(const std::string& str, char delimiter) { return output; } +std::string_view U8stringToString(std::u8string_view u8str) { + return std::string_view{reinterpret_cast(u8str.data()), u8str.size()}; +} + #ifdef _WIN32 static std::wstring CPToUTF16(u32 code_page, std::string_view input) { const auto size = diff --git a/src/common/string_util.h b/src/common/string_util.h index 23e82b93c..18972de44 100644 --- a/src/common/string_util.h +++ b/src/common/string_util.h @@ -16,6 +16,8 @@ void ToLowerInPlace(std::string& str); std::vector SplitString(const std::string& str, char delimiter); +std::string_view U8stringToString(std::u8string_view u8str); + #ifdef _WIN32 [[nodiscard]] std::string UTF16ToUTF8(std::wstring_view input); [[nodiscard]] std::wstring UTF8ToUTF16W(std::string_view str); diff --git a/src/core/debug_state.cpp b/src/core/debug_state.cpp index 562cb62e8..649624924 100644 --- a/src/core/debug_state.cpp +++ b/src/core/debug_state.cpp @@ -177,9 +177,10 @@ void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr, } } -void DebugStateImpl::CollectShader(const std::string& name, std::span spv, - std::span raw_code) { - shader_dump_list.emplace_back(name, std::vector{spv.begin(), spv.end()}, - std::vector{raw_code.begin(), raw_code.end()}); - std::ranges::sort(shader_dump_list, {}, &ShaderDump::name); +void DebugStateImpl::CollectShader(const std::string& name, vk::ShaderModule module, + std::span spv, std::span raw_code, + std::span patch_spv, bool is_patched) { + shader_dump_list.emplace_back(name, module, std::vector{spv.begin(), spv.end()}, + std::vector{raw_code.begin(), raw_code.end()}, + std::vector{patch_spv.begin(), patch_spv.end()}, is_patched); } diff --git a/src/core/debug_state.h b/src/core/debug_state.h index 759755b52..fa2e5cd9d 100644 --- a/src/core/debug_state.h +++ b/src/core/debug_state.h @@ -12,7 +12,7 @@ #include "common/types.h" #include "video_core/amdgpu/liverpool.h" -#include "video_core/renderer_vulkan/vk_pipeline_cache.h" +#include "video_core/renderer_vulkan/vk_graphics_pipeline.h" #ifdef _WIN32 #ifndef WIN32_LEAN_AND_MEAN @@ -76,29 +76,46 @@ struct FrameDump { struct ShaderDump { std::string name; + vk::ShaderModule module; + std::vector spv; - std::vector raw_code; + std::vector isa; + std::vector patch_spv; + std::string patch_source{}; + + bool loaded_data = false; + bool is_patched = false; std::string cache_spv_disasm{}; - std::string cache_raw_disasm{}; + std::string cache_isa_disasm{}; + std::string cache_patch_disasm{}; - ShaderDump(std::string name, std::vector spv, std::vector raw_code) - : name(std::move(name)), spv(std::move(spv)), raw_code(std::move(raw_code)) {} + ShaderDump(std::string name, vk::ShaderModule module, std::vector spv, + std::vector isa, std::vector patch_spv, bool is_patched) + : name(std::move(name)), module(module), spv(std::move(spv)), isa(std::move(isa)), + patch_spv(std::move(patch_spv)), is_patched(is_patched) {} ShaderDump(const ShaderDump& other) = delete; ShaderDump(ShaderDump&& other) noexcept - : name{std::move(other.name)}, spv{std::move(other.spv)}, - raw_code{std::move(other.raw_code)}, cache_spv_disasm{std::move(other.cache_spv_disasm)}, - cache_raw_disasm{std::move(other.cache_raw_disasm)} {} + : name{std::move(other.name)}, module{std::move(other.module)}, spv{std::move(other.spv)}, + isa{std::move(other.isa)}, patch_spv{std::move(other.patch_spv)}, + patch_source{std::move(other.patch_source)}, + cache_spv_disasm{std::move(other.cache_spv_disasm)}, + cache_isa_disasm{std::move(other.cache_isa_disasm)}, + cache_patch_disasm{std::move(other.cache_patch_disasm)} {} ShaderDump& operator=(const ShaderDump& other) = delete; ShaderDump& operator=(ShaderDump&& other) noexcept { if (this == &other) return *this; name = std::move(other.name); + module = std::move(other.module); spv = std::move(other.spv); - raw_code = std::move(other.raw_code); + isa = std::move(other.isa); + patch_spv = std::move(other.patch_spv); + patch_source = std::move(other.patch_source); cache_spv_disasm = std::move(other.cache_spv_disasm); - cache_raw_disasm = std::move(other.cache_raw_disasm); + cache_isa_disasm = std::move(other.cache_isa_disasm); + cache_patch_disasm = std::move(other.cache_patch_disasm); return *this; } }; @@ -186,8 +203,9 @@ public: void PushRegsDump(uintptr_t base_addr, uintptr_t header_addr, const AmdGpu::Liverpool::Regs& regs, bool is_compute = false); - void CollectShader(const std::string& name, std::span spv, - std::span raw_code); + void CollectShader(const std::string& name, vk::ShaderModule module, std::span spv, + std::span raw_code, std::span patch_spv, + bool is_patched); }; } // namespace DebugStateType diff --git a/src/core/devtools/options.h b/src/core/devtools/options.h index 70e1d137b..a859a2eec 100644 --- a/src/core/devtools/options.h +++ b/src/core/devtools/options.h @@ -10,8 +10,8 @@ struct ImGuiTextBuffer; namespace Core::Devtools { struct TOptions { - std::string disassembler_cli_isa{"clrxdisasm --raw \"{src}\""}; - std::string disassembler_cli_spv{"spirv-cross -V \"{src}\""}; + std::string disassembler_cli_isa{"clrxdisasm --raw {src}"}; + std::string disassembler_cli_spv{"spirv-cross -V {src}"}; bool frame_dump_render_on_collapse{false}; }; diff --git a/src/core/devtools/widget/common.h b/src/core/devtools/widget/common.h index 5f669eb65..75eb55301 100644 --- a/src/core/devtools/widget/common.h +++ b/src/core/devtools/widget/common.h @@ -117,7 +117,7 @@ static bool IsDrawCall(AmdGpu::PM4ItOpcode opcode) { inline std::optional exec_cli(const char* cli) { std::array buffer{}; std::string output; - const auto f = popen(cli, "r"); + const auto f = popen(cli, "rt"); if (!f) { pclose(f); return {}; @@ -129,21 +129,27 @@ inline std::optional exec_cli(const char* cli) { return output; } -inline std::string RunDisassembler(const std::string& disassembler_cli, - const std::vector& shader_code) { +template +inline std::string RunDisassembler(const std::string& disassembler_cli, const T& shader_code, + bool* success = nullptr) { std::string shader_dis; if (disassembler_cli.empty()) { shader_dis = "No disassembler set"; + if (success) { + *success = false; + } } else { auto bin_path = std::filesystem::temp_directory_path() / "shadps4_tmp_shader.bin"; constexpr std::string_view src_arg = "{src}"; - std::string cli = disassembler_cli; + std::string cli = disassembler_cli + " 2>&1"; const auto pos = cli.find(src_arg); if (pos == std::string::npos) { - DebugState.ShowDebugMessage("Disassembler CLI does not contain {src} argument\n" + - disassembler_cli); + shader_dis = "Disassembler CLI does not contain {src} argument"; + if (success) { + *success = false; + } } else { cli.replace(pos, src_arg.size(), "\"" + bin_path.string() + "\""); Common::FS::IOFile file(bin_path, Common::FS::FileAccessMode::Write); @@ -151,9 +157,16 @@ inline std::string RunDisassembler(const std::string& disassembler_cli, file.Close(); auto result = exec_cli(cli.c_str()); - shader_dis = result.value_or("Could not disassemble shader"); - if (shader_dis.empty()) { - shader_dis = "Disassembly empty or failed"; + if (result) { + shader_dis = result.value(); + if (success) { + *success = true; + } + } else { + if (success) { + *success = false; + } + shader_dis = "Could not disassemble shader"; } std::filesystem::remove(bin_path); diff --git a/src/core/devtools/widget/shader_list.cpp b/src/core/devtools/widget/shader_list.cpp index b056880dd..80c939718 100644 --- a/src/core/devtools/widget/shader_list.cpp +++ b/src/core/devtools/widget/shader_list.cpp @@ -1,66 +1,221 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include + #include "shader_list.h" #include #include "common.h" #include "common/config.h" +#include "common/path_util.h" +#include "common/string_util.h" #include "core/debug_state.h" #include "core/devtools/options.h" #include "imgui/imgui_std.h" +#include "sdl_window.h" +#include "video_core/renderer_vulkan/vk_presenter.h" +#include "video_core/renderer_vulkan/vk_rasterizer.h" + +extern std::unique_ptr presenter; using namespace ImGui; namespace Core::Devtools::Widget { -void ShaderList::DrawShader(DebugStateType::ShaderDump& value) { - if (!loaded_data) { - loaded_data = true; - if (value.cache_raw_disasm.empty()) { - value.cache_raw_disasm = RunDisassembler(Options.disassembler_cli_isa, value.raw_code); - } - isa_editor.SetText(value.cache_raw_disasm); +ShaderList::Selection::Selection(int index) : index(index) { + isa_editor.SetPalette(TextEditor::GetDarkPalette()); + isa_editor.SetReadOnly(true); + glsl_editor.SetPalette(TextEditor::GetDarkPalette()); + glsl_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::GLSL()); + presenter->GetWindow().RequestKeyboard(); +} +ShaderList::Selection::~Selection() { + presenter->GetWindow().ReleaseKeyboard(); +} + +void ShaderList::Selection::ReloadShader(DebugStateType::ShaderDump& value) { + auto& spv = value.is_patched ? value.patch_spv : value.spv; + if (spv.empty()) { + return; + } + auto& cache = presenter->GetRasterizer().GetPipelineCache(); + if (const auto m = cache.ReplaceShader(value.module, spv); m) { + value.module = *m; + } +} + +bool ShaderList::Selection::DrawShader(DebugStateType::ShaderDump& value) { + if (!value.loaded_data) { + value.loaded_data = true; + if (value.cache_isa_disasm.empty()) { + value.cache_isa_disasm = RunDisassembler(Options.disassembler_cli_isa, value.isa); + } if (value.cache_spv_disasm.empty()) { value.cache_spv_disasm = RunDisassembler(Options.disassembler_cli_spv, value.spv); } - spv_editor.SetText(value.cache_spv_disasm); + if (!value.patch_spv.empty() && value.cache_patch_disasm.empty()) { + value.cache_patch_disasm = RunDisassembler("spirv-dis {src}", value.patch_spv); + } + patch_path = + Common::FS::GetUserPath(Common::FS::PathType::ShaderDir) / "patch" / value.name; + patch_bin_path = patch_path; + patch_bin_path += ".spv"; + patch_path += ".glsl"; + if (std::filesystem::exists(patch_path)) { + std::ifstream file{patch_path}; + value.patch_source = + std::string{std::istreambuf_iterator{file}, std::istreambuf_iterator{}}; + } + + value.is_patched = !value.patch_spv.empty(); + if (!value.is_patched) { // No patch + isa_editor.SetText(value.cache_isa_disasm); + glsl_editor.SetText(value.cache_spv_disasm); + } else { + isa_editor.SetText(value.cache_patch_disasm); + isa_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::SPIRV()); + glsl_editor.SetText(value.patch_source); + glsl_editor.SetReadOnly(false); + } } - if (SmallButton("<-")) { - selected_shader = -1; + char name[64]; + snprintf(name, sizeof(name), "Shader %s", value.name.c_str()); + SetNextWindowSize({450.0f, 600.0f}, ImGuiCond_FirstUseEver); + if (!Begin(name, &open, ImGuiWindowFlags_NoNav)) { + End(); + return open; } - SameLine(); + Text("%s", value.name.c_str()); SameLine(0.0f, 7.0f); - if (BeginCombo("Shader type", showing_isa ? "ISA" : "SPIRV", ImGuiComboFlags_WidthFitPreview)) { - if (Selectable("SPIRV")) { - showing_isa = false; + if (Checkbox("Enable patch", &value.is_patched)) { + if (value.is_patched) { + if (value.patch_source.empty()) { + value.patch_source = value.cache_spv_disasm; + } + isa_editor.SetText(value.cache_patch_disasm); + isa_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::SPIRV()); + glsl_editor.SetText(value.patch_source); + glsl_editor.SetReadOnly(false); + if (!value.patch_spv.empty()) { + ReloadShader(value); + } + } else { + isa_editor.SetText(value.cache_isa_disasm); + isa_editor.SetLanguageDefinition(TextEditor::LanguageDefinition()); + glsl_editor.SetText(value.cache_spv_disasm); + glsl_editor.SetReadOnly(true); + ReloadShader(value); } - if (Selectable("ISA")) { - showing_isa = true; - } - EndCombo(); } - if (showing_isa) { - isa_editor.Render("ISA", GetContentRegionAvail()); + if (value.is_patched) { + if (BeginCombo("Shader type", showing_bin ? "SPIRV" : "GLSL", + ImGuiComboFlags_WidthFitPreview)) { + if (Selectable("GLSL")) { + showing_bin = false; + } + if (Selectable("SPIRV")) { + showing_bin = true; + } + EndCombo(); + } } else { - spv_editor.Render("SPIRV", GetContentRegionAvail()); + if (BeginCombo("Shader type", showing_bin ? "ISA" : "GLSL", + ImGuiComboFlags_WidthFitPreview)) { + if (Selectable("GLSL")) { + showing_bin = false; + } + if (Selectable("ISA")) { + showing_bin = true; + } + EndCombo(); + } } -} -ShaderList::ShaderList() { - isa_editor.SetPalette(TextEditor::GetDarkPalette()); - isa_editor.SetReadOnly(true); - spv_editor.SetPalette(TextEditor::GetDarkPalette()); - spv_editor.SetReadOnly(true); - spv_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::GLSL()); + if (value.is_patched) { + bool save = false; + bool compile = false; + SameLine(0.0f, 3.0f); + if (Button("Save")) { + save = true; + } + SameLine(); + if (Button("Save & Compile")) { + save = true; + compile = true; + } + if (save) { + value.patch_source = glsl_editor.GetText(); + std::ofstream file{patch_path, std::ios::binary | std::ios::trunc}; + file << value.patch_source; + std::string msg = "Patch saved to "; + msg += Common::U8stringToString(patch_path.u8string()); + DebugState.ShowDebugMessage(msg); + } + if (compile) { + static std::map stage_arg = { + {"vs", "vert"}, + {"gs", "geom"}, + {"fs", "frag"}, + {"cs", "comp"}, + }; + auto stage = stage_arg.find(value.name.substr(0, 2)); + if (stage == stage_arg.end()) { + DebugState.ShowDebugMessage(std::string{"Invalid shader stage: "} + + value.name.substr(0, 2)); + } else { + std::string cmd = + fmt::format("glslc --target-env=vulkan1.3 --target-spv=spv1.6 " + "-fshader-stage={} {{src}} -o \"{}\"", + stage->second, Common::U8stringToString(patch_bin_path.u8string())); + bool success = false; + auto res = RunDisassembler(cmd, value.patch_source, &success); + if (!res.empty() || !success) { + DebugState.ShowDebugMessage("Compilation failed:\n" + res); + } else { + Common::FS::IOFile file{patch_bin_path, Common::FS::FileAccessMode::Read}; + value.patch_spv.resize(file.GetSize() / sizeof(u32)); + file.Read(value.patch_spv); + value.cache_patch_disasm = + RunDisassembler("spirv-dis {src}", value.patch_spv, &success); + if (!success) { + DebugState.ShowDebugMessage("Decompilation failed (Compile was ok):\n" + + res); + } else { + isa_editor.SetText(value.cache_patch_disasm); + ReloadShader(value); + } + } + } + } + } + + if (showing_bin) { + isa_editor.Render(value.is_patched ? "SPIRV" : "ISA", GetContentRegionAvail()); + } else { + glsl_editor.Render("GLSL", GetContentRegionAvail()); + } + + End(); + return open; } void ShaderList::Draw() { + for (auto it = open_shaders.begin(); it != open_shaders.end();) { + auto& selection = *it; + auto& shader = DebugState.shader_dump_list[selection.index]; + if (!selection.DrawShader(shader)) { + it = open_shaders.erase(it); + } else { + ++it; + } + } + SetNextWindowSize({500.0f, 600.0f}, ImGuiCond_FirstUseEver); if (!Begin("Shader list", &open)) { End(); @@ -73,18 +228,19 @@ void ShaderList::Draw() { return; } - if (selected_shader >= 0) { - DrawShader(DebugState.shader_dump_list[selected_shader]); - End(); - return; - } - auto width = GetContentRegionAvail().x; int i = 0; for (const auto& shader : DebugState.shader_dump_list) { - if (ButtonEx(shader.name.c_str(), {width, 20.0f}, ImGuiButtonFlags_NoHoveredOnFocus)) { - selected_shader = i; - loaded_data = false; + char name[128]; + if (shader.is_patched) { + snprintf(name, sizeof(name), "%s (PATCH ON)", shader.name.c_str()); + } else if (!shader.patch_spv.empty()) { + snprintf(name, sizeof(name), "%s (PATCH OFF)", shader.name.c_str()); + } else { + snprintf(name, sizeof(name), "%s", shader.name.c_str()); + } + if (ButtonEx(name, {width, 20.0f}, ImGuiButtonFlags_NoHoveredOnFocus)) { + open_shaders.emplace_back(i); } i++; } diff --git a/src/core/devtools/widget/shader_list.h b/src/core/devtools/widget/shader_list.h index 5a47f656d..2534ded35 100644 --- a/src/core/devtools/widget/shader_list.h +++ b/src/core/devtools/widget/shader_list.h @@ -6,20 +6,32 @@ #include "core/debug_state.h" #include "text_editor.h" +#include + namespace Core::Devtools::Widget { class ShaderList { - int selected_shader = -1; - TextEditor isa_editor{}; - TextEditor spv_editor{}; - bool loaded_data = false; - bool showing_isa = false; + struct Selection { + explicit Selection(int index); + ~Selection(); - void DrawShader(DebugStateType::ShaderDump& value); + void ReloadShader(DebugStateType::ShaderDump& value); + + bool DrawShader(DebugStateType::ShaderDump& value); + + int index; + TextEditor isa_editor{}; + TextEditor glsl_editor{}; + bool open = true; + bool showing_bin = false; + + std::filesystem::path patch_path; + std::filesystem::path patch_bin_path; + }; + + std::vector open_shaders{}; public: - ShaderList(); - bool open = false; void Draw(); diff --git a/src/core/devtools/widget/text_editor.cpp b/src/core/devtools/widget/text_editor.cpp index 07f2f658d..7171cac47 100644 --- a/src/core/devtools/widget/text_editor.cpp +++ b/src/core/devtools/widget/text_editor.cpp @@ -1059,7 +1059,8 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) { if (!mIgnoreImGuiChild) ImGui::BeginChild(aTitle, aSize, aBorder, ImGuiWindowFlags_HorizontalScrollbar | - ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NoMove); + ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_NoNav); if (mHandleKeyboardInputs) { HandleKeyboardInputs(); @@ -2331,4 +2332,50 @@ const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::GLSL() { return langDef; } +// Source: https://github.com/dfranx/ImGuiColorTextEdit/blob/master/TextEditor.cpp +const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::SPIRV() { + static bool inited = false; + static LanguageDefinition langDef; + if (!inited) { + /* + langDef.mTokenRegexStrings.push_back(std::make_pair("[ \\t]*#[ + \\t]*[a-zA-Z_]+", PaletteIndex::Preprocessor)); + langDef.mTokenRegexStrings.push_back(std::make_pair("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", + PaletteIndex::Punctuation)); + */ + + langDef.mTokenRegexStrings.push_back(std::make_pair( + "L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); + langDef.mTokenRegexStrings.push_back( + std::make_pair("[ =\\t]Op[a-zA-Z]*", PaletteIndex::Keyword)); + langDef.mTokenRegexStrings.push_back( + std::make_pair("%[_a-zA-Z0-9]*", PaletteIndex::Identifier)); + langDef.mTokenRegexStrings.push_back(std::make_pair( + "[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair( + "[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair( + "0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair( + "0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number)); + + langDef.mCommentStart = "/*"; + langDef.mCommentEnd = "*/"; + langDef.mSingleLineComment = ";"; + + langDef.mCaseSensitive = true; + langDef.mAutoIndentation = false; + + langDef.mName = "SPIR-V"; + + inited = true; + } + return langDef; +} + } // namespace Core::Devtools::Widget diff --git a/src/core/devtools/widget/text_editor.h b/src/core/devtools/widget/text_editor.h index 5c3f29f11..aa81d0d23 100644 --- a/src/core/devtools/widget/text_editor.h +++ b/src/core/devtools/widget/text_editor.h @@ -161,6 +161,7 @@ public: : mPreprocChar('#'), mAutoIndentation(true), mTokenize(nullptr), mCaseSensitive(true) {} static const LanguageDefinition& GLSL(); + static const LanguageDefinition& SPIRV(); }; TextEditor(); diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index d95e8d634..f6b57436f 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -168,6 +168,21 @@ void WindowSDL::InitTimers() { SDL_AddTimer(100, &PollController, controller); } +void WindowSDL::RequestKeyboard() { + if (keyboard_grab == 0) { + SDL_StartTextInput(window); + } + keyboard_grab++; +} + +void WindowSDL::ReleaseKeyboard() { + ASSERT(keyboard_grab > 0); + keyboard_grab--; + if (keyboard_grab == 0) { + SDL_StopTextInput(window); + } +} + void WindowSDL::OnResize() { SDL_GetWindowSizeInPixels(window, &width, &height); ImGui::Core::OnResize(); diff --git a/src/sdl_window.h b/src/sdl_window.h index 78d0e582f..78d4bbc39 100644 --- a/src/sdl_window.h +++ b/src/sdl_window.h @@ -41,6 +41,8 @@ struct WindowSystemInfo { }; class WindowSDL { + int keyboard_grab = 0; + public: explicit WindowSDL(s32 width, s32 height, Input::GameController* controller, std::string_view window_title); @@ -69,6 +71,9 @@ public: void WaitEvent(); void InitTimers(); + void RequestKeyboard(); + void ReleaseKeyboard(); + private: void OnResize(); void OnKeyPress(const SDL_Event* event); diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp index 09d4e4195..8d495ab06 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp @@ -13,7 +13,7 @@ namespace Vulkan { ComputePipeline::ComputePipeline(const Instance& instance_, Scheduler& scheduler_, DescriptorHeap& desc_heap_, vk::PipelineCache pipeline_cache, - u64 compute_key_, const Shader::Info& info_, + ComputePipelineKey compute_key_, const Shader::Info& info_, vk::ShaderModule module) : Pipeline{instance_, scheduler_, desc_heap_, pipeline_cache, true}, compute_key{compute_key_} { auto& info = stages[int(Shader::Stage::Compute)]; diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.h b/src/video_core/renderer_vulkan/vk_compute_pipeline.h index ca429b58d..1c28e461c 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.h @@ -17,15 +17,33 @@ class Instance; class Scheduler; class DescriptorHeap; +struct ComputePipelineKey { + size_t value; + + friend bool operator==(const ComputePipelineKey& lhs, const ComputePipelineKey& rhs) { + return lhs.value == rhs.value; + } + friend bool operator!=(const ComputePipelineKey& lhs, const ComputePipelineKey& rhs) { + return !(lhs == rhs); + } +}; + class ComputePipeline : public Pipeline { public: ComputePipeline(const Instance& instance, Scheduler& scheduler, DescriptorHeap& desc_heap, - vk::PipelineCache pipeline_cache, u64 compute_key, const Shader::Info& info, - vk::ShaderModule module); + vk::PipelineCache pipeline_cache, ComputePipelineKey compute_key, + const Shader::Info& info, vk::ShaderModule module); ~ComputePipeline(); private: - u64 compute_key; + ComputePipelineKey compute_key; }; } // namespace Vulkan + +template <> +struct std::hash { + std::size_t operator()(const Vulkan::ComputePipelineKey& key) const noexcept { + return std::hash{}(key.value); + } +}; diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index 91ffe4ea4..2834fceb7 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + #include #include "common/types.h" diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 53bdc79a6..276e4ef29 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -189,10 +189,19 @@ const GraphicsPipeline* PipelineCache::GetGraphicsPipeline() { } const auto [it, is_new] = graphics_pipelines.try_emplace(graphics_key); if (is_new) { - it.value() = graphics_pipeline_pool.Create(instance, scheduler, desc_heap, graphics_key, - *pipeline_cache, infos, fetch_shader, modules); + it.value() = + std::make_unique(instance, scheduler, desc_heap, graphics_key, + *pipeline_cache, infos, fetch_shader, modules); + if (Config::collectShadersForDebug()) { + for (auto stage = 0; stage < MaxShaderStages; ++stage) { + if (infos[stage]) { + auto& m = modules[stage]; + module_related_pipelines[m].emplace_back(graphics_key); + } + } + } } - return it->second; + return it->second.get(); } const ComputePipeline* PipelineCache::GetComputePipeline() { @@ -201,10 +210,14 @@ const ComputePipeline* PipelineCache::GetComputePipeline() { } const auto [it, is_new] = compute_pipelines.try_emplace(compute_key); if (is_new) { - it.value() = compute_pipeline_pool.Create(instance, scheduler, desc_heap, *pipeline_cache, - compute_key, *infos[0], modules[0]); + it.value() = std::make_unique( + instance, scheduler, desc_heap, *pipeline_cache, compute_key, *infos[0], modules[0]); + if (Config::collectShadersForDebug()) { + auto& m = modules[0]; + module_related_pipelines[m].emplace_back(compute_key); + } } - return it->second; + return it->second.get(); } bool PipelineCache::RefreshGraphicsKey() { @@ -401,7 +414,7 @@ bool PipelineCache::RefreshComputeKey() { Shader::Backend::Bindings binding{}; const auto* cs_pgm = &liverpool->regs.cs_program; const auto cs_params = Liverpool::GetParams(*cs_pgm); - std::tie(infos[0], modules[0], fetch_shader, compute_key) = + std::tie(infos[0], modules[0], fetch_shader, compute_key.value) = GetProgram(Shader::Stage::Compute, cs_params, binding); return true; } @@ -417,17 +430,23 @@ vk::ShaderModule PipelineCache::CompileModule(Shader::Info& info, const auto ir_program = Shader::TranslateProgram(code, pools, info, runtime_info, profile); auto spv = Shader::Backend::SPIRV::EmitSPIRV(profile, runtime_info, ir_program, binding); DumpShader(spv, info.pgm_hash, info.stage, perm_idx, "spv"); + + vk::ShaderModule module; + auto patch = GetShaderPatch(info.pgm_hash, info.stage, perm_idx, "spv"); - if (patch) { - spv = *patch; + const bool is_patched = patch && Config::patchShaders(); + if (is_patched) { LOG_INFO(Loader, "Loaded patch for {} shader {:#x}", info.stage, info.pgm_hash); + module = CompileSPV(*patch, instance.GetDevice()); + } else { + module = CompileSPV(spv, instance.GetDevice()); } - const auto module = CompileSPV(spv, instance.GetDevice()); - const auto name = fmt::format("{}_{:#x}_{}", info.stage, info.pgm_hash, perm_idx); + const auto name = fmt::format("{}_{:#018x}_{}", info.stage, info.pgm_hash, perm_idx); Vulkan::SetObjectName(instance.GetDevice(), module, name); if (Config::collectShadersForDebug()) { - DebugState.CollectShader(name, spv, code); + DebugState.CollectShader(name, module, spv, code, patch ? *patch : std::span{}, + is_patched); } return module; } @@ -438,17 +457,17 @@ PipelineCache::GetProgram(Shader::Stage stage, Shader::ShaderParams params, const auto runtime_info = BuildRuntimeInfo(stage); auto [it_pgm, new_program] = program_cache.try_emplace(params.hash); if (new_program) { - Program* program = program_pool.Create(stage, params); + it_pgm.value() = std::make_unique(stage, params); + auto& program = it_pgm.value(); auto start = binding; const auto module = CompileModule(program->info, runtime_info, params.code, 0, binding); const auto spec = Shader::StageSpecialization(program->info, runtime_info, profile, start); program->AddPermut(module, std::move(spec)); - it_pgm.value() = program; return std::make_tuple(&program->info, module, spec.fetch_shader_data, HashCombine(params.hash, 0)); } - Program* program = it_pgm->second; + auto& program = it_pgm.value(); auto& info = program->info; info.RefreshFlatBuf(); const auto spec = Shader::StageSpecialization(info, runtime_info, profile, binding); @@ -469,6 +488,34 @@ PipelineCache::GetProgram(Shader::Stage stage, Shader::ShaderParams params, HashCombine(params.hash, perm_idx)); } +std::optional PipelineCache::ReplaceShader(vk::ShaderModule module, + std::span spv_code) { + std::optional new_module{}; + for (const auto& [_, program] : program_cache) { + for (auto& m : program->modules) { + if (m.module == module) { + const auto& d = instance.GetDevice(); + d.destroyShaderModule(m.module); + m.module = CompileSPV(spv_code, d); + new_module = m.module; + } + } + } + if (module_related_pipelines.contains(module)) { + auto& pipeline_keys = module_related_pipelines[module]; + for (auto& key : pipeline_keys) { + if (std::holds_alternative(key)) { + auto& graphics_key = std::get(key); + graphics_pipelines.erase(graphics_key); + } else if (std::holds_alternative(key)) { + auto& compute_key = std::get(key); + compute_pipelines.erase(compute_key); + } + } + } + return new_module; +} + void PipelineCache::DumpShader(std::span code, u64 hash, Shader::Stage stage, size_t perm_idx, std::string_view ext) { if (!Config::dumpShaders()) { @@ -488,9 +535,6 @@ void PipelineCache::DumpShader(std::span code, u64 hash, Shader::Stag std::optional> PipelineCache::GetShaderPatch(u64 hash, Shader::Stage stage, size_t perm_idx, std::string_view ext) { - if (!Config::patchShaders()) { - return {}; - } using namespace Common::FS; const auto patch_dir = GetUserPath(PathType::ShaderDir) / "patch"; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index e4a8abd4f..c5c2fc98e 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include "shader_recompiler/profile.h" #include "shader_recompiler/recompiler.h" @@ -11,6 +12,13 @@ #include "video_core/renderer_vulkan/vk_graphics_pipeline.h" #include "video_core/renderer_vulkan/vk_resource_pool.h" +template <> +struct std::hash { + std::size_t operator()(const vk::ShaderModule& module) const noexcept { + return std::hash{}(reinterpret_cast((VkShaderModule)module)); + } +}; + namespace Shader { struct Info; } @@ -52,6 +60,9 @@ public: GetProgram(Shader::Stage stage, Shader::ShaderParams params, Shader::Backend::Bindings& binding); + std::optional ReplaceShader(vk::ShaderModule module, + std::span spv_code); + private: bool RefreshGraphicsKey(); bool RefreshComputeKey(); @@ -74,17 +85,19 @@ private: vk::UniquePipelineLayout pipeline_layout; Shader::Profile profile{}; Shader::Pools pools; - tsl::robin_map program_cache; - Common::ObjectPool program_pool; - Common::ObjectPool graphics_pipeline_pool; - Common::ObjectPool compute_pipeline_pool; - tsl::robin_map compute_pipelines; - tsl::robin_map graphics_pipelines; + tsl::robin_map> program_cache; + tsl::robin_map> compute_pipelines; + tsl::robin_map> graphics_pipelines; std::array infos{}; std::array modules{}; std::optional fetch_shader{}; GraphicsPipelineKey graphics_key{}; - u64 compute_key{}; + ComputePipelineKey compute_key{}; + + // Only if Config::collectShadersForDebug() + tsl::robin_map>> + module_related_pipelines; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_presenter.h b/src/video_core/renderer_vulkan/vk_presenter.h index 4d9226dec..4c29af0f0 100644 --- a/src/video_core/renderer_vulkan/vk_presenter.h +++ b/src/video_core/renderer_vulkan/vk_presenter.h @@ -53,6 +53,10 @@ public: return pp_settings.gamma; } + Frontend::WindowSDL& GetWindow() const { + return window; + } + Frame* PrepareFrame(const Libraries::VideoOut::BufferAttributeGroup& attribute, VAddr cpu_address, bool is_eop) { auto desc = VideoCore::TextureCache::VideoOutDesc{attribute, cpu_address}; @@ -90,6 +94,10 @@ public: draw_scheduler.Flush(info); } + Rasterizer& GetRasterizer() const { + return *rasterizer.get(); + } + private: void CreatePostProcessPipeline(); Frame* PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop = true); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index fe8aceba7..1936276a2 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -54,6 +54,10 @@ public: u64 Flush(); void Finish(); + PipelineCache& GetPipelineCache() { + return pipeline_cache; + } + private: RenderState PrepareRenderState(u32 mrt_mask); void BeginRendering(const GraphicsPipeline& pipeline, RenderState& state); From cd9fc5d0e935ac12f469d609b1a643964fb33129 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Tue, 10 Dec 2024 03:08:53 -0800 Subject: [PATCH 141/549] thread: Apply alternate signal stack to created threads. (#1724) --- src/core/thread.cpp | 30 +++++++++++++++++++++++++++--- src/core/thread.h | 1 + 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/core/thread.cpp b/src/core/thread.cpp index f87e3c8dc..0d0804cea 100644 --- a/src/core/thread.cpp +++ b/src/core/thread.cpp @@ -1,15 +1,15 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "libraries/kernel/threads/pthread.h" -#include "thread.h" - +#include "common/alignment.h" #include "core/libraries/kernel/threads/pthread.h" +#include "thread.h" #ifdef _WIN64 #include #include "common/ntapi.h" #else +#include #include #endif @@ -113,6 +113,17 @@ void NativeThread::Exit() { NtTerminateThread(nullptr, 0); #else + // Disable and free the signal stack. + constexpr stack_t sig_stack = { + .ss_flags = SS_DISABLE, + }; + sigaltstack(&sig_stack, nullptr); + + if (sig_stack_ptr) { + free(sig_stack_ptr); + sig_stack_ptr = nullptr; + } + pthread_exit(nullptr); #endif } @@ -122,6 +133,19 @@ void NativeThread::Initialize() { tid = GetCurrentThreadId(); #else tid = (u64)pthread_self(); + + // Set up an alternate signal handler stack to avoid overflowing small thread stacks. + const size_t page_size = getpagesize(); + const size_t sig_stack_size = Common::AlignUp(std::max(64_KB, MINSIGSTKSZ), page_size); + ASSERT_MSG(posix_memalign(&sig_stack_ptr, page_size, sig_stack_size) == 0, + "Failed to allocate signal stack: {}", errno); + + const stack_t sig_stack = { + .ss_sp = sig_stack_ptr, + .ss_size = sig_stack_size, + .ss_flags = 0, + }; + ASSERT_MSG(sigaltstack(&sig_stack, nullptr) == 0, "Failed to set signal stack: {}", errno); #endif } diff --git a/src/core/thread.h b/src/core/thread.h index 3bac0e699..bd777a2e6 100644 --- a/src/core/thread.h +++ b/src/core/thread.h @@ -37,6 +37,7 @@ private: void* native_handle; #else uintptr_t native_handle; + void* sig_stack_ptr; #endif u64 tid; }; From aa5293e3ad6d4fd6b83da219c6ee5d11d24fe5eb Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Tue, 10 Dec 2024 08:12:58 -0300 Subject: [PATCH 142/549] Delete Patches Button (#1722) --- src/qt_gui/cheats_patches.cpp | 39 ++++++++++++++++++++++++++++++++ src/qt_gui/translations/pt_BR.ts | 2 +- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/qt_gui/cheats_patches.cpp b/src/qt_gui/cheats_patches.cpp index 3e7c22451..446b5a0ea 100644 --- a/src/qt_gui/cheats_patches.cpp +++ b/src/qt_gui/cheats_patches.cpp @@ -51,6 +51,9 @@ void CheatsPatches::setupUI() { QString CHEATS_DIR_QString; Common::FS::PathToQString(CHEATS_DIR_QString, Common::FS::GetUserPath(Common::FS::PathType::CheatsDir)); + QString PATCHS_DIR_QString; + Common::FS::PathToQString(PATCHS_DIR_QString, + Common::FS::GetUserPath(Common::FS::PathType::PatchesDir)); QString NameCheatJson = m_gameSerial + "_" + m_gameVersion + ".json"; m_cheatFilePath = CHEATS_DIR_QString + "/" + NameCheatJson; @@ -237,9 +240,45 @@ void CheatsPatches::setupUI() { }); patchesControlLayout->addWidget(patchesButton); + QPushButton* deletePatchButton = new QPushButton(tr("Delete File")); + connect(deletePatchButton, &QPushButton::clicked, [this, PATCHS_DIR_QString]() { + QStringListModel* model = qobject_cast(patchesListView->model()); + if (!model) { + return; + } + QItemSelectionModel* selectionModel = patchesListView->selectionModel(); + if (!selectionModel) { + return; + } + QModelIndexList selectedIndexes = selectionModel->selectedIndexes(); + if (selectedIndexes.isEmpty()) { + QMessageBox::warning(this, tr("Delete File"), tr("No files selected.")); + return; + } + QModelIndex selectedIndex = selectedIndexes.first(); + QString selectedFileName = model->data(selectedIndex).toString(); + + int ret = QMessageBox::warning( + this, tr("Delete File"), + QString(tr("Do you want to delete the selected file?\\n%1").replace("\\n", "\n")) + .arg(selectedFileName), + QMessageBox::Yes | QMessageBox::No); + + if (ret == QMessageBox::Yes) { + QString fileName = selectedFileName.split('|').first().trimmed(); + QString directoryName = selectedFileName.split('|').last().trimmed(); + QString filePath = PATCHS_DIR_QString + "/" + directoryName + "/" + fileName; + + QFile::remove(filePath); + createFilesJson(directoryName); + populateFileListPatches(); + } + }); + QPushButton* saveButton = new QPushButton(tr("Save")); connect(saveButton, &QPushButton::clicked, this, &CheatsPatches::onSaveButtonClicked); + patchesControlLayout->addWidget(deletePatchButton); patchesControlLayout->addWidget(saveButton); patchesLayout->addLayout(patchesControlLayout); diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index 7ea63b9fb..dfe785673 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -841,7 +841,7 @@ Cheats / Patches for - Cheats / Patches for + Cheats / Patches para From bf41ab6c40a1236550e26c64ae897d0d74a33968 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Tue, 10 Dec 2024 03:13:34 -0800 Subject: [PATCH 143/549] memory: Handle 0 alignment in MemoryManager::Allocate (#1692) --- src/core/memory.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 82e4b7ad3..980beee79 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -96,12 +96,12 @@ PAddr MemoryManager::PoolExpand(PAddr search_start, PAddr search_end, size_t siz PAddr MemoryManager::Allocate(PAddr search_start, PAddr search_end, size_t size, u64 alignment, int memory_type) { std::scoped_lock lk{mutex}; + alignment = alignment > 0 ? alignment : 16_KB; auto dmem_area = FindDmemArea(search_start); const auto is_suitable = [&] { - const auto aligned_base = alignment > 0 ? Common::AlignUp(dmem_area->second.base, alignment) - : dmem_area->second.base; + const auto aligned_base = Common::AlignUp(dmem_area->second.base, alignment); const auto alignment_size = aligned_base - dmem_area->second.base; const auto remaining_size = dmem_area->second.size >= alignment_size ? dmem_area->second.size - alignment_size : 0; @@ -114,7 +114,7 @@ PAddr MemoryManager::Allocate(PAddr search_start, PAddr search_end, size_t size, // Align free position PAddr free_addr = dmem_area->second.base; - free_addr = alignment > 0 ? Common::AlignUp(free_addr, alignment) : free_addr; + free_addr = Common::AlignUp(free_addr, alignment); // Add the allocated region to the list and commit its pages. auto& area = CarveDmemArea(free_addr, size)->second; From 41fd1c84cf51fbb8b3784e038c72d4bb37b8f406 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Tue, 10 Dec 2024 04:43:32 -0800 Subject: [PATCH 144/549] semaphore: Use handles to properly handle semaphore double-delete. (#1728) --- .../libraries/kernel/threads/semaphore.cpp | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/core/libraries/kernel/threads/semaphore.cpp b/src/core/libraries/kernel/threads/semaphore.cpp index 39c1a0233..f25a76c2b 100644 --- a/src/core/libraries/kernel/threads/semaphore.cpp +++ b/src/core/libraries/kernel/threads/semaphore.cpp @@ -9,6 +9,7 @@ #include "core/libraries/kernel/sync/semaphore.h" #include "common/logging/log.h" +#include "common/slot_vector.h" #include "core/libraries/kernel/kernel.h" #include "core/libraries/kernel/orbis_error.h" #include "core/libraries/kernel/posix_error.h" @@ -188,7 +189,9 @@ public: bool is_fifo; }; -using OrbisKernelSema = OrbisSem*; +using OrbisKernelSema = Common::SlotId; + +static Common::SlotVector> orbis_sems; s32 PS4_SYSV_ABI sceKernelCreateSema(OrbisKernelSema* sem, const char* pName, u32 attr, s32 initCount, s32 maxCount, const void* pOptParam) { @@ -196,47 +199,48 @@ s32 PS4_SYSV_ABI sceKernelCreateSema(OrbisKernelSema* sem, const char* pName, u3 LOG_ERROR(Lib_Kernel, "Semaphore creation parameters are invalid!"); return ORBIS_KERNEL_ERROR_EINVAL; } - *sem = new OrbisSem(initCount, maxCount, pName, attr == 1); + *sem = orbis_sems.insert( + std::move(std::make_unique(initCount, maxCount, pName, attr == 1))); return ORBIS_OK; } s32 PS4_SYSV_ABI sceKernelWaitSema(OrbisKernelSema sem, s32 needCount, u32* pTimeout) { - if (!sem) { + if (!orbis_sems.is_allocated(sem)) { return ORBIS_KERNEL_ERROR_ESRCH; } - return sem->Wait(true, needCount, pTimeout); + return orbis_sems[sem]->Wait(true, needCount, pTimeout); } s32 PS4_SYSV_ABI sceKernelSignalSema(OrbisKernelSema sem, s32 signalCount) { - if (!sem) { + if (!orbis_sems.is_allocated(sem)) { return ORBIS_KERNEL_ERROR_ESRCH; } - if (!sem->Signal(signalCount)) { + if (!orbis_sems[sem]->Signal(signalCount)) { return ORBIS_KERNEL_ERROR_EINVAL; } return ORBIS_OK; } s32 PS4_SYSV_ABI sceKernelPollSema(OrbisKernelSema sem, s32 needCount) { - if (!sem) { + if (!orbis_sems.is_allocated(sem)) { return ORBIS_KERNEL_ERROR_ESRCH; } - return sem->Wait(false, needCount, nullptr); + return orbis_sems[sem]->Wait(false, needCount, nullptr); } int PS4_SYSV_ABI sceKernelCancelSema(OrbisKernelSema sem, s32 setCount, s32* pNumWaitThreads) { - if (!sem) { + if (!orbis_sems.is_allocated(sem)) { return ORBIS_KERNEL_ERROR_ESRCH; } - return sem->Cancel(setCount, pNumWaitThreads); + return orbis_sems[sem]->Cancel(setCount, pNumWaitThreads); } int PS4_SYSV_ABI sceKernelDeleteSema(OrbisKernelSema sem) { - if (!sem) { + if (!orbis_sems.is_allocated(sem)) { return ORBIS_KERNEL_ERROR_ESRCH; } - sem->Delete(); - delete sem; + orbis_sems[sem]->Delete(); + orbis_sems.erase(sem); return ORBIS_OK; } From e5e1aba24172b4cf41e1c4ea56adf0b0984b815b Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Tue, 10 Dec 2024 04:44:08 -0800 Subject: [PATCH 145/549] renderer_vulkan: Introduce shader HLE system with copy shader implementation. (#1683) * renderer_vulkan: Introduce shader HLE system with copy shader implementation. Co-authored-by: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> * buffer_cache: Handle obtaining buffer views partially within buffers. * vk_shader_hle: Make more efficient --------- Co-authored-by: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> --- CMakeLists.txt | 2 + src/video_core/buffer_cache/buffer_cache.cpp | 10 +- src/video_core/buffer_cache/buffer_cache.h | 3 +- .../renderer_vulkan/vk_rasterizer.cpp | 6 + .../renderer_vulkan/vk_rasterizer.h | 8 + src/video_core/renderer_vulkan/vk_scheduler.h | 4 + .../renderer_vulkan/vk_shader_hle.cpp | 139 ++++++++++++++++++ .../renderer_vulkan/vk_shader_hle.h | 20 +++ .../texture_cache/texture_cache.cpp | 8 +- 9 files changed, 195 insertions(+), 5 deletions(-) create mode 100644 src/video_core/renderer_vulkan/vk_shader_hle.cpp create mode 100644 src/video_core/renderer_vulkan/vk_shader_hle.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 7de79a43d..b057f55d6 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -738,6 +738,8 @@ set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp src/video_core/renderer_vulkan/vk_resource_pool.h src/video_core/renderer_vulkan/vk_scheduler.cpp src/video_core/renderer_vulkan/vk_scheduler.h + src/video_core/renderer_vulkan/vk_shader_hle.cpp + src/video_core/renderer_vulkan/vk_shader_hle.h src/video_core/renderer_vulkan/vk_shader_util.cpp src/video_core/renderer_vulkan/vk_shader_util.h src/video_core/renderer_vulkan/vk_swapchain.cpp diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index 1abdb230b..e9fc06493 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -360,7 +360,8 @@ std::pair BufferCache::ObtainBuffer(VAddr device_addr, u32 size, b return {&buffer, buffer.Offset(device_addr)}; } -std::pair BufferCache::ObtainViewBuffer(VAddr gpu_addr, u32 size) { +std::pair BufferCache::ObtainViewBuffer(VAddr gpu_addr, u32 size, bool prefer_gpu) { + // Check if any buffer contains the full requested range. const u64 page = gpu_addr >> CACHING_PAGEBITS; const BufferId buffer_id = page_table[page]; if (buffer_id) { @@ -370,6 +371,13 @@ std::pair BufferCache::ObtainViewBuffer(VAddr gpu_addr, u32 size) return {&buffer, buffer.Offset(gpu_addr)}; } } + // If no buffer contains the full requested range but some buffer within was GPU-modified, + // fall back to ObtainBuffer to create a full buffer and avoid losing GPU modifications. + // This is only done if the request prefers to use GPU memory, otherwise we can skip it. + if (prefer_gpu && memory_tracker.IsRegionGpuModified(gpu_addr, size)) { + return ObtainBuffer(gpu_addr, size, false, false); + } + // In all other cases, just do a CPU copy to the staging buffer. const u32 offset = staging_buffer.Copy(gpu_addr, size, 16); return {&staging_buffer, offset}; } diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 3dab95db7..e62913413 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -96,7 +96,8 @@ public: BufferId buffer_id = {}); /// Attempts to obtain a buffer without modifying the cache contents. - [[nodiscard]] std::pair ObtainViewBuffer(VAddr gpu_addr, u32 size); + [[nodiscard]] std::pair ObtainViewBuffer(VAddr gpu_addr, u32 size, + bool prefer_gpu); /// Return true when a region is registered on the cache [[nodiscard]] bool IsRegionRegistered(VAddr addr, size_t size); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 0471fdb0a..33358b850 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -8,6 +8,7 @@ #include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_rasterizer.h" #include "video_core/renderer_vulkan/vk_scheduler.h" +#include "video_core/renderer_vulkan/vk_shader_hle.h" #include "video_core/texture_cache/image_view.h" #include "video_core/texture_cache/texture_cache.h" #include "vk_rasterizer.h" @@ -318,6 +319,11 @@ void Rasterizer::DispatchDirect() { return; } + const auto& cs = pipeline->GetStage(Shader::Stage::Compute); + if (ExecuteShaderHLE(cs, liverpool->regs, *this)) { + return; + } + if (!BindResources(pipeline)) { return; } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 1936276a2..9214372ee 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -28,6 +28,14 @@ public: AmdGpu::Liverpool* liverpool); ~Rasterizer(); + [[nodiscard]] Scheduler& GetScheduler() noexcept { + return scheduler; + } + + [[nodiscard]] VideoCore::BufferCache& GetBufferCache() noexcept { + return buffer_cache; + } + [[nodiscard]] VideoCore::TextureCache& GetTextureCache() noexcept { return texture_cache; } diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index 1140bfbc2..45a9228c9 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -10,6 +10,10 @@ #include "video_core/renderer_vulkan/vk_master_semaphore.h" #include "video_core/renderer_vulkan/vk_resource_pool.h" +namespace tracy { +class VkCtxScope; +} + namespace Vulkan { class Instance; diff --git a/src/video_core/renderer_vulkan/vk_shader_hle.cpp b/src/video_core/renderer_vulkan/vk_shader_hle.cpp new file mode 100644 index 000000000..df9d40f07 --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_shader_hle.cpp @@ -0,0 +1,139 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "shader_recompiler/info.h" +#include "video_core/renderer_vulkan/vk_scheduler.h" +#include "video_core/renderer_vulkan/vk_shader_hle.h" + +#include "vk_rasterizer.h" + +namespace Vulkan { + +static constexpr u64 COPY_SHADER_HASH = 0xfefebf9f; + +bool ExecuteCopyShaderHLE(const Shader::Info& info, const AmdGpu::Liverpool::Regs& regs, + Rasterizer& rasterizer) { + auto& scheduler = rasterizer.GetScheduler(); + auto& buffer_cache = rasterizer.GetBufferCache(); + + // Copy shader defines three formatted buffers as inputs: control, source, and destination. + const auto ctl_buf_sharp = info.texture_buffers[0].GetSharp(info); + const auto src_buf_sharp = info.texture_buffers[1].GetSharp(info); + const auto dst_buf_sharp = info.texture_buffers[2].GetSharp(info); + const auto buf_stride = src_buf_sharp.GetStride(); + ASSERT(buf_stride == dst_buf_sharp.GetStride()); + + struct CopyShaderControl { + u32 dst_idx; + u32 src_idx; + u32 end; + }; + static_assert(sizeof(CopyShaderControl) == 12); + ASSERT(ctl_buf_sharp.GetStride() == sizeof(CopyShaderControl)); + const auto ctl_buf = reinterpret_cast(ctl_buf_sharp.base_address); + + static std::vector copies; + copies.clear(); + copies.reserve(regs.cs_program.dim_x); + + for (u32 i = 0; i < regs.cs_program.dim_x; i++) { + const auto& [dst_idx, src_idx, end] = ctl_buf[i]; + const u32 local_dst_offset = dst_idx * buf_stride; + const u32 local_src_offset = src_idx * buf_stride; + const u32 local_size = (end + 1) * buf_stride; + copies.emplace_back(local_src_offset, local_dst_offset, local_size); + } + + scheduler.EndRendering(); + + static constexpr vk::MemoryBarrier READ_BARRIER{ + .srcAccessMask = vk::AccessFlagBits::eMemoryWrite, + .dstAccessMask = vk::AccessFlagBits::eTransferRead | vk::AccessFlagBits::eTransferWrite, + }; + static constexpr vk::MemoryBarrier WRITE_BARRIER{ + .srcAccessMask = vk::AccessFlagBits::eTransferWrite, + .dstAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite, + }; + scheduler.CommandBuffer().pipelineBarrier( + vk::PipelineStageFlagBits::eAllCommands, vk::PipelineStageFlagBits::eTransfer, + vk::DependencyFlagBits::eByRegion, READ_BARRIER, {}, {}); + + static constexpr vk::DeviceSize MaxDistanceForMerge = 64_MB; + u32 batch_start = 0; + u32 batch_end = 1; + + while (batch_end < copies.size()) { + // Place first copy into the current batch + const auto& copy = copies[batch_start]; + auto src_offset_min = copy.srcOffset; + auto src_offset_max = copy.srcOffset + copy.size; + auto dst_offset_min = copy.dstOffset; + auto dst_offset_max = copy.dstOffset + copy.size; + + for (int i = batch_start + 1; i < copies.size(); i++) { + // Compute new src and dst bounds if we were to batch this copy + const auto [src_offset, dst_offset, size] = copies[i]; + auto new_src_offset_min = std::min(src_offset_min, src_offset); + auto new_src_offset_max = std::max(src_offset_max, src_offset + size); + if (new_src_offset_max - new_src_offset_min > MaxDistanceForMerge) { + continue; + } + + auto new_dst_offset_min = std::min(dst_offset_min, dst_offset); + auto new_dst_offset_max = std::max(dst_offset_max, dst_offset + size); + if (new_dst_offset_max - new_dst_offset_min > MaxDistanceForMerge) { + continue; + } + + // We can batch this copy + src_offset_min = new_src_offset_min; + src_offset_max = new_src_offset_max; + dst_offset_min = new_dst_offset_min; + dst_offset_max = new_dst_offset_max; + if (i != batch_end) { + std::swap(copies[i], copies[batch_end]); + } + ++batch_end; + } + + // Obtain buffers for the total source and destination ranges. + const auto [src_buf, src_buf_offset] = + buffer_cache.ObtainBuffer(src_buf_sharp.base_address + src_offset_min, + src_offset_max - src_offset_min, false, false); + const auto [dst_buf, dst_buf_offset] = + buffer_cache.ObtainBuffer(dst_buf_sharp.base_address + dst_offset_min, + dst_offset_max - dst_offset_min, true, false); + + // Apply found buffer base. + const auto vk_copies = std::span{copies}.subspan(batch_start, batch_end - batch_start); + for (auto& copy : vk_copies) { + copy.srcOffset = copy.srcOffset - src_offset_min + src_buf_offset; + copy.dstOffset = copy.dstOffset - dst_offset_min + dst_buf_offset; + } + + // Execute buffer copies. + LOG_TRACE(Render_Vulkan, "HLE buffer copy: src_size = {}, dst_size = {}", + src_offset_max - src_offset_min, dst_offset_max - dst_offset_min); + scheduler.CommandBuffer().copyBuffer(src_buf->Handle(), dst_buf->Handle(), vk_copies); + batch_start = batch_end; + ++batch_end; + } + + scheduler.CommandBuffer().pipelineBarrier( + vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eAllCommands, + vk::DependencyFlagBits::eByRegion, WRITE_BARRIER, {}, {}); + + return true; +} + +bool ExecuteShaderHLE(const Shader::Info& info, const AmdGpu::Liverpool::Regs& regs, + Rasterizer& rasterizer) { + switch (info.pgm_hash) { + case COPY_SHADER_HASH: + return ExecuteCopyShaderHLE(info, regs, rasterizer); + default: + return false; + } +} + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_shader_hle.h b/src/video_core/renderer_vulkan/vk_shader_hle.h new file mode 100644 index 000000000..fda9b1735 --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_shader_hle.h @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "video_core/amdgpu/liverpool.h" + +namespace Shader { +struct Info; +} + +namespace Vulkan { + +class Rasterizer; + +/// Attempts to execute a shader using HLE if possible. +bool ExecuteShaderHLE(const Shader::Info& info, const AmdGpu::Liverpool::Regs& regs, + Rasterizer& rasterizer); + +} // namespace Vulkan diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index 1670648b3..0e5bbc1f3 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -466,6 +466,9 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule const auto& num_mips = image.info.resources.levels; ASSERT(num_mips == image.info.mips_layout.size()); + const bool is_gpu_modified = True(image.flags & ImageFlagBits::GpuModified); + const bool is_gpu_dirty = True(image.flags & ImageFlagBits::GpuDirty); + boost::container::small_vector image_copy{}; for (u32 m = 0; m < num_mips; m++) { const u32 width = std::max(image.info.size.width >> m, 1u); @@ -475,8 +478,6 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule const auto& mip = image.info.mips_layout[m]; // Protect GPU modified resources from accidental CPU reuploads. - const bool is_gpu_modified = True(image.flags & ImageFlagBits::GpuModified); - const bool is_gpu_dirty = True(image.flags & ImageFlagBits::GpuDirty); if (is_gpu_modified && !is_gpu_dirty) { const u8* addr = std::bit_cast(image.info.guest_address); const u64 hash = XXH3_64bits(addr + mip.offset, mip.size); @@ -515,7 +516,8 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule const VAddr image_addr = image.info.guest_address; const size_t image_size = image.info.guest_size_bytes; - const auto [vk_buffer, buf_offset] = buffer_cache.ObtainViewBuffer(image_addr, image_size); + const auto [vk_buffer, buf_offset] = + buffer_cache.ObtainViewBuffer(image_addr, image_size, is_gpu_dirty); // The obtained buffer may be written by a shader so we need to emit a barrier to prevent RAW // hazard if (auto barrier = vk_buffer->GetBarrier(vk::AccessFlagBits2::eTransferRead, From 00543fe640b211c73ca1d3b02cc5e84d735f3ace Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Tue, 10 Dec 2024 10:40:54 -0300 Subject: [PATCH 146/549] Remove game from list after deletion (#1730) --- src/qt_gui/gui_context_menus.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h index 7da7341da..6eef1230c 100644 --- a/src/qt_gui/gui_context_menus.h +++ b/src/qt_gui/gui_context_menus.h @@ -360,6 +360,7 @@ public: QMessageBox::Yes | QMessageBox::No); if (reply == QMessageBox::Yes) { dir.removeRecursively(); + widget->removeRow(itemID); } } } From ea8ad359477e73e14c1f4d89c2e96d0064b28512 Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Tue, 10 Dec 2024 13:57:30 -0300 Subject: [PATCH 147/549] Fix delete cheats button (#1731) --- src/qt_gui/cheats_patches.cpp | 26 +++++++++++++++++++++++--- src/qt_gui/cheats_patches.h | 1 + 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/qt_gui/cheats_patches.cpp b/src/qt_gui/cheats_patches.cpp index 446b5a0ea..2fea0b6ea 100644 --- a/src/qt_gui/cheats_patches.cpp +++ b/src/qt_gui/cheats_patches.cpp @@ -955,15 +955,33 @@ void CheatsPatches::createFilesJson(const QString& repository) { jsonFile.close(); } -void CheatsPatches::addCheatsToLayout(const QJsonArray& modsArray, const QJsonArray& creditsArray) { +void CheatsPatches::clearListCheats() { QLayoutItem* item; while ((item = rightLayout->takeAt(0)) != nullptr) { - delete item->widget(); - delete item; + QWidget* widget = item->widget(); + if (widget) { + delete widget; + } else { + QLayout* layout = item->layout(); + if (layout) { + QLayoutItem* innerItem; + while ((innerItem = layout->takeAt(0)) != nullptr) { + QWidget* innerWidget = innerItem->widget(); + if (innerWidget) { + delete innerWidget; + } + delete innerItem; + } + delete layout; + } + } } m_cheats.clear(); m_cheatCheckBoxes.clear(); +} +void CheatsPatches::addCheatsToLayout(const QJsonArray& modsArray, const QJsonArray& creditsArray) { + clearListCheats(); int maxWidthButton = 0; for (const QJsonValue& modValue : modsArray) { @@ -1056,6 +1074,8 @@ void CheatsPatches::addCheatsToLayout(const QJsonArray& modsArray, const QJsonAr } void CheatsPatches::populateFileListCheats() { + clearListCheats(); + QString cheatsDir; Common::FS::PathToQString(cheatsDir, Common::FS::GetUserPath(Common::FS::PathType::CheatsDir)); diff --git a/src/qt_gui/cheats_patches.h b/src/qt_gui/cheats_patches.h index b07e828c2..4217436f6 100644 --- a/src/qt_gui/cheats_patches.h +++ b/src/qt_gui/cheats_patches.h @@ -36,6 +36,7 @@ public: const QString& m_gameVersion, bool showMessageBox); void downloadPatches(const QString repository, const bool showMessageBox); void createFilesJson(const QString& repository); + void clearListCheats(); void compatibleVersionNotice(const QString repository); signals: From b8a443c728c50c778ea29eca94747f9321e8949b Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 10 Dec 2024 21:15:43 +0100 Subject: [PATCH 148/549] Fix compiling due to typedefs varying across platforms (#1729) * Fix compiling on modern C++ compilers https://github.com/shadps4-emu/shadPS4/commit/cd9fc5d0e935ac12f469d609b1a643964fb33129 broke it * Fix order * Test * Test putting flags in old order * Remove designated initializer --- src/core/thread.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/core/thread.cpp b/src/core/thread.cpp index 0d0804cea..07681e6b9 100644 --- a/src/core/thread.cpp +++ b/src/core/thread.cpp @@ -140,13 +140,12 @@ void NativeThread::Initialize() { ASSERT_MSG(posix_memalign(&sig_stack_ptr, page_size, sig_stack_size) == 0, "Failed to allocate signal stack: {}", errno); - const stack_t sig_stack = { - .ss_sp = sig_stack_ptr, - .ss_size = sig_stack_size, - .ss_flags = 0, - }; + stack_t sig_stack; + sig_stack.ss_sp = sig_stack_ptr; + sig_stack.ss_size = sig_stack_size; + sig_stack.ss_flags = 0; ASSERT_MSG(sigaltstack(&sig_stack, nullptr) == 0, "Failed to set signal stack: {}", errno); #endif } -} // namespace Core \ No newline at end of file +} // namespace Core From e36c4d5f75cf1457a23c8fea7d45cd22cd61fc00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A5IGA?= <164882787+Xphalnos@users.noreply.github.com> Date: Tue, 10 Dec 2024 21:16:16 +0100 Subject: [PATCH 149/549] Displays "Never Played" if the game has never been played (#1697) * Displays "Never Played" if the game has never been played * Update nb.ts + pt_BR.ts --- src/qt_gui/game_list_frame.cpp | 4 ++-- src/qt_gui/translations/ar.ts | 5 +++++ src/qt_gui/translations/da_DK.ts | 5 +++++ src/qt_gui/translations/de.ts | 5 +++++ src/qt_gui/translations/el.ts | 5 +++++ src/qt_gui/translations/en.ts | 5 +++++ src/qt_gui/translations/es_ES.ts | 5 +++++ src/qt_gui/translations/fa_IR.ts | 5 +++++ src/qt_gui/translations/fi.ts | 5 +++++ src/qt_gui/translations/fr.ts | 5 +++++ src/qt_gui/translations/hu_HU.ts | 5 +++++ src/qt_gui/translations/id.ts | 5 +++++ src/qt_gui/translations/it.ts | 5 +++++ src/qt_gui/translations/ja_JP.ts | 5 +++++ src/qt_gui/translations/ko_KR.ts | 5 +++++ src/qt_gui/translations/lt_LT.ts | 5 +++++ src/qt_gui/translations/nb.ts | 5 +++++ src/qt_gui/translations/nl.ts | 5 +++++ src/qt_gui/translations/pl_PL.ts | 5 +++++ src/qt_gui/translations/pt_BR.ts | 5 +++++ src/qt_gui/translations/ro_RO.ts | 5 +++++ src/qt_gui/translations/ru_RU.ts | 5 +++++ src/qt_gui/translations/sq.ts | 5 +++++ src/qt_gui/translations/tr_TR.ts | 5 +++++ src/qt_gui/translations/uk_UA.ts | 5 +++++ src/qt_gui/translations/vi_VN.ts | 5 +++++ src/qt_gui/translations/zh_CN.ts | 5 +++++ src/qt_gui/translations/zh_TW.ts | 5 +++++ 28 files changed, 137 insertions(+), 2 deletions(-) diff --git a/src/qt_gui/game_list_frame.cpp b/src/qt_gui/game_list_frame.cpp index 99628b083..47bfbfef9 100644 --- a/src/qt_gui/game_list_frame.cpp +++ b/src/qt_gui/game_list_frame.cpp @@ -31,7 +31,7 @@ GameListFrame::GameListFrame(std::shared_ptr game_info_get, QWidg this->setColumnWidth(4, 90); // Firmware this->setColumnWidth(5, 90); // Size this->setColumnWidth(6, 90); // Version - this->setColumnWidth(7, 100); // Play Time + this->setColumnWidth(7, 120); // Play Time QStringList headers; headers << tr("Icon") << tr("Name") << tr("Serial") << tr("Region") << tr("Firmware") << tr("Size") << tr("Version") << tr("Play Time") << tr("Path"); @@ -105,7 +105,7 @@ void GameListFrame::PopulateGameList() { QString playTime = GetPlayTime(m_game_info->m_games[i].serial); if (playTime.isEmpty()) { m_game_info->m_games[i].play_time = "0:00:00"; - SetTableItem(i, 7, "0"); + SetTableItem(i, 7, tr("Never Played")); } else { QStringList timeParts = playTime.split(':'); int hours = timeParts[0].toInt(); diff --git a/src/qt_gui/translations/ar.ts b/src/qt_gui/translations/ar.ts index 45a030062..3f861187e 100644 --- a/src/qt_gui/translations/ar.ts +++ b/src/qt_gui/translations/ar.ts @@ -1359,6 +1359,11 @@ Play Time وقت اللعب + + + Never Played + Never Played + CheckUpdate diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts index fa7e9c50b..3539159e2 100644 --- a/src/qt_gui/translations/da_DK.ts +++ b/src/qt_gui/translations/da_DK.ts @@ -1359,6 +1359,11 @@ Play Time Spilletid + + + Never Played + Never Played + CheckUpdate diff --git a/src/qt_gui/translations/de.ts b/src/qt_gui/translations/de.ts index 0fa534bb9..f34402ac9 100644 --- a/src/qt_gui/translations/de.ts +++ b/src/qt_gui/translations/de.ts @@ -1359,6 +1359,11 @@ Play Time Spielzeit + + + Never Played + Never Played + CheckUpdate diff --git a/src/qt_gui/translations/el.ts b/src/qt_gui/translations/el.ts index 5233454b9..65cee641a 100644 --- a/src/qt_gui/translations/el.ts +++ b/src/qt_gui/translations/el.ts @@ -1359,6 +1359,11 @@ Play Time Χρόνος παιχνιδιού + + + Never Played + Never Played + CheckUpdate diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index cd5a5fe8a..7ae583040 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -1359,6 +1359,11 @@ Play Time Play Time + + + Never Played + Never Played + CheckUpdate diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index 0598e04f3..3d1f291a6 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -1359,6 +1359,11 @@ Play Time Tiempo de Juego + + + Never Played + Never Played + CheckUpdate diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index 3cd72cac3..58de03346 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -1359,6 +1359,11 @@ Play Time زمان بازی + + + Never Played + Never Played + CheckUpdate diff --git a/src/qt_gui/translations/fi.ts b/src/qt_gui/translations/fi.ts index 8c9518d0f..0a7f2b250 100644 --- a/src/qt_gui/translations/fi.ts +++ b/src/qt_gui/translations/fi.ts @@ -1359,6 +1359,11 @@ Play Time Peliaika + + + Never Played + Never Played + CheckUpdate diff --git a/src/qt_gui/translations/fr.ts b/src/qt_gui/translations/fr.ts index a9903d43c..fad90622a 100644 --- a/src/qt_gui/translations/fr.ts +++ b/src/qt_gui/translations/fr.ts @@ -1359,6 +1359,11 @@ Play Time Temps de jeu + + + Never Played + Jamais joué + CheckUpdate diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index 135fc4231..937e3f188 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -1359,6 +1359,11 @@ Play Time Játékidő + + + Never Played + Never Played + CheckUpdate diff --git a/src/qt_gui/translations/id.ts b/src/qt_gui/translations/id.ts index 5c6148b86..80873daa9 100644 --- a/src/qt_gui/translations/id.ts +++ b/src/qt_gui/translations/id.ts @@ -1359,6 +1359,11 @@ Play Time Waktu Bermain + + + Never Played + Never Played + CheckUpdate diff --git a/src/qt_gui/translations/it.ts b/src/qt_gui/translations/it.ts index b496cc330..9094a7ed5 100644 --- a/src/qt_gui/translations/it.ts +++ b/src/qt_gui/translations/it.ts @@ -1359,6 +1359,11 @@ Play Time Tempo di Gioco
+ + + Never Played + Never Played + CheckUpdate diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index 3cee79951..ad1f383fe 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -1359,6 +1359,11 @@ Play Time プレイ時間 + + + Never Played + Never Played + CheckUpdate diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts index ea449ebc1..a528db295 100644 --- a/src/qt_gui/translations/ko_KR.ts +++ b/src/qt_gui/translations/ko_KR.ts @@ -1359,6 +1359,11 @@ Play Time Play Time + + + Never Played + Never Played + CheckUpdate diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts index dd467283f..4a2820399 100644 --- a/src/qt_gui/translations/lt_LT.ts +++ b/src/qt_gui/translations/lt_LT.ts @@ -1359,6 +1359,11 @@ Play Time Žaidimo laikas + + + Never Played + Never Played + CheckUpdate diff --git a/src/qt_gui/translations/nb.ts b/src/qt_gui/translations/nb.ts index 303baadaf..028646740 100644 --- a/src/qt_gui/translations/nb.ts +++ b/src/qt_gui/translations/nb.ts @@ -1359,6 +1359,11 @@ Play Time Spilletid + + + Never Played + Never Played + CheckUpdate diff --git a/src/qt_gui/translations/nl.ts b/src/qt_gui/translations/nl.ts index 399aef8be..b66cb94e4 100644 --- a/src/qt_gui/translations/nl.ts +++ b/src/qt_gui/translations/nl.ts @@ -1359,6 +1359,11 @@ Play Time Speeltijd + + + Never Played + Never Played + CheckUpdate diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index 730b37124..8236cf720 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -1359,6 +1359,11 @@ Play Time Czas gry + + + Never Played + Never Played + CheckUpdate diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index dfe785673..5faccf6c5 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -1359,6 +1359,11 @@ Play Time Tempo Jogado + + + Never Played + Nunca jogado + CheckUpdate diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index d73b1be6c..2439e69e2 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -1359,6 +1359,11 @@ Play Time Timp de Joacă + + + Never Played + Never Played + CheckUpdate diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index ab7e2c40e..ccee34517 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -1359,6 +1359,11 @@ Play Time Времени в игре + + + Never Played + Never Played + CheckUpdate diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index bc8f24162..4a02298e8 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -1359,6 +1359,11 @@ Play Time Koha e luajtjes + + + Never Played + Never Played + CheckUpdate diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index 5944f980f..83a45e529 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -1359,6 +1359,11 @@ Play Time Oynama Süresi + + + Never Played + Never Played + CheckUpdate diff --git a/src/qt_gui/translations/uk_UA.ts b/src/qt_gui/translations/uk_UA.ts index b28035335..805fff151 100644 --- a/src/qt_gui/translations/uk_UA.ts +++ b/src/qt_gui/translations/uk_UA.ts @@ -1359,6 +1359,11 @@ Play Time Час у грі + + + Never Played + Never Played + CheckUpdate diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index b5ae8bd39..1ac3d042d 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -1359,6 +1359,11 @@ Play Time Thời gian chơi + + + Never Played + Never Played + CheckUpdate diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index 989e71071..19fb8edff 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -1359,6 +1359,11 @@ Play Time 游戏时间 + + + Never Played + Never Played + CheckUpdate diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index b650a74ea..fbd6d624d 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -1359,6 +1359,11 @@ Play Time 遊玩時間 + + + Never Played + Never Played + CheckUpdate From 14c2be8c670286080e6277a413d03d5019f1ed21 Mon Sep 17 00:00:00 2001 From: rainmakerv2 <30595646+rainmakerv3@users.noreply.github.com> Date: Wed, 11 Dec 2024 20:57:11 +0800 Subject: [PATCH 150/549] Add default value for Separate Update Folder (#1735) Co-authored-by: rainmakerv2 <30595646+jpau02@users.noreply.github.com> --- src/common/config.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/config.cpp b/src/common/config.cpp index eae8897c8..3db98a438 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -743,6 +743,7 @@ void setDefaultValues() { emulator_language = "en"; m_language = 1; gpuId = -1; + separateupdatefolder = false; } } // namespace Config From 51bf98a7b5178f9578c395bb92ad8e965833dc53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A5IGA?= <164882787+Xphalnos@users.noreply.github.com> Date: Wed, 11 Dec 2024 15:07:33 +0100 Subject: [PATCH 151/549] Fix for R4G4B4A4UnormPack16 Tiled image (#1738) --- src/video_core/texture_cache/tile_manager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/video_core/texture_cache/tile_manager.cpp b/src/video_core/texture_cache/tile_manager.cpp index 2bc3bf282..fc3d35e3e 100644 --- a/src/video_core/texture_cache/tile_manager.cpp +++ b/src/video_core/texture_cache/tile_manager.cpp @@ -174,6 +174,7 @@ vk::Format DemoteImageFormatForDetiling(vk::Format format) { switch (format) { case vk::Format::eR8Unorm: return vk::Format::eR8Uint; + case vk::Format::eR4G4B4A4UnormPack16: case vk::Format::eR8G8Unorm: case vk::Format::eR16Sfloat: case vk::Format::eR16Unorm: From 2a953391ef9a1ccd0df8b84a9ab365b0c5d2a701 Mon Sep 17 00:00:00 2001 From: "Daniel R." <47796739+polybiusproxy@users.noreply.github.com> Date: Wed, 11 Dec 2024 19:40:45 +0100 Subject: [PATCH 152/549] liverpool: implement Rewind and IndirectBuffer packets --- src/video_core/amdgpu/liverpool.cpp | 35 +++++++++++++++++++++++++++++ src/video_core/amdgpu/pm4_cmds.h | 13 +++++++++++ 2 files changed, 48 insertions(+) diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index a4eae8e7a..8db2d63c4 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -610,6 +610,17 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); break; } + case PM4ItOpcode::Rewind: { + const PM4CmdRewind* rewind = reinterpret_cast(header); + while (!rewind->Valid()) { + mapped_queues[GfxQueueId].cs_state = regs.cs_program; + TracyFiberLeave; + co_yield {}; + TracyFiberEnter(dcb_task_name); + regs.cs_program = mapped_queues[GfxQueueId].cs_state; + } + break; + } case PM4ItOpcode::WaitRegMem: { const auto* wait_reg_mem = reinterpret_cast(header); // ASSERT(wait_reg_mem->engine.Value() == PM4CmdWaitRegMem::Engine::Me); @@ -630,6 +641,19 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); + auto task = ProcessGraphics( + {indirect_buffer->Address(), indirect_buffer->ib_size}, {}); + while (!task.handle.done()) { + task.handle.resume(); + + TracyFiberLeave; + co_yield {}; + TracyFiberEnter(dcb_task_name); + }; + break; + } case PM4ItOpcode::IncrementDeCounter: { ++cblock.de_count; break; @@ -730,6 +754,17 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, int vqid) { case PM4ItOpcode::AcquireMem: { break; } + case PM4ItOpcode::Rewind: { + const PM4CmdRewind* rewind = reinterpret_cast(header); + while (!rewind->Valid()) { + mapped_queues[vqid].cs_state = regs.cs_program; + TracyFiberLeave; + co_yield {}; + TracyFiberEnter(acb_task_name); + regs.cs_program = mapped_queues[vqid].cs_state; + } + break; + } case PM4ItOpcode::SetShReg: { const auto* set_data = reinterpret_cast(header); std::memcpy(®s.reg_array[ShRegWordOffset + set_data->reg_offset], header + 2, diff --git a/src/video_core/amdgpu/pm4_cmds.h b/src/video_core/amdgpu/pm4_cmds.h index be6751285..238e09fad 100644 --- a/src/video_core/amdgpu/pm4_cmds.h +++ b/src/video_core/amdgpu/pm4_cmds.h @@ -418,6 +418,19 @@ struct PM4DmaData { } }; +struct PM4CmdRewind { + PM4Type3Header header; + union { + u32 raw; + BitField<24, 1, u32> offload_enable; ///< Enable offload polling valid bit to IQ + BitField<31, 1, u32> valid; ///< Set when subsequent packets are valid + }; + + bool Valid() const { + return valid; + } +}; + struct PM4CmdWaitRegMem { enum class Engine : u32 { Me = 0u, Pfp = 1u }; enum class MemSpace : u32 { Register = 0u, Memory = 1u }; From 8db1beaec6dc964d3d743af4807a104321963d49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A5IGA?= <164882787+Xphalnos@users.noreply.github.com> Date: Wed, 11 Dec 2024 19:44:18 +0100 Subject: [PATCH 153/549] Displays FPS before frame latency (#1736) --- src/core/devtools/layer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/devtools/layer.cpp b/src/core/devtools/layer.cpp index 2c2099f4d..776f3377d 100644 --- a/src/core/devtools/layer.cpp +++ b/src/core/devtools/layer.cpp @@ -254,7 +254,7 @@ void L::DrawAdvanced() { void L::DrawSimple() { const auto io = GetIO(); - Text("Frame time: %.3f ms (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); + Text("%.1f FPS (%.2f ms)", io.Framerate, 1000.0f / io.Framerate); } static void LoadSettings(const char* line) { @@ -338,6 +338,7 @@ void L::Draw() { const auto fn = DebugState.flip_frame_count.load(); frame_graph.AddFrame(fn, io.DeltaTime); } + if (IsKeyPressed(ImGuiKey_F10, false)) { if (io.KeyCtrl) { show_advanced_debug = !show_advanced_debug; From e612e881ac2bd3112c38dc0660771447e5e62218 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Wed, 11 Dec 2024 11:10:40 -0800 Subject: [PATCH 154/549] renderer_vulkan: Bind null color attachments when target is masked out. (#1740) * renderer_vulkan: Bind null color attachments when target is masked out. * Simplify setting null color attachment --- src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | 5 +++-- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 11 ++++++++--- src/video_core/renderer_vulkan/vk_scheduler.h | 2 -- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 276e4ef29..b9f318f7a 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -279,7 +279,7 @@ bool PipelineCache::RefreshGraphicsKey() { // recompiler. for (auto cb = 0u, remapped_cb = 0u; cb < Liverpool::NumColorBuffers; ++cb) { auto const& col_buf = regs.color_buffers[cb]; - if (skip_cb_binding || !col_buf) { + if (skip_cb_binding || !col_buf || !regs.color_target_mask.GetMask(cb)) { continue; } const auto base_format = @@ -385,7 +385,8 @@ bool PipelineCache::RefreshGraphicsKey() { // Second pass to fill remain CB pipeline key data for (auto cb = 0u, remapped_cb = 0u; cb < Liverpool::NumColorBuffers; ++cb) { auto const& col_buf = regs.color_buffers[cb]; - if (skip_cb_binding || !col_buf || (key.mrt_mask & (1u << cb)) == 0) { + if (skip_cb_binding || !col_buf || !regs.color_target_mask.GetMask(cb) || + (key.mrt_mask & (1u << cb)) == 0) { key.color_formats[cb] = vk::Format::eUndefined; key.mrt_swizzles[cb] = Liverpool::ColorBuffer::SwapMode::Standard; continue; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 33358b850..496ea5163 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -103,6 +103,14 @@ RenderState Rasterizer::PrepareRenderState(u32 mrt_mask) { continue; } + // If the color buffer is still bound but rendering to it is disabled by the target + // mask, we need to prevent the render area from being affected by unbound render target + // extents. + if (!regs.color_target_mask.GetMask(col_buf_id)) { + state.color_attachments[state.num_color_attachments++].imageView = VK_NULL_HANDLE; + continue; + } + const auto& hint = liverpool->last_cb_extent[col_buf_id]; auto& [image_id, desc] = cb_descs.emplace_back(std::piecewise_construct, std::tuple{}, std::tuple{col_buf, hint}); @@ -118,7 +126,6 @@ RenderState Rasterizer::PrepareRenderState(u32 mrt_mask) { const auto mip = image_view.info.range.base.level; state.width = std::min(state.width, std::max(image.info.size.width >> mip, 1u)); state.height = std::min(state.height, std::max(image.info.size.height >> mip, 1u)); - state.color_images[state.num_color_attachments] = image.image; state.color_attachments[state.num_color_attachments++] = { .imageView = *image_view.image_view, .imageLayout = vk::ImageLayout::eUndefined, @@ -153,7 +160,6 @@ RenderState Rasterizer::PrepareRenderState(u32 mrt_mask) { state.width = std::min(state.width, image.info.size.width); state.height = std::min(state.height, image.info.size.height); - state.depth_image = image.image; state.depth_attachment = { .imageView = *image_view.image_view, .imageLayout = vk::ImageLayout::eUndefined, @@ -716,7 +722,6 @@ void Rasterizer::BeginRendering(const GraphicsPipeline& pipeline, RenderState& s auto& image = texture_cache.GetImage(view.image_id); state.color_attachments[cb_index].imageView = *view.image_view; state.color_attachments[cb_index].imageLayout = image.last_state.layout; - state.color_images[cb_index] = image.image; const auto mip = view.info.range.base.level; state.width = std::min(state.width, std::max(image.info.size.width >> mip, 1u)); diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index 45a9228c9..cdd33745a 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -20,9 +20,7 @@ class Instance; struct RenderState { std::array color_attachments{}; - std::array color_images{}; vk::RenderingAttachmentInfo depth_attachment{}; - vk::Image depth_image{}; u32 num_color_attachments{}; bool has_depth{}; bool has_stencil{}; From 14f7dc3527de3249b2356e48dba4c54f58a2a114 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Wed, 11 Dec 2024 11:11:24 -0800 Subject: [PATCH 155/549] cache: Invalidate pages for file reads. (#1726) * cache: Invalidate pages for file reads. * texture_cache: Simplify invalidate intersection check. * vk_rasterizer: Make aware of mapped memory ranges. * buffer_cache: Remove redundant page calculations. Called functions will convert to page numbers/addresses themselves. * file_system: Simplify memory invalidation and add a few missed cases. --- src/core/libraries/kernel/file_system.cpp | 29 ++++++++++++------- src/core/libraries/kernel/file_system.h | 3 ++ src/core/libraries/kernel/kernel.cpp | 26 ++--------------- src/core/memory.cpp | 6 ++++ src/core/memory.h | 2 ++ src/video_core/page_manager.cpp | 21 +++++--------- .../renderer_vulkan/vk_rasterizer.cpp | 22 ++++++++++++-- .../renderer_vulkan/vk_rasterizer.h | 4 ++- .../texture_cache/texture_cache.cpp | 19 +++++++----- src/video_core/texture_cache/texture_cache.h | 2 +- 10 files changed, 74 insertions(+), 60 deletions(-) diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index 2a65255fb..5ba9976c6 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -1,22 +1,22 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include + #include "common/assert.h" #include "common/logging/log.h" #include "common/scope_exit.h" #include "common/singleton.h" +#include "core/devices/logger.h" +#include "core/devices/nop_device.h" #include "core/file_sys/fs.h" #include "core/libraries/kernel/file_system.h" #include "core/libraries/kernel/orbis_error.h" #include "core/libraries/libs.h" +#include "core/memory.h" #include "kernel.h" -#include -#include - -#include "core/devices/logger.h" -#include "core/devices/nop_device.h" - namespace D = Core::Devices; using FactoryDevice = std::function(u32, const char*, int, u16)>; @@ -201,7 +201,7 @@ int PS4_SYSV_ABI posix_close(int d) { return result; } -size_t PS4_SYSV_ABI sceKernelWrite(int d, const void* buf, size_t nbytes) { +s64 PS4_SYSV_ABI sceKernelWrite(int d, const void* buf, size_t nbytes) { auto* h = Common::Singleton::Instance(); auto* file = h->GetFile(d); if (file == nullptr) { @@ -246,6 +246,15 @@ int PS4_SYSV_ABI sceKernelUnlink(const char* path) { return ORBIS_OK; } +size_t ReadFile(Common::FS::IOFile& file, void* buf, size_t nbytes) { + const auto* memory = Core::Memory::Instance(); + // Invalidate up to the actual number of bytes that could be read. + const auto remaining = file.GetSize() - file.Tell(); + memory->InvalidateMemory(reinterpret_cast(buf), std::min(nbytes, remaining)); + + return file.ReadRaw(buf, nbytes); +} + size_t PS4_SYSV_ABI _readv(int d, const SceKernelIovec* iov, int iovcnt) { auto* h = Common::Singleton::Instance(); auto* file = h->GetFile(d); @@ -264,7 +273,7 @@ size_t PS4_SYSV_ABI _readv(int d, const SceKernelIovec* iov, int iovcnt) { } size_t total_read = 0; for (int i = 0; i < iovcnt; i++) { - total_read += file->f.ReadRaw(iov[i].iov_base, iov[i].iov_len); + total_read += ReadFile(file->f, iov[i].iov_base, iov[i].iov_len); } return total_read; } @@ -351,7 +360,7 @@ s64 PS4_SYSV_ABI sceKernelRead(int d, void* buf, size_t nbytes) { if (file->type == Core::FileSys::FileType::Device) { return file->device->read(buf, nbytes); } - return file->f.ReadRaw(buf, nbytes); + return ReadFile(file->f, buf, nbytes); } int PS4_SYSV_ABI posix_read(int d, void* buf, size_t nbytes) { @@ -541,7 +550,7 @@ s64 PS4_SYSV_ABI sceKernelPreadv(int d, SceKernelIovec* iov, int iovcnt, s64 off } size_t total_read = 0; for (int i = 0; i < iovcnt; i++) { - total_read += file->f.ReadRaw(iov[i].iov_base, iov[i].iov_len); + total_read += ReadFile(file->f, iov[i].iov_base, iov[i].iov_len); } return total_read; } diff --git a/src/core/libraries/kernel/file_system.h b/src/core/libraries/kernel/file_system.h index dcbb3957d..6443962ff 100644 --- a/src/core/libraries/kernel/file_system.h +++ b/src/core/libraries/kernel/file_system.h @@ -65,6 +65,9 @@ constexpr int ORBIS_KERNEL_O_DSYNC = 0x1000; constexpr int ORBIS_KERNEL_O_DIRECT = 0x00010000; constexpr int ORBIS_KERNEL_O_DIRECTORY = 0x00020000; +s64 PS4_SYSV_ABI sceKernelWrite(int d, const void* buf, size_t nbytes); +s64 PS4_SYSV_ABI sceKernelRead(int d, void* buf, size_t nbytes); + void RegisterFileSystem(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/kernel.cpp b/src/core/libraries/kernel/kernel.cpp index bda446257..b05c96fad 100644 --- a/src/core/libraries/kernel/kernel.cpp +++ b/src/core/libraries/kernel/kernel.cpp @@ -133,33 +133,11 @@ void PS4_SYSV_ABI sceLibcHeapGetTraceInfo(HeapInfoInfo* info) { } s64 PS4_SYSV_ABI ps4__write(int d, const char* buf, std::size_t nbytes) { - auto* h = Common::Singleton::Instance(); - auto* file = h->GetFile(d); - if (file == nullptr) { - return ORBIS_KERNEL_ERROR_EBADF; - } - std::scoped_lock lk{file->m_mutex}; - if (file->type == Core::FileSys::FileType::Device) { - return file->device->write(buf, nbytes); - } - return file->f.WriteRaw(buf, nbytes); + return sceKernelWrite(d, buf, nbytes); } s64 PS4_SYSV_ABI ps4__read(int d, void* buf, u64 nbytes) { - if (d == 0) { - return static_cast( - strlen(std::fgets(static_cast(buf), static_cast(nbytes), stdin))); - } - auto* h = Common::Singleton::Instance(); - auto* file = h->GetFile(d); - if (file == nullptr) { - return ORBIS_KERNEL_ERROR_EBADF; - } - std::scoped_lock lk{file->m_mutex}; - if (file->type == Core::FileSys::FileType::Device) { - return file->device->read(buf, nbytes); - } - return file->f.ReadRaw(buf, nbytes); + return sceKernelRead(d, buf, nbytes); } struct OrbisKernelUuid { diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 980beee79..41db7df4b 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -587,6 +587,12 @@ void MemoryManager::NameVirtualRange(VAddr virtual_addr, size_t size, std::strin it->second.name = name; } +void MemoryManager::InvalidateMemory(const VAddr addr, const u64 size) const { + if (rasterizer) { + rasterizer->InvalidateMemory(addr, size); + } +} + VAddr MemoryManager::SearchFree(VAddr virtual_addr, size_t size, u32 alignment) { // If the requested address is below the mapped range, start search from the lowest address auto min_search_address = impl.SystemManagedVirtualBase(); diff --git a/src/core/memory.h b/src/core/memory.h index 364609451..a9f2df322 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -211,6 +211,8 @@ public: void NameVirtualRange(VAddr virtual_addr, size_t size, std::string_view name); + void InvalidateMemory(VAddr addr, u64 size) const; + private: VMAHandle FindVMA(VAddr target) { return std::prev(vma_map.upper_bound(target)); diff --git a/src/video_core/page_manager.cpp b/src/video_core/page_manager.cpp index fefae81f4..556555c25 100644 --- a/src/video_core/page_manager.cpp +++ b/src/video_core/page_manager.cpp @@ -114,8 +114,7 @@ struct PageManager::Impl { // Notify rasterizer about the fault. const VAddr addr = msg.arg.pagefault.address; - const VAddr addr_page = GetPageAddr(addr); - rasterizer->InvalidateMemory(addr, addr_page, PAGESIZE); + rasterizer->InvalidateMemory(addr, 1); } } @@ -135,17 +134,14 @@ struct PageManager::Impl { } void OnMap(VAddr address, size_t size) { - owned_ranges += boost::icl::interval::right_open(address, address + size); + // No-op } void OnUnmap(VAddr address, size_t size) { - owned_ranges -= boost::icl::interval::right_open(address, address + size); + // No-op } void Protect(VAddr address, size_t size, bool allow_write) { - ASSERT_MSG(owned_ranges.find(address) != owned_ranges.end(), - "Attempted to track non-GPU memory at address {:#x}, size {:#x}.", address, - size); auto* memory = Core::Memory::Instance(); auto& impl = memory->GetAddressSpace(); impl.Protect(address, size, @@ -155,17 +151,13 @@ struct PageManager::Impl { static bool GuestFaultSignalHandler(void* context, void* fault_address) { const auto addr = reinterpret_cast(fault_address); - const bool is_write = Common::IsWriteError(context); - if (is_write && owned_ranges.find(addr) != owned_ranges.end()) { - const VAddr addr_aligned = GetPageAddr(addr); - rasterizer->InvalidateMemory(addr, addr_aligned, PAGESIZE); - return true; + if (Common::IsWriteError(context)) { + return rasterizer->InvalidateMemory(addr, 1); } return false; } inline static Vulkan::Rasterizer* rasterizer; - inline static boost::icl::interval_set owned_ranges; }; #endif @@ -210,6 +202,9 @@ void PageManager::UpdatePagesCachedCount(VAddr addr, u64 size, s32 delta) { const VAddr interval_start_addr = boost::icl::first(interval) << PageShift; const VAddr interval_end_addr = boost::icl::last_next(interval) << PageShift; const u32 interval_size = interval_end_addr - interval_start_addr; + ASSERT_MSG(rasterizer->IsMapped(interval_start_addr, interval_size), + "Attempted to track non-GPU memory at address {:#x}, size {:#x}.", + interval_start_addr, interval_size); if (delta > 0 && count == delta) { impl->Protect(interval_start_addr, interval_size, false); } else if (delta < 0 && count == -delta) { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 496ea5163..5fd0d99a4 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -841,12 +841,27 @@ u32 Rasterizer::ReadDataFromGds(u32 gds_offset) { return value; } -void Rasterizer::InvalidateMemory(VAddr addr, VAddr addr_aligned, u64 size) { - buffer_cache.InvalidateMemory(addr_aligned, size); - texture_cache.InvalidateMemory(addr, addr_aligned, size); +bool Rasterizer::InvalidateMemory(VAddr addr, u64 size) { + if (!IsMapped(addr, size)) { + // Not GPU mapped memory, can skip invalidation logic entirely. + return false; + } + buffer_cache.InvalidateMemory(addr, size); + texture_cache.InvalidateMemory(addr, size); + return true; +} + +bool Rasterizer::IsMapped(VAddr addr, u64 size) { + if (size == 0) { + // There is no memory, so not mapped. + return false; + } + return mapped_ranges.find(boost::icl::interval::right_open(addr, addr + size)) != + mapped_ranges.end(); } void Rasterizer::MapMemory(VAddr addr, u64 size) { + mapped_ranges += boost::icl::interval::right_open(addr, addr + size); page_manager.OnGpuMap(addr, size); } @@ -854,6 +869,7 @@ void Rasterizer::UnmapMemory(VAddr addr, u64 size) { buffer_cache.InvalidateMemory(addr, size); texture_cache.UnmapMemory(addr, size); page_manager.OnGpuUnmap(addr, size); + mapped_ranges -= boost::icl::interval::right_open(addr, addr + size); } void Rasterizer::UpdateDynamicState(const GraphicsPipeline& pipeline) { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 9214372ee..ec1b5e134 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -54,7 +54,8 @@ public: void InlineData(VAddr address, const void* value, u32 num_bytes, bool is_gds); u32 ReadDataFromGds(u32 gsd_offset); - void InvalidateMemory(VAddr addr, VAddr addr_aligned, u64 size); + bool InvalidateMemory(VAddr addr, u64 size); + bool IsMapped(VAddr addr, u64 size); void MapMemory(VAddr addr, u64 size); void UnmapMemory(VAddr addr, u64 size); @@ -100,6 +101,7 @@ private: VideoCore::TextureCache texture_cache; AmdGpu::Liverpool* liverpool; Core::MemoryManager* memory; + boost::icl::interval_set mapped_ranges; PipelineCache pipeline_cache; boost::container::static_vector< diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index 0e5bbc1f3..66132753d 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -56,24 +56,27 @@ void TextureCache::MarkAsMaybeDirty(ImageId image_id, Image& image) { UntrackImage(image_id); } -void TextureCache::InvalidateMemory(VAddr addr, VAddr page_addr, size_t size) { +void TextureCache::InvalidateMemory(VAddr addr, size_t size) { std::scoped_lock lock{mutex}; - ForEachImageInRegion(page_addr, size, [&](ImageId image_id, Image& image) { + const auto end = addr + size; + const auto pages_start = PageManager::GetPageAddr(addr); + const auto pages_end = PageManager::GetNextPageAddr(addr + size - 1); + ForEachImageInRegion(pages_start, pages_end - pages_start, [&](ImageId image_id, Image& image) { const auto image_begin = image.info.guest_address; const auto image_end = image.info.guest_address + image.info.guest_size_bytes; - const auto page_end = page_addr + size; - if (image_begin <= addr && addr < image_end) { - // This image was definitely accessed by this page fault. - // Untrack image, so the range is unprotected and the guest can write freely + if (image_begin < end && addr < image_end) { + // Start or end of the modified region is in the image, or the image is entirely within + // the modified region, so the image was definitely accessed by this page fault. + // Untrack the image, so that the range is unprotected and the guest can write freely. image.flags |= ImageFlagBits::CpuDirty; UntrackImage(image_id); - } else if (page_end < image_end) { + } else if (pages_end < image_end) { // This page access may or may not modify the image. // We should not mark it as dirty now. If it really was modified // it will receive more invalidations on its other pages. // Remove tracking from this page only. UntrackImageHead(image_id); - } else if (image_begin < page_addr) { + } else if (image_begin < pages_start) { // This page access does not modify the image but the page should be untracked. // We should not mark this image as dirty now. If it really was modified // it will receive more invalidations on its other pages. diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 676ede777..3ef81a699 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -95,7 +95,7 @@ public: ~TextureCache(); /// Invalidates any image in the logical page range. - void InvalidateMemory(VAddr addr, VAddr page_addr, size_t size); + void InvalidateMemory(VAddr addr, size_t size); /// Marks an image as dirty if it exists at the provided address. void InvalidateMemoryFromGPU(VAddr address, size_t max_size); From 0211b7e38e448801b99cd4855b188b3800a4a2e5 Mon Sep 17 00:00:00 2001 From: slick-daddy <129640104+slick-daddy@users.noreply.github.com> Date: Wed, 11 Dec 2024 22:11:34 +0300 Subject: [PATCH 156/549] Minor Translation Fixes (#1691) * Update tr_TR.ts Minor translation fixes. Mainly contains capitalization and small changes. * Update tr_TR.ts --- src/qt_gui/translations/tr_TR.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index 83a45e529..4c77bc16a 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -351,7 +351,7 @@ Download Cheats/Patches - Hileler / Yamanlar İndir + Hileleri/Yamaları İndir @@ -505,7 +505,7 @@ Is PS4 Pro - PS4 Pro mu + PS4 Pro @@ -545,12 +545,12 @@ Hide Cursor - İmleci gizle + İmleci Gizle Hide Cursor Idle Timeout - İmleç için hareketsizlik zaman aşımı + İmleç İçin Hareketsizlik Zaman Aşımı @@ -665,7 +665,7 @@ Check for Updates - Güncellemeleri kontrol et + Güncellemeleri Kontrol Et @@ -703,7 +703,7 @@ Download Patches For All Games - Tüm Oyunlar İçin Yamanları İndir + Tüm Oyunlar İçin Yamaları İndir @@ -758,7 +758,7 @@ Patch detected! - Yamanın tespit edildi! + Yama tespit edildi! @@ -941,7 +941,7 @@ Unable to open files.json for reading. - files.json dosyasını okumak için açılamadı. + files.json dosyası okumak için açılamadı. @@ -1169,7 +1169,7 @@ ps4proCheckBox - PS4 Pro Mu:\nEmülatörü bir PS4 PRO gibi çalıştırır; bu, bunu destekleyen oyunlarda özel özellikleri etkinleştirebilir. + PS4 Pro:\nEmülatörü bir PS4 PRO gibi çalıştırır; bu, bunu destekleyen oyunlarda özel özellikleri etkinleştirebilir. @@ -1493,4 +1493,4 @@ Güncelleme betiği dosyası oluşturulamadı - \ No newline at end of file + From b82993c56805245fc75742a3e0a72db770cdf054 Mon Sep 17 00:00:00 2001 From: "Daniel R." <47796739+polybiusproxy@users.noreply.github.com> Date: Wed, 11 Dec 2024 20:12:35 +0100 Subject: [PATCH 157/549] core/kernel: implement condvar signalto --- src/core/libraries/kernel/threads/condvar.cpp | 15 ++++++++++++--- src/core/libraries/kernel/threads/pthread.h | 2 +- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/core/libraries/kernel/threads/condvar.cpp b/src/core/libraries/kernel/threads/condvar.cpp index 2927899d9..853526559 100644 --- a/src/core/libraries/kernel/threads/condvar.cpp +++ b/src/core/libraries/kernel/threads/condvar.cpp @@ -177,7 +177,7 @@ int PS4_SYSV_ABI posix_pthread_cond_reltimedwait_np(PthreadCondT* cond, PthreadM return cvp->Wait(mutex, THR_RELTIME, usec); } -int PthreadCond::Signal() { +int PthreadCond::Signal(Pthread* thread) { Pthread* curthread = g_curthread; SleepqLock(this); @@ -187,7 +187,8 @@ int PthreadCond::Signal() { return 0; } - Pthread* td = sq->sq_blocked.front(); + Pthread* td = thread ? thread : sq->sq_blocked.front(); + PthreadMutex* mp = td->mutex_obj; has_user_waiters = SleepqRemove(sq, td); @@ -262,7 +263,13 @@ int PthreadCond::Broadcast() { int PS4_SYSV_ABI posix_pthread_cond_signal(PthreadCondT* cond) { PthreadCond* cvp{}; CHECK_AND_INIT_COND - return cvp->Signal(); + return cvp->Signal(nullptr); +} + +int PS4_SYSV_ABI posix_pthread_cond_signalto_np(PthreadCondT* cond, Pthread* thread) { + PthreadCond* cvp{}; + CHECK_AND_INIT_COND + return cvp->Signal(thread); } int PS4_SYSV_ABI posix_pthread_cond_broadcast(PthreadCondT* cond) { @@ -358,6 +365,8 @@ void RegisterCond(Core::Loader::SymbolsResolver* sym) { ORBIS(posix_pthread_cond_reltimedwait_np)); LIB_FUNCTION("g+PZd2hiacg", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_cond_destroy)); + LIB_FUNCTION("o69RpYO-Mu0", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_cond_signalto_np)); } } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/pthread.h b/src/core/libraries/kernel/threads/pthread.h index 456c2ef37..089156776 100644 --- a/src/core/libraries/kernel/threads/pthread.h +++ b/src/core/libraries/kernel/threads/pthread.h @@ -123,7 +123,7 @@ struct PthreadCond { int Wait(PthreadMutexT* mutex, const OrbisKernelTimespec* abstime, u64 usec = 0); - int Signal(); + int Signal(Pthread* thread); int Broadcast(); }; using PthreadCondT = PthreadCond*; From 0a9c437ec80ece0f8e1ab222b85537c1bb49b46e Mon Sep 17 00:00:00 2001 From: TheTurtle Date: Wed, 11 Dec 2024 21:17:55 +0200 Subject: [PATCH 158/549] hot-fix: Enforce minimum stack size of 64KB Fixes some crashes in BB from unity pt 1 --- src/core/libraries/kernel/threads/pthread.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/core/libraries/kernel/threads/pthread.cpp b/src/core/libraries/kernel/threads/pthread.cpp index c83af86d0..08886c6eb 100644 --- a/src/core/libraries/kernel/threads/pthread.cpp +++ b/src/core/libraries/kernel/threads/pthread.cpp @@ -243,6 +243,13 @@ int PS4_SYSV_ABI posix_pthread_create_name_np(PthreadT* thread, const PthreadAtt static int TidCounter = 1; new_thread->tid = ++TidCounter; + if (new_thread->attr.stackaddr_attr == 0) { + /* Enforce minimum stack size of 64 KB */ + static constexpr size_t MinimumStack = 64_KB; + auto& stacksize = new_thread->attr.stacksize_attr; + stacksize = std::max(stacksize, MinimumStack); + } + if (thread_state->CreateStack(&new_thread->attr) != 0) { /* Insufficient memory to create a stack: */ thread_state->Free(curthread, new_thread); From 714605c6a7d980868171c76121a23db328edd001 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Wed, 11 Dec 2024 12:51:39 -0800 Subject: [PATCH 159/549] renderer_vulkan: Require exact image format for resolve pass. (#1742) --- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 6 ++++-- src/video_core/texture_cache/texture_cache.cpp | 13 ++++++++++--- src/video_core/texture_cache/texture_cache.h | 1 + 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 5fd0d99a4..bfcdc9538 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -790,8 +790,10 @@ void Rasterizer::Resolve() { mrt0_hint}; VideoCore::TextureCache::RenderTargetDesc mrt1_desc{liverpool->regs.color_buffers[1], mrt1_hint}; - auto& mrt0_image = texture_cache.GetImage(texture_cache.FindImage(mrt0_desc)); - auto& mrt1_image = texture_cache.GetImage(texture_cache.FindImage(mrt1_desc)); + auto& mrt0_image = + texture_cache.GetImage(texture_cache.FindImage(mrt0_desc, VideoCore::FindFlags::ExactFmt)); + auto& mrt1_image = + texture_cache.GetImage(texture_cache.FindImage(mrt1_desc, VideoCore::FindFlags::ExactFmt)); VideoCore::SubresourceRange mrt0_range; mrt0_range.base.layer = liverpool->regs.color_buffers[0].view.slice_start; diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index 66132753d..153314d2b 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -324,6 +324,10 @@ ImageId TextureCache::FindImage(BaseDesc& desc, FindFlags flags) { !IsVulkanFormatCompatible(info.pixel_format, cache_image.info.pixel_format)) { continue; } + if (True(flags & FindFlags::ExactFmt) && + info.pixel_format != cache_image.info.pixel_format) { + continue; + } ASSERT((cache_image.info.type == info.type || info.size == Extent3D{1, 1, 1} || True(flags & FindFlags::RelaxFmt))); image_id = cache_id; @@ -348,9 +352,12 @@ ImageId TextureCache::FindImage(BaseDesc& desc, FindFlags flags) { } if (image_id) { - Image& image_resoved = slot_images[image_id]; - - if (image_resoved.info.resources < info.resources) { + Image& image_resolved = slot_images[image_id]; + if (True(flags & FindFlags::ExactFmt) && + info.pixel_format != image_resolved.info.pixel_format) { + // Cannot reuse this image as we need the exact requested format. + image_id = {}; + } else if (image_resolved.info.resources < info.resources) { // The image was clearly picked up wrong. FreeImage(image_id); image_id = {}; diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 3ef81a699..430415ed2 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -28,6 +28,7 @@ enum class FindFlags { RelaxDim = 1 << 1, ///< Do not check the dimentions of image, only address. RelaxSize = 1 << 2, ///< Do not check that the size matches exactly. RelaxFmt = 1 << 3, ///< Do not check that format is compatible. + ExactFmt = 1 << 4, ///< Require the format to be exactly the same. }; DECLARE_ENUM_FLAG_OPERATORS(FindFlags) From 7f4265834a3112e53133783d62627d683611ba71 Mon Sep 17 00:00:00 2001 From: TheTurtle Date: Thu, 12 Dec 2024 03:33:49 +0200 Subject: [PATCH 160/549] hot-fix: Fix race in rwlock Resetting the owner should be before the lock is unlocked, otherwise a waiter might lock and set a new owner before its reset. --- src/core/libraries/kernel/threads/rwlock.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/libraries/kernel/threads/rwlock.cpp b/src/core/libraries/kernel/threads/rwlock.cpp index affaaf994..ff211e48c 100644 --- a/src/core/libraries/kernel/threads/rwlock.cpp +++ b/src/core/libraries/kernel/threads/rwlock.cpp @@ -177,13 +177,13 @@ int PS4_SYSV_ABI posix_pthread_rwlock_unlock(PthreadRwlockT* rwlock) { } if (prwlock->owner == curthread) { - prwlock->lock.unlock(); prwlock->owner = nullptr; + prwlock->lock.unlock(); } else { - prwlock->lock.unlock_shared(); if (prwlock->owner == nullptr) { curthread->rdlock_count--; } + prwlock->lock.unlock_shared(); } return 0; From 5789d1a5fbf5033ff08c2ed356087e1869d08296 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Wed, 11 Dec 2024 23:47:07 -0800 Subject: [PATCH 161/549] playgo: Lower scePlayGoGetLocus log to debug. (#1748) --- src/core/libraries/playgo/playgo.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/libraries/playgo/playgo.cpp b/src/core/libraries/playgo/playgo.cpp index 13d6f991f..848533ff7 100644 --- a/src/core/libraries/playgo/playgo.cpp +++ b/src/core/libraries/playgo/playgo.cpp @@ -137,8 +137,8 @@ s32 PS4_SYSV_ABI scePlayGoGetLanguageMask(OrbisPlayGoHandle handle, s32 PS4_SYSV_ABI scePlayGoGetLocus(OrbisPlayGoHandle handle, const OrbisPlayGoChunkId* chunkIds, uint32_t numberOfEntries, OrbisPlayGoLocus* outLoci) { - LOG_INFO(Lib_PlayGo, "called handle = {}, chunkIds = {}, numberOfEntries = {}", handle, - *chunkIds, numberOfEntries); + LOG_DEBUG(Lib_PlayGo, "called handle = {}, chunkIds = {}, numberOfEntries = {}", handle, + *chunkIds, numberOfEntries); if (handle != PlaygoHandle) { return ORBIS_PLAYGO_ERROR_BAD_HANDLE; From 3d1e332c6f0e67e558d541a65fedf80cf1c9c27f Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Thu, 12 Dec 2024 01:05:59 -0800 Subject: [PATCH 162/549] renderer_vulkan: Disable culling for RectList. (#1749) --- .../renderer_vulkan/liverpool_to_vk.cpp | 27 +++++++++++++++++++ .../renderer_vulkan/liverpool_to_vk.h | 2 ++ .../renderer_vulkan/vk_graphics_pipeline.cpp | 4 ++- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp index f0f7d352c..fa8d28ba0 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp @@ -65,6 +65,33 @@ vk::CompareOp CompareOp(Liverpool::CompareFunc func) { } } +bool IsPrimitiveCulled(AmdGpu::PrimitiveType type) { + switch (type) { + case AmdGpu::PrimitiveType::TriangleList: + case AmdGpu::PrimitiveType::TriangleFan: + case AmdGpu::PrimitiveType::TriangleStrip: + case AmdGpu::PrimitiveType::PatchPrimitive: + case AmdGpu::PrimitiveType::AdjTriangleList: + case AmdGpu::PrimitiveType::AdjTriangleStrip: + case AmdGpu::PrimitiveType::QuadList: + case AmdGpu::PrimitiveType::QuadStrip: + case AmdGpu::PrimitiveType::Polygon: + return true; + case AmdGpu::PrimitiveType::None: + case AmdGpu::PrimitiveType::PointList: + case AmdGpu::PrimitiveType::LineList: + case AmdGpu::PrimitiveType::LineStrip: + case AmdGpu::PrimitiveType::AdjLineList: + case AmdGpu::PrimitiveType::AdjLineStrip: + case AmdGpu::PrimitiveType::RectList: // Screen-aligned rectangles that are not culled + case AmdGpu::PrimitiveType::LineLoop: + return false; + default: + UNREACHABLE(); + return true; + } +} + vk::PrimitiveTopology PrimitiveType(AmdGpu::PrimitiveType type) { switch (type) { case AmdGpu::PrimitiveType::PointList: diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.h b/src/video_core/renderer_vulkan/liverpool_to_vk.h index 287ba691e..ebd09f0ee 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.h +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.h @@ -18,6 +18,8 @@ vk::StencilOp StencilOp(Liverpool::StencilFunc op); vk::CompareOp CompareOp(Liverpool::CompareFunc func); +bool IsPrimitiveCulled(AmdGpu::PrimitiveType type); + vk::PrimitiveTopology PrimitiveType(AmdGpu::PrimitiveType type); vk::PolygonMode PolygonMode(Liverpool::PolygonMode mode); diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index d53204c77..814657e5d 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -110,7 +110,9 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul .depthClampEnable = false, .rasterizerDiscardEnable = false, .polygonMode = LiverpoolToVK::PolygonMode(key.polygon_mode), - .cullMode = LiverpoolToVK::CullMode(key.cull_mode), + .cullMode = LiverpoolToVK::IsPrimitiveCulled(key.prim_type) + ? LiverpoolToVK::CullMode(key.cull_mode) + : vk::CullModeFlagBits::eNone, .frontFace = key.front_face == Liverpool::FrontFace::Clockwise ? vk::FrontFace::eClockwise : vk::FrontFace::eCounterClockwise, From 7aa868562c86564fc9ba94fc3a727c283a2bda99 Mon Sep 17 00:00:00 2001 From: Osyotr Date: Thu, 12 Dec 2024 16:45:59 +0300 Subject: [PATCH 163/549] video_core: add eR5G5B5A1UnormPack16 support to the detiler (#1741) --- src/video_core/texture_cache/tile_manager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/video_core/texture_cache/tile_manager.cpp b/src/video_core/texture_cache/tile_manager.cpp index fc3d35e3e..d8d23c400 100644 --- a/src/video_core/texture_cache/tile_manager.cpp +++ b/src/video_core/texture_cache/tile_manager.cpp @@ -175,6 +175,7 @@ vk::Format DemoteImageFormatForDetiling(vk::Format format) { case vk::Format::eR8Unorm: return vk::Format::eR8Uint; case vk::Format::eR4G4B4A4UnormPack16: + case vk::Format::eR5G5B5A1UnormPack16: case vk::Format::eR8G8Unorm: case vk::Format::eR16Sfloat: case vk::Format::eR16Unorm: From ec8e5d5ef17870165e8e8ac6a02f29a56ac76060 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Thu, 12 Dec 2024 10:45:18 -0800 Subject: [PATCH 164/549] renderer_vulkan: Fix some color attachment indexing issues. (#1755) --- .../renderer_vulkan/vk_graphics_pipeline.cpp | 8 ++--- .../renderer_vulkan/vk_graphics_pipeline.h | 1 + .../renderer_vulkan/vk_pipeline_cache.cpp | 34 +++++++++++++------ .../renderer_vulkan/vk_platform.cpp | 1 + .../renderer_vulkan/vk_rasterizer.cpp | 1 + 5 files changed, 30 insertions(+), 15 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 814657e5d..795537574 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -229,17 +229,15 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul }); } - const auto it = std::ranges::find(key.color_formats, vk::Format::eUndefined); - const u32 num_color_formats = std::distance(key.color_formats.begin(), it); const vk::PipelineRenderingCreateInfoKHR pipeline_rendering_ci = { - .colorAttachmentCount = num_color_formats, + .colorAttachmentCount = key.num_color_attachments, .pColorAttachmentFormats = key.color_formats.data(), .depthAttachmentFormat = key.depth_format, .stencilAttachmentFormat = key.stencil_format, }; std::array attachments; - for (u32 i = 0; i < num_color_formats; i++) { + for (u32 i = 0; i < key.num_color_attachments; i++) { const auto& control = key.blend_controls[i]; const auto src_color = LiverpoolToVK::BlendFactor(control.color_src_factor); const auto dst_color = LiverpoolToVK::BlendFactor(control.color_dst_factor); @@ -292,7 +290,7 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul const vk::PipelineColorBlendStateCreateInfo color_blending = { .logicOpEnable = false, .logicOp = vk::LogicOp::eCopy, - .attachmentCount = num_color_formats, + .attachmentCount = key.num_color_attachments, .pAttachments = attachments.data(), .blendConstants = std::array{1.0f, 1.0f, 1.0f, 1.0f}, }; diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index 2834fceb7..703a0680e 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -29,6 +29,7 @@ using Liverpool = AmdGpu::Liverpool; struct GraphicsPipelineKey { std::array stage_hashes; + u32 num_color_attachments; std::array color_formats; std::array color_num_formats; std::array mrt_swizzles; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index b9f318f7a..0fa77e19b 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -268,6 +268,7 @@ bool PipelineCache::RefreshGraphicsKey() { // `RenderingInfo` is assumed to be initialized with a contiguous array of valid color // attachments. This might be not a case as HW color buffers can be bound in an arbitrary // order. We need to do some arrays compaction at this stage + key.num_color_attachments = 0; key.color_formats.fill(vk::Format::eUndefined); key.color_num_formats.fill(AmdGpu::NumberFormat::Unorm); key.blend_controls.fill({}); @@ -277,11 +278,19 @@ bool PipelineCache::RefreshGraphicsKey() { // First pass of bindings check to idenitfy formats and swizzles and pass them to rhe shader // recompiler. - for (auto cb = 0u, remapped_cb = 0u; cb < Liverpool::NumColorBuffers; ++cb) { + for (auto cb = 0u; cb < Liverpool::NumColorBuffers; ++cb) { auto const& col_buf = regs.color_buffers[cb]; - if (skip_cb_binding || !col_buf || !regs.color_target_mask.GetMask(cb)) { + if (skip_cb_binding || !col_buf) { + // No attachment bound and no incremented index. continue; } + + const auto remapped_cb = key.num_color_attachments++; + if (!regs.color_target_mask.GetMask(cb)) { + // Bound to null handle, skip over this attachment index. + continue; + } + const auto base_format = LiverpoolToVK::SurfaceFormat(col_buf.info.format, col_buf.NumFormat()); key.color_formats[remapped_cb] = @@ -290,8 +299,6 @@ bool PipelineCache::RefreshGraphicsKey() { if (base_format == key.color_formats[remapped_cb]) { key.mrt_swizzles[remapped_cb] = col_buf.info.comp_swap.Value(); } - - ++remapped_cb; } fetch_shader = std::nullopt; @@ -385,10 +392,18 @@ bool PipelineCache::RefreshGraphicsKey() { // Second pass to fill remain CB pipeline key data for (auto cb = 0u, remapped_cb = 0u; cb < Liverpool::NumColorBuffers; ++cb) { auto const& col_buf = regs.color_buffers[cb]; - if (skip_cb_binding || !col_buf || !regs.color_target_mask.GetMask(cb) || - (key.mrt_mask & (1u << cb)) == 0) { - key.color_formats[cb] = vk::Format::eUndefined; - key.mrt_swizzles[cb] = Liverpool::ColorBuffer::SwapMode::Standard; + if (skip_cb_binding || !col_buf) { + // No attachment bound and no incremented index. + continue; + } + + if (!regs.color_target_mask.GetMask(cb) || (key.mrt_mask & (1u << cb)) == 0) { + // Attachment is masked out by either color_target_mask or shader mrt_mask. In the case + // of the latter we need to change format to undefined, and either way we need to + // increment the index for the null attachment binding. + key.color_formats[remapped_cb] = vk::Format::eUndefined; + key.mrt_swizzles[remapped_cb] = Liverpool::ColorBuffer::SwapMode::Standard; + ++remapped_cb; continue; } @@ -397,10 +412,9 @@ bool PipelineCache::RefreshGraphicsKey() { !col_buf.info.blend_bypass); key.write_masks[remapped_cb] = vk::ColorComponentFlags{regs.color_target_mask.GetMask(cb)}; key.cb_shader_mask.SetMask(remapped_cb, regs.color_shader_mask.GetMask(cb)); + ++remapped_cb; num_samples = std::max(num_samples, 1u << col_buf.attrib.num_samples_log2); - - ++remapped_cb; } // It seems that the number of samples > 1 set in the AA config doesn't mean we're always diff --git a/src/video_core/renderer_vulkan/vk_platform.cpp b/src/video_core/renderer_vulkan/vk_platform.cpp index 2e717397b..f5e513611 100644 --- a/src/video_core/renderer_vulkan/vk_platform.cpp +++ b/src/video_core/renderer_vulkan/vk_platform.cpp @@ -44,6 +44,7 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL DebugUtilsCallback( case 0xc81ad50e: case 0xb7c39078: case 0x32868fde: // vkCreateBufferView(): pCreateInfo->range does not equal VK_WHOLE_SIZE + case 0x1012616b: // `VK_FORMAT_UNDEFINED` does not match fragment shader output type return VK_FALSE; default: break; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index bfcdc9538..9abf1b527 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -100,6 +100,7 @@ RenderState Rasterizer::PrepareRenderState(u32 mrt_mask) { // an unnecessary transition and may result in state conflict if the resource is already // bound for reading. if ((mrt_mask & (1 << col_buf_id)) == 0) { + state.color_attachments[state.num_color_attachments++].imageView = VK_NULL_HANDLE; continue; } From 1e3d034f9682956e5737126ef8b97d54cdf08d2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20Cea=20L=C3=B3pez?= Date: Thu, 12 Dec 2024 21:45:56 +0100 Subject: [PATCH 165/549] Fix HLE buffer copy not executed when there's only 1 copy. (#1754) --- src/video_core/renderer_vulkan/vk_shader_hle.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_shader_hle.cpp b/src/video_core/renderer_vulkan/vk_shader_hle.cpp index df9d40f07..d1d4f9af3 100644 --- a/src/video_core/renderer_vulkan/vk_shader_hle.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_hle.cpp @@ -60,7 +60,7 @@ bool ExecuteCopyShaderHLE(const Shader::Info& info, const AmdGpu::Liverpool::Reg static constexpr vk::DeviceSize MaxDistanceForMerge = 64_MB; u32 batch_start = 0; - u32 batch_end = 1; + u32 batch_end = copies.size() > 1 ? 1 : 0; while (batch_end < copies.size()) { // Place first copy into the current batch @@ -72,7 +72,7 @@ bool ExecuteCopyShaderHLE(const Shader::Info& info, const AmdGpu::Liverpool::Reg for (int i = batch_start + 1; i < copies.size(); i++) { // Compute new src and dst bounds if we were to batch this copy - const auto [src_offset, dst_offset, size] = copies[i]; + const auto& [src_offset, dst_offset, size] = copies[i]; auto new_src_offset_min = std::min(src_offset_min, src_offset); auto new_src_offset_max = std::max(src_offset_max, src_offset + size); if (new_src_offset_max - new_src_offset_min > MaxDistanceForMerge) { From 2a19d915e8b8420c92ad819775cd9ae6dd7aa734 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Thu, 12 Dec 2024 22:46:20 +0200 Subject: [PATCH 166/549] fix for detecting more that 2 players and play both with player 1 keys (#1750) --- src/core/libraries/pad/pad.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index ec4186f11..98f086dd9 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -155,6 +155,9 @@ int PS4_SYSV_ABI scePadGetFeatureReport() { } int PS4_SYSV_ABI scePadGetHandle(s32 userId, s32 type, s32 index) { + if (userId == -1) { + return ORBIS_PAD_ERROR_DEVICE_NO_HANDLE; + } LOG_DEBUG(Lib_Pad, "(DUMMY) called"); return 1; } @@ -246,6 +249,9 @@ int PS4_SYSV_ABI scePadMbusTerm() { int PS4_SYSV_ABI scePadOpen(s32 userId, s32 type, s32 index, const OrbisPadOpenParam* pParam) { LOG_INFO(Lib_Pad, "(DUMMY) called user_id = {} type = {} index = {}", userId, type, index); + if (userId == -1) { + return ORBIS_PAD_ERROR_DEVICE_NO_HANDLE; + } if (Config::getUseSpecialPad()) { if (type != ORBIS_PAD_PORT_TYPE_SPECIAL) return ORBIS_PAD_ERROR_DEVICE_NOT_CONNECTED; @@ -346,6 +352,9 @@ int PS4_SYSV_ABI scePadReadHistory() { } int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) { + if (handle == ORBIS_PAD_ERROR_DEVICE_NO_HANDLE) { + return ORBIS_PAD_ERROR_INVALID_HANDLE; + } auto* controller = Common::Singleton::Instance(); int connectedCount = 0; bool isConnected = false; From 3f1061de5613c0c4a74d6394a6493491280bc03f Mon Sep 17 00:00:00 2001 From: rainmakerv2 <30595646+rainmakerv3@users.noreply.github.com> Date: Fri, 13 Dec 2024 04:46:31 +0800 Subject: [PATCH 167/549] Resubmit - Prevent settings from being saved when close button is pressed instead of save (#1747) * Do not save settings when close button pressed instead of save * Update src/common/config.h Co-authored-by: TheTurtle * Revert "Update src/common/config.h" This reverts commit 125303ea8674b25e93a4c4cf7b93a0357eac19f4. --------- Co-authored-by: rainmakerv2 <30595646+jpau02@users.noreply.github.com> Co-authored-by: TheTurtle --- src/common/config.cpp | 52 ++++-- src/common/config.h | 4 +- src/emulator.cpp | 2 +- src/qt_gui/main_window.cpp | 6 +- src/qt_gui/settings_dialog.cpp | 291 ++++++++++++++++----------------- src/qt_gui/settings_dialog.h | 2 + 6 files changed, 194 insertions(+), 163 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index 3db98a438..4d07ba29f 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -422,6 +422,10 @@ void setEmulatorLanguage(std::string language) { emulator_language = language; } +void setGameInstallDirs(const std::vector& settings_install_dirs_config) { + settings_install_dirs = settings_install_dirs_config; +} + u32 getMainWindowGeometryX() { return main_window_geometry_x; } @@ -673,14 +677,6 @@ void save(const std::filesystem::path& path) { data["Vulkan"]["crashDiagnostic"] = vkCrashDiagnostic; data["Debug"]["DebugDump"] = isDebugDump; data["Debug"]["CollectShader"] = isShaderDebug; - data["GUI"]["theme"] = mw_themes; - data["GUI"]["iconSize"] = m_icon_size; - data["GUI"]["sliderPos"] = m_slider_pos; - data["GUI"]["iconSizeGrid"] = m_icon_size_grid; - data["GUI"]["sliderPosGrid"] = m_slider_pos_grid; - data["GUI"]["gameTableMode"] = m_table_mode; - data["GUI"]["mw_width"] = m_window_size_W; - data["GUI"]["mw_height"] = m_window_size_H; std::vector install_dirs; for (const auto& dirString : settings_install_dirs) { @@ -690,6 +686,43 @@ void save(const std::filesystem::path& path) { data["GUI"]["addonInstallDir"] = std::string{fmt::UTF(settings_addon_install_dir.u8string()).data}; + data["GUI"]["emulatorLanguage"] = emulator_language; + data["Settings"]["consoleLanguage"] = m_language; + + std::ofstream file(path, std::ios::binary); + file << data; + file.close(); +} + +void saveMainWindow(const std::filesystem::path& path) { + toml::value data; + + std::error_code error; + if (std::filesystem::exists(path, error)) { + try { + std::ifstream ifs; + ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); + ifs.open(path, std::ios_base::binary); + data = toml::parse(ifs, std::string{fmt::UTF(path.filename().u8string()).data}); + } catch (const std::exception& ex) { + fmt::print("Exception trying to parse config file. Exception: {}\n", ex.what()); + return; + } + } else { + if (error) { + fmt::print("Filesystem error: {}\n", error.message()); + } + fmt::print("Saving new configuration file {}\n", fmt::UTF(path.u8string())); + } + + data["GUI"]["mw_width"] = m_window_size_W; + data["GUI"]["mw_height"] = m_window_size_H; + data["GUI"]["theme"] = mw_themes; + data["GUI"]["iconSize"] = m_icon_size; + data["GUI"]["sliderPos"] = m_slider_pos; + data["GUI"]["iconSizeGrid"] = m_icon_size_grid; + data["GUI"]["sliderPosGrid"] = m_slider_pos_grid; + data["GUI"]["gameTableMode"] = m_table_mode; data["GUI"]["geometry_x"] = main_window_geometry_x; data["GUI"]["geometry_y"] = main_window_geometry_y; data["GUI"]["geometry_w"] = main_window_geometry_w; @@ -697,9 +730,6 @@ void save(const std::filesystem::path& path) { data["GUI"]["pkgDirs"] = m_pkg_viewer; data["GUI"]["elfDirs"] = m_elf_viewer; data["GUI"]["recentFiles"] = m_recent_files; - data["GUI"]["emulatorLanguage"] = emulator_language; - - data["Settings"]["consoleLanguage"] = m_language; std::ofstream file(path, std::ios::binary); file << data; diff --git a/src/common/config.h b/src/common/config.h index d98c94480..ff3b3703f 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -13,6 +13,7 @@ enum HideCursorState : s16 { Never, Idle, Always }; void load(const std::filesystem::path& path); void save(const std::filesystem::path& path); +void saveMainWindow(const std::filesystem::path& path); bool isNeoMode(); bool isFullscreenMode(); @@ -67,6 +68,7 @@ void setNeoMode(bool enable); void setUserName(const std::string& type); void setUpdateChannel(const std::string& type); void setSeparateUpdateEnabled(bool use); +void setGameInstallDirs(const std::vector& settings_install_dirs_config); void setCursorState(s16 cursorState); void setCursorHideTimeout(int newcursorHideTimeout); @@ -128,4 +130,4 @@ void setDefaultValues(); // settings u32 GetLanguage(); -}; // namespace Config +}; // namespace Config \ No newline at end of file diff --git a/src/emulator.cpp b/src/emulator.cpp index 60d6e18d7..eeac5973a 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -100,7 +100,7 @@ Emulator::Emulator() { Emulator::~Emulator() { const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); - Config::save(config_dir / "config.toml"); + Config::saveMainWindow(config_dir / "config.toml"); } void Emulator::Run(const std::filesystem::path& file) { diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 4c40084d3..3eb629c0b 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -35,7 +35,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWi MainWindow::~MainWindow() { SaveWindowState(); const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); - Config::save(config_dir / "config.toml"); + Config::saveMainWindow(config_dir / "config.toml"); } bool MainWindow::Init() { @@ -1006,7 +1006,7 @@ void MainWindow::AddRecentFiles(QString filePath) { } Config::setRecentFiles(vec); const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); - Config::save(config_dir / "config.toml"); + Config::saveMainWindow(config_dir / "config.toml"); CreateRecentGameActions(); // Refresh the QActions. } @@ -1077,4 +1077,4 @@ bool MainWindow::eventFilter(QObject* obj, QEvent* event) { } } return QMainWindow::eventFilter(obj, event); -} +} \ No newline at end of file diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index 1fd4b6e8b..e67c14ccb 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -12,12 +12,13 @@ #ifdef ENABLE_UPDATER #include "check_update.h" #endif +#include #include "common/logging/backend.h" #include "common/logging/filter.h" +#include "common/logging/formatter.h" #include "main_window.h" #include "settings_dialog.h" #include "ui_settings_dialog.h" - QStringList languageNames = {"Arabic", "Czech", "Danish", @@ -94,13 +95,18 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge connect(ui->buttonBox, &QDialogButtonBox::clicked, this, [this, config_dir](QAbstractButton* button) { if (button == ui->buttonBox->button(QDialogButtonBox::Save)) { + UpdateSettings(); Config::save(config_dir / "config.toml"); QWidget::close(); } else if (button == ui->buttonBox->button(QDialogButtonBox::Apply)) { + UpdateSettings(); Config::save(config_dir / "config.toml"); } else if (button == ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)) { Config::setDefaultValues(); + Config::save(config_dir / "config.toml"); LoadValuesFromConfig(); + } else if (button == ui->buttonBox->button(QDialogButtonBox::Close)) { + ResetInstallFolders(); } if (Common::Log::IsActive()) { Common::Log::Filter filter; @@ -119,35 +125,6 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge // GENERAL TAB { - connect(ui->userNameLineEdit, &QLineEdit::textChanged, this, - [](const QString& text) { Config::setUserName(text.toStdString()); }); - - connect(ui->consoleLanguageComboBox, QOverload::of(&QComboBox::currentIndexChanged), - this, [](int index) { - if (index >= 0 && index < languageIndexes.size()) { - int languageCode = languageIndexes[index]; - Config::setLanguage(languageCode); - } - }); - - connect(ui->fullscreenCheckBox, &QCheckBox::stateChanged, this, - [](int val) { Config::setFullscreenMode(val); }); - - connect(ui->separateUpdatesCheckBox, &QCheckBox::stateChanged, this, - [](int val) { Config::setSeparateUpdateEnabled(val); }); - - connect(ui->showSplashCheckBox, &QCheckBox::stateChanged, this, - [](int val) { Config::setShowSplash(val); }); - - connect(ui->ps4proCheckBox, &QCheckBox::stateChanged, this, - [](int val) { Config::setNeoMode(val); }); - - connect(ui->logTypeComboBox, &QComboBox::currentTextChanged, this, - [](const QString& text) { Config::setLogType(text.toStdString()); }); - - connect(ui->logFilterLineEdit, &QLineEdit::textChanged, this, - [](const QString& text) { Config::setLogFilter(text.toStdString()); }); - #ifdef ENABLE_UPDATER connect(ui->updateCheckBox, &QCheckBox::stateChanged, this, [](int state) { Config::setAutoUpdate(state == Qt::Checked); }); @@ -163,74 +140,12 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge ui->updaterGroupBox->setVisible(false); ui->GUIgroupBox->setMaximumSize(265, 16777215); #endif - - connect(ui->playBGMCheckBox, &QCheckBox::stateChanged, this, [](int val) { - Config::setPlayBGM(val); - if (val == Qt::Unchecked) { - BackgroundMusicPlayer::getInstance().stopMusic(); - } - }); - - connect(ui->BGMVolumeSlider, &QSlider::valueChanged, this, [](float val) { - Config::setBGMvolume(val); - BackgroundMusicPlayer::getInstance().setVolume(val); - }); - -#ifdef ENABLE_DISCORD_RPC - connect(ui->discordRPCCheckbox, &QCheckBox::stateChanged, this, [](int val) { - Config::setEnableDiscordRPC(val); - auto* rpc = Common::Singleton::Instance(); - if (val == Qt::Checked) { - rpc->init(); - rpc->setStatusIdling(); - } else { - rpc->shutdown(); - } - }); -#endif } // Input TAB { connect(ui->hideCursorComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, - [this](s16 index) { - Config::setCursorState(index); - OnCursorStateChanged(index); - }); - - connect(ui->idleTimeoutSpinBox, &QSpinBox::valueChanged, this, - [](int index) { Config::setCursorHideTimeout(index); }); - - connect(ui->backButtonBehaviorComboBox, QOverload::of(&QComboBox::currentIndexChanged), - this, [this](int index) { - if (index >= 0 && index < ui->backButtonBehaviorComboBox->count()) { - QString data = ui->backButtonBehaviorComboBox->itemData(index).toString(); - Config::setBackButtonBehavior(data.toStdString()); - } - }); - } - - // GPU TAB - { - // First options is auto selection -1, so gpuId on the GUI will always have to subtract 1 - // when setting and add 1 when getting to select the correct gpu in Qt - connect(ui->graphicsAdapterBox, &QComboBox::currentIndexChanged, this, - [](int index) { Config::setGpuId(index - 1); }); - - connect(ui->widthSpinBox, &QSpinBox::valueChanged, this, - [](int val) { Config::setScreenWidth(val); }); - - connect(ui->heightSpinBox, &QSpinBox::valueChanged, this, - [](int val) { Config::setScreenHeight(val); }); - - connect(ui->vblankSpinBox, &QSpinBox::valueChanged, this, - [](int val) { Config::setVblankDiv(val); }); - - connect(ui->dumpShadersCheckBox, &QCheckBox::stateChanged, this, - [](int val) { Config::setDumpShaders(val); }); - - connect(ui->nullGpuCheckBox, &QCheckBox::stateChanged, this, - [](int val) { Config::setNullGpu(val); }); + [this](s16 index) { OnCursorStateChanged(index); }); } // PATH TAB @@ -262,21 +177,6 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge }); } - // DEBUG TAB - { - connect(ui->debugDump, &QCheckBox::stateChanged, this, - [](int val) { Config::setDebugDump(val); }); - - connect(ui->vkValidationCheckBox, &QCheckBox::stateChanged, this, - [](int val) { Config::setVkValidation(val); }); - - connect(ui->vkSyncValidationCheckBox, &QCheckBox::stateChanged, this, - [](int val) { Config::setVkSyncValidation(val); }); - - connect(ui->rdocCheckBox, &QCheckBox::stateChanged, this, - [](int val) { Config::setRdocEnabled(val); }); - } - // Descriptions { // General @@ -323,40 +223,69 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge } void SettingsDialog::LoadValuesFromConfig() { - ui->consoleLanguageComboBox->setCurrentIndex( - std::distance( - languageIndexes.begin(), - std::find(languageIndexes.begin(), languageIndexes.end(), Config::GetLanguage())) % - languageIndexes.size()); - ui->emulatorLanguageComboBox->setCurrentIndex(languages[Config::getEmulatorLanguage()]); - ui->hideCursorComboBox->setCurrentIndex(Config::getCursorState()); - OnCursorStateChanged(Config::getCursorState()); - ui->idleTimeoutSpinBox->setValue(Config::getCursorHideTimeout()); - ui->graphicsAdapterBox->setCurrentIndex(Config::getGpuId() + 1); - ui->widthSpinBox->setValue(Config::getScreenWidth()); - ui->heightSpinBox->setValue(Config::getScreenHeight()); - ui->vblankSpinBox->setValue(Config::vblankDiv()); - ui->dumpShadersCheckBox->setChecked(Config::dumpShaders()); - ui->nullGpuCheckBox->setChecked(Config::nullGpu()); - ui->playBGMCheckBox->setChecked(Config::getPlayBGM()); - ui->BGMVolumeSlider->setValue((Config::getBGMvolume())); - ui->discordRPCCheckbox->setChecked(Config::getEnableDiscordRPC()); - ui->fullscreenCheckBox->setChecked(Config::isFullscreenMode()); - ui->separateUpdatesCheckBox->setChecked(Config::getSeparateUpdateEnabled()); - ui->showSplashCheckBox->setChecked(Config::showSplash()); - ui->ps4proCheckBox->setChecked(Config::isNeoMode()); - ui->logTypeComboBox->setCurrentText(QString::fromStdString(Config::getLogType())); - ui->logFilterLineEdit->setText(QString::fromStdString(Config::getLogFilter())); - ui->userNameLineEdit->setText(QString::fromStdString(Config::getUserName())); - ui->debugDump->setChecked(Config::debugDump()); - ui->vkValidationCheckBox->setChecked(Config::vkValidationEnabled()); - ui->vkSyncValidationCheckBox->setChecked(Config::vkValidationSyncEnabled()); - ui->rdocCheckBox->setChecked(Config::isRdocEnabled()); + std::filesystem::path userdir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); + std::error_code error; + if (!std::filesystem::exists(userdir / "Config.toml", error)) { + Config::load(userdir / "Config.toml"); + return; + } + + try { + std::ifstream ifs; + ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); + const toml::value data = toml::parse(userdir / "Config.toml"); + } catch (std::exception& ex) { + fmt::print("Got exception trying to load config file. Exception: {}\n", ex.what()); + return; + } + + const toml::value data = toml::parse(userdir / "Config.toml"); + const QVector languageIndexes = {21, 23, 14, 6, 18, 1, 12, 22, 2, 4, 25, 24, 29, 5, 0, 9, + 15, 16, 17, 7, 26, 8, 11, 20, 3, 13, 27, 10, 19, 30, 28}; + + ui->consoleLanguageComboBox->setCurrentIndex( + std::distance(languageIndexes.begin(), + std::find(languageIndexes.begin(), languageIndexes.end(), + toml::find_or(data, "Settings", "consoleLanguage", 6))) % + languageIndexes.size()); + ui->emulatorLanguageComboBox->setCurrentIndex( + languages[toml::find_or(data, "GUI", "emulatorLanguage", "en")]); + ui->hideCursorComboBox->setCurrentIndex(toml::find_or(data, "Input", "cursorState", 1)); + OnCursorStateChanged(toml::find_or(data, "Input", "cursorState", 1)); + ui->idleTimeoutSpinBox->setValue(toml::find_or(data, "Input", "cursorHideTimeout", 5)); + // First options is auto selection -1, so gpuId on the GUI will always have to subtract 1 + // when setting and add 1 when getting to select the correct gpu in Qt + ui->graphicsAdapterBox->setCurrentIndex(toml::find_or(data, "Vulkan", "gpuId", -1) + 1); + ui->widthSpinBox->setValue(toml::find_or(data, "GPU", "screenWidth", 1280)); + ui->heightSpinBox->setValue(toml::find_or(data, "GPU", "screenHeight", 720)); + ui->vblankSpinBox->setValue(toml::find_or(data, "GPU", "vblankDivider", 1)); + ui->dumpShadersCheckBox->setChecked(toml::find_or(data, "GPU", "dumpShaders", false)); + ui->nullGpuCheckBox->setChecked(toml::find_or(data, "GPU", "nullGpu", false)); + ui->playBGMCheckBox->setChecked(toml::find_or(data, "General", "playBGM", false)); + ui->BGMVolumeSlider->setValue(toml::find_or(data, "General", "BGMvolume", 50)); + ui->discordRPCCheckbox->setChecked( + toml::find_or(data, "General", "enableDiscordRPC", true)); + ui->fullscreenCheckBox->setChecked(toml::find_or(data, "General", "Fullscreen", false)); + ui->separateUpdatesCheckBox->setChecked( + toml::find_or(data, "General", "separateUpdateEnabled", false)); + ui->showSplashCheckBox->setChecked(toml::find_or(data, "General", "showSplash", false)); + ui->ps4proCheckBox->setChecked(toml::find_or(data, "General", "isPS4Pro", false)); + ui->logTypeComboBox->setCurrentText( + QString::fromStdString(toml::find_or(data, "General", "logType", "async"))); + ui->logFilterLineEdit->setText( + QString::fromStdString(toml::find_or(data, "General", "logFilter", ""))); + ui->userNameLineEdit->setText( + QString::fromStdString(toml::find_or(data, "General", "userName", "shadPS4"))); + ui->debugDump->setChecked(toml::find_or(data, "Debug", "DebugDump", false)); + ui->vkValidationCheckBox->setChecked(toml::find_or(data, "Vulkan", "validation", false)); + ui->vkSyncValidationCheckBox->setChecked( + toml::find_or(data, "Vulkan", "validation_sync", false)); + ui->rdocCheckBox->setChecked(toml::find_or(data, "Vulkan", "rdocEnable", false)); #ifdef ENABLE_UPDATER - ui->updateCheckBox->setChecked(Config::autoUpdate()); - std::string updateChannel = Config::getUpdateChannel(); + ui->updateCheckBox->setChecked(toml::find_or(data, "General", "autoUpdate", false)); + std::string updateChannel = toml::find_or(data, "General", "updateChannel", ""); if (updateChannel != "Release" && updateChannel != "Nightly") { if (Common::isRelease) { updateChannel = "Release"; @@ -367,18 +296,13 @@ void SettingsDialog::LoadValuesFromConfig() { ui->updateComboBox->setCurrentText(QString::fromStdString(updateChannel)); #endif - for (const auto& dir : Config::getGameInstallDirs()) { - QString path_string; - Common::FS::PathToQString(path_string, dir); - QListWidgetItem* item = new QListWidgetItem(path_string); - ui->gameFoldersListWidget->addItem(item); - } - - QString backButtonBehavior = QString::fromStdString(Config::getBackButtonBehavior()); + QString backButtonBehavior = QString::fromStdString( + toml::find_or(data, "Input", "backButtonBehavior", "left")); int index = ui->backButtonBehaviorComboBox->findData(backButtonBehavior); ui->backButtonBehaviorComboBox->setCurrentIndex(index != -1 ? index : 0); ui->removeFolderButton->setEnabled(!ui->gameFoldersListWidget->selectedItems().isEmpty()); + ResetInstallFolders(); } void SettingsDialog::InitializeEmulatorLanguages() { @@ -554,3 +478,76 @@ bool SettingsDialog::eventFilter(QObject* obj, QEvent* event) { } return QDialog::eventFilter(obj, event); } + +void SettingsDialog::UpdateSettings() { + + const QVector TouchPadIndex = {"left", "center", "right", "none"}; + Config::setBackButtonBehavior(TouchPadIndex[ui->backButtonBehaviorComboBox->currentIndex()]); + Config::setNeoMode(ui->ps4proCheckBox->isChecked()); + Config::setFullscreenMode(ui->fullscreenCheckBox->isChecked()); + Config::setPlayBGM(ui->playBGMCheckBox->isChecked()); + Config::setNeoMode(ui->ps4proCheckBox->isChecked()); + Config::setLogType(ui->logTypeComboBox->currentText().toStdString()); + Config::setLogFilter(ui->logFilterLineEdit->text().toStdString()); + Config::setUserName(ui->userNameLineEdit->text().toStdString()); + Config::setCursorState(ui->hideCursorComboBox->currentIndex()); + Config::setCursorHideTimeout(ui->idleTimeoutSpinBox->value()); + Config::setGpuId(ui->graphicsAdapterBox->currentIndex() - 1); + Config::setBGMvolume(ui->BGMVolumeSlider->value()); + Config::setLanguage(languageIndexes[ui->consoleLanguageComboBox->currentIndex()]); + Config::setEnableDiscordRPC(ui->discordRPCCheckbox->isChecked()); + Config::setScreenWidth(ui->widthSpinBox->value()); + Config::setScreenHeight(ui->heightSpinBox->value()); + Config::setVblankDiv(ui->vblankSpinBox->value()); + Config::setDumpShaders(ui->dumpShadersCheckBox->isChecked()); + Config::setNullGpu(ui->nullGpuCheckBox->isChecked()); + Config::setSeparateUpdateEnabled(ui->separateUpdatesCheckBox->isChecked()); + Config::setShowSplash(ui->showSplashCheckBox->isChecked()); + Config::setDebugDump(ui->debugDump->isChecked()); + Config::setVkValidation(ui->vkValidationCheckBox->isChecked()); + Config::setVkSyncValidation(ui->vkSyncValidationCheckBox->isChecked()); + Config::setRdocEnabled(ui->rdocCheckBox->isChecked()); + Config::setAutoUpdate(ui->updateCheckBox->isChecked()); + Config::setUpdateChannel(ui->updateComboBox->currentText().toStdString()); + +#ifdef ENABLE_DISCORD_RPC + auto* rpc = Common::Singleton::Instance(); + if (Config::getEnableDiscordRPC()) { + rpc->init(); + rpc->setStatusIdling(); + } else { + rpc->shutdown(); + } +#endif + + BackgroundMusicPlayer::getInstance().setVolume(ui->BGMVolumeSlider->value()); + ResetInstallFolders(); +} + +void SettingsDialog::ResetInstallFolders() { + + std::filesystem::path userdir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); + const toml::value data = toml::parse(userdir / "Config.toml"); + + if (data.contains("GUI")) { + const toml::value& gui = data.at("GUI"); + const auto install_dir_array = + toml::find_or>(gui, "installDirs", {}); + std::vector settings_install_dirs_config = {}; + + for (const auto& dir : install_dir_array) { + if (std::find(settings_install_dirs_config.begin(), settings_install_dirs_config.end(), + dir) == settings_install_dirs_config.end()) { + settings_install_dirs_config.push_back(dir); + } + } + + for (const auto& dir : settings_install_dirs_config) { + QString path_string; + Common::FS::PathToQString(path_string, dir); + QListWidgetItem* item = new QListWidgetItem(path_string); + ui->gameFoldersListWidget->addItem(item); + } + Config::setGameInstallDirs(settings_install_dirs_config); + } +} \ No newline at end of file diff --git a/src/qt_gui/settings_dialog.h b/src/qt_gui/settings_dialog.h index 8cdded980..987b35d45 100644 --- a/src/qt_gui/settings_dialog.h +++ b/src/qt_gui/settings_dialog.h @@ -31,6 +31,8 @@ signals: private: void LoadValuesFromConfig(); + void UpdateSettings(); + void ResetInstallFolders(); void InitializeEmulatorLanguages(); void OnLanguageChanged(int index); void OnCursorStateChanged(s16 index); From 5be807fc8ac6bada55c37428a51cee081bf64498 Mon Sep 17 00:00:00 2001 From: TheTurtle Date: Fri, 13 Dec 2024 00:31:49 +0200 Subject: [PATCH 168/549] hot-fix: Fix order of operands --- src/shader_recompiler/backend/spirv/emit_spirv_image.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index fe2660705..736410dcd 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp @@ -130,8 +130,8 @@ Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, u32 handle, const Id sampler = ctx.OpLoad(ctx.sampler_type, ctx.samplers[handle >> 16]); const Id sampled_image = ctx.OpSampledImage(texture.sampled_type, image, sampler); ImageOperands operands; - operands.AddOffset(ctx, offset); operands.Add(spv::ImageOperandsMask::Lod, lod); + operands.AddOffset(ctx, offset); const Id sample = ctx.OpImageSampleDrefExplicitLod(result_type, sampled_image, coords, dref, operands.mask, operands.operands); const Id sample_typed = texture.is_integer ? ctx.OpBitcast(ctx.F32[1], sample) : sample; From 91d57e830be312e5296fa7bfc265dfe87f13582c Mon Sep 17 00:00:00 2001 From: rainmakerv2 <30595646+rainmakerv3@users.noreply.github.com> Date: Fri, 13 Dec 2024 14:27:36 +0800 Subject: [PATCH 169/549] Fix lowercase filenames fox Linux (#1760) Fix uppercase config filenames Co-authored-by: rainmakerv2 <30595646+jpau02@users.noreply.github.com> --- src/qt_gui/settings_dialog.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index e67c14ccb..f74f86435 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -226,21 +226,21 @@ void SettingsDialog::LoadValuesFromConfig() { std::filesystem::path userdir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); std::error_code error; - if (!std::filesystem::exists(userdir / "Config.toml", error)) { - Config::load(userdir / "Config.toml"); + if (!std::filesystem::exists(userdir / "config.toml", error)) { + Config::load(userdir / "config.toml"); return; } try { std::ifstream ifs; ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); - const toml::value data = toml::parse(userdir / "Config.toml"); + const toml::value data = toml::parse(userdir / "config.toml"); } catch (std::exception& ex) { fmt::print("Got exception trying to load config file. Exception: {}\n", ex.what()); return; } - const toml::value data = toml::parse(userdir / "Config.toml"); + const toml::value data = toml::parse(userdir / "config.toml"); const QVector languageIndexes = {21, 23, 14, 6, 18, 1, 12, 22, 2, 4, 25, 24, 29, 5, 0, 9, 15, 16, 17, 7, 26, 8, 11, 20, 3, 13, 27, 10, 19, 30, 28}; @@ -527,7 +527,7 @@ void SettingsDialog::UpdateSettings() { void SettingsDialog::ResetInstallFolders() { std::filesystem::path userdir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); - const toml::value data = toml::parse(userdir / "Config.toml"); + const toml::value data = toml::parse(userdir / "config.toml"); if (data.contains("GUI")) { const toml::value& gui = data.at("GUI"); From f587931ed387efbf83e8e947bf9859885bbac297 Mon Sep 17 00:00:00 2001 From: rainmakerv2 <30595646+rainmakerv3@users.noreply.github.com> Date: Fri, 13 Dec 2024 15:52:54 +0800 Subject: [PATCH 170/549] Fix for adding game folders (#1761) Co-authored-by: rainmakerv2 <30595646+jpau02@users.noreply.github.com> --- src/qt_gui/settings_dialog.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index f74f86435..09d3674f7 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -521,7 +521,6 @@ void SettingsDialog::UpdateSettings() { #endif BackgroundMusicPlayer::getInstance().setVolume(ui->BGMVolumeSlider->value()); - ResetInstallFolders(); } void SettingsDialog::ResetInstallFolders() { From bab00dbca8be8c0f3fb8433dea1b7bad24012a71 Mon Sep 17 00:00:00 2001 From: TheTurtle Date: Fri, 13 Dec 2024 18:23:01 +0200 Subject: [PATCH 171/549] kernel: Fix module finding Patch by Elbread --- src/core/libraries/kernel/process.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/libraries/kernel/process.cpp b/src/core/libraries/kernel/process.cpp index 6c29d9305..ba7964bc4 100644 --- a/src/core/libraries/kernel/process.cpp +++ b/src/core/libraries/kernel/process.cpp @@ -50,6 +50,9 @@ s32 PS4_SYSV_ABI sceKernelLoadStartModule(const char* moduleFileName, size_t arg return handle; } handle = linker->LoadModule(path, true); + if (handle == -1) { + return ORBIS_KERNEL_ERROR_EINVAL; + } auto* module = linker->GetModule(handle); linker->RelocateAnyImports(module); From 8acefd25e77d527eb4a250572ed91161c342e144 Mon Sep 17 00:00:00 2001 From: TheTurtle Date: Fri, 13 Dec 2024 18:26:16 +0200 Subject: [PATCH 172/549] hot-fix the hot-fix --- src/core/libraries/kernel/process.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/libraries/kernel/process.cpp b/src/core/libraries/kernel/process.cpp index ba7964bc4..97cc01ebc 100644 --- a/src/core/libraries/kernel/process.cpp +++ b/src/core/libraries/kernel/process.cpp @@ -51,7 +51,7 @@ s32 PS4_SYSV_ABI sceKernelLoadStartModule(const char* moduleFileName, size_t arg } handle = linker->LoadModule(path, true); if (handle == -1) { - return ORBIS_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_ESRCH; } auto* module = linker->GetModule(handle); linker->RelocateAnyImports(module); From cfbd8691261e4d0b06a6ed29ec68c333ef6cc8d1 Mon Sep 17 00:00:00 2001 From: TheTurtle Date: Fri, 13 Dec 2024 18:28:19 +0200 Subject: [PATCH 173/549] texture_cache: Improve support for stencil reads (#1758) * texture_cache: Improve support for stencil reads * libraries: Supress some spammy logs * core: Support loading font libraries * texture_cache: Remove assert --- src/core/libraries/audio3d/audio3d.cpp | 2 +- src/core/libraries/gnmdriver/gnmdriver.cpp | 2 +- .../libraries/libc_internal/libc_internal.cpp | 12 +++++++++++ src/emulator.cpp | 7 +++++-- src/video_core/amdgpu/liverpool.h | 4 ++++ .../renderer_vulkan/vk_rasterizer.cpp | 18 ++++++++++------ src/video_core/texture_cache/image.cpp | 4 +++- src/video_core/texture_cache/image.h | 5 +++++ src/video_core/texture_cache/image_info.cpp | 3 +++ src/video_core/texture_cache/image_info.h | 2 +- src/video_core/texture_cache/image_view.cpp | 2 +- .../texture_cache/texture_cache.cpp | 21 +++++++++++++++++++ 12 files changed, 69 insertions(+), 13 deletions(-) diff --git a/src/core/libraries/audio3d/audio3d.cpp b/src/core/libraries/audio3d/audio3d.cpp index 44670d87b..d896524c6 100644 --- a/src/core/libraries/audio3d/audio3d.cpp +++ b/src/core/libraries/audio3d/audio3d.cpp @@ -80,7 +80,7 @@ int PS4_SYSV_ABI sceAudio3dPortGetAttributesSupported(OrbisAudio3dPortId uiPortI int PS4_SYSV_ABI sceAudio3dPortGetQueueLevel(OrbisAudio3dPortId uiPortId, u32* pQueueLevel, u32* pQueueAvailable) { - LOG_INFO(Lib_Audio3d, "uiPortId = {}", uiPortId); + LOG_TRACE(Lib_Audio3d, "uiPortId = {}", uiPortId); return ORBIS_OK; } diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index 18035e6ce..dbf085fb3 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -971,7 +971,7 @@ s32 PS4_SYSV_ABI sceGnmFindResourcesPublic() { } void PS4_SYSV_ABI sceGnmFlushGarlic() { - LOG_WARNING(Lib_GnmDriver, "(STUBBED) called"); + LOG_TRACE(Lib_GnmDriver, "(STUBBED) called"); } int PS4_SYSV_ABI sceGnmGetCoredumpAddress() { diff --git a/src/core/libraries/libc_internal/libc_internal.cpp b/src/core/libraries/libc_internal/libc_internal.cpp index eb6046c7a..8453a78b9 100644 --- a/src/core/libraries/libc_internal/libc_internal.cpp +++ b/src/core/libraries/libc_internal/libc_internal.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include "common/assert.h" #include "common/logging/log.h" @@ -65,6 +66,15 @@ char* PS4_SYSV_ABI internal_strncpy(char* dest, const char* src, std::size_t cou return std::strncpy(dest, src, count); } +int PS4_SYSV_ABI internal_strncpy_s(char* dest, size_t destsz, const char* src, size_t count) { +#ifdef _WIN64 + return strncpy_s(dest, destsz, src, count); +#else + std::strcpy(dest, src); + return 0; +#endif +} + char* PS4_SYSV_ABI internal_strcat(char* dest, const char* src) { return std::strcat(dest, src); } @@ -237,6 +247,8 @@ void RegisterlibSceLibcInternal(Core::Loader::SymbolsResolver* sym) { internal_strlen); LIB_FUNCTION("6sJWiWSRuqk", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_strncpy); + LIB_FUNCTION("YNzNkJzYqEg", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_strncpy_s); LIB_FUNCTION("Ls4tzzhimqQ", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_strcat); LIB_FUNCTION("ob5xAW4ln-0", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, diff --git a/src/emulator.cpp b/src/emulator.cpp index eeac5973a..c517bc284 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -266,7 +266,7 @@ void Emulator::Run(const std::filesystem::path& file) { } void Emulator::LoadSystemModules(const std::filesystem::path& file, std::string game_serial) { - constexpr std::array ModulesToLoad{ + constexpr std::array ModulesToLoad{ {{"libSceNgs2.sprx", &Libraries::Ngs2::RegisterlibSceNgs2}, {"libSceFiber.sprx", &Libraries::Fiber::RegisterlibSceFiber}, {"libSceUlt.sprx", nullptr}, @@ -276,7 +276,10 @@ void Emulator::LoadSystemModules(const std::filesystem::path& file, std::string {"libSceDiscMap.sprx", &Libraries::DiscMap::RegisterlibSceDiscMap}, {"libSceRtc.sprx", &Libraries::Rtc::RegisterlibSceRtc}, {"libSceJpegEnc.sprx", &Libraries::JpegEnc::RegisterlibSceJpegEnc}, - {"libSceCesCs.sprx", nullptr}}}; + {"libSceCesCs.sprx", nullptr}, + {"libSceFont.sprx", nullptr}, + {"libSceFontFt.sprx", nullptr}, + {"libSceFreeTypeOt.sprx", nullptr}}}; std::vector found_modules; const auto& sys_module_path = Common::FS::GetUserPath(Common::FS::PathType::SysModuleDir); diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index ca3b01612..9bc3454d8 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -431,6 +431,10 @@ struct Liverpool { return u64(z_read_base) << 8; } + u64 StencilAddress() const { + return u64(stencil_read_base) << 8; + } + u32 NumSamples() const { return 1u << z_info.num_samples; // spec doesn't say it is a log2 } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 9abf1b527..eb2ef3600 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -616,18 +616,24 @@ void Rasterizer::BindTextures(const Shader::Info& stage, Shader::Backend::Bindin auto& [image_id, desc] = image_bindings.emplace_back(std::piecewise_construct, std::tuple{}, std::tuple{tsharp, image_desc}); image_id = texture_cache.FindImage(desc); - auto& image = texture_cache.GetImage(image_id); - if (image.binding.is_bound) { + auto* image = &texture_cache.GetImage(image_id); + if (image->depth_id) { + // If this image has an associated depth image, it's a stencil attachment. + // Redirect the access to the actual depth-stencil buffer. + image_id = image->depth_id; + image = &texture_cache.GetImage(image_id); + } + if (image->binding.is_bound) { // The image is already bound. In case if it is about to be used as storage we need // to force general layout on it. - image.binding.force_general |= image_desc.is_storage; + image->binding.force_general |= image_desc.is_storage; } - if (image.binding.is_target) { + if (image->binding.is_target) { // The image is already bound as target. Since we read and output to it need to force // general layout too. - image.binding.force_general = 1u; + image->binding.force_general = 1u; } - image.binding.is_bound = 1u; + image->binding.is_bound = 1u; } // Second pass to re-bind images that were updated after binding diff --git a/src/video_core/texture_cache/image.cpp b/src/video_core/texture_cache/image.cpp index e7e1ce1da..03339d280 100644 --- a/src/video_core/texture_cache/image.cpp +++ b/src/video_core/texture_cache/image.cpp @@ -145,8 +145,10 @@ Image::Image(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_, const ImageInfo& info_) : instance{&instance_}, scheduler{&scheduler_}, info{info_}, image{instance->GetDevice(), instance->GetAllocator()} { + if (info.pixel_format == vk::Format::eUndefined) { + return; + } mip_hashes.resize(info.resources.levels); - ASSERT(info.pixel_format != vk::Format::eUndefined); // Here we force `eExtendedUsage` as don't know all image usage cases beforehand. In normal case // the texture cache should re-create the resource with the usage requested vk::ImageCreateFlags flags{vk::ImageCreateFlagBits::eMutableFormat | diff --git a/src/video_core/texture_cache/image.h b/src/video_core/texture_cache/image.h index a1b1b007f..473dd731e 100644 --- a/src/video_core/texture_cache/image.h +++ b/src/video_core/texture_cache/image.h @@ -92,6 +92,10 @@ struct Image { return image_view_ids[std::distance(image_view_infos.begin(), it)]; } + void AssociateDepth(ImageId image_id) { + depth_id = image_id; + } + boost::container::small_vector GetBarriers( vk::ImageLayout dst_layout, vk::Flags dst_mask, vk::PipelineStageFlags2 dst_stage, std::optional subres_range); @@ -116,6 +120,7 @@ struct Image { VAddr track_addr_end = 0; std::vector image_view_infos; std::vector image_view_ids; + ImageId depth_id{}; // Resource state tracking struct { diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index 0ed36ee39..1445d41cd 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -298,6 +298,9 @@ ImageInfo::ImageInfo(const AmdGpu::Liverpool::DepthBuffer& buffer, u32 num_slice resources.layers = num_slices; meta_info.htile_addr = buffer.z_info.tile_surface_en ? htile_address : 0; + stencil_addr = buffer.StencilAddress(); + stencil_size = pitch * size.height * sizeof(u8); + guest_address = buffer.Address(); const auto depth_slice_sz = buffer.GetDepthSliceSize(); guest_size_bytes = depth_slice_sz * num_slices; diff --git a/src/video_core/texture_cache/image_info.h b/src/video_core/texture_cache/image_info.h index e12ae3be1..a657310a8 100644 --- a/src/video_core/texture_cache/image_info.h +++ b/src/video_core/texture_cache/image_info.h @@ -69,7 +69,7 @@ struct ImageInfo { } props{}; // Surface properties with impact on various calculation factors vk::Format pixel_format = vk::Format::eUndefined; - vk::ImageType type = vk::ImageType::e1D; + vk::ImageType type = vk::ImageType::e2D; SubresourceExtent resources; Extent3D size{1, 1, 1}; u32 num_bits{}; diff --git a/src/video_core/texture_cache/image_view.cpp b/src/video_core/texture_cache/image_view.cpp index 61f1aaafe..12ad201d1 100644 --- a/src/video_core/texture_cache/image_view.cpp +++ b/src/video_core/texture_cache/image_view.cpp @@ -170,7 +170,7 @@ ImageView::ImageView(const Vulkan::Instance& instance, const ImageViewInfo& info format = image.info.pixel_format; aspect = vk::ImageAspectFlagBits::eDepth; } - if (image.aspect_mask & vk::ImageAspectFlagBits::eStencil && format == vk::Format::eR8Unorm) { + if (image.aspect_mask & vk::ImageAspectFlagBits::eStencil && format == vk::Format::eR8Uint) { format = image.info.pixel_format; aspect = vk::ImageAspectFlagBits::eStencil; } diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index 153314d2b..897d6f67e 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -443,6 +443,27 @@ ImageView& TextureCache::FindDepthTarget(BaseDesc& desc) { } } + // If there is a stencil attachment, link depth and stencil. + if (desc.info.stencil_addr != 0) { + ImageId stencil_id{}; + ForEachImageInRegion(desc.info.stencil_addr, desc.info.stencil_size, + [&](ImageId image_id, Image& image) { + if (image.info.guest_address == desc.info.stencil_addr) { + stencil_id = image_id; + } + }); + if (!stencil_id) { + ImageInfo info{}; + info.guest_address = desc.info.stencil_addr; + info.guest_size_bytes = desc.info.stencil_size; + info.size = desc.info.size; + stencil_id = slot_images.insert(instance, scheduler, info); + RegisterImage(stencil_id); + } + Image& image = slot_images[stencil_id]; + image.AssociateDepth(image_id); + } + return RegisterImageView(image_id, desc.view_info); } From 306279901fccb634b7722de1b4cc17f70dd70f6b Mon Sep 17 00:00:00 2001 From: MajorP93 Date: Fri, 13 Dec 2024 17:30:16 +0100 Subject: [PATCH 174/549] ci: Use link-time optimization for building (#1636) * ci: Use link-time optimization for building * cmake: Set CMP0069 policy to new for external dependencies * This enables LTO also when building external dependencies that do not handle CMP0069 in their CMake scripts. --- .github/workflows/build.yml | 12 ++++++------ externals/CMakeLists.txt | 3 +++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 878c10868..bacfbea0d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -89,7 +89,7 @@ jobs: arch: amd64 - name: Configure CMake - run: cmake --fresh -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + run: cmake --fresh -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $env:NUMBER_OF_PROCESSORS @@ -143,7 +143,7 @@ jobs: arch: amd64 - name: Configure CMake - run: cmake --fresh -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + run: cmake --fresh -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $env:NUMBER_OF_PROCESSORS @@ -201,7 +201,7 @@ jobs: variant: sccache - name: Configure CMake - run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_OSX_ARCHITECTURES=x86_64 -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache + run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_OSX_ARCHITECTURES=x86_64 -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(sysctl -n hw.ncpu) @@ -265,7 +265,7 @@ jobs: variant: sccache - name: Configure CMake - run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_OSX_ARCHITECTURES=x86_64 -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache + run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_OSX_ARCHITECTURES=x86_64 -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(sysctl -n hw.ncpu) @@ -312,7 +312,7 @@ jobs: key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }} - name: Configure CMake - run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc) @@ -368,7 +368,7 @@ jobs: key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }} - name: Configure CMake - run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 082be211a..e1e67f235 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -8,6 +8,9 @@ set_directory_properties(PROPERTIES SYSTEM ON ) +# Set CMP0069 policy to "NEW" in order to ensure consistent behavior when building external targets with LTO enabled +set(CMAKE_POLICY_DEFAULT_CMP0069 NEW) + if (MSVC) # Silence "deprecation" warnings add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS) From 028be3ba5d7da1a0782c053f43cf606c78d9b71b Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 13 Dec 2024 11:49:07 -0800 Subject: [PATCH 175/549] shader_recompiler: Emulate unnormalized sampler coordinates in shader. (#1762) * shader_recompiler: Emulate unnormalized sampler coordinates in shader. * Address review comments. --- .../spirv/emit_spirv_floating_point.cpp | 8 ++++ .../backend/spirv/emit_spirv_instructions.h | 2 + .../frontend/translate/vector_memory.cpp | 1 + src/shader_recompiler/ir/ir_emitter.cpp | 14 +++++++ src/shader_recompiler/ir/ir_emitter.h | 1 + src/shader_recompiler/ir/opcodes.inc | 2 + .../ir/passes/resource_tracking_pass.cpp | 41 ++++++++++++++----- src/shader_recompiler/ir/reg.h | 3 +- src/shader_recompiler/specialization.h | 16 ++++++++ src/video_core/texture_cache/sampler.cpp | 2 +- 10 files changed, 78 insertions(+), 12 deletions(-) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp index e822eabef..1e8f31ddc 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp @@ -87,6 +87,14 @@ Id EmitFPMul64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { return Decorate(ctx, inst, ctx.OpFMul(ctx.F64[1], a, b)); } +Id EmitFPDiv32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { + return Decorate(ctx, inst, ctx.OpFDiv(ctx.F32[1], a, b)); +} + +Id EmitFPDiv64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { + return Decorate(ctx, inst, ctx.OpFDiv(ctx.F64[1], a, b)); +} + Id EmitFPNeg16(EmitContext& ctx, Id value) { return ctx.OpFNegate(ctx.F16[1], value); } diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index cc3db880c..071b430d5 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h @@ -189,6 +189,8 @@ Id EmitFPMin64(EmitContext& ctx, Id a, Id b); Id EmitFPMul16(EmitContext& ctx, IR::Inst* inst, Id a, Id b); Id EmitFPMul32(EmitContext& ctx, IR::Inst* inst, Id a, Id b); Id EmitFPMul64(EmitContext& ctx, IR::Inst* inst, Id a, Id b); +Id EmitFPDiv32(EmitContext& ctx, IR::Inst* inst, Id a, Id b); +Id EmitFPDiv64(EmitContext& ctx, IR::Inst* inst, Id a, Id b); Id EmitFPNeg16(EmitContext& ctx, Id value); Id EmitFPNeg32(EmitContext& ctx, Id value); Id EmitFPNeg64(EmitContext& ctx, Id value); diff --git a/src/shader_recompiler/frontend/translate/vector_memory.cpp b/src/shader_recompiler/frontend/translate/vector_memory.cpp index b7ad3b36b..74b9c905d 100644 --- a/src/shader_recompiler/frontend/translate/vector_memory.cpp +++ b/src/shader_recompiler/frontend/translate/vector_memory.cpp @@ -527,6 +527,7 @@ IR::Value EmitImageSample(IR::IREmitter& ir, const GcnInst& inst, const IR::Scal info.has_offset.Assign(flags.test(MimgModifier::Offset)); info.has_lod.Assign(flags.any(MimgModifier::Lod)); info.is_array.Assign(mimg.da); + info.is_unnormalized.Assign(mimg.unrm); if (gather) { info.gather_comp.Assign(std::bit_width(mimg.dmask) - 1); diff --git a/src/shader_recompiler/ir/ir_emitter.cpp b/src/shader_recompiler/ir/ir_emitter.cpp index 78e7f2289..5fa20b744 100644 --- a/src/shader_recompiler/ir/ir_emitter.cpp +++ b/src/shader_recompiler/ir/ir_emitter.cpp @@ -692,6 +692,20 @@ F32F64 IREmitter::FPMul(const F32F64& a, const F32F64& b) { } } +F32F64 IREmitter::FPDiv(const F32F64& a, const F32F64& b) { + if (a.Type() != b.Type()) { + UNREACHABLE_MSG("Mismatching types {} and {}", a.Type(), b.Type()); + } + switch (a.Type()) { + case Type::F32: + return Inst(Opcode::FPDiv32, a, b); + case Type::F64: + return Inst(Opcode::FPDiv64, a, b); + default: + ThrowInvalidType(a.Type()); + } +} + F32F64 IREmitter::FPFma(const F32F64& a, const F32F64& b, const F32F64& c) { if (a.Type() != b.Type() || a.Type() != c.Type()) { UNREACHABLE_MSG("Mismatching types {}, {}, and {}", a.Type(), b.Type(), c.Type()); diff --git a/src/shader_recompiler/ir/ir_emitter.h b/src/shader_recompiler/ir/ir_emitter.h index cbd3780de..e6608cba7 100644 --- a/src/shader_recompiler/ir/ir_emitter.h +++ b/src/shader_recompiler/ir/ir_emitter.h @@ -158,6 +158,7 @@ public: [[nodiscard]] F32F64 FPAdd(const F32F64& a, const F32F64& b); [[nodiscard]] F32F64 FPSub(const F32F64& a, const F32F64& b); [[nodiscard]] F32F64 FPMul(const F32F64& a, const F32F64& b); + [[nodiscard]] F32F64 FPDiv(const F32F64& a, const F32F64& b); [[nodiscard]] F32F64 FPFma(const F32F64& a, const F32F64& b, const F32F64& c); [[nodiscard]] F32F64 FPAbs(const F32F64& value); diff --git a/src/shader_recompiler/ir/opcodes.inc b/src/shader_recompiler/ir/opcodes.inc index 0283ccd0f..60232a3a1 100644 --- a/src/shader_recompiler/ir/opcodes.inc +++ b/src/shader_recompiler/ir/opcodes.inc @@ -184,6 +184,8 @@ OPCODE(FPMin32, F32, F32, OPCODE(FPMin64, F64, F64, F64, ) OPCODE(FPMul32, F32, F32, F32, ) OPCODE(FPMul64, F64, F64, F64, ) +OPCODE(FPDiv32, F32, F32, F32, ) +OPCODE(FPDiv64, F64, F64, F64, ) OPCODE(FPNeg32, F32, F32, ) OPCODE(FPNeg64, F64, F64, ) OPCODE(FPRecip32, F32, F32, ) diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index 89c5c78a0..995851049 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -420,26 +420,29 @@ void PatchImageSampleInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descriptors& descriptors, const IR::Inst* producer, const u32 image_binding, const AmdGpu::Image& image) { // Read sampler sharp. This doesn't exist for IMAGE_LOAD/IMAGE_STORE instructions - const u32 sampler_binding = [&] { + const auto [sampler_binding, sampler] = [&] -> std::pair { ASSERT(producer->GetOpcode() == IR::Opcode::CompositeConstructU32x2); const IR::Value& handle = producer->Arg(1); // Inline sampler resource. if (handle.IsImmediate()) { LOG_WARNING(Render_Vulkan, "Inline sampler detected"); - return descriptors.Add(SamplerResource{ + const auto inline_sampler = AmdGpu::Sampler{.raw0 = handle.U32()}; + const auto binding = descriptors.Add(SamplerResource{ .sharp_idx = std::numeric_limits::max(), - .inline_sampler = AmdGpu::Sampler{.raw0 = handle.U32()}, + .inline_sampler = inline_sampler, }); + return {binding, inline_sampler}; } // Normal sampler resource. const auto ssharp_handle = handle.InstRecursive(); const auto& [ssharp_ud, disable_aniso] = TryDisableAnisoLod0(ssharp_handle); const auto ssharp = TrackSharp(ssharp_ud, info); - return descriptors.Add(SamplerResource{ + const auto binding = descriptors.Add(SamplerResource{ .sharp_idx = ssharp, .associated_image = image_binding, .disable_aniso = disable_aniso, }); + return {binding, info.ReadUdSharp(ssharp)}; }(); IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; @@ -539,28 +542,46 @@ void PatchImageSampleInstruction(IR::Block& block, IR::Inst& inst, Info& info, } }(); + const auto unnormalized = sampler.force_unnormalized || inst_info.is_unnormalized; + // Query dimensions of image if needed for normalization. + // We can't use the image sharp because it could be bound to a different image later. + const auto dimensions = + unnormalized ? ir.ImageQueryDimension(ir.Imm32(image_binding), ir.Imm32(0u), ir.Imm1(false)) + : IR::Value{}; + const auto get_coord = [&](u32 idx, u32 dim_idx) -> IR::Value { + const auto coord = get_addr_reg(idx); + if (unnormalized) { + // Normalize the coordinate for sampling, dividing by its corresponding dimension. + return ir.FPDiv(coord, + ir.BitCast(IR::U32{ir.CompositeExtract(dimensions, dim_idx)})); + } + return coord; + }; + // Now we can load body components as noted in Table 8.9 Image Opcodes with Sampler const IR::Value coords = [&] -> IR::Value { switch (image.GetType()) { case AmdGpu::ImageType::Color1D: // x addr_reg = addr_reg + 1; - return get_addr_reg(addr_reg - 1); + return get_coord(addr_reg - 1, 0); case AmdGpu::ImageType::Color1DArray: // x, slice [[fallthrough]]; case AmdGpu::ImageType::Color2D: // x, y addr_reg = addr_reg + 2; - return ir.CompositeConstruct(get_addr_reg(addr_reg - 2), get_addr_reg(addr_reg - 1)); + return ir.CompositeConstruct(get_coord(addr_reg - 2, 0), get_coord(addr_reg - 1, 1)); case AmdGpu::ImageType::Color2DArray: // x, y, slice [[fallthrough]]; case AmdGpu::ImageType::Color2DMsaa: // x, y, frag - [[fallthrough]]; + addr_reg = addr_reg + 3; + return ir.CompositeConstruct(get_coord(addr_reg - 3, 0), get_coord(addr_reg - 2, 1), + get_addr_reg(addr_reg - 1)); case AmdGpu::ImageType::Color3D: // x, y, z addr_reg = addr_reg + 3; - return ir.CompositeConstruct(get_addr_reg(addr_reg - 3), get_addr_reg(addr_reg - 2), - get_addr_reg(addr_reg - 1)); + return ir.CompositeConstruct(get_coord(addr_reg - 3, 0), get_coord(addr_reg - 2, 1), + get_coord(addr_reg - 1, 2)); case AmdGpu::ImageType::Cube: // x, y, face addr_reg = addr_reg + 3; - return PatchCubeCoord(ir, get_addr_reg(addr_reg - 3), get_addr_reg(addr_reg - 2), + return PatchCubeCoord(ir, get_coord(addr_reg - 3, 0), get_coord(addr_reg - 2, 1), get_addr_reg(addr_reg - 1), false, inst_info.is_array); default: UNREACHABLE(); diff --git a/src/shader_recompiler/ir/reg.h b/src/shader_recompiler/ir/reg.h index 3004d2b86..ca2e9ceb9 100644 --- a/src/shader_recompiler/ir/reg.h +++ b/src/shader_recompiler/ir/reg.h @@ -40,7 +40,8 @@ union TextureInstInfo { BitField<6, 2, u32> gather_comp; BitField<8, 1, u32> has_derivatives; BitField<9, 1, u32> is_array; - BitField<10, 1, u32> is_gather; + BitField<10, 1, u32> is_unnormalized; + BitField<11, 1, u32> is_gather; }; union BufferInstInfo { diff --git a/src/shader_recompiler/specialization.h b/src/shader_recompiler/specialization.h index 2a3bd62f4..bc8627c1c 100644 --- a/src/shader_recompiler/specialization.h +++ b/src/shader_recompiler/specialization.h @@ -49,6 +49,12 @@ struct FMaskSpecialization { auto operator<=>(const FMaskSpecialization&) const = default; }; +struct SamplerSpecialization { + bool force_unnormalized = false; + + auto operator<=>(const SamplerSpecialization&) const = default; +}; + /** * Alongside runtime information, this structure also checks bound resources * for compatibility. Can be used as a key for storing shader permutations. @@ -67,6 +73,7 @@ struct StageSpecialization { boost::container::small_vector tex_buffers; boost::container::small_vector images; boost::container::small_vector fmasks; + boost::container::small_vector samplers; Backend::Bindings start{}; explicit StageSpecialization(const Info& info_, RuntimeInfo runtime_info_, @@ -107,6 +114,10 @@ struct StageSpecialization { spec.width = sharp.width; spec.height = sharp.height; }); + ForEachSharp(samplers, info->samplers, + [](auto& spec, const auto& desc, AmdGpu::Sampler sharp) { + spec.force_unnormalized = sharp.force_unnormalized; + }); } void ForEachSharp(auto& spec_list, auto& desc_list, auto&& func) { @@ -175,6 +186,11 @@ struct StageSpecialization { return false; } } + for (u32 i = 0; i < samplers.size(); i++) { + if (samplers[i] != other.samplers[i]) { + return false; + } + } return true; } }; diff --git a/src/video_core/texture_cache/sampler.cpp b/src/video_core/texture_cache/sampler.cpp index e47f53abf..9f4bc7a7e 100644 --- a/src/video_core/texture_cache/sampler.cpp +++ b/src/video_core/texture_cache/sampler.cpp @@ -25,7 +25,7 @@ Sampler::Sampler(const Vulkan::Instance& instance, const AmdGpu::Sampler& sample .minLod = sampler.MinLod(), .maxLod = sampler.MaxLod(), .borderColor = LiverpoolToVK::BorderColor(sampler.border_color_type), - .unnormalizedCoordinates = bool(sampler.force_unnormalized), + .unnormalizedCoordinates = false, // Handled in shader due to Vulkan limitations. }; auto [sampler_result, smplr] = instance.GetDevice().createSamplerUnique(sampler_ci); ASSERT_MSG(sampler_result == vk::Result::eSuccess, "Failed to create sampler: {}", From 722a0e36be3486d2084bae557bc6722d7b895b3d Mon Sep 17 00:00:00 2001 From: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> Date: Fri, 13 Dec 2024 21:49:37 +0200 Subject: [PATCH 176/549] graphics: Improve handling of color buffer and storage image swizzles (#1763) * liverpool_to_vk: Remove wrong component swap formats * shader_recompiler: Handle storage and buffer format swizzles * shader_recompiler: Skip unsupported depth export * image_view: Remove image format swizzle * Platform support is not always guaranteed --- .../frontend/translate/export.cpp | 5 +++ .../ir/passes/resource_tracking_pass.cpp | 42 +++++++++++++++++++ src/shader_recompiler/specialization.h | 11 ++++- src/video_core/amdgpu/resource.h | 9 ++++ .../renderer_vulkan/liverpool_to_vk.cpp | 9 ---- src/video_core/texture_cache/image_view.cpp | 39 ----------------- 6 files changed, 66 insertions(+), 49 deletions(-) diff --git a/src/shader_recompiler/frontend/translate/export.cpp b/src/shader_recompiler/frontend/translate/export.cpp index f82f8fc1b..f4914577d 100644 --- a/src/shader_recompiler/frontend/translate/export.cpp +++ b/src/shader_recompiler/frontend/translate/export.cpp @@ -13,6 +13,11 @@ void Translator::EmitExport(const GcnInst& inst) { const auto& exp = inst.control.exp; const IR::Attribute attrib{exp.target}; + if (attrib == IR::Attribute::Depth && exp.en != 1) { + LOG_WARNING(Render_Vulkan, "Unsupported depth export"); + return; + } + const std::array vsrc = { IR::VectorReg(inst.src[0].code), IR::VectorReg(inst.src[1].code), diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index 995851049..398579ad4 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -137,6 +137,35 @@ bool IsImageInstruction(const IR::Inst& inst) { } } +IR::Value SwizzleVector(IR::IREmitter& ir, auto sharp, IR::Value texel) { + boost::container::static_vector comps; + for (u32 i = 0; i < 4; i++) { + switch (sharp.GetSwizzle(i)) { + case AmdGpu::CompSwizzle::Zero: + comps.emplace_back(ir.Imm32(0.f)); + break; + case AmdGpu::CompSwizzle::One: + comps.emplace_back(ir.Imm32(1.f)); + break; + case AmdGpu::CompSwizzle::Red: + comps.emplace_back(ir.CompositeExtract(texel, 0)); + break; + case AmdGpu::CompSwizzle::Green: + comps.emplace_back(ir.CompositeExtract(texel, 1)); + break; + case AmdGpu::CompSwizzle::Blue: + comps.emplace_back(ir.CompositeExtract(texel, 2)); + break; + case AmdGpu::CompSwizzle::Alpha: + comps.emplace_back(ir.CompositeExtract(texel, 3)); + break; + default: + UNREACHABLE(); + } + } + return ir.CompositeConstruct(comps[0], comps[1], comps[2], comps[3]); +}; + class Descriptors { public: explicit Descriptors(Info& info_) @@ -388,6 +417,15 @@ void PatchTextureBufferInstruction(IR::Block& block, IR::Inst& inst, Info& info, IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; inst.SetArg(0, ir.Imm32(binding)); ASSERT(!buffer.swizzle_enable && !buffer.add_tid_enable); + + // Apply dst_sel swizzle on formatted buffer instructions + if (inst.GetOpcode() == IR::Opcode::StoreBufferFormatF32) { + inst.SetArg(2, SwizzleVector(ir, buffer, inst.Arg(2))); + } else { + const auto inst_info = inst.Flags(); + const auto texel = ir.LoadBufferFormat(inst.Arg(0), inst.Arg(1), inst_info); + inst.ReplaceUsesWith(SwizzleVector(ir, buffer, texel)); + } } IR::Value PatchCubeCoord(IR::IREmitter& ir, const IR::Value& s, const IR::Value& t, @@ -732,6 +770,10 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip }(); inst.SetArg(1, coords); + if (inst.GetOpcode() == IR::Opcode::ImageWrite) { + inst.SetArg(2, SwizzleVector(ir, image, inst.Arg(2))); + } + if (inst_info.has_lod) { ASSERT(inst.GetOpcode() == IR::Opcode::ImageFetch); ASSERT(image.GetType() != AmdGpu::ImageType::Color2DMsaa && diff --git a/src/shader_recompiler/specialization.h b/src/shader_recompiler/specialization.h index bc8627c1c..9b5dd8fa1 100644 --- a/src/shader_recompiler/specialization.h +++ b/src/shader_recompiler/specialization.h @@ -31,6 +31,7 @@ struct BufferSpecialization { struct TextureBufferSpecialization { bool is_integer = false; + u32 dst_select = 0; auto operator<=>(const TextureBufferSpecialization&) const = default; }; @@ -38,8 +39,12 @@ struct TextureBufferSpecialization { struct ImageSpecialization { AmdGpu::ImageType type = AmdGpu::ImageType::Color2D; bool is_integer = false; + u32 dst_select = 0; - auto operator<=>(const ImageSpecialization&) const = default; + bool operator==(const ImageSpecialization& other) const { + return type == other.type && is_integer == other.is_integer && + (dst_select != 0 ? dst_select == other.dst_select : true); + } }; struct FMaskSpecialization { @@ -103,11 +108,15 @@ struct StageSpecialization { ForEachSharp(binding, tex_buffers, info->texture_buffers, [](auto& spec, const auto& desc, AmdGpu::Buffer sharp) { spec.is_integer = AmdGpu::IsInteger(sharp.GetNumberFmt()); + spec.dst_select = sharp.DstSelect(); }); ForEachSharp(binding, images, info->images, [](auto& spec, const auto& desc, AmdGpu::Image sharp) { spec.type = sharp.GetBoundType(); spec.is_integer = AmdGpu::IsInteger(sharp.GetNumberFmt()); + if (desc.is_storage) { + spec.dst_select = sharp.DstSelect(); + } }); ForEachSharp(binding, fmasks, info->fmasks, [](auto& spec, const auto& desc, AmdGpu::Image sharp) { diff --git a/src/video_core/amdgpu/resource.h b/src/video_core/amdgpu/resource.h index ba87425f2..5d7417559 100644 --- a/src/video_core/amdgpu/resource.h +++ b/src/video_core/amdgpu/resource.h @@ -52,6 +52,10 @@ struct Buffer { return std::memcmp(this, &other, sizeof(Buffer)) == 0; } + u32 DstSelect() const { + return dst_sel_x | (dst_sel_y << 3) | (dst_sel_z << 6) | (dst_sel_w << 9); + } + CompSwizzle GetSwizzle(u32 comp) const noexcept { const std::array select{dst_sel_x, dst_sel_y, dst_sel_z, dst_sel_w}; return static_cast(select[comp]); @@ -204,6 +208,11 @@ struct Image { return dst_sel_x | (dst_sel_y << 3) | (dst_sel_z << 6) | (dst_sel_w << 9); } + CompSwizzle GetSwizzle(u32 comp) const noexcept { + const std::array select{dst_sel_x, dst_sel_y, dst_sel_z, dst_sel_w}; + return static_cast(select[comp]); + } + static char SelectComp(u32 sel) { switch (sel) { case 0: diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp index fa8d28ba0..ec0bb3bb7 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp @@ -699,15 +699,6 @@ vk::Format AdjustColorBufferFormat(vk::Format base_format, default: break; } - } else if (comp_swap_reverse) { - switch (base_format) { - case vk::Format::eR8G8B8A8Unorm: - return vk::Format::eA8B8G8R8UnormPack32; - case vk::Format::eR8G8B8A8Srgb: - return vk::Format::eA8B8G8R8SrgbPack32; - default: - break; - } } return base_format; } diff --git a/src/video_core/texture_cache/image_view.cpp b/src/video_core/texture_cache/image_view.cpp index 12ad201d1..cc467e9a4 100644 --- a/src/video_core/texture_cache/image_view.cpp +++ b/src/video_core/texture_cache/image_view.cpp @@ -50,34 +50,6 @@ vk::ComponentSwizzle ConvertComponentSwizzle(u32 dst_sel) { } } -bool IsIdentityMapping(u32 dst_sel, u32 num_components) { - return (num_components == 1 && dst_sel == 0b001'000'000'100) || - (num_components == 2 && dst_sel == 0b001'000'101'100) || - (num_components == 3 && dst_sel == 0b001'110'101'100) || - (num_components == 4 && dst_sel == 0b111'110'101'100); -} - -vk::Format TrySwizzleFormat(vk::Format format, u32 dst_sel) { - // BGRA - if (dst_sel == 0b111100101110) { - switch (format) { - case vk::Format::eR8G8B8A8Unorm: - return vk::Format::eB8G8R8A8Unorm; - case vk::Format::eR8G8B8A8Snorm: - return vk::Format::eB8G8R8A8Snorm; - case vk::Format::eR8G8B8A8Uint: - return vk::Format::eB8G8R8A8Uint; - case vk::Format::eR8G8B8A8Sint: - return vk::Format::eB8G8R8A8Sint; - case vk::Format::eR8G8B8A8Srgb: - return vk::Format::eB8G8R8A8Srgb; - default: - break; - } - } - return format; -} - ImageViewInfo::ImageViewInfo(const AmdGpu::Image& image, const Shader::ImageResource& desc) noexcept : is_storage{desc.is_storage} { const auto dfmt = image.GetDataFmt(); @@ -120,17 +92,6 @@ ImageViewInfo::ImageViewInfo(const AmdGpu::Image& image, const Shader::ImageReso mapping.b = ConvertComponentSwizzle(image.dst_sel_z); mapping.a = ConvertComponentSwizzle(image.dst_sel_w); } - // Check for unfortunate case of storage images being swizzled - const u32 num_comps = AmdGpu::NumComponents(image.GetDataFmt()); - const u32 dst_sel = image.DstSelect(); - if (is_storage && !IsIdentityMapping(dst_sel, num_comps)) { - if (auto new_format = TrySwizzleFormat(format, dst_sel); new_format != format) { - format = new_format; - return; - } - LOG_ERROR(Render_Vulkan, "Storage image (num_comps = {}) requires swizzling {}", num_comps, - image.DstSelectName()); - } } ImageViewInfo::ImageViewInfo(const AmdGpu::Liverpool::ColorBuffer& col_buffer) noexcept { From f1c23d514b204e0f90c8538743978706fabc30b8 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 13 Dec 2024 11:51:39 -0800 Subject: [PATCH 177/549] shader_recompiler: Implement FREXP instructions. (#1766) --- externals/sirit | 2 +- .../spirv/emit_spirv_floating_point.cpp | 26 ++++++++++++- .../backend/spirv/emit_spirv_instructions.h | 7 +++- .../backend/spirv/spirv_emit_context.cpp | 4 ++ .../backend/spirv/spirv_emit_context.h | 2 + .../frontend/translate/translate.h | 5 +++ .../frontend/translate/vector_alu.cpp | 37 ++++++++++++++++++- src/shader_recompiler/ir/ir_emitter.cpp | 33 ++++++++++++++++- src/shader_recompiler/ir/ir_emitter.h | 4 +- src/shader_recompiler/ir/opcodes.inc | 7 +++- 10 files changed, 119 insertions(+), 8 deletions(-) diff --git a/externals/sirit b/externals/sirit index 6cecb95d6..e12b6b592 160000 --- a/externals/sirit +++ b/externals/sirit @@ -1 +1 @@ -Subproject commit 6cecb95d679c82c413d1f989e0b7ad9af130600d +Subproject commit e12b6b592ce9917a85303c555259488643c56f47 diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp index 1e8f31ddc..a63be87e2 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp @@ -225,10 +225,34 @@ Id EmitFPTrunc64(EmitContext& ctx, Id value) { return ctx.OpTrunc(ctx.F64[1], value); } -Id EmitFPFract(EmitContext& ctx, Id value) { +Id EmitFPFract32(EmitContext& ctx, Id value) { return ctx.OpFract(ctx.F32[1], value); } +Id EmitFPFract64(EmitContext& ctx, Id value) { + return ctx.OpFract(ctx.F64[1], value); +} + +Id EmitFPFrexpSig32(EmitContext& ctx, Id value) { + const auto frexp = ctx.OpFrexpStruct(ctx.frexp_result_f32, value); + return ctx.OpCompositeExtract(ctx.F32[1], frexp, 0); +} + +Id EmitFPFrexpSig64(EmitContext& ctx, Id value) { + const auto frexp = ctx.OpFrexpStruct(ctx.frexp_result_f64, value); + return ctx.OpCompositeExtract(ctx.F64[1], frexp, 0); +} + +Id EmitFPFrexpExp32(EmitContext& ctx, Id value) { + const auto frexp = ctx.OpFrexpStruct(ctx.frexp_result_f32, value); + return ctx.OpCompositeExtract(ctx.U32[1], frexp, 1); +} + +Id EmitFPFrexpExp64(EmitContext& ctx, Id value) { + const auto frexp = ctx.OpFrexpStruct(ctx.frexp_result_f64, value); + return ctx.OpCompositeExtract(ctx.U32[1], frexp, 1); +} + Id EmitFPOrdEqual16(EmitContext& ctx, Id lhs, Id rhs) { return ctx.OpFOrdEqual(ctx.U1[1], lhs, rhs); } diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index 071b430d5..4ff53670e 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h @@ -222,7 +222,12 @@ Id EmitFPCeil64(EmitContext& ctx, Id value); Id EmitFPTrunc16(EmitContext& ctx, Id value); Id EmitFPTrunc32(EmitContext& ctx, Id value); Id EmitFPTrunc64(EmitContext& ctx, Id value); -Id EmitFPFract(EmitContext& ctx, Id value); +Id EmitFPFract32(EmitContext& ctx, Id value); +Id EmitFPFract64(EmitContext& ctx, Id value); +Id EmitFPFrexpSig32(EmitContext& ctx, Id value); +Id EmitFPFrexpSig64(EmitContext& ctx, Id value); +Id EmitFPFrexpExp32(EmitContext& ctx, Id value); +Id EmitFPFrexpExp64(EmitContext& ctx, Id value); Id EmitFPOrdEqual16(EmitContext& ctx, Id lhs, Id rhs); Id EmitFPOrdEqual32(EmitContext& ctx, Id lhs, Id rhs); Id EmitFPOrdEqual64(EmitContext& ctx, Id lhs, Id rhs); diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 5c7278c6b..1ada2f1f9 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -147,6 +147,10 @@ void EmitContext::DefineArithmeticTypes() { full_result_i32x2 = Name(TypeStruct(S32[1], S32[1]), "full_result_i32x2"); full_result_u32x2 = Name(TypeStruct(U32[1], U32[1]), "full_result_u32x2"); + frexp_result_f32 = Name(TypeStruct(F32[1], U32[1]), "frexp_result_f32"); + if (info.uses_fp64) { + frexp_result_f64 = Name(TypeStruct(F64[1], U32[1]), "frexp_result_f64"); + } } void EmitContext::DefineInterfaces() { diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index 4e5e7dd3b..cd1293328 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -148,6 +148,8 @@ public: Id full_result_i32x2; Id full_result_u32x2; + Id frexp_result_f32; + Id frexp_result_f64; Id pi_x2; diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index 43f3ccef2..2f320a6c7 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -200,6 +200,11 @@ public: void V_BFREV_B32(const GcnInst& inst); void V_FFBH_U32(const GcnInst& inst); void V_FFBL_B32(const GcnInst& inst); + void V_FREXP_EXP_I32_F64(const GcnInst& inst); + void V_FREXP_MANT_F64(const GcnInst& inst); + void V_FRACT_F64(const GcnInst& inst); + void V_FREXP_EXP_I32_F32(const GcnInst& inst); + void V_FREXP_MANT_F32(const GcnInst& inst); void V_MOVRELD_B32(const GcnInst& inst); void V_MOVRELS_B32(const GcnInst& inst); void V_MOVRELSD_B32(const GcnInst& inst); diff --git a/src/shader_recompiler/frontend/translate/vector_alu.cpp b/src/shader_recompiler/frontend/translate/vector_alu.cpp index 8149230db..3e9e677a7 100644 --- a/src/shader_recompiler/frontend/translate/vector_alu.cpp +++ b/src/shader_recompiler/frontend/translate/vector_alu.cpp @@ -179,6 +179,16 @@ void Translator::EmitVectorAlu(const GcnInst& inst) { return V_FFBH_U32(inst); case Opcode::V_FFBL_B32: return V_FFBL_B32(inst); + case Opcode::V_FREXP_EXP_I32_F64: + return V_FREXP_EXP_I32_F64(inst); + case Opcode::V_FREXP_MANT_F64: + return V_FREXP_MANT_F64(inst); + case Opcode::V_FRACT_F64: + return V_FRACT_F64(inst); + case Opcode::V_FREXP_EXP_I32_F32: + return V_FREXP_EXP_I32_F32(inst); + case Opcode::V_FREXP_MANT_F32: + return V_FREXP_MANT_F32(inst); case Opcode::V_MOVRELD_B32: return V_MOVRELD_B32(inst); case Opcode::V_MOVRELS_B32: @@ -733,7 +743,7 @@ void Translator::V_CVT_F32_UBYTE(u32 index, const GcnInst& inst) { void Translator::V_FRACT_F32(const GcnInst& inst) { const IR::F32 src0{GetSrc(inst.src[0])}; - SetDst(inst.dst[0], ir.Fract(src0)); + SetDst(inst.dst[0], ir.FPFract(src0)); } void Translator::V_TRUNC_F32(const GcnInst& inst) { @@ -822,6 +832,31 @@ void Translator::V_FFBL_B32(const GcnInst& inst) { SetDst(inst.dst[0], ir.FindILsb(src0)); } +void Translator::V_FREXP_EXP_I32_F64(const GcnInst& inst) { + const IR::F64 src0{GetSrc64(inst.src[0])}; + SetDst(inst.dst[0], ir.FPFrexpExp(src0)); +} + +void Translator::V_FREXP_MANT_F64(const GcnInst& inst) { + const IR::F64 src0{GetSrc64(inst.src[0])}; + SetDst64(inst.dst[0], ir.FPFrexpSig(src0)); +} + +void Translator::V_FRACT_F64(const GcnInst& inst) { + const IR::F32 src0{GetSrc64(inst.src[0])}; + SetDst64(inst.dst[0], ir.FPFract(src0)); +} + +void Translator::V_FREXP_EXP_I32_F32(const GcnInst& inst) { + const IR::F32 src0{GetSrc(inst.src[0])}; + SetDst(inst.dst[0], ir.FPFrexpExp(src0)); +} + +void Translator::V_FREXP_MANT_F32(const GcnInst& inst) { + const IR::F32 src0{GetSrc(inst.src[0])}; + SetDst(inst.dst[0], ir.FPFrexpSig(src0)); +} + void Translator::V_MOVRELD_B32(const GcnInst& inst) { const IR::U32 src_val{GetSrc(inst.src[0])}; u32 dst_vgprno = inst.dst[0].code - static_cast(IR::VectorReg::V0); diff --git a/src/shader_recompiler/ir/ir_emitter.cpp b/src/shader_recompiler/ir/ir_emitter.cpp index 5fa20b744..29b406699 100644 --- a/src/shader_recompiler/ir/ir_emitter.cpp +++ b/src/shader_recompiler/ir/ir_emitter.cpp @@ -869,8 +869,37 @@ F32F64 IREmitter::FPTrunc(const F32F64& value) { } } -F32 IREmitter::Fract(const F32& value) { - return Inst(Opcode::FPFract, value); +F32F64 IREmitter::FPFract(const F32F64& value) { + switch (value.Type()) { + case Type::F32: + return Inst(Opcode::FPFract32, value); + case Type::F64: + return Inst(Opcode::FPFract64, value); + default: + ThrowInvalidType(value.Type()); + } +} + +F32F64 IREmitter::FPFrexpSig(const F32F64& value) { + switch (value.Type()) { + case Type::F32: + return Inst(Opcode::FPFrexpSig32, value); + case Type::F64: + return Inst(Opcode::FPFrexpSig64, value); + default: + ThrowInvalidType(value.Type()); + } +} + +U32 IREmitter::FPFrexpExp(const F32F64& value) { + switch (value.Type()) { + case Type::F32: + return Inst(Opcode::FPFrexpExp32, value); + case Type::F64: + return Inst(Opcode::FPFrexpExp64, value); + default: + ThrowInvalidType(value.Type()); + } } U1 IREmitter::FPEqual(const F32F64& lhs, const F32F64& rhs, bool ordered) { diff --git a/src/shader_recompiler/ir/ir_emitter.h b/src/shader_recompiler/ir/ir_emitter.h index e6608cba7..f77e22b82 100644 --- a/src/shader_recompiler/ir/ir_emitter.h +++ b/src/shader_recompiler/ir/ir_emitter.h @@ -180,7 +180,9 @@ public: [[nodiscard]] F32F64 FPFloor(const F32F64& value); [[nodiscard]] F32F64 FPCeil(const F32F64& value); [[nodiscard]] F32F64 FPTrunc(const F32F64& value); - [[nodiscard]] F32 Fract(const F32& value); + [[nodiscard]] F32F64 FPFract(const F32F64& value); + [[nodiscard]] F32F64 FPFrexpSig(const F32F64& value); + [[nodiscard]] U32 FPFrexpExp(const F32F64& value); [[nodiscard]] U1 FPEqual(const F32F64& lhs, const F32F64& rhs, bool ordered = true); [[nodiscard]] U1 FPNotEqual(const F32F64& lhs, const F32F64& rhs, bool ordered = true); diff --git a/src/shader_recompiler/ir/opcodes.inc b/src/shader_recompiler/ir/opcodes.inc index 60232a3a1..8f40ed985 100644 --- a/src/shader_recompiler/ir/opcodes.inc +++ b/src/shader_recompiler/ir/opcodes.inc @@ -210,7 +210,12 @@ OPCODE(FPCeil32, F32, F32, OPCODE(FPCeil64, F64, F64, ) OPCODE(FPTrunc32, F32, F32, ) OPCODE(FPTrunc64, F64, F64, ) -OPCODE(FPFract, F32, F32, ) +OPCODE(FPFract32, F32, F32, ) +OPCODE(FPFract64, F64, F64, ) +OPCODE(FPFrexpSig32, F32, F32, ) +OPCODE(FPFrexpSig64, F64, F64, ) +OPCODE(FPFrexpExp32, U32, F32, ) +OPCODE(FPFrexpExp64, U32, F64, ) OPCODE(FPOrdEqual32, U1, F32, F32, ) OPCODE(FPOrdEqual64, U1, F64, F64, ) From 715ac8a2795be8a471e8ff9c02c716725215c43e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20Cea=20L=C3=B3pez?= Date: Fri, 13 Dec 2024 23:27:09 +0100 Subject: [PATCH 178/549] vk_shader_hle: Don't alter the order of the skipped copies. (#1757) * vk_shader_hle: Don't alter the order of the skipped copies. * Simplification. * Format. * More simplification. --- src/video_core/renderer_vulkan/vk_shader_hle.cpp | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_shader_hle.cpp b/src/video_core/renderer_vulkan/vk_shader_hle.cpp index d1d4f9af3..b863dce21 100644 --- a/src/video_core/renderer_vulkan/vk_shader_hle.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_hle.cpp @@ -60,7 +60,7 @@ bool ExecuteCopyShaderHLE(const Shader::Info& info, const AmdGpu::Liverpool::Reg static constexpr vk::DeviceSize MaxDistanceForMerge = 64_MB; u32 batch_start = 0; - u32 batch_end = copies.size() > 1 ? 1 : 0; + u32 batch_end = 0; while (batch_end < copies.size()) { // Place first copy into the current batch @@ -70,19 +70,19 @@ bool ExecuteCopyShaderHLE(const Shader::Info& info, const AmdGpu::Liverpool::Reg auto dst_offset_min = copy.dstOffset; auto dst_offset_max = copy.dstOffset + copy.size; - for (int i = batch_start + 1; i < copies.size(); i++) { + for (++batch_end; batch_end < copies.size(); batch_end++) { // Compute new src and dst bounds if we were to batch this copy - const auto& [src_offset, dst_offset, size] = copies[i]; + const auto& [src_offset, dst_offset, size] = copies[batch_end]; auto new_src_offset_min = std::min(src_offset_min, src_offset); auto new_src_offset_max = std::max(src_offset_max, src_offset + size); if (new_src_offset_max - new_src_offset_min > MaxDistanceForMerge) { - continue; + break; } auto new_dst_offset_min = std::min(dst_offset_min, dst_offset); auto new_dst_offset_max = std::max(dst_offset_max, dst_offset + size); if (new_dst_offset_max - new_dst_offset_min > MaxDistanceForMerge) { - continue; + break; } // We can batch this copy @@ -90,10 +90,6 @@ bool ExecuteCopyShaderHLE(const Shader::Info& info, const AmdGpu::Liverpool::Reg src_offset_max = new_src_offset_max; dst_offset_min = new_dst_offset_min; dst_offset_max = new_dst_offset_max; - if (i != batch_end) { - std::swap(copies[i], copies[batch_end]); - } - ++batch_end; } // Obtain buffers for the total source and destination ranges. @@ -116,7 +112,6 @@ bool ExecuteCopyShaderHLE(const Shader::Info& info, const AmdGpu::Liverpool::Reg src_offset_max - src_offset_min, dst_offset_max - dst_offset_min); scheduler.CommandBuffer().copyBuffer(src_buf->Handle(), dst_buf->Handle(), vk_copies); batch_start = batch_end; - ++batch_end; } scheduler.CommandBuffer().pipelineBarrier( From 8bb1e8fcdb511f28c08924789251454315bcc617 Mon Sep 17 00:00:00 2001 From: rainmakerv2 <30595646+rainmakerv3@users.noreply.github.com> Date: Sat, 14 Dec 2024 16:17:00 +0800 Subject: [PATCH 179/549] Resize trophy pop up windows based on window size (#1769) --- src/core/libraries/np_trophy/trophy_ui.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/core/libraries/np_trophy/trophy_ui.cpp b/src/core/libraries/np_trophy/trophy_ui.cpp index 618f8db46..55ef7b8de 100644 --- a/src/core/libraries/np_trophy/trophy_ui.cpp +++ b/src/core/libraries/np_trophy/trophy_ui.cpp @@ -38,21 +38,22 @@ void TrophyUI::Finish() { void TrophyUI::Draw() { const auto& io = GetIO(); + float AdjustWidth = io.DisplaySize.x / 1280; + float AdjustHeight = io.DisplaySize.y / 720; const ImVec2 window_size{ - std::min(io.DisplaySize.x, 250.f), - std::min(io.DisplaySize.y, 70.f), + std::min(io.DisplaySize.x, (300 * AdjustWidth)), + std::min(io.DisplaySize.y, (70 * AdjustHeight)), }; SetNextWindowSize(window_size); SetNextWindowCollapsed(false); - SetNextWindowPos(ImVec2(io.DisplaySize.x - 250, 50)); + SetNextWindowPos(ImVec2(io.DisplaySize.x - (300 * AdjustWidth), (50 * AdjustHeight))); KeepNavHighlight(); - if (Begin("Trophy Window", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoInputs)) { if (trophy_icon) { - Image(trophy_icon.GetTexture().im_id, ImVec2(50, 50)); + Image(trophy_icon.GetTexture().im_id, ImVec2((50 * AdjustWidth), (50 * AdjustHeight))); ImGui::SameLine(); } else { // placeholder @@ -61,6 +62,7 @@ void TrophyUI::Draw() { GetColorU32(ImVec4{0.7f})); ImGui::Indent(60); } + SetWindowFontScale((1.2 * AdjustHeight)); TextWrapped("Trophy earned!\n%s", trophy_name.c_str()); } End(); From 32556ad0d86ea01aacb136f00a879082bcca66c0 Mon Sep 17 00:00:00 2001 From: Alexandre Bouvier Date: Sat, 14 Dec 2024 08:18:05 +0000 Subject: [PATCH 180/549] cmake: fix double alias (#1771) --- CMakeLists.txt | 4 ++-- externals/CMakeLists.txt | 2 +- externals/sirit | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b057f55d6..2e21a33c4 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -875,7 +875,7 @@ endif() create_target_directory_groups(shadps4) target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG) -target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::SPIRV glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers) +target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers) target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h") target_compile_definitions(Dear_ImGui PRIVATE IMGUI_USER_CONFIG="${PROJECT_SOURCE_DIR}/src/imgui/imgui_config.h") @@ -1016,4 +1016,4 @@ if (ENABLE_QT_GUI AND CMAKE_SYSTEM_NAME STREQUAL "Linux") install(FILES "dist/net.shadps4.shadPS4.metainfo.xml" DESTINATION "share/metainfo") install(FILES ".github/shadps4.png" DESTINATION "share/icons/hicolor/512x512/apps" RENAME "net.shadps4.shadPS4.png") install(FILES "src/images/net.shadps4.shadPS4.svg" DESTINATION "share/icons/hicolor/scalable/apps") -endif() \ No newline at end of file +endif() diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index e1e67f235..dcc9d2bc0 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -110,7 +110,7 @@ if (NOT TARGET glslang::glslang) set(ENABLE_OPT OFF CACHE BOOL "") add_subdirectory(glslang) file(COPY glslang/SPIRV DESTINATION glslang/glslang FILES_MATCHING PATTERN "*.h") - target_include_directories(SPIRV INTERFACE "${CMAKE_CURRENT_BINARY_DIR}/glslang") + target_include_directories(glslang INTERFACE "${CMAKE_CURRENT_BINARY_DIR}/glslang") endif() # Robin-map diff --git a/externals/sirit b/externals/sirit index e12b6b592..5b5ff49a5 160000 --- a/externals/sirit +++ b/externals/sirit @@ -1 +1 @@ -Subproject commit e12b6b592ce9917a85303c555259488643c56f47 +Subproject commit 5b5ff49a58f5be27af1058794c6ca907dabc05b3 From a57ccf9112ec9971695cb08eeee27a876020ab19 Mon Sep 17 00:00:00 2001 From: rainmakerv2 <30595646+rainmakerv3@users.noreply.github.com> Date: Sat, 14 Dec 2024 16:18:34 +0800 Subject: [PATCH 181/549] Save main window together with config to avoid rare crash (#1772) --- src/common/config.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/config.cpp b/src/common/config.cpp index 4d07ba29f..403b0e32f 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -692,6 +692,7 @@ void save(const std::filesystem::path& path) { std::ofstream file(path, std::ios::binary); file << data; file.close(); + saveMainWindow(path); } void saveMainWindow(const std::filesystem::path& path) { From 40e8a40ada9a707b7cf3001ed1d8f835d0c7d2ad Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sat, 14 Dec 2024 00:20:04 -0800 Subject: [PATCH 182/549] externals: Add MoltenVK as an external. (#1767) --- .github/workflows/build.yml | 14 ++---- .gitmodules | 14 +++++- CMakeLists.txt | 3 +- documents/building-macos.md | 8 ++- externals/CMakeLists.txt | 24 +++++---- externals/MoltenVK/CMakeLists.txt | 81 +++++++++++++++++++++++++++++++ externals/MoltenVK/MoltenVK | 1 + externals/MoltenVK/SPIRV-Cross | 1 + externals/MoltenVK/cereal | 1 + 9 files changed, 119 insertions(+), 28 deletions(-) create mode 100644 externals/MoltenVK/CMakeLists.txt create mode 160000 externals/MoltenVK/MoltenVK create mode 160000 externals/MoltenVK/SPIRV-Cross create mode 160000 externals/MoltenVK/cereal diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bacfbea0d..3b5690438 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -174,11 +174,6 @@ jobs: with: xcode-version: latest - - name: Install MoltenVK - run: | - arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" - arch -x86_64 /usr/local/bin/brew install molten-vk - - name: Cache CMake Configuration uses: actions/cache@v4 env: @@ -210,7 +205,7 @@ jobs: run: | mkdir upload mv ${{github.workspace}}/build/shadps4 upload - cp $(arch -x86_64 /usr/local/bin/brew --prefix)/opt/molten-vk/lib/libMoltenVK.dylib upload + cp ${{github.workspace}}/build/externals/MoltenVK/libMoltenVK.dylib upload tar cf shadps4-macos-sdl.tar.gz -C upload . - uses: actions/upload-artifact@v4 with: @@ -230,11 +225,8 @@ jobs: with: xcode-version: latest - - name: Install MoltenVK and Setup Qt - run: | - arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" - arch -x86_64 /usr/local/bin/brew install molten-vk - - uses: jurplel/install-qt-action@v4 + - name: Setup Qt + uses: jurplel/install-qt-action@v4 with: version: 6.7.3 host: mac diff --git a/.gitmodules b/.gitmodules index 8010250a9..3d0d21c5b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -106,4 +106,16 @@ [submodule "externals/libpng"] path = externals/libpng url = https://github.com/pnggroup/libpng - shallow = true \ No newline at end of file + shallow = true +[submodule "externals/MoltenVK/SPIRV-Cross"] + path = externals/MoltenVK/SPIRV-Cross + url = https://github.com/KhronosGroup/SPIRV-Cross + shallow = true +[submodule "externals/MoltenVK/MoltenVK"] + path = externals/MoltenVK/MoltenVK + url = https://github.com/KhronosGroup/MoltenVK + shallow = true +[submodule "externals/MoltenVK/cereal"] + path = externals/MoltenVK/cereal + url = https://github.com/USCiLab/cereal + shallow = true diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e21a33c4..1e54f7a00 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -894,8 +894,7 @@ if (APPLE) target_compile_definitions(shadps4 PRIVATE USE_SYSTEM_VULKAN_LOADER=1) else() # Link MoltenVK for Vulkan support - find_library(MOLTENVK MoltenVK REQUIRED) - target_link_libraries(shadps4 PRIVATE ${MOLTENVK}) + target_link_libraries(shadps4 PRIVATE MoltenVK) endif() if (ARCHITECTURE STREQUAL "x86_64") diff --git a/documents/building-macos.md b/documents/building-macos.md index d8cc414e2..9a1a021ee 100644 --- a/documents/building-macos.md +++ b/documents/building-macos.md @@ -24,23 +24,21 @@ eval $(/opt/homebrew/bin/brew shellenv) brew install clang-format cmake ``` -Next, install x86_64 Homebrew and libraries. +Next, install x86_64 Qt. You can skip these steps and move on to **Cloning and compiling** if you do not intend to build the Qt GUI. **If you are on an ARM Mac:** ``` # Installs x86_64 Homebrew to /usr/local arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" # Installs libraries. -arch -x86_64 /usr/local/bin/brew install molten-vk qt@6 +arch -x86_64 /usr/local/bin/brew install qt@6 ``` **If you are on an x86_64 Mac:** ``` -brew install molten-vk qt@6 +brew install qt@6 ``` -If you don't need the Qt GUI you can remove `qt@6` from the last command. - ### Cloning and compiling: Clone the repository recursively: diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index dcc9d2bc0..1ab23a403 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -177,15 +177,6 @@ if (NOT TARGET PNG::PNG) add_library(PNG::PNG ALIAS png_static) endif() -if (APPLE) - # date - if (NOT TARGET date::date-tz) - option(BUILD_TZ_LIB "" ON) - option(USE_SYSTEM_TZ_DB "" ON) - add_subdirectory(date) - endif() -endif() - # Dear ImGui add_library(Dear_ImGui dear_imgui/imgui.cpp @@ -232,3 +223,18 @@ if (NOT TARGET stb::headers) target_include_directories(stb INTERFACE stb) add_library(stb::headers ALIAS stb) endif() + +# Apple-only dependencies +if (APPLE) + # date + if (NOT TARGET date::date-tz) + option(BUILD_TZ_LIB "" ON) + option(USE_SYSTEM_TZ_DB "" ON) + add_subdirectory(date) + endif() + + # MoltenVK + if (NOT TARGET MoltenVK) + add_subdirectory(MoltenVK) + endif() +endif() diff --git a/externals/MoltenVK/CMakeLists.txt b/externals/MoltenVK/CMakeLists.txt new file mode 100644 index 000000000..00e3231ee --- /dev/null +++ b/externals/MoltenVK/CMakeLists.txt @@ -0,0 +1,81 @@ +# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prepare version information +find_package(Git) +if(GIT_FOUND) + execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD + OUTPUT_VARIABLE MVK_GIT_REV + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) +endif() +set(MVK_VERSION "1.2.12") +set(MVK_GENERATED_INCLUDES ${CMAKE_CURRENT_BINARY_DIR}/Generated) +file(WRITE ${MVK_GENERATED_INCLUDES}/mvkGitRevDerived.h "static const char* mvkRevString = \"${MVK_GIT_REV}\";") + +# Find required system libraries +find_library(APPKIT_LIBRARY AppKit REQUIRED) +find_library(FOUNDATION_LIBRARY Foundation REQUIRED) +find_library(IOKIT_LIBRARY IOKit REQUIRED) +find_library(IOSURFACE_LIBRARY IOSurface REQUIRED) +find_library(METAL_LIBRARY Metal REQUIRED) +find_library(QUARTZCORE_LIBRARY QuartzCore REQUIRED) + +# cereal +option(SKIP_PORTABILITY_TEST "" ON) +option(BUILD_DOC "" OFF) +option(BUILD_SANDBOX "" OFF) +option(SKIP_PERFORMANCE_COMPARISON "" ON) +option(SPIRV_CROSS_SKIP_INSTALL "" ON) +add_subdirectory(cereal) + +# SPIRV-Cross +option(SPIRV_CROSS_CLI "" OFF) +option(SPIRV_CROSS_ENABLE_TESTS "" OFF) +option(SPIRV_CROSS_ENABLE_HLSL "" OFF) +option(SPIRV_CROSS_ENABLE_CPP "" OFF) +option(SPIRV_CROSS_SKIP_INSTALL "" ON) +add_subdirectory(SPIRV-Cross) + +# Common +set(MVK_COMMON_DIR ${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK/Common) +file(GLOB_RECURSE MVK_COMMON_SOURCES CONFIGURE_DEPENDS + ${MVK_COMMON_DIR}/*.cpp + ${MVK_COMMON_DIR}/*.m + ${MVK_COMMON_DIR}/*.mm) +set(MVK_COMMON_INCLUDES ${MVK_COMMON_DIR}) + +add_library(MoltenVKCommon STATIC ${MVK_COMMON_SOURCES}) +target_include_directories(MoltenVKCommon PUBLIC ${MVK_COMMON_INCLUDES}) +target_compile_options(MoltenVKCommon PRIVATE -w) + +# MoltenVKShaderConverter +set(MVK_SHADER_CONVERTER_DIR ${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK/MoltenVKShaderConverter) +file(GLOB_RECURSE MVK_SHADER_CONVERTER_SOURCES CONFIGURE_DEPENDS + ${MVK_SHADER_CONVERTER_DIR}/MoltenVKShaderConverter/*.cpp + ${MVK_SHADER_CONVERTER_DIR}/MoltenVKShaderConverter/*.m + ${MVK_SHADER_CONVERTER_DIR}/MoltenVKShaderConverter/*.mm) +set(MVK_SHADER_CONVERTER_INCLUDES ${MVK_SHADER_CONVERTER_DIR} ${MVK_SHADER_CONVERTER_DIR}/include) + +add_library(MoltenVKShaderConverter STATIC ${MVK_SHADER_CONVERTER_SOURCES}) +target_include_directories(MoltenVKShaderConverter PUBLIC ${MVK_SHADER_CONVERTER_INCLUDES}) +target_compile_options(MoltenVKShaderConverter PRIVATE -w) +target_link_libraries(MoltenVKShaderConverter PRIVATE spirv-cross-msl spirv-cross-reflect MoltenVKCommon) +target_compile_definitions(MoltenVKShaderConverter PRIVATE MVK_EXCLUDE_SPIRV_TOOLS=1) + +# MoltenVK +set(MVK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK/MoltenVK) +file(GLOB_RECURSE MVK_SOURCES CONFIGURE_DEPENDS + ${MVK_DIR}/MoltenVK/*.cpp + ${MVK_DIR}/MoltenVK/*.m + ${MVK_DIR}/MoltenVK/*.mm) +file(GLOB MVK_SRC_INCLUDES LIST_DIRECTORIES ON ${MVK_DIR}/MoltenVK/*) +set(MVK_INCLUDES ${MVK_SRC_INCLUDES} ${MVK_GENERATED_INCLUDES} ${MVK_DIR}/include) + +add_library(MoltenVK SHARED ${MVK_SOURCES}) +target_include_directories(MoltenVK PRIVATE ${MVK_INCLUDES}) +target_compile_options(MoltenVK PRIVATE -w) +target_link_libraries(MoltenVK PRIVATE + ${APPKIT_LIBRARY} ${FOUNDATION_LIBRARY} ${IOKIT_LIBRARY} ${IOSURFACE_LIBRARY} ${METAL_LIBRARY} ${QUARTZCORE_LIBRARY} + Vulkan::Headers cereal::cereal spirv-cross-msl MoltenVKCommon MoltenVKShaderConverter) +target_compile_definitions(MoltenVK PRIVATE MVK_FRAMEWORK_VERSION=${MVK_VERSION} MVK_USE_METAL_PRIVATE_API=1) diff --git a/externals/MoltenVK/MoltenVK b/externals/MoltenVK/MoltenVK new file mode 160000 index 000000000..5ad3ee5d2 --- /dev/null +++ b/externals/MoltenVK/MoltenVK @@ -0,0 +1 @@ +Subproject commit 5ad3ee5d2f84342950c3fe93dec97719574d1932 diff --git a/externals/MoltenVK/SPIRV-Cross b/externals/MoltenVK/SPIRV-Cross new file mode 160000 index 000000000..6173e24b3 --- /dev/null +++ b/externals/MoltenVK/SPIRV-Cross @@ -0,0 +1 @@ +Subproject commit 6173e24b31f09a0c3217103a130e74c4ddec14a6 diff --git a/externals/MoltenVK/cereal b/externals/MoltenVK/cereal new file mode 160000 index 000000000..d1fcec807 --- /dev/null +++ b/externals/MoltenVK/cereal @@ -0,0 +1 @@ +Subproject commit d1fcec807b372f04e4c1041b3058e11c12853e6e From 8caca4df32c05a11af8351590dcfa0fa5266eb11 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sat, 14 Dec 2024 02:03:42 -0800 Subject: [PATCH 183/549] shader_recompiler: Support VK_AMD_shader_image_load_store_lod for IMAGE_STORE_MIP (#1770) * shader_recompiler: Support VK_AMD_shader_image_load_store_lod for IMAGE_STORE_MIP * emit_spirv: Fix missing extension declaration. --- .../backend/spirv/emit_spirv.cpp | 4 ++++ .../backend/spirv/emit_spirv_image.cpp | 17 ++++++++++++----- .../backend/spirv/emit_spirv_instructions.h | 8 ++++---- .../frontend/translate/translate.h | 2 +- .../frontend/translate/vector_memory.cpp | 11 ++++++++--- src/shader_recompiler/ir/ir_emitter.cpp | 17 +++++++++-------- src/shader_recompiler/ir/ir_emitter.h | 10 ++++++---- src/shader_recompiler/ir/opcodes.inc | 6 +++--- .../ir/passes/resource_tracking_pass.cpp | 8 +++++--- src/shader_recompiler/profile.h | 1 + src/video_core/renderer_vulkan/vk_instance.cpp | 1 + src/video_core/renderer_vulkan/vk_instance.h | 6 ++++++ .../renderer_vulkan/vk_pipeline_cache.cpp | 1 + 13 files changed, 61 insertions(+), 31 deletions(-) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index 23800fc49..ab9d6afae 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -222,6 +222,10 @@ void SetupCapabilities(const Info& info, const Profile& profile, EmitContext& ct ctx.AddCapability(spv::Capability::StorageImageExtendedFormats); ctx.AddCapability(spv::Capability::StorageImageReadWithoutFormat); ctx.AddCapability(spv::Capability::StorageImageWriteWithoutFormat); + if (profile.supports_image_load_store_lod) { + ctx.AddExtension("SPV_AMD_shader_image_load_store_lod"); + ctx.AddCapability(spv::Capability::ImageReadWriteLodAMD); + } } if (info.has_texel_buffers) { ctx.AddCapability(spv::Capability::SampledBuffer); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index 736410dcd..8da9280d0 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp @@ -168,8 +168,8 @@ Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, return texture.is_integer ? ctx.OpBitcast(ctx.F32[4], texels) : texels; } -Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, const IR::Value& offset, - Id lod, Id ms) { +Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id lod, + const IR::Value& offset, Id ms) { const auto& texture = ctx.images[handle & 0xFFFF]; const Id image = ctx.OpLoad(texture.image_type, texture.id); const Id result_type = texture.data_types->Get(4); @@ -236,15 +236,22 @@ Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id return texture.is_integer ? ctx.OpBitcast(ctx.F32[4], sample) : sample; } -Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) { +Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id lod) { UNREACHABLE_MSG("SPIR-V Instruction"); } -void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id color) { +void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id lod, Id color) { const auto& texture = ctx.images[handle & 0xFFFF]; const Id image = ctx.OpLoad(texture.image_type, texture.id); const Id color_type = texture.data_types->Get(4); - ctx.OpImageWrite(image, coords, ctx.OpBitcast(color_type, color)); + ImageOperands operands; + if (ctx.profile.supports_image_load_store_lod) { + operands.Add(spv::ImageOperandsMask::Lod, lod); + } else if (lod.value != 0) { + LOG_WARNING(Render, "Image write with LOD not supported by driver"); + } + ctx.OpImageWrite(image, coords, ctx.OpBitcast(color_type, color), operands.mask, + operands.operands); } } // namespace Shader::Backend::SPIRV diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index 4ff53670e..057b0d692 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h @@ -392,14 +392,14 @@ Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, const IR::Value& offset); Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, const IR::Value& offset, Id dref); -Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, const IR::Value& offset, - Id lod, Id ms); +Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id lod, + const IR::Value& offset, Id ms); Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, u32 handle, Id lod, bool skip_mips); Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords); Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id derivatives_dx, Id derivatives_dy, const IR::Value& offset, const IR::Value& lod_clamp); -Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); -void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id color); +Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id lod); +void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id lod, Id color); Id EmitImageAtomicIAdd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value); Id EmitImageAtomicSMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value); diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index 2f320a6c7..198cea276 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -276,7 +276,7 @@ public: // Image Memory // MIMG void IMAGE_LOAD(bool has_mip, const GcnInst& inst); - void IMAGE_STORE(const GcnInst& inst); + void IMAGE_STORE(bool has_mip, const GcnInst& inst); void IMAGE_GET_RESINFO(const GcnInst& inst); void IMAGE_ATOMIC(AtomicOp op, const GcnInst& inst); void IMAGE_SAMPLE(const GcnInst& inst); diff --git a/src/shader_recompiler/frontend/translate/vector_memory.cpp b/src/shader_recompiler/frontend/translate/vector_memory.cpp index 74b9c905d..eadd1c4db 100644 --- a/src/shader_recompiler/frontend/translate/vector_memory.cpp +++ b/src/shader_recompiler/frontend/translate/vector_memory.cpp @@ -98,7 +98,9 @@ void Translator::EmitVectorMemory(const GcnInst& inst) { // Buffer store operations case Opcode::IMAGE_STORE: - return IMAGE_STORE(inst); + return IMAGE_STORE(false, inst); + case Opcode::IMAGE_STORE_MIP: + return IMAGE_STORE(true, inst); // Image misc operations case Opcode::IMAGE_GET_RESINFO: @@ -423,7 +425,7 @@ void Translator::IMAGE_LOAD(bool has_mip, const GcnInst& inst) { } } -void Translator::IMAGE_STORE(const GcnInst& inst) { +void Translator::IMAGE_STORE(bool has_mip, const GcnInst& inst) { const auto& mimg = inst.control.mimg; IR::VectorReg addr_reg{inst.src[0].code}; IR::VectorReg data_reg{inst.dst[0].code}; @@ -434,6 +436,9 @@ void Translator::IMAGE_STORE(const GcnInst& inst) { ir.CompositeConstruct(ir.GetVectorReg(addr_reg), ir.GetVectorReg(addr_reg + 1), ir.GetVectorReg(addr_reg + 2), ir.GetVectorReg(addr_reg + 3)); + IR::TextureInstInfo info{}; + info.has_lod.Assign(has_mip); + boost::container::static_vector comps; for (u32 i = 0; i < 4; i++) { if (((mimg.dmask >> i) & 1) == 0) { @@ -443,7 +448,7 @@ void Translator::IMAGE_STORE(const GcnInst& inst) { comps.push_back(ir.GetVectorReg(data_reg++)); } const IR::Value value = ir.CompositeConstruct(comps[0], comps[1], comps[2], comps[3]); - ir.ImageWrite(handle, body, value, {}); + ir.ImageWrite(handle, body, {}, value, info); } void Translator::IMAGE_GET_RESINFO(const GcnInst& inst) { diff --git a/src/shader_recompiler/ir/ir_emitter.cpp b/src/shader_recompiler/ir/ir_emitter.cpp index 29b406699..3ebc82e64 100644 --- a/src/shader_recompiler/ir/ir_emitter.cpp +++ b/src/shader_recompiler/ir/ir_emitter.cpp @@ -1599,9 +1599,9 @@ Value IREmitter::ImageGatherDref(const Value& handle, const Value& coords, const return Inst(Opcode::ImageGatherDref, Flags{info}, handle, coords, offset, dref); } -Value IREmitter::ImageFetch(const Value& handle, const Value& coords, const Value& offset, - const U32& lod, const U32& multisampling, TextureInstInfo info) { - return Inst(Opcode::ImageFetch, Flags{info}, handle, coords, offset, lod, multisampling); +Value IREmitter::ImageFetch(const Value& handle, const Value& coords, const U32& lod, + const Value& offset, const U32& multisampling, TextureInstInfo info) { + return Inst(Opcode::ImageFetch, Flags{info}, handle, coords, lod, offset, multisampling); } Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod, @@ -1625,13 +1625,14 @@ Value IREmitter::ImageGradient(const Value& handle, const Value& coords, offset, lod_clamp); } -Value IREmitter::ImageRead(const Value& handle, const Value& coords, TextureInstInfo info) { - return Inst(Opcode::ImageRead, Flags{info}, handle, coords); +Value IREmitter::ImageRead(const Value& handle, const Value& coords, const U32& lod, + TextureInstInfo info) { + return Inst(Opcode::ImageRead, Flags{info}, handle, coords, lod); } -void IREmitter::ImageWrite(const Value& handle, const Value& coords, const Value& color, - TextureInstInfo info) { - Inst(Opcode::ImageWrite, Flags{info}, handle, coords, color); +void IREmitter::ImageWrite(const Value& handle, const Value& coords, const U32& lod, + const Value& color, TextureInstInfo info) { + Inst(Opcode::ImageWrite, Flags{info}, handle, coords, lod, color); } // Debug print maps to SPIRV's NonSemantic DebugPrintf instruction diff --git a/src/shader_recompiler/ir/ir_emitter.h b/src/shader_recompiler/ir/ir_emitter.h index f77e22b82..068aba14d 100644 --- a/src/shader_recompiler/ir/ir_emitter.h +++ b/src/shader_recompiler/ir/ir_emitter.h @@ -314,14 +314,16 @@ public: TextureInstInfo info); [[nodiscard]] Value ImageGatherDref(const Value& handle, const Value& coords, const Value& offset, const F32& dref, TextureInstInfo info); - [[nodiscard]] Value ImageFetch(const Value& handle, const Value& coords, const Value& offset, - const U32& lod, const U32& multisampling, TextureInstInfo info); + [[nodiscard]] Value ImageFetch(const Value& handle, const Value& coords, const U32& lod, + const Value& offset, const U32& multisampling, + TextureInstInfo info); [[nodiscard]] Value ImageGradient(const Value& handle, const Value& coords, const Value& derivatives_dx, const Value& derivatives_dy, const Value& offset, const F32& lod_clamp, TextureInstInfo info); - [[nodiscard]] Value ImageRead(const Value& handle, const Value& coords, TextureInstInfo info); - void ImageWrite(const Value& handle, const Value& coords, const Value& color, + [[nodiscard]] Value ImageRead(const Value& handle, const Value& coords, const U32& lod, + TextureInstInfo info); + void ImageWrite(const Value& handle, const Value& coords, const U32& lod, const Value& color, TextureInstInfo info); void EmitVertex(); diff --git a/src/shader_recompiler/ir/opcodes.inc b/src/shader_recompiler/ir/opcodes.inc index 8f40ed985..477275824 100644 --- a/src/shader_recompiler/ir/opcodes.inc +++ b/src/shader_recompiler/ir/opcodes.inc @@ -334,12 +334,12 @@ OPCODE(ImageSampleDrefImplicitLod, F32x4, Opaq OPCODE(ImageSampleDrefExplicitLod, F32x4, Opaque, Opaque, F32, F32, Opaque, ) OPCODE(ImageGather, F32x4, Opaque, Opaque, Opaque, ) OPCODE(ImageGatherDref, F32x4, Opaque, Opaque, Opaque, F32, ) -OPCODE(ImageFetch, F32x4, Opaque, Opaque, Opaque, U32, Opaque, ) +OPCODE(ImageFetch, F32x4, Opaque, Opaque, U32, Opaque, Opaque, ) OPCODE(ImageQueryDimensions, U32x4, Opaque, U32, U1, ) OPCODE(ImageQueryLod, F32x4, Opaque, Opaque, ) OPCODE(ImageGradient, F32x4, Opaque, Opaque, Opaque, Opaque, Opaque, F32, ) -OPCODE(ImageRead, U32x4, Opaque, Opaque, ) -OPCODE(ImageWrite, Void, Opaque, Opaque, U32x4, ) +OPCODE(ImageRead, U32x4, Opaque, Opaque, U32, ) +OPCODE(ImageWrite, Void, Opaque, Opaque, U32, U32x4, ) // Image atomic operations OPCODE(ImageAtomicIAdd32, U32, Opaque, Opaque, U32, ) diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index 398579ad4..f436db07a 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -771,14 +771,16 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip inst.SetArg(1, coords); if (inst.GetOpcode() == IR::Opcode::ImageWrite) { - inst.SetArg(2, SwizzleVector(ir, image, inst.Arg(2))); + inst.SetArg(3, SwizzleVector(ir, image, inst.Arg(3))); } if (inst_info.has_lod) { - ASSERT(inst.GetOpcode() == IR::Opcode::ImageFetch); + ASSERT(inst.GetOpcode() == IR::Opcode::ImageFetch || + inst.GetOpcode() == IR::Opcode::ImageRead || + inst.GetOpcode() == IR::Opcode::ImageWrite); ASSERT(image.GetType() != AmdGpu::ImageType::Color2DMsaa && image.GetType() != AmdGpu::ImageType::Color2DMsaaArray); - inst.SetArg(3, arg); + inst.SetArg(2, arg); } else if (image.GetType() == AmdGpu::ImageType::Color2DMsaa || image.GetType() == AmdGpu::ImageType::Color2DMsaaArray) { inst.SetArg(4, arg); diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index 96c458d44..c00e37f9c 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h @@ -23,6 +23,7 @@ struct Profile { bool support_fp32_denorm_flush{}; bool support_explicit_workgroup_layout{}; bool support_legacy_vertex_attributes{}; + bool supports_image_load_store_lod{}; bool has_broken_spirv_clamp{}; bool lower_left_origin_mode{}; bool needs_manual_interpolation{}; diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 81784eb60..2f9695055 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -267,6 +267,7 @@ bool Instance::CreateDevice() { list_restart = add_extension(VK_EXT_PRIMITIVE_TOPOLOGY_LIST_RESTART_EXTENSION_NAME); maintenance5 = add_extension(VK_KHR_MAINTENANCE_5_EXTENSION_NAME); legacy_vertex_attributes = add_extension(VK_EXT_LEGACY_VERTEX_ATTRIBUTES_EXTENSION_NAME); + image_load_store_lod = add_extension(VK_AMD_SHADER_IMAGE_LOAD_STORE_LOD_EXTENSION_NAME); // These extensions are promoted by Vulkan 1.3, but for greater compatibility we use Vulkan 1.2 // with extensions. diff --git a/src/video_core/renderer_vulkan/vk_instance.h b/src/video_core/renderer_vulkan/vk_instance.h index 81303c9cc..2b4bd612f 100644 --- a/src/video_core/renderer_vulkan/vk_instance.h +++ b/src/video_core/renderer_vulkan/vk_instance.h @@ -158,6 +158,11 @@ public: return legacy_vertex_attributes; } + /// Returns true when VK_AMD_shader_image_load_store_lod is supported. + bool IsImageLoadStoreLodSupported() const { + return image_load_store_lod; + } + /// Returns true when geometry shaders are supported by the device bool IsGeometryStageSupported() const { return features.geometryShader; @@ -327,6 +332,7 @@ private: bool maintenance5{}; bool list_restart{}; bool legacy_vertex_attributes{}; + bool image_load_store_lod{}; u64 min_imported_host_pointer_alignment{}; u32 subgroup_size{}; bool tooling_info{}; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 0fa77e19b..ff27b742f 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -172,6 +172,7 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_, .support_fp32_denorm_flush = bool(vk12_props.shaderDenormFlushToZeroFloat32), .support_explicit_workgroup_layout = true, .support_legacy_vertex_attributes = instance_.IsLegacyVertexAttributesSupported(), + .supports_image_load_store_lod = instance_.IsImageLoadStoreLodSupported(), .needs_manual_interpolation = instance.IsFragmentShaderBarycentricSupported() && instance.GetDriverID() == vk::DriverId::eNvidiaProprietary, }; From 3e226225080cca81693e034fdb0f0d0b30b8d4dd Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sat, 14 Dec 2024 02:04:30 -0800 Subject: [PATCH 184/549] renderer_vulkan: Remove some fallbacks and misc format queries that are no longer needed. (#1773) --- .../renderer_vulkan/vk_instance.cpp | 26 +++---------------- src/video_core/renderer_vulkan/vk_instance.h | 4 --- src/video_core/texture_cache/image_view.cpp | 3 +-- 3 files changed, 5 insertions(+), 28 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 2f9695055..e844150b2 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -68,11 +68,10 @@ std::unordered_map GetFormatProperties( } // Other miscellaneous formats, e.g. for color buffers, swizzles, or compatibility static constexpr std::array misc_formats = { - vk::Format::eA2R10G10B10UnormPack32, vk::Format::eA8B8G8R8UnormPack32, - vk::Format::eA8B8G8R8SrgbPack32, vk::Format::eB8G8R8A8Unorm, - vk::Format::eB8G8R8A8Snorm, vk::Format::eB8G8R8A8Uint, - vk::Format::eB8G8R8A8Sint, vk::Format::eB8G8R8A8Srgb, - vk::Format::eR5G6B5UnormPack16, vk::Format::eD24UnormS8Uint, + vk::Format::eA2R10G10B10UnormPack32, + vk::Format::eB8G8R8A8Unorm, + vk::Format::eB8G8R8A8Srgb, + vk::Format::eD24UnormS8Uint, }; for (const auto& format : misc_formats) { if (!format_properties.contains(format)) { @@ -583,8 +582,6 @@ bool Instance::IsFormatSupported(const vk::Format format, static vk::Format GetAlternativeFormat(const vk::Format format) { switch (format) { - case vk::Format::eB5G6R5UnormPack16: - return vk::Format::eR5G6B5UnormPack16; case vk::Format::eD16UnormS8Uint: return vk::Format::eD24UnormS8Uint; default: @@ -604,19 +601,4 @@ vk::Format Instance::GetSupportedFormat(const vk::Format format, return format; } -vk::ComponentMapping Instance::GetSupportedComponentSwizzle( - const vk::Format format, const vk::ComponentMapping swizzle, - const vk::FormatFeatureFlags2 flags) const { - if (IsFormatSupported(format, flags)) [[likely]] { - return swizzle; - } - - vk::ComponentMapping supported_swizzle = swizzle; - if (format == vk::Format::eB5G6R5UnormPack16) { - // B5G6R5 -> R5G6B5 - std::swap(supported_swizzle.r, supported_swizzle.b); - } - return supported_swizzle; -} - } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_instance.h b/src/video_core/renderer_vulkan/vk_instance.h index 2b4bd612f..54a9b9873 100644 --- a/src/video_core/renderer_vulkan/vk_instance.h +++ b/src/video_core/renderer_vulkan/vk_instance.h @@ -33,10 +33,6 @@ public: [[nodiscard]] vk::Format GetSupportedFormat(vk::Format format, vk::FormatFeatureFlags2 flags) const; - /// Re-orders a component swizzle for format compatibility, if needed. - [[nodiscard]] vk::ComponentMapping GetSupportedComponentSwizzle( - vk::Format format, vk::ComponentMapping swizzle, vk::FormatFeatureFlags2 flags) const; - /// Returns the Vulkan instance vk::Instance GetInstance() const { return *instance; diff --git a/src/video_core/texture_cache/image_view.cpp b/src/video_core/texture_cache/image_view.cpp index cc467e9a4..41c45019e 100644 --- a/src/video_core/texture_cache/image_view.cpp +++ b/src/video_core/texture_cache/image_view.cpp @@ -141,8 +141,7 @@ ImageView::ImageView(const Vulkan::Instance& instance, const ImageViewInfo& info .image = image.image, .viewType = info.type, .format = instance.GetSupportedFormat(format, image.format_features), - .components = - instance.GetSupportedComponentSwizzle(format, info.mapping, image.format_features), + .components = info.mapping, .subresourceRange{ .aspectMask = aspect, .baseMipLevel = info.range.base.level, From 3c0c921ef5006f1d30eac356e72edb6140d1da1e Mon Sep 17 00:00:00 2001 From: baggins183 Date: Sat, 14 Dec 2024 02:56:17 -0800 Subject: [PATCH 185/549] Tessellation (#1528) * shader_recompiler: Tessellation WIP * fix compiler errors after merge DONT MERGE set log file to /dev/null DONT MERGE linux pthread bb fix save work DONT MERGE dump ir save more work fix mistake with ES shader skip list add input patch control points dynamic state random stuff * WIP Tessellation partial implementation. Squash commits * test: make local/tcs use attr arrays * attr arrays in TCS/TES * dont define empty attr arrays * switch to special opcodes for tess tcs/tes reads and tcs writes * impl tcs/tes read attr insts * rebase fix * save some work * save work probably broken and slow * put Vertex LogicalStage after TCS and TES to fix bindings * more refactors * refactor pattern matching and optimize modulos (disabled) * enable modulo opt * copyright * rebase fixes * remove some prints * remove some stuff * Add TCS/TES support for shader patching and use LogicalStage * refactor and handle wider DS instructions * get rid of GetAttributes for special tess constants reads. Immediately replace some upon seeing readconstbuffer. Gets rid of some extra passes over IR * stop relying on GNMX HsConstants struct. Change runtime_info.hs_info and some regs * delete some more stuff * update comments for current implementation * some cleanup * uint error * more cleanup * remove patch control points dynamic state (because runtime_info already depends on it) * fix potential problem with determining passthrough --------- Co-authored-by: IndecisiveTurtle <47210458+raphaelthegreat@users.noreply.github.com> --- CMakeLists.txt | 3 + src/core/debug_state.cpp | 9 +- src/core/debug_state.h | 20 +- src/core/devtools/widget/shader_list.cpp | 17 +- src/core/libraries/gnmdriver/gnmdriver.cpp | 7 +- .../backend/spirv/emit_spirv.cpp | 74 +- .../backend/spirv/emit_spirv_barriers.cpp | 13 +- .../spirv/emit_spirv_context_get_set.cpp | 146 +++- .../backend/spirv/emit_spirv_instructions.h | 9 +- .../backend/spirv/spirv_emit_context.cpp | 149 +++- .../backend/spirv/spirv_emit_context.h | 26 +- src/shader_recompiler/frontend/tessellation.h | 38 + .../frontend/translate/data_share.cpp | 9 +- .../frontend/translate/scalar_alu.cpp | 10 +- .../frontend/translate/translate.cpp | 41 +- .../frontend/translate/translate.h | 5 +- .../frontend/translate/vector_alu.cpp | 10 +- .../frontend/translate/vector_memory.cpp | 12 +- src/shader_recompiler/info.h | 22 +- src/shader_recompiler/ir/attribute.cpp | 12 + src/shader_recompiler/ir/attribute.h | 14 +- src/shader_recompiler/ir/basic_block.cpp | 2 + src/shader_recompiler/ir/ir_emitter.cpp | 35 +- src/shader_recompiler/ir/ir_emitter.h | 15 +- src/shader_recompiler/ir/microinstruction.cpp | 2 + src/shader_recompiler/ir/opcodes.h | 2 +- src/shader_recompiler/ir/opcodes.inc | 4 + .../ir/passes/constant_propagation_pass.cpp | 26 +- .../ir/passes/constant_propogation.h | 4 + .../ir/passes/hull_shader_transform.cpp | 744 ++++++++++++++++++ src/shader_recompiler/ir/passes/ir_passes.h | 3 + .../ir/passes/ring_access_elimination.cpp | 45 +- .../ir/passes/shader_info_collection_pass.cpp | 16 + src/shader_recompiler/ir/patch.cpp | 28 + src/shader_recompiler/ir/patch.h | 173 ++++ src/shader_recompiler/ir/pattern_matching.h | 127 +++ src/shader_recompiler/ir/reg.h | 3 +- src/shader_recompiler/ir/type.h | 2 +- src/shader_recompiler/ir/value.cpp | 2 + src/shader_recompiler/ir/value.h | 9 + src/shader_recompiler/recompiler.cpp | 26 +- src/shader_recompiler/recompiler.h | 2 +- src/shader_recompiler/runtime_info.h | 78 +- src/shader_recompiler/specialization.h | 12 + src/video_core/amdgpu/liverpool.h | 49 +- src/video_core/amdgpu/types.h | 95 +++ .../renderer_vulkan/vk_compute_pipeline.cpp | 2 +- .../renderer_vulkan/vk_graphics_pipeline.cpp | 32 +- .../renderer_vulkan/vk_graphics_pipeline.h | 3 +- .../renderer_vulkan/vk_instance.cpp | 2 + .../renderer_vulkan/vk_pipeline_cache.cpp | 105 ++- .../renderer_vulkan/vk_pipeline_cache.h | 18 +- .../renderer_vulkan/vk_pipeline_common.h | 10 +- .../renderer_vulkan/vk_rasterizer.cpp | 13 +- 54 files changed, 2146 insertions(+), 189 deletions(-) create mode 100644 src/shader_recompiler/frontend/tessellation.h create mode 100644 src/shader_recompiler/ir/passes/constant_propogation.h create mode 100644 src/shader_recompiler/ir/passes/hull_shader_transform.cpp create mode 100644 src/shader_recompiler/ir/patch.cpp create mode 100644 src/shader_recompiler/ir/patch.h create mode 100644 src/shader_recompiler/ir/pattern_matching.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e54f7a00..78d8421a3 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -664,6 +664,7 @@ set(SHADER_RECOMPILER src/shader_recompiler/exception.h src/shader_recompiler/ir/passes/constant_propagation_pass.cpp src/shader_recompiler/ir/passes/dead_code_elimination_pass.cpp src/shader_recompiler/ir/passes/flatten_extended_userdata_pass.cpp + src/shader_recompiler/ir/passes/hull_shader_transform.cpp src/shader_recompiler/ir/passes/identity_removal_pass.cpp src/shader_recompiler/ir/passes/ir_passes.h src/shader_recompiler/ir/passes/lower_shared_mem_to_registers.cpp @@ -683,6 +684,8 @@ set(SHADER_RECOMPILER src/shader_recompiler/exception.h src/shader_recompiler/ir/opcodes.cpp src/shader_recompiler/ir/opcodes.h src/shader_recompiler/ir/opcodes.inc + src/shader_recompiler/ir/patch.cpp + src/shader_recompiler/ir/patch.h src/shader_recompiler/ir/post_order.cpp src/shader_recompiler/ir/post_order.h src/shader_recompiler/ir/program.cpp diff --git a/src/core/debug_state.cpp b/src/core/debug_state.cpp index 649624924..c68fd469d 100644 --- a/src/core/debug_state.cpp +++ b/src/core/debug_state.cpp @@ -177,10 +177,11 @@ void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr, } } -void DebugStateImpl::CollectShader(const std::string& name, vk::ShaderModule module, - std::span spv, std::span raw_code, - std::span patch_spv, bool is_patched) { - shader_dump_list.emplace_back(name, module, std::vector{spv.begin(), spv.end()}, +void DebugStateImpl::CollectShader(const std::string& name, Shader::LogicalStage l_stage, + vk::ShaderModule module, std::span spv, + std::span raw_code, std::span patch_spv, + bool is_patched) { + shader_dump_list.emplace_back(name, l_stage, module, std::vector{spv.begin(), spv.end()}, std::vector{raw_code.begin(), raw_code.end()}, std::vector{patch_spv.begin(), patch_spv.end()}, is_patched); } diff --git a/src/core/debug_state.h b/src/core/debug_state.h index fa2e5cd9d..0db5bc468 100644 --- a/src/core/debug_state.h +++ b/src/core/debug_state.h @@ -76,6 +76,7 @@ struct FrameDump { struct ShaderDump { std::string name; + Shader::LogicalStage l_stage; vk::ShaderModule module; std::vector spv; @@ -90,16 +91,17 @@ struct ShaderDump { std::string cache_isa_disasm{}; std::string cache_patch_disasm{}; - ShaderDump(std::string name, vk::ShaderModule module, std::vector spv, - std::vector isa, std::vector patch_spv, bool is_patched) - : name(std::move(name)), module(module), spv(std::move(spv)), isa(std::move(isa)), - patch_spv(std::move(patch_spv)), is_patched(is_patched) {} + ShaderDump(std::string name, Shader::LogicalStage l_stage, vk::ShaderModule module, + std::vector spv, std::vector isa, std::vector patch_spv, + bool is_patched) + : name(std::move(name)), l_stage(l_stage), module(module), spv(std::move(spv)), + isa(std::move(isa)), patch_spv(std::move(patch_spv)), is_patched(is_patched) {} ShaderDump(const ShaderDump& other) = delete; ShaderDump(ShaderDump&& other) noexcept - : name{std::move(other.name)}, module{std::move(other.module)}, spv{std::move(other.spv)}, - isa{std::move(other.isa)}, patch_spv{std::move(other.patch_spv)}, - patch_source{std::move(other.patch_source)}, + : name{std::move(other.name)}, l_stage(other.l_stage), module{std::move(other.module)}, + spv{std::move(other.spv)}, isa{std::move(other.isa)}, + patch_spv{std::move(other.patch_spv)}, patch_source{std::move(other.patch_source)}, cache_spv_disasm{std::move(other.cache_spv_disasm)}, cache_isa_disasm{std::move(other.cache_isa_disasm)}, cache_patch_disasm{std::move(other.cache_patch_disasm)} {} @@ -108,6 +110,7 @@ struct ShaderDump { if (this == &other) return *this; name = std::move(other.name); + l_stage = other.l_stage; module = std::move(other.module); spv = std::move(other.spv); isa = std::move(other.isa); @@ -203,7 +206,8 @@ public: void PushRegsDump(uintptr_t base_addr, uintptr_t header_addr, const AmdGpu::Liverpool::Regs& regs, bool is_compute = false); - void CollectShader(const std::string& name, vk::ShaderModule module, std::span spv, + void CollectShader(const std::string& name, Shader::LogicalStage l_stage, + vk::ShaderModule module, std::span spv, std::span raw_code, std::span patch_spv, bool is_patched); }; diff --git a/src/core/devtools/widget/shader_list.cpp b/src/core/devtools/widget/shader_list.cpp index 80c939718..2c97db7fd 100644 --- a/src/core/devtools/widget/shader_list.cpp +++ b/src/core/devtools/widget/shader_list.cpp @@ -158,16 +158,17 @@ bool ShaderList::Selection::DrawShader(DebugStateType::ShaderDump& value) { DebugState.ShowDebugMessage(msg); } if (compile) { - static std::map stage_arg = { - {"vs", "vert"}, - {"gs", "geom"}, - {"fs", "frag"}, - {"cs", "comp"}, + static std::map stage_arg = { + {Shader::LogicalStage::Vertex, "vert"}, + {Shader::LogicalStage::TessellationControl, "tesc"}, + {Shader::LogicalStage::TessellationEval, "tese"}, + {Shader::LogicalStage::Geometry, "geom"}, + {Shader::LogicalStage::Fragment, "frag"}, + {Shader::LogicalStage::Compute, "comp"}, }; - auto stage = stage_arg.find(value.name.substr(0, 2)); + auto stage = stage_arg.find(value.l_stage); if (stage == stage_arg.end()) { - DebugState.ShowDebugMessage(std::string{"Invalid shader stage: "} + - value.name.substr(0, 2)); + DebugState.ShowDebugMessage(std::string{"Invalid shader stage"}); } else { std::string cmd = fmt::format("glslc --target-env=vulkan1.3 --target-spv=spv1.6 " diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index dbf085fb3..e85b8b890 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -1642,7 +1642,6 @@ s32 PS4_SYSV_ABI sceGnmSetGsShader(u32* cmdbuf, u32 size, const u32* gs_regs) { s32 PS4_SYSV_ABI sceGnmSetHsShader(u32* cmdbuf, u32 size, const u32* hs_regs, u32 param4) { LOG_TRACE(Lib_GnmDriver, "called"); - if (!cmdbuf || size < 0x1E) { return -1; } @@ -1660,11 +1659,13 @@ s32 PS4_SYSV_ABI sceGnmSetHsShader(u32* cmdbuf, u32 size, const u32* hs_regs, u3 cmdbuf = PM4CmdSetData::SetShReg(cmdbuf, 0x108u, hs_regs[0], 0u); // SPI_SHADER_PGM_LO_HS cmdbuf = PM4CmdSetData::SetShReg(cmdbuf, 0x10au, hs_regs[2], hs_regs[3]); // SPI_SHADER_PGM_RSRC1_HS/SPI_SHADER_PGM_RSRC2_HS - cmdbuf = PM4CmdSetData::SetContextReg(cmdbuf, 0x286u, hs_regs[5], - hs_regs[5]); // VGT_HOS_MAX_TESS_LEVEL + cmdbuf = PM4CmdSetData::SetContextReg(cmdbuf, 0x286u, + hs_regs[5], // VGT_HOS_MAX_TESS_LEVEL + hs_regs[6]); // VGT_HOS_MIN_TESS_LEVEL cmdbuf = PM4CmdSetData::SetContextReg(cmdbuf, 0x2dbu, hs_regs[4]); // VGT_TF_PARAM cmdbuf = PM4CmdSetData::SetContextReg(cmdbuf, 0x2d6u, param4); // VGT_LS_HS_CONFIG + // right padding? WriteTrailingNop<11>(cmdbuf); return ORBIS_OK; } diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index ab9d6afae..e545e8e36 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -1,6 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later - #include #include #include @@ -13,6 +12,7 @@ #include "shader_recompiler/frontend/translate/translate.h" #include "shader_recompiler/ir/basic_block.h" #include "shader_recompiler/ir/program.h" +#include "shader_recompiler/runtime_info.h" #include "video_core/amdgpu/types.h" namespace Shader::Backend::SPIRV { @@ -72,7 +72,10 @@ ArgType Arg(EmitContext& ctx, const IR::Value& arg) { return arg.VectorReg(); } else if constexpr (std::is_same_v) { return arg.StringLiteral(); + } else if constexpr (std::is_same_v) { + return arg.Patch(); } + UNREACHABLE(); } template @@ -206,6 +209,32 @@ Id DefineMain(EmitContext& ctx, const IR::Program& program) { return main; } +spv::ExecutionMode ExecutionMode(AmdGpu::TessellationType primitive) { + switch (primitive) { + case AmdGpu::TessellationType::Isoline: + return spv::ExecutionMode::Isolines; + case AmdGpu::TessellationType::Triangle: + return spv::ExecutionMode::Triangles; + case AmdGpu::TessellationType::Quad: + return spv::ExecutionMode::Quads; + } + UNREACHABLE_MSG("Tessellation primitive {}", primitive); +} + +spv::ExecutionMode ExecutionMode(AmdGpu::TessellationPartitioning spacing) { + switch (spacing) { + case AmdGpu::TessellationPartitioning::Integer: + return spv::ExecutionMode::SpacingEqual; + case AmdGpu::TessellationPartitioning::FracOdd: + return spv::ExecutionMode::SpacingFractionalOdd; + case AmdGpu::TessellationPartitioning::FracEven: + return spv::ExecutionMode::SpacingFractionalEven; + default: + break; + } + UNREACHABLE_MSG("Tessellation spacing {}", spacing); +} + void SetupCapabilities(const Info& info, const Profile& profile, EmitContext& ctx) { ctx.AddCapability(spv::Capability::Image1D); ctx.AddCapability(spv::Capability::Sampled1D); @@ -248,36 +277,55 @@ void SetupCapabilities(const Info& info, const Profile& profile, EmitContext& ct if (info.uses_group_ballot) { ctx.AddCapability(spv::Capability::GroupNonUniformBallot); } - if (info.stage == Stage::Export || info.stage == Stage::Vertex) { + const auto stage = info.l_stage; + if (stage == LogicalStage::Vertex) { ctx.AddExtension("SPV_KHR_shader_draw_parameters"); ctx.AddCapability(spv::Capability::DrawParameters); } - if (info.stage == Stage::Geometry) { + if (stage == LogicalStage::Geometry) { ctx.AddCapability(spv::Capability::Geometry); } if (info.stage == Stage::Fragment && profile.needs_manual_interpolation) { ctx.AddExtension("SPV_KHR_fragment_shader_barycentric"); ctx.AddCapability(spv::Capability::FragmentBarycentricKHR); } + if (stage == LogicalStage::TessellationControl || stage == LogicalStage::TessellationEval) { + ctx.AddCapability(spv::Capability::Tessellation); + } } -void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) { - const auto& info = program.info; +void DefineEntryPoint(const Info& info, EmitContext& ctx, Id main) { const std::span interfaces(ctx.interfaces.data(), ctx.interfaces.size()); spv::ExecutionModel execution_model{}; - switch (program.info.stage) { - case Stage::Compute: { + switch (info.l_stage) { + case LogicalStage::Compute: { const std::array workgroup_size{ctx.runtime_info.cs_info.workgroup_size}; execution_model = spv::ExecutionModel::GLCompute; ctx.AddExecutionMode(main, spv::ExecutionMode::LocalSize, workgroup_size[0], workgroup_size[1], workgroup_size[2]); break; } - case Stage::Export: - case Stage::Vertex: + case LogicalStage::Vertex: execution_model = spv::ExecutionModel::Vertex; break; - case Stage::Fragment: + case LogicalStage::TessellationControl: + execution_model = spv::ExecutionModel::TessellationControl; + ctx.AddCapability(spv::Capability::Tessellation); + ctx.AddExecutionMode(main, spv::ExecutionMode::OutputVertices, + ctx.runtime_info.hs_info.NumOutputControlPoints()); + break; + case LogicalStage::TessellationEval: { + execution_model = spv::ExecutionModel::TessellationEvaluation; + const auto& vs_info = ctx.runtime_info.vs_info; + ctx.AddExecutionMode(main, ExecutionMode(vs_info.tess_type)); + ctx.AddExecutionMode(main, ExecutionMode(vs_info.tess_partitioning)); + ctx.AddExecutionMode(main, + vs_info.tess_topology == AmdGpu::TessellationTopology::TriangleCcw + ? spv::ExecutionMode::VertexOrderCcw + : spv::ExecutionMode::VertexOrderCw); + break; + } + case LogicalStage::Fragment: execution_model = spv::ExecutionModel::Fragment; if (ctx.profile.lower_left_origin_mode) { ctx.AddExecutionMode(main, spv::ExecutionMode::OriginLowerLeft); @@ -292,7 +340,7 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) { ctx.AddExecutionMode(main, spv::ExecutionMode::DepthReplacing); } break; - case Stage::Geometry: + case LogicalStage::Geometry: execution_model = spv::ExecutionModel::Geometry; ctx.AddExecutionMode(main, GetInputPrimitiveType(ctx.runtime_info.gs_info.in_primitive)); ctx.AddExecutionMode(main, @@ -303,7 +351,7 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) { ctx.runtime_info.gs_info.num_invocations); break; default: - throw NotImplementedException("Stage {}", u32(program.info.stage)); + UNREACHABLE_MSG("Stage {}", u32(info.stage)); } ctx.AddEntryPoint(execution_model, main, "main", interfaces); } @@ -349,7 +397,7 @@ std::vector EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_in const IR::Program& program, Bindings& binding) { EmitContext ctx{profile, runtime_info, program.info, binding}; const Id main{DefineMain(ctx, program)}; - DefineEntryPoint(program, ctx, main); + DefineEntryPoint(program.info, ctx, main); SetupCapabilities(program.info, profile, ctx); SetupFloatMode(ctx, profile, runtime_info, main); PatchPhiNodes(program, ctx); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp index 22b3523aa..611225e8b 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp @@ -18,9 +18,16 @@ void MemoryBarrier(EmitContext& ctx, spv::Scope scope) { void EmitBarrier(EmitContext& ctx) { const auto execution{spv::Scope::Workgroup}; - const auto memory{spv::Scope::Workgroup}; - const auto memory_semantics{spv::MemorySemanticsMask::AcquireRelease | - spv::MemorySemanticsMask::WorkgroupMemory}; + spv::Scope memory; + spv::MemorySemanticsMask memory_semantics; + if (ctx.l_stage == Shader::LogicalStage::TessellationControl) { + memory = spv::Scope::Invocation; + memory_semantics = spv::MemorySemanticsMask::MaskNone; + } else { + memory = spv::Scope::Workgroup; + memory_semantics = + spv::MemorySemanticsMask::AcquireRelease | spv::MemorySemanticsMask::WorkgroupMemory; + } ctx.OpControlBarrier(ctx.ConstU32(static_cast(execution)), ctx.ConstU32(static_cast(memory)), ctx.ConstU32(static_cast(memory_semantics))); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp index d005169c4..f3db6af56 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp @@ -4,6 +4,9 @@ #include "common/assert.h" #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" #include "shader_recompiler/backend/spirv/spirv_emit_context.h" +#include "shader_recompiler/ir/attribute.h" +#include "shader_recompiler/ir/patch.h" +#include "shader_recompiler/runtime_info.h" #include @@ -45,13 +48,19 @@ Id VsOutputAttrPointer(EmitContext& ctx, VsOutput output) { Id OutputAttrPointer(EmitContext& ctx, IR::Attribute attr, u32 element) { if (IR::IsParam(attr)) { - const u32 index{u32(attr) - u32(IR::Attribute::Param0)}; - const auto& info{ctx.output_params.at(index)}; - ASSERT(info.num_components > 0); - if (info.num_components == 1) { - return info.id; + const u32 attr_index{u32(attr) - u32(IR::Attribute::Param0)}; + if (ctx.stage == Stage::Local && ctx.runtime_info.ls_info.links_with_tcs) { + const auto component_ptr = ctx.TypePointer(spv::StorageClass::Output, ctx.F32[1]); + return ctx.OpAccessChain(component_ptr, ctx.output_attr_array, ctx.ConstU32(attr_index), + ctx.ConstU32(element)); } else { - return ctx.OpAccessChain(info.pointer_type, info.id, ctx.ConstU32(element)); + const auto& info{ctx.output_params.at(attr_index)}; + ASSERT(info.num_components > 0); + if (info.num_components == 1) { + return info.id; + } else { + return ctx.OpAccessChain(info.pointer_type, info.id, ctx.ConstU32(element)); + } } } if (IR::IsMrt(attr)) { @@ -82,9 +91,13 @@ Id OutputAttrPointer(EmitContext& ctx, IR::Attribute attr, u32 element) { std::pair OutputAttrComponentType(EmitContext& ctx, IR::Attribute attr) { if (IR::IsParam(attr)) { - const u32 index{u32(attr) - u32(IR::Attribute::Param0)}; - const auto& info{ctx.output_params.at(index)}; - return {info.component_type, info.is_integer}; + if (ctx.stage == Stage::Local && ctx.runtime_info.ls_info.links_with_tcs) { + return {ctx.F32[1], false}; + } else { + const u32 index{u32(attr) - u32(IR::Attribute::Param0)}; + const auto& info{ctx.output_params.at(index)}; + return {info.component_type, info.is_integer}; + } } if (IR::IsMrt(attr)) { const u32 index{u32(attr) - u32(IR::Attribute::RenderTarget0)}; @@ -171,12 +184,11 @@ Id EmitReadStepRate(EmitContext& ctx, int rate_idx) { rate_idx == 0 ? ctx.u32_zero_value : ctx.u32_one_value)); } -Id EmitGetAttributeForGeometry(EmitContext& ctx, IR::Attribute attr, u32 comp, u32 index) { +Id EmitGetAttributeForGeometry(EmitContext& ctx, IR::Attribute attr, u32 comp, Id index) { if (IR::IsPosition(attr)) { ASSERT(attr == IR::Attribute::Position0); const auto position_arr_ptr = ctx.TypePointer(spv::StorageClass::Input, ctx.F32[4]); - const auto pointer{ - ctx.OpAccessChain(position_arr_ptr, ctx.gl_in, ctx.ConstU32(index), ctx.ConstU32(0u))}; + const auto pointer{ctx.OpAccessChain(position_arr_ptr, ctx.gl_in, index, ctx.ConstU32(0u))}; const auto position_comp_ptr = ctx.TypePointer(spv::StorageClass::Input, ctx.F32[1]); return ctx.OpLoad(ctx.F32[1], ctx.OpAccessChain(position_comp_ptr, pointer, ctx.ConstU32(comp))); @@ -186,7 +198,7 @@ Id EmitGetAttributeForGeometry(EmitContext& ctx, IR::Attribute attr, u32 comp, u const u32 param_id{u32(attr) - u32(IR::Attribute::Param0)}; const auto param = ctx.input_params.at(param_id).id; const auto param_arr_ptr = ctx.TypePointer(spv::StorageClass::Input, ctx.F32[4]); - const auto pointer{ctx.OpAccessChain(param_arr_ptr, param, ctx.ConstU32(index))}; + const auto pointer{ctx.OpAccessChain(param_arr_ptr, param, index)}; const auto position_comp_ptr = ctx.TypePointer(spv::StorageClass::Input, ctx.F32[1]); return ctx.OpLoad(ctx.F32[1], ctx.OpAccessChain(position_comp_ptr, pointer, ctx.ConstU32(comp))); @@ -194,9 +206,27 @@ Id EmitGetAttributeForGeometry(EmitContext& ctx, IR::Attribute attr, u32 comp, u UNREACHABLE(); } -Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, u32 comp, u32 index) { - if (ctx.info.stage == Stage::Geometry) { +Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, u32 comp, Id index) { + if (ctx.info.l_stage == LogicalStage::Geometry) { return EmitGetAttributeForGeometry(ctx, attr, comp, index); + } else if (ctx.info.l_stage == LogicalStage::TessellationControl || + ctx.info.l_stage == LogicalStage::TessellationEval) { + if (IR::IsTessCoord(attr)) { + const u32 component = attr == IR::Attribute::TessellationEvaluationPointU ? 0 : 1; + const auto component_ptr = ctx.TypePointer(spv::StorageClass::Input, ctx.F32[1]); + const auto pointer{ + ctx.OpAccessChain(component_ptr, ctx.tess_coord, ctx.ConstU32(component))}; + return ctx.OpLoad(ctx.F32[1], pointer); + } else if (IR::IsParam(attr)) { + const u32 param_id{u32(attr) - u32(IR::Attribute::Param0)}; + const auto param = ctx.input_params.at(param_id).id; + const auto param_arr_ptr = ctx.TypePointer(spv::StorageClass::Input, ctx.F32[4]); + const auto pointer{ctx.OpAccessChain(param_arr_ptr, param, index)}; + const auto position_comp_ptr = ctx.TypePointer(spv::StorageClass::Input, ctx.F32[1]); + return ctx.OpLoad(ctx.F32[1], + ctx.OpAccessChain(position_comp_ptr, pointer, ctx.ConstU32(comp))); + } + UNREACHABLE(); } if (IR::IsParam(attr)) { @@ -242,8 +272,14 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, u32 comp, u32 index) { } return coord; } + case IR::Attribute::TessellationEvaluationPointU: + return ctx.OpLoad(ctx.F32[1], + ctx.OpAccessChain(ctx.input_f32, ctx.tess_coord, ctx.u32_zero_value)); + case IR::Attribute::TessellationEvaluationPointV: + return ctx.OpLoad(ctx.F32[1], + ctx.OpAccessChain(ctx.input_f32, ctx.tess_coord, ctx.ConstU32(1U))); default: - throw NotImplementedException("Read attribute {}", attr); + UNREACHABLE_MSG("Read attribute {}", attr); } } @@ -266,10 +302,32 @@ Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, u32 comp) { return ctx.OpSelect(ctx.U32[1], ctx.OpLoad(ctx.U1[1], ctx.front_facing), ctx.u32_one_value, ctx.u32_zero_value); case IR::Attribute::PrimitiveId: - ASSERT(ctx.info.stage == Stage::Geometry); return ctx.OpLoad(ctx.U32[1], ctx.primitive_id); + case IR::Attribute::InvocationId: + ASSERT(ctx.info.l_stage == LogicalStage::Geometry || + ctx.info.l_stage == LogicalStage::TessellationControl); + return ctx.OpLoad(ctx.U32[1], ctx.invocation_id); + case IR::Attribute::PatchVertices: + ASSERT(ctx.info.l_stage == LogicalStage::TessellationControl); + return ctx.OpLoad(ctx.U32[1], ctx.patch_vertices); + case IR::Attribute::PackedHullInvocationInfo: { + ASSERT(ctx.info.l_stage == LogicalStage::TessellationControl); + // [0:8]: patch id within VGT + // [8:12]: output control point id + // But 0:8 should be treated as 0 for attribute addressing purposes + if (ctx.runtime_info.hs_info.IsPassthrough()) { + // Gcn shader would run with 1 thread, but we need to run a thread for + // each output control point. + // If Gcn shader uses this value, we should make sure all threads in the + // Vulkan shader use 0 + return ctx.ConstU32(0u); + } else { + const Id invocation_id = ctx.OpLoad(ctx.U32[1], ctx.invocation_id); + return ctx.OpShiftLeftLogical(ctx.U32[1], invocation_id, ctx.ConstU32(8u)); + } + } default: - throw NotImplementedException("Read U32 attribute {}", attr); + UNREACHABLE_MSG("Read U32 attribute {}", attr); } } @@ -287,6 +345,58 @@ void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, u32 elemen } } +Id EmitGetTessGenericAttribute(EmitContext& ctx, Id vertex_index, Id attr_index, Id comp_index) { + const auto attr_comp_ptr = ctx.TypePointer(spv::StorageClass::Input, ctx.F32[1]); + return ctx.OpLoad(ctx.F32[1], ctx.OpAccessChain(attr_comp_ptr, ctx.input_attr_array, + vertex_index, attr_index, comp_index)); +} + +void EmitSetTcsGenericAttribute(EmitContext& ctx, Id value, Id attr_index, Id comp_index) { + // Implied vertex index is invocation_id + const auto component_ptr = ctx.TypePointer(spv::StorageClass::Output, ctx.F32[1]); + Id pointer = + ctx.OpAccessChain(component_ptr, ctx.output_attr_array, + ctx.OpLoad(ctx.U32[1], ctx.invocation_id), attr_index, comp_index); + ctx.OpStore(pointer, value); +} + +Id EmitGetPatch(EmitContext& ctx, IR::Patch patch) { + const u32 index{IR::GenericPatchIndex(patch)}; + const Id element{ctx.ConstU32(IR::GenericPatchElement(patch))}; + const Id type{ctx.l_stage == LogicalStage::TessellationControl ? ctx.output_f32 + : ctx.input_f32}; + const Id pointer{ctx.OpAccessChain(type, ctx.patches.at(index), element)}; + return ctx.OpLoad(ctx.F32[1], pointer); +} + +void EmitSetPatch(EmitContext& ctx, IR::Patch patch, Id value) { + const Id pointer{[&] { + if (IR::IsGeneric(patch)) { + const u32 index{IR::GenericPatchIndex(patch)}; + const Id element{ctx.ConstU32(IR::GenericPatchElement(patch))}; + return ctx.OpAccessChain(ctx.output_f32, ctx.patches.at(index), element); + } + switch (patch) { + case IR::Patch::TessellationLodLeft: + case IR::Patch::TessellationLodRight: + case IR::Patch::TessellationLodTop: + case IR::Patch::TessellationLodBottom: { + const u32 index{static_cast(patch) - u32(IR::Patch::TessellationLodLeft)}; + const Id index_id{ctx.ConstU32(index)}; + return ctx.OpAccessChain(ctx.output_f32, ctx.output_tess_level_outer, index_id); + } + case IR::Patch::TessellationLodInteriorU: + return ctx.OpAccessChain(ctx.output_f32, ctx.output_tess_level_inner, + ctx.u32_zero_value); + case IR::Patch::TessellationLodInteriorV: + return ctx.OpAccessChain(ctx.output_f32, ctx.output_tess_level_inner, ctx.ConstU32(1u)); + default: + UNREACHABLE_MSG("Patch {}", u32(patch)); + } + }()}; + ctx.OpStore(pointer, value); +} + template static Id EmitLoadBufferU32xN(EmitContext& ctx, u32 handle, Id address) { auto& buffer = ctx.buffers[handle]; diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index 057b0d692..f71c61af6 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h @@ -9,6 +9,7 @@ namespace Shader::IR { enum class Attribute : u64; enum class ScalarReg : u32; +enum class Patch : u64; class Inst; class Value; } // namespace Shader::IR @@ -27,8 +28,6 @@ Id EmitConditionRef(EmitContext& ctx, const IR::Value& value); void EmitReference(EmitContext&); void EmitPhiMove(EmitContext&); void EmitJoin(EmitContext& ctx); -void EmitWorkgroupMemoryBarrier(EmitContext& ctx); -void EmitDeviceMemoryBarrier(EmitContext& ctx); void EmitGetScc(EmitContext& ctx); void EmitGetExec(EmitContext& ctx); void EmitGetVcc(EmitContext& ctx); @@ -85,9 +84,13 @@ Id EmitBufferAtomicAnd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id addres Id EmitBufferAtomicOr32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value); Id EmitBufferAtomicXor32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value); Id EmitBufferAtomicSwap32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value); -Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, u32 comp, u32 index); +Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, u32 comp, Id index); Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, u32 comp); void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, u32 comp); +Id EmitGetTessGenericAttribute(EmitContext& ctx, Id vertex_index, Id attr_index, Id comp_index); +void EmitSetTcsGenericAttribute(EmitContext& ctx, Id value, Id attr_index, Id comp_index); +Id EmitGetPatch(EmitContext& ctx, IR::Patch patch); +void EmitSetPatch(EmitContext& ctx, IR::Patch patch, Id value); void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, Id value); void EmitSetSampleMask(EmitContext& ctx, Id value); void EmitSetFragDepth(EmitContext& ctx, Id value); diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 1ada2f1f9..2e09e70a7 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -6,6 +6,7 @@ #include "shader_recompiler/backend/spirv/spirv_emit_context.h" #include "shader_recompiler/frontend/fetch_shader.h" #include "shader_recompiler/ir/passes/srt.h" +#include "shader_recompiler/runtime_info.h" #include "video_core/amdgpu/types.h" #include @@ -34,7 +35,7 @@ std::string_view StageName(Stage stage) { case Stage::Compute: return "cs"; } - throw InvalidArgument("Invalid stage {}", u32(stage)); + UNREACHABLE_MSG("Invalid hw stage {}", u32(stage)); } static constexpr u32 NumVertices(AmdGpu::PrimitiveType type) { @@ -65,7 +66,7 @@ void Name(EmitContext& ctx, Id object, std::string_view format_str, Args&&... ar EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_info_, const Info& info_, Bindings& binding_) : Sirit::Module(profile_.supported_spirv), info{info_}, runtime_info{runtime_info_}, - profile{profile_}, stage{info.stage}, binding{binding_} { + profile{profile_}, stage{info.stage}, l_stage{info.l_stage}, binding{binding_} { AddCapability(spv::Capability::Shader); DefineArithmeticTypes(); DefineInterfaces(); @@ -268,9 +269,8 @@ void EmitContext::DefineInputs() { U32[1], spv::BuiltIn::SubgroupLocalInvocationId, spv::StorageClass::Input); Decorate(subgroup_local_invocation_id, spv::Decoration::Flat); } - switch (stage) { - case Stage::Export: - case Stage::Vertex: { + switch (l_stage) { + case LogicalStage::Vertex: { vertex_index = DefineVariable(U32[1], spv::BuiltIn::VertexIndex, spv::StorageClass::Input); base_vertex = DefineVariable(U32[1], spv::BuiltIn::BaseVertex, spv::StorageClass::Input); instance_id = DefineVariable(U32[1], spv::BuiltIn::InstanceIndex, spv::StorageClass::Input); @@ -311,12 +311,11 @@ void EmitContext::DefineInputs() { } input_params[attrib.semantic] = GetAttributeInfo(sharp.GetNumberFmt(), id, 4, false); - interfaces.push_back(id); } } break; } - case Stage::Fragment: + case LogicalStage::Fragment: frag_coord = DefineVariable(F32[4], spv::BuiltIn::FragCoord, spv::StorageClass::Input); frag_depth = DefineVariable(F32[1], spv::BuiltIn::FragDepth, spv::StorageClass::Output); front_facing = DefineVariable(U1[1], spv::BuiltIn::FrontFacing, spv::StorageClass::Input); @@ -351,15 +350,14 @@ void EmitContext::DefineInputs() { } input_params[semantic] = GetAttributeInfo(AmdGpu::NumberFormat::Float, attr_id, num_components, false); - interfaces.push_back(attr_id); } break; - case Stage::Compute: + case LogicalStage::Compute: workgroup_id = DefineVariable(U32[3], spv::BuiltIn::WorkgroupId, spv::StorageClass::Input); local_invocation_id = DefineVariable(U32[3], spv::BuiltIn::LocalInvocationId, spv::StorageClass::Input); break; - case Stage::Geometry: { + case LogicalStage::Geometry: { primitive_id = DefineVariable(U32[1], spv::BuiltIn::PrimitiveId, spv::StorageClass::Input); const auto gl_per_vertex = Name(TypeStruct(TypeVector(F32[1], 4), F32[1], TypeArray(F32[1], ConstU32(1u))), @@ -389,15 +387,129 @@ void EmitContext::DefineInputs() { } break; } + case LogicalStage::TessellationControl: { + invocation_id = + DefineVariable(U32[1], spv::BuiltIn::InvocationId, spv::StorageClass::Input); + patch_vertices = + DefineVariable(U32[1], spv::BuiltIn::PatchVertices, spv::StorageClass::Input); + primitive_id = DefineVariable(U32[1], spv::BuiltIn::PrimitiveId, spv::StorageClass::Input); + + const u32 num_attrs = runtime_info.hs_info.ls_stride >> 4; + if (num_attrs > 0) { + const Id per_vertex_type{TypeArray(F32[4], ConstU32(num_attrs))}; + // The input vertex count isn't statically known, so make length 32 (what glslang does) + const Id patch_array_type{TypeArray(per_vertex_type, ConstU32(32u))}; + input_attr_array = DefineInput(patch_array_type, 0); + Name(input_attr_array, "in_attrs"); + } + break; + } + case LogicalStage::TessellationEval: { + tess_coord = DefineInput(F32[3], std::nullopt, spv::BuiltIn::TessCoord); + primitive_id = DefineVariable(U32[1], spv::BuiltIn::PrimitiveId, spv::StorageClass::Input); + + const u32 num_attrs = runtime_info.vs_info.hs_output_cp_stride >> 4; + if (num_attrs > 0) { + const Id per_vertex_type{TypeArray(F32[4], ConstU32(num_attrs))}; + // The input vertex count isn't statically known, so make length 32 (what glslang does) + const Id patch_array_type{TypeArray(per_vertex_type, ConstU32(32u))}; + input_attr_array = DefineInput(patch_array_type, 0); + Name(input_attr_array, "in_attrs"); + } + + u32 patch_base_location = runtime_info.vs_info.hs_output_cp_stride >> 4; + for (size_t index = 0; index < 30; ++index) { + if (!(info.uses_patches & (1U << index))) { + continue; + } + const Id id{DefineInput(F32[4], patch_base_location + index)}; + Decorate(id, spv::Decoration::Patch); + Name(id, fmt::format("patch_in{}", index)); + patches[index] = id; + } + break; + } default: break; } } void EmitContext::DefineOutputs() { - switch (stage) { - case Stage::Export: - case Stage::Vertex: { + switch (l_stage) { + case LogicalStage::Vertex: { + // No point in defining builtin outputs (i.e. position) unless next stage is fragment? + // Might cause problems linking with tcs + + output_position = DefineVariable(F32[4], spv::BuiltIn::Position, spv::StorageClass::Output); + const bool has_extra_pos_stores = info.stores.Get(IR::Attribute::Position1) || + info.stores.Get(IR::Attribute::Position2) || + info.stores.Get(IR::Attribute::Position3); + if (has_extra_pos_stores) { + const Id type{TypeArray(F32[1], ConstU32(8U))}; + clip_distances = + DefineVariable(type, spv::BuiltIn::ClipDistance, spv::StorageClass::Output); + cull_distances = + DefineVariable(type, spv::BuiltIn::CullDistance, spv::StorageClass::Output); + } + if (stage == Shader::Stage::Local && runtime_info.ls_info.links_with_tcs) { + const u32 num_attrs = runtime_info.ls_info.ls_stride >> 4; + if (num_attrs > 0) { + const Id type{TypeArray(F32[4], ConstU32(num_attrs))}; + output_attr_array = DefineOutput(type, 0); + Name(output_attr_array, "out_attrs"); + } + } else { + for (u32 i = 0; i < IR::NumParams; i++) { + const IR::Attribute param{IR::Attribute::Param0 + i}; + if (!info.stores.GetAny(param)) { + continue; + } + const u32 num_components = info.stores.NumComponents(param); + const Id id{DefineOutput(F32[num_components], i)}; + Name(id, fmt::format("out_attr{}", i)); + output_params[i] = + GetAttributeInfo(AmdGpu::NumberFormat::Float, id, num_components, true); + } + } + break; + } + case LogicalStage::TessellationControl: { + if (info.stores_tess_level_outer) { + const Id type{TypeArray(F32[1], ConstU32(4U))}; + output_tess_level_outer = + DefineOutput(type, std::nullopt, spv::BuiltIn::TessLevelOuter); + Decorate(output_tess_level_outer, spv::Decoration::Patch); + } + if (info.stores_tess_level_inner) { + const Id type{TypeArray(F32[1], ConstU32(2U))}; + output_tess_level_inner = + DefineOutput(type, std::nullopt, spv::BuiltIn::TessLevelInner); + Decorate(output_tess_level_inner, spv::Decoration::Patch); + } + + const u32 num_attrs = runtime_info.hs_info.hs_output_cp_stride >> 4; + if (num_attrs > 0) { + const Id per_vertex_type{TypeArray(F32[4], ConstU32(num_attrs))}; + // The input vertex count isn't statically known, so make length 32 (what glslang does) + const Id patch_array_type{TypeArray( + per_vertex_type, ConstU32(runtime_info.hs_info.NumOutputControlPoints()))}; + output_attr_array = DefineOutput(patch_array_type, 0); + Name(output_attr_array, "out_attrs"); + } + + u32 patch_base_location = runtime_info.hs_info.hs_output_cp_stride >> 4; + for (size_t index = 0; index < 30; ++index) { + if (!(info.uses_patches & (1U << index))) { + continue; + } + const Id id{DefineOutput(F32[4], patch_base_location + index)}; + Decorate(id, spv::Decoration::Patch); + Name(id, fmt::format("patch_out{}", index)); + patches[index] = id; + } + break; + } + case LogicalStage::TessellationEval: { output_position = DefineVariable(F32[4], spv::BuiltIn::Position, spv::StorageClass::Output); const bool has_extra_pos_stores = info.stores.Get(IR::Attribute::Position1) || info.stores.Get(IR::Attribute::Position2) || @@ -419,11 +531,10 @@ void EmitContext::DefineOutputs() { Name(id, fmt::format("out_attr{}", i)); output_params[i] = GetAttributeInfo(AmdGpu::NumberFormat::Float, id, num_components, true); - interfaces.push_back(id); } break; } - case Stage::Fragment: + case LogicalStage::Fragment: for (u32 i = 0; i < IR::NumRenderTargets; i++) { const IR::Attribute mrt{IR::Attribute::RenderTarget0 + i}; if (!info.stores.GetAny(mrt)) { @@ -435,22 +546,22 @@ void EmitContext::DefineOutputs() { const Id id{DefineOutput(type, i)}; Name(id, fmt::format("frag_color{}", i)); frag_outputs[i] = GetAttributeInfo(num_format, id, num_components, true); - interfaces.push_back(id); } break; - case Stage::Geometry: { + case LogicalStage::Geometry: { output_position = DefineVariable(F32[4], spv::BuiltIn::Position, spv::StorageClass::Output); for (u32 attr_id = 0; attr_id < info.gs_copy_data.num_attrs; attr_id++) { const Id id{DefineOutput(F32[4], attr_id)}; Name(id, fmt::format("out_attr{}", attr_id)); output_params[attr_id] = {id, output_f32, F32[1], 4u}; - interfaces.push_back(id); } break; } - default: + case LogicalStage::Compute: break; + default: + UNREACHABLE(); } } diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index cd1293328..583d96b99 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -46,14 +46,18 @@ public: void DefineBufferOffsets(); void DefineInterpolatedAttribs(); - [[nodiscard]] Id DefineInput(Id type, u32 location) { - const Id input_id{DefineVar(type, spv::StorageClass::Input)}; - Decorate(input_id, spv::Decoration::Location, location); + [[nodiscard]] Id DefineInput(Id type, std::optional location = std::nullopt, + std::optional builtin = std::nullopt) { + const Id input_id{DefineVariable(type, builtin, spv::StorageClass::Input)}; + if (location) { + Decorate(input_id, spv::Decoration::Location, *location); + } return input_id; } - [[nodiscard]] Id DefineOutput(Id type, std::optional location = std::nullopt) { - const Id output_id{DefineVar(type, spv::StorageClass::Output)}; + [[nodiscard]] Id DefineOutput(Id type, std::optional location = std::nullopt, + std::optional builtin = std::nullopt) { + const Id output_id{DefineVariable(type, builtin, spv::StorageClass::Output)}; if (location) { Decorate(output_id, spv::Decoration::Location, *location); } @@ -131,7 +135,8 @@ public: const Info& info; const RuntimeInfo& runtime_info; const Profile& profile; - Stage stage{}; + Stage stage; + LogicalStage l_stage{}; Id void_id{}; Id U8{}; @@ -188,8 +193,15 @@ public: Id clip_distances{}; Id cull_distances{}; + Id patch_vertices{}; + Id output_tess_level_outer{}; + Id output_tess_level_inner{}; + Id tess_coord; + std::array patches{}; + Id workgroup_id{}; Id local_invocation_id{}; + Id invocation_id{}; // for instanced geoshaders or output vertices within TCS patch Id subgroup_local_invocation_id{}; Id image_u32{}; @@ -252,6 +264,8 @@ public: bool is_loaded{}; s32 buffer_handle{-1}; }; + Id input_attr_array; + Id output_attr_array; std::array input_params{}; std::array output_params{}; std::array frag_outputs{}; diff --git a/src/shader_recompiler/frontend/tessellation.h b/src/shader_recompiler/frontend/tessellation.h new file mode 100644 index 000000000..bfcaa4fdc --- /dev/null +++ b/src/shader_recompiler/frontend/tessellation.h @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Shader { + +struct TessellationDataConstantBuffer { + u32 ls_stride; + u32 hs_cp_stride; // HullStateConstants::m_cpStride != 0 ? HullStateConstants::m_cpStride : + // ls_stride + u32 num_patches; // num patches submitted in threadgroup + u32 hs_output_base; // HullStateConstants::m_numInputCP::m_cpStride != 0 ? + // HullStateConstants::m_numInputCP * ls_stride * num_patches : 0 + // basically 0 when passthrough + u32 patch_const_size; // 16 * num_patch_attrs + u32 patch_const_base; // hs_output_base + patch_output_size + u32 patch_output_size; // output_cp_stride * num_output_cp_per_patch + f32 off_chip_tessellation_factor_threshold; + u32 first_edge_tess_factor_index; +}; + +// Assign names to dword fields of TessellationDataConstantBuffer +enum class TessConstantAttribute : u32 { + LsStride, + HsCpStride, + HsNumPatch, + HsOutputBase, + PatchConstSize, + PatchConstBase, + PatchOutputSize, + OffChipTessellationFactorThreshold, + FirstEdgeTessFactorIndex, +}; + +} // namespace Shader \ No newline at end of file diff --git a/src/shader_recompiler/frontend/translate/data_share.cpp b/src/shader_recompiler/frontend/translate/data_share.cpp index 5914f9fe3..116935b94 100644 --- a/src/shader_recompiler/frontend/translate/data_share.cpp +++ b/src/shader_recompiler/frontend/translate/data_share.cpp @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later - #include "shader_recompiler/frontend/translate/translate.h" #include "shader_recompiler/ir/reg.h" +#include "shader_recompiler/runtime_info.h" namespace Shader::Gcn { @@ -73,10 +73,11 @@ void Translator::EmitDataShare(const GcnInst& inst) { void Translator::V_READFIRSTLANE_B32(const GcnInst& inst) { const IR::U32 value{GetSrc(inst.src[0])}; - if (info.stage != Stage::Compute) { - SetDst(inst.dst[0], value); - } else { + if (info.l_stage == LogicalStage::Compute || + info.l_stage == LogicalStage::TessellationControl) { SetDst(inst.dst[0], ir.ReadFirstLane(value)); + } else { + SetDst(inst.dst[0], value); } } diff --git a/src/shader_recompiler/frontend/translate/scalar_alu.cpp b/src/shader_recompiler/frontend/translate/scalar_alu.cpp index 5b411d83e..1ef0d82d8 100644 --- a/src/shader_recompiler/frontend/translate/scalar_alu.cpp +++ b/src/shader_recompiler/frontend/translate/scalar_alu.cpp @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include "common/assert.h" #include "shader_recompiler/frontend/translate/translate.h" namespace Shader::Gcn { @@ -78,8 +80,10 @@ void Translator::EmitScalarAlu(const GcnInst& inst) { return S_BFM_B32(inst); case Opcode::S_MUL_I32: return S_MUL_I32(inst); + case Opcode::S_BFE_I32: + return S_BFE(inst, true); case Opcode::S_BFE_U32: - return S_BFE_U32(inst); + return S_BFE(inst, false); case Opcode::S_ABSDIFF_I32: return S_ABSDIFF_I32(inst); @@ -434,12 +438,12 @@ void Translator::S_MUL_I32(const GcnInst& inst) { SetDst(inst.dst[0], ir.IMul(GetSrc(inst.src[0]), GetSrc(inst.src[1]))); } -void Translator::S_BFE_U32(const GcnInst& inst) { +void Translator::S_BFE(const GcnInst& inst, bool is_signed) { const IR::U32 src0{GetSrc(inst.src[0])}; const IR::U32 src1{GetSrc(inst.src[1])}; const IR::U32 offset{ir.BitwiseAnd(src1, ir.Imm32(0x1F))}; const IR::U32 count{ir.BitFieldExtract(src1, ir.Imm32(16), ir.Imm32(7))}; - const IR::U32 result{ir.BitFieldExtract(src0, offset, count)}; + const IR::U32 result{ir.BitFieldExtract(src0, offset, count, is_signed)}; SetDst(inst.dst[0], result); ir.SetScc(ir.INotEqual(result, ir.Imm32(0))); } diff --git a/src/shader_recompiler/frontend/translate/translate.cpp b/src/shader_recompiler/frontend/translate/translate.cpp index 97978ff6b..3031e6643 100644 --- a/src/shader_recompiler/frontend/translate/translate.cpp +++ b/src/shader_recompiler/frontend/translate/translate.cpp @@ -8,6 +8,8 @@ #include "shader_recompiler/frontend/fetch_shader.h" #include "shader_recompiler/frontend/translate/translate.h" #include "shader_recompiler/info.h" +#include "shader_recompiler/ir/attribute.h" +#include "shader_recompiler/ir/reg.h" #include "shader_recompiler/runtime_info.h" #include "video_core/amdgpu/resource.h" #include "video_core/amdgpu/types.h" @@ -34,9 +36,8 @@ void Translator::EmitPrologue() { } IR::VectorReg dst_vreg = IR::VectorReg::V0; - switch (info.stage) { - case Stage::Vertex: - case Stage::Export: + switch (info.l_stage) { + case LogicalStage::Vertex: // v0: vertex ID, always present ir.SetVectorReg(dst_vreg++, ir.GetAttributeU32(IR::Attribute::VertexId)); // v1: instance ID, step rate 0 @@ -52,7 +53,7 @@ void Translator::EmitPrologue() { ir.SetVectorReg(dst_vreg++, ir.GetAttributeU32(IR::Attribute::InstanceId)); } break; - case Stage::Fragment: + case LogicalStage::Fragment: dst_vreg = IR::VectorReg::V0; if (runtime_info.fs_info.addr_flags.persp_sample_ena) { ++dst_vreg; // I @@ -122,7 +123,30 @@ void Translator::EmitPrologue() { } } break; - case Stage::Compute: + case LogicalStage::TessellationControl: { + // Should be laid out like: + // [0:8]: patch id within VGT + // [8:12]: output control point id + ir.SetVectorReg(IR::VectorReg::V1, + ir.GetAttributeU32(IR::Attribute::PackedHullInvocationInfo)); + // TODO PrimitiveId is probably V2 but haven't seen it yet + break; + } + case LogicalStage::TessellationEval: + ir.SetVectorReg(IR::VectorReg::V0, + ir.GetAttribute(IR::Attribute::TessellationEvaluationPointU)); + ir.SetVectorReg(IR::VectorReg::V1, + ir.GetAttribute(IR::Attribute::TessellationEvaluationPointV)); + // V2 is similar to PrimitiveID but not the same. It seems to only be used in + // compiler-generated address calculations. Its probably the patch id within the + // patches running locally on a given VGT (or CU, whichever is the granularity of LDS + // memory) + // Set to 0. See explanation in comment describing hull/domain passes + ir.SetVectorReg(IR::VectorReg::V2, ir.Imm32(0u)); + // V3 is the actual PrimitiveID as intended by the shader author. + ir.SetVectorReg(IR::VectorReg::V3, ir.GetAttributeU32(IR::Attribute::PrimitiveId)); + break; + case LogicalStage::Compute: ir.SetVectorReg(dst_vreg++, ir.GetAttributeU32(IR::Attribute::LocalInvocationId, 0)); ir.SetVectorReg(dst_vreg++, ir.GetAttributeU32(IR::Attribute::LocalInvocationId, 1)); ir.SetVectorReg(dst_vreg++, ir.GetAttributeU32(IR::Attribute::LocalInvocationId, 2)); @@ -137,7 +161,7 @@ void Translator::EmitPrologue() { ir.SetScalarReg(dst_sreg++, ir.GetAttributeU32(IR::Attribute::WorkgroupId, 2)); } break; - case Stage::Geometry: + case LogicalStage::Geometry: switch (runtime_info.gs_info.out_primitive[0]) { case AmdGpu::GsOutputPrimitiveType::TriangleStrip: ir.SetVectorReg(IR::VectorReg::V3, ir.Imm32(2u)); // vertex 2 @@ -152,7 +176,7 @@ void Translator::EmitPrologue() { ir.SetVectorReg(IR::VectorReg::V2, ir.GetAttributeU32(IR::Attribute::PrimitiveId)); break; default: - throw NotImplementedException("Unknown shader stage"); + UNREACHABLE_MSG("Unknown shader stage"); } } @@ -503,7 +527,8 @@ void Translate(IR::Block* block, u32 pc, std::span inst_list, Inf // Special case for emitting fetch shader. if (inst.opcode == Opcode::S_SWAPPC_B64) { - ASSERT(info.stage == Stage::Vertex || info.stage == Stage::Export); + ASSERT(info.stage == Stage::Vertex || info.stage == Stage::Export || + info.stage == Stage::Local); translator.EmitFetch(inst); continue; } diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index 198cea276..60bad1864 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -94,7 +94,8 @@ public: void S_ASHR_I32(const GcnInst& inst); void S_BFM_B32(const GcnInst& inst); void S_MUL_I32(const GcnInst& inst); - void S_BFE_U32(const GcnInst& inst); + void S_BFE(const GcnInst& inst, bool is_signed); + void S_BFE_I32(const GcnInst& inst); void S_ABSDIFF_I32(const GcnInst& inst); void S_NOT_B32(const GcnInst& inst); @@ -217,7 +218,7 @@ public: // VOP3a void V_MAD_F32(const GcnInst& inst); - void V_MAD_I32_I24(const GcnInst& inst, bool is_signed = false); + void V_MAD_I32_I24(const GcnInst& inst, bool is_signed = true); void V_MAD_U32_U24(const GcnInst& inst); void V_CUBEID_F32(const GcnInst& inst); void V_CUBESC_F32(const GcnInst& inst); diff --git a/src/shader_recompiler/frontend/translate/vector_alu.cpp b/src/shader_recompiler/frontend/translate/vector_alu.cpp index 3e9e677a7..2b32ca2ce 100644 --- a/src/shader_recompiler/frontend/translate/vector_alu.cpp +++ b/src/shader_recompiler/frontend/translate/vector_alu.cpp @@ -1060,8 +1060,14 @@ void Translator::V_CUBEMA_F32(const GcnInst& inst) { void Translator::V_BFE_U32(bool is_signed, const GcnInst& inst) { const IR::U32 src0{GetSrc(inst.src[0])}; - const IR::U32 src1{ir.BitwiseAnd(GetSrc(inst.src[1]), ir.Imm32(0x1F))}; - const IR::U32 src2{ir.BitwiseAnd(GetSrc(inst.src[2]), ir.Imm32(0x1F))}; + IR::U32 src1{GetSrc(inst.src[1])}; + IR::U32 src2{GetSrc(inst.src[2])}; + if (!src1.IsImmediate()) { + src1 = ir.BitwiseAnd(src1, ir.Imm32(0x1F)); + } + if (!src2.IsImmediate()) { + src2 = ir.BitwiseAnd(src2, ir.Imm32(0x1F)); + } SetDst(inst.dst[0], ir.BitFieldExtract(src0, src1, src2, is_signed)); } diff --git a/src/shader_recompiler/frontend/translate/vector_memory.cpp b/src/shader_recompiler/frontend/translate/vector_memory.cpp index eadd1c4db..072b1f88e 100644 --- a/src/shader_recompiler/frontend/translate/vector_memory.cpp +++ b/src/shader_recompiler/frontend/translate/vector_memory.cpp @@ -189,7 +189,8 @@ void Translator::BUFFER_LOAD(u32 num_dwords, bool is_typed, const GcnInst& inst) buffer_info.index_enable.Assign(mtbuf.idxen); buffer_info.offset_enable.Assign(mtbuf.offen); buffer_info.inst_offset.Assign(mtbuf.offset); - buffer_info.ring_access.Assign(is_ring); + buffer_info.globally_coherent.Assign(mtbuf.glc); + buffer_info.system_coherent.Assign(mtbuf.slc); if (is_typed) { const auto dmft = static_cast(mtbuf.dfmt); const auto nfmt = static_cast(mtbuf.nfmt); @@ -247,11 +248,15 @@ void Translator::BUFFER_STORE(u32 num_dwords, bool is_typed, const GcnInst& inst const IR::ScalarReg sharp{inst.src[2].code * 4}; const IR::Value soffset{GetSrc(inst.src[3])}; - if (info.stage != Stage::Export && info.stage != Stage::Geometry) { + if (info.stage != Stage::Export && info.stage != Stage::Hull && info.stage != Stage::Geometry) { ASSERT_MSG(soffset.IsImmediate() && soffset.U32() == 0, "Non immediate offset not supported"); } + if (info.stage == Stage::Hull) { + // printf("here\n"); // break + } + IR::Value address = [&] -> IR::Value { if (is_ring) { return ir.CompositeConstruct(ir.GetVectorReg(vaddr), soffset); @@ -269,7 +274,8 @@ void Translator::BUFFER_STORE(u32 num_dwords, bool is_typed, const GcnInst& inst buffer_info.index_enable.Assign(mtbuf.idxen); buffer_info.offset_enable.Assign(mtbuf.offen); buffer_info.inst_offset.Assign(mtbuf.offset); - buffer_info.ring_access.Assign(is_ring); + buffer_info.globally_coherent.Assign(mtbuf.glc); + buffer_info.system_coherent.Assign(mtbuf.slc); if (is_typed) { const auto dmft = static_cast(mtbuf.dfmt); const auto nfmt = static_cast(mtbuf.nfmt); diff --git a/src/shader_recompiler/info.h b/src/shader_recompiler/info.h index 494bbb4bb..dbea2af8a 100644 --- a/src/shader_recompiler/info.h +++ b/src/shader_recompiler/info.h @@ -11,6 +11,7 @@ #include "common/types.h" #include "shader_recompiler/backend/bindings.h" #include "shader_recompiler/frontend/copy_shader.h" +#include "shader_recompiler/frontend/tessellation.h" #include "shader_recompiler/ir/attribute.h" #include "shader_recompiler/ir/passes/srt.h" #include "shader_recompiler/ir/reg.h" @@ -163,6 +164,7 @@ struct Info { UserDataMask ud_mask{}; CopyShaderData gs_copy_data; + u32 uses_patches{}; BufferResourceList buffers; TextureBufferResourceList texture_buffers; @@ -173,8 +175,12 @@ struct Info { PersistentSrtInfo srt_info; std::vector flattened_ud_buf; + IR::ScalarReg tess_consts_ptr_base = IR::ScalarReg::Max; + s32 tess_consts_dword_offset = -1; + std::span user_data; Stage stage; + LogicalStage l_stage; u64 pgm_hash{}; VAddr pgm_base; @@ -190,14 +196,16 @@ struct Info { bool uses_shared{}; bool uses_fp16{}; bool uses_fp64{}; + bool stores_tess_level_outer{}; + bool stores_tess_level_inner{}; bool translation_failed{}; // indicates that shader has unsupported instructions bool has_readconst{}; u8 mrt_mask{0u}; bool has_fetch_shader{false}; u32 fetch_shader_sgpr_base{0u}; - explicit Info(Stage stage_, ShaderParams params) - : stage{stage_}, pgm_hash{params.hash}, pgm_base{params.Base()}, + explicit Info(Stage stage_, LogicalStage l_stage_, ShaderParams params) + : stage{stage_}, l_stage{l_stage_}, pgm_hash{params.hash}, pgm_base{params.Base()}, user_data{params.user_data} {} template @@ -244,6 +252,16 @@ struct Info { srt_info.walker_func(user_data.data(), flattened_ud_buf.data()); } } + + void ReadTessConstantBuffer(TessellationDataConstantBuffer& tess_constants) const { + ASSERT(tess_consts_dword_offset >= 0); // We've already tracked the V# UD + auto buf = ReadUdReg(static_cast(tess_consts_ptr_base), + static_cast(tess_consts_dword_offset)); + VAddr tess_constants_addr = buf.base_address; + memcpy(&tess_constants, + reinterpret_cast(tess_constants_addr), + sizeof(tess_constants)); + } }; constexpr AmdGpu::Buffer BufferResource::GetSharp(const Info& info) const noexcept { diff --git a/src/shader_recompiler/ir/attribute.cpp b/src/shader_recompiler/ir/attribute.cpp index e219dfb64..6a267e21b 100644 --- a/src/shader_recompiler/ir/attribute.cpp +++ b/src/shader_recompiler/ir/attribute.cpp @@ -104,6 +104,8 @@ std::string NameOf(Attribute attribute) { return "VertexId"; case Attribute::InstanceId: return "InstanceId"; + case Attribute::PrimitiveId: + return "PrimitiveId"; case Attribute::FragCoord: return "FragCoord"; case Attribute::IsFrontFace: @@ -114,6 +116,16 @@ std::string NameOf(Attribute attribute) { return "LocalInvocationId"; case Attribute::LocalInvocationIndex: return "LocalInvocationIndex"; + case Attribute::InvocationId: + return "InvocationId"; + case Attribute::PatchVertices: + return "PatchVertices"; + case Attribute::TessellationEvaluationPointU: + return "TessellationEvaluationPointU"; + case Attribute::TessellationEvaluationPointV: + return "TessellationEvaluationPointV"; + case Attribute::PackedHullInvocationInfo: + return "PackedHullInvocationInfo"; default: break; } diff --git a/src/shader_recompiler/ir/attribute.h b/src/shader_recompiler/ir/attribute.h index 0890e88f1..bcb2b44a9 100644 --- a/src/shader_recompiler/ir/attribute.h +++ b/src/shader_recompiler/ir/attribute.h @@ -72,8 +72,13 @@ enum class Attribute : u64 { LocalInvocationId = 75, LocalInvocationIndex = 76, FragCoord = 77, - InstanceId0 = 78, // step rate 0 - InstanceId1 = 79, // step rate 1 + InstanceId0 = 78, // step rate 0 + InstanceId1 = 79, // step rate 1 + InvocationId = 80, // TCS id in output patch and instanced geometry shader id + PatchVertices = 81, + TessellationEvaluationPointU = 82, + TessellationEvaluationPointV = 83, + PackedHullInvocationInfo = 84, // contains patch id within the VGT and invocation ID Max, }; @@ -85,6 +90,11 @@ constexpr bool IsPosition(Attribute attribute) noexcept { return attribute >= Attribute::Position0 && attribute <= Attribute::Position3; } +constexpr bool IsTessCoord(Attribute attribute) noexcept { + return attribute >= Attribute::TessellationEvaluationPointU && + attribute <= Attribute::TessellationEvaluationPointV; +} + constexpr bool IsParam(Attribute attribute) noexcept { return attribute >= Attribute::Param0 && attribute <= Attribute::Param31; } diff --git a/src/shader_recompiler/ir/basic_block.cpp b/src/shader_recompiler/ir/basic_block.cpp index b4d1a78c7..a312eabde 100644 --- a/src/shader_recompiler/ir/basic_block.cpp +++ b/src/shader_recompiler/ir/basic_block.cpp @@ -94,6 +94,8 @@ static std::string ArgToIndex(std::map& inst_to_index, size return fmt::format("{}", arg.VectorReg()); case Type::Attribute: return fmt::format("{}", arg.Attribute()); + case Type::Patch: + return fmt::format("{}", arg.Patch()); default: return ""; } diff --git a/src/shader_recompiler/ir/ir_emitter.cpp b/src/shader_recompiler/ir/ir_emitter.cpp index 3ebc82e64..21df53391 100644 --- a/src/shader_recompiler/ir/ir_emitter.cpp +++ b/src/shader_recompiler/ir/ir_emitter.cpp @@ -266,8 +266,8 @@ void IREmitter::SetM0(const U32& value) { Inst(Opcode::SetM0, value); } -F32 IREmitter::GetAttribute(IR::Attribute attribute, u32 comp, u32 index) { - return Inst(Opcode::GetAttribute, attribute, Imm32(comp), Imm32(index)); +F32 IREmitter::GetAttribute(IR::Attribute attribute, u32 comp, IR::Value index) { + return Inst(Opcode::GetAttribute, attribute, Imm32(comp), index); } U32 IREmitter::GetAttributeU32(IR::Attribute attribute, u32 comp) { @@ -278,6 +278,24 @@ void IREmitter::SetAttribute(IR::Attribute attribute, const F32& value, u32 comp Inst(Opcode::SetAttribute, attribute, value, Imm32(comp)); } +F32 IREmitter::GetTessGenericAttribute(const U32& vertex_index, const U32& attr_index, + const U32& comp_index) { + return Inst(IR::Opcode::GetTessGenericAttribute, vertex_index, attr_index, comp_index); +} + +void IREmitter::SetTcsGenericAttribute(const F32& value, const U32& attr_index, + const U32& comp_index) { + Inst(Opcode::SetTcsGenericAttribute, value, attr_index, comp_index); +} + +F32 IREmitter::GetPatch(Patch patch) { + return Inst(Opcode::GetPatch, patch); +} + +void IREmitter::SetPatch(Patch patch, const F32& value) { + Inst(Opcode::SetPatch, patch, value); +} + Value IREmitter::LoadShared(int bit_size, bool is_signed, const U32& offset) { switch (bit_size) { case 32: @@ -552,6 +570,19 @@ Value IREmitter::CompositeConstruct(const Value& e1, const Value& e2, const Valu } } +Value IREmitter::CompositeConstruct(std::span elements) { + switch (elements.size()) { + case 2: + return CompositeConstruct(elements[0], elements[1]); + case 3: + return CompositeConstruct(elements[0], elements[1], elements[2]); + case 4: + return CompositeConstruct(elements[0], elements[1], elements[2], elements[3]); + default: + UNREACHABLE_MSG("Composite construct with greater than 4 elements"); + } +} + Value IREmitter::CompositeExtract(const Value& vector, size_t element) { const auto read{[&](Opcode opcode, size_t limit) -> Value { if (element >= limit) { diff --git a/src/shader_recompiler/ir/ir_emitter.h b/src/shader_recompiler/ir/ir_emitter.h index 068aba14d..95713565b 100644 --- a/src/shader_recompiler/ir/ir_emitter.h +++ b/src/shader_recompiler/ir/ir_emitter.h @@ -10,6 +10,7 @@ #include "shader_recompiler/ir/attribute.h" #include "shader_recompiler/ir/basic_block.h" #include "shader_recompiler/ir/condition.h" +#include "shader_recompiler/ir/patch.h" #include "shader_recompiler/ir/value.h" namespace Shader::IR { @@ -80,10 +81,18 @@ public: [[nodiscard]] U1 Condition(IR::Condition cond); - [[nodiscard]] F32 GetAttribute(Attribute attribute, u32 comp = 0, u32 index = 0); + [[nodiscard]] F32 GetAttribute(Attribute attribute, u32 comp = 0, + IR::Value index = IR::Value(u32(0u))); [[nodiscard]] U32 GetAttributeU32(Attribute attribute, u32 comp = 0); void SetAttribute(Attribute attribute, const F32& value, u32 comp = 0); + [[nodiscard]] F32 GetTessGenericAttribute(const U32& vertex_index, const U32& attr_index, + const U32& comp_index); + void SetTcsGenericAttribute(const F32& value, const U32& attr_index, const U32& comp_index); + + [[nodiscard]] F32 GetPatch(Patch patch); + void SetPatch(Patch patch, const F32& value); + [[nodiscard]] Value LoadShared(int bit_size, bool is_signed, const U32& offset); void WriteShared(int bit_size, const Value& value, const U32& offset); @@ -138,6 +147,8 @@ public: [[nodiscard]] Value CompositeConstruct(const Value& e1, const Value& e2, const Value& e3); [[nodiscard]] Value CompositeConstruct(const Value& e1, const Value& e2, const Value& e3, const Value& e4); + [[nodiscard]] Value CompositeConstruct(std::span values); + [[nodiscard]] Value CompositeExtract(const Value& vector, size_t element); [[nodiscard]] Value CompositeInsert(const Value& vector, const Value& object, size_t element); @@ -335,6 +346,7 @@ private: template T Inst(Opcode op, Args... args) { auto it{block->PrependNewInst(insertion_point, op, {Value{args}...})}; + it->SetParent(block); return T{Value{&*it}}; } @@ -352,6 +364,7 @@ private: u32 raw_flags{}; std::memcpy(&raw_flags, &flags.proxy, sizeof(flags.proxy)); auto it{block->PrependNewInst(insertion_point, op, {Value{args}...}, raw_flags)}; + it->SetParent(block); return T{Value{&*it}}; } }; diff --git a/src/shader_recompiler/ir/microinstruction.cpp b/src/shader_recompiler/ir/microinstruction.cpp index 9b4ad63d2..6e7bbe661 100644 --- a/src/shader_recompiler/ir/microinstruction.cpp +++ b/src/shader_recompiler/ir/microinstruction.cpp @@ -52,6 +52,8 @@ bool Inst::MayHaveSideEffects() const noexcept { case Opcode::Discard: case Opcode::DiscardCond: case Opcode::SetAttribute: + case Opcode::SetTcsGenericAttribute: + case Opcode::SetPatch: case Opcode::StoreBufferU32: case Opcode::StoreBufferU32x2: case Opcode::StoreBufferU32x3: diff --git a/src/shader_recompiler/ir/opcodes.h b/src/shader_recompiler/ir/opcodes.h index be640297a..cd73ace7e 100644 --- a/src/shader_recompiler/ir/opcodes.h +++ b/src/shader_recompiler/ir/opcodes.h @@ -30,7 +30,7 @@ constexpr Type Opaque{Type::Opaque}; constexpr Type ScalarReg{Type::ScalarReg}; constexpr Type VectorReg{Type::VectorReg}; constexpr Type Attribute{Type::Attribute}; -constexpr Type SystemValue{Type::SystemValue}; +constexpr Type Patch{Type::Patch}; constexpr Type U1{Type::U1}; constexpr Type U8{Type::U8}; constexpr Type U16{Type::U16}; diff --git a/src/shader_recompiler/ir/opcodes.inc b/src/shader_recompiler/ir/opcodes.inc index 477275824..470f9fbe5 100644 --- a/src/shader_recompiler/ir/opcodes.inc +++ b/src/shader_recompiler/ir/opcodes.inc @@ -60,6 +60,10 @@ OPCODE(SetGotoVariable, Void, U32, OPCODE(GetAttribute, F32, Attribute, U32, U32, ) OPCODE(GetAttributeU32, U32, Attribute, U32, ) OPCODE(SetAttribute, Void, Attribute, F32, U32, ) +OPCODE(GetPatch, F32, Patch, ) +OPCODE(SetPatch, Void, Patch, F32, ) +OPCODE(GetTessGenericAttribute, F32, U32, U32, U32, ) +OPCODE(SetTcsGenericAttribute, Void, F32, U32, U32, ) // Flags OPCODE(GetScc, U1, Void, ) diff --git a/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp b/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp index 9624ce6a5..16b07e1a1 100644 --- a/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp +++ b/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp @@ -216,6 +216,18 @@ void FoldAdd(IR::Block& block, IR::Inst& inst) { } } +template +void FoldMul(IR::Block& block, IR::Inst& inst) { + if (!FoldCommutative(inst, [](T a, T b) { return a * b; })) { + return; + } + const IR::Value rhs{inst.Arg(1)}; + if (rhs.IsImmediate() && Arg(rhs) == 0) { + inst.ReplaceUsesWithAndRemove(IR::Value(0u)); + return; + } +} + void FoldCmpClass(IR::Block& block, IR::Inst& inst) { ASSERT_MSG(inst.Arg(1).IsImmediate(), "Unable to resolve compare operation"); const auto class_mask = static_cast(inst.Arg(1).U32()); @@ -292,7 +304,19 @@ void ConstantPropagation(IR::Block& block, IR::Inst& inst) { FoldWhenAllImmediates(inst, [](u32 a) { return static_cast(a); }); return; case IR::Opcode::IMul32: - FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a * b; }); + FoldMul(block, inst); + return; + case IR::Opcode::UDiv32: + FoldWhenAllImmediates(inst, [](u32 a, u32 b) { + ASSERT_MSG(b != 0, "Folding UDiv32 with divisor 0"); + return a / b; + }); + return; + case IR::Opcode::UMod32: + FoldWhenAllImmediates(inst, [](u32 a, u32 b) { + ASSERT_MSG(b != 0, "Folding UMod32 with modulo 0"); + return a % b; + }); return; case IR::Opcode::FPCmpClass32: FoldCmpClass(block, inst); diff --git a/src/shader_recompiler/ir/passes/constant_propogation.h b/src/shader_recompiler/ir/passes/constant_propogation.h new file mode 100644 index 000000000..313a3cc6a --- /dev/null +++ b/src/shader_recompiler/ir/passes/constant_propogation.h @@ -0,0 +1,4 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once \ No newline at end of file diff --git a/src/shader_recompiler/ir/passes/hull_shader_transform.cpp b/src/shader_recompiler/ir/passes/hull_shader_transform.cpp new file mode 100644 index 000000000..5cf02b6d0 --- /dev/null +++ b/src/shader_recompiler/ir/passes/hull_shader_transform.cpp @@ -0,0 +1,744 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later +#include "common/assert.h" +#include "shader_recompiler/info.h" +#include "shader_recompiler/ir/attribute.h" +#include "shader_recompiler/ir/breadth_first_search.h" +#include "shader_recompiler/ir/ir_emitter.h" +#include "shader_recompiler/ir/opcodes.h" +#include "shader_recompiler/ir/pattern_matching.h" +#include "shader_recompiler/ir/program.h" +#include "shader_recompiler/runtime_info.h" + +namespace Shader::Optimization { + +/** + * Tessellation shaders pass outputs to the next shader using LDS. + * The Hull shader stage receives input control points stored in LDS. + * + * These passes attempt to resolve LDS accesses to attribute accesses and correctly + * write to the tessellation factor tables. + * + * The LDS layout is: + * - TCS inputs for patch 0 + * - TCS inputs for patch 1 + * - TCS inputs for patch 2 + * - ... + * - TCS outputs for patch 0 + * - TCS outputs for patch 1 + * - TCS outputs for patch 2 + * - ... + * - PatchConst TCS outputs for patch 0 + * - PatchConst TCS outputs for patch 1 + * - PatchConst TCS outputs for patch 2 + * + * + * If the Hull stage does not write any new control points the driver will + * optimize LDS layout so input and output control point spaces overlap. + * (Passthrough) + * + * The gnm driver requires a V# holding special constants to be bound + * for reads by the shader. + * The Hull and Domain shaders read values from this buffer which + * contain size and offset information required to address input, output, + * or PatchConst attributes within the current patch. + * See the TessellationDataConstantBuffer struct to see the layout of this V#. + * + * Tessellation factors are stored to a special tessellation factor V# that is automatically bound + * by the driver. This is the input to the fixed function tessellator that actually subdivides the + * domain. We translate these to writes to SPIR-V builtins for tessellation factors in the Hull + * shader. + * The offset into the tess factor buffer determines which factor the shader is writing. + * Additionally, most hull shaders seem to redundantly write tess factors to PatchConst + * attributes, even if dead in the domain shader. We just treat these as generic PatchConst writes. + * + * LDS reads in the Hull shader can be from input control points, and in the the Domain shader can + * be hs output control points (output from the perspective of the Hull shader) and patchconst + * values. + * LDS stores in the Hull shader can either be output control point writes or per-patch + * (PatchConst) data writes. The Domain shader exports attributes using EXP instructions, unless its + * followed by the geometry stage (but we havent seen this yet), so nothing special there. + * The address calculations can vary significantly and can't be easily pattern matched. We are at + * the mercy of instruction selection the ps4 compiler wanted to use. + * Generally though, they could look something like this: + * Input control point: + * addr = PatchIdInVgt * input_cp_stride * #input_cp_per_patch + index * input_cp_stride + * + attr# * 16 + component + * Output control point: + * addr = #patches * input_cp_stride * #input_cp_per_patch + * + PatchIdInVgt * output_patch_stride + InvocationID * output_cp_stride + + attr# * 16 + component + * Per patch output: + * addr = #patches * input_cp_stride * #cp_per_input_patch + * + #patches * output_patch_stride + * + PatchIdInVgt * per_patch_output_stride + attr# * 16 + component + * + * output_patch_stride and output_cp_stride are usually compile time constants in the gcn + * + * Hull shaders can probably also read output control points corresponding to other threads, like + * shared memory (but we havent seen this yet). + * ^ This is an UNREACHABLE for now. We may need to insert additional barriers if this happens. + * They should also be able to read PatchConst values, + * although not sure if this happens in practice. + * + * To determine which type of attribute (input, output, patchconst) we the check the users of + * TessConstants V# reads to deduce which type of attribute a given load/store to LDS + * is touching. + * + * In the Hull shader, both the PatchId within the VGT group (PatchIdInVgt) and the output control + * point id (InvocationId) are packed in VGPR1 by the driver like + * V1 = InvocationId << 8 | PatchIdInVgt + * The shader typically uses V_BFE_(U|S)32 to extract them. We use the starting bit_pos to determine + * which is which. + * + * This pass does not attempt to deduce the exact attribute referenced in a LDS load/store. + * Instead, it feeds the address in the LDS load/store to the get/set Insts we use for TCS in/out's, + * TES in's, and PatchConst in/out's. + * + * TCS/TES Input attributes: + * We define input attributes using an array in the shader roughly like this: + * // equivalent GLSL in TCS + * layout (location = 0) in vec4 in_attrs[][NUM_INPUT_ATTRIBUTES]; + * + * Here the NUM_INPUT_ATTRIBUTES is derived from the ls_stride member of the TessConstants V#. + * We divide ls_stride (in bytes) by 16 to get the number of vec4 attributes. + * For TES, the number of attributes comes from hs_cp_stride / 16. + * The first (outer) dimension is unsized but corresponds to the number of vertices in the hs input + * patch (for Hull) or the hs output patch (for Domain). + * + * For input reads in TCS or TES, we emit SPIR-V like: + * float value = in_attrs[addr / ls_stride][(addr % ls_stride) >> 4][(addr & 0xF) >> 2]; + * + * For output writes, we assume the control point index is InvocationId, since high level languages + * impose that restriction (although maybe it's technically possible on hardware). So SPIR-V looks + * like this: + * layout (location = 0) in vec4 in_attrs[][NUM_OUTPUT_ATTRIBUTES]; + * out_attrs[InvocationId][(addr % hs_cp_stride) >> 4][(addr & 0xF) >> 2] = value; + * + * NUM_OUTPUT_ATTRIBUTES is derived by hs_cp_stride / 16, so it can link with the TES in_attrs + * variable. + * + * Another challenge is the fact that the GCN shader needs to address attributes from LDS as a whole + * which contains the attributes from many patches. On the other hand, higher level shading + * languages restrict attribute access to the patch of the current thread, which is naturally a + * restriction in SPIR-V also. + * The addresses the ps4 compiler generates for loads/stores and the fact that LDS holds many + * patches' attributes are just implementation details of the ps4 driver/compiler. To deal with + * this, we can replace certain TessConstant V# reads with 0, which only contribute to the base + * address of the current patch's attributes in LDS and not the indexes within the local patch. + * + * (A perfect implementation might need emulation of the VGTs in mesh/compute, loading/storing + * attributes to buffers and not caring about whether they are hs input, hs output, or patchconst + * attributes) + * + */ + +namespace { + +using namespace Shader::Optimiation::PatternMatching; + +static void InitTessConstants(IR::ScalarReg sharp_ptr_base, s32 sharp_dword_offset, + Shader::Info& info, Shader::RuntimeInfo& runtime_info, + TessellationDataConstantBuffer& tess_constants) { + info.tess_consts_ptr_base = sharp_ptr_base; + info.tess_consts_dword_offset = sharp_dword_offset; + info.ReadTessConstantBuffer(tess_constants); + if (info.l_stage == LogicalStage::TessellationControl) { + runtime_info.hs_info.InitFromTessConstants(tess_constants); + } else { + runtime_info.vs_info.InitFromTessConstants(tess_constants); + } + + return; +} + +struct TessSharpLocation { + IR::ScalarReg ptr_base; + u32 dword_off; +}; + +std::optional FindTessConstantSharp(IR::Inst* read_const_buffer) { + IR::Value sharp_ptr_base; + IR::Value sharp_dword_offset; + + IR::Value rv = IR::Value{read_const_buffer}; + IR::Value handle = read_const_buffer->Arg(0); + + if (M_COMPOSITECONSTRUCTU32X4(M_GETUSERDATA(MatchImm(sharp_dword_offset)), MatchIgnore(), + MatchIgnore(), MatchIgnore()) + .Match(handle)) { + return TessSharpLocation{.ptr_base = IR::ScalarReg::Max, + .dword_off = static_cast(sharp_dword_offset.ScalarReg())}; + } else if (M_COMPOSITECONSTRUCTU32X4( + M_READCONST(M_COMPOSITECONSTRUCTU32X2(M_GETUSERDATA(MatchImm(sharp_ptr_base)), + MatchIgnore()), + MatchImm(sharp_dword_offset)), + MatchIgnore(), MatchIgnore(), MatchIgnore()) + .Match(handle)) { + return TessSharpLocation{.ptr_base = sharp_ptr_base.ScalarReg(), + .dword_off = sharp_dword_offset.U32()}; + } + return {}; +} + +// Walker that helps deduce what type of attribute a DS instruction is reading +// or writing, which could be an input control point, output control point, +// or per-patch constant (PatchConst). +// For certain ReadConstBuffer instructions using the tess constants V#,, we visit the users +// recursively and increment a counter on the Load/WriteShared users. +// Namely NumPatch (from m_hsNumPatch), HsOutputBase (m_hsOutputBase), +// and PatchConstBase (m_patchConstBase). +// In addr calculations, the term NumPatch * ls_stride * #input_cp_in_patch +// is used as an addend to skip the region for input control points, and similarly +// NumPatch * hs_cp_stride * #output_cp_in_patch is used to skip the region +// for output control points. +// +// TODO: this will break if AMD compiler used distributive property like +// TcsNumPatches * (ls_stride * #input_cp_in_patch + hs_cp_stride * #output_cp_in_patch) +class TessConstantUseWalker { +public: + void MarkTessAttributeUsers(IR::Inst* read_const_buffer, TessConstantAttribute attr) { + u32 inc; + switch (attr) { + case TessConstantAttribute::HsNumPatch: + case TessConstantAttribute::HsOutputBase: + inc = 1; + break; + case TessConstantAttribute::PatchConstBase: + inc = 2; + break; + default: + UNREACHABLE(); + } + + for (IR::Use use : read_const_buffer->Uses()) { + MarkTessAttributeUsersHelper(use, inc); + } + + ++seq_num; + } + +private: + void MarkTessAttributeUsersHelper(IR::Use use, u32 inc) { + IR::Inst* inst = use.user; + + switch (use.user->GetOpcode()) { + case IR::Opcode::LoadSharedU32: + case IR::Opcode::LoadSharedU64: + case IR::Opcode::LoadSharedU128: + case IR::Opcode::WriteSharedU32: + case IR::Opcode::WriteSharedU64: + case IR::Opcode::WriteSharedU128: { + u32 counter = inst->Flags(); + inst->SetFlags(counter + inc); + // Stop here + return; + } + case IR::Opcode::Phi: { + struct PhiCounter { + u16 seq_num; + u8 unique_edge; + u8 counter; + }; + + PhiCounter count = inst->Flags(); + ASSERT_MSG(count.counter == 0 || count.unique_edge == use.operand); + // the point of seq_num is to tell us if we've already traversed this + // phi on the current walk. Alternatively we could keep a set of phi's + // seen on the current walk. This is to handle phi cycles + if (count.seq_num == 0) { + // First time we've encountered this phi + count.seq_num = seq_num; + // Mark the phi as having been traversed originally through this edge + count.unique_edge = use.operand; + count.counter = inc; + } else if (count.seq_num < seq_num) { + count.seq_num = seq_num; + // For now, assume we are visiting this phi via the same edge + // as on other walks. If not, some dataflow analysis might be necessary + ASSERT(count.unique_edge == use.operand); + count.counter += inc; + } else { + // count.seq_num == seq_num + // there's a cycle, and we've already been here on this walk + return; + } + inst->SetFlags(count); + break; + } + default: + break; + } + + for (IR::Use use : inst->Uses()) { + MarkTessAttributeUsersHelper(use, inc); + } + } + + u32 seq_num{1u}; +}; + +enum class AttributeRegion : u32 { InputCP, OutputCP, PatchConst }; + +static AttributeRegion GetAttributeRegionKind(IR::Inst* ring_access, const Shader::Info& info, + const Shader::RuntimeInfo& runtime_info) { + u32 count = ring_access->Flags(); + if (count == 0) { + return AttributeRegion::InputCP; + } else if (info.l_stage == LogicalStage::TessellationControl && + runtime_info.hs_info.IsPassthrough()) { + ASSERT(count <= 1); + return AttributeRegion::PatchConst; + } else { + ASSERT(count <= 2); + return AttributeRegion(count); + } +} + +static bool IsDivisibleByStride(IR::Value term, u32 stride) { + IR::Value a, b; + if (MatchU32(stride).Match(term)) { + return true; + } else if (M_BITFIELDUEXTRACT(MatchValue(a), MatchU32(0), MatchU32(24)).Match(term) || + M_BITFIELDSEXTRACT(MatchValue(a), MatchU32(0), MatchU32(24)).Match(term)) { + return IsDivisibleByStride(a, stride); + } else if (M_IMUL32(MatchValue(a), MatchValue(b)).Match(term)) { + return IsDivisibleByStride(a, stride) || IsDivisibleByStride(b, stride); + } + return false; +} + +// Return true if we can eliminate any addends +static bool TryOptimizeAddendInModulo(IR::Value addend, u32 stride, std::vector& addends) { + IR::Value a, b; + if (M_IADD32(MatchValue(a), MatchValue(b)).Match(addend)) { + bool ret = false; + ret = TryOptimizeAddendInModulo(a, stride, addends); + ret |= TryOptimizeAddendInModulo(b, stride, addends); + return ret; + } else if (!IsDivisibleByStride(addend, stride)) { + addends.push_back(IR::U32{addend}); + return false; + } else { + return true; + } +} + +// In calculation (a + b + ...) % stride +// Use this fact +// (a + b) mod N = (a mod N + b mod N) mod N +// If any addend is divisible by stride, then we can replace it with 0 in the attribute +// or component index calculation +static IR::U32 TryOptimizeAddressModulo(IR::U32 addr, u32 stride, IR::IREmitter& ir) { + std::vector addends; + if (TryOptimizeAddendInModulo(addr, stride, addends)) { + addr = ir.Imm32(0); + for (auto& addend : addends) { + addr = ir.IAdd(addr, addend); + } + } + return addr; +} + +// TODO: can optimize div in control point index similarly to mod + +// Read a TCS input (InputCP region) or TES input (OutputCP region) +static IR::F32 ReadTessInputComponent(IR::U32 addr, const u32 stride, IR::IREmitter& ir, + u32 off_dw) { + if (off_dw > 0) { + addr = ir.IAdd(addr, ir.Imm32(off_dw)); + } + const IR::U32 control_point_index = ir.IDiv(addr, ir.Imm32(stride)); + const IR::U32 addr_for_attrs = TryOptimizeAddressModulo(addr, stride, ir); + const IR::U32 attr_index = + ir.ShiftRightLogical(ir.IMod(addr_for_attrs, ir.Imm32(stride)), ir.Imm32(4u)); + const IR::U32 comp_index = + ir.ShiftRightLogical(ir.BitwiseAnd(addr_for_attrs, ir.Imm32(0xFU)), ir.Imm32(2u)); + return ir.GetTessGenericAttribute(control_point_index, attr_index, comp_index); +} + +} // namespace + +void HullShaderTransform(IR::Program& program, RuntimeInfo& runtime_info) { + const Info& info = program.info; + + for (IR::Block* block : program.blocks) { + for (IR::Inst& inst : block->Instructions()) { + const auto opcode = inst.GetOpcode(); + switch (opcode) { + case IR::Opcode::StoreBufferU32: + case IR::Opcode::StoreBufferU32x2: + case IR::Opcode::StoreBufferU32x3: + case IR::Opcode::StoreBufferU32x4: { + const auto info = inst.Flags(); + if (!info.globally_coherent) { + break; + } + IR::IREmitter ir{*block, IR::Block::InstructionList::s_iterator_to(inst)}; + const auto GetValue = [&](IR::Value data) -> IR::F32 { + if (auto* inst = data.TryInstRecursive(); + inst && inst->GetOpcode() == IR::Opcode::BitCastU32F32) { + return IR::F32{inst->Arg(0)}; + } + return ir.BitCast(IR::U32{data}); + }; + const u32 num_dwords = u32(opcode) - u32(IR::Opcode::StoreBufferU32) + 1; + IR::U32 index = IR::U32{inst.Arg(1)}; + ASSERT(index.IsImmediate()); + const u32 gcn_factor_idx = (info.inst_offset.Value() + index.U32()) >> 2; + + const IR::Value data = inst.Arg(2); + auto get_factor_attr = [&](u32 gcn_factor_idx) -> IR::Patch { + // The hull outputs tess factors in different formats depending on the shader. + // For triangle domains, it seems to pack the entries into 4 consecutive floats, + // with the 3 edge factors followed by the 1 interior factor. + // For quads, it does 4 edge factors then 2 interior. + // There is a tess factor stride member of the GNMX hull constants struct in + // a hull program shader binary archive, but this doesn't seem to be + // communicated to the driver. + // The layout seems to be implied by the type of the abstract domain. + switch (runtime_info.hs_info.tess_type) { + case AmdGpu::TessellationType::Quad: + ASSERT(gcn_factor_idx < 6); + return IR::PatchFactor(gcn_factor_idx); + case AmdGpu::TessellationType::Triangle: + ASSERT(gcn_factor_idx < 4); + if (gcn_factor_idx == 3) { + return IR::Patch::TessellationLodInteriorU; + } + return IR::PatchFactor(gcn_factor_idx); + default: + // Point domain types haven't been seen so far + UNREACHABLE_MSG("Unhandled tess type"); + } + }; + + inst.Invalidate(); + if (num_dwords == 1) { + ir.SetPatch(get_factor_attr(gcn_factor_idx), GetValue(data)); + break; + } + auto* inst = data.TryInstRecursive(); + ASSERT(inst && (inst->GetOpcode() == IR::Opcode::CompositeConstructU32x2 || + inst->GetOpcode() == IR::Opcode::CompositeConstructU32x3 || + inst->GetOpcode() == IR::Opcode::CompositeConstructU32x4)); + for (s32 i = 0; i < num_dwords; i++) { + ir.SetPatch(get_factor_attr(gcn_factor_idx + i), GetValue(inst->Arg(i))); + } + break; + } + + case IR::Opcode::WriteSharedU32: + case IR::Opcode::WriteSharedU64: + case IR::Opcode::WriteSharedU128: { + IR::IREmitter ir{*block, IR::Block::InstructionList::s_iterator_to(inst)}; + const u32 num_dwords = opcode == IR::Opcode::WriteSharedU32 + ? 1 + : (opcode == IR::Opcode::WriteSharedU64 ? 2 : 4); + const IR::U32 addr{inst.Arg(0)}; + const IR::U32 data{inst.Arg(1).Resolve()}; + + const auto SetOutput = [&](IR::U32 addr, IR::U32 value, AttributeRegion output_kind, + u32 off_dw) { + const IR::F32 data_component = ir.BitCast(value); + + if (output_kind == AttributeRegion::OutputCP) { + if (off_dw > 0) { + addr = ir.IAdd(addr, ir.Imm32(off_dw)); + } + u32 stride = runtime_info.hs_info.hs_output_cp_stride; + // Invocation ID array index is implicit, handled by SPIRV backend + const IR::U32 addr_for_attrs = TryOptimizeAddressModulo(addr, stride, ir); + const IR::U32 attr_index = ir.ShiftRightLogical( + ir.IMod(addr_for_attrs, ir.Imm32(stride)), ir.Imm32(4u)); + const IR::U32 comp_index = ir.ShiftRightLogical( + ir.BitwiseAnd(addr_for_attrs, ir.Imm32(0xFU)), ir.Imm32(2u)); + ir.SetTcsGenericAttribute(data_component, attr_index, comp_index); + } else { + ASSERT(output_kind == AttributeRegion::PatchConst); + ASSERT_MSG(addr.IsImmediate(), "patch addr non imm, inst {}", + fmt::ptr(addr.Inst())); + ir.SetPatch(IR::PatchGeneric((addr.U32() >> 2) + off_dw), data_component); + } + }; + + AttributeRegion region = GetAttributeRegionKind(&inst, info, runtime_info); + if (num_dwords == 1) { + SetOutput(addr, data, region, 0); + } else { + for (auto i = 0; i < num_dwords; i++) { + SetOutput(addr, IR::U32{data.Inst()->Arg(i)}, region, i); + } + } + inst.Invalidate(); + break; + } + + case IR::Opcode::LoadSharedU32: { + case IR::Opcode::LoadSharedU64: + case IR::Opcode::LoadSharedU128: + IR::IREmitter ir{*block, IR::Block::InstructionList::s_iterator_to(inst)}; + const IR::U32 addr{inst.Arg(0)}; + AttributeRegion region = GetAttributeRegionKind(&inst, info, runtime_info); + const u32 num_dwords = opcode == IR::Opcode::LoadSharedU32 + ? 1 + : (opcode == IR::Opcode::LoadSharedU64 ? 2 : 4); + ASSERT_MSG(region == AttributeRegion::InputCP, + "Unhandled read of output or patchconst attribute in hull shader"); + IR::Value attr_read; + if (num_dwords == 1) { + attr_read = ir.BitCast( + ReadTessInputComponent(addr, runtime_info.hs_info.ls_stride, ir, 0)); + } else { + boost::container::static_vector read_components; + for (auto i = 0; i < num_dwords; i++) { + const IR::F32 component = + ReadTessInputComponent(addr, runtime_info.hs_info.ls_stride, ir, i); + read_components.push_back(ir.BitCast(component)); + } + attr_read = ir.CompositeConstruct(read_components); + } + inst.ReplaceUsesWithAndRemove(attr_read); + break; + } + + default: + break; + } + } + } + + if (runtime_info.hs_info.IsPassthrough()) { + // Copy input attributes to output attributes, indexed by InvocationID + // Passthrough should imply that input and output patches have same number of vertices + IR::Block* entry_block = *program.blocks.begin(); + auto it = std::ranges::find_if(entry_block->Instructions(), [](IR::Inst& inst) { + return inst.GetOpcode() == IR::Opcode::Prologue; + }); + ASSERT(it != entry_block->end()); + ++it; + ASSERT(it != entry_block->end()); + ++it; + // Prologue + // SetExec #true + // <- insert here + // ... + IR::IREmitter ir{*entry_block, it}; + + ASSERT(runtime_info.hs_info.ls_stride % 16 == 0); + u32 num_attributes = runtime_info.hs_info.ls_stride / 16; + const auto invocation_id = ir.GetAttributeU32(IR::Attribute::InvocationId); + for (u32 attr_no = 0; attr_no < num_attributes; attr_no++) { + for (u32 comp = 0; comp < 4; comp++) { + IR::F32 attr_read = + ir.GetTessGenericAttribute(invocation_id, ir.Imm32(attr_no), ir.Imm32(comp)); + // InvocationId is implicit index for output control point writes + ir.SetTcsGenericAttribute(attr_read, ir.Imm32(attr_no), ir.Imm32(comp)); + } + } + // We could wrap the rest of the program in an if stmt + // CopyInputAttrsToOutputs(); // psuedocode + // if (InvocationId == 0) { + // PatchConstFunction(); + // } + // But as long as we treat invocation ID as 0 for all threads, shouldn't matter functionally + } +} + +void DomainShaderTransform(IR::Program& program, RuntimeInfo& runtime_info) { + Info& info = program.info; + + for (IR::Block* block : program.blocks) { + for (IR::Inst& inst : block->Instructions()) { + IR::IREmitter ir{*block, IR::Block::InstructionList::s_iterator_to(inst)}; + const auto opcode = inst.GetOpcode(); + switch (inst.GetOpcode()) { + case IR::Opcode::LoadSharedU32: { + case IR::Opcode::LoadSharedU64: + case IR::Opcode::LoadSharedU128: + const IR::U32 addr{inst.Arg(0)}; + AttributeRegion region = GetAttributeRegionKind(&inst, info, runtime_info); + const u32 num_dwords = opcode == IR::Opcode::LoadSharedU32 + ? 1 + : (opcode == IR::Opcode::LoadSharedU64 ? 2 : 4); + const auto GetInput = [&](IR::U32 addr, u32 off_dw) -> IR::F32 { + if (region == AttributeRegion::OutputCP) { + return ReadTessInputComponent( + addr, runtime_info.vs_info.hs_output_cp_stride, ir, off_dw); + } else { + ASSERT(region == AttributeRegion::PatchConst); + return ir.GetPatch(IR::PatchGeneric((addr.U32() >> 2) + off_dw)); + } + }; + IR::Value attr_read; + if (num_dwords == 1) { + attr_read = ir.BitCast(GetInput(addr, 0)); + } else { + boost::container::static_vector read_components; + for (auto i = 0; i < num_dwords; i++) { + const IR::F32 component = GetInput(addr, i); + read_components.push_back(ir.BitCast(component)); + } + attr_read = ir.CompositeConstruct(read_components); + } + inst.ReplaceUsesWithAndRemove(attr_read); + break; + } + default: + break; + } + } + } +} + +// Run before either hull or domain transform +void TessellationPreprocess(IR::Program& program, RuntimeInfo& runtime_info) { + TessellationDataConstantBuffer tess_constants; + Shader::Info& info = program.info; + // Find the TessellationDataConstantBuffer V# + for (IR::Block* block : program.blocks) { + for (IR::Inst& inst : block->Instructions()) { + auto found_tess_consts_sharp = [&]() -> bool { + switch (inst.GetOpcode()) { + case IR::Opcode::LoadSharedU32: + case IR::Opcode::LoadSharedU64: + case IR::Opcode::LoadSharedU128: + case IR::Opcode::WriteSharedU32: + case IR::Opcode::WriteSharedU64: + case IR::Opcode::WriteSharedU128: { + IR::Value addr = inst.Arg(0); + auto read_const_buffer = IR::BreadthFirstSearch( + addr, [](IR::Inst* maybe_tess_const) -> std::optional { + if (maybe_tess_const->GetOpcode() == IR::Opcode::ReadConstBuffer) { + return maybe_tess_const; + } + return std::nullopt; + }); + if (read_const_buffer) { + auto sharp_location = FindTessConstantSharp(read_const_buffer.value()); + if (sharp_location) { + if (info.tess_consts_dword_offset >= 0) { + // Its possible theres a readconstbuffer that contributes to an + // LDS address and isnt a TessConstant V# read. Could improve on + // this somehow + ASSERT_MSG(static_cast(sharp_location->dword_off) == + info.tess_consts_dword_offset && + sharp_location->ptr_base == + info.tess_consts_ptr_base, + "TessConstants V# is ambiguous"); + } + InitTessConstants(sharp_location->ptr_base, + static_cast(sharp_location->dword_off), info, + runtime_info, tess_constants); + return true; + } + UNREACHABLE_MSG("Failed to match tess constant sharp"); + } + return false; + } + default: + return false; + } + }(); + + if (found_tess_consts_sharp) { + break; + } + } + } + + ASSERT(info.tess_consts_dword_offset >= 0); + + TessConstantUseWalker walker; + + for (IR::Block* block : program.blocks) { + for (IR::Inst& inst : block->Instructions()) { + if (inst.GetOpcode() == IR::Opcode::ReadConstBuffer) { + auto sharp_location = FindTessConstantSharp(&inst); + if (sharp_location && sharp_location->ptr_base == info.tess_consts_ptr_base && + sharp_location->dword_off == info.tess_consts_dword_offset) { + // The shader is reading from the TessConstants V# + IR::Value index = inst.Arg(1); + + ASSERT_MSG(index.IsImmediate(), + "Tessellation constant read with dynamic index"); + u32 off_dw = index.U32(); + ASSERT(off_dw <= + static_cast(TessConstantAttribute::FirstEdgeTessFactorIndex)); + + auto tess_const_attr = static_cast(off_dw); + switch (tess_const_attr) { + case TessConstantAttribute::LsStride: + // If not, we may need to make this runtime state for TES + ASSERT(info.l_stage == LogicalStage::TessellationControl); + inst.ReplaceUsesWithAndRemove(IR::Value(tess_constants.ls_stride)); + break; + case TessConstantAttribute::HsCpStride: + inst.ReplaceUsesWithAndRemove(IR::Value(tess_constants.hs_cp_stride)); + break; + case TessConstantAttribute::HsNumPatch: + case TessConstantAttribute::HsOutputBase: + case TessConstantAttribute::PatchConstBase: + walker.MarkTessAttributeUsers(&inst, tess_const_attr); + // We should be able to safely set these to 0 so that indexing happens only + // within the local patch in the recompiled Vulkan shader. This assumes + // these values only contribute to address calculations for in/out + // attributes in the original gcn shader. + // See the explanation for why we set V2 to 0 when emitting the prologue. + inst.ReplaceUsesWithAndRemove(IR::Value(0u)); + break; + case Shader::TessConstantAttribute::PatchConstSize: + case Shader::TessConstantAttribute::PatchOutputSize: + case Shader::TessConstantAttribute::OffChipTessellationFactorThreshold: + case Shader::TessConstantAttribute::FirstEdgeTessFactorIndex: + // May need to replace PatchConstSize and PatchOutputSize with 0 + break; + default: + UNREACHABLE_MSG("Read past end of TessConstantsBuffer"); + } + } + } + } + } + + // These pattern matching are neccessary for now unless we support dynamic indexing of + // PatchConst attributes and tess factors. PatchConst should be easy, turn those into a single + // vec4 array like in/out attrs. Not sure about tess factors. + if (info.l_stage == LogicalStage::TessellationControl) { + // Replace the BFEs on V1 (packed with patch id within VGT and output cp id) + for (IR::Block* block : program.blocks) { + for (auto it = block->Instructions().begin(); it != block->Instructions().end(); it++) { + IR::Inst& inst = *it; + if (M_BITFIELDUEXTRACT( + M_GETATTRIBUTEU32(MatchAttribute(IR::Attribute::PackedHullInvocationInfo), + MatchIgnore()), + MatchU32(0), MatchU32(8)) + .Match(IR::Value{&inst})) { + IR::IREmitter emit(*block, it); + // This is the patch id within the VGT, not the actual PrimitiveId + // in the draw + IR::Value replacement(0u); + inst.ReplaceUsesWithAndRemove(replacement); + } else if (M_BITFIELDUEXTRACT( + M_GETATTRIBUTEU32( + MatchAttribute(IR::Attribute::PackedHullInvocationInfo), + MatchIgnore()), + MatchU32(8), MatchU32(5)) + .Match(IR::Value{&inst})) { + IR::IREmitter ir(*block, it); + IR::Value replacement; + if (runtime_info.hs_info.IsPassthrough()) { + // Deal with annoying pattern in BB where InvocationID use makes no + // sense (in addr calculation for patchconst or tess factor write) + replacement = ir.Imm32(0); + } else { + replacement = ir.GetAttributeU32(IR::Attribute::InvocationId); + } + inst.ReplaceUsesWithAndRemove(replacement); + } + } + } + } +} + +} // namespace Shader::Optimization diff --git a/src/shader_recompiler/ir/passes/ir_passes.h b/src/shader_recompiler/ir/passes/ir_passes.h index 7bd47992c..61f43e7e4 100644 --- a/src/shader_recompiler/ir/passes/ir_passes.h +++ b/src/shader_recompiler/ir/passes/ir_passes.h @@ -18,5 +18,8 @@ void CollectShaderInfoPass(IR::Program& program); void LowerSharedMemToRegisters(IR::Program& program); void RingAccessElimination(const IR::Program& program, const RuntimeInfo& runtime_info, Stage stage); +void TessellationPreprocess(IR::Program& program, RuntimeInfo& runtime_info); +void HullShaderTransform(IR::Program& program, RuntimeInfo& runtime_info); +void DomainShaderTransform(IR::Program& program, RuntimeInfo& runtime_info); } // namespace Shader::Optimization diff --git a/src/shader_recompiler/ir/passes/ring_access_elimination.cpp b/src/shader_recompiler/ir/passes/ring_access_elimination.cpp index eb1be2967..d6f1efb12 100644 --- a/src/shader_recompiler/ir/passes/ring_access_elimination.cpp +++ b/src/shader_recompiler/ir/passes/ring_access_elimination.cpp @@ -1,11 +1,13 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "common/assert.h" #include "shader_recompiler/ir/ir_emitter.h" #include "shader_recompiler/ir/opcodes.h" #include "shader_recompiler/ir/program.h" #include "shader_recompiler/ir/reg.h" #include "shader_recompiler/recompiler.h" +#include "shader_recompiler/runtime_info.h" namespace Shader::Optimization { @@ -23,12 +25,45 @@ void RingAccessElimination(const IR::Program& program, const RuntimeInfo& runtim }; switch (stage) { + case Stage::Local: { + ForEachInstruction([=](IR::IREmitter& ir, IR::Inst& inst) { + const auto opcode = inst.GetOpcode(); + switch (opcode) { + case IR::Opcode::WriteSharedU64: + case IR::Opcode::WriteSharedU32: { + bool is_composite = opcode == IR::Opcode::WriteSharedU64; + u32 num_components = opcode == IR::Opcode::WriteSharedU32 ? 1 : 2; + + u32 offset = 0; + const auto* addr = inst.Arg(0).InstRecursive(); + if (addr->GetOpcode() == IR::Opcode::IAdd32) { + ASSERT(addr->Arg(1).IsImmediate()); + offset = addr->Arg(1).U32(); + } + IR::Value data = inst.Arg(1).Resolve(); + for (s32 i = 0; i < num_components; i++) { + const auto attrib = IR::Attribute::Param0 + (offset / 16); + const auto comp = (offset / 4) % 4; + const IR::U32 value = IR::U32{is_composite ? data.Inst()->Arg(i) : data}; + ir.SetAttribute(attrib, ir.BitCast(value), comp); + offset += 4; + } + inst.Invalidate(); + break; + } + default: + break; + } + }); + break; + } case Stage::Export: { ForEachInstruction([=](IR::IREmitter& ir, IR::Inst& inst) { const auto opcode = inst.GetOpcode(); switch (opcode) { case IR::Opcode::StoreBufferU32: { - if (!inst.Flags().ring_access) { + const auto info = inst.Flags(); + if (!info.system_coherent || !info.globally_coherent) { break; } @@ -61,12 +96,13 @@ void RingAccessElimination(const IR::Program& program, const RuntimeInfo& runtim const auto opcode = inst.GetOpcode(); switch (opcode) { case IR::Opcode::LoadBufferU32: { - if (!inst.Flags().ring_access) { + const auto info = inst.Flags(); + if (!info.system_coherent || !info.globally_coherent) { break; } const auto shl_inst = inst.Arg(1).TryInstRecursive(); - const auto vertex_id = shl_inst->Arg(0).Resolve().U32() >> 2; + const auto vertex_id = ir.Imm32(shl_inst->Arg(0).Resolve().U32() >> 2); const auto offset = inst.Arg(1).TryInstRecursive()->Arg(1); const auto bucket = offset.Resolve().U32() / 256u; const auto attrib = bucket < 4 ? IR::Attribute::Position0 @@ -80,7 +116,8 @@ void RingAccessElimination(const IR::Program& program, const RuntimeInfo& runtim break; } case IR::Opcode::StoreBufferU32: { - if (!inst.Flags().ring_access) { + const auto buffer_info = inst.Flags(); + if (!buffer_info.system_coherent || !buffer_info.globally_coherent) { break; } diff --git a/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp b/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp index 8b93d72e3..c34b59b88 100644 --- a/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp +++ b/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp @@ -17,6 +17,22 @@ void Visit(Info& info, IR::Inst& inst) { case IR::Opcode::GetUserData: info.ud_mask.Set(inst.Arg(0).ScalarReg()); break; + case IR::Opcode::SetPatch: { + const auto patch = inst.Arg(0).Patch(); + if (patch <= IR::Patch::TessellationLodBottom) { + info.stores_tess_level_outer = true; + } else if (patch <= IR::Patch::TessellationLodInteriorV) { + info.stores_tess_level_inner = true; + } else { + info.uses_patches |= 1U << IR::GenericPatchIndex(patch); + } + break; + } + case IR::Opcode::GetPatch: { + const auto patch = inst.Arg(0).Patch(); + info.uses_patches |= 1U << IR::GenericPatchIndex(patch); + break; + } case IR::Opcode::LoadSharedU32: case IR::Opcode::LoadSharedU64: case IR::Opcode::WriteSharedU32: diff --git a/src/shader_recompiler/ir/patch.cpp b/src/shader_recompiler/ir/patch.cpp new file mode 100644 index 000000000..2485bc5b4 --- /dev/null +++ b/src/shader_recompiler/ir/patch.cpp @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "shader_recompiler/ir/patch.h" + +namespace Shader::IR { + +std::string NameOf(Patch patch) { + switch (patch) { + case Patch::TessellationLodLeft: + return "TessellationLodLeft"; + case Patch::TessellationLodTop: + return "TessellationLodTop"; + case Patch::TessellationLodRight: + return "TessellationLodRight"; + case Patch::TessellationLodBottom: + return "TessellationLodBottom"; + case Patch::TessellationLodInteriorU: + return "TessellationLodInteriorU"; + case Patch::TessellationLodInteriorV: + return "TessellationLodInteriorV"; + default: + const u32 index = u32(patch) - u32(Patch::Component0); + return fmt::format("Component{}", index); + } +} + +} // namespace Shader::IR diff --git a/src/shader_recompiler/ir/patch.h b/src/shader_recompiler/ir/patch.h new file mode 100644 index 000000000..65d2192e6 --- /dev/null +++ b/src/shader_recompiler/ir/patch.h @@ -0,0 +1,173 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "common/types.h" + +namespace Shader::IR { + +enum class Patch : u64 { + TessellationLodLeft, + TessellationLodTop, + TessellationLodRight, + TessellationLodBottom, + TessellationLodInteriorU, + TessellationLodInteriorV, + Component0, + Component1, + Component2, + Component3, + Component4, + Component5, + Component6, + Component7, + Component8, + Component9, + Component10, + Component11, + Component12, + Component13, + Component14, + Component15, + Component16, + Component17, + Component18, + Component19, + Component20, + Component21, + Component22, + Component23, + Component24, + Component25, + Component26, + Component27, + Component28, + Component29, + Component30, + Component31, + Component32, + Component33, + Component34, + Component35, + Component36, + Component37, + Component38, + Component39, + Component40, + Component41, + Component42, + Component43, + Component44, + Component45, + Component46, + Component47, + Component48, + Component49, + Component50, + Component51, + Component52, + Component53, + Component54, + Component55, + Component56, + Component57, + Component58, + Component59, + Component60, + Component61, + Component62, + Component63, + Component64, + Component65, + Component66, + Component67, + Component68, + Component69, + Component70, + Component71, + Component72, + Component73, + Component74, + Component75, + Component76, + Component77, + Component78, + Component79, + Component80, + Component81, + Component82, + Component83, + Component84, + Component85, + Component86, + Component87, + Component88, + Component89, + Component90, + Component91, + Component92, + Component93, + Component94, + Component95, + Component96, + Component97, + Component98, + Component99, + Component100, + Component101, + Component102, + Component103, + Component104, + Component105, + Component106, + Component107, + Component108, + Component109, + Component110, + Component111, + Component112, + Component113, + Component114, + Component115, + Component116, + Component117, + Component118, + Component119, +}; +static_assert(static_cast(Patch::Component119) == 125); + +constexpr bool IsGeneric(Patch patch) noexcept { + return patch >= Patch::Component0 && patch <= Patch::Component119; +} + +constexpr Patch PatchFactor(u32 index) { + return static_cast(index); +} + +constexpr Patch PatchGeneric(u32 index) { + return static_cast(static_cast(Patch::Component0) + index); +} + +constexpr u32 GenericPatchIndex(Patch patch) { + return (static_cast(patch) - static_cast(Patch::Component0)) / 4; +} + +constexpr u32 GenericPatchElement(Patch patch) { + return (static_cast(patch) - static_cast(Patch::Component0)) % 4; +} + +[[nodiscard]] std::string NameOf(Patch patch); + +} // namespace Shader::IR + +template <> +struct fmt::formatter { + constexpr auto parse(format_parse_context& ctx) { + return ctx.begin(); + } + auto format(const Shader::IR::Patch patch, format_context& ctx) const { + return fmt::format_to(ctx.out(), "{}", Shader::IR::NameOf(patch)); + } +}; diff --git a/src/shader_recompiler/ir/pattern_matching.h b/src/shader_recompiler/ir/pattern_matching.h new file mode 100644 index 000000000..1279f14c3 --- /dev/null +++ b/src/shader_recompiler/ir/pattern_matching.h @@ -0,0 +1,127 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "shader_recompiler/ir/attribute.h" +#include "shader_recompiler/ir/value.h" + +namespace Shader::Optimiation::PatternMatching { + +// Attempt at pattern matching for Insts and Values +// Needs improvement, mostly a convenience + +template +struct MatchObject { + inline bool Match(IR::Value v) { + return static_cast(this)->Match(v); + } +}; + +struct MatchValue : MatchObject { + MatchValue(IR::Value& return_val_) : return_val(return_val_) {} + + inline bool Match(IR::Value v) { + return_val = v; + return true; + } + +private: + IR::Value& return_val; +}; + +struct MatchIgnore : MatchObject { + MatchIgnore() {} + + inline bool Match(IR::Value v) { + return true; + } +}; + +struct MatchImm : MatchObject { + MatchImm(IR::Value& v) : return_val(v) {} + + inline bool Match(IR::Value v) { + if (!v.IsImmediate()) { + return false; + } + + return_val = v; + return true; + } + +private: + IR::Value& return_val; +}; + +struct MatchAttribute : MatchObject { + MatchAttribute(IR::Attribute attribute_) : attribute(attribute_) {} + + inline bool Match(IR::Value v) { + return v.Type() == IR::Type::Attribute && v.Attribute() == attribute; + } + +private: + IR::Attribute attribute; +}; + +struct MatchU32 : MatchObject { + MatchU32(u32 imm_) : imm(imm_) {} + + inline bool Match(IR::Value v) { + return v.IsImmediate() && v.Type() == IR::Type::U32 && v.U32() == imm; + } + +private: + u32 imm; +}; + +template +struct MatchInstObject : MatchObject> { + static_assert(sizeof...(Args) == IR::NumArgsOf(opcode)); + MatchInstObject(Args&&... args) : pattern(std::forward_as_tuple(args...)) {} + + inline bool Match(IR::Value v) { + IR::Inst* inst = v.TryInstRecursive(); + if (!inst || inst->GetOpcode() != opcode) { + return false; + } + + bool matched = true; + + [&](std::index_sequence) { + ((matched = matched && std::get(pattern).Match(inst->Arg(Is))), ...); + }(std::make_index_sequence{}); + + return matched; + } + +private: + using MatchArgs = std::tuple; + MatchArgs pattern; +}; + +template +inline auto MakeInstPattern(Args&&... args) { + return MatchInstObject(std::forward(args)...); +} + +// Conveniences. TODO probably simpler way of doing this +#define M_READCONST(...) MakeInstPattern(__VA_ARGS__) +#define M_GETUSERDATA(...) MakeInstPattern(__VA_ARGS__) +#define M_BITFIELDUEXTRACT(...) MakeInstPattern(__VA_ARGS__) +#define M_BITFIELDSEXTRACT(...) MakeInstPattern(__VA_ARGS__) +#define M_GETATTRIBUTEU32(...) MakeInstPattern(__VA_ARGS__) +#define M_UMOD32(...) MakeInstPattern(__VA_ARGS__) +#define M_SHIFTRIGHTLOGICAL32(...) MakeInstPattern(__VA_ARGS__) +#define M_IADD32(...) MakeInstPattern(__VA_ARGS__) +#define M_IMUL32(...) MakeInstPattern(__VA_ARGS__) +#define M_BITWISEAND32(...) MakeInstPattern(__VA_ARGS__) +#define M_GETTESSGENERICATTRIBUTE(...) \ + MakeInstPattern(__VA_ARGS__) +#define M_SETTCSGENERICATTRIBUTE(...) \ + MakeInstPattern(__VA_ARGS__) +#define M_COMPOSITECONSTRUCTU32X2(...) \ + MakeInstPattern(__VA_ARGS__) +#define M_COMPOSITECONSTRUCTU32X4(...) \ + MakeInstPattern(__VA_ARGS__) + +} // namespace Shader::Optimiation::PatternMatching \ No newline at end of file diff --git a/src/shader_recompiler/ir/reg.h b/src/shader_recompiler/ir/reg.h index ca2e9ceb9..19e0da3dd 100644 --- a/src/shader_recompiler/ir/reg.h +++ b/src/shader_recompiler/ir/reg.h @@ -49,7 +49,8 @@ union BufferInstInfo { BitField<0, 1, u32> index_enable; BitField<1, 1, u32> offset_enable; BitField<2, 12, u32> inst_offset; - BitField<14, 1, u32> ring_access; // global + system coherency + BitField<14, 1, u32> system_coherent; + BitField<15, 1, u32> globally_coherent; }; enum class ScalarReg : u32 { diff --git a/src/shader_recompiler/ir/type.h b/src/shader_recompiler/ir/type.h index ec855a77e..0f043fb64 100644 --- a/src/shader_recompiler/ir/type.h +++ b/src/shader_recompiler/ir/type.h @@ -15,7 +15,7 @@ enum class Type { ScalarReg = 1 << 1, VectorReg = 1 << 2, Attribute = 1 << 3, - SystemValue = 1 << 4, + Patch = 1 << 4, U1 = 1 << 5, U8 = 1 << 6, U16 = 1 << 7, diff --git a/src/shader_recompiler/ir/value.cpp b/src/shader_recompiler/ir/value.cpp index 889e99556..8826b80f2 100644 --- a/src/shader_recompiler/ir/value.cpp +++ b/src/shader_recompiler/ir/value.cpp @@ -16,6 +16,8 @@ Value::Value(IR::VectorReg reg) noexcept : type{Type::VectorReg}, vreg{reg} {} Value::Value(IR::Attribute value) noexcept : type{Type::Attribute}, attribute{value} {} +Value::Value(IR::Patch patch) noexcept : type{Type::Patch}, patch{patch} {} + Value::Value(bool value) noexcept : type{Type::U1}, imm_u1{value} {} Value::Value(u8 value) noexcept : type{Type::U8}, imm_u8{value} {} diff --git a/src/shader_recompiler/ir/value.h b/src/shader_recompiler/ir/value.h index dbe8b5cc4..ed1e5536a 100644 --- a/src/shader_recompiler/ir/value.h +++ b/src/shader_recompiler/ir/value.h @@ -16,6 +16,7 @@ #include "shader_recompiler/exception.h" #include "shader_recompiler/ir/attribute.h" #include "shader_recompiler/ir/opcodes.h" +#include "shader_recompiler/ir/patch.h" #include "shader_recompiler/ir/reg.h" #include "shader_recompiler/ir/type.h" @@ -34,6 +35,7 @@ public: explicit Value(IR::ScalarReg reg) noexcept; explicit Value(IR::VectorReg reg) noexcept; explicit Value(IR::Attribute value) noexcept; + explicit Value(IR::Patch patch) noexcept; explicit Value(bool value) noexcept; explicit Value(u8 value) noexcept; explicit Value(u16 value) noexcept; @@ -56,6 +58,7 @@ public: [[nodiscard]] IR::ScalarReg ScalarReg() const; [[nodiscard]] IR::VectorReg VectorReg() const; [[nodiscard]] IR::Attribute Attribute() const; + [[nodiscard]] IR::Patch Patch() const; [[nodiscard]] bool U1() const; [[nodiscard]] u8 U8() const; [[nodiscard]] u16 U16() const; @@ -75,6 +78,7 @@ private: IR::ScalarReg sreg; IR::VectorReg vreg; IR::Attribute attribute; + IR::Patch patch; bool imm_u1; u8 imm_u8; u16 imm_u16; @@ -330,6 +334,11 @@ inline IR::Attribute Value::Attribute() const { return attribute; } +inline IR::Patch Value::Patch() const { + DEBUG_ASSERT(type == Type::Patch); + return patch; +} + inline bool Value::U1() const { if (IsIdentity()) { return inst->Arg(0).U1(); diff --git a/src/shader_recompiler/recompiler.cpp b/src/shader_recompiler/recompiler.cpp index 64f842c42..ad57adb6a 100644 --- a/src/shader_recompiler/recompiler.cpp +++ b/src/shader_recompiler/recompiler.cpp @@ -1,6 +1,9 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "common/config.h" +#include "common/io_file.h" +#include "common/path_util.h" #include "shader_recompiler/frontend/control_flow_graph.h" #include "shader_recompiler/frontend/decode.h" #include "shader_recompiler/frontend/structured_control_flow.h" @@ -29,7 +32,7 @@ IR::BlockList GenerateBlocks(const IR::AbstractSyntaxList& syntax_list) { } IR::Program TranslateProgram(std::span code, Pools& pools, Info& info, - const RuntimeInfo& runtime_info, const Profile& profile) { + RuntimeInfo& runtime_info, const Profile& profile) { // Ensure first instruction is expected. constexpr u32 token_mov_vcchi = 0xBEEB03FF; if (code[0] != token_mov_vcchi) { @@ -60,12 +63,29 @@ IR::Program TranslateProgram(std::span code, Pools& pools, Info& info program.post_order_blocks = Shader::IR::PostOrder(program.syntax_list.front()); // Run optimization passes + const auto stage = program.info.stage; + Shader::Optimization::SsaRewritePass(program.post_order_blocks); + Shader::Optimization::IdentityRemovalPass(program.blocks); + if (info.l_stage == LogicalStage::TessellationControl) { + // Tess passes require previous const prop passes for now (for simplicity). TODO allow + // fine grained folding or opportunistic folding we set an operand to an immediate + Shader::Optimization::ConstantPropagationPass(program.post_order_blocks); + Shader::Optimization::TessellationPreprocess(program, runtime_info); + Shader::Optimization::ConstantPropagationPass(program.post_order_blocks); + Shader::Optimization::HullShaderTransform(program, runtime_info); + } else if (info.l_stage == LogicalStage::TessellationEval) { + Shader::Optimization::ConstantPropagationPass(program.post_order_blocks); + Shader::Optimization::TessellationPreprocess(program, runtime_info); + Shader::Optimization::ConstantPropagationPass(program.post_order_blocks); + Shader::Optimization::DomainShaderTransform(program, runtime_info); + } Shader::Optimization::ConstantPropagationPass(program.post_order_blocks); - if (program.info.stage != Stage::Compute) { + Shader::Optimization::RingAccessElimination(program, runtime_info, stage); + if (stage != Stage::Compute) { Shader::Optimization::LowerSharedMemToRegisters(program); } - Shader::Optimization::RingAccessElimination(program, runtime_info, program.info.stage); + Shader::Optimization::ConstantPropagationPass(program.post_order_blocks); Shader::Optimization::FlattenExtendedUserdataPass(program); Shader::Optimization::ResourceTrackingPass(program); Shader::Optimization::IdentityRemovalPass(program.blocks); diff --git a/src/shader_recompiler/recompiler.h b/src/shader_recompiler/recompiler.h index f8acf6c9e..8180c29b3 100644 --- a/src/shader_recompiler/recompiler.h +++ b/src/shader_recompiler/recompiler.h @@ -28,6 +28,6 @@ struct Pools { }; [[nodiscard]] IR::Program TranslateProgram(std::span code, Pools& pools, Info& info, - const RuntimeInfo& runtime_info, const Profile& profile); + RuntimeInfo& runtime_info, const Profile& profile); } // namespace Shader diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index 4c779a368..23e23c118 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -7,6 +7,7 @@ #include #include #include "common/types.h" +#include "shader_recompiler/frontend/tessellation.h" #include "video_core/amdgpu/liverpool.h" #include "video_core/amdgpu/types.h" @@ -21,12 +22,31 @@ enum class Stage : u32 { Local, Compute, }; -constexpr u32 MaxStageTypes = 7; + +// Vertex intentionally comes after TCS/TES due to order of compilation +enum class LogicalStage : u32 { + Fragment, + TessellationControl, + TessellationEval, + Vertex, + Geometry, + Compute, + NumLogicalStages +}; + +constexpr u32 MaxStageTypes = static_cast(LogicalStage::NumLogicalStages); [[nodiscard]] constexpr Stage StageFromIndex(size_t index) noexcept { return static_cast(index); } +struct LocalRuntimeInfo { + u32 ls_stride; + bool links_with_tcs; + + auto operator<=>(const LocalRuntimeInfo&) const noexcept = default; +}; + struct ExportRuntimeInfo { u32 vertex_data_size; @@ -64,9 +84,57 @@ struct VertexRuntimeInfo { u32 num_outputs; std::array outputs; bool emulate_depth_negative_one_to_one{}; + // Domain + AmdGpu::TessellationType tess_type; + AmdGpu::TessellationTopology tess_topology; + AmdGpu::TessellationPartitioning tess_partitioning; + u32 hs_output_cp_stride{}; bool operator==(const VertexRuntimeInfo& other) const noexcept { - return emulate_depth_negative_one_to_one == other.emulate_depth_negative_one_to_one; + return emulate_depth_negative_one_to_one == other.emulate_depth_negative_one_to_one && + tess_type == other.tess_type && tess_topology == other.tess_topology && + tess_partitioning == other.tess_partitioning && + hs_output_cp_stride == other.hs_output_cp_stride; + } + + void InitFromTessConstants(Shader::TessellationDataConstantBuffer& tess_constants) { + hs_output_cp_stride = tess_constants.hs_cp_stride; + } +}; + +struct HullRuntimeInfo { + // from registers + u32 num_input_control_points; + u32 num_threads; + AmdGpu::TessellationType tess_type; + + // from tess constants buffer + u32 ls_stride; + u32 hs_output_cp_stride; + u32 hs_output_base; + + auto operator<=>(const HullRuntimeInfo&) const noexcept = default; + + // It might be possible for a non-passthrough TCS to have these conditions, in some + // dumb situation. + // In that case, it should be fine to assume passthrough and declare some extra + // output control points and attributes that shouldnt be read by the TES anyways + bool IsPassthrough() const { + return hs_output_base == 0 && ls_stride == hs_output_cp_stride && num_threads == 1; + }; + + // regs.ls_hs_config.hs_output_control_points contains the number of threads, which + // isn't exactly the number of output control points. + // For passthrough shaders, the register field is set to 1, so use the number of + // input control points + u32 NumOutputControlPoints() const { + return IsPassthrough() ? num_input_control_points : num_threads; + } + + void InitFromTessConstants(Shader::TessellationDataConstantBuffer& tess_constants) { + ls_stride = tess_constants.ls_stride; + hs_output_cp_stride = tess_constants.hs_cp_stride; + hs_output_base = tess_constants.hs_output_base; } }; @@ -150,8 +218,10 @@ struct RuntimeInfo { AmdGpu::FpDenormMode fp_denorm_mode32; AmdGpu::FpRoundMode fp_round_mode32; union { + LocalRuntimeInfo ls_info; ExportRuntimeInfo es_info; VertexRuntimeInfo vs_info; + HullRuntimeInfo hs_info; GeometryRuntimeInfo gs_info; FragmentRuntimeInfo fs_info; ComputeRuntimeInfo cs_info; @@ -174,6 +244,10 @@ struct RuntimeInfo { return es_info == other.es_info; case Stage::Geometry: return gs_info == other.gs_info; + case Stage::Hull: + return hs_info == other.hs_info; + case Stage::Local: + return ls_info == other.ls_info; default: return true; } diff --git a/src/shader_recompiler/specialization.h b/src/shader_recompiler/specialization.h index 9b5dd8fa1..5799c4c95 100644 --- a/src/shader_recompiler/specialization.h +++ b/src/shader_recompiler/specialization.h @@ -127,6 +127,18 @@ struct StageSpecialization { [](auto& spec, const auto& desc, AmdGpu::Sampler sharp) { spec.force_unnormalized = sharp.force_unnormalized; }); + + // Initialize runtime_info fields that rely on analysis in tessellation passes + if (info->l_stage == LogicalStage::TessellationControl || + info->l_stage == LogicalStage::TessellationEval) { + Shader::TessellationDataConstantBuffer tess_constants; + info->ReadTessConstantBuffer(tess_constants); + if (info->l_stage == LogicalStage::TessellationControl) { + runtime_info.hs_info.InitFromTessConstants(tess_constants); + } else { + runtime_info.vs_info.InitFromTessConstants(tess_constants); + } + } } void ForEachSharp(auto& spec_list, auto& desc_list, auto&& func) { diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index 9bc3454d8..b6172d37b 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -143,6 +143,13 @@ struct Liverpool { } }; + struct HsTessFactorClamp { + // I've only seen min=0.0, max=1.0 so far. + // TODO why is max set to 1.0? Makes no sense + float hs_max_tess; + float hs_min_tess; + }; + struct ComputeProgram { u32 dispatch_initiator; u32 dim_x; @@ -956,6 +963,7 @@ struct Liverpool { enum VgtStages : u32 { Vs = 0u, // always enabled EsGs = 0xB0u, + LsHs = 0x45u, }; VgtStages raw; @@ -963,7 +971,8 @@ struct Liverpool { BitField<2, 1, u32> hs_en; BitField<3, 2, u32> es_en; BitField<5, 1, u32> gs_en; - BitField<6, 1, u32> vs_en; + BitField<6, 2, u32> vs_en; + BitField<8, 1, u32> dynamic_hs; bool IsStageEnabled(u32 stage) const { switch (stage) { @@ -1059,6 +1068,28 @@ struct Liverpool { }; }; + union LsHsConfig { + u32 raw; + BitField<0, 8, u32> num_patches; + BitField<8, 6, u32> hs_input_control_points; + BitField<14, 6, u32> hs_output_control_points; + }; + + union TessellationConfig { + u32 raw; + BitField<0, 2, TessellationType> type; + BitField<2, 3, TessellationPartitioning> partitioning; + BitField<5, 3, TessellationTopology> topology; + }; + + union TessFactorMemoryBase { + u32 base; + + u64 MemoryBase() const { + return static_cast(base) << 8; + } + }; + union Eqaa { u32 raw; BitField<0, 1, u32> max_anchor_samples; @@ -1109,7 +1140,7 @@ struct Liverpool { ShaderProgram es_program; INSERT_PADDING_WORDS(0x2C); ShaderProgram hs_program; - INSERT_PADDING_WORDS(0x2C); + INSERT_PADDING_WORDS(0x2D48 - 0x2d08 - 20); ShaderProgram ls_program; INSERT_PADDING_WORDS(0xA4); ComputeProgram cs_program; @@ -1176,7 +1207,9 @@ struct Liverpool { PolygonControl polygon_control; ViewportControl viewport_control; VsOutputControl vs_output_control; - INSERT_PADDING_WORDS(0xA290 - 0xA207 - 1); + INSERT_PADDING_WORDS(0xA287 - 0xA207 - 1); + HsTessFactorClamp hs_clamp; + INSERT_PADDING_WORDS(0xA290 - 0xA287 - 2); GsMode vgt_gs_mode; INSERT_PADDING_WORDS(1); ModeControl mode_control; @@ -1200,9 +1233,10 @@ struct Liverpool { BitField<0, 11, u32> vgt_gs_max_vert_out; INSERT_PADDING_WORDS(0xA2D5 - 0xA2CE - 1); ShaderStageEnable stage_enable; - INSERT_PADDING_WORDS(1); + LsHsConfig ls_hs_config; u32 vgt_gs_vert_itemsize[4]; - INSERT_PADDING_WORDS(4); + TessellationConfig tess_config; + INSERT_PADDING_WORDS(3); PolygonOffset poly_offset; GsInstances vgt_gs_instance_cnt; StreamOutConfig vgt_strmout_config; @@ -1216,6 +1250,8 @@ struct Liverpool { INSERT_PADDING_WORDS(0xC24C - 0xC243); u32 num_indices; VgtNumInstances num_instances; + INSERT_PADDING_WORDS(0xC250 - 0xC24D - 1); + TessFactorMemoryBase vgt_tf_memory_base; }; std::array reg_array{}; @@ -1431,6 +1467,7 @@ static_assert(GFX6_3D_REG_INDEX(color_control) == 0xA202); static_assert(GFX6_3D_REG_INDEX(clipper_control) == 0xA204); static_assert(GFX6_3D_REG_INDEX(viewport_control) == 0xA206); static_assert(GFX6_3D_REG_INDEX(vs_output_control) == 0xA207); +static_assert(GFX6_3D_REG_INDEX(hs_clamp) == 0xA287); static_assert(GFX6_3D_REG_INDEX(vgt_gs_mode) == 0xA290); static_assert(GFX6_3D_REG_INDEX(mode_control) == 0xA292); static_assert(GFX6_3D_REG_INDEX(vgt_gs_out_prim_type) == 0xA29B); @@ -1445,6 +1482,7 @@ static_assert(GFX6_3D_REG_INDEX(vgt_gsvs_ring_itemsize) == 0xA2AC); static_assert(GFX6_3D_REG_INDEX(vgt_gs_max_vert_out) == 0xA2CE); static_assert(GFX6_3D_REG_INDEX(stage_enable) == 0xA2D5); static_assert(GFX6_3D_REG_INDEX(vgt_gs_vert_itemsize[0]) == 0xA2D7); +static_assert(GFX6_3D_REG_INDEX(tess_config) == 0xA2DB); static_assert(GFX6_3D_REG_INDEX(poly_offset) == 0xA2DF); static_assert(GFX6_3D_REG_INDEX(vgt_gs_instance_cnt) == 0xA2E4); static_assert(GFX6_3D_REG_INDEX(vgt_strmout_config) == 0xA2E5); @@ -1456,6 +1494,7 @@ static_assert(GFX6_3D_REG_INDEX(color_buffers[0].slice) == 0xA31A); static_assert(GFX6_3D_REG_INDEX(color_buffers[7].base_address) == 0xA381); static_assert(GFX6_3D_REG_INDEX(primitive_type) == 0xC242); static_assert(GFX6_3D_REG_INDEX(num_instances) == 0xC24D); +static_assert(GFX6_3D_REG_INDEX(vgt_tf_memory_base) == 0xc250); #undef GFX6_3D_REG_INDEX diff --git a/src/video_core/amdgpu/types.h b/src/video_core/amdgpu/types.h index 6b95ed910..fa8491665 100644 --- a/src/video_core/amdgpu/types.h +++ b/src/video_core/amdgpu/types.h @@ -3,6 +3,8 @@ #pragma once +#include +#include #include "common/types.h" namespace AmdGpu { @@ -21,6 +23,69 @@ enum class FpDenormMode : u32 { InOutAllow = 3, }; +enum class TessellationType : u32 { + Isoline = 0, + Triangle = 1, + Quad = 2, +}; + +constexpr std::string_view NameOf(TessellationType type) { + switch (type) { + case TessellationType::Isoline: + return "Isoline"; + case TessellationType::Triangle: + return "Triangle"; + case TessellationType::Quad: + return "Quad"; + default: + return "Unknown"; + } +} + +enum class TessellationPartitioning : u32 { + Integer = 0, + Pow2 = 1, + FracOdd = 2, + FracEven = 3, +}; + +constexpr std::string_view NameOf(TessellationPartitioning partitioning) { + switch (partitioning) { + case TessellationPartitioning::Integer: + return "Integer"; + case TessellationPartitioning::Pow2: + return "Pow2"; + case TessellationPartitioning::FracOdd: + return "FracOdd"; + case TessellationPartitioning::FracEven: + return "FracEven"; + default: + return "Unknown"; + } +} + +enum class TessellationTopology : u32 { + Point = 0, + Line = 1, + TriangleCw = 2, + TriangleCcw = 3, +}; + +constexpr std::string_view NameOf(TessellationTopology topology) { + switch (topology) { + case TessellationTopology::Point: + return "Point"; + case TessellationTopology::Line: + return "Line"; + case TessellationTopology::TriangleCw: + return "TriangleCw"; + case TessellationTopology::TriangleCcw: + return "TriangleCcw"; + default: + return "Unknown"; + } +} + // See `VGT_PRIMITIVE_TYPE` description in [Radeon Sea Islands 3D/Compute Register Reference Guide] enum class PrimitiveType : u32 { None = 0, @@ -118,3 +183,33 @@ enum class NumberFormat : u32 { }; } // namespace AmdGpu + +template <> +struct fmt::formatter { + constexpr auto parse(format_parse_context& ctx) { + return ctx.begin(); + } + auto format(AmdGpu::TessellationType type, format_context& ctx) const { + return fmt::format_to(ctx.out(), "{}", AmdGpu::NameOf(type)); + } +}; + +template <> +struct fmt::formatter { + constexpr auto parse(format_parse_context& ctx) { + return ctx.begin(); + } + auto format(AmdGpu::TessellationPartitioning type, format_context& ctx) const { + return fmt::format_to(ctx.out(), "{}", AmdGpu::NameOf(type)); + } +}; + +template <> +struct fmt::formatter { + constexpr auto parse(format_parse_context& ctx) { + return ctx.begin(); + } + auto format(AmdGpu::TessellationTopology type, format_context& ctx) const { + return fmt::format_to(ctx.out(), "{}", AmdGpu::NameOf(type)); + } +}; diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp index 8d495ab06..a39b18378 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp @@ -16,7 +16,7 @@ ComputePipeline::ComputePipeline(const Instance& instance_, Scheduler& scheduler ComputePipelineKey compute_key_, const Shader::Info& info_, vk::ShaderModule module) : Pipeline{instance_, scheduler_, desc_heap_, pipeline_cache, true}, compute_key{compute_key_} { - auto& info = stages[int(Shader::Stage::Compute)]; + auto& info = stages[int(Shader::LogicalStage::Compute)]; info = &info_; const vk::PipelineShaderStageCreateInfo shader_ci = { diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 795537574..222ffb5a9 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -8,6 +8,7 @@ #include "common/assert.h" #include "common/scope_exit.h" +#include "shader_recompiler/runtime_info.h" #include "video_core/amdgpu/resource.h" #include "video_core/buffer_cache/buffer_cache.h" #include "video_core/renderer_vulkan/vk_graphics_pipeline.h" @@ -52,7 +53,7 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul boost::container::static_vector vertex_bindings; boost::container::static_vector vertex_attributes; if (fetch_shader && !instance.IsVertexInputDynamicState()) { - const auto& vs_info = GetStage(Shader::Stage::Vertex); + const auto& vs_info = GetStage(Shader::LogicalStage::Vertex); for (const auto& attrib : fetch_shader->attributes) { if (attrib.UsesStepRates()) { // Skip attribute binding as the data will be pulled by shader @@ -106,6 +107,10 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul key.primitive_restart_index == 0xFFFFFFFF, "Primitive restart index other than -1 is not supported yet"); + const vk::PipelineTessellationStateCreateInfo tessellation_state = { + .patchControlPoints = key.patch_control_points, + }; + const vk::PipelineRasterizationStateCreateInfo raster_state = { .depthClampEnable = false, .rasterizerDiscardEnable = false, @@ -204,7 +209,7 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul boost::container::static_vector shader_stages; - auto stage = u32(Shader::Stage::Vertex); + auto stage = u32(Shader::LogicalStage::Vertex); if (infos[stage]) { shader_stages.emplace_back(vk::PipelineShaderStageCreateInfo{ .stage = vk::ShaderStageFlagBits::eVertex, @@ -212,7 +217,7 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul .pName = "main", }); } - stage = u32(Shader::Stage::Geometry); + stage = u32(Shader::LogicalStage::Geometry); if (infos[stage]) { shader_stages.emplace_back(vk::PipelineShaderStageCreateInfo{ .stage = vk::ShaderStageFlagBits::eGeometry, @@ -220,7 +225,23 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul .pName = "main", }); } - stage = u32(Shader::Stage::Fragment); + stage = u32(Shader::LogicalStage::TessellationControl); + if (infos[stage]) { + shader_stages.emplace_back(vk::PipelineShaderStageCreateInfo{ + .stage = vk::ShaderStageFlagBits::eTessellationControl, + .module = modules[stage], + .pName = "main", + }); + } + stage = u32(Shader::LogicalStage::TessellationEval); + if (infos[stage]) { + shader_stages.emplace_back(vk::PipelineShaderStageCreateInfo{ + .stage = vk::ShaderStageFlagBits::eTessellationEvaluation, + .module = modules[stage], + .pName = "main", + }); + } + stage = u32(Shader::LogicalStage::Fragment); if (infos[stage]) { shader_stages.emplace_back(vk::PipelineShaderStageCreateInfo{ .stage = vk::ShaderStageFlagBits::eFragment, @@ -301,6 +322,8 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul .pStages = shader_stages.data(), .pVertexInputState = !instance.IsVertexInputDynamicState() ? &vertex_input_info : nullptr, .pInputAssemblyState = &input_assembly, + .pTessellationState = + stages[u32(Shader::LogicalStage::TessellationControl)] ? &tessellation_state : nullptr, .pViewportState = &viewport_info, .pRasterizationState = &raster_state, .pMultisampleState = &multisampling, @@ -327,7 +350,6 @@ void GraphicsPipeline::BuildDescSetLayout() { if (!stage) { continue; } - if (stage->has_readconst) { bindings.push_back({ .binding = binding++, diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index 703a0680e..444c8517e 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -52,6 +52,7 @@ struct GraphicsPipelineKey { std::array blend_controls; std::array write_masks; std::array vertex_buffer_formats; + u32 patch_control_points; bool operator==(const GraphicsPipelineKey& key) const noexcept { return std::memcmp(this, &key, sizeof(key)) == 0; @@ -73,7 +74,7 @@ public: bool IsEmbeddedVs() const noexcept { static constexpr size_t EmbeddedVsHash = 0x9b2da5cf47f8c29f; - return key.stage_hashes[u32(Shader::Stage::Vertex)] == EmbeddedVsHash; + return key.stage_hashes[u32(Shader::LogicalStage::Vertex)] == EmbeddedVsHash; } auto GetWriteMasks() const { diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index e844150b2..76efb215d 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -327,6 +327,7 @@ bool Instance::CreateDevice() { .imageCubeArray = features.imageCubeArray, .independentBlend = features.independentBlend, .geometryShader = features.geometryShader, + .tessellationShader = features.tessellationShader, .logicOp = features.logicOp, .depthBiasClamp = features.depthBiasClamp, .fillModeNonSolid = features.fillModeNonSolid, @@ -378,6 +379,7 @@ bool Instance::CreateDevice() { vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT{ .extendedDynamicState = true, }, + vk::PhysicalDeviceExtendedDynamicState2FeaturesEXT{}, vk::PhysicalDeviceExtendedDynamicState3FeaturesEXT{ .extendedDynamicState3ColorWriteMask = true, }, diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index ff27b742f..58473496f 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -22,6 +22,8 @@ extern std::unique_ptr presenter; namespace Vulkan { +using Shader::LogicalStage; +using Shader::Stage; using Shader::VsOutput; constexpr static std::array DescriptorHeapSizes = { @@ -78,7 +80,7 @@ void GatherVertexOutputs(Shader::VertexRuntimeInfo& info, : (ctl.IsCullDistEnabled(7) ? VsOutput::CullDist7 : VsOutput::None)); } -Shader::RuntimeInfo PipelineCache::BuildRuntimeInfo(Shader::Stage stage) { +Shader::RuntimeInfo PipelineCache::BuildRuntimeInfo(Stage stage, LogicalStage l_stage) { auto info = Shader::RuntimeInfo{stage}; const auto& regs = liverpool->regs; const auto BuildCommon = [&](const auto& program) { @@ -89,20 +91,47 @@ Shader::RuntimeInfo PipelineCache::BuildRuntimeInfo(Shader::Stage stage) { info.fp_round_mode32 = program.settings.fp_round_mode32; }; switch (stage) { - case Shader::Stage::Export: { + case Stage::Local: { + BuildCommon(regs.ls_program); + if (regs.stage_enable.IsStageEnabled(static_cast(Stage::Hull))) { + info.ls_info.links_with_tcs = true; + Shader::TessellationDataConstantBuffer tess_constants; + const auto* pgm = regs.ProgramForStage(static_cast(Stage::Hull)); + const auto params = Liverpool::GetParams(*pgm); + const auto& hull_info = program_cache.at(params.hash)->info; + hull_info.ReadTessConstantBuffer(tess_constants); + info.ls_info.ls_stride = tess_constants.ls_stride; + } + break; + } + case Stage::Hull: { + BuildCommon(regs.hs_program); + info.hs_info.num_input_control_points = regs.ls_hs_config.hs_input_control_points.Value(); + info.hs_info.num_threads = regs.ls_hs_config.hs_output_control_points.Value(); + info.hs_info.tess_type = regs.tess_config.type; + + // We need to initialize most hs_info fields after finding the V# with tess constants + break; + } + case Stage::Export: { BuildCommon(regs.es_program); info.es_info.vertex_data_size = regs.vgt_esgs_ring_itemsize; break; } - case Shader::Stage::Vertex: { + case Stage::Vertex: { BuildCommon(regs.vs_program); GatherVertexOutputs(info.vs_info, regs.vs_output_control); info.vs_info.emulate_depth_negative_one_to_one = !instance.IsDepthClipControlSupported() && regs.clipper_control.clip_space == Liverpool::ClipSpace::MinusWToW; + if (l_stage == LogicalStage::TessellationEval) { + info.vs_info.tess_type = regs.tess_config.type; + info.vs_info.tess_topology = regs.tess_config.topology; + info.vs_info.tess_partitioning = regs.tess_config.partitioning; + } break; } - case Shader::Stage::Geometry: { + case Stage::Geometry: { BuildCommon(regs.gs_program); auto& gs_info = info.gs_info; gs_info.output_vertices = regs.vgt_gs_max_vert_out; @@ -121,7 +150,7 @@ Shader::RuntimeInfo PipelineCache::BuildRuntimeInfo(Shader::Stage stage) { DumpShader(gs_info.vs_copy, gs_info.vs_copy_hash, Shader::Stage::Vertex, 0, "copy.bin"); break; } - case Shader::Stage::Fragment: { + case Stage::Fragment: { BuildCommon(regs.ps_program); info.fs_info.en_flags = regs.ps_input_ena; info.fs_info.addr_flags = regs.ps_input_addr; @@ -143,7 +172,7 @@ Shader::RuntimeInfo PipelineCache::BuildRuntimeInfo(Shader::Stage stage) { } break; } - case Shader::Stage::Compute: { + case Stage::Compute: { const auto& cs_pgm = regs.cs_program; info.num_user_data = cs_pgm.settings.num_user_regs; info.num_allocated_vgprs = regs.cs_program.settings.num_vgprs * 4; @@ -277,6 +306,11 @@ bool PipelineCache::RefreshGraphicsKey() { key.mrt_swizzles.fill(Liverpool::ColorBuffer::SwapMode::Standard); key.vertex_buffer_formats.fill(vk::Format::eUndefined); + key.patch_control_points = 0; + if (regs.stage_enable.hs_en.Value()) { + key.patch_control_points = regs.ls_hs_config.hs_input_control_points.Value(); + } + // First pass of bindings check to idenitfy formats and swizzles and pass them to rhe shader // recompiler. for (auto cb = 0u; cb < Liverpool::NumColorBuffers; ++cb) { @@ -305,7 +339,7 @@ bool PipelineCache::RefreshGraphicsKey() { fetch_shader = std::nullopt; Shader::Backend::Bindings binding{}; - const auto& TryBindStageRemap = [&](Shader::Stage stage_in, Shader::Stage stage_out) -> bool { + const auto& TryBindStage = [&](Shader::Stage stage_in, Shader::LogicalStage stage_out) -> bool { const auto stage_in_idx = static_cast(stage_in); const auto stage_out_idx = static_cast(stage_out); if (!regs.stage_enable.IsStageEnabled(stage_in_idx)) { @@ -332,23 +366,23 @@ bool PipelineCache::RefreshGraphicsKey() { auto params = Liverpool::GetParams(*pgm); std::optional fetch_shader_; std::tie(infos[stage_out_idx], modules[stage_out_idx], fetch_shader_, - key.stage_hashes[stage_out_idx]) = GetProgram(stage_in, params, binding); + key.stage_hashes[stage_out_idx]) = + GetProgram(stage_in, stage_out, params, binding); if (fetch_shader_) { fetch_shader = fetch_shader_; } return true; }; - const auto& TryBindStage = [&](Shader::Stage stage) { return TryBindStageRemap(stage, stage); }; - const auto& IsGsFeaturesSupported = [&]() -> bool { // These checks are temporary until all functionality is implemented. return !regs.vgt_gs_mode.onchip && !regs.vgt_strmout_config.raw; }; - TryBindStage(Shader::Stage::Fragment); + infos.fill(nullptr); + TryBindStage(Stage::Fragment, LogicalStage::Fragment); - const auto* fs_info = infos[static_cast(Shader::Stage::Fragment)]; + const auto* fs_info = infos[static_cast(LogicalStage::Fragment)]; key.mrt_mask = fs_info ? fs_info->mrt_mask : 0u; switch (regs.stage_enable.raw) { @@ -356,22 +390,36 @@ bool PipelineCache::RefreshGraphicsKey() { if (!instance.IsGeometryStageSupported() || !IsGsFeaturesSupported()) { return false; } - if (!TryBindStageRemap(Shader::Stage::Export, Shader::Stage::Vertex)) { + if (!TryBindStage(Stage::Export, LogicalStage::Vertex)) { return false; } - if (!TryBindStage(Shader::Stage::Geometry)) { + if (!TryBindStage(Stage::Geometry, LogicalStage::Geometry)) { + return false; + } + break; + } + case Liverpool::ShaderStageEnable::VgtStages::LsHs: { + if (!instance.IsTessellationSupported()) { + break; + } + if (!TryBindStage(Stage::Hull, LogicalStage::TessellationControl)) { + return false; + } + if (!TryBindStage(Stage::Vertex, LogicalStage::TessellationEval)) { + return false; + } + if (!TryBindStage(Stage::Local, LogicalStage::Vertex)) { return false; } break; } default: { - TryBindStage(Shader::Stage::Vertex); - infos[static_cast(Shader::Stage::Geometry)] = nullptr; + TryBindStage(Stage::Vertex, LogicalStage::Vertex); break; } } - const auto vs_info = infos[static_cast(Shader::Stage::Vertex)]; + const auto vs_info = infos[static_cast(Shader::LogicalStage::Vertex)]; if (vs_info && fetch_shader && !instance.IsVertexInputDynamicState()) { u32 vertex_binding = 0; for (const auto& attrib : fetch_shader->attributes) { @@ -424,19 +472,18 @@ bool PipelineCache::RefreshGraphicsKey() { key.num_samples = num_samples; return true; -} +} // namespace Vulkan bool PipelineCache::RefreshComputeKey() { Shader::Backend::Bindings binding{}; const auto* cs_pgm = &liverpool->regs.cs_program; const auto cs_params = Liverpool::GetParams(*cs_pgm); std::tie(infos[0], modules[0], fetch_shader, compute_key.value) = - GetProgram(Shader::Stage::Compute, cs_params, binding); + GetProgram(Shader::Stage::Compute, LogicalStage::Compute, cs_params, binding); return true; } -vk::ShaderModule PipelineCache::CompileModule(Shader::Info& info, - const Shader::RuntimeInfo& runtime_info, +vk::ShaderModule PipelineCache::CompileModule(Shader::Info& info, Shader::RuntimeInfo& runtime_info, std::span code, size_t perm_idx, Shader::Backend::Bindings& binding) { LOG_INFO(Render_Vulkan, "Compiling {} shader {:#x} {}", info.stage, info.pgm_hash, @@ -461,19 +508,19 @@ vk::ShaderModule PipelineCache::CompileModule(Shader::Info& info, const auto name = fmt::format("{}_{:#018x}_{}", info.stage, info.pgm_hash, perm_idx); Vulkan::SetObjectName(instance.GetDevice(), module, name); if (Config::collectShadersForDebug()) { - DebugState.CollectShader(name, module, spv, code, patch ? *patch : std::span{}, - is_patched); + DebugState.CollectShader(name, info.l_stage, module, spv, code, + patch ? *patch : std::span{}, is_patched); } return module; } -std::tuple, u64> -PipelineCache::GetProgram(Shader::Stage stage, Shader::ShaderParams params, - Shader::Backend::Bindings& binding) { - const auto runtime_info = BuildRuntimeInfo(stage); +PipelineCache::Result PipelineCache::GetProgram(Stage stage, LogicalStage l_stage, + Shader::ShaderParams params, + Shader::Backend::Bindings& binding) { + auto runtime_info = BuildRuntimeInfo(stage, l_stage); auto [it_pgm, new_program] = program_cache.try_emplace(params.hash); if (new_program) { - it_pgm.value() = std::make_unique(stage, params); + it_pgm.value() = std::make_unique(stage, l_stage, params); auto& program = it_pgm.value(); auto start = binding; const auto module = CompileModule(program->info, runtime_info, params.code, 0, binding); @@ -492,7 +539,7 @@ PipelineCache::GetProgram(Shader::Stage stage, Shader::ShaderParams params, const auto it = std::ranges::find(program->modules, spec, &Program::Module::spec); if (it == program->modules.end()) { - auto new_info = Shader::Info(stage, params); + auto new_info = Shader::Info(stage, l_stage, params); module = CompileModule(new_info, runtime_info, params.code, perm_idx, binding); program->AddPermut(module, std::move(spec)); } else { diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index c5c2fc98e..ec4406448 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h @@ -34,11 +34,13 @@ struct Program { vk::ShaderModule module; Shader::StageSpecialization spec; }; + using ModuleList = boost::container::small_vector; Shader::Info info; - boost::container::small_vector modules; + ModuleList modules; - explicit Program(Shader::Stage stage, Shader::ShaderParams params) : info{stage, params} {} + explicit Program(Shader::Stage stage, Shader::LogicalStage l_stage, Shader::ShaderParams params) + : info{stage, l_stage, params} {} void AddPermut(vk::ShaderModule module, const Shader::StageSpecialization&& spec) { modules.emplace_back(module, std::move(spec)); @@ -55,10 +57,10 @@ public: const ComputePipeline* GetComputePipeline(); - std::tuple, - u64> - GetProgram(Shader::Stage stage, Shader::ShaderParams params, - Shader::Backend::Bindings& binding); + using Result = std::tuple, u64>; + Result GetProgram(Shader::Stage stage, Shader::LogicalStage l_stage, + Shader::ShaderParams params, Shader::Backend::Bindings& binding); std::optional ReplaceShader(vk::ShaderModule module, std::span spv_code); @@ -71,10 +73,10 @@ private: std::string_view ext); std::optional> GetShaderPatch(u64 hash, Shader::Stage stage, size_t perm_idx, std::string_view ext); - vk::ShaderModule CompileModule(Shader::Info& info, const Shader::RuntimeInfo& runtime_info, + vk::ShaderModule CompileModule(Shader::Info& info, Shader::RuntimeInfo& runtime_info, std::span code, size_t perm_idx, Shader::Backend::Bindings& binding); - Shader::RuntimeInfo BuildRuntimeInfo(Shader::Stage stage); + Shader::RuntimeInfo BuildRuntimeInfo(Shader::Stage stage, Shader::LogicalStage l_stage); private: const Instance& instance; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_common.h b/src/video_core/renderer_vulkan/vk_pipeline_common.h index 8c48c83f7..1b13a1797 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_common.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_common.h @@ -14,9 +14,10 @@ class BufferCache; namespace Vulkan { -static constexpr auto gp_stage_flags = vk::ShaderStageFlagBits::eVertex | - vk::ShaderStageFlagBits::eGeometry | - vk::ShaderStageFlagBits::eFragment; +static constexpr auto gp_stage_flags = + vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eTessellationControl | + vk::ShaderStageFlagBits::eTessellationEvaluation | vk::ShaderStageFlagBits::eGeometry | + vk::ShaderStageFlagBits::eFragment; class Instance; class Scheduler; @@ -37,6 +38,7 @@ public: } auto GetStages() const { + static_assert(static_cast(Shader::LogicalStage::Compute) == Shader::MaxStageTypes - 1); if (is_compute) { return std::span{stages.cend() - 1, stages.cend()}; } else { @@ -44,7 +46,7 @@ public: } } - const Shader::Info& GetStage(Shader::Stage stage) const noexcept { + const Shader::Info& GetStage(Shader::LogicalStage stage) const noexcept { return *stages[u32(stage)]; } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index eb2ef3600..fef4c7ec5 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -4,6 +4,7 @@ #include "common/config.h" #include "common/debug.h" #include "core/memory.h" +#include "shader_recompiler/runtime_info.h" #include "video_core/amdgpu/liverpool.h" #include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_rasterizer.h" @@ -48,10 +49,6 @@ void Rasterizer::CpSync() { bool Rasterizer::FilterDraw() { const auto& regs = liverpool->regs; - // Tessellation is unsupported so skip the draw to avoid locking up the driver. - if (regs.primitive_type == AmdGpu::PrimitiveType::PatchPrimitive) { - return false; - } // There are several cases (e.g. FCE, FMask/HTile decompression) where we don't need to do an // actual draw hence can skip pipeline creation. if (regs.color_control.mode == Liverpool::ColorControl::OperationMode::EliminateFastClear) { @@ -214,7 +211,7 @@ void Rasterizer::Draw(bool is_indexed, u32 index_offset) { return; } - const auto& vs_info = pipeline->GetStage(Shader::Stage::Vertex); + const auto& vs_info = pipeline->GetStage(Shader::LogicalStage::Vertex); const auto& fetch_shader = pipeline->GetFetchShader(); buffer_cache.BindVertexBuffers(vs_info, fetch_shader); const u32 num_indices = buffer_cache.BindIndexBuffer(is_indexed, index_offset); @@ -271,7 +268,7 @@ void Rasterizer::DrawIndirect(bool is_indexed, VAddr arg_address, u32 offset, u3 return; } - const auto& vs_info = pipeline->GetStage(Shader::Stage::Vertex); + const auto& vs_info = pipeline->GetStage(Shader::LogicalStage::Vertex); const auto& fetch_shader = pipeline->GetFetchShader(); buffer_cache.BindVertexBuffers(vs_info, fetch_shader); buffer_cache.BindIndexBuffer(is_indexed, 0); @@ -326,7 +323,7 @@ void Rasterizer::DispatchDirect() { return; } - const auto& cs = pipeline->GetStage(Shader::Stage::Compute); + const auto& cs = pipeline->GetStage(Shader::LogicalStage::Compute); if (ExecuteShaderHLE(cs, liverpool->regs, *this)) { return; } @@ -387,7 +384,7 @@ bool Rasterizer::BindResources(const Pipeline* pipeline) { const auto& regs = liverpool->regs; if (pipeline->IsCompute()) { - const auto& info = pipeline->GetStage(Shader::Stage::Compute); + const auto& info = pipeline->GetStage(Shader::LogicalStage::Compute); // Most of the time when a metadata is updated with a shader it gets cleared. It means // we can skip the whole dispatch and update the tracked state instead. Also, it is not From cafd40f2c2f2d0062979ad1ec12b6d755eeb4e81 Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Sat, 14 Dec 2024 15:33:06 +0300 Subject: [PATCH 186/549] DmaData and Recompiler fixes (#1775) * liverpool: fix dmadata packet handling * recompiler: emit a label right after s_branch to prevent dead code interferrence * specialize barriers --- .../frontend/control_flow_graph.cpp | 1 + src/video_core/amdgpu/liverpool.cpp | 26 ++-- src/video_core/buffer_cache/buffer_cache.cpp | 124 +++++++++++++++++- src/video_core/buffer_cache/buffer_cache.h | 1 + .../renderer_vulkan/vk_rasterizer.cpp | 4 + .../renderer_vulkan/vk_rasterizer.h | 1 + 6 files changed, 140 insertions(+), 17 deletions(-) diff --git a/src/shader_recompiler/frontend/control_flow_graph.cpp b/src/shader_recompiler/frontend/control_flow_graph.cpp index 8c3122b28..1fb129f6c 100644 --- a/src/shader_recompiler/frontend/control_flow_graph.cpp +++ b/src/shader_recompiler/frontend/control_flow_graph.cpp @@ -80,6 +80,7 @@ void CFG::EmitLabels() { if (inst.IsUnconditionalBranch()) { const u32 target = inst.BranchTarget(pc); AddLabel(target); + AddLabel(pc + inst.length); } else if (inst.IsConditionalBranch()) { const u32 true_label = inst.BranchTarget(pc); const u32 false_label = pc + inst.length; diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index 8db2d63c4..820903ab7 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -573,21 +573,21 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spansrc_sel == DmaDataSrc::Memory && dma_data->dst_sel == DmaDataDst::Gds) { - rasterizer->InlineData(dma_data->dst_addr_lo, - dma_data->SrcAddress(), - dma_data->NumBytes(), true); + rasterizer->CopyBuffer(dma_data->dst_addr_lo, dma_data->SrcAddress(), + dma_data->NumBytes(), true, false); } else if (dma_data->src_sel == DmaDataSrc::Data && dma_data->dst_sel == DmaDataDst::Memory) { rasterizer->InlineData(dma_data->DstAddress(), &dma_data->data, sizeof(u32), false); } else if (dma_data->src_sel == DmaDataSrc::Gds && dma_data->dst_sel == DmaDataDst::Memory) { - // LOG_WARNING(Render_Vulkan, "GDS memory read"); + rasterizer->CopyBuffer(dma_data->DstAddress(), dma_data->src_addr_lo, + dma_data->NumBytes(), false, true); } else if (dma_data->src_sel == DmaDataSrc::Memory && dma_data->dst_sel == DmaDataDst::Memory) { - rasterizer->InlineData(dma_data->DstAddress(), - dma_data->SrcAddress(), - dma_data->NumBytes(), false); + rasterizer->CopyBuffer(dma_data->DstAddress(), + dma_data->SrcAddress(), dma_data->NumBytes(), + false, false); } else { UNREACHABLE_MSG("WriteData src_sel = {}, dst_sel = {}", u32(dma_data->src_sel.Value()), u32(dma_data->dst_sel.Value())); @@ -731,20 +731,20 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, int vqid) { rasterizer->InlineData(dma_data->dst_addr_lo, &dma_data->data, sizeof(u32), true); } else if (dma_data->src_sel == DmaDataSrc::Memory && dma_data->dst_sel == DmaDataDst::Gds) { - rasterizer->InlineData(dma_data->dst_addr_lo, dma_data->SrcAddress(), - dma_data->NumBytes(), true); + rasterizer->CopyBuffer(dma_data->dst_addr_lo, dma_data->SrcAddress(), + dma_data->NumBytes(), true, false); } else if (dma_data->src_sel == DmaDataSrc::Data && dma_data->dst_sel == DmaDataDst::Memory) { rasterizer->InlineData(dma_data->DstAddress(), &dma_data->data, sizeof(u32), false); } else if (dma_data->src_sel == DmaDataSrc::Gds && dma_data->dst_sel == DmaDataDst::Memory) { - // LOG_WARNING(Render_Vulkan, "GDS memory read"); + rasterizer->CopyBuffer(dma_data->DstAddress(), dma_data->src_addr_lo, + dma_data->NumBytes(), false, true); } else if (dma_data->src_sel == DmaDataSrc::Memory && dma_data->dst_sel == DmaDataDst::Memory) { - rasterizer->InlineData(dma_data->DstAddress(), - dma_data->SrcAddress(), dma_data->NumBytes(), - false); + rasterizer->CopyBuffer(dma_data->DstAddress(), dma_data->SrcAddress(), + dma_data->NumBytes(), false, false); } else { UNREACHABLE_MSG("WriteData src_sel = {}, dst_sel = {}", u32(dma_data->src_sel.Value()), u32(dma_data->dst_sel.Value())); diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index e9fc06493..31b2a2c58 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -312,8 +312,23 @@ void BufferCache::InlineData(VAddr address, const void* value, u32 num_bytes, bo const BufferId buffer_id = FindBuffer(address, num_bytes); return &slot_buffers[buffer_id]; }(); - const vk::BufferMemoryBarrier2 buf_barrier = { - .srcStageMask = vk::PipelineStageFlagBits2::eTransfer, + const vk::BufferMemoryBarrier2 buf_barrier_before = { + .srcStageMask = vk::PipelineStageFlagBits2::eAllCommands, + .srcAccessMask = vk::AccessFlagBits2::eMemoryRead, + .dstStageMask = vk::PipelineStageFlagBits2::eAllCommands, + .dstAccessMask = vk::AccessFlagBits2::eTransferWrite, + .buffer = buffer->Handle(), + .offset = buffer->Offset(address), + .size = num_bytes, + }; + cmdbuf.pipelineBarrier2(vk::DependencyInfo{ + .dependencyFlags = vk::DependencyFlagBits::eByRegion, + .bufferMemoryBarrierCount = 1, + .pBufferMemoryBarriers = &buf_barrier_before, + }); + cmdbuf.updateBuffer(buffer->Handle(), buffer->Offset(address), num_bytes, value); + const vk::BufferMemoryBarrier2 buf_barrier_after = { + .srcStageMask = vk::PipelineStageFlagBits2::eAllCommands, .srcAccessMask = vk::AccessFlagBits2::eTransferWrite, .dstStageMask = vk::PipelineStageFlagBits2::eAllCommands, .dstAccessMask = vk::AccessFlagBits2::eMemoryRead, @@ -324,9 +339,96 @@ void BufferCache::InlineData(VAddr address, const void* value, u32 num_bytes, bo cmdbuf.pipelineBarrier2(vk::DependencyInfo{ .dependencyFlags = vk::DependencyFlagBits::eByRegion, .bufferMemoryBarrierCount = 1, - .pBufferMemoryBarriers = &buf_barrier, + .pBufferMemoryBarriers = &buf_barrier_after, + }); +} + +void BufferCache::CopyBuffer(VAddr dst, VAddr src, u32 num_bytes, bool dst_gds, bool src_gds) { + if (!dst_gds && !IsRegionRegistered(dst, num_bytes)) { + if (!src_gds && !IsRegionRegistered(src, num_bytes)) { + // Both buffers were not transferred to GPU yet. Can safely copy in host memory. + memcpy(std::bit_cast(dst), std::bit_cast(src), num_bytes); + return; + } + // Without a readback there's nothing we can do with this + // Fallback to creating dst buffer on GPU to at least have this data there + } + if (!src_gds && !IsRegionRegistered(src, num_bytes)) { + InlineData(dst, std::bit_cast(src), num_bytes, dst_gds); + return; + } + auto& src_buffer = [&] -> const Buffer& { + if (src_gds) { + return gds_buffer; + } + const BufferId buffer_id = FindBuffer(src, num_bytes); + return slot_buffers[buffer_id]; + }(); + auto& dst_buffer = [&] -> const Buffer& { + if (dst_gds) { + return gds_buffer; + } + const BufferId buffer_id = FindBuffer(dst, num_bytes); + return slot_buffers[buffer_id]; + }(); + vk::BufferCopy region{ + .srcOffset = src_buffer.Offset(src), + .dstOffset = dst_buffer.Offset(dst), + .size = num_bytes, + }; + const vk::BufferMemoryBarrier2 buf_barriers_before[2] = { + { + .srcStageMask = vk::PipelineStageFlagBits2::eAllCommands, + .srcAccessMask = vk::AccessFlagBits2::eMemoryRead, + .dstStageMask = vk::PipelineStageFlagBits2::eAllCommands, + .dstAccessMask = vk::AccessFlagBits2::eTransferWrite, + .buffer = dst_buffer.Handle(), + .offset = dst_buffer.Offset(dst), + .size = num_bytes, + }, + { + .srcStageMask = vk::PipelineStageFlagBits2::eAllCommands, + .srcAccessMask = vk::AccessFlagBits2::eMemoryWrite, + .dstStageMask = vk::PipelineStageFlagBits2::eAllCommands, + .dstAccessMask = vk::AccessFlagBits2::eTransferRead, + .buffer = src_buffer.Handle(), + .offset = src_buffer.Offset(src), + .size = num_bytes, + }, + }; + scheduler.EndRendering(); + const auto cmdbuf = scheduler.CommandBuffer(); + cmdbuf.pipelineBarrier2(vk::DependencyInfo{ + .dependencyFlags = vk::DependencyFlagBits::eByRegion, + .bufferMemoryBarrierCount = 2, + .pBufferMemoryBarriers = buf_barriers_before, + }); + cmdbuf.copyBuffer(src_buffer.Handle(), dst_buffer.Handle(), region); + const vk::BufferMemoryBarrier2 buf_barriers_after[2] = { + { + .srcStageMask = vk::PipelineStageFlagBits2::eAllCommands, + .srcAccessMask = vk::AccessFlagBits2::eTransferWrite, + .dstStageMask = vk::PipelineStageFlagBits2::eAllCommands, + .dstAccessMask = vk::AccessFlagBits2::eMemoryRead, + .buffer = dst_buffer.Handle(), + .offset = dst_buffer.Offset(dst), + .size = num_bytes, + }, + { + .srcStageMask = vk::PipelineStageFlagBits2::eAllCommands, + .srcAccessMask = vk::AccessFlagBits2::eTransferRead, + .dstStageMask = vk::PipelineStageFlagBits2::eAllCommands, + .dstAccessMask = vk::AccessFlagBits2::eMemoryWrite, + .buffer = src_buffer.Handle(), + .offset = src_buffer.Offset(src), + .size = num_bytes, + }, + }; + cmdbuf.pipelineBarrier2(vk::DependencyInfo{ + .dependencyFlags = vk::DependencyFlagBits::eByRegion, + .bufferMemoryBarrierCount = 2, + .pBufferMemoryBarriers = buf_barriers_after, }); - cmdbuf.updateBuffer(buffer->Handle(), buf_barrier.offset, num_bytes, value); } std::pair BufferCache::ObtainHostUBO(std::span data) { @@ -701,8 +803,22 @@ bool BufferCache::SynchronizeBufferFromImage(Buffer& buffer, VAddr device_addr, scheduler.EndRendering(); image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, {}); const auto cmdbuf = scheduler.CommandBuffer(); + static constexpr vk::MemoryBarrier READ_BARRIER{ + .srcAccessMask = vk::AccessFlagBits::eMemoryWrite, + .dstAccessMask = vk::AccessFlagBits::eTransferRead | vk::AccessFlagBits::eTransferWrite, + }; + static constexpr vk::MemoryBarrier WRITE_BARRIER{ + .srcAccessMask = vk::AccessFlagBits::eTransferWrite, + .dstAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite, + }; + cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands, + vk::PipelineStageFlagBits::eTransfer, + vk::DependencyFlagBits::eByRegion, READ_BARRIER, {}, {}); cmdbuf.copyImageToBuffer(image.image, vk::ImageLayout::eTransferSrcOptimal, buffer.buffer, copies); + cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands, + vk::PipelineStageFlagBits::eTransfer, + vk::DependencyFlagBits::eByRegion, WRITE_BARRIER, {}, {}); } return true; } diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index e62913413..4c57e9c29 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -87,6 +87,7 @@ public: /// Writes a value to GPU buffer. void InlineData(VAddr address, const void* value, u32 num_bytes, bool is_gds); + void CopyBuffer(VAddr dst, VAddr src, u32 num_bytes, bool dst_gds, bool src_gds); [[nodiscard]] std::pair ObtainHostUBO(std::span data); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index fef4c7ec5..9e9b40ca5 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -840,6 +840,10 @@ void Rasterizer::InlineData(VAddr address, const void* value, u32 num_bytes, boo buffer_cache.InlineData(address, value, num_bytes, is_gds); } +void Rasterizer::CopyBuffer(VAddr dst, VAddr src, u32 num_bytes, bool dst_gds, bool src_gds) { + buffer_cache.CopyBuffer(dst, src, num_bytes, dst_gds, src_gds); +} + u32 Rasterizer::ReadDataFromGds(u32 gds_offset) { auto* gds_buf = buffer_cache.GetGdsBuffer(); u32 value; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index ec1b5e134..b5bead697 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -53,6 +53,7 @@ public: void ScopedMarkerInsertColor(const std::string_view& str, const u32 color); void InlineData(VAddr address, const void* value, u32 num_bytes, bool is_gds); + void CopyBuffer(VAddr dst, VAddr src, u32 num_bytes, bool dst_gds, bool src_gds); u32 ReadDataFromGds(u32 gsd_offset); bool InvalidateMemory(VAddr addr, u64 size); bool IsMapped(VAddr addr, u64 size); From e752f04cde25941818e141cc933ffa380cc321e5 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sat, 14 Dec 2024 04:33:24 -0800 Subject: [PATCH 187/549] shader_recompiler: Fixups from stencil changes (#1776) --- src/shader_recompiler/frontend/translate/export.cpp | 2 +- src/video_core/texture_cache/image_view.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/shader_recompiler/frontend/translate/export.cpp b/src/shader_recompiler/frontend/translate/export.cpp index f4914577d..5927aa696 100644 --- a/src/shader_recompiler/frontend/translate/export.cpp +++ b/src/shader_recompiler/frontend/translate/export.cpp @@ -13,7 +13,7 @@ void Translator::EmitExport(const GcnInst& inst) { const auto& exp = inst.control.exp; const IR::Attribute attrib{exp.target}; - if (attrib == IR::Attribute::Depth && exp.en != 1) { + if (attrib == IR::Attribute::Depth && exp.en != 0 && exp.en != 1) { LOG_WARNING(Render_Vulkan, "Unsupported depth export"); return; } diff --git a/src/video_core/texture_cache/image_view.cpp b/src/video_core/texture_cache/image_view.cpp index 41c45019e..ec1fda0d8 100644 --- a/src/video_core/texture_cache/image_view.cpp +++ b/src/video_core/texture_cache/image_view.cpp @@ -131,7 +131,8 @@ ImageView::ImageView(const Vulkan::Instance& instance, const ImageViewInfo& info format = image.info.pixel_format; aspect = vk::ImageAspectFlagBits::eDepth; } - if (image.aspect_mask & vk::ImageAspectFlagBits::eStencil && format == vk::Format::eR8Uint) { + if (image.aspect_mask & vk::ImageAspectFlagBits::eStencil && + (format == vk::Format::eR8Uint || format == vk::Format::eR8Unorm)) { format = image.info.pixel_format; aspect = vk::ImageAspectFlagBits::eStencil; } From 27447537c3e846e0da923fdb1525e4253b555849 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sat, 14 Dec 2024 06:12:41 -0800 Subject: [PATCH 188/549] externals: Update sirit to fix debug assert (#1783) --- externals/sirit | 2 +- src/shader_recompiler/backend/spirv/emit_spirv_image.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/externals/sirit b/externals/sirit index 5b5ff49a5..1e74f4ef8 160000 --- a/externals/sirit +++ b/externals/sirit @@ -1 +1 @@ -Subproject commit 5b5ff49a58f5be27af1058794c6ca907dabc05b3 +Subproject commit 1e74f4ef8d2a0e3221a4de51977663f342b53c35 diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index 8da9280d0..e5d4f3077 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp @@ -247,7 +247,7 @@ void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id ImageOperands operands; if (ctx.profile.supports_image_load_store_lod) { operands.Add(spv::ImageOperandsMask::Lod, lod); - } else if (lod.value != 0) { + } else if (Sirit::ValidId(lod)) { LOG_WARNING(Render, "Image write with LOD not supported by driver"); } ctx.OpImageWrite(image, coords, ctx.OpBitcast(color_type, color), operands.mask, From e9ede8d62749d2697c0b807296846d132acf4919 Mon Sep 17 00:00:00 2001 From: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> Date: Sat, 14 Dec 2024 16:17:14 +0200 Subject: [PATCH 189/549] Revert "DmaData and Recompiler fixes (#1775)" (#1784) This reverts commit cafd40f2c2f2d0062979ad1ec12b6d755eeb4e81. --- .../frontend/control_flow_graph.cpp | 1 - src/video_core/amdgpu/liverpool.cpp | 26 ++-- src/video_core/buffer_cache/buffer_cache.cpp | 124 +----------------- src/video_core/buffer_cache/buffer_cache.h | 1 - .../renderer_vulkan/vk_rasterizer.cpp | 4 - .../renderer_vulkan/vk_rasterizer.h | 1 - 6 files changed, 17 insertions(+), 140 deletions(-) diff --git a/src/shader_recompiler/frontend/control_flow_graph.cpp b/src/shader_recompiler/frontend/control_flow_graph.cpp index 1fb129f6c..8c3122b28 100644 --- a/src/shader_recompiler/frontend/control_flow_graph.cpp +++ b/src/shader_recompiler/frontend/control_flow_graph.cpp @@ -80,7 +80,6 @@ void CFG::EmitLabels() { if (inst.IsUnconditionalBranch()) { const u32 target = inst.BranchTarget(pc); AddLabel(target); - AddLabel(pc + inst.length); } else if (inst.IsConditionalBranch()) { const u32 true_label = inst.BranchTarget(pc); const u32 false_label = pc + inst.length; diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index 820903ab7..8db2d63c4 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -573,21 +573,21 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spansrc_sel == DmaDataSrc::Memory && dma_data->dst_sel == DmaDataDst::Gds) { - rasterizer->CopyBuffer(dma_data->dst_addr_lo, dma_data->SrcAddress(), - dma_data->NumBytes(), true, false); + rasterizer->InlineData(dma_data->dst_addr_lo, + dma_data->SrcAddress(), + dma_data->NumBytes(), true); } else if (dma_data->src_sel == DmaDataSrc::Data && dma_data->dst_sel == DmaDataDst::Memory) { rasterizer->InlineData(dma_data->DstAddress(), &dma_data->data, sizeof(u32), false); } else if (dma_data->src_sel == DmaDataSrc::Gds && dma_data->dst_sel == DmaDataDst::Memory) { - rasterizer->CopyBuffer(dma_data->DstAddress(), dma_data->src_addr_lo, - dma_data->NumBytes(), false, true); + // LOG_WARNING(Render_Vulkan, "GDS memory read"); } else if (dma_data->src_sel == DmaDataSrc::Memory && dma_data->dst_sel == DmaDataDst::Memory) { - rasterizer->CopyBuffer(dma_data->DstAddress(), - dma_data->SrcAddress(), dma_data->NumBytes(), - false, false); + rasterizer->InlineData(dma_data->DstAddress(), + dma_data->SrcAddress(), + dma_data->NumBytes(), false); } else { UNREACHABLE_MSG("WriteData src_sel = {}, dst_sel = {}", u32(dma_data->src_sel.Value()), u32(dma_data->dst_sel.Value())); @@ -731,20 +731,20 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, int vqid) { rasterizer->InlineData(dma_data->dst_addr_lo, &dma_data->data, sizeof(u32), true); } else if (dma_data->src_sel == DmaDataSrc::Memory && dma_data->dst_sel == DmaDataDst::Gds) { - rasterizer->CopyBuffer(dma_data->dst_addr_lo, dma_data->SrcAddress(), - dma_data->NumBytes(), true, false); + rasterizer->InlineData(dma_data->dst_addr_lo, dma_data->SrcAddress(), + dma_data->NumBytes(), true); } else if (dma_data->src_sel == DmaDataSrc::Data && dma_data->dst_sel == DmaDataDst::Memory) { rasterizer->InlineData(dma_data->DstAddress(), &dma_data->data, sizeof(u32), false); } else if (dma_data->src_sel == DmaDataSrc::Gds && dma_data->dst_sel == DmaDataDst::Memory) { - rasterizer->CopyBuffer(dma_data->DstAddress(), dma_data->src_addr_lo, - dma_data->NumBytes(), false, true); + // LOG_WARNING(Render_Vulkan, "GDS memory read"); } else if (dma_data->src_sel == DmaDataSrc::Memory && dma_data->dst_sel == DmaDataDst::Memory) { - rasterizer->CopyBuffer(dma_data->DstAddress(), dma_data->SrcAddress(), - dma_data->NumBytes(), false, false); + rasterizer->InlineData(dma_data->DstAddress(), + dma_data->SrcAddress(), dma_data->NumBytes(), + false); } else { UNREACHABLE_MSG("WriteData src_sel = {}, dst_sel = {}", u32(dma_data->src_sel.Value()), u32(dma_data->dst_sel.Value())); diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index 31b2a2c58..e9fc06493 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -312,23 +312,8 @@ void BufferCache::InlineData(VAddr address, const void* value, u32 num_bytes, bo const BufferId buffer_id = FindBuffer(address, num_bytes); return &slot_buffers[buffer_id]; }(); - const vk::BufferMemoryBarrier2 buf_barrier_before = { - .srcStageMask = vk::PipelineStageFlagBits2::eAllCommands, - .srcAccessMask = vk::AccessFlagBits2::eMemoryRead, - .dstStageMask = vk::PipelineStageFlagBits2::eAllCommands, - .dstAccessMask = vk::AccessFlagBits2::eTransferWrite, - .buffer = buffer->Handle(), - .offset = buffer->Offset(address), - .size = num_bytes, - }; - cmdbuf.pipelineBarrier2(vk::DependencyInfo{ - .dependencyFlags = vk::DependencyFlagBits::eByRegion, - .bufferMemoryBarrierCount = 1, - .pBufferMemoryBarriers = &buf_barrier_before, - }); - cmdbuf.updateBuffer(buffer->Handle(), buffer->Offset(address), num_bytes, value); - const vk::BufferMemoryBarrier2 buf_barrier_after = { - .srcStageMask = vk::PipelineStageFlagBits2::eAllCommands, + const vk::BufferMemoryBarrier2 buf_barrier = { + .srcStageMask = vk::PipelineStageFlagBits2::eTransfer, .srcAccessMask = vk::AccessFlagBits2::eTransferWrite, .dstStageMask = vk::PipelineStageFlagBits2::eAllCommands, .dstAccessMask = vk::AccessFlagBits2::eMemoryRead, @@ -339,96 +324,9 @@ void BufferCache::InlineData(VAddr address, const void* value, u32 num_bytes, bo cmdbuf.pipelineBarrier2(vk::DependencyInfo{ .dependencyFlags = vk::DependencyFlagBits::eByRegion, .bufferMemoryBarrierCount = 1, - .pBufferMemoryBarriers = &buf_barrier_after, - }); -} - -void BufferCache::CopyBuffer(VAddr dst, VAddr src, u32 num_bytes, bool dst_gds, bool src_gds) { - if (!dst_gds && !IsRegionRegistered(dst, num_bytes)) { - if (!src_gds && !IsRegionRegistered(src, num_bytes)) { - // Both buffers were not transferred to GPU yet. Can safely copy in host memory. - memcpy(std::bit_cast(dst), std::bit_cast(src), num_bytes); - return; - } - // Without a readback there's nothing we can do with this - // Fallback to creating dst buffer on GPU to at least have this data there - } - if (!src_gds && !IsRegionRegistered(src, num_bytes)) { - InlineData(dst, std::bit_cast(src), num_bytes, dst_gds); - return; - } - auto& src_buffer = [&] -> const Buffer& { - if (src_gds) { - return gds_buffer; - } - const BufferId buffer_id = FindBuffer(src, num_bytes); - return slot_buffers[buffer_id]; - }(); - auto& dst_buffer = [&] -> const Buffer& { - if (dst_gds) { - return gds_buffer; - } - const BufferId buffer_id = FindBuffer(dst, num_bytes); - return slot_buffers[buffer_id]; - }(); - vk::BufferCopy region{ - .srcOffset = src_buffer.Offset(src), - .dstOffset = dst_buffer.Offset(dst), - .size = num_bytes, - }; - const vk::BufferMemoryBarrier2 buf_barriers_before[2] = { - { - .srcStageMask = vk::PipelineStageFlagBits2::eAllCommands, - .srcAccessMask = vk::AccessFlagBits2::eMemoryRead, - .dstStageMask = vk::PipelineStageFlagBits2::eAllCommands, - .dstAccessMask = vk::AccessFlagBits2::eTransferWrite, - .buffer = dst_buffer.Handle(), - .offset = dst_buffer.Offset(dst), - .size = num_bytes, - }, - { - .srcStageMask = vk::PipelineStageFlagBits2::eAllCommands, - .srcAccessMask = vk::AccessFlagBits2::eMemoryWrite, - .dstStageMask = vk::PipelineStageFlagBits2::eAllCommands, - .dstAccessMask = vk::AccessFlagBits2::eTransferRead, - .buffer = src_buffer.Handle(), - .offset = src_buffer.Offset(src), - .size = num_bytes, - }, - }; - scheduler.EndRendering(); - const auto cmdbuf = scheduler.CommandBuffer(); - cmdbuf.pipelineBarrier2(vk::DependencyInfo{ - .dependencyFlags = vk::DependencyFlagBits::eByRegion, - .bufferMemoryBarrierCount = 2, - .pBufferMemoryBarriers = buf_barriers_before, - }); - cmdbuf.copyBuffer(src_buffer.Handle(), dst_buffer.Handle(), region); - const vk::BufferMemoryBarrier2 buf_barriers_after[2] = { - { - .srcStageMask = vk::PipelineStageFlagBits2::eAllCommands, - .srcAccessMask = vk::AccessFlagBits2::eTransferWrite, - .dstStageMask = vk::PipelineStageFlagBits2::eAllCommands, - .dstAccessMask = vk::AccessFlagBits2::eMemoryRead, - .buffer = dst_buffer.Handle(), - .offset = dst_buffer.Offset(dst), - .size = num_bytes, - }, - { - .srcStageMask = vk::PipelineStageFlagBits2::eAllCommands, - .srcAccessMask = vk::AccessFlagBits2::eTransferRead, - .dstStageMask = vk::PipelineStageFlagBits2::eAllCommands, - .dstAccessMask = vk::AccessFlagBits2::eMemoryWrite, - .buffer = src_buffer.Handle(), - .offset = src_buffer.Offset(src), - .size = num_bytes, - }, - }; - cmdbuf.pipelineBarrier2(vk::DependencyInfo{ - .dependencyFlags = vk::DependencyFlagBits::eByRegion, - .bufferMemoryBarrierCount = 2, - .pBufferMemoryBarriers = buf_barriers_after, + .pBufferMemoryBarriers = &buf_barrier, }); + cmdbuf.updateBuffer(buffer->Handle(), buf_barrier.offset, num_bytes, value); } std::pair BufferCache::ObtainHostUBO(std::span data) { @@ -803,22 +701,8 @@ bool BufferCache::SynchronizeBufferFromImage(Buffer& buffer, VAddr device_addr, scheduler.EndRendering(); image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, {}); const auto cmdbuf = scheduler.CommandBuffer(); - static constexpr vk::MemoryBarrier READ_BARRIER{ - .srcAccessMask = vk::AccessFlagBits::eMemoryWrite, - .dstAccessMask = vk::AccessFlagBits::eTransferRead | vk::AccessFlagBits::eTransferWrite, - }; - static constexpr vk::MemoryBarrier WRITE_BARRIER{ - .srcAccessMask = vk::AccessFlagBits::eTransferWrite, - .dstAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite, - }; - cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands, - vk::PipelineStageFlagBits::eTransfer, - vk::DependencyFlagBits::eByRegion, READ_BARRIER, {}, {}); cmdbuf.copyImageToBuffer(image.image, vk::ImageLayout::eTransferSrcOptimal, buffer.buffer, copies); - cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands, - vk::PipelineStageFlagBits::eTransfer, - vk::DependencyFlagBits::eByRegion, WRITE_BARRIER, {}, {}); } return true; } diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 4c57e9c29..e62913413 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -87,7 +87,6 @@ public: /// Writes a value to GPU buffer. void InlineData(VAddr address, const void* value, u32 num_bytes, bool is_gds); - void CopyBuffer(VAddr dst, VAddr src, u32 num_bytes, bool dst_gds, bool src_gds); [[nodiscard]] std::pair ObtainHostUBO(std::span data); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 9e9b40ca5..fef4c7ec5 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -840,10 +840,6 @@ void Rasterizer::InlineData(VAddr address, const void* value, u32 num_bytes, boo buffer_cache.InlineData(address, value, num_bytes, is_gds); } -void Rasterizer::CopyBuffer(VAddr dst, VAddr src, u32 num_bytes, bool dst_gds, bool src_gds) { - buffer_cache.CopyBuffer(dst, src, num_bytes, dst_gds, src_gds); -} - u32 Rasterizer::ReadDataFromGds(u32 gds_offset) { auto* gds_buf = buffer_cache.GetGdsBuffer(); u32 value; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index b5bead697..ec1b5e134 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -53,7 +53,6 @@ public: void ScopedMarkerInsertColor(const std::string_view& str, const u32 color); void InlineData(VAddr address, const void* value, u32 num_bytes, bool is_gds); - void CopyBuffer(VAddr dst, VAddr src, u32 num_bytes, bool dst_gds, bool src_gds); u32 ReadDataFromGds(u32 gsd_offset); bool InvalidateMemory(VAddr addr, u64 size); bool IsMapped(VAddr addr, u64 size); From 8b88344679af4a45e7d5e35089e778b463eac13b Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sat, 14 Dec 2024 12:46:19 -0800 Subject: [PATCH 190/549] vk_instance: Remove unused dynamic state 2 features struct (#1791) --- src/video_core/renderer_vulkan/vk_instance.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 76efb215d..d7bfaee4e 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -379,7 +379,6 @@ bool Instance::CreateDevice() { vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT{ .extendedDynamicState = true, }, - vk::PhysicalDeviceExtendedDynamicState2FeaturesEXT{}, vk::PhysicalDeviceExtendedDynamicState3FeaturesEXT{ .extendedDynamicState3ColorWriteMask = true, }, From f93677b95371a83db695151341d4629e133d2203 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sat, 14 Dec 2024 12:46:35 -0800 Subject: [PATCH 191/549] resource_tracking_pass: Fix converting dimensions to float for normalization. (#1790) --- .../ir/passes/resource_tracking_pass.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index f436db07a..a59398952 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -586,12 +586,13 @@ void PatchImageSampleInstruction(IR::Block& block, IR::Inst& inst, Info& info, const auto dimensions = unnormalized ? ir.ImageQueryDimension(ir.Imm32(image_binding), ir.Imm32(0u), ir.Imm1(false)) : IR::Value{}; - const auto get_coord = [&](u32 idx, u32 dim_idx) -> IR::Value { - const auto coord = get_addr_reg(idx); + const auto get_coord = [&](u32 coord_idx, u32 dim_idx) -> IR::Value { + const auto coord = get_addr_reg(coord_idx); if (unnormalized) { // Normalize the coordinate for sampling, dividing by its corresponding dimension. - return ir.FPDiv(coord, - ir.BitCast(IR::U32{ir.CompositeExtract(dimensions, dim_idx)})); + const auto dim = + ir.ConvertUToF(32, 32, IR::U32{ir.CompositeExtract(dimensions, dim_idx)}); + return ir.FPDiv(coord, dim); } return coord; }; From 876445faf1b0ef63ddb9d0111e35b74bd31b4a42 Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Sat, 14 Dec 2024 23:46:55 +0300 Subject: [PATCH 192/549] recompiler: emit a label right after s_branch to prevent dead code interferrence (#1785) --- src/shader_recompiler/frontend/control_flow_graph.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/shader_recompiler/frontend/control_flow_graph.cpp b/src/shader_recompiler/frontend/control_flow_graph.cpp index 8c3122b28..0816ec088 100644 --- a/src/shader_recompiler/frontend/control_flow_graph.cpp +++ b/src/shader_recompiler/frontend/control_flow_graph.cpp @@ -80,6 +80,8 @@ void CFG::EmitLabels() { if (inst.IsUnconditionalBranch()) { const u32 target = inst.BranchTarget(pc); AddLabel(target); + // Emit this label so that the block ends with s_branch instruction + AddLabel(pc + inst.length); } else if (inst.IsConditionalBranch()) { const u32 true_label = inst.BranchTarget(pc); const u32 false_label = pc + inst.length; From af26c945b10c400f9720dbb29857876867a57c35 Mon Sep 17 00:00:00 2001 From: Connor Garey Date: Sat, 14 Dec 2024 22:30:17 +0000 Subject: [PATCH 193/549] Fix for "shadPS4" not being given on Linux volume mixers (#1789) --- src/sdl_window.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index f6b57436f..4b13844b8 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include #include #include @@ -68,6 +69,9 @@ static Uint32 SDLCALL PollController(void* userdata, SDL_TimerID timer_id, Uint3 WindowSDL::WindowSDL(s32 width_, s32 height_, Input::GameController* controller_, std::string_view window_title) : width{width_}, height{height_}, controller{controller_} { + if (!SDL_SetHint(SDL_HINT_APP_NAME, "shadPS4")) { + UNREACHABLE_MSG("Failed to set SDL window hint: {}", SDL_GetError()); + } if (!SDL_Init(SDL_INIT_VIDEO)) { UNREACHABLE_MSG("Failed to initialize SDL video subsystem: {}", SDL_GetError()); } From 0fd1ab674bbb6b41f5cc6d46ca5ed4bcd6d6052c Mon Sep 17 00:00:00 2001 From: psucien <168137814+psucien@users.noreply.github.com> Date: Sat, 14 Dec 2024 23:54:46 +0100 Subject: [PATCH 194/549] GPU processor refactoring (#1787) * coroutine code prettification * asc queues submission refactoring * better asc ring context handling * final touches and review notes * even more simplification for context saving --- src/common/debug.h | 3 + src/core/debug_state.cpp | 68 +++--- src/core/debug_state.h | 8 +- src/core/libraries/gnmdriver/gnmdriver.cpp | 36 ++-- .../backend/spirv/spirv_emit_context.cpp | 2 +- src/video_core/amdgpu/liverpool.cpp | 204 ++++++++++-------- src/video_core/amdgpu/liverpool.h | 25 ++- .../renderer_vulkan/vk_pipeline_cache.cpp | 9 +- .../renderer_vulkan/vk_rasterizer.cpp | 6 +- .../renderer_vulkan/vk_shader_hle.cpp | 16 +- .../renderer_vulkan/vk_shader_hle.h | 2 +- src/video_core/texture_cache/tile_manager.cpp | 1 + 12 files changed, 234 insertions(+), 146 deletions(-) diff --git a/src/common/debug.h b/src/common/debug.h index 091c6191d..4d42aa4ab 100644 --- a/src/common/debug.h +++ b/src/common/debug.h @@ -57,3 +57,6 @@ enum MarkersPalette : int { tracy::SourceLocationData{nullptr, name, TracyFile, (uint32_t)TracyLine, 0}; #define FRAME_END FrameMark + +#define FIBER_ENTER(name) TracyFiberEnter(name) +#define FIBER_EXIT TracyFiberLeave diff --git a/src/core/debug_state.cpp b/src/core/debug_state.cpp index c68fd469d..daf614bd9 100644 --- a/src/core/debug_state.cpp +++ b/src/core/debug_state.cpp @@ -142,41 +142,61 @@ void DebugStateImpl::PushQueueDump(QueueDump dump) { frame.queues.push_back(std::move(dump)); } -void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr, - const AmdGpu::Liverpool::Regs& regs, bool is_compute) { - std::scoped_lock lock{frame_dump_list_mutex}; +std::optional DebugStateImpl::GetRegDump(uintptr_t base_addr, uintptr_t header_addr) { const auto it = waiting_reg_dumps.find(header_addr); if (it == waiting_reg_dumps.end()) { - return; + return std::nullopt; } auto& frame = *it->second; waiting_reg_dumps.erase(it); waiting_reg_dumps_dbg.erase(waiting_reg_dumps_dbg.find(header_addr)); - auto& dump = frame.regs[header_addr - base_addr]; - dump.regs = regs; - if (is_compute) { - dump.is_compute = true; - const auto& cs = dump.regs.cs_program; - dump.cs_data = PipelineComputerProgramDump{ - .cs_program = cs, - .code = std::vector{cs.Code().begin(), cs.Code().end()}, - }; - } else { - for (int i = 0; i < RegDump::MaxShaderStages; i++) { - if (regs.stage_enable.IsStageEnabled(i)) { - auto stage = regs.ProgramForStage(i); - if (stage->address_lo != 0) { - auto code = stage->Code(); - dump.stages[i] = PipelineShaderProgramDump{ - .user_data = *stage, - .code = std::vector{code.begin(), code.end()}, - }; - } + return &frame.regs[header_addr - base_addr]; +} + +void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr, + const AmdGpu::Liverpool::Regs& regs) { + std::scoped_lock lock{frame_dump_list_mutex}; + + auto dump = GetRegDump(base_addr, header_addr); + if (!dump) { + return; + } + + (*dump)->regs = regs; + + for (int i = 0; i < RegDump::MaxShaderStages; i++) { + if ((*dump)->regs.stage_enable.IsStageEnabled(i)) { + auto stage = (*dump)->regs.ProgramForStage(i); + if (stage->address_lo != 0) { + auto code = stage->Code(); + (*dump)->stages[i] = PipelineShaderProgramDump{ + .user_data = *stage, + .code = std::vector{code.begin(), code.end()}, + }; } } } } +void DebugStateImpl::PushRegsDumpCompute(uintptr_t base_addr, uintptr_t header_addr, + const CsState& cs_state) { + std::scoped_lock lock{frame_dump_list_mutex}; + + auto dump = GetRegDump(base_addr, header_addr); + if (!dump) { + return; + } + + (*dump)->is_compute = true; + auto& cs = (*dump)->regs.cs_program; + cs = cs_state; + + (*dump)->cs_data = PipelineComputerProgramDump{ + .cs_program = cs, + .code = std::vector{cs.Code().begin(), cs.Code().end()}, + }; +} + void DebugStateImpl::CollectShader(const std::string& name, Shader::LogicalStage l_stage, vk::ShaderModule module, std::span spv, std::span raw_code, std::span patch_spv, diff --git a/src/core/debug_state.h b/src/core/debug_state.h index 0db5bc468..a0e428b6b 100644 --- a/src/core/debug_state.h +++ b/src/core/debug_state.h @@ -11,7 +11,6 @@ #include #include "common/types.h" -#include "video_core/amdgpu/liverpool.h" #include "video_core/renderer_vulkan/vk_graphics_pipeline.h" #ifdef _WIN32 @@ -204,12 +203,17 @@ public: void PushQueueDump(QueueDump dump); void PushRegsDump(uintptr_t base_addr, uintptr_t header_addr, - const AmdGpu::Liverpool::Regs& regs, bool is_compute = false); + const AmdGpu::Liverpool::Regs& regs); + using CsState = AmdGpu::Liverpool::ComputeProgram; + void PushRegsDumpCompute(uintptr_t base_addr, uintptr_t header_addr, const CsState& cs_state); void CollectShader(const std::string& name, Shader::LogicalStage l_stage, vk::ShaderModule module, std::span spv, std::span raw_code, std::span patch_spv, bool is_patched); + +private: + std::optional GetRegDump(uintptr_t base_addr, uintptr_t header_addr); }; } // namespace DebugStateType diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index e85b8b890..583339dd9 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -296,17 +296,12 @@ static_assert(CtxInitSequence400.size() == 0x61); // In case if `submitDone` is issued we need to block submissions until GPU idle static u32 submission_lock{}; std::condition_variable cv_lock{}; -static std::mutex m_submission{}; +std::mutex m_submission{}; static u64 frames_submitted{}; // frame counter static bool send_init_packet{true}; // initialize HW state before first game's submit in a frame static int sdk_version{0}; -struct AscQueueInfo { - VAddr map_addr; - u32* read_addr; - u32 ring_size_dw; -}; -static Common::SlotVector asc_queues{}; +static u32 asc_next_offs_dw[Liverpool::NumComputeRings]; static constexpr VAddr tessellation_factors_ring_addr = Core::SYSTEM_RESERVED_MAX - 0xFFFFFFF; static constexpr u32 tessellation_offchip_buffer_size = 0x800000u; @@ -506,11 +501,19 @@ void PS4_SYSV_ABI sceGnmDingDong(u32 gnm_vqid, u32 next_offs_dw) { } auto vqid = gnm_vqid - 1; - auto& asc_queue = asc_queues[{vqid}]; - const auto* acb_ptr = reinterpret_cast(asc_queue.map_addr + *asc_queue.read_addr); - const auto acb_size = next_offs_dw ? (next_offs_dw << 2u) - *asc_queue.read_addr - : (asc_queue.ring_size_dw << 2u) - *asc_queue.read_addr; - const std::span acb_span{acb_ptr, acb_size >> 2u}; + auto& asc_queue = liverpool->asc_queues[{vqid}]; + + const auto& offs_dw = asc_next_offs_dw[vqid]; + + if (next_offs_dw < offs_dw) { + ASSERT_MSG(next_offs_dw == 0, "ACB submission is split at the end of ring buffer"); + } + + const auto* acb_ptr = reinterpret_cast(asc_queue.map_addr) + offs_dw; + const auto acb_size_dw = (next_offs_dw ? next_offs_dw : asc_queue.ring_size_dw) - offs_dw; + const std::span acb_span{acb_ptr, acb_size_dw}; + + asc_next_offs_dw[vqid] = next_offs_dw; if (DebugState.DumpingCurrentFrame()) { static auto last_frame_num = -1LL; @@ -545,9 +548,6 @@ void PS4_SYSV_ABI sceGnmDingDong(u32 gnm_vqid, u32 next_offs_dw) { }); } liverpool->SubmitAsc(gnm_vqid, acb_span); - - *asc_queue.read_addr += acb_size; - *asc_queue.read_addr %= asc_queue.ring_size_dw * 4; } void PS4_SYSV_ABI sceGnmDingDongForWorkload(u32 gnm_vqid, u32 next_offs_dw, u64 workload_id) { @@ -1266,12 +1266,16 @@ int PS4_SYSV_ABI sceGnmMapComputeQueue(u32 pipe_id, u32 queue_id, VAddr ring_bas return ORBIS_GNM_ERROR_COMPUTEQUEUE_INVALID_READ_PTR_ADDR; } - auto vqid = asc_queues.insert(VAddr(ring_base_addr), read_ptr_addr, ring_size_dw); + const auto vqid = + liverpool->asc_queues.insert(VAddr(ring_base_addr), read_ptr_addr, ring_size_dw, pipe_id); // We need to offset index as `dingDong` assumes it to be from the range [1..64] const auto gnm_vqid = vqid.index + 1; LOG_INFO(Lib_GnmDriver, "ASC pipe {} queue {} mapped to vqueue {}", pipe_id, queue_id, gnm_vqid); + const auto& queue = liverpool->asc_queues[vqid]; + *queue.read_addr = 0u; + return gnm_vqid; } diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 2e09e70a7..5f0ad298e 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -294,7 +294,7 @@ void EmitContext::DefineInputs() { }); // Note that we pass index rather than Id input_params[attrib.semantic] = SpirvAttribute{ - .id = rate_idx, + .id = {rate_idx}, .pointer_type = input_u32, .component_type = U32[1], .num_components = std::min(attrib.num_elements, num_components), diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index 8db2d63c4..8cca636c0 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include + #include "common/assert.h" #include "common/config.h" #include "common/debug.h" @@ -18,7 +20,32 @@ namespace AmdGpu { static const char* dcb_task_name{"DCB_TASK"}; static const char* ccb_task_name{"CCB_TASK"}; -static const char* acb_task_name{"ACB_TASK"}; + +#define MAX_NAMES 56 +static_assert(Liverpool::NumComputeRings <= MAX_NAMES); + +#define NAME_NUM(z, n, name) BOOST_PP_STRINGIZE(name) BOOST_PP_STRINGIZE(n), +#define NAME_ARRAY(name, num) {BOOST_PP_REPEAT(num, NAME_NUM, name)} + +static const char* acb_task_name[] = NAME_ARRAY(ACB_TASK, MAX_NAMES); + +#define YIELD(name) \ + FIBER_EXIT; \ + co_yield {}; \ + FIBER_ENTER(name); + +#define YIELD_CE() YIELD(ccb_task_name) +#define YIELD_GFX() YIELD(dcb_task_name) +#define YIELD_ASC(id) YIELD(acb_task_name[id]) + +#define RESUME(task, name) \ + FIBER_EXIT; \ + task.handle.resume(); \ + FIBER_ENTER(name); + +#define RESUME_CE(task) RESUME(task, ccb_task_name) +#define RESUME_GFX(task) RESUME(task, dcb_task_name) +#define RESUME_ASC(task, id) RESUME(task, acb_task_name[id]) std::array Liverpool::ConstantEngine::constants_heap; @@ -60,7 +87,7 @@ void Liverpool::Process(std::stop_token stoken) { VideoCore::StartCapture(); - int qid = -1; + curr_qid = -1; while (num_submits || num_commands) { @@ -79,9 +106,9 @@ void Liverpool::Process(std::stop_token stoken) { --num_commands; } - qid = (qid + 1) % NumTotalQueues; + curr_qid = (curr_qid + 1) % num_mapped_queues; - auto& queue = mapped_queues[qid]; + auto& queue = mapped_queues[curr_qid]; Task::Handle task{}; { @@ -119,7 +146,7 @@ void Liverpool::Process(std::stop_token stoken) { } Liverpool::Task Liverpool::ProcessCeUpdate(std::span ccb) { - TracyFiberEnter(ccb_task_name); + FIBER_ENTER(ccb_task_name); while (!ccb.empty()) { const auto* header = reinterpret_cast(ccb.data()); @@ -155,9 +182,7 @@ Liverpool::Task Liverpool::ProcessCeUpdate(std::span ccb) { case PM4ItOpcode::WaitOnDeCounterDiff: { const auto diff = it_body[0]; while ((cblock.de_count - cblock.ce_count) >= diff) { - TracyFiberLeave; - co_yield {}; - TracyFiberEnter(ccb_task_name); + YIELD_CE(); } break; } @@ -165,13 +190,12 @@ Liverpool::Task Liverpool::ProcessCeUpdate(std::span ccb) { const auto* indirect_buffer = reinterpret_cast(header); auto task = ProcessCeUpdate({indirect_buffer->Address(), indirect_buffer->ib_size}); - while (!task.handle.done()) { - task.handle.resume(); + RESUME_CE(task); - TracyFiberLeave; - co_yield {}; - TracyFiberEnter(ccb_task_name); - }; + while (!task.handle.done()) { + YIELD_CE(); + RESUME_CE(task); + } break; } default: @@ -182,11 +206,11 @@ Liverpool::Task Liverpool::ProcessCeUpdate(std::span ccb) { ccb = NextPacket(ccb, header->type3.NumWords() + 1); } - TracyFiberLeave; + FIBER_EXIT; } Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span ccb) { - TracyFiberEnter(dcb_task_name); + FIBER_ENTER(dcb_task_name); cblock.Reset(); @@ -197,9 +221,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(dcb.data()); @@ -353,8 +375,18 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); - std::memcpy(®s.reg_array[ShRegWordOffset + set_data->reg_offset], header + 2, - (count - 1) * sizeof(u32)); + const auto set_size = (count - 1) * sizeof(u32); + + if (set_data->reg_offset >= 0x200 && + set_data->reg_offset <= (0x200 + sizeof(ComputeProgram) / 4)) { + ASSERT(set_size <= sizeof(ComputeProgram)); + auto* addr = reinterpret_cast(&mapped_queues[GfxQueueId].cs_state) + + (set_data->reg_offset - 0x200); + std::memcpy(addr, header + 2, set_size); + } else { + std::memcpy(®s.reg_array[ShRegWordOffset + set_data->reg_offset], header + 2, + set_size); + } break; } case PM4ItOpcode::SetUconfigReg: { @@ -474,15 +506,16 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); - regs.cs_program.dim_x = dispatch_direct->dim_x; - regs.cs_program.dim_y = dispatch_direct->dim_y; - regs.cs_program.dim_z = dispatch_direct->dim_z; - regs.cs_program.dispatch_initiator = dispatch_direct->dispatch_initiator; + auto& cs_program = GetCsRegs(); + cs_program.dim_x = dispatch_direct->dim_x; + cs_program.dim_y = dispatch_direct->dim_y; + cs_program.dim_z = dispatch_direct->dim_z; + cs_program.dispatch_initiator = dispatch_direct->dispatch_initiator; if (DebugState.DumpingCurrentReg()) { - DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs, - true); + DebugState.PushRegsDumpCompute(base_addr, reinterpret_cast(header), + cs_program); } - if (rasterizer && (regs.cs_program.dispatch_initiator & 1)) { + if (rasterizer && (cs_program.dispatch_initiator & 1)) { const auto cmd_address = reinterpret_cast(header); rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:Dispatch", cmd_address)); rasterizer->DispatchDirect(); @@ -493,14 +526,15 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); + auto& cs_program = GetCsRegs(); const auto offset = dispatch_indirect->data_offset; const auto ib_address = mapped_queues[GfxQueueId].indirect_args_addr; const auto size = sizeof(PM4CmdDispatchIndirect::GroupDimensions); if (DebugState.DumpingCurrentReg()) { - DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs, - true); + DebugState.PushRegsDumpCompute(base_addr, reinterpret_cast(header), + cs_program); } - if (rasterizer && (regs.cs_program.dispatch_initiator & 1)) { + if (rasterizer && (cs_program.dispatch_initiator & 1)) { const auto cmd_address = reinterpret_cast(header); rasterizer->ScopeMarkerBegin( fmt::format("dcb:{}:DispatchIndirect", cmd_address)); @@ -613,11 +647,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); while (!rewind->Valid()) { - mapped_queues[GfxQueueId].cs_state = regs.cs_program; - TracyFiberLeave; - co_yield {}; - TracyFiberEnter(dcb_task_name); - regs.cs_program = mapped_queues[GfxQueueId].cs_state; + YIELD_GFX(); } break; } @@ -633,11 +663,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spanWaitVoLabel([&] { return wait_reg_mem->Test(); }); } while (!wait_reg_mem->Test()) { - mapped_queues[GfxQueueId].cs_state = regs.cs_program; - TracyFiberLeave; - co_yield {}; - TracyFiberEnter(dcb_task_name); - regs.cs_program = mapped_queues[GfxQueueId].cs_state; + YIELD_GFX(); } break; } @@ -645,13 +671,12 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); auto task = ProcessGraphics( {indirect_buffer->Address(), indirect_buffer->ib_size}, {}); - while (!task.handle.done()) { - task.handle.resume(); + RESUME_GFX(task); - TracyFiberLeave; - co_yield {}; - TracyFiberEnter(dcb_task_name); - }; + while (!task.handle.done()) { + YIELD_GFX(); + RESUME_GFX(task); + } break; } case PM4ItOpcode::IncrementDeCounter: { @@ -660,9 +685,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span dcb, std::span acb, int vqid) { - TracyFiberEnter(acb_task_name); +template +Liverpool::Task Liverpool::ProcessCompute(std::span acb, u32 vqid) { + FIBER_ENTER(acb_task_name[vqid]); + const auto& queue = asc_queues[{vqid}]; auto base_addr = reinterpret_cast(acb.data()); while (!acb.empty()) { @@ -711,15 +736,14 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, int vqid) { } case PM4ItOpcode::IndirectBuffer: { const auto* indirect_buffer = reinterpret_cast(header); - auto task = ProcessCompute( + auto task = ProcessCompute( {indirect_buffer->Address(), indirect_buffer->ib_size}, vqid); - while (!task.handle.done()) { - task.handle.resume(); + RESUME_ASC(task, vqid); - TracyFiberLeave; - co_yield {}; - TracyFiberEnter(acb_task_name); - }; + while (!task.handle.done()) { + YIELD_ASC(vqid); + RESUME_ASC(task, vqid); + } break; } case PM4ItOpcode::DmaData: { @@ -757,30 +781,38 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, int vqid) { case PM4ItOpcode::Rewind: { const PM4CmdRewind* rewind = reinterpret_cast(header); while (!rewind->Valid()) { - mapped_queues[vqid].cs_state = regs.cs_program; - TracyFiberLeave; - co_yield {}; - TracyFiberEnter(acb_task_name); - regs.cs_program = mapped_queues[vqid].cs_state; + YIELD_ASC(vqid); } break; } case PM4ItOpcode::SetShReg: { const auto* set_data = reinterpret_cast(header); - std::memcpy(®s.reg_array[ShRegWordOffset + set_data->reg_offset], header + 2, - (count - 1) * sizeof(u32)); + const auto set_size = (count - 1) * sizeof(u32); + + if (set_data->reg_offset >= 0x200 && + set_data->reg_offset <= (0x200 + sizeof(ComputeProgram) / 4)) { + ASSERT(set_size <= sizeof(ComputeProgram)); + auto* addr = reinterpret_cast(&mapped_queues[vqid + 1].cs_state) + + (set_data->reg_offset - 0x200); + std::memcpy(addr, header + 2, set_size); + } else { + std::memcpy(®s.reg_array[ShRegWordOffset + set_data->reg_offset], header + 2, + set_size); + } break; } case PM4ItOpcode::DispatchDirect: { const auto* dispatch_direct = reinterpret_cast(header); - regs.cs_program.dim_x = dispatch_direct->dim_x; - regs.cs_program.dim_y = dispatch_direct->dim_y; - regs.cs_program.dim_z = dispatch_direct->dim_z; - regs.cs_program.dispatch_initiator = dispatch_direct->dispatch_initiator; + auto& cs_program = GetCsRegs(); + cs_program.dim_x = dispatch_direct->dim_x; + cs_program.dim_y = dispatch_direct->dim_y; + cs_program.dim_z = dispatch_direct->dim_z; + cs_program.dispatch_initiator = dispatch_direct->dispatch_initiator; if (DebugState.DumpingCurrentReg()) { - DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs, true); + DebugState.PushRegsDumpCompute(base_addr, reinterpret_cast(header), + cs_program); } - if (rasterizer && (regs.cs_program.dispatch_initiator & 1)) { + if (rasterizer && (cs_program.dispatch_initiator & 1)) { const auto cmd_address = reinterpret_cast(header); rasterizer->ScopeMarkerBegin(fmt::format("acb[{}]:{}:Dispatch", vqid, cmd_address)); rasterizer->DispatchDirect(); @@ -803,17 +835,13 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, int vqid) { const auto* wait_reg_mem = reinterpret_cast(header); ASSERT(wait_reg_mem->engine.Value() == PM4CmdWaitRegMem::Engine::Me); while (!wait_reg_mem->Test()) { - mapped_queues[vqid].cs_state = regs.cs_program; - TracyFiberLeave; - co_yield {}; - TracyFiberEnter(acb_task_name); - regs.cs_program = mapped_queues[vqid].cs_state; + YIELD_ASC(vqid); } break; } case PM4ItOpcode::ReleaseMem: { const auto* release_mem = reinterpret_cast(header); - release_mem->SignalFence(Platform::InterruptId::Compute0RelMem); // <--- + release_mem->SignalFence(static_cast(queue.pipe_id)); break; } default: @@ -821,10 +849,16 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, int vqid) { static_cast(opcode), count); } - acb = NextPacket(acb, header->type3.NumWords() + 1); + const auto packet_size_dw = header->type3.NumWords() + 1; + acb = NextPacket(acb, packet_size_dw); + + if constexpr (!is_indirect) { + *queue.read_addr += packet_size_dw; + *queue.read_addr %= queue.ring_size_dw; + } } - TracyFiberLeave; + FIBER_EXIT; } std::pair, std::span> Liverpool::CopyCmdBuffers( @@ -881,10 +915,11 @@ void Liverpool::SubmitGfx(std::span dcb, std::span ccb) { submit_cv.notify_one(); } -void Liverpool::SubmitAsc(u32 vqid, std::span acb) { - ASSERT_MSG(vqid >= 0 && vqid < NumTotalQueues, "Invalid virtual ASC queue index"); - auto& queue = mapped_queues[vqid]; +void Liverpool::SubmitAsc(u32 gnm_vqid, std::span acb) { + ASSERT_MSG(gnm_vqid > 0 && gnm_vqid < NumTotalQueues, "Invalid virtual ASC queue index"); + auto& queue = mapped_queues[gnm_vqid]; + const auto vqid = gnm_vqid - 1; const auto& task = ProcessCompute(acb, vqid); { std::scoped_lock lock{queue.m_access}; @@ -892,6 +927,7 @@ void Liverpool::SubmitAsc(u32 vqid, std::span acb) { } std::scoped_lock lk{submit_mutex}; + num_mapped_queues = std::max(num_mapped_queues, gnm_vqid + 1); ++num_submits; submit_cv.notify_one(); } diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index b6172d37b..4c74d37d0 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -16,6 +16,7 @@ #include "common/assert.h" #include "common/bit_field.h" #include "common/polyfill_thread.h" +#include "common/slot_vector.h" #include "common/types.h" #include "common/unique_function.h" #include "shader_recompiler/params.h" @@ -45,7 +46,8 @@ struct Liverpool { static constexpr u32 NumGfxRings = 1u; // actually 2, but HP is reserved by system software static constexpr u32 NumComputePipes = 7u; // actually 8, but #7 is reserved by system software static constexpr u32 NumQueuesPerPipe = 8u; - static constexpr u32 NumTotalQueues = NumGfxRings + (NumComputePipes * NumQueuesPerPipe); + static constexpr u32 NumComputeRings = NumComputePipes * NumQueuesPerPipe; + static constexpr u32 NumTotalQueues = NumGfxRings + NumComputeRings; static_assert(NumTotalQueues < 64u); // need to fit into u64 bitmap for ffs static constexpr u32 NumColorBuffers = 8; @@ -1143,7 +1145,7 @@ struct Liverpool { INSERT_PADDING_WORDS(0x2D48 - 0x2d08 - 20); ShaderProgram ls_program; INSERT_PADDING_WORDS(0xA4); - ComputeProgram cs_program; + ComputeProgram cs_program; // shadowed by `cs_state` in `mapped_queues` INSERT_PADDING_WORDS(0xA008 - 0x2E00 - 80 - 3 - 5); DepthRenderControl depth_render_control; INSERT_PADDING_WORDS(1); @@ -1298,7 +1300,7 @@ public: ~Liverpool(); void SubmitGfx(std::span dcb, std::span ccb); - void SubmitAsc(u32 vqid, std::span acb); + void SubmitAsc(u32 gnm_vqid, std::span acb); void SubmitDone() noexcept { std::scoped_lock lk{submit_mutex}; @@ -1341,6 +1343,18 @@ public: gfx_queue.dcb_buffer.reserve(GfxReservedSize); } + inline ComputeProgram& GetCsRegs() { + return mapped_queues[curr_qid].cs_state; + } + + struct AscQueueInfo { + VAddr map_addr; + u32* read_addr; + u32 ring_size_dw; + u32 pipe_id; + }; + Common::SlotVector asc_queues{}; + private: struct Task { struct promise_type { @@ -1378,7 +1392,8 @@ private: std::span ccb); Task ProcessGraphics(std::span dcb, std::span ccb); Task ProcessCeUpdate(std::span ccb); - Task ProcessCompute(std::span acb, int vqid); + template + Task ProcessCompute(std::span acb, u32 vqid); void Process(std::stop_token stoken); @@ -1393,6 +1408,7 @@ private: VAddr indirect_args_addr{}; }; std::array mapped_queues{}; + u32 num_mapped_queues{1u}; // GFX is always available struct ConstantEngine { void Reset() { @@ -1421,6 +1437,7 @@ private: std::mutex submit_mutex; std::condition_variable_any submit_cv; std::queue> command_queue{}; + int curr_qid{-1}; }; static_assert(GFX6_3D_REG_INDEX(ps_program) == 0x2C08); diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 58473496f..50396287b 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -173,9 +173,9 @@ Shader::RuntimeInfo PipelineCache::BuildRuntimeInfo(Stage stage, LogicalStage l_ break; } case Stage::Compute: { - const auto& cs_pgm = regs.cs_program; + const auto& cs_pgm = liverpool->GetCsRegs(); info.num_user_data = cs_pgm.settings.num_user_regs; - info.num_allocated_vgprs = regs.cs_program.settings.num_vgprs * 4; + info.num_allocated_vgprs = cs_pgm.settings.num_vgprs * 4; info.cs_info.workgroup_size = {cs_pgm.num_thread_x.full, cs_pgm.num_thread_y.full, cs_pgm.num_thread_z.full}; info.cs_info.tgid_enable = {cs_pgm.IsTgidEnabled(0), cs_pgm.IsTgidEnabled(1), @@ -476,8 +476,8 @@ bool PipelineCache::RefreshGraphicsKey() { bool PipelineCache::RefreshComputeKey() { Shader::Backend::Bindings binding{}; - const auto* cs_pgm = &liverpool->regs.cs_program; - const auto cs_params = Liverpool::GetParams(*cs_pgm); + const auto& cs_pgm = liverpool->GetCsRegs(); + const auto cs_params = Liverpool::GetParams(cs_pgm); std::tie(infos[0], modules[0], fetch_shader, compute_key.value) = GetProgram(Shader::Stage::Compute, LogicalStage::Compute, cs_params, binding); return true; @@ -529,6 +529,7 @@ PipelineCache::Result PipelineCache::GetProgram(Stage stage, LogicalStage l_stag return std::make_tuple(&program->info, module, spec.fetch_shader_data, HashCombine(params.hash, 0)); } + it_pgm.value()->info.user_data = params.user_data; auto& program = it_pgm.value(); auto& info = program->info; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index fef4c7ec5..bd8906f86 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -317,14 +317,14 @@ void Rasterizer::DrawIndirect(bool is_indexed, VAddr arg_address, u32 offset, u3 void Rasterizer::DispatchDirect() { RENDERER_TRACE; - const auto& cs_program = liverpool->regs.cs_program; + const auto& cs_program = liverpool->GetCsRegs(); const ComputePipeline* pipeline = pipeline_cache.GetComputePipeline(); if (!pipeline) { return; } const auto& cs = pipeline->GetStage(Shader::LogicalStage::Compute); - if (ExecuteShaderHLE(cs, liverpool->regs, *this)) { + if (ExecuteShaderHLE(cs, liverpool->regs, cs_program, *this)) { return; } @@ -344,7 +344,7 @@ void Rasterizer::DispatchDirect() { void Rasterizer::DispatchIndirect(VAddr address, u32 offset, u32 size) { RENDERER_TRACE; - const auto& cs_program = liverpool->regs.cs_program; + const auto& cs_program = liverpool->GetCsRegs(); const ComputePipeline* pipeline = pipeline_cache.GetComputePipeline(); if (!pipeline) { return; diff --git a/src/video_core/renderer_vulkan/vk_shader_hle.cpp b/src/video_core/renderer_vulkan/vk_shader_hle.cpp index b863dce21..ff78f5d24 100644 --- a/src/video_core/renderer_vulkan/vk_shader_hle.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_hle.cpp @@ -2,17 +2,19 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "shader_recompiler/info.h" +#include "video_core/renderer_vulkan/vk_rasterizer.h" #include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_shader_hle.h" -#include "vk_rasterizer.h" +extern std::unique_ptr liverpool; namespace Vulkan { static constexpr u64 COPY_SHADER_HASH = 0xfefebf9f; -bool ExecuteCopyShaderHLE(const Shader::Info& info, const AmdGpu::Liverpool::Regs& regs, - Rasterizer& rasterizer) { +static bool ExecuteCopyShaderHLE(const Shader::Info& info, + const AmdGpu::Liverpool::ComputeProgram& cs_program, + Rasterizer& rasterizer) { auto& scheduler = rasterizer.GetScheduler(); auto& buffer_cache = rasterizer.GetBufferCache(); @@ -34,9 +36,9 @@ bool ExecuteCopyShaderHLE(const Shader::Info& info, const AmdGpu::Liverpool::Reg static std::vector copies; copies.clear(); - copies.reserve(regs.cs_program.dim_x); + copies.reserve(cs_program.dim_x); - for (u32 i = 0; i < regs.cs_program.dim_x; i++) { + for (u32 i = 0; i < cs_program.dim_x; i++) { const auto& [dst_idx, src_idx, end] = ctl_buf[i]; const u32 local_dst_offset = dst_idx * buf_stride; const u32 local_src_offset = src_idx * buf_stride; @@ -122,10 +124,10 @@ bool ExecuteCopyShaderHLE(const Shader::Info& info, const AmdGpu::Liverpool::Reg } bool ExecuteShaderHLE(const Shader::Info& info, const AmdGpu::Liverpool::Regs& regs, - Rasterizer& rasterizer) { + const AmdGpu::Liverpool::ComputeProgram& cs_program, Rasterizer& rasterizer) { switch (info.pgm_hash) { case COPY_SHADER_HASH: - return ExecuteCopyShaderHLE(info, regs, rasterizer); + return ExecuteCopyShaderHLE(info, cs_program, rasterizer); default: return false; } diff --git a/src/video_core/renderer_vulkan/vk_shader_hle.h b/src/video_core/renderer_vulkan/vk_shader_hle.h index fda9b1735..008de8003 100644 --- a/src/video_core/renderer_vulkan/vk_shader_hle.h +++ b/src/video_core/renderer_vulkan/vk_shader_hle.h @@ -15,6 +15,6 @@ class Rasterizer; /// Attempts to execute a shader using HLE if possible. bool ExecuteShaderHLE(const Shader::Info& info, const AmdGpu::Liverpool::Regs& regs, - Rasterizer& rasterizer); + const AmdGpu::Liverpool::ComputeProgram& cs_program, Rasterizer& rasterizer); } // namespace Vulkan diff --git a/src/video_core/texture_cache/tile_manager.cpp b/src/video_core/texture_cache/tile_manager.cpp index d8d23c400..a5e09e45d 100644 --- a/src/video_core/texture_cache/tile_manager.cpp +++ b/src/video_core/texture_cache/tile_manager.cpp @@ -212,6 +212,7 @@ vk::Format DemoteImageFormatForDetiling(vk::Format format) { case vk::Format::eBc7SrgbBlock: case vk::Format::eBc7UnormBlock: case vk::Format::eBc6HUfloatBlock: + case vk::Format::eR32G32B32A32Uint: case vk::Format::eR32G32B32A32Sfloat: return vk::Format::eR32G32B32A32Uint; default: From d2ac92481b514bd2dacbd60abdc460cd076d9d3c Mon Sep 17 00:00:00 2001 From: Richard Habitzreuter Date: Sun, 15 Dec 2024 11:28:36 -0300 Subject: [PATCH 195/549] style: add Gruvbox theme (#1796) --- src/qt_gui/main_window.cpp | 13 +++++++++++++ src/qt_gui/main_window_themes.cpp | 22 ++++++++++++++++++++-- src/qt_gui/main_window_themes.h | 8 +------- src/qt_gui/main_window_ui.h | 6 ++++++ 4 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 3eb629c0b..0b5137c4b 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -111,6 +111,7 @@ void MainWindow::CreateActions() { m_theme_act_group->addAction(ui->setThemeGreen); m_theme_act_group->addAction(ui->setThemeBlue); m_theme_act_group->addAction(ui->setThemeViolet); + m_theme_act_group->addAction(ui->setThemeGruvbox); } void MainWindow::AddUiWidgets() { @@ -540,6 +541,14 @@ void MainWindow::CreateConnects() { isIconBlack = false; } }); + connect(ui->setThemeGruvbox, &QAction::triggered, &m_window_themes, [this]() { + m_window_themes.SetWindowTheme(Theme::Gruvbox, ui->mw_searchbar); + Config::setMainWindowTheme(static_cast(Theme::Gruvbox)); + if (isIconBlack) { + SetUiIcons(false); + isIconBlack = false; + } + }); } void MainWindow::StartGame() { @@ -912,6 +921,10 @@ void MainWindow::SetLastUsedTheme() { ui->setThemeViolet->setChecked(true); isIconBlack = false; SetUiIcons(false); + case Theme::Gruvbox: + ui->setThemeGruvbox->setChecked(true); + isIconBlack = false; + SetUiIcons(false); break; } } diff --git a/src/qt_gui/main_window_themes.cpp b/src/qt_gui/main_window_themes.cpp index 35e64ef74..65dd04269 100644 --- a/src/qt_gui/main_window_themes.cpp +++ b/src/qt_gui/main_window_themes.cpp @@ -15,7 +15,6 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { themePalette.setColor(QPalette::Window, QColor(50, 50, 50)); themePalette.setColor(QPalette::WindowText, Qt::white); themePalette.setColor(QPalette::Base, QColor(20, 20, 20)); - themePalette.setColor(QPalette::AlternateBase, QColor(25, 25, 25)); themePalette.setColor(QPalette::AlternateBase, QColor(53, 53, 53)); themePalette.setColor(QPalette::ToolTipBase, Qt::white); themePalette.setColor(QPalette::ToolTipText, Qt::white); @@ -28,7 +27,6 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { themePalette.setColor(QPalette::HighlightedText, Qt::black); qApp->setPalette(themePalette); break; - case Theme::Light: mw_searchbar->setStyleSheet("background-color: #ffffff;" // Light gray background "color: #000000;" // Black text @@ -115,6 +113,26 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { themePalette.setColor(QPalette::Highlight, QColor(42, 130, 218)); // Light blue highlight themePalette.setColor(QPalette::HighlightedText, Qt::black); // Black highlighted text + qApp->setPalette(themePalette); + break; + case Theme::Gruvbox: + mw_searchbar->setStyleSheet("background-color: #1d2021;" + "color: #f9f5d7;" + "border: 2px solid #f9f5d7;" + "padding: 5px;"); + themePalette.setColor(QPalette::Window, QColor(29, 32, 33)); + themePalette.setColor(QPalette::WindowText, QColor(249, 245, 215)); + themePalette.setColor(QPalette::Base, QColor(29, 32, 33)); + themePalette.setColor(QPalette::AlternateBase, QColor(50, 48, 47)); + themePalette.setColor(QPalette::ToolTipBase, QColor(249, 245, 215)); + themePalette.setColor(QPalette::ToolTipText, QColor(249, 245, 215)); + themePalette.setColor(QPalette::Text, QColor(249, 245, 215)); + themePalette.setColor(QPalette::Button, QColor(40, 40, 40)); + themePalette.setColor(QPalette::ButtonText, QColor(249, 245, 215)); + themePalette.setColor(QPalette::BrightText, QColor(251, 73, 52)); + themePalette.setColor(QPalette::Link, QColor(131, 165, 152)); + themePalette.setColor(QPalette::Highlight, QColor(131, 165, 152)); + themePalette.setColor(QPalette::HighlightedText, Qt::black); qApp->setPalette(themePalette); break; } diff --git a/src/qt_gui/main_window_themes.h b/src/qt_gui/main_window_themes.h index 6da70e995..d162da87b 100644 --- a/src/qt_gui/main_window_themes.h +++ b/src/qt_gui/main_window_themes.h @@ -7,13 +7,7 @@ #include #include -enum class Theme : int { - Dark, - Light, - Green, - Blue, - Violet, -}; +enum class Theme : int { Dark, Light, Green, Blue, Violet, Gruvbox }; class WindowThemes : public QObject { Q_OBJECT diff --git a/src/qt_gui/main_window_ui.h b/src/qt_gui/main_window_ui.h index 5ff572f86..df64361fd 100644 --- a/src/qt_gui/main_window_ui.h +++ b/src/qt_gui/main_window_ui.h @@ -36,6 +36,7 @@ public: QAction* setThemeGreen; QAction* setThemeBlue; QAction* setThemeViolet; + QAction* setThemeGruvbox; QWidget* centralWidget; QLineEdit* mw_searchbar; QPushButton* playButton; @@ -158,6 +159,9 @@ public: setThemeViolet = new QAction(MainWindow); setThemeViolet->setObjectName("setThemeViolet"); setThemeViolet->setCheckable(true); + setThemeGruvbox = new QAction(MainWindow); + setThemeGruvbox->setObjectName("setThemeGruvbox"); + setThemeGruvbox->setCheckable(true); centralWidget = new QWidget(MainWindow); centralWidget->setObjectName("centralWidget"); sizePolicy.setHeightForWidth(centralWidget->sizePolicy().hasHeightForWidth()); @@ -282,6 +286,7 @@ public: menuThemes->addAction(setThemeGreen); menuThemes->addAction(setThemeBlue); menuThemes->addAction(setThemeViolet); + menuThemes->addAction(setThemeGruvbox); menuGame_List_Icons->addAction(setIconSizeTinyAct); menuGame_List_Icons->addAction(setIconSizeSmallAct); menuGame_List_Icons->addAction(setIconSizeMediumAct); @@ -368,6 +373,7 @@ public: setThemeGreen->setText(QCoreApplication::translate("MainWindow", "Green", nullptr)); setThemeBlue->setText(QCoreApplication::translate("MainWindow", "Blue", nullptr)); setThemeViolet->setText(QCoreApplication::translate("MainWindow", "Violet", nullptr)); + setThemeGruvbox->setText("Gruvbox"); toolBar->setWindowTitle(QCoreApplication::translate("MainWindow", "toolBar", nullptr)); } // retranslateUi }; From 9aa1c13c7e20079716a55657e1d47bda1aafd3ff Mon Sep 17 00:00:00 2001 From: baggins183 Date: Sun, 15 Dec 2024 06:30:19 -0800 Subject: [PATCH 196/549] Fix some compiler problems with ds3 (#1793) - Implement S_CMOVK_I32 - Handle Isoline abstract patch type --- .../frontend/translate/scalar_alu.cpp | 20 +++++++++++-------- .../frontend/translate/translate.h | 2 +- .../ir/passes/hull_shader_transform.cpp | 10 ++++++---- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/shader_recompiler/frontend/translate/scalar_alu.cpp b/src/shader_recompiler/frontend/translate/scalar_alu.cpp index 1ef0d82d8..e731e299a 100644 --- a/src/shader_recompiler/frontend/translate/scalar_alu.cpp +++ b/src/shader_recompiler/frontend/translate/scalar_alu.cpp @@ -161,8 +161,9 @@ void Translator::EmitSOPK(const GcnInst& inst) { switch (inst.opcode) { // SOPK case Opcode::S_MOVK_I32: - return S_MOVK(inst); - + return S_MOVK(inst, false); + case Opcode::S_CMOVK_I32: + return S_MOVK(inst, true); case Opcode::S_CMPK_EQ_I32: return S_CMPK(ConditionOp::EQ, true, inst); case Opcode::S_CMPK_LG_I32: @@ -458,13 +459,16 @@ void Translator::S_ABSDIFF_I32(const GcnInst& inst) { // SOPK -void Translator::S_MOVK(const GcnInst& inst) { - const auto simm16 = inst.control.sopk.simm; - if (simm16 & (1 << 15)) { - // TODO: need to verify the case of imm sign extension - UNREACHABLE(); +void Translator::S_MOVK(const GcnInst& inst, bool is_conditional) { + const s16 simm16 = inst.control.sopk.simm; + // do the sign extension + const s32 simm32 = static_cast(simm16); + IR::U32 val = ir.Imm32(simm32); + if (is_conditional) { + // if !SCC its a NOP + val = IR::U32{ir.Select(ir.GetScc(), val, GetSrc(inst.dst[0]))}; } - SetDst(inst.dst[0], ir.Imm32(simm16)); + SetDst(inst.dst[0], val); } void Translator::S_CMPK(ConditionOp cond, bool is_signed, const GcnInst& inst) { diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index 60bad1864..8e575fcad 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -100,7 +100,7 @@ public: void S_NOT_B32(const GcnInst& inst); // SOPK - void S_MOVK(const GcnInst& inst); + void S_MOVK(const GcnInst& inst, bool is_conditional); void S_CMPK(ConditionOp cond, bool is_signed, const GcnInst& inst); void S_ADDK_I32(const GcnInst& inst); void S_MULK_I32(const GcnInst& inst); diff --git a/src/shader_recompiler/ir/passes/hull_shader_transform.cpp b/src/shader_recompiler/ir/passes/hull_shader_transform.cpp index 5cf02b6d0..895c9823e 100644 --- a/src/shader_recompiler/ir/passes/hull_shader_transform.cpp +++ b/src/shader_recompiler/ir/passes/hull_shader_transform.cpp @@ -398,8 +398,8 @@ void HullShaderTransform(IR::Program& program, RuntimeInfo& runtime_info) { // communicated to the driver. // The layout seems to be implied by the type of the abstract domain. switch (runtime_info.hs_info.tess_type) { - case AmdGpu::TessellationType::Quad: - ASSERT(gcn_factor_idx < 6); + case AmdGpu::TessellationType::Isoline: + ASSERT(gcn_factor_idx < 2); return IR::PatchFactor(gcn_factor_idx); case AmdGpu::TessellationType::Triangle: ASSERT(gcn_factor_idx < 4); @@ -407,9 +407,11 @@ void HullShaderTransform(IR::Program& program, RuntimeInfo& runtime_info) { return IR::Patch::TessellationLodInteriorU; } return IR::PatchFactor(gcn_factor_idx); + case AmdGpu::TessellationType::Quad: + ASSERT(gcn_factor_idx < 6); + return IR::PatchFactor(gcn_factor_idx); default: - // Point domain types haven't been seen so far - UNREACHABLE_MSG("Unhandled tess type"); + UNREACHABLE(); } }; From 3001b007f6a450f62526fa61724753ab532bef20 Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Sun, 15 Dec 2024 11:30:53 -0300 Subject: [PATCH 197/549] Keybord on README / Fix Play Time (#1786) * Keybord on README F10_F11_F12 * Update game_list_frame.cpp --- README.md | 7 +++++++ src/qt_gui/game_list_frame.cpp | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 18e69546c..7ef5bdf65 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,13 @@ For more information on how to test, debug and report issues with the emulator o # Keyboard mapping +| Button | Function | +|-------------|-------------| +F10 | FPS Counter +Ctrl+F10 | Video Debug Info +F11 | Fullscreen +F12 | Trigger RenderDoc Capture + > [!NOTE] > Xbox and DualShock controllers work out of the box. diff --git a/src/qt_gui/game_list_frame.cpp b/src/qt_gui/game_list_frame.cpp index 47bfbfef9..63f6b63b8 100644 --- a/src/qt_gui/game_list_frame.cpp +++ b/src/qt_gui/game_list_frame.cpp @@ -123,7 +123,7 @@ void GameListFrame::PopulateGameList() { formattedPlayTime = formattedPlayTime.trimmed(); m_game_info->m_games[i].play_time = playTime.toStdString(); if (formattedPlayTime.isEmpty()) { - SetTableItem(i, 7, "0"); + SetTableItem(i, 7, QString("%1s").arg(seconds)); } else { SetTableItem(i, 7, formattedPlayTime); } From e7c4ffe032a0dd5605322363b9766425ea5531f9 Mon Sep 17 00:00:00 2001 From: psucien Date: Sun, 15 Dec 2024 20:53:29 +0100 Subject: [PATCH 198/549] hot-fix: Tracy operation restored; memory leak fix as a bonus --- externals/CMakeLists.txt | 2 +- src/common/debug.h | 7 +++++++ src/core/libraries/gnmdriver/gnmdriver.cpp | 3 +++ src/video_core/amdgpu/liverpool.cpp | 3 ++- src/video_core/renderer_vulkan/vk_instance.cpp | 4 +++- 5 files changed, 16 insertions(+), 3 deletions(-) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 1ab23a403..dbe6794d8 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -193,7 +193,7 @@ option(TRACY_ENABLE "" ON) option(TRACY_NO_CRASH_HANDLER "" ON) # Otherwise texture cache exceptions will be treaten as a crash option(TRACY_ON_DEMAND "" ON) option(TRACY_NO_FRAME_IMAGE "" ON) -option(TRACY_FIBERS "" ON) # For AmdGpu frontend profiling +option(TRACY_FIBERS "" OFF) # For AmdGpu frontend profiling, disabled due to instability option(TRACY_NO_SYSTEM_TRACING "" ON) option(TRACY_NO_CALLSTACK "" ON) option(TRACY_NO_CODE_TRANSFER "" ON) diff --git a/src/common/debug.h b/src/common/debug.h index 4d42aa4ab..882e9e5c4 100644 --- a/src/common/debug.h +++ b/src/common/debug.h @@ -17,6 +17,8 @@ static inline bool IsProfilerConnected() { return tracy::GetProfiler().IsConnected(); } +#define TRACY_GPU_ENABLED 0 + #define CUSTOM_LOCK(type, varname) \ tracy::LockableCtx varname { \ []() -> const tracy::SourceLocationData* { \ @@ -58,5 +60,10 @@ enum MarkersPalette : int { #define FRAME_END FrameMark +#ifdef TRACY_FIBERS #define FIBER_ENTER(name) TracyFiberEnter(name) #define FIBER_EXIT TracyFiberLeave +#else +#define FIBER_ENTER(name) +#define FIBER_EXIT +#endif diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index 583339dd9..1a6007bf8 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -488,6 +488,7 @@ int PS4_SYSV_ABI sceGnmDestroyWorkloadStream() { } void PS4_SYSV_ABI sceGnmDingDong(u32 gnm_vqid, u32 next_offs_dw) { + HLE_TRACE; LOG_DEBUG(Lib_GnmDriver, "vqid {}, offset_dw {}", gnm_vqid, next_offs_dw); if (gnm_vqid == 0) { @@ -2166,6 +2167,7 @@ int PS4_SYSV_ABI sceGnmSubmitCommandBuffersForWorkload(u32 workload, u32 count, u32* dcb_sizes_in_bytes, const u32* ccb_gpu_addrs[], u32* ccb_sizes_in_bytes) { + HLE_TRACE; LOG_DEBUG(Lib_GnmDriver, "called"); if (!dcb_gpu_addrs || !dcb_sizes_in_bytes) { @@ -2258,6 +2260,7 @@ s32 PS4_SYSV_ABI sceGnmSubmitCommandBuffers(u32 count, const u32* dcb_gpu_addrs[ } int PS4_SYSV_ABI sceGnmSubmitDone() { + HLE_TRACE; LOG_DEBUG(Lib_GnmDriver, "called"); WaitGpuIdle(); if (!liverpool->IsGpuIdle()) { diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index 8cca636c0..5dd3edd6d 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -659,7 +659,8 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spanAddress(); - if (vo_port->IsVoLabel(wait_addr) && num_submits == 1) { + if (vo_port->IsVoLabel(wait_addr) && + num_submits == mapped_queues[GfxQueueId].submits.size()) { vo_port->WaitVoLabel([&] { return wait_reg_mem->Test(); }); } while (!wait_reg_mem->Test()) { diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index d7bfaee4e..b479c1464 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -9,6 +9,7 @@ #include "common/assert.h" #include "common/config.h" +#include "common/debug.h" #include "sdl_window.h" #include "video_core/renderer_vulkan/liverpool_to_vk.h" #include "video_core/renderer_vulkan/vk_instance.h" @@ -261,7 +262,8 @@ bool Instance::CreateDevice() { // The next two extensions are required to be available together in order to support write masks color_write_en = add_extension(VK_EXT_COLOR_WRITE_ENABLE_EXTENSION_NAME); color_write_en &= add_extension(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); - const bool calibrated_timestamps = add_extension(VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME); + const bool calibrated_timestamps = + TRACY_GPU_ENABLED ? add_extension(VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME) : false; const bool robustness = add_extension(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME); list_restart = add_extension(VK_EXT_PRIMITIVE_TOPOLOGY_LIST_RESTART_EXTENSION_NAME); maintenance5 = add_extension(VK_KHR_MAINTENANCE_5_EXTENSION_NAME); From 8a4e03228aca87fd31da6730e60723ece601a1c5 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sun, 15 Dec 2024 16:11:15 -0800 Subject: [PATCH 199/549] spirv_emit_context: Prevent double-add of GS in attributes to interface. (#1800) --- src/shader_recompiler/backend/spirv/spirv_emit_context.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 5f0ad298e..5d2ec6f96 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -381,9 +381,8 @@ void EmitContext::DefineInputs() { for (int param_id = 0; param_id < num_params; ++param_id) { const Id type{TypeArray(F32[4], ConstU32(num_verts_in))}; const Id id{DefineInput(type, param_id)}; - Name(id, fmt::format("in_attr{}", param_id)); + Name(id, fmt::format("gs_in_attr{}", param_id)); input_params[param_id] = {id, input_f32, F32[1], 4}; - interfaces.push_back(id); } break; } From 5585e42677d3f00c71679fe20aab1446f9a5bca3 Mon Sep 17 00:00:00 2001 From: Richard Habitzreuter Date: Tue, 17 Dec 2024 07:32:30 -0300 Subject: [PATCH 200/549] style: add rounded borders and focus color styling to the search bar (#1804) --- src/qt_gui/main_window.cpp | 1 + src/qt_gui/main_window_themes.cpp | 64 +++++++++++++++++-------------- 2 files changed, 37 insertions(+), 28 deletions(-) diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 0b5137c4b..9c81bcf11 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -921,6 +921,7 @@ void MainWindow::SetLastUsedTheme() { ui->setThemeViolet->setChecked(true); isIconBlack = false; SetUiIcons(false); + break; case Theme::Gruvbox: ui->setThemeGruvbox->setChecked(true); isIconBlack = false; diff --git a/src/qt_gui/main_window_themes.cpp b/src/qt_gui/main_window_themes.cpp index 65dd04269..a52b4466e 100644 --- a/src/qt_gui/main_window_themes.cpp +++ b/src/qt_gui/main_window_themes.cpp @@ -8,10 +8,12 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { switch (theme) { case Theme::Dark: - mw_searchbar->setStyleSheet("background-color: #1e1e1e;" // Dark background - "color: #ffffff;" // White text - "border: 2px solid #ffffff;" // White border - "padding: 5px;"); + mw_searchbar->setStyleSheet( + "QLineEdit {" + "background-color: #1e1e1e; color: #ffffff; border: 1px solid #ffffff; " + "border-radius: 4px; padding: 5px; }" + "QLineEdit:focus {" + "border: 1px solid #2A82DA; }"); themePalette.setColor(QPalette::Window, QColor(50, 50, 50)); themePalette.setColor(QPalette::WindowText, Qt::white); themePalette.setColor(QPalette::Base, QColor(20, 20, 20)); @@ -28,10 +30,12 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { qApp->setPalette(themePalette); break; case Theme::Light: - mw_searchbar->setStyleSheet("background-color: #ffffff;" // Light gray background - "color: #000000;" // Black text - "border: 2px solid #000000;" // Black border - "padding: 5px;"); + mw_searchbar->setStyleSheet( + "QLineEdit {" + "background-color: #ffffff; color: #000000; border: 1px solid #000000; " + "border-radius: 4px; padding: 5px; }" + "QLineEdit:focus {" + "border: 1px solid #2A82DA; }"); themePalette.setColor(QPalette::Window, QColor(240, 240, 240)); // Light gray themePalette.setColor(QPalette::WindowText, Qt::black); // Black themePalette.setColor(QPalette::Base, QColor(230, 230, 230, 80)); // Grayish @@ -46,12 +50,13 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { themePalette.setColor(QPalette::HighlightedText, Qt::white); // White qApp->setPalette(themePalette); break; - case Theme::Green: - mw_searchbar->setStyleSheet("background-color: #1e1e1e;" // Dark background - "color: #ffffff;" // White text - "border: 2px solid #ffffff;" // White border - "padding: 5px;"); + mw_searchbar->setStyleSheet( + "QLineEdit {" + "background-color: #192819; color: #ffffff; border: 1px solid #ffffff; " + "border-radius: 4px; padding: 5px; }" + "QLineEdit:focus {" + "border: 1px solid #2A82DA; }"); themePalette.setColor(QPalette::Window, QColor(53, 69, 53)); // Dark green background themePalette.setColor(QPalette::WindowText, Qt::white); // White text themePalette.setColor(QPalette::Base, QColor(25, 40, 25)); // Darker green base @@ -66,15 +71,15 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { themePalette.setColor(QPalette::Link, QColor(42, 130, 218)); // Light blue links themePalette.setColor(QPalette::Highlight, QColor(42, 130, 218)); // Light blue highlight themePalette.setColor(QPalette::HighlightedText, Qt::black); // Black highlighted text - qApp->setPalette(themePalette); break; - case Theme::Blue: - mw_searchbar->setStyleSheet("background-color: #1e1e1e;" // Dark background - "color: #ffffff;" // White text - "border: 2px solid #ffffff;" // White border - "padding: 5px;"); + mw_searchbar->setStyleSheet( + "QLineEdit {" + "background-color: #14283c; color: #ffffff; border: 1px solid #ffffff; " + "border-radius: 4px; padding: 5px; }" + "QLineEdit:focus {" + "border: 1px solid #2A82DA; }"); themePalette.setColor(QPalette::Window, QColor(40, 60, 90)); // Dark blue background themePalette.setColor(QPalette::WindowText, Qt::white); // White text themePalette.setColor(QPalette::Base, QColor(20, 40, 60)); // Darker blue base @@ -92,12 +97,13 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { qApp->setPalette(themePalette); break; - case Theme::Violet: - mw_searchbar->setStyleSheet("background-color: #1e1e1e;" // Dark background - "color: #ffffff;" // White text - "border: 2px solid #ffffff;" // White border - "padding: 5px;"); + mw_searchbar->setStyleSheet( + "QLineEdit {" + "background-color: #501e5a; color: #ffffff; border: 1px solid #ffffff; " + "border-radius: 4px; padding: 5px; }" + "QLineEdit:focus {" + "border: 1px solid #2A82DA; }"); themePalette.setColor(QPalette::Window, QColor(100, 50, 120)); // Violet background themePalette.setColor(QPalette::WindowText, Qt::white); // White text themePalette.setColor(QPalette::Base, QColor(80, 30, 90)); // Darker violet base @@ -116,10 +122,12 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { qApp->setPalette(themePalette); break; case Theme::Gruvbox: - mw_searchbar->setStyleSheet("background-color: #1d2021;" - "color: #f9f5d7;" - "border: 2px solid #f9f5d7;" - "padding: 5px;"); + mw_searchbar->setStyleSheet( + "QLineEdit {" + "background-color: #1d2021; color: #f9f5d7; border: 1px solid #f9f5d7; " + "border-radius: 4px; padding: 5px; }" + "QLineEdit:focus {" + "border: 1px solid #83A598; }"); themePalette.setColor(QPalette::Window, QColor(29, 32, 33)); themePalette.setColor(QPalette::WindowText, QColor(249, 245, 215)); themePalette.setColor(QPalette::Base, QColor(29, 32, 33)); From 3c8e25e8e48f5a7618d84d28a534e3530c538790 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Tue, 17 Dec 2024 02:34:43 -0800 Subject: [PATCH 201/549] fs: Fix wrong mounts being matched by partial guest path. (#1809) --- src/core/file_sys/fs.cpp | 21 +++++++++++++++++---- src/core/file_sys/fs.h | 9 ++++++--- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/core/file_sys/fs.cpp b/src/core/file_sys/fs.cpp index 0fdbb2783..92f725cc7 100644 --- a/src/core/file_sys/fs.cpp +++ b/src/core/file_sys/fs.cpp @@ -10,16 +10,28 @@ namespace Core::FileSys { +std::string RemoveTrailingSlashes(const std::string& path) { + // Remove trailing slashes to make comparisons simpler. + std::string path_sanitized = path; + while (path_sanitized.ends_with("/")) { + path_sanitized.pop_back(); + } + return path_sanitized; +} + void MntPoints::Mount(const std::filesystem::path& host_folder, const std::string& guest_folder, bool read_only) { std::scoped_lock lock{m_mutex}; - m_mnt_pairs.emplace_back(host_folder, guest_folder, read_only); + const auto guest_folder_sanitized = RemoveTrailingSlashes(guest_folder); + m_mnt_pairs.emplace_back(host_folder, guest_folder_sanitized, read_only); } void MntPoints::Unmount(const std::filesystem::path& host_folder, const std::string& guest_folder) { std::scoped_lock lock{m_mutex}; - auto it = std::remove_if(m_mnt_pairs.begin(), m_mnt_pairs.end(), - [&](const MntPair& pair) { return pair.mount == guest_folder; }); + const auto guest_folder_sanitized = RemoveTrailingSlashes(guest_folder); + auto it = std::remove_if(m_mnt_pairs.begin(), m_mnt_pairs.end(), [&](const MntPair& pair) { + return pair.mount == guest_folder_sanitized; + }); m_mnt_pairs.erase(it, m_mnt_pairs.end()); } @@ -47,7 +59,8 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_rea } // Nothing to do if getting the mount itself. - if (corrected_path == mount->mount) { + const auto corrected_path_sanitized = RemoveTrailingSlashes(corrected_path); + if (corrected_path_sanitized == mount->mount) { return mount->host_path; } diff --git a/src/core/file_sys/fs.h b/src/core/file_sys/fs.h index b0153c162..e219887c8 100644 --- a/src/core/file_sys/fs.h +++ b/src/core/file_sys/fs.h @@ -22,7 +22,7 @@ class MntPoints { public: struct MntPair { std::filesystem::path host_path; - std::string mount; // e.g /app0/ + std::string mount; // e.g /app0 bool read_only; }; @@ -39,8 +39,11 @@ public: const MntPair* GetMount(const std::string& guest_path) { std::scoped_lock lock{m_mutex}; - const auto it = std::ranges::find_if( - m_mnt_pairs, [&](const auto& mount) { return guest_path.starts_with(mount.mount); }); + const auto it = std::ranges::find_if(m_mnt_pairs, [&](const auto& mount) { + // When doing starts-with check, add a trailing slash to make sure we don't match + // against only part of the mount path. + return guest_path == mount.mount || guest_path.starts_with(mount.mount + "/"); + }); return it == m_mnt_pairs.end() ? nullptr : &*it; } From aa5c1c10dfa560d55c59da2d7f841c3e9e0485fc Mon Sep 17 00:00:00 2001 From: ElBread3 <92335081+ElBread3@users.noreply.github.com> Date: Tue, 17 Dec 2024 04:42:21 -0600 Subject: [PATCH 202/549] More Fixes for Separate Update (#1487) * handle getdents + fix condition + add info to description * fix not handling dents errors * to not overwrite it, only gather separate update entries when normal folder is done * fix always setting entries to 0 and guest name including "UPDATE" * reset indexes on completion * don't use concat, fixes long standing bug * make sce_module module loading take both paths into account --- src/core/file_sys/fs.cpp | 10 ++++ src/core/file_sys/fs.h | 10 ++++ src/core/libraries/kernel/file_system.cpp | 58 ++++++++++++++++++++++- src/emulator.cpp | 46 +++++++++++++----- src/qt_gui/gui_context_menus.h | 22 ++++----- src/qt_gui/translations/en.ts | 2 +- 6 files changed, 119 insertions(+), 29 deletions(-) diff --git a/src/core/file_sys/fs.cpp b/src/core/file_sys/fs.cpp index 92f725cc7..45ba67b93 100644 --- a/src/core/file_sys/fs.cpp +++ b/src/core/file_sys/fs.cpp @@ -199,4 +199,14 @@ void HandleTable::CreateStdHandles() { setup("/dev/stderr", new Devices::Logger("stderr", true)); // stderr } +int HandleTable::GetFileDescriptor(File* file) { + std::scoped_lock lock{m_mutex}; + auto it = std::find(m_files.begin(), m_files.end(), file); + + if (it != m_files.end()) { + return std::distance(m_files.begin(), it); + } + return 0; +} + } // namespace Core::FileSys diff --git a/src/core/file_sys/fs.h b/src/core/file_sys/fs.h index e219887c8..56df32ad0 100644 --- a/src/core/file_sys/fs.h +++ b/src/core/file_sys/fs.h @@ -9,6 +9,7 @@ #include #include #include "common/io_file.h" +#include "common/logging/formatter.h" #include "core/devices/base_device.h" namespace Core::FileSys { @@ -37,6 +38,14 @@ public: std::filesystem::path GetHostPath(std::string_view guest_directory, bool* is_read_only = nullptr); + const MntPair* GetMountFromHostPath(const std::string& host_path) { + std::scoped_lock lock{m_mutex}; + const auto it = std::ranges::find_if(m_mnt_pairs, [&](const MntPair& mount) { + return host_path.starts_with(std::string{fmt::UTF(mount.host_path.u8string()).data}); + }); + return it == m_mnt_pairs.end() ? nullptr : &*it; + } + const MntPair* GetMount(const std::string& guest_path) { std::scoped_lock lock{m_mutex}; const auto it = std::ranges::find_if(m_mnt_pairs, [&](const auto& mount) { @@ -86,6 +95,7 @@ public: void DeleteHandle(int d); File* GetFile(int d); File* GetFile(const std::filesystem::path& host_name); + int GetFileDescriptor(File* file); void CreateStdHandles(); diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index 5ba9976c6..57efbb631 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -695,12 +695,66 @@ static int GetDents(int fd, char* buf, int nbytes, s64* basep) { return sizeof(OrbisKernelDirent); } +static int HandleSeparateUpdateDents(int fd, char* buf, int nbytes, s64* basep) { + int dir_entries = 0; + + auto* h = Common::Singleton::Instance(); + auto* mnt = Common::Singleton::Instance(); + auto* file = h->GetFile(fd); + auto update_dir_name = std::string{fmt::UTF(file->m_host_name.u8string()).data}; + auto mount = mnt->GetMountFromHostPath(update_dir_name); + auto suffix = std::string{fmt::UTF(mount->host_path.u8string()).data}; + + size_t pos = update_dir_name.find("-UPDATE"); + if (pos != std::string::npos) { + update_dir_name.erase(pos, 7); + auto guest_name = mount->mount + "/" + update_dir_name.substr(suffix.size() + 1); + int descriptor; + + auto existent_folder = h->GetFile(update_dir_name); + if (!existent_folder) { + u32 handle = h->CreateHandle(); + auto* new_file = h->GetFile(handle); + new_file->type = Core::FileSys::FileType::Directory; + new_file->m_guest_name = guest_name; + new_file->m_host_name = update_dir_name; + if (!std::filesystem::is_directory(new_file->m_host_name)) { + h->DeleteHandle(handle); + return dir_entries; + } else { + new_file->dirents = GetDirectoryEntries(new_file->m_host_name); + new_file->dirents_index = 0; + } + new_file->is_opened = true; + descriptor = h->GetFileDescriptor(new_file); + } else { + descriptor = h->GetFileDescriptor(existent_folder); + } + + dir_entries = GetDents(descriptor, buf, nbytes, basep); + if (dir_entries == ORBIS_OK && existent_folder) { + existent_folder->dirents_index = 0; + file->dirents_index = 0; + } + } + + return dir_entries; +} + int PS4_SYSV_ABI sceKernelGetdents(int fd, char* buf, int nbytes) { - return GetDents(fd, buf, nbytes, nullptr); + int a = GetDents(fd, buf, nbytes, nullptr); + if (a == ORBIS_OK) { + return HandleSeparateUpdateDents(fd, buf, nbytes, nullptr); + } + return a; } int PS4_SYSV_ABI sceKernelGetdirentries(int fd, char* buf, int nbytes, s64* basep) { - return GetDents(fd, buf, nbytes, basep); + int a = GetDents(fd, buf, nbytes, basep); + if (a == ORBIS_OK) { + return HandleSeparateUpdateDents(fd, buf, nbytes, basep); + } + return a; } s64 PS4_SYSV_ABI sceKernelPwrite(int d, void* buf, size_t nbytes, s64 offset) { diff --git a/src/emulator.cpp b/src/emulator.cpp index c517bc284..252a34418 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include #include "common/config.h" @@ -106,9 +107,11 @@ Emulator::~Emulator() { void Emulator::Run(const std::filesystem::path& file) { // Use the eboot from the separated updates folder if it's there - std::filesystem::path game_patch_folder = file.parent_path().concat("-UPDATE"); - bool use_game_patch = std::filesystem::exists(game_patch_folder / "sce_sys"); - std::filesystem::path eboot_path = use_game_patch ? game_patch_folder / file.filename() : file; + std::filesystem::path game_patch_folder = file.parent_path(); + game_patch_folder += "-UPDATE"; + std::filesystem::path eboot_path = std::filesystem::exists(game_patch_folder / file.filename()) + ? game_patch_folder / file.filename() + : file; // Applications expect to be run from /app0 so mount the file's parent path as app0. auto* mnt = Common::Singleton::Instance(); @@ -226,20 +229,37 @@ void Emulator::Run(const std::filesystem::path& file) { LoadSystemModules(eboot_path, game_info.game_serial); // Load all prx from game's sce_module folder - std::filesystem::path sce_module_folder = file.parent_path() / "sce_module"; - if (std::filesystem::is_directory(sce_module_folder)) { - for (const auto& entry : std::filesystem::directory_iterator(sce_module_folder)) { - std::filesystem::path module_path = entry.path(); - std::filesystem::path update_module_path = - eboot_path.parent_path() / "sce_module" / entry.path().filename(); - if (std::filesystem::exists(update_module_path) && use_game_patch) { - module_path = update_module_path; + std::vector modules_to_load; + std::filesystem::path game_module_folder = file.parent_path() / "sce_module"; + if (std::filesystem::is_directory(game_module_folder)) { + for (const auto& entry : std::filesystem::directory_iterator(game_module_folder)) { + if (entry.is_regular_file()) { + modules_to_load.push_back(entry.path()); } - LOG_INFO(Loader, "Loading {}", fmt::UTF(module_path.u8string())); - linker->LoadModule(module_path); } } + // Load all prx from separate update's sce_module folder + std::filesystem::path update_module_folder = game_patch_folder / "sce_module"; + if (std::filesystem::is_directory(update_module_folder)) { + for (const auto& entry : std::filesystem::directory_iterator(update_module_folder)) { + auto it = std::find_if(modules_to_load.begin(), modules_to_load.end(), + [&entry](const std::filesystem::path& p) { + return p.filename() == entry.path().filename(); + }); + if (it != modules_to_load.end()) { + *it = entry.path(); + } else { + modules_to_load.push_back(entry.path()); + } + } + } + + for (const auto& module_path : modules_to_load) { + LOG_INFO(Loader, "Loading {}", fmt::UTF(module_path.u8string())); + linker->LoadModule(module_path); + } + #ifdef ENABLE_DISCORD_RPC // Discord RPC if (Config::getEnableDiscordRPC()) { diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h index 6eef1230c..3cc12c11e 100644 --- a/src/qt_gui/gui_context_menus.h +++ b/src/qt_gui/gui_context_menus.h @@ -122,11 +122,11 @@ public: if (selected == &openSfoViewer) { PSF psf; - QString game_update_path; - Common::FS::PathToQString(game_update_path, m_games[itemID].path.concat("-UPDATE")); std::filesystem::path game_folder_path = m_games[itemID].path; - if (std::filesystem::exists(Common::FS::PathFromQString(game_update_path))) { - game_folder_path = Common::FS::PathFromQString(game_update_path); + std::filesystem::path game_update_path = game_folder_path; + game_update_path += "UPDATE"; + if (std::filesystem::exists(game_update_path)) { + game_folder_path = game_update_path; } if (psf.Open(game_folder_path / "sce_sys" / "param.sfo")) { int rows = psf.GetEntries().size(); @@ -320,21 +320,17 @@ public: bool error = false; QString folder_path, game_update_path, dlc_path; Common::FS::PathToQString(folder_path, m_games[itemID].path); - Common::FS::PathToQString(game_update_path, m_games[itemID].path.concat("-UPDATE")); + game_update_path = folder_path + "-UPDATE"; Common::FS::PathToQString( dlc_path, Config::getAddonInstallDir() / Common::FS::PathFromQString(folder_path).parent_path().filename()); QString message_type = tr("Game"); if (selected == deleteUpdate) { - if (!Config::getSeparateUpdateEnabled()) { - QMessageBox::critical(nullptr, tr("Error"), - QString(tr("requiresEnableSeparateUpdateFolder_MSG"))); - error = true; - } else if (!std::filesystem::exists( - Common::FS::PathFromQString(game_update_path))) { - QMessageBox::critical(nullptr, tr("Error"), - QString(tr("This game has no update to delete!"))); + if (!std::filesystem::exists(Common::FS::PathFromQString(game_update_path))) { + QMessageBox::critical( + nullptr, tr("Error"), + QString(tr("This game has no separate update to delete!"))); error = true; } else { folder_path = game_update_path; diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index 7ae583040..9eccec8ea 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -1159,7 +1159,7 @@ separateUpdatesCheckBox - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. From 87773a417b96417a14bab695422f70e80697f4e4 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Tue, 17 Dec 2024 05:04:19 -0800 Subject: [PATCH 203/549] mac: Choose whether system Vulkan is needed at runtime. (#1780) --- CMakeLists.txt | 13 ++- REUSE.toml | 1 + externals/MoltenVK/CMakeLists.txt | 16 ++- externals/MoltenVK/MoltenVK_icd.json | 8 ++ src/video_core/renderer_vulkan/vk_common.h | 4 - .../renderer_vulkan/vk_platform.cpp | 105 +++++++++++------- 6 files changed, 95 insertions(+), 52 deletions(-) create mode 100644 externals/MoltenVK/MoltenVK_icd.json diff --git a/CMakeLists.txt b/CMakeLists.txt index 78d8421a3..8f0397e86 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -892,11 +892,16 @@ if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") endif() if (APPLE) - option(USE_SYSTEM_VULKAN_LOADER "Enables using the system Vulkan loader instead of directly linking with MoltenVK. Useful for loading validation layers." OFF) - if (USE_SYSTEM_VULKAN_LOADER) - target_compile_definitions(shadps4 PRIVATE USE_SYSTEM_VULKAN_LOADER=1) + if (ENABLE_QT_GUI) + # Include MoltenVK in the app bundle, along with an ICD file so it can be found by the system Vulkan loader if used for loading layers. + target_sources(shadps4 PRIVATE externals/MoltenVK/MoltenVK_icd.json) + set_source_files_properties(externals/MoltenVK/MoltenVK_icd.json + PROPERTIES MACOSX_PACKAGE_LOCATION Resources/vulkan/icd.d) + add_custom_command(TARGET shadps4 POST_BUILD + COMMAND cmake -E copy $ $/Contents/Frameworks/libMoltenVK.dylib) + set_property(TARGET shadps4 APPEND PROPERTY BUILD_RPATH "@executable_path/../Frameworks") else() - # Link MoltenVK for Vulkan support + # For non-bundled SDL build, just do a normal library link. target_link_libraries(shadps4 PRIVATE MoltenVK) endif() diff --git a/REUSE.toml b/REUSE.toml index 747679c8b..cba63adf1 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -15,6 +15,7 @@ path = [ "documents/changelog.md", "documents/Quickstart/2.png", "documents/Screenshots/*", + "externals/MoltenVK/MoltenVK_icd.json", "scripts/ps4_names.txt", "src/images/about_icon.png", "src/images/controller_icon.png", diff --git a/externals/MoltenVK/CMakeLists.txt b/externals/MoltenVK/CMakeLists.txt index 00e3231ee..908c2847c 100644 --- a/externals/MoltenVK/CMakeLists.txt +++ b/externals/MoltenVK/CMakeLists.txt @@ -1,17 +1,29 @@ # SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project # SPDX-License-Identifier: GPL-2.0-or-later -# Prepare version information +# Prepare MoltenVK Git revision find_package(Git) if(GIT_FOUND) execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD OUTPUT_VARIABLE MVK_GIT_REV + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) endif() -set(MVK_VERSION "1.2.12") set(MVK_GENERATED_INCLUDES ${CMAKE_CURRENT_BINARY_DIR}/Generated) file(WRITE ${MVK_GENERATED_INCLUDES}/mvkGitRevDerived.h "static const char* mvkRevString = \"${MVK_GIT_REV}\";") +message(STATUS "MoltenVK revision: ${MVK_GIT_REV}") + +# Prepare MoltenVK version +file(READ ${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK/MoltenVK/MoltenVK/API/mvk_private_api.h MVK_PRIVATE_API) +string(REGEX MATCH "#define MVK_VERSION_MAJOR [0-9]+" MVK_VERSION_MAJOR_LINE "${MVK_PRIVATE_API}") +string(REGEX MATCH "[0-9]+" MVK_VERSION_MAJOR "${MVK_VERSION_MAJOR_LINE}") +string(REGEX MATCH "#define MVK_VERSION_MINOR [0-9]+" MVK_VERSION_MINOR_LINE "${MVK_PRIVATE_API}") +string(REGEX MATCH "[0-9]+" MVK_VERSION_MINOR "${MVK_VERSION_MINOR_LINE}") +string(REGEX MATCH "#define MVK_VERSION_PATCH [0-9]+" MVK_VERSION_PATCH_LINE "${MVK_PRIVATE_API}") +string(REGEX MATCH "[0-9]+" MVK_VERSION_PATCH "${MVK_VERSION_PATCH_LINE}") +set(MVK_VERSION "${MVK_VERSION_MAJOR}.${MVK_VERSION_MINOR}.${MVK_VERSION_PATCH}") +message(STATUS "MoltenVK version: ${MVK_VERSION}") # Find required system libraries find_library(APPKIT_LIBRARY AppKit REQUIRED) diff --git a/externals/MoltenVK/MoltenVK_icd.json b/externals/MoltenVK/MoltenVK_icd.json new file mode 100644 index 000000000..2c3319263 --- /dev/null +++ b/externals/MoltenVK/MoltenVK_icd.json @@ -0,0 +1,8 @@ +{ + "file_format_version": "1.0.0", + "ICD": { + "library_path": "../../../Frameworks/libMoltenVK.dylib", + "api_version": "1.2.0", + "is_portability_driver": true + } +} diff --git a/src/video_core/renderer_vulkan/vk_common.h b/src/video_core/renderer_vulkan/vk_common.h index 9178aeb65..5fe199e0e 100644 --- a/src/video_core/renderer_vulkan/vk_common.h +++ b/src/video_core/renderer_vulkan/vk_common.h @@ -3,10 +3,6 @@ #pragma once -#if defined(__APPLE__) && !USE_SYSTEM_VULKAN_LOADER -#define VULKAN_HPP_ENABLE_DYNAMIC_LOADER_TOOL 0 -#endif - // Include vulkan-hpp header #define VK_ENABLE_BETA_EXTENSIONS #define VK_NO_PROTOTYPES diff --git a/src/video_core/renderer_vulkan/vk_platform.cpp b/src/video_core/renderer_vulkan/vk_platform.cpp index f5e513611..dbdabe0d9 100644 --- a/src/video_core/renderer_vulkan/vk_platform.cpp +++ b/src/video_core/renderer_vulkan/vk_platform.cpp @@ -14,6 +14,7 @@ #endif #include +#include #include "common/assert.h" #include "common/config.h" #include "common/logging/log.h" @@ -21,15 +22,6 @@ #include "sdl_window.h" #include "video_core/renderer_vulkan/vk_platform.h" -#if VULKAN_HPP_ENABLE_DYNAMIC_LOADER_TOOL -static vk::detail::DynamicLoader dl; -#else -extern "C" { -VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr(VkInstance instance, - const char* pName); -} -#endif - namespace Vulkan { static const char* const VALIDATION_LAYER_NAME = "VK_LAYER_KHRONOS_validation"; @@ -199,15 +191,57 @@ std::vector GetInstanceExtensions(Frontend::WindowSystemType window return extensions; } +std::vector GetInstanceLayers(bool enable_validation, bool enable_crash_diagnostic) { + const auto [properties_result, properties] = vk::enumerateInstanceLayerProperties(); + if (properties_result != vk::Result::eSuccess || properties.empty()) { + LOG_ERROR(Render_Vulkan, "Failed to query layer properties: {}", + vk::to_string(properties_result)); + return {}; + } + + std::vector layers; + layers.reserve(2); + + if (enable_validation) { + layers.push_back(VALIDATION_LAYER_NAME); + } + if (enable_crash_diagnostic) { + layers.push_back(CRASH_DIAGNOSTIC_LAYER_NAME); + } + + // Sanitize layer list + std::erase_if(layers, [&](const char* layer) -> bool { + const auto it = std::ranges::find_if(properties, [layer](const auto& prop) { + return std::strcmp(layer, prop.layerName) == 0; + }); + if (it == properties.end()) { + LOG_ERROR(Render_Vulkan, "Requested layer {} is not available", layer); + return true; + } + return false; + }); + + return layers; +} + vk::UniqueInstance CreateInstance(Frontend::WindowSystemType window_type, bool enable_validation, bool enable_crash_diagnostic) { LOG_INFO(Render_Vulkan, "Creating vulkan instance"); -#if VULKAN_HPP_ENABLE_DYNAMIC_LOADER_TOOL - auto vkGetInstanceProcAddr = - dl.getProcAddress("vkGetInstanceProcAddr"); +#ifdef __APPLE__ + // If the Vulkan loader exists in /usr/local/lib, give it priority. The Vulkan SDK + // installs it here by default but it is not in the default library search path. + // The loader has a clause to check for it, but at a lower priority than the bundled + // libMoltenVK.dylib, so we need to handle it ourselves to give it priority. + static const std::string usr_local_path = "/usr/local/lib/libvulkan.dylib"; + static vk::detail::DynamicLoader dl = std::filesystem::exists(usr_local_path) + ? vk::detail::DynamicLoader(usr_local_path) + : vk::detail::DynamicLoader(); +#else + static vk::detail::DynamicLoader dl; #endif - VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr); + VULKAN_HPP_DEFAULT_DISPATCHER.init( + dl.getProcAddress("vkGetInstanceProcAddr")); const auto [available_version_result, available_version] = VULKAN_HPP_DEFAULT_DISPATCHER.vkEnumerateInstanceVersion @@ -230,38 +264,25 @@ vk::UniqueInstance CreateInstance(Frontend::WindowSystemType window_type, bool e .apiVersion = available_version, }; - u32 num_layers = 0; - std::array layers; + const auto layers = GetInstanceLayers(enable_validation, enable_crash_diagnostic); - vk::Bool32 enable_force_barriers = vk::False; - const char* log_path{}; + const std::string extensions_string = fmt::format("{}", fmt::join(extensions, ", ")); + const std::string layers_string = fmt::format("{}", fmt::join(layers, ", ")); + LOG_INFO(Render_Vulkan, "Enabled instance extensions: {}", extensions_string); + LOG_INFO(Render_Vulkan, "Enabled instance layers: {}", layers_string); -#if VULKAN_HPP_ENABLE_DYNAMIC_LOADER_TOOL - if (enable_validation) { - layers[num_layers++] = VALIDATION_LAYER_NAME; - } + // Validation settings + vk::Bool32 enable_sync = Config::vkValidationSyncEnabled() ? vk::True : vk::False; + vk::Bool32 enable_gpuav = Config::vkValidationSyncEnabled() ? vk::True : vk::False; + const char* gpuav_mode = + Config::vkValidationGpuEnabled() ? "GPU_BASED_GPU_ASSISTED" : "GPU_BASED_NONE"; - if (enable_crash_diagnostic) { - layers[num_layers++] = CRASH_DIAGNOSTIC_LAYER_NAME; - static const auto crash_diagnostic_path = - Common::FS::GetUserPathString(Common::FS::PathType::LogDir); - log_path = crash_diagnostic_path.c_str(); - enable_force_barriers = vk::True; - } -#else - if (enable_validation || enable_crash_diagnostic) { - LOG_WARNING(Render_Vulkan, - "Skipping loading Vulkan layers as dynamic loading is not enabled."); - } -#endif + // Crash diagnostics settings + static const auto crash_diagnostic_path = + Common::FS::GetUserPathString(Common::FS::PathType::LogDir); + const char* log_path = crash_diagnostic_path.c_str(); + vk::Bool32 enable_force_barriers = vk::True; - vk::Bool32 enable_sync = - enable_validation && Config::vkValidationSyncEnabled() ? vk::True : vk::False; - vk::Bool32 enable_gpuav = - enable_validation && Config::vkValidationSyncEnabled() ? vk::True : vk::False; - const char* gpuav_mode = enable_validation && Config::vkValidationGpuEnabled() - ? "GPU_BASED_GPU_ASSISTED" - : "GPU_BASED_NONE"; const std::array layer_setings = { vk::LayerSettingEXT{ .pLayerName = VALIDATION_LAYER_NAME, @@ -331,7 +352,7 @@ vk::UniqueInstance CreateInstance(Frontend::WindowSystemType window_type, bool e vk::StructureChain instance_ci_chain = { vk::InstanceCreateInfo{ .pApplicationInfo = &application_info, - .enabledLayerCount = num_layers, + .enabledLayerCount = static_cast(layers.size()), .ppEnabledLayerNames = layers.data(), .enabledExtensionCount = static_cast(extensions.size()), .ppEnabledExtensionNames = extensions.data(), From ccfb1bbfa8ab7e536d684fd2c80b90fcfe66b5f6 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Tue, 17 Dec 2024 21:56:08 -0800 Subject: [PATCH 204/549] vk_instance: Add additional fallback for missing D16UnormS8Uint. (#1810) --- .../renderer_vulkan/vk_instance.cpp | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index b479c1464..790e76400 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -583,23 +583,20 @@ bool Instance::IsFormatSupported(const vk::Format format, return (GetFormatFeatureFlags(format) & flags) == flags; } -static vk::Format GetAlternativeFormat(const vk::Format format) { - switch (format) { - case vk::Format::eD16UnormS8Uint: - return vk::Format::eD24UnormS8Uint; - default: - return format; - } -} - vk::Format Instance::GetSupportedFormat(const vk::Format format, const vk::FormatFeatureFlags2 flags) const { - if (IsFormatSupported(format, flags)) [[likely]] { - return format; - } - const vk::Format alternative = GetAlternativeFormat(format); - if (IsFormatSupported(alternative, flags)) [[likely]] { - return alternative; + if (!IsFormatSupported(format, flags)) [[unlikely]] { + switch (format) { + case vk::Format::eD16UnormS8Uint: + if (IsFormatSupported(vk::Format::eD24UnormS8Uint, flags)) { + return vk::Format::eD24UnormS8Uint; + } + if (IsFormatSupported(vk::Format::eD32SfloatS8Uint, flags)) { + return vk::Format::eD32SfloatS8Uint; + } + default: + break; + } } return format; } From be4c38bf1c4ebd6bf669176cc5123cbf9103bd01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?= Date: Wed, 18 Dec 2024 20:48:00 +0100 Subject: [PATCH 205/549] Handle 32bit int ImageFormat (#1823) --- src/shader_recompiler/backend/spirv/spirv_emit_context.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 5d2ec6f96..255a3e2b2 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -696,6 +696,10 @@ spv::ImageFormat GetFormat(const AmdGpu::Image& image) { image.GetNumberFmt() == AmdGpu::NumberFormat::Uint) { return spv::ImageFormat::R32ui; } + if (image.GetDataFmt() == AmdGpu::DataFormat::Format32 && + image.GetNumberFmt() == AmdGpu::NumberFormat::Sint) { + return spv::ImageFormat::R32i; + } if (image.GetDataFmt() == AmdGpu::DataFormat::Format32 && image.GetNumberFmt() == AmdGpu::NumberFormat::Float) { return spv::ImageFormat::R32f; From b1b4c8c48777a380e26da844e7a71cf3a94e4ce5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?= Date: Wed, 18 Dec 2024 20:57:58 +0100 Subject: [PATCH 206/549] Handle setting Vcc in Translator::SetDst64 (#1826) --- src/shader_recompiler/frontend/translate/translate.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/shader_recompiler/frontend/translate/translate.cpp b/src/shader_recompiler/frontend/translate/translate.cpp index 3031e6643..a14bff706 100644 --- a/src/shader_recompiler/frontend/translate/translate.cpp +++ b/src/shader_recompiler/frontend/translate/translate.cpp @@ -439,7 +439,8 @@ void Translator::SetDst64(const InstOperand& operand, const IR::U64F64& value_ra ir.SetVectorReg(IR::VectorReg(operand.code + 1), hi); return ir.SetVectorReg(IR::VectorReg(operand.code), lo); case OperandField::VccLo: - UNREACHABLE(); + ir.SetVccLo(lo); + return ir.SetVccHi(hi); case OperandField::VccHi: UNREACHABLE(); case OperandField::M0: From 32435674f24708b5d0533c3960b9447870ea35f4 Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Wed, 18 Dec 2024 14:05:35 -0600 Subject: [PATCH 207/549] Misc UE4 fixes (#1821) * Add ExecLo case to S_SAVEEXEC_B64 Seen in CUSA38209 * S_BCNT1_I32_B32 Turtle said our implementation of S_BCNT1_I32_B64 was meant to be for S_BCNT1_I32_B32, so renaming the opcode is the fix. --- src/shader_recompiler/frontend/translate/scalar_alu.cpp | 8 +++++--- src/shader_recompiler/frontend/translate/translate.h | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/shader_recompiler/frontend/translate/scalar_alu.cpp b/src/shader_recompiler/frontend/translate/scalar_alu.cpp index e731e299a..f96fd0f40 100644 --- a/src/shader_recompiler/frontend/translate/scalar_alu.cpp +++ b/src/shader_recompiler/frontend/translate/scalar_alu.cpp @@ -98,8 +98,8 @@ void Translator::EmitScalarAlu(const GcnInst& inst) { break; case Opcode::S_BREV_B32: return S_BREV_B32(inst); - case Opcode::S_BCNT1_I32_B64: - return S_BCNT1_I32_B64(inst); + case Opcode::S_BCNT1_I32_B32: + return S_BCNT1_I32_B32(inst); case Opcode::S_FF1_I32_B32: return S_FF1_I32_B32(inst); case Opcode::S_AND_SAVEEXEC_B64: @@ -579,7 +579,7 @@ void Translator::S_BREV_B32(const GcnInst& inst) { SetDst(inst.dst[0], ir.BitReverse(GetSrc(inst.src[0]))); } -void Translator::S_BCNT1_I32_B64(const GcnInst& inst) { +void Translator::S_BCNT1_I32_B32(const GcnInst& inst) { const IR::U32 result = ir.BitCount(GetSrc(inst.src[0])); SetDst(inst.dst[0], result); ir.SetScc(ir.INotEqual(result, ir.Imm32(0))); @@ -602,6 +602,8 @@ void Translator::S_SAVEEXEC_B64(NegateMode negate, bool is_or, const GcnInst& in return ir.GetVcc(); case OperandField::ScalarGPR: return ir.GetThreadBitScalarReg(IR::ScalarReg(inst.src[0].code)); + case OperandField::ExecLo: + return ir.GetExec(); default: UNREACHABLE(); } diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index 8e575fcad..218b66d74 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -110,7 +110,7 @@ public: void S_MOV_B64(const GcnInst& inst); void S_NOT_B64(const GcnInst& inst); void S_BREV_B32(const GcnInst& inst); - void S_BCNT1_I32_B64(const GcnInst& inst); + void S_BCNT1_I32_B32(const GcnInst& inst); void S_FF1_I32_B32(const GcnInst& inst); void S_GETPC_B64(u32 pc, const GcnInst& inst); void S_SAVEEXEC_B64(NegateMode negate, bool is_or, const GcnInst& inst); From 1e0809903680dd401641ce24d964b6e75d629482 Mon Sep 17 00:00:00 2001 From: Mahmoud Adel <94652220+AboMedoz@users.noreply.github.com> Date: Wed, 18 Dec 2024 22:06:30 +0200 Subject: [PATCH 208/549] add R8Uint in image Detiling (#1812) used by InFamous, and maybe other games --- src/video_core/texture_cache/tile_manager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/video_core/texture_cache/tile_manager.cpp b/src/video_core/texture_cache/tile_manager.cpp index a5e09e45d..94d37c993 100644 --- a/src/video_core/texture_cache/tile_manager.cpp +++ b/src/video_core/texture_cache/tile_manager.cpp @@ -172,6 +172,7 @@ void ConvertTileToLinear(u8* dst, const u8* src, u32 width, u32 height, bool is_ vk::Format DemoteImageFormatForDetiling(vk::Format format) { switch (format) { + case vk::Format::eR8Uint: case vk::Format::eR8Unorm: return vk::Format::eR8Uint; case vk::Format::eR4G4B4A4UnormPack16: From adf4b635f743ed2bc1d4d8d18ebacdd45649f7b4 Mon Sep 17 00:00:00 2001 From: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> Date: Wed, 18 Dec 2024 22:11:09 +0200 Subject: [PATCH 209/549] hot-fix: Proper abi on init_routine --- src/core/libraries/kernel/threads/pthread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/libraries/kernel/threads/pthread.cpp b/src/core/libraries/kernel/threads/pthread.cpp index 08886c6eb..372f05bff 100644 --- a/src/core/libraries/kernel/threads/pthread.cpp +++ b/src/core/libraries/kernel/threads/pthread.cpp @@ -327,7 +327,7 @@ void PS4_SYSV_ABI sched_yield() { std::this_thread::yield(); } -int PS4_SYSV_ABI posix_pthread_once(PthreadOnce* once_control, void (*init_routine)()) { +int PS4_SYSV_ABI posix_pthread_once(PthreadOnce* once_control, void PS4_SYSV_ABI (*init_routine)()) { for (;;) { auto state = once_control->state.load(); if (state == PthreadOnceState::Done) { From 188eebb92a9258ace01fa78b28c5a61e951acea1 Mon Sep 17 00:00:00 2001 From: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> Date: Thu, 19 Dec 2024 10:18:28 +0200 Subject: [PATCH 210/549] ir: Add heuristic based LDS barrier pass (#1801) * ir: Add heuristic based LDS barrier pass * Attempts to insert barriers after zero-depth divergant conditional blocks in shaders that use shared memory * lds_barriers: Limit to nvidia * Intel has historically had problems with cs barriers, will debug other time --- CMakeLists.txt | 1 + src/shader_recompiler/ir/passes/ir_passes.h | 5 ++ .../ir/passes/shared_memory_barrier_pass.cpp | 46 +++++++++++++++++++ src/shader_recompiler/profile.h | 1 + src/shader_recompiler/recompiler.cpp | 1 + .../renderer_vulkan/vk_pipeline_cache.cpp | 1 + 6 files changed, 55 insertions(+) create mode 100644 src/shader_recompiler/ir/passes/shared_memory_barrier_pass.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f0397e86..cc6fb6b93 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -671,6 +671,7 @@ set(SHADER_RECOMPILER src/shader_recompiler/exception.h src/shader_recompiler/ir/passes/resource_tracking_pass.cpp src/shader_recompiler/ir/passes/ring_access_elimination.cpp src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp + src/shader_recompiler/ir/passes/shared_memory_barrier_pass.cpp src/shader_recompiler/ir/passes/ssa_rewrite_pass.cpp src/shader_recompiler/ir/abstract_syntax_list.h src/shader_recompiler/ir/attribute.cpp diff --git a/src/shader_recompiler/ir/passes/ir_passes.h b/src/shader_recompiler/ir/passes/ir_passes.h index 61f43e7e4..8a71d9e1f 100644 --- a/src/shader_recompiler/ir/passes/ir_passes.h +++ b/src/shader_recompiler/ir/passes/ir_passes.h @@ -6,6 +6,10 @@ #include "shader_recompiler/ir/basic_block.h" #include "shader_recompiler/ir/program.h" +namespace Shader { +struct Profile; +} + namespace Shader::Optimization { void SsaRewritePass(IR::BlockList& program); @@ -21,5 +25,6 @@ void RingAccessElimination(const IR::Program& program, const RuntimeInfo& runtim void TessellationPreprocess(IR::Program& program, RuntimeInfo& runtime_info); void HullShaderTransform(IR::Program& program, RuntimeInfo& runtime_info); void DomainShaderTransform(IR::Program& program, RuntimeInfo& runtime_info); +void SharedMemoryBarrierPass(IR::Program& program, const Profile& profile); } // namespace Shader::Optimization diff --git a/src/shader_recompiler/ir/passes/shared_memory_barrier_pass.cpp b/src/shader_recompiler/ir/passes/shared_memory_barrier_pass.cpp new file mode 100644 index 000000000..cae001e96 --- /dev/null +++ b/src/shader_recompiler/ir/passes/shared_memory_barrier_pass.cpp @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "shader_recompiler/ir/breadth_first_search.h" +#include "shader_recompiler/ir/ir_emitter.h" +#include "shader_recompiler/ir/program.h" +#include "shader_recompiler/profile.h" + +namespace Shader::Optimization { + +void SharedMemoryBarrierPass(IR::Program& program, const Profile& profile) { + if (!program.info.uses_shared || !profile.needs_lds_barriers) { + return; + } + using Type = IR::AbstractSyntaxNode::Type; + u32 branch_depth{}; + for (const IR::AbstractSyntaxNode& node : program.syntax_list) { + if (node.type == Type::EndIf) { + --branch_depth; + continue; + } + if (node.type != Type::If) { + continue; + } + u32 curr_depth = branch_depth++; + if (curr_depth != 0) { + continue; + } + const IR::U1 cond = node.data.if_node.cond; + const auto insert_barrier = IR::BreadthFirstSearch(cond, [](IR::Inst* inst) -> std::optional { + if (inst->GetOpcode() == IR::Opcode::GetAttributeU32 && + inst->Arg(0).Attribute() == IR::Attribute::LocalInvocationId) { + return true; + } + return std::nullopt; + }); + if (insert_barrier) { + IR::Block* const merge = node.data.if_node.merge; + auto insert_point = std::ranges::find_if_not(merge->Instructions(), IR::IsPhi); + IR::IREmitter ir{*merge, insert_point}; + ir.Barrier(); + } + } +} + +} // namespace Shader::Optimization diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index c00e37f9c..fc8c5956e 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h @@ -27,6 +27,7 @@ struct Profile { bool has_broken_spirv_clamp{}; bool lower_left_origin_mode{}; bool needs_manual_interpolation{}; + bool needs_lds_barriers{}; u64 min_ssbo_alignment{}; }; diff --git a/src/shader_recompiler/recompiler.cpp b/src/shader_recompiler/recompiler.cpp index ad57adb6a..bb027a11e 100644 --- a/src/shader_recompiler/recompiler.cpp +++ b/src/shader_recompiler/recompiler.cpp @@ -91,6 +91,7 @@ IR::Program TranslateProgram(std::span code, Pools& pools, Info& info Shader::Optimization::IdentityRemovalPass(program.blocks); Shader::Optimization::DeadCodeEliminationPass(program); Shader::Optimization::CollectShaderInfoPass(program); + Shader::Optimization::SharedMemoryBarrierPass(program, profile); return program; } diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 50396287b..4b88bd374 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -204,6 +204,7 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_, .supports_image_load_store_lod = instance_.IsImageLoadStoreLodSupported(), .needs_manual_interpolation = instance.IsFragmentShaderBarycentricSupported() && instance.GetDriverID() == vk::DriverId::eNvidiaProprietary, + .needs_lds_barriers = instance.GetDriverID() == vk::DriverId::eNvidiaProprietary, }; auto [cache_result, cache] = instance.GetDevice().createPipelineCacheUnique({}); ASSERT_MSG(cache_result == vk::Result::eSuccess, "Failed to create pipeline cache: {}", From b0b74243af97bc71a5681c279f738608dc5c6ebe Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Thu, 19 Dec 2024 10:25:03 +0200 Subject: [PATCH 211/549] clang-fix --- src/core/libraries/kernel/threads/pthread.cpp | 3 ++- .../ir/passes/shared_memory_barrier_pass.cpp | 15 ++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/core/libraries/kernel/threads/pthread.cpp b/src/core/libraries/kernel/threads/pthread.cpp index 372f05bff..610e61238 100644 --- a/src/core/libraries/kernel/threads/pthread.cpp +++ b/src/core/libraries/kernel/threads/pthread.cpp @@ -327,7 +327,8 @@ void PS4_SYSV_ABI sched_yield() { std::this_thread::yield(); } -int PS4_SYSV_ABI posix_pthread_once(PthreadOnce* once_control, void PS4_SYSV_ABI (*init_routine)()) { +int PS4_SYSV_ABI posix_pthread_once(PthreadOnce* once_control, + void PS4_SYSV_ABI (*init_routine)()) { for (;;) { auto state = once_control->state.load(); if (state == PthreadOnceState::Done) { diff --git a/src/shader_recompiler/ir/passes/shared_memory_barrier_pass.cpp b/src/shader_recompiler/ir/passes/shared_memory_barrier_pass.cpp index cae001e96..aad8fb148 100644 --- a/src/shader_recompiler/ir/passes/shared_memory_barrier_pass.cpp +++ b/src/shader_recompiler/ir/passes/shared_memory_barrier_pass.cpp @@ -27,13 +27,14 @@ void SharedMemoryBarrierPass(IR::Program& program, const Profile& profile) { continue; } const IR::U1 cond = node.data.if_node.cond; - const auto insert_barrier = IR::BreadthFirstSearch(cond, [](IR::Inst* inst) -> std::optional { - if (inst->GetOpcode() == IR::Opcode::GetAttributeU32 && - inst->Arg(0).Attribute() == IR::Attribute::LocalInvocationId) { - return true; - } - return std::nullopt; - }); + const auto insert_barrier = + IR::BreadthFirstSearch(cond, [](IR::Inst* inst) -> std::optional { + if (inst->GetOpcode() == IR::Opcode::GetAttributeU32 && + inst->Arg(0).Attribute() == IR::Attribute::LocalInvocationId) { + return true; + } + return std::nullopt; + }); if (insert_barrier) { IR::Block* const merge = node.data.if_node.merge; auto insert_point = std::ranges::find_if_not(merge->Instructions(), IR::IsPhi); From f2a989b9da5bbcc0b27e64fd37fdde95a9620a53 Mon Sep 17 00:00:00 2001 From: f8ith Date: Thu, 19 Dec 2024 16:58:11 +0800 Subject: [PATCH 212/549] wip: added status column for compat data (#1668) * wip: added basic gui for compat data * data is currently pulled directly from github API, awaiting server infra * removed unused initalizer * fixes * fix cmake * wip: add some testing date / version * add tooltip * fix nested QJsonObject * fix tooltip color * fix clang-format * Edit style * Add clickable status * formatting * import order * typo * fix clang format 2 --------- Co-authored-by: georgemoralis --- CMakeLists.txt | 2 + src/qt_gui/compatibility_info.cpp | 227 ++++++++++++++++++++++++++++++ src/qt_gui/compatibility_info.h | 97 +++++++++++++ src/qt_gui/game_info.cpp | 2 + src/qt_gui/game_list_frame.cpp | 143 ++++++++++++++++--- src/qt_gui/game_list_frame.h | 39 +++-- src/qt_gui/game_list_utils.h | 7 + src/qt_gui/main_window.cpp | 4 +- src/qt_gui/main_window.h | 3 + 9 files changed, 488 insertions(+), 36 deletions(-) create mode 100644 src/qt_gui/compatibility_info.cpp create mode 100644 src/qt_gui/compatibility_info.h diff --git a/CMakeLists.txt b/CMakeLists.txt index cc6fb6b93..172733840 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -811,6 +811,8 @@ set(QT_GUI src/qt_gui/about_dialog.cpp src/qt_gui/background_music_player.h src/qt_gui/cheats_patches.cpp src/qt_gui/cheats_patches.h + src/qt_gui/compatibility_info.cpp + src/qt_gui/compatibility_info.h src/qt_gui/main_window_ui.h src/qt_gui/main_window.cpp src/qt_gui/main_window.h diff --git a/src/qt_gui/compatibility_info.cpp b/src/qt_gui/compatibility_info.cpp new file mode 100644 index 000000000..c8d6bf36d --- /dev/null +++ b/src/qt_gui/compatibility_info.cpp @@ -0,0 +1,227 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include + +#include "common/path_util.h" +#include "compatibility_info.h" + +CompatibilityInfoClass::CompatibilityInfoClass() + : m_network_manager(new QNetworkAccessManager(this)) { + QStringList file_paths; + std::filesystem::path compatibility_file_path = + Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / "compatibility_data.json"; + Common::FS::PathToQString(m_compatibility_filename, compatibility_file_path); +}; +CompatibilityInfoClass::~CompatibilityInfoClass() = default; + +void CompatibilityInfoClass::UpdateCompatibilityDatabase(QWidget* parent) { + if (LoadCompatibilityFile()) + return; + + QNetworkReply* reply = FetchPage(1); + WaitForReply(reply); + + QProgressDialog dialog(tr("Fetching compatibility data, please wait"), tr("Cancel"), 0, 0, + parent); + dialog.setWindowTitle(tr("Loading...")); + + int remaining_pages = 0; + if (reply->hasRawHeader("link")) { + QRegularExpression last_page_re("(\\d+)(?=>; rel=\"last\")"); + QRegularExpressionMatch last_page_match = + last_page_re.match(QString(reply->rawHeader("link"))); + if (last_page_match.hasMatch()) { + remaining_pages = last_page_match.captured(0).toInt() - 1; + } + } + + if (reply->error() != QNetworkReply::NoError) { + reply->deleteLater(); + QMessageBox::critical(parent, tr("Error"), + tr("Unable to update compatibility data! Try again later.")); + // Try loading compatibility_file.json again + LoadCompatibilityFile(); + return; + } + + ExtractCompatibilityInfo(reply->readAll()); + + QVector replies(remaining_pages); + QFutureWatcher future_watcher; + + for (int i = 0; i < remaining_pages; i++) { + replies[i] = FetchPage(i + 2); + } + + future_watcher.setFuture(QtConcurrent::map(replies, WaitForReply)); + connect(&future_watcher, &QFutureWatcher::finished, [&]() { + for (int i = 0; i < remaining_pages; i++) { + if (replies[i]->error() == QNetworkReply::NoError) { + ExtractCompatibilityInfo(replies[i]->readAll()); + } + replies[i]->deleteLater(); + } + + QFile compatibility_file(m_compatibility_filename); + + if (!compatibility_file.open(QIODevice::WriteOnly | QIODevice::Truncate | + QIODevice::Text)) { + QMessageBox::critical(parent, tr("Error"), + tr("Unable to open compatibility.json for writing.")); + return; + } + + QJsonDocument json_doc; + m_compatibility_database["version"] = COMPAT_DB_VERSION; + + json_doc.setObject(m_compatibility_database); + compatibility_file.write(json_doc.toJson()); + compatibility_file.close(); + + dialog.reset(); + }); + connect(&dialog, &QProgressDialog::canceled, &future_watcher, &QFutureWatcher::cancel); + dialog.setRange(0, remaining_pages); + connect(&future_watcher, &QFutureWatcher::progressValueChanged, &dialog, + &QProgressDialog::setValue); + dialog.exec(); +} + +QNetworkReply* CompatibilityInfoClass::FetchPage(int page_num) { + QUrl url = QUrl("https://api.github.com/repos/shadps4-emu/shadps4-game-compatibility/issues"); + QUrlQuery query; + query.addQueryItem("per_page", QString("100")); + query.addQueryItem( + "tags", QString("status-ingame status-playable status-nothing status-boots status-menus")); + query.addQueryItem("page", QString::number(page_num)); + url.setQuery(query); + + QNetworkRequest request(url); + QNetworkReply* reply = m_network_manager->get(request); + + return reply; +} + +void CompatibilityInfoClass::WaitForReply(QNetworkReply* reply) { + QEventLoop loop; + connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); + loop.exec(); + return; +}; + +CompatibilityEntry CompatibilityInfoClass::GetCompatibilityInfo(const std::string& serial) { + QString title_id = QString::fromStdString(serial); + if (m_compatibility_database.contains(title_id)) { + { + for (int os_int = 0; os_int != static_cast(OSType::Last); os_int++) { + QString os_string = OSTypeToString.at(static_cast(os_int)); + QJsonObject compatibility_obj = m_compatibility_database[title_id].toObject(); + if (compatibility_obj.contains(os_string)) { + QJsonObject compatibility_entry_obj = compatibility_obj[os_string].toObject(); + CompatibilityEntry compatibility_entry{ + LabelToCompatStatus.at(compatibility_entry_obj["status"].toString()), + compatibility_entry_obj["version"].toString(), + QDateTime::fromString(compatibility_entry_obj["last_tested"].toString(), + Qt::ISODate), + compatibility_entry_obj["url"].toString(), + compatibility_entry_obj["issue_number"].toInt()}; + return compatibility_entry; + } + } + } + } + return CompatibilityEntry{CompatibilityStatus::Unknown}; +} + +bool CompatibilityInfoClass::LoadCompatibilityFile() { + // Returns true if compatibility is loaded succescfully + QFileInfo check_file(m_compatibility_filename); + const auto modified_delta = QDateTime::currentDateTime() - check_file.lastModified(); + if (!check_file.exists() || !check_file.isFile() || + std::chrono::duration_cast(modified_delta).count() > 60) { + return false; + } + + QFile compatibility_file(m_compatibility_filename); + if (!compatibility_file.open(QIODevice::ReadOnly)) { + compatibility_file.close(); + return false; + } + QByteArray json_data = compatibility_file.readAll(); + compatibility_file.close(); + + QJsonDocument json_doc = QJsonDocument::fromJson(json_data); + if (json_doc.isEmpty() || json_doc.isNull()) { + return false; + } + + // Check database version + int version_number; + if (json_doc.object()["version"].isDouble()) { + if (json_doc.object()["version"].toInt() < COMPAT_DB_VERSION) + return false; + } else + return false; + + m_compatibility_database = json_doc.object(); + return true; +} + +void CompatibilityInfoClass::ExtractCompatibilityInfo(QByteArray response) { + QJsonDocument json_doc(QJsonDocument::fromJson(response)); + + if (json_doc.isNull()) { + return; + } + + QJsonArray json_arr; + + json_arr = json_doc.array(); + + for (const auto& issue_ref : std::as_const(json_arr)) { + QJsonObject issue_obj = issue_ref.toObject(); + QString title_id; + QRegularExpression title_id_regex("CUSA[0-9]{5}"); + QRegularExpressionMatch title_id_match = + title_id_regex.match(issue_obj["title"].toString()); + QString current_os = "os-unknown"; + QString compatibility_status = "status-unknown"; + if (issue_obj.contains("labels") && title_id_match.hasMatch()) { + title_id = title_id_match.captured(0); + const QJsonArray& label_array = issue_obj["labels"].toArray(); + for (const auto& elem : label_array) { + QString label = elem.toObject()["name"].toString(); + if (LabelToOSType.contains(label)) { + current_os = label; + continue; + } + if (LabelToCompatStatus.contains(label)) { + compatibility_status = label; + continue; + } + } + + // QJson does not support editing nested objects directly.. + + QJsonObject compatibility_obj = m_compatibility_database[title_id].toObject(); + + QJsonObject compatibility_data{ + {{"status", compatibility_status}, + {"last_tested", issue_obj["updated_at"]}, + {"version", issue_obj["milestone"].isNull() + ? "unknown" + : issue_obj["milestone"].toObject()["title"].toString()}, + {"url", issue_obj["html_url"]}, + {"issue_number", issue_obj["number"]}}}; + + compatibility_obj[current_os] = compatibility_data; + + m_compatibility_database[title_id] = compatibility_obj; + } + } + + return; +} diff --git a/src/qt_gui/compatibility_info.h b/src/qt_gui/compatibility_info.h new file mode 100644 index 000000000..2b970670a --- /dev/null +++ b/src/qt_gui/compatibility_info.h @@ -0,0 +1,97 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include + +#include "common/config.h" +#include "core/file_format/psf.h" + +static constexpr int COMPAT_DB_VERSION = 1; + +enum class CompatibilityStatus { + Unknown, + Nothing, + Boots, + Menus, + Ingame, + Playable, +}; + +// Prioritize different compatibility reports based on user's platform +enum class OSType { +#ifdef Q_OS_WIN + Win32 = 0, + Unknown, + Linux, + macOS, +#elif defined(Q_OS_LINUX) + Linux = 0, + Unknown, + Win32, + macOS, +#elif defined(Q_OS_MAC) + macOS = 0, + Unknown, + Linux, + Win32, +#endif + // Fake enum to allow for iteration + Last +}; + +struct CompatibilityEntry { + CompatibilityStatus status; + QString version; + QDateTime last_tested; + QString url; + int issue_number; +}; + +class CompatibilityInfoClass : public QObject { + Q_OBJECT +public: + // Please think of a better alternative + inline static const std::unordered_map LabelToCompatStatus = { + {QStringLiteral("status-nothing"), CompatibilityStatus::Nothing}, + {QStringLiteral("status-boots"), CompatibilityStatus::Boots}, + {QStringLiteral("status-menus"), CompatibilityStatus::Menus}, + {QStringLiteral("status-ingame"), CompatibilityStatus::Ingame}, + {QStringLiteral("status-playable"), CompatibilityStatus::Playable}}; + inline static const std::unordered_map LabelToOSType = { + {QStringLiteral("os-linux"), OSType::Linux}, + {QStringLiteral("os-macOS"), OSType::macOS}, + {QStringLiteral("os-windows"), OSType::Win32}, + }; + + inline static const std::unordered_map CompatStatusToString = { + {CompatibilityStatus::Unknown, QStringLiteral("Unknown")}, + {CompatibilityStatus::Nothing, QStringLiteral("Nothing")}, + {CompatibilityStatus::Boots, QStringLiteral("Boots")}, + {CompatibilityStatus::Menus, QStringLiteral("Menus")}, + {CompatibilityStatus::Ingame, QStringLiteral("Ingame")}, + {CompatibilityStatus::Playable, QStringLiteral("Playable")}}; + inline static const std::unordered_map OSTypeToString = { + {OSType::Linux, QStringLiteral("os-linux")}, + {OSType::macOS, QStringLiteral("os-macOS")}, + {OSType::Win32, QStringLiteral("os-windows")}, + {OSType::Unknown, QStringLiteral("os-unknown")}}; + + CompatibilityInfoClass(); + ~CompatibilityInfoClass(); + void UpdateCompatibilityDatabase(QWidget* parent = nullptr); + bool LoadCompatibilityFile(); + CompatibilityEntry GetCompatibilityInfo(const std::string& serial); + void ExtractCompatibilityInfo(QByteArray response); + static void WaitForReply(QNetworkReply* reply); + QNetworkReply* FetchPage(int page_num); + +private: + QNetworkAccessManager* m_network_manager; + QString m_compatibility_filename; + QJsonObject m_compatibility_database; +}; \ No newline at end of file diff --git a/src/qt_gui/game_info.cpp b/src/qt_gui/game_info.cpp index 48643f8ed..e4750fa1d 100644 --- a/src/qt_gui/game_info.cpp +++ b/src/qt_gui/game_info.cpp @@ -4,6 +4,7 @@ #include #include "common/path_util.h" +#include "compatibility_info.h" #include "game_info.h" GameInfoClass::GameInfoClass() = default; @@ -22,6 +23,7 @@ void GameInfoClass::GetGameInfo(QWidget* parent) { } } } + m_games = QtConcurrent::mapped(filePaths, [&](const QString& path) { return readGameInfo(Common::FS::PathFromQString(path)); }).results(); diff --git a/src/qt_gui/game_list_frame.cpp b/src/qt_gui/game_list_frame.cpp index 63f6b63b8..d43c35ef4 100644 --- a/src/qt_gui/game_list_frame.cpp +++ b/src/qt_gui/game_list_frame.cpp @@ -1,12 +1,17 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include "common/logging/log.h" #include "common/path_util.h" #include "common/string_util.h" #include "game_list_frame.h" +#include "game_list_utils.h" -GameListFrame::GameListFrame(std::shared_ptr game_info_get, QWidget* parent) - : QTableWidget(parent), m_game_info(game_info_get) { +GameListFrame::GameListFrame(std::shared_ptr game_info_get, + std::shared_ptr compat_info_get, + QWidget* parent) + : QTableWidget(parent), m_game_info(game_info_get), m_compat_info(compat_info_get) { icon_size = Config::getIconSize(); this->setShowGrid(false); this->setEditTriggers(QAbstractItemView::NoEditTriggers); @@ -17,29 +22,30 @@ GameListFrame::GameListFrame(std::shared_ptr game_info_get, QWidg this->verticalScrollBar()->installEventFilter(this); this->verticalScrollBar()->setSingleStep(20); this->horizontalScrollBar()->setSingleStep(20); - this->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed); + this->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); this->verticalHeader()->setVisible(false); this->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu); this->horizontalHeader()->setHighlightSections(false); this->horizontalHeader()->setSortIndicatorShown(true); this->horizontalHeader()->setStretchLastSection(true); this->setContextMenuPolicy(Qt::CustomContextMenu); - this->setColumnCount(9); + this->setColumnCount(10); this->setColumnWidth(1, 300); // Name - this->setColumnWidth(2, 120); // Serial - this->setColumnWidth(3, 90); // Region - this->setColumnWidth(4, 90); // Firmware - this->setColumnWidth(5, 90); // Size - this->setColumnWidth(6, 90); // Version - this->setColumnWidth(7, 120); // Play Time + this->setColumnWidth(2, 140); // Compatibility + this->setColumnWidth(3, 120); // Serial + this->setColumnWidth(4, 90); // Region + this->setColumnWidth(5, 90); // Firmware + this->setColumnWidth(6, 90); // Size + this->setColumnWidth(7, 90); // Version + this->setColumnWidth(8, 120); // Play Time QStringList headers; - headers << tr("Icon") << tr("Name") << tr("Serial") << tr("Region") << tr("Firmware") - << tr("Size") << tr("Version") << tr("Play Time") << tr("Path"); + headers << tr("Icon") << tr("Name") << tr("Compatibility") << tr("Serial") << tr("Region") + << tr("Firmware") << tr("Size") << tr("Version") << tr("Play Time") << tr("Path"); this->setHorizontalHeaderLabels(headers); this->horizontalHeader()->setSortIndicatorShown(true); this->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents); - this->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Fixed); this->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Fixed); + this->horizontalHeader()->setSectionResizeMode(4, QHeaderView::Fixed); PopulateGameList(); connect(this, &QTableWidget::currentCellChanged, this, &GameListFrame::onCurrentCellChanged); @@ -68,6 +74,12 @@ GameListFrame::GameListFrame(std::shared_ptr game_info_get, QWidg connect(this, &QTableWidget::customContextMenuRequested, this, [=, this](const QPoint& pos) { m_gui_context_menus.RequestGameMenu(pos, m_game_info->m_games, this, true); }); + + connect(this, &QTableWidget::cellClicked, this, [=, this](int row, int column) { + if (column == 2 && !m_game_info->m_games[row].compatibility.url.isEmpty()) { + QDesktopServices::openUrl(QUrl(m_game_info->m_games[row].compatibility.url)); + } + }); } void GameListFrame::onCurrentCellChanged(int currentRow, int currentColumn, int previousRow, @@ -96,16 +108,20 @@ void GameListFrame::PopulateGameList() { for (int i = 0; i < m_game_info->m_games.size(); i++) { SetTableItem(i, 1, QString::fromStdString(m_game_info->m_games[i].name)); - SetTableItem(i, 2, QString::fromStdString(m_game_info->m_games[i].serial)); - SetRegionFlag(i, 3, QString::fromStdString(m_game_info->m_games[i].region)); - SetTableItem(i, 4, QString::fromStdString(m_game_info->m_games[i].fw)); - SetTableItem(i, 5, QString::fromStdString(m_game_info->m_games[i].size)); - SetTableItem(i, 6, QString::fromStdString(m_game_info->m_games[i].version)); + SetTableItem(i, 3, QString::fromStdString(m_game_info->m_games[i].serial)); + SetRegionFlag(i, 4, QString::fromStdString(m_game_info->m_games[i].region)); + SetTableItem(i, 5, QString::fromStdString(m_game_info->m_games[i].fw)); + SetTableItem(i, 6, QString::fromStdString(m_game_info->m_games[i].size)); + SetTableItem(i, 7, QString::fromStdString(m_game_info->m_games[i].version)); + + m_game_info->m_games[i].compatibility = + m_compat_info->GetCompatibilityInfo(m_game_info->m_games[i].serial); + SetCompatibilityItem(i, 2, m_game_info->m_games[i].compatibility); QString playTime = GetPlayTime(m_game_info->m_games[i].serial); if (playTime.isEmpty()) { m_game_info->m_games[i].play_time = "0:00:00"; - SetTableItem(i, 7, tr("Never Played")); + SetTableItem(i, 8, tr("Never Played")); } else { QStringList timeParts = playTime.split(':'); int hours = timeParts[0].toInt(); @@ -123,15 +139,15 @@ void GameListFrame::PopulateGameList() { formattedPlayTime = formattedPlayTime.trimmed(); m_game_info->m_games[i].play_time = playTime.toStdString(); if (formattedPlayTime.isEmpty()) { - SetTableItem(i, 7, QString("%1s").arg(seconds)); + SetTableItem(i, 8, QString("%1s").arg(seconds)); } else { - SetTableItem(i, 7, formattedPlayTime); + SetTableItem(i, 8, formattedPlayTime); } } QString path; Common::FS::PathToQString(path, m_game_info->m_games[i].path); - SetTableItem(i, 8, path); + SetTableItem(i, 9, path); } } @@ -203,6 +219,89 @@ void GameListFrame::ResizeIcons(int iconSize) { this->horizontalHeader()->setSectionResizeMode(8, QHeaderView::ResizeToContents); } +void GameListFrame::SetCompatibilityItem(int row, int column, CompatibilityEntry entry) { + QTableWidgetItem* item = new QTableWidgetItem(); + QWidget* widget = new QWidget(this); + QGridLayout* layout = new QGridLayout(widget); + + widget->setStyleSheet("QToolTip {background-color: black; color: white;}"); + + QColor color; + QString status_explanation; + + switch (entry.status) { + case CompatibilityStatus::Unknown: + color = QStringLiteral("#000000"); + status_explanation = tr("Compatibility is untested"); + break; + case CompatibilityStatus::Nothing: + color = QStringLiteral("#212121"); + status_explanation = tr("Games does not initialize properly / crashes the emulator"); + break; + case CompatibilityStatus::Boots: + color = QStringLiteral("#828282"); + status_explanation = tr("Game boots, but only displays a blank screen"); + break; + case CompatibilityStatus::Menus: + color = QStringLiteral("#FF0000"); + status_explanation = tr("Game displays an image but does not go past the menu"); + break; + case CompatibilityStatus::Ingame: + color = QStringLiteral("#F2D624"); + status_explanation = tr("Game has game-breaking glitches or unplayable performance"); + break; + case CompatibilityStatus::Playable: + color = QStringLiteral("#47D35C"); + status_explanation = + tr("Game can be completed with playable performance and no major glitches"); + break; + } + + QString tooltip_string; + + if (entry.status == CompatibilityStatus::Unknown) { + tooltip_string = status_explanation; + } else { + tooltip_string = + "

" + tr("Click to go to issue") + "" + "
" + tr("Last updated") + + QString(": %1 (%2)").arg(entry.last_tested.toString("yyyy-MM-dd"), entry.version) + + "
" + status_explanation + "

"; + } + + QPixmap circle_pixmap(16, 16); + circle_pixmap.fill(Qt::transparent); + QPainter painter(&circle_pixmap); + painter.setRenderHint(QPainter::Antialiasing); + painter.setPen(color); + painter.setBrush(color); + painter.drawEllipse({circle_pixmap.width() / 2.0, circle_pixmap.height() / 2.0}, 6.0, 6.0); + + QLabel* dotLabel = new QLabel("", widget); + dotLabel->setPixmap(circle_pixmap); + + QLabel* label = new QLabel(m_compat_info->CompatStatusToString.at(entry.status), widget); + + label->setStyleSheet("color: white; font-size: 16px; font-weight: bold;"); + + // Create shadow effect + QGraphicsDropShadowEffect* shadowEffect = new QGraphicsDropShadowEffect(); + shadowEffect->setBlurRadius(5); // Set the blur radius of the shadow + shadowEffect->setColor(QColor(0, 0, 0, 160)); // Set the color and opacity of the shadow + shadowEffect->setOffset(2, 2); // Set the offset of the shadow + + label->setGraphicsEffect(shadowEffect); // Apply shadow effect to the QLabel + + layout->addWidget(dotLabel, 0, 0, -1, 1); + layout->addWidget(label, 0, 1, 1, 1); + layout->setAlignment(Qt::AlignLeft); + widget->setLayout(layout); + widget->setToolTip(tooltip_string); + this->setItem(row, column, item); + this->setCellWidget(row, column, widget); + + return; +} + void GameListFrame::SetTableItem(int row, int column, QString itemStr) { QTableWidgetItem* item = new QTableWidgetItem(); QWidget* widget = new QWidget(this); diff --git a/src/qt_gui/game_list_frame.h b/src/qt_gui/game_list_frame.h index 6da2734a8..8c6fcb1e2 100644 --- a/src/qt_gui/game_list_frame.h +++ b/src/qt_gui/game_list_frame.h @@ -3,9 +3,14 @@ #pragma once +#include +#include +#include +#include #include #include "background_music_player.h" +#include "compatibility_info.h" #include "game_info.h" #include "game_list_utils.h" #include "gui_context_menus.h" @@ -13,7 +18,9 @@ class GameListFrame : public QTableWidget { Q_OBJECT public: - explicit GameListFrame(std::shared_ptr game_info_get, QWidget* parent = nullptr); + explicit GameListFrame(std::shared_ptr game_info_get, + std::shared_ptr compat_info_get, + QWidget* parent = nullptr); Q_SIGNALS: void GameListFrameClosed(); @@ -29,6 +36,7 @@ public Q_SLOTS: private: void SetTableItem(int row, int column, QString itemStr); void SetRegionFlag(int row, int column, QString itemStr); + void SetCompatibilityItem(int row, int column, CompatibilityEntry entry); QString GetPlayTime(const std::string& serial); QList m_columnActs; GameInfoClass* game_inf_get = nullptr; @@ -42,6 +50,7 @@ public: GameListUtils m_game_list_utils; GuiContextMenus m_gui_context_menus; std::shared_ptr m_game_info; + std::shared_ptr m_compat_info; int icon_size; @@ -59,18 +68,20 @@ public: case 1: return a.name < b.name; case 2: - return a.serial.substr(4) < b.serial.substr(4); + return a.compatibility.status < b.compatibility.status; case 3: - return a.region < b.region; + return a.serial.substr(4) < b.serial.substr(4); case 4: - return parseAsFloat(a.fw, 0) < parseAsFloat(b.fw, 0); + return a.region < b.region; case 5: - return parseSizeMB(b.size) < parseSizeMB(a.size); + return parseAsFloat(a.fw, 0) < parseAsFloat(b.fw, 0); case 6: - return a.version < b.version; + return parseSizeMB(b.size) < parseSizeMB(a.size); case 7: - return a.play_time < b.play_time; + return a.version < b.version; case 8: + return a.play_time < b.play_time; + case 9: return a.path < b.path; default: return false; @@ -82,18 +93,20 @@ public: case 1: return a.name > b.name; case 2: - return a.serial.substr(4) > b.serial.substr(4); + return a.compatibility.status > b.compatibility.status; case 3: - return a.region > b.region; + return a.serial.substr(4) > b.serial.substr(4); case 4: - return parseAsFloat(a.fw, 0) > parseAsFloat(b.fw, 0); + return a.region > b.region; case 5: - return parseSizeMB(b.size) > parseSizeMB(a.size); + return parseAsFloat(a.fw, 0) > parseAsFloat(b.fw, 0); case 6: - return a.version > b.version; + return parseSizeMB(b.size) > parseSizeMB(a.size); case 7: - return a.play_time > b.play_time; + return a.version > b.version; case 8: + return a.play_time > b.play_time; + case 9: return a.path > b.path; default: return false; diff --git a/src/qt_gui/game_list_utils.h b/src/qt_gui/game_list_utils.h index 3d710c5b7..16c0307c8 100644 --- a/src/qt_gui/game_list_utils.h +++ b/src/qt_gui/game_list_utils.h @@ -3,7 +3,13 @@ #pragma once +#include +#include +#include +#include +#include #include "common/path_util.h" +#include "compatibility_info.h" struct GameInfo { std::filesystem::path path; // root path of game directory @@ -21,6 +27,7 @@ struct GameInfo { std::string fw = "Unknown"; std::string play_time = "Unknown"; + CompatibilityEntry compatibility = CompatibilityEntry{CompatibilityStatus::Unknown}; }; class GameListUtils { diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 9c81bcf11..90cc947f4 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -138,7 +138,7 @@ void MainWindow::CreateDockWindows() { setCentralWidget(phCentralWidget); m_dock_widget.reset(new QDockWidget(tr("Game List"), this)); - m_game_list_frame.reset(new GameListFrame(m_game_info, this)); + m_game_list_frame.reset(new GameListFrame(m_game_info, m_compat_info, this)); m_game_list_frame->setObjectName("gamelist"); m_game_grid_frame.reset(new GameGridFrame(m_game_info, this)); m_game_grid_frame->setObjectName("gamegridlist"); @@ -184,6 +184,8 @@ void MainWindow::CreateDockWindows() { } void MainWindow::LoadGameLists() { + // Update compatibility database + m_compat_info->UpdateCompatibilityDatabase(this); // Get game info from game folders. m_game_info->GetGameInfo(this); if (isTableList) { diff --git a/src/qt_gui/main_window.h b/src/qt_gui/main_window.h index 5ae2540ec..d3623c3d0 100644 --- a/src/qt_gui/main_window.h +++ b/src/qt_gui/main_window.h @@ -10,6 +10,7 @@ #include "background_music_player.h" #include "common/config.h" #include "common/path_util.h" +#include "compatibility_info.h" #include "core/file_format/psf.h" #include "core/file_sys/fs.h" #include "elf_viewer.h" @@ -92,6 +93,8 @@ private: PSF psf; std::shared_ptr m_game_info = std::make_shared(); + std::shared_ptr m_compat_info = + std::make_shared(); QTranslator* translator; From 953fb1463850f071e5df2a25532c8e41eb198013 Mon Sep 17 00:00:00 2001 From: Alessandro Ampala <48158436+alessandroampala@users.noreply.github.com> Date: Thu, 19 Dec 2024 09:59:26 +0100 Subject: [PATCH 213/549] Fix update on windows when path contains powershell wildcards (#1391) (#1779) * Fix update process on windows when there are some powershell wildcards in the installation path. (#1391) * Fix coding style error. (#1391) --- src/qt_gui/check_update.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/qt_gui/check_update.cpp b/src/qt_gui/check_update.cpp index a9aba0b84..d713f67fb 100644 --- a/src/qt_gui/check_update.cpp +++ b/src/qt_gui/check_update.cpp @@ -347,7 +347,11 @@ void CheckUpdate::DownloadUpdate(const QString& url) { QString userPath; Common::FS::PathToQString(userPath, Common::FS::GetUserPath(Common::FS::PathType::UserDir)); +#ifdef Q_OS_WIN + QString tempDownloadPath = QString(getenv("LOCALAPPDATA")) + "/Temp/temp_download_update"; +#else QString tempDownloadPath = userPath + "/temp_download_update"; +#endif QDir dir(tempDownloadPath); if (!dir.exists()) { dir.mkpath("."); @@ -393,6 +397,11 @@ void CheckUpdate::Install() { QString processCommand; #ifdef Q_OS_WIN + // On windows, overwrite tempDirPath with AppData/Local/Temp folder + // due to PowerShell Expand-Archive not being able to handle correctly + // paths in square brackets (ie: ./[shadps4]) + tempDirPath = QString(getenv("LOCALAPPDATA")) + "/Temp/temp_download_update"; + // Windows Batch Script scriptFileName = tempDirPath + "/update.ps1"; scriptContent = QStringLiteral( @@ -408,10 +417,11 @@ void CheckUpdate::Install() { "Start-Sleep -Seconds 3\n" "Copy-Item -Recurse -Force '%2\\*' '%3\\'\n" "Start-Sleep -Seconds 2\n" - "Remove-Item -Force '%3\\update.ps1'\n" - "Remove-Item -Force '%3\\temp_download_update.zip'\n" - "Start-Process '%3\\shadps4.exe'\n" - "Remove-Item -Recurse -Force '%2'\n"); + "Remove-Item -Force -LiteralPath '%3\\update.ps1'\n" + "Remove-Item -Force -LiteralPath '%3\\temp_download_update.zip'\n" + "Remove-Item -Recurse -Force '%2'\n" + "Start-Process -FilePath '%3\\shadps4.exe' " + "-WorkingDirectory ([WildcardPattern]::Escape('%3'))\n"); arguments << "-ExecutionPolicy" << "Bypass" << "-File" << scriptFileName; From 39fed1f469104112afe03047644d40d72131409d Mon Sep 17 00:00:00 2001 From: alvinkim101 <160981360+alvinkim101@users.noreply.github.com> Date: Fri, 20 Dec 2024 10:27:27 -0800 Subject: [PATCH 214/549] Fix splash assert (#1832) --- src/core/file_format/splash.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/file_format/splash.cpp b/src/core/file_format/splash.cpp index b68702157..4eb701cf7 100644 --- a/src/core/file_format/splash.cpp +++ b/src/core/file_format/splash.cpp @@ -9,7 +9,7 @@ #include "splash.h" bool Splash::Open(const std::filesystem::path& filepath) { - ASSERT_MSG(filepath.stem().string() != "png", "Unexpected file format passed"); + ASSERT_MSG(filepath.extension().string() == ".png", "Unexpected file format passed"); Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Read); if (!file.IsOpen()) { From 8d8bb050554b6c95042dd9462e744055dd35cf8c Mon Sep 17 00:00:00 2001 From: "Daniel R." <47796739+polybiusproxy@users.noreply.github.com> Date: Sat, 21 Dec 2024 10:20:24 +0100 Subject: [PATCH 215/549] renderer_vulkan: add support for Polygon draws (#1798) --- src/video_core/buffer_cache/buffer_cache.cpp | 36 +++++++++++++++---- .../renderer_vulkan/liverpool_to_vk.cpp | 1 + .../renderer_vulkan/liverpool_to_vk.h | 9 +++++ .../renderer_vulkan/vk_rasterizer.cpp | 7 ++-- 4 files changed, 43 insertions(+), 10 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index e9fc06493..f265fb68d 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -235,25 +235,44 @@ bool BufferCache::BindVertexBuffers( } u32 BufferCache::BindIndexBuffer(bool& is_indexed, u32 index_offset) { - // Emulate QuadList primitive type with CPU made index buffer. + // Emulate QuadList and Polygon primitive types with CPU made index buffer. const auto& regs = liverpool->regs; - if (regs.primitive_type == AmdGpu::PrimitiveType::QuadList && !is_indexed) { - is_indexed = true; + if (!is_indexed) { + bool needs_index_buffer = false; + if (regs.primitive_type == AmdGpu::PrimitiveType::QuadList || + regs.primitive_type == AmdGpu::PrimitiveType::Polygon) { + needs_index_buffer = true; + } + + if (!needs_index_buffer) { + return regs.num_indices; + } // Emit indices. const u32 index_size = 3 * regs.num_indices; const auto [data, offset] = stream_buffer.Map(index_size); - Vulkan::LiverpoolToVK::EmitQuadToTriangleListIndices(data, regs.num_indices); + + switch (regs.primitive_type) { + case AmdGpu::PrimitiveType::QuadList: + Vulkan::LiverpoolToVK::EmitQuadToTriangleListIndices(data, regs.num_indices); + break; + case AmdGpu::PrimitiveType::Polygon: + Vulkan::LiverpoolToVK::EmitPolygonToTriangleListIndices(data, regs.num_indices); + break; + default: + UNREACHABLE(); + break; + } + stream_buffer.Commit(); // Bind index buffer. + is_indexed = true; + const auto cmdbuf = scheduler.CommandBuffer(); cmdbuf.bindIndexBuffer(stream_buffer.Handle(), offset, vk::IndexType::eUint16); return index_size / sizeof(u16); } - if (!is_indexed) { - return regs.num_indices; - } // Figure out index type and size. const bool is_index16 = @@ -288,6 +307,9 @@ u32 BufferCache::BindIndexBuffer(bool& is_indexed, u32 index_offset) { cmdbuf.bindIndexBuffer(stream_buffer.Handle(), offset, index_type); return new_index_size / index_size; } + if (regs.primitive_type == AmdGpu::PrimitiveType::Polygon) { + UNREACHABLE(); + } // Bind index buffer. const u32 index_buffer_size = regs.num_indices * index_size; diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp index ec0bb3bb7..6df89dbae 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp @@ -117,6 +117,7 @@ vk::PrimitiveTopology PrimitiveType(AmdGpu::PrimitiveType type) { case AmdGpu::PrimitiveType::PatchPrimitive: return vk::PrimitiveTopology::ePatchList; case AmdGpu::PrimitiveType::QuadList: + case AmdGpu::PrimitiveType::Polygon: // Needs to generate index buffer on the fly. return vk::PrimitiveTopology::eTriangleList; case AmdGpu::PrimitiveType::RectList: diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.h b/src/video_core/renderer_vulkan/liverpool_to_vk.h index ebd09f0ee..72bddc6b6 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.h +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.h @@ -98,6 +98,15 @@ void ConvertQuadToTriangleListIndices(u8* out_ptr, const u8* in_ptr, u32 num_ver } } +inline void EmitPolygonToTriangleListIndices(u8* out_ptr, u32 num_vertices) { + u16* out_data = reinterpret_cast(out_ptr); + for (u16 i = 1; i < num_vertices - 1; i++) { + *out_data++ = 0; + *out_data++ = i; + *out_data++ = i + 1; + } +} + static inline vk::Format PromoteFormatToDepth(vk::Format fmt) { if (fmt == vk::Format::eR32Sfloat) { return vk::Format::eD32Sfloat; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index bd8906f86..df05b73a3 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -246,11 +246,12 @@ void Rasterizer::DrawIndirect(bool is_indexed, VAddr arg_address, u32 offset, u3 } const auto& regs = liverpool->regs; - if (regs.primitive_type == AmdGpu::PrimitiveType::QuadList) { - // For QuadList we use generated index buffer to convert quads to triangles. Since it + if (regs.primitive_type == AmdGpu::PrimitiveType::QuadList || + regs.primitive_type == AmdGpu::PrimitiveType::Polygon) { + // We use a generated index buffer to convert quad lists and polygons to triangles. Since it // changes type of the draw, arguments are not valid for this case. We need to run a // conversion pass to repack the indirect arguments buffer first. - LOG_WARNING(Render_Vulkan, "QuadList primitive type is not supported for indirect draw"); + LOG_WARNING(Render_Vulkan, "Primitive type is not supported for indirect draw"); return; } From 08182f814f2dc7a15f52c8566f6bf70a19b19fea Mon Sep 17 00:00:00 2001 From: Martin Date: Sun, 22 Dec 2024 01:49:12 +0100 Subject: [PATCH 216/549] Disable userfaultfd again by making it opt-in (#1777) * Disable userfaultfd again * Let userfd be be opt-in instead of disabled --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 172733840..d0c27c503 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -890,7 +890,8 @@ if (ENABLE_DISCORD_RPC) target_compile_definitions(shadps4 PRIVATE ENABLE_DISCORD_RPC) endif() -if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") +# Optional due to https://github.com/shadps4-emu/shadPS4/issues/1704 +if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND ENABLE_USERFAULTFD) target_compile_definitions(shadps4 PRIVATE ENABLE_USERFAULTFD) endif() From 7e890def481367694e2078dec21d2d9160e9fb77 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sat, 21 Dec 2024 16:49:34 -0800 Subject: [PATCH 217/549] fs: Return nullptr when file descriptor is out of bounds. (#1842) --- src/core/file_sys/fs.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/file_sys/fs.cpp b/src/core/file_sys/fs.cpp index 45ba67b93..bf340e9e3 100644 --- a/src/core/file_sys/fs.cpp +++ b/src/core/file_sys/fs.cpp @@ -171,6 +171,9 @@ void HandleTable::DeleteHandle(int d) { File* HandleTable::GetFile(int d) { std::scoped_lock lock{m_mutex}; + if (d < 0 || d >= m_files.size()) { + return nullptr; + } return m_files.at(d); } From fb2c035c0568cc33cbe8e02db91cc60d0db1db5b Mon Sep 17 00:00:00 2001 From: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> Date: Sun, 22 Dec 2024 02:49:42 +0200 Subject: [PATCH 218/549] vk_rasterizer: Fix stencil clears (#1840) --- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 15 ++++++++++++--- src/video_core/renderer_vulkan/vk_scheduler.cpp | 2 +- src/video_core/renderer_vulkan/vk_scheduler.h | 1 + 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index df05b73a3..f8efd9b81 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -161,10 +161,18 @@ RenderState Rasterizer::PrepareRenderState(u32 mrt_mask) { state.depth_attachment = { .imageView = *image_view.image_view, .imageLayout = vk::ImageLayout::eUndefined, - .loadOp = is_clear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, + .loadOp = is_clear && regs.depth_control.depth_enable ? vk::AttachmentLoadOp::eClear + : vk::AttachmentLoadOp::eLoad, .storeOp = vk::AttachmentStoreOp::eStore, - .clearValue = vk::ClearValue{.depthStencil = {.depth = regs.depth_clear, - .stencil = regs.stencil_clear}}, + .clearValue = vk::ClearValue{.depthStencil = {.depth = regs.depth_clear}}, + }; + state.stencil_attachment = { + .imageView = *image_view.image_view, + .imageLayout = vk::ImageLayout::eUndefined, + .loadOp = is_clear && regs.depth_control.stencil_enable ? vk::AttachmentLoadOp::eClear + : vk::AttachmentLoadOp::eLoad, + .storeOp = vk::AttachmentStoreOp::eStore, + .clearValue = vk::ClearValue{.depthStencil = {.stencil = regs.stencil_clear}}, }; texture_cache.TouchMeta(htile_address, slice, false); state.has_depth = @@ -778,6 +786,7 @@ void Rasterizer::BeginRendering(const GraphicsPipeline& pipeline, RenderState& s desc.view_info.range); } state.depth_attachment.imageLayout = image.last_state.layout; + state.stencil_attachment.imageLayout = image.last_state.layout; image.usage.depth_target = true; image.usage.stencil = has_stencil; } diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 2c4e7a3c6..81415f8b5 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -47,7 +47,7 @@ void Scheduler::BeginRendering(const RenderState& new_state) { ? render_state.color_attachments.data() : nullptr, .pDepthAttachment = render_state.has_depth ? &render_state.depth_attachment : nullptr, - .pStencilAttachment = render_state.has_stencil ? &render_state.depth_attachment : nullptr, + .pStencilAttachment = render_state.has_stencil ? &render_state.stencil_attachment : nullptr, }; current_cmdbuf.beginRendering(rendering_info); diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index cdd33745a..fd5e68373 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -21,6 +21,7 @@ class Instance; struct RenderState { std::array color_attachments{}; vk::RenderingAttachmentInfo depth_attachment{}; + vk::RenderingAttachmentInfo stencil_attachment{}; u32 num_color_attachments{}; bool has_depth{}; bool has_stencil{}; From 0931802151f0c54a9adced601d8ee2ca1e80a6fc Mon Sep 17 00:00:00 2001 From: f8ith Date: Sun, 22 Dec 2024 17:05:44 +0800 Subject: [PATCH 219/549] compatibility_data: various fixes (#1847) * temporarily disable status column + various fixes * add configuration option to enable compatibility data * clang format --- src/common/config.cpp | 25 +++++++++++++++ src/common/config.h | 4 +++ src/qt_gui/compatibility_info.cpp | 51 +++++++++++++++++++++++++------ src/qt_gui/compatibility_info.h | 3 +- src/qt_gui/game_list_frame.cpp | 5 +++ src/qt_gui/main_window.cpp | 4 ++- 6 files changed, 81 insertions(+), 11 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index 403b0e32f..9d5a99d9a 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -64,6 +64,8 @@ static bool vkCrashDiagnostic = false; static s16 cursorState = HideCursorState::Idle; static int cursorHideTimeout = 5; // 5 seconds (default) static bool separateupdatefolder = false; +static bool compatibilityData = false; +static bool checkCompatibilityOnStartup = false; // Gui std::vector settings_install_dirs = {}; @@ -224,6 +226,14 @@ bool getSeparateUpdateEnabled() { return separateupdatefolder; } +bool getCompatibilityEnabled() { + return compatibilityData; +} + +bool getCheckCompatibilityOnStartup() { + return checkCompatibilityOnStartup; +} + void setGpuId(s32 selectedGpuId) { gpuId = selectedGpuId; } @@ -344,6 +354,14 @@ void setSeparateUpdateEnabled(bool use) { separateupdatefolder = use; } +void setCompatibilityEnabled(bool use) { + compatibilityData = use; +} + +void setCheckCompatibilityOnStartup(bool use) { + checkCompatibilityOnStartup = use; +} + void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h) { main_window_geometry_x = x; main_window_geometry_y = y; @@ -544,6 +562,9 @@ void load(const std::filesystem::path& path) { isShowSplash = toml::find_or(general, "showSplash", true); isAutoUpdate = toml::find_or(general, "autoUpdate", false); separateupdatefolder = toml::find_or(general, "separateUpdateEnabled", false); + compatibilityData = toml::find_or(general, "compatibilityEnabled", false); + checkCompatibilityOnStartup = + toml::find_or(general, "checkCompatibilityOnStartup", false); } if (data.contains("Input")) { @@ -656,6 +677,8 @@ void save(const std::filesystem::path& path) { data["General"]["showSplash"] = isShowSplash; data["General"]["autoUpdate"] = isAutoUpdate; data["General"]["separateUpdateEnabled"] = separateupdatefolder; + data["General"]["compatibilityEnabled"] = compatibilityData; + data["General"]["checkCompatibilityOnStartup"] = checkCompatibilityOnStartup; data["Input"]["cursorState"] = cursorState; data["Input"]["cursorHideTimeout"] = cursorHideTimeout; data["Input"]["backButtonBehavior"] = backButtonBehavior; @@ -775,6 +798,8 @@ void setDefaultValues() { m_language = 1; gpuId = -1; separateupdatefolder = false; + compatibilityData = false; + checkCompatibilityOnStartup = false; } } // namespace Config diff --git a/src/common/config.h b/src/common/config.h index ff3b3703f..a4e6c3b12 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -21,6 +21,8 @@ bool getPlayBGM(); int getBGMvolume(); bool getEnableDiscordRPC(); bool getSeparateUpdateEnabled(); +bool getCompatibilityEnabled(); +bool getCheckCompatibilityOnStartup(); std::string getLogFilter(); std::string getLogType(); @@ -69,6 +71,8 @@ void setUserName(const std::string& type); void setUpdateChannel(const std::string& type); void setSeparateUpdateEnabled(bool use); void setGameInstallDirs(const std::vector& settings_install_dirs_config); +void setCompatibilityEnabled(bool use); +void setCheckCompatibilityOnStartup(bool use); void setCursorState(s16 cursorState); void setCursorHideTimeout(int newcursorHideTimeout); diff --git a/src/qt_gui/compatibility_info.cpp b/src/qt_gui/compatibility_info.cpp index c8d6bf36d..aecac60cd 100644 --- a/src/qt_gui/compatibility_info.cpp +++ b/src/qt_gui/compatibility_info.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "common/path_util.h" #include "compatibility_info.h" @@ -22,7 +23,8 @@ void CompatibilityInfoClass::UpdateCompatibilityDatabase(QWidget* parent) { return; QNetworkReply* reply = FetchPage(1); - WaitForReply(reply); + if (!WaitForReply(reply)) + return; QProgressDialog dialog(tr("Fetching compatibility data, please wait"), tr("Cancel"), 0, 0, parent); @@ -57,12 +59,17 @@ void CompatibilityInfoClass::UpdateCompatibilityDatabase(QWidget* parent) { } future_watcher.setFuture(QtConcurrent::map(replies, WaitForReply)); - connect(&future_watcher, &QFutureWatcher::finished, [&]() { + connect(&future_watcher, &QFutureWatcher::finished, [&]() { for (int i = 0; i < remaining_pages; i++) { - if (replies[i]->error() == QNetworkReply::NoError) { - ExtractCompatibilityInfo(replies[i]->readAll()); + if (replies[i]->bytesAvailable()) { + if (replies[i]->error() == QNetworkReply::NoError) { + ExtractCompatibilityInfo(replies[i]->readAll()); + } + replies[i]->deleteLater(); + } else { + // This means the request timed out + return; } - replies[i]->deleteLater(); } QFile compatibility_file(m_compatibility_filename); @@ -83,6 +90,16 @@ void CompatibilityInfoClass::UpdateCompatibilityDatabase(QWidget* parent) { dialog.reset(); }); + connect(&future_watcher, &QFutureWatcher::canceled, [&]() { + // Cleanup if user cancels pulling data + for (int i = 0; i < remaining_pages; i++) { + if (!replies[i]->bytesAvailable()) { + replies[i]->deleteLater(); + } else if (!replies[i]->isFinished()) { + replies[i]->abort(); + } + } + }); connect(&dialog, &QProgressDialog::canceled, &future_watcher, &QFutureWatcher::cancel); dialog.setRange(0, remaining_pages); connect(&future_watcher, &QFutureWatcher::progressValueChanged, &dialog, @@ -105,20 +122,34 @@ QNetworkReply* CompatibilityInfoClass::FetchPage(int page_num) { return reply; } -void CompatibilityInfoClass::WaitForReply(QNetworkReply* reply) { +bool CompatibilityInfoClass::WaitForReply(QNetworkReply* reply) { + // Returns true if reply succeeded, false if reply timed out + QTimer timer; + timer.setSingleShot(true); + QEventLoop loop; connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); + connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); + timer.start(5000); loop.exec(); - return; + + if (timer.isActive()) { + timer.stop(); + return true; + } else { + disconnect(reply, SIGNAL(finished()), &loop, SLOT(quit())); + reply->abort(); + return false; + } }; CompatibilityEntry CompatibilityInfoClass::GetCompatibilityInfo(const std::string& serial) { QString title_id = QString::fromStdString(serial); if (m_compatibility_database.contains(title_id)) { { + QJsonObject compatibility_obj = m_compatibility_database[title_id].toObject(); for (int os_int = 0; os_int != static_cast(OSType::Last); os_int++) { QString os_string = OSTypeToString.at(static_cast(os_int)); - QJsonObject compatibility_obj = m_compatibility_database[title_id].toObject(); if (compatibility_obj.contains(os_string)) { QJsonObject compatibility_entry_obj = compatibility_obj[os_string].toObject(); CompatibilityEntry compatibility_entry{ @@ -133,7 +164,9 @@ CompatibilityEntry CompatibilityInfoClass::GetCompatibilityInfo(const std::strin } } } - return CompatibilityEntry{CompatibilityStatus::Unknown}; + + return CompatibilityEntry{CompatibilityStatus::Unknown, "", QDateTime::currentDateTime(), "", + 0}; } bool CompatibilityInfoClass::LoadCompatibilityFile() { diff --git a/src/qt_gui/compatibility_info.h b/src/qt_gui/compatibility_info.h index 2b970670a..dcbaef847 100644 --- a/src/qt_gui/compatibility_info.h +++ b/src/qt_gui/compatibility_info.h @@ -57,6 +57,7 @@ class CompatibilityInfoClass : public QObject { public: // Please think of a better alternative inline static const std::unordered_map LabelToCompatStatus = { + {QStringLiteral("status-unknown"), CompatibilityStatus::Unknown}, {QStringLiteral("status-nothing"), CompatibilityStatus::Nothing}, {QStringLiteral("status-boots"), CompatibilityStatus::Boots}, {QStringLiteral("status-menus"), CompatibilityStatus::Menus}, @@ -87,7 +88,7 @@ public: bool LoadCompatibilityFile(); CompatibilityEntry GetCompatibilityInfo(const std::string& serial); void ExtractCompatibilityInfo(QByteArray response); - static void WaitForReply(QNetworkReply* reply); + static bool WaitForReply(QNetworkReply* reply); QNetworkReply* FetchPage(int page_num); private: diff --git a/src/qt_gui/game_list_frame.cpp b/src/qt_gui/game_list_frame.cpp index d43c35ef4..53159d8e7 100644 --- a/src/qt_gui/game_list_frame.cpp +++ b/src/qt_gui/game_list_frame.cpp @@ -80,6 +80,11 @@ GameListFrame::GameListFrame(std::shared_ptr game_info_get, QDesktopServices::openUrl(QUrl(m_game_info->m_games[row].compatibility.url)); } }); + + // Do not show status column if it is not enabled + if (!Config::getCompatibilityEnabled()) { + this->setColumnHidden(2, true); + } } void GameListFrame::onCurrentCellChanged(int currentRow, int currentColumn, int previousRow, diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 90cc947f4..d7d2a856a 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -185,7 +185,9 @@ void MainWindow::CreateDockWindows() { void MainWindow::LoadGameLists() { // Update compatibility database - m_compat_info->UpdateCompatibilityDatabase(this); + if (Config::getCheckCompatibilityOnStartup()) { + m_compat_info->UpdateCompatibilityDatabase(this); + } // Get game info from game folders. m_game_info->GetGameInfo(this); if (isTableList) { From fac21a5362cae488f4b61b950fc52a46210645e2 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sun, 22 Dec 2024 01:46:28 -0800 Subject: [PATCH 220/549] audio: Move port logic out of SDL backend and define backend interface. (#1848) --- src/core/libraries/audio/audioout.cpp | 169 ++++++++++++++++++-- src/core/libraries/audio/audioout.h | 2 +- src/core/libraries/audio/audioout_backend.h | 19 +++ src/core/libraries/audio/sdl_audio.cpp | 133 +++------------ src/core/libraries/audio/sdl_audio.h | 36 +---- 5 files changed, 203 insertions(+), 156 deletions(-) create mode 100644 src/core/libraries/audio/audioout_backend.h diff --git a/src/core/libraries/audio/audioout.cpp b/src/core/libraries/audio/audioout.cpp index 78b04cc90..db43ee928 100644 --- a/src/core/libraries/audio/audioout.cpp +++ b/src/core/libraries/audio/audioout.cpp @@ -2,6 +2,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include +#include #include #include "common/assert.h" @@ -13,7 +15,22 @@ namespace Libraries::AudioOut { -static std::unique_ptr audio; +struct PortOut { + void* impl; + u32 samples_num; + u32 freq; + OrbisAudioOutParamFormat format; + OrbisAudioOutPort type; + int channels_num; + bool is_float; + std::array volume; + u8 sample_size; + bool is_open; +}; +std::shared_mutex ports_mutex; +std::array ports_out{}; + +static std::unique_ptr audio; static std::string_view GetAudioOutPort(OrbisAudioOutPort port) { switch (port) { @@ -70,6 +87,58 @@ static std::string_view GetAudioOutParamAttr(OrbisAudioOutParamAttr attr) { } } +static bool IsFormatFloat(const OrbisAudioOutParamFormat format) { + switch (format) { + case OrbisAudioOutParamFormat::S16Mono: + case OrbisAudioOutParamFormat::S16Stereo: + case OrbisAudioOutParamFormat::S16_8CH: + case OrbisAudioOutParamFormat::S16_8CH_Std: + return false; + case OrbisAudioOutParamFormat::FloatMono: + case OrbisAudioOutParamFormat::FloatStereo: + case OrbisAudioOutParamFormat::Float_8CH: + case OrbisAudioOutParamFormat::Float_8CH_Std: + return true; + default: + UNREACHABLE_MSG("Unknown format"); + } +} + +static int GetFormatNumChannels(const OrbisAudioOutParamFormat format) { + switch (format) { + case OrbisAudioOutParamFormat::S16Mono: + case OrbisAudioOutParamFormat::FloatMono: + return 1; + case OrbisAudioOutParamFormat::S16Stereo: + case OrbisAudioOutParamFormat::FloatStereo: + return 2; + case OrbisAudioOutParamFormat::S16_8CH: + case OrbisAudioOutParamFormat::Float_8CH: + case OrbisAudioOutParamFormat::S16_8CH_Std: + case OrbisAudioOutParamFormat::Float_8CH_Std: + return 8; + default: + UNREACHABLE_MSG("Unknown format"); + } +} + +static u8 GetFormatSampleSize(const OrbisAudioOutParamFormat format) { + switch (format) { + case OrbisAudioOutParamFormat::S16Mono: + case OrbisAudioOutParamFormat::S16Stereo: + case OrbisAudioOutParamFormat::S16_8CH: + case OrbisAudioOutParamFormat::S16_8CH_Std: + return 2; + case OrbisAudioOutParamFormat::FloatMono: + case OrbisAudioOutParamFormat::FloatStereo: + case OrbisAudioOutParamFormat::Float_8CH: + case OrbisAudioOutParamFormat::Float_8CH_Std: + return 4; + default: + UNREACHABLE_MSG("Unknown format"); + } +} + int PS4_SYSV_ABI sceAudioOutDeviceIdOpen() { LOG_ERROR(Lib_AudioOut, "(STUBBED) called"); return ORBIS_OK; @@ -110,8 +179,21 @@ int PS4_SYSV_ABI sceAudioOutChangeAppModuleState() { return ORBIS_OK; } -int PS4_SYSV_ABI sceAudioOutClose() { - LOG_ERROR(Lib_AudioOut, "(STUBBED) called"); +int PS4_SYSV_ABI sceAudioOutClose(s32 handle) { + LOG_INFO(Lib_AudioOut, "handle = {}", handle); + if (handle < 1 || handle > SCE_AUDIO_OUT_NUM_PORTS) { + return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; + } + + std::scoped_lock lock(ports_mutex); + auto& port = ports_out.at(handle - 1); + if (!port.is_open) { + return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; + } + + audio->Close(port.impl); + port.impl = nullptr; + port.is_open = false; return ORBIS_OK; } @@ -180,16 +262,21 @@ int PS4_SYSV_ABI sceAudioOutGetPortState(s32 handle, OrbisAudioOutPortState* sta return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; } - const auto [type, channels_num] = audio->GetStatus(handle); + std::scoped_lock lock(ports_mutex); + const auto& port = ports_out.at(handle - 1); + if (!port.is_open) { + return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; + } + state->rerouteCounter = 0; state->volume = 127; - switch (type) { + switch (port.type) { case OrbisAudioOutPort::Main: case OrbisAudioOutPort::Bgm: case OrbisAudioOutPort::Voice: state->output = 1; - state->channel = (channels_num > 2 ? 2 : channels_num); + state->channel = port.channels_num > 2 ? 2 : port.channels_num; break; case OrbisAudioOutPort::Personal: case OrbisAudioOutPort::Padspk: @@ -276,7 +363,7 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id, u32 sample_rate, OrbisAudioOutParamExtendedInformation param_type) { LOG_INFO(Lib_AudioOut, - "AudioOutOpen id = {} port_type = {} index = {} lenght= {} sample_rate = {} " + "id = {} port_type = {} index = {} length = {} sample_rate = {} " "param_type = {} attr = {}", user_id, GetAudioOutPort(port_type), index, length, sample_rate, GetAudioOutParamFormat(param_type.data_format), @@ -310,7 +397,26 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id, LOG_ERROR(Lib_AudioOut, "Invalid format attribute"); return ORBIS_AUDIO_OUT_ERROR_INVALID_FORMAT; } - return audio->Open(port_type, length, sample_rate, format); + + std::scoped_lock lock{ports_mutex}; + const auto port = std::ranges::find(ports_out, false, &PortOut::is_open); + if (port == ports_out.end()) { + LOG_ERROR(Lib_AudioOut, "Audio ports are full"); + return ORBIS_AUDIO_OUT_ERROR_PORT_FULL; + } + + port->is_open = true; + port->type = port_type; + port->samples_num = length; + port->freq = sample_rate; + port->format = format; + port->is_float = IsFormatFloat(format); + port->channels_num = GetFormatNumChannels(format); + port->sample_size = GetFormatSampleSize(format); + port->volume.fill(SCE_AUDIO_OUT_VOLUME_0DB); + + port->impl = audio->Open(port->is_float, port->channels_num, port->freq); + return std::distance(ports_out.begin(), port) + 1; } int PS4_SYSV_ABI sceAudioOutOpenEx() { @@ -326,7 +432,15 @@ s32 PS4_SYSV_ABI sceAudioOutOutput(s32 handle, const void* ptr) { // Nothing to output return ORBIS_OK; } - return audio->Output(handle, ptr); + + auto& port = ports_out.at(handle - 1); + if (!port.is_open) { + return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; + } + + const size_t data_size = port.samples_num * port.sample_size * port.channels_num; + audio->Output(port.impl, ptr, data_size); + return ORBIS_OK; } int PS4_SYSV_ABI sceAudioOutOutputs(OrbisAudioOutOutputParam* param, u32 num) { @@ -431,7 +545,42 @@ s32 PS4_SYSV_ABI sceAudioOutSetVolume(s32 handle, s32 flag, s32* vol) { if (handle < 1 || handle > SCE_AUDIO_OUT_NUM_PORTS) { return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; } - return audio->SetVolume(handle, flag, vol); + + std::scoped_lock lock(ports_mutex); + auto& port = ports_out.at(handle - 1); + if (!port.is_open) { + return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; + } + + for (int i = 0; i < port.channels_num; i++, flag >>= 1u) { + auto bit = flag & 0x1u; + if (bit == 1) { + int src_index = i; + if (port.format == OrbisAudioOutParamFormat::Float_8CH_Std || + port.format == OrbisAudioOutParamFormat::S16_8CH_Std) { + switch (i) { + case 4: + src_index = 6; + break; + case 5: + src_index = 7; + break; + case 6: + src_index = 4; + break; + case 7: + src_index = 5; + break; + default: + break; + } + } + port.volume[i] = vol[src_index]; + } + } + + audio->SetVolume(port.impl, port.volume); + return ORBIS_OK; } int PS4_SYSV_ABI sceAudioOutSetVolumeDown() { diff --git a/src/core/libraries/audio/audioout.h b/src/core/libraries/audio/audioout.h index e8e718b87..c66a0e9f5 100644 --- a/src/core/libraries/audio/audioout.h +++ b/src/core/libraries/audio/audioout.h @@ -64,7 +64,7 @@ int PS4_SYSV_ABI sceAudioOutA3dExit(); int PS4_SYSV_ABI sceAudioOutA3dInit(); int PS4_SYSV_ABI sceAudioOutAttachToApplicationByPid(); int PS4_SYSV_ABI sceAudioOutChangeAppModuleState(); -int PS4_SYSV_ABI sceAudioOutClose(); +int PS4_SYSV_ABI sceAudioOutClose(s32 handle); int PS4_SYSV_ABI sceAudioOutDetachFromApplicationByPid(); int PS4_SYSV_ABI sceAudioOutExConfigureOutputMode(); int PS4_SYSV_ABI sceAudioOutExGetSystemInfo(); diff --git a/src/core/libraries/audio/audioout_backend.h b/src/core/libraries/audio/audioout_backend.h new file mode 100644 index 000000000..238ef0201 --- /dev/null +++ b/src/core/libraries/audio/audioout_backend.h @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +namespace Libraries::AudioOut { + +class AudioOutBackend { +public: + AudioOutBackend() = default; + virtual ~AudioOutBackend() = default; + + virtual void* Open(bool is_float, int num_channels, u32 sample_rate) = 0; + virtual void Close(void* impl) = 0; + virtual void Output(void* impl, const void* ptr, size_t size) = 0; + virtual void SetVolume(void* impl, std::array ch_volumes) = 0; +}; + +} // namespace Libraries::AudioOut diff --git a/src/core/libraries/audio/sdl_audio.cpp b/src/core/libraries/audio/sdl_audio.cpp index 8cc823abe..ce385ad9c 100644 --- a/src/core/libraries/audio/sdl_audio.cpp +++ b/src/core/libraries/audio/sdl_audio.cpp @@ -1,141 +1,44 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include #include #include #include #include "common/assert.h" -#include "core/libraries/audio/audioout_error.h" #include "core/libraries/audio/sdl_audio.h" namespace Libraries::AudioOut { constexpr int AUDIO_STREAM_BUFFER_THRESHOLD = 65536; // Define constant for buffer threshold -s32 SDLAudioOut::Open(OrbisAudioOutPort type, u32 samples_num, u32 freq, - OrbisAudioOutParamFormat format) { - std::scoped_lock lock{m_mutex}; - const auto port = std::ranges::find(ports_out, false, &PortOut::is_open); - if (port == ports_out.end()) { - LOG_ERROR(Lib_AudioOut, "Audio ports are full"); - return ORBIS_AUDIO_OUT_ERROR_PORT_FULL; - } - - port->is_open = true; - port->type = type; - port->samples_num = samples_num; - port->freq = freq; - port->format = format; - SDL_AudioFormat sampleFormat; - switch (format) { - case OrbisAudioOutParamFormat::S16Mono: - sampleFormat = SDL_AUDIO_S16; - port->channels_num = 1; - port->sample_size = 2; - break; - case OrbisAudioOutParamFormat::FloatMono: - sampleFormat = SDL_AUDIO_F32; - port->channels_num = 1; - port->sample_size = 4; - break; - case OrbisAudioOutParamFormat::S16Stereo: - sampleFormat = SDL_AUDIO_S16; - port->channels_num = 2; - port->sample_size = 2; - break; - case OrbisAudioOutParamFormat::FloatStereo: - sampleFormat = SDL_AUDIO_F32; - port->channels_num = 2; - port->sample_size = 4; - break; - case OrbisAudioOutParamFormat::S16_8CH: - sampleFormat = SDL_AUDIO_S16; - port->channels_num = 8; - port->sample_size = 2; - break; - case OrbisAudioOutParamFormat::Float_8CH: - sampleFormat = SDL_AUDIO_F32; - port->channels_num = 8; - port->sample_size = 4; - break; - case OrbisAudioOutParamFormat::S16_8CH_Std: - sampleFormat = SDL_AUDIO_S16; - port->channels_num = 8; - port->sample_size = 2; - break; - case OrbisAudioOutParamFormat::Float_8CH_Std: - sampleFormat = SDL_AUDIO_F32; - port->channels_num = 8; - port->sample_size = 4; - break; - default: - UNREACHABLE_MSG("Unknown format"); - } - - port->volume.fill(Libraries::AudioOut::SCE_AUDIO_OUT_VOLUME_0DB); - +void* SDLAudioOut::Open(bool is_float, int num_channels, u32 sample_rate) { SDL_AudioSpec fmt; SDL_zero(fmt); - fmt.format = sampleFormat; - fmt.channels = port->channels_num; - fmt.freq = freq; - port->stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &fmt, NULL, NULL); - SDL_ResumeAudioDevice(SDL_GetAudioStreamDevice(port->stream)); - return std::distance(ports_out.begin(), port) + 1; + fmt.format = is_float ? SDL_AUDIO_F32 : SDL_AUDIO_S16; + fmt.channels = num_channels; + fmt.freq = sample_rate; + + auto* stream = + SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &fmt, nullptr, nullptr); + SDL_ResumeAudioStreamDevice(stream); + return stream; } -s32 SDLAudioOut::Output(s32 handle, const void* ptr) { - auto& port = ports_out.at(handle - 1); - if (!port.is_open) { - return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; - } +void SDLAudioOut::Close(void* impl) { + SDL_DestroyAudioStream(static_cast(impl)); +} - const size_t data_size = port.samples_num * port.sample_size * port.channels_num; - bool result = SDL_PutAudioStreamData(port.stream, ptr, data_size); - while (SDL_GetAudioStreamAvailable(port.stream) > AUDIO_STREAM_BUFFER_THRESHOLD) { +void SDLAudioOut::Output(void* impl, const void* ptr, size_t size) { + auto* stream = static_cast(impl); + SDL_PutAudioStreamData(stream, ptr, size); + while (SDL_GetAudioStreamAvailable(stream) > AUDIO_STREAM_BUFFER_THRESHOLD) { SDL_Delay(0); } - return result ? ORBIS_OK : -1; } -s32 SDLAudioOut::SetVolume(s32 handle, s32 bitflag, s32* volume) { - using Libraries::AudioOut::OrbisAudioOutParamFormat; - auto& port = ports_out.at(handle - 1); - if (!port.is_open) { - return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; - } - - for (int i = 0; i < port.channels_num; i++, bitflag >>= 1u) { - auto bit = bitflag & 0x1u; - - if (bit == 1) { - int src_index = i; - if (port.format == OrbisAudioOutParamFormat::Float_8CH_Std || - port.format == OrbisAudioOutParamFormat::S16_8CH_Std) { - switch (i) { - case 4: - src_index = 6; - break; - case 5: - src_index = 7; - break; - case 6: - src_index = 4; - break; - case 7: - src_index = 5; - break; - default: - break; - } - } - port.volume[i] = volume[src_index]; - } - } - - return ORBIS_OK; +void SDLAudioOut::SetVolume(void* impl, std::array ch_volumes) { + // Not yet implemented } } // namespace Libraries::AudioOut diff --git a/src/core/libraries/audio/sdl_audio.h b/src/core/libraries/audio/sdl_audio.h index 2c34f8e29..d55f2f6e3 100644 --- a/src/core/libraries/audio/sdl_audio.h +++ b/src/core/libraries/audio/sdl_audio.h @@ -3,40 +3,16 @@ #pragma once -#include -#include -#include "core/libraries/audio/audioout.h" +#include "core/libraries/audio/audioout_backend.h" namespace Libraries::AudioOut { -class SDLAudioOut { +class SDLAudioOut final : public AudioOutBackend { public: - explicit SDLAudioOut() = default; - ~SDLAudioOut() = default; - - s32 Open(OrbisAudioOutPort type, u32 samples_num, u32 freq, OrbisAudioOutParamFormat format); - s32 Output(s32 handle, const void* ptr); - s32 SetVolume(s32 handle, s32 bitflag, s32* volume); - - constexpr std::pair GetStatus(s32 handle) const { - const auto& port = ports_out.at(handle - 1); - return std::make_pair(port.type, port.channels_num); - } - -private: - struct PortOut { - SDL_AudioStream* stream; - u32 samples_num; - u32 freq; - OrbisAudioOutParamFormat format; - OrbisAudioOutPort type; - int channels_num; - std::array volume; - u8 sample_size; - bool is_open; - }; - std::shared_mutex m_mutex; - std::array ports_out{}; + void* Open(bool is_float, int num_channels, u32 sample_rate) override; + void Close(void* impl) override; + void Output(void* impl, const void* ptr, size_t size) override; + void SetVolume(void* impl, std::array ch_volumes) override; }; } // namespace Libraries::AudioOut From 5eebb04de964b053ec3d091c8c3931ae305c96a9 Mon Sep 17 00:00:00 2001 From: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> Date: Sun, 22 Dec 2024 15:31:10 +0200 Subject: [PATCH 221/549] vk_rasterizer: hot fix --- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index f8efd9b81..b7cfb8cf0 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -152,8 +152,9 @@ RenderState Rasterizer::PrepareRenderState(u32 mrt_mask) { image.binding.is_target = 1u; const auto slice = image_view.info.range.base.layer; - const bool is_clear = regs.depth_render_control.depth_clear_enable || - texture_cache.IsMetaCleared(htile_address, slice); + const bool is_depth_clear = regs.depth_render_control.depth_clear_enable || + texture_cache.IsMetaCleared(htile_address, slice); + const bool is_stencil_clear = regs.depth_render_control.stencil_clear_enable; ASSERT(desc.view_info.range.extent.layers == 1); state.width = std::min(state.width, image.info.size.width); @@ -161,16 +162,14 @@ RenderState Rasterizer::PrepareRenderState(u32 mrt_mask) { state.depth_attachment = { .imageView = *image_view.image_view, .imageLayout = vk::ImageLayout::eUndefined, - .loadOp = is_clear && regs.depth_control.depth_enable ? vk::AttachmentLoadOp::eClear - : vk::AttachmentLoadOp::eLoad, + .loadOp = is_depth_clear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, .storeOp = vk::AttachmentStoreOp::eStore, .clearValue = vk::ClearValue{.depthStencil = {.depth = regs.depth_clear}}, }; state.stencil_attachment = { .imageView = *image_view.image_view, .imageLayout = vk::ImageLayout::eUndefined, - .loadOp = is_clear && regs.depth_control.stencil_enable ? vk::AttachmentLoadOp::eClear - : vk::AttachmentLoadOp::eLoad, + .loadOp = is_stencil_clear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, .storeOp = vk::AttachmentStoreOp::eStore, .clearValue = vk::ClearValue{.depthStencil = {.stencil = regs.stencil_clear}}, }; From 433d9459e09cd9ce3a1d02cc78d901b66d25335f Mon Sep 17 00:00:00 2001 From: rainmakerv2 <30595646+rainmakerv3@users.noreply.github.com> Date: Sun, 22 Dec 2024 21:46:45 +0800 Subject: [PATCH 222/549] Disable trophy pop-up with config setting (#1834) --- src/common/config.cpp | 12 +++++++++++ src/common/config.h | 2 ++ src/core/libraries/np_trophy/trophy_ui.cpp | 6 +++++- src/qt_gui/settings_dialog.cpp | 8 ++++++- src/qt_gui/settings_dialog.ui | 25 ++++++++++------------ src/qt_gui/translations/en.ts | 5 +++++ 6 files changed, 42 insertions(+), 16 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index 9d5a99d9a..deef0fa88 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -34,6 +34,7 @@ namespace Config { static bool isNeo = false; static bool isFullscreen = false; static bool playBGM = false; +static bool isTrophyPopupDisabled = false; static int BGMvolume = 50; static bool enableDiscordRPC = false; static u32 screenWidth = 1280; @@ -98,6 +99,10 @@ bool isFullscreenMode() { return isFullscreen; } +bool getisTrophyPopupDisabled() { + return isTrophyPopupDisabled; +} + bool getPlayBGM() { return playBGM; } @@ -294,6 +299,10 @@ void setFullscreenMode(bool enable) { isFullscreen = enable; } +void setisTrophyPopupDisabled(bool disable) { + isTrophyPopupDisabled = disable; +} + void setPlayBGM(bool enable) { playBGM = enable; } @@ -549,6 +558,7 @@ void load(const std::filesystem::path& path) { isNeo = toml::find_or(general, "isPS4Pro", false); isFullscreen = toml::find_or(general, "Fullscreen", false); playBGM = toml::find_or(general, "playBGM", false); + isTrophyPopupDisabled = toml::find_or(general, "isTrophyPopupDisabled", false); BGMvolume = toml::find_or(general, "BGMvolume", 50); enableDiscordRPC = toml::find_or(general, "enableDiscordRPC", true); logFilter = toml::find_or(general, "logFilter", ""); @@ -667,6 +677,7 @@ void save(const std::filesystem::path& path) { data["General"]["isPS4Pro"] = isNeo; data["General"]["Fullscreen"] = isFullscreen; + data["General"]["isTrophyPopupDisabled"] = isTrophyPopupDisabled; data["General"]["playBGM"] = playBGM; data["General"]["BGMvolume"] = BGMvolume; data["General"]["enableDiscordRPC"] = enableDiscordRPC; @@ -763,6 +774,7 @@ void saveMainWindow(const std::filesystem::path& path) { void setDefaultValues() { isNeo = false; isFullscreen = false; + isTrophyPopupDisabled = false; playBGM = false; BGMvolume = 50; enableDiscordRPC = true; diff --git a/src/common/config.h b/src/common/config.h index a4e6c3b12..701aadb12 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -19,6 +19,7 @@ bool isNeoMode(); bool isFullscreenMode(); bool getPlayBGM(); int getBGMvolume(); +bool getisTrophyPopupDisabled(); bool getEnableDiscordRPC(); bool getSeparateUpdateEnabled(); bool getCompatibilityEnabled(); @@ -62,6 +63,7 @@ void setGpuId(s32 selectedGpuId); void setScreenWidth(u32 width); void setScreenHeight(u32 height); void setFullscreenMode(bool enable); +void setisTrophyPopupDisabled(bool disable); void setPlayBGM(bool enable); void setBGMvolume(int volume); void setEnableDiscordRPC(bool enable); diff --git a/src/core/libraries/np_trophy/trophy_ui.cpp b/src/core/libraries/np_trophy/trophy_ui.cpp index 55ef7b8de..4bb8c8240 100644 --- a/src/core/libraries/np_trophy/trophy_ui.cpp +++ b/src/core/libraries/np_trophy/trophy_ui.cpp @@ -5,6 +5,7 @@ #include #include #include "common/assert.h" +#include "common/config.h" #include "common/singleton.h" #include "imgui/imgui_std.h" #include "trophy_ui.h" @@ -82,7 +83,10 @@ void TrophyUI::Draw() { void AddTrophyToQueue(const std::filesystem::path& trophyIconPath, const std::string& trophyName) { std::lock_guard lock(queueMtx); - if (current_trophy_ui.has_value()) { + + if (Config::getisTrophyPopupDisabled()) { + return; + } else if (current_trophy_ui.has_value()) { TrophyInfo new_trophy; new_trophy.trophy_icon_path = trophyIconPath; new_trophy.trophy_name = trophyName; diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index 09d3674f7..97c891e4f 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -194,6 +194,7 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge ui->updaterGroupBox->installEventFilter(this); #endif ui->GUIgroupBox->installEventFilter(this); + ui->disableTrophycheckBox->installEventFilter(this); // Input ui->hideCursorGroupBox->installEventFilter(this); @@ -263,6 +264,8 @@ void SettingsDialog::LoadValuesFromConfig() { ui->dumpShadersCheckBox->setChecked(toml::find_or(data, "GPU", "dumpShaders", false)); ui->nullGpuCheckBox->setChecked(toml::find_or(data, "GPU", "nullGpu", false)); ui->playBGMCheckBox->setChecked(toml::find_or(data, "General", "playBGM", false)); + ui->disableTrophycheckBox->setChecked( + toml::find_or(data, "General", "isTrophyPopupDisabled", false)); ui->BGMVolumeSlider->setValue(toml::find_or(data, "General", "BGMvolume", 50)); ui->discordRPCCheckbox->setChecked( toml::find_or(data, "General", "enableDiscordRPC", true)); @@ -397,6 +400,8 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) { #endif } else if (elementName == "GUIgroupBox") { text = tr("GUIgroupBox"); + } else if (elementName == "disableTrophycheckBox") { + text = tr("disableTrophycheckBox"); } // Input @@ -485,6 +490,7 @@ void SettingsDialog::UpdateSettings() { Config::setBackButtonBehavior(TouchPadIndex[ui->backButtonBehaviorComboBox->currentIndex()]); Config::setNeoMode(ui->ps4proCheckBox->isChecked()); Config::setFullscreenMode(ui->fullscreenCheckBox->isChecked()); + Config::setisTrophyPopupDisabled(ui->disableTrophycheckBox->isChecked()); Config::setPlayBGM(ui->playBGMCheckBox->isChecked()); Config::setNeoMode(ui->ps4proCheckBox->isChecked()); Config::setLogType(ui->logTypeComboBox->currentText().toStdString()); @@ -549,4 +555,4 @@ void SettingsDialog::ResetInstallFolders() { } Config::setGameInstallDirs(settings_install_dirs_config); } -} \ No newline at end of file +} diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index cce728f65..faa0bf847 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -59,9 +59,9 @@ 0 - 0 - 832 - 431 + -97 + 815 + 618 @@ -469,6 +469,13 @@ 11 + + + + Disable Trophy Pop-ups + + + @@ -561,16 +568,6 @@ - - - - - 0 - 61 - - - - @@ -696,7 +693,7 @@ 5 - + true diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index 9eccec8ea..ddaa4fe0d 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -1201,6 +1201,11 @@ GUIgroupBox Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI.
+ + + disableTrophycheckBox + Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + hideCursorGroupBox From aba2b290742c3f240cf910d0f81fbd2361eb03be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quang=20Ng=C3=B4?= Date: Sun, 22 Dec 2024 20:47:28 +0700 Subject: [PATCH 223/549] gui: start the emulator in new thread (#1829) --- src/qt_gui/main_window.cpp | 25 +++++++++++++++++-------- src/qt_gui/main_window.h | 1 + 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index d7d2a856a..39f7c7b97 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -556,7 +556,6 @@ void MainWindow::CreateConnects() { } void MainWindow::StartGame() { - isGameRunning = true; BackgroundMusicPlayer::getInstance().stopMusic(); QString gamePath = ""; int table_mode = Config::getTableMode(); @@ -579,13 +578,12 @@ void MainWindow::StartGame() { } if (gamePath != "") { AddRecentFiles(gamePath); - Core::Emulator emulator; const auto path = Common::FS::PathFromQString(gamePath); if (!std::filesystem::exists(path)) { QMessageBox::critical(nullptr, tr("Run Game"), QString(tr("Eboot.bin file not found"))); return; } - emulator.Run(path); + StartEmulator(path); } } @@ -682,13 +680,12 @@ void MainWindow::BootGame() { QString(tr("Only one file can be selected!"))); } else { std::filesystem::path path = Common::FS::PathFromQString(fileNames[0]); - Core::Emulator emulator; if (!std::filesystem::exists(path)) { QMessageBox::critical(nullptr, tr("Run Game"), QString(tr("Eboot.bin file not found"))); return; } - emulator.Run(path); + StartEmulator(path); } } } @@ -1042,12 +1039,11 @@ void MainWindow::CreateRecentGameActions() { connect(m_recent_files_group, &QActionGroup::triggered, this, [this](QAction* action) { auto gamePath = Common::FS::PathFromQString(action->text()); AddRecentFiles(action->text()); // Update the list. - Core::Emulator emulator; if (!std::filesystem::exists(gamePath)) { QMessageBox::critical(nullptr, tr("Run Game"), QString(tr("Eboot.bin file not found"))); return; } - emulator.Run(gamePath); + StartEmulator(gamePath); }); } @@ -1095,4 +1091,17 @@ bool MainWindow::eventFilter(QObject* obj, QEvent* event) { } } return QMainWindow::eventFilter(obj, event); -} \ No newline at end of file +} + +void MainWindow::StartEmulator(std::filesystem::path path) { + if (isGameRunning) { + QMessageBox::critical(nullptr, tr("Run Game"), QString(tr("Game is already running!"))); + return; + } + std::thread emulator_thread([=] { + Core::Emulator emulator; + emulator.Run(path); + }); + emulator_thread.detach(); + isGameRunning = true; +} diff --git a/src/qt_gui/main_window.h b/src/qt_gui/main_window.h index d3623c3d0..f4163defa 100644 --- a/src/qt_gui/main_window.h +++ b/src/qt_gui/main_window.h @@ -69,6 +69,7 @@ private: void LoadTranslation(); void PlayBackgroundMusic(); QIcon RecolorIcon(const QIcon& icon, bool isWhite); + void StartEmulator(std::filesystem::path); bool isIconBlack = false; bool isTableList = true; bool isGameRunning = false; From 14dc13683227dbe1682aa1c13c85abb667d2aee2 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sun, 22 Dec 2024 06:08:48 -0800 Subject: [PATCH 224/549] renderer_vulkan: Various attachment cleanup and fixes. (#1795) --- src/video_core/amdgpu/liverpool.h | 28 ++++ .../renderer_vulkan/vk_graphics_pipeline.h | 4 - .../renderer_vulkan/vk_pipeline_cache.cpp | 37 ++--- .../renderer_vulkan/vk_rasterizer.cpp | 133 +++++++++++------- .../renderer_vulkan/vk_rasterizer.h | 1 - .../renderer_vulkan/vk_scheduler.cpp | 4 +- src/video_core/texture_cache/image_info.cpp | 4 +- 7 files changed, 123 insertions(+), 88 deletions(-) diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index 4c74d37d0..83271a82d 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -428,6 +428,14 @@ struct Liverpool { BitField<0, 22, u32> tile_max; } depth_slice; + bool DepthValid() const { + return Address() != 0 && z_info.format != ZFormat::Invalid; + } + + bool StencilValid() const { + return Address() != 0 && stencil_info.format != StencilFormat::Invalid; + } + u32 Pitch() const { return (depth_size.pitch_tile_max + 1) << 3; } @@ -1275,6 +1283,26 @@ struct Liverpool { return nullptr; } + u32 NumSamples() const { + // It seems that the number of samples > 1 set in the AA config doesn't mean we're + // always rendering with MSAA, so we need to derive MS ratio from the CB and DB + // settings. + u32 num_samples = 1u; + if (color_control.mode != ColorControl::OperationMode::Disable) { + for (auto cb = 0u; cb < NumColorBuffers; ++cb) { + const auto& col_buf = color_buffers[cb]; + if (!col_buf) { + continue; + } + num_samples = std::max(num_samples, col_buf.NumSamples()); + } + } + if (depth_buffer.DepthValid() || depth_buffer.StencilValid()) { + num_samples = std::max(num_samples, depth_buffer.NumSamples()); + } + return num_samples; + } + void SetDefaults(); }; diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index 444c8517e..f25341bbb 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -85,10 +85,6 @@ public: return key.mrt_mask; } - bool IsDepthEnabled() const { - return key.depth_stencil.depth_enable.Value(); - } - [[nodiscard]] bool IsPrimitiveListTopology() const { return key.prim_type == AmdGpu::PrimitiveType::PointList || key.prim_type == AmdGpu::PrimitiveType::LineList || diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 4b88bd374..43e02dd9d 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -258,32 +258,28 @@ bool PipelineCache::RefreshGraphicsKey() { auto& key = graphics_key; key.depth_stencil = regs.depth_control; + key.stencil = regs.stencil_control; key.depth_stencil.depth_write_enable.Assign(regs.depth_control.depth_write_enable.Value() && !regs.depth_render_control.depth_clear_enable); key.depth_bias_enable = regs.polygon_control.NeedsBias(); - const auto& db = regs.depth_buffer; - const auto ds_format = instance.GetSupportedFormat( - LiverpoolToVK::DepthFormat(db.z_info.format, db.stencil_info.format), + const auto depth_format = instance.GetSupportedFormat( + LiverpoolToVK::DepthFormat(regs.depth_buffer.z_info.format, + regs.depth_buffer.stencil_info.format), vk::FormatFeatureFlagBits2::eDepthStencilAttachment); - if (db.z_info.format != AmdGpu::Liverpool::DepthBuffer::ZFormat::Invalid) { - key.depth_format = ds_format; + if (regs.depth_buffer.DepthValid()) { + key.depth_format = depth_format; } else { key.depth_format = vk::Format::eUndefined; + key.depth_stencil.depth_enable.Assign(false); } - if (regs.depth_control.depth_enable) { - key.depth_stencil.depth_enable.Assign(key.depth_format != vk::Format::eUndefined); - } - key.stencil = regs.stencil_control; - - if (db.stencil_info.format != AmdGpu::Liverpool::DepthBuffer::StencilFormat::Invalid) { - key.stencil_format = key.depth_format; + if (regs.depth_buffer.StencilValid()) { + key.stencil_format = depth_format; } else { key.stencil_format = vk::Format::eUndefined; + key.depth_stencil.stencil_enable.Assign(false); } - if (key.depth_stencil.stencil_enable) { - key.depth_stencil.stencil_enable.Assign(key.stencil_format != vk::Format::eUndefined); - } + key.prim_type = regs.primitive_type; key.enable_primitive_restart = regs.enable_primitive_restart & 1; key.primitive_restart_index = regs.primitive_restart_index; @@ -291,7 +287,7 @@ bool PipelineCache::RefreshGraphicsKey() { key.cull_mode = regs.polygon_control.CullingMode(); key.clip_space = regs.clipper_control.clip_space; key.front_face = regs.polygon_control.front_face; - key.num_samples = regs.aa_config.NumSamples(); + key.num_samples = regs.NumSamples(); const bool skip_cb_binding = regs.color_control.mode == AmdGpu::Liverpool::ColorControl::OperationMode::Disable; @@ -437,8 +433,6 @@ bool PipelineCache::RefreshGraphicsKey() { } } - u32 num_samples = 1u; - // Second pass to fill remain CB pipeline key data for (auto cb = 0u, remapped_cb = 0u; cb < Liverpool::NumColorBuffers; ++cb) { auto const& col_buf = regs.color_buffers[cb]; @@ -463,15 +457,8 @@ bool PipelineCache::RefreshGraphicsKey() { key.write_masks[remapped_cb] = vk::ColorComponentFlags{regs.color_target_mask.GetMask(cb)}; key.cb_shader_mask.SetMask(remapped_cb, regs.color_shader_mask.GetMask(cb)); ++remapped_cb; - - num_samples = std::max(num_samples, 1u << col_buf.attrib.num_samples_log2); } - // It seems that the number of samples > 1 set in the AA config doesn't mean we're always - // rendering with MSAA, so we need to derive MS ratio from the CB settings. - num_samples = std::max(num_samples, regs.depth_buffer.NumSamples()); - key.num_samples = num_samples; - return true; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index b7cfb8cf0..a0899f7c8 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -87,9 +87,11 @@ RenderState Rasterizer::PrepareRenderState(u32 mrt_mask) { LOG_WARNING(Render_Vulkan, "Color buffers require gamma correction"); } + const bool skip_cb_binding = + regs.color_control.mode == AmdGpu::Liverpool::ColorControl::OperationMode::Disable; for (auto col_buf_id = 0u; col_buf_id < Liverpool::NumColorBuffers; ++col_buf_id) { const auto& col_buf = regs.color_buffers[col_buf_id]; - if (!col_buf) { + if (skip_cb_binding || !col_buf) { continue; } @@ -134,12 +136,8 @@ RenderState Rasterizer::PrepareRenderState(u32 mrt_mask) { }; } - using ZFormat = AmdGpu::Liverpool::DepthBuffer::ZFormat; - using StencilFormat = AmdGpu::Liverpool::DepthBuffer::StencilFormat; - if (regs.depth_buffer.Address() != 0 && - ((regs.depth_control.depth_enable && regs.depth_buffer.z_info.format != ZFormat::Invalid) || - (regs.depth_control.stencil_enable && - regs.depth_buffer.stencil_info.format != StencilFormat::Invalid))) { + if ((regs.depth_control.depth_enable && regs.depth_buffer.DepthValid()) || + (regs.depth_control.stencil_enable && regs.depth_buffer.StencilValid())) { const auto htile_address = regs.depth_htile_data_base.GetAddress(); const auto& hint = liverpool->last_db_extent; auto& [image_id, desc] = @@ -159,25 +157,29 @@ RenderState Rasterizer::PrepareRenderState(u32 mrt_mask) { state.width = std::min(state.width, image.info.size.width); state.height = std::min(state.height, image.info.size.height); - state.depth_attachment = { - .imageView = *image_view.image_view, - .imageLayout = vk::ImageLayout::eUndefined, - .loadOp = is_depth_clear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, - .storeOp = vk::AttachmentStoreOp::eStore, - .clearValue = vk::ClearValue{.depthStencil = {.depth = regs.depth_clear}}, - }; - state.stencil_attachment = { - .imageView = *image_view.image_view, - .imageLayout = vk::ImageLayout::eUndefined, - .loadOp = is_stencil_clear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, - .storeOp = vk::AttachmentStoreOp::eStore, - .clearValue = vk::ClearValue{.depthStencil = {.stencil = regs.stencil_clear}}, - }; + state.has_depth = regs.depth_buffer.DepthValid(); + state.has_stencil = regs.depth_buffer.StencilValid(); + if (state.has_depth) { + state.depth_attachment = { + .imageView = *image_view.image_view, + .imageLayout = vk::ImageLayout::eUndefined, + .loadOp = + is_depth_clear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, + .storeOp = vk::AttachmentStoreOp::eStore, + .clearValue = vk::ClearValue{.depthStencil = {.depth = regs.depth_clear}}, + }; + } + if (state.has_stencil) { + state.stencil_attachment = { + .imageView = *image_view.image_view, + .imageLayout = vk::ImageLayout::eUndefined, + .loadOp = + is_stencil_clear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, + .storeOp = vk::AttachmentStoreOp::eStore, + .clearValue = vk::ClearValue{.depthStencil = {.stencil = regs.stencil_clear}}, + }; + } texture_cache.TouchMeta(htile_address, slice, false); - state.has_depth = - regs.depth_buffer.z_info.format != AmdGpu::Liverpool::DepthBuffer::ZFormat::Invalid; - state.has_stencil = regs.depth_buffer.stencil_info.format != - AmdGpu::Liverpool::DepthBuffer::StencilFormat::Invalid; } return state; @@ -815,34 +817,60 @@ void Rasterizer::Resolve() { mrt1_range.base.layer = liverpool->regs.color_buffers[1].view.slice_start; mrt1_range.extent.layers = liverpool->regs.color_buffers[1].NumSlices() - mrt1_range.base.layer; - vk::ImageResolve region = { - .srcSubresource = - { - .aspectMask = vk::ImageAspectFlagBits::eColor, - .mipLevel = 0, - .baseArrayLayer = mrt0_range.base.layer, - .layerCount = mrt0_range.extent.layers, - }, - .srcOffset = {0, 0, 0}, - .dstSubresource = - { - .aspectMask = vk::ImageAspectFlagBits::eColor, - .mipLevel = 0, - .baseArrayLayer = mrt1_range.base.layer, - .layerCount = mrt1_range.extent.layers, - }, - .dstOffset = {0, 0, 0}, - .extent = {mrt1_image.info.size.width, mrt1_image.info.size.height, 1}, - }; - mrt0_image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, mrt0_range); mrt1_image.Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite, mrt1_range); - cmdbuf.resolveImage(mrt0_image.image, vk::ImageLayout::eTransferSrcOptimal, mrt1_image.image, - vk::ImageLayout::eTransferDstOptimal, region); + if (mrt0_image.info.num_samples == 1) { + // Vulkan does not allow resolve from a single sample image, so change it to a copy. + // Note that resolving a single-sampled image doesn't really make sense, but a game might do + // it. + vk::ImageCopy region = { + .srcSubresource = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .mipLevel = 0, + .baseArrayLayer = mrt0_range.base.layer, + .layerCount = mrt0_range.extent.layers, + }, + .srcOffset = {0, 0, 0}, + .dstSubresource = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .mipLevel = 0, + .baseArrayLayer = mrt1_range.base.layer, + .layerCount = mrt1_range.extent.layers, + }, + .dstOffset = {0, 0, 0}, + .extent = {mrt1_image.info.size.width, mrt1_image.info.size.height, 1}, + }; + cmdbuf.copyImage(mrt0_image.image, vk::ImageLayout::eTransferSrcOptimal, mrt1_image.image, + vk::ImageLayout::eTransferDstOptimal, region); + } else { + vk::ImageResolve region = { + .srcSubresource = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .mipLevel = 0, + .baseArrayLayer = mrt0_range.base.layer, + .layerCount = mrt0_range.extent.layers, + }, + .srcOffset = {0, 0, 0}, + .dstSubresource = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .mipLevel = 0, + .baseArrayLayer = mrt1_range.base.layer, + .layerCount = mrt1_range.extent.layers, + }, + .dstOffset = {0, 0, 0}, + .extent = {mrt1_image.info.size.width, mrt1_image.info.size.height, 1}, + }; + cmdbuf.resolveImage(mrt0_image.image, vk::ImageLayout::eTransferSrcOptimal, + mrt1_image.image, vk::ImageLayout::eTransferDstOptimal, region); + } } void Rasterizer::InlineData(VAddr address, const void* value, u32 num_bytes, bool is_gds) { @@ -998,6 +1026,10 @@ void Rasterizer::UpdateViewportScissorState() { enable_offset ? regs.window_offset.window_y_offset : 0); for (u32 idx = 0; idx < Liverpool::NumViewports; idx++) { + if (regs.viewports[idx].xscale == 0) { + // Scissor and viewport counts should be equal. + continue; + } auto vp_scsr = scsr; if (regs.mode_control.vport_scissor_enable) { vp_scsr.top_left_x = @@ -1020,13 +1052,6 @@ void Rasterizer::UpdateViewportScissorState() { cmdbuf.setScissor(0, scissors); } -void Rasterizer::UpdateDepthStencilState() { - auto& depth = liverpool->regs.depth_control; - - const auto cmdbuf = scheduler.CommandBuffer(); - cmdbuf.setDepthBoundsTestEnable(depth.depth_bounds_enable); -} - void Rasterizer::ScopeMarkerBegin(const std::string_view& str) { if (Config::nullGpu() || !Config::vkMarkersEnabled()) { return; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index ec1b5e134..80b22c7d8 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -74,7 +74,6 @@ private: void UpdateDynamicState(const GraphicsPipeline& pipeline); void UpdateViewportScissorState(); - void UpdateDepthStencilState(); bool FilterDraw(); diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 81415f8b5..f6b0edda4 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -30,7 +30,7 @@ void Scheduler::BeginRendering(const RenderState& new_state) { is_rendering = true; render_state = new_state; - const auto witdh = + const auto width = render_state.width != std::numeric_limits::max() ? render_state.width : 1; const auto height = render_state.height != std::numeric_limits::max() ? render_state.height : 1; @@ -39,7 +39,7 @@ void Scheduler::BeginRendering(const RenderState& new_state) { .renderArea = { .offset = {0, 0}, - .extent = {witdh, height}, + .extent = {width, height}, }, .layerCount = 1, .colorAttachmentCount = render_state.num_color_attachments, diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index 1445d41cd..606ede558 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -266,7 +266,7 @@ ImageInfo::ImageInfo(const AmdGpu::Liverpool::ColorBuffer& buffer, props.is_tiled = buffer.IsTiled(); tiling_mode = buffer.GetTilingMode(); pixel_format = LiverpoolToVK::SurfaceFormat(buffer.info.format, buffer.NumFormat()); - num_samples = 1 << buffer.attrib.num_fragments_log2; + num_samples = buffer.NumSamples(); num_bits = NumBits(buffer.info.format); type = vk::ImageType::e2D; size.width = hint.Valid() ? hint.width : buffer.Pitch(); @@ -289,7 +289,7 @@ ImageInfo::ImageInfo(const AmdGpu::Liverpool::DepthBuffer& buffer, u32 num_slice props.is_tiled = false; pixel_format = LiverpoolToVK::DepthFormat(buffer.z_info.format, buffer.stencil_info.format); type = vk::ImageType::e2D; - num_samples = 1 << buffer.z_info.num_samples; // spec doesn't say it is a log2 + num_samples = buffer.NumSamples(); num_bits = buffer.NumBits(); size.width = hint.Valid() ? hint.width : buffer.Pitch(); size.height = hint.Valid() ? hint.height : buffer.Height(); From ab5240d8d2e452ac4e0a434b353080a5c3a7e963 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sun, 22 Dec 2024 06:31:59 -0800 Subject: [PATCH 225/549] qt: Do not run emulator on separate thread on Mac. (#1849) --- src/qt_gui/main_window.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 39f7c7b97..adb42fc26 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -1098,10 +1098,16 @@ void MainWindow::StartEmulator(std::filesystem::path path) { QMessageBox::critical(nullptr, tr("Run Game"), QString(tr("Game is already running!"))); return; } + isGameRunning = true; +#ifdef __APPLE__ + // SDL on macOS requires main thread. + Core::Emulator emulator; + emulator.Run(path); +#else std::thread emulator_thread([=] { Core::Emulator emulator; emulator.Run(path); }); emulator_thread.detach(); - isGameRunning = true; +#endif } From 8a409d86d4c34ff6e07f5f107ce55ed68e12f2ba Mon Sep 17 00:00:00 2001 From: setepenre <61476054+setepenre@users.noreply.github.com> Date: Sun, 22 Dec 2024 16:18:07 +0100 Subject: [PATCH 226/549] post-processing: rework gamma correction (#1756) --- src/video_core/host_shaders/post_process.frag | 8 +++++++- src/video_core/renderer_vulkan/vk_presenter.cpp | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/video_core/host_shaders/post_process.frag b/src/video_core/host_shaders/post_process.frag index fcced3232..d501e9813 100644 --- a/src/video_core/host_shaders/post_process.frag +++ b/src/video_core/host_shaders/post_process.frag @@ -12,8 +12,14 @@ layout(push_constant) uniform settings { float gamma; } pp; +const float cutoff = 0.0031308, a = 1.055, b = 0.055, d = 12.92; +vec3 gamma(vec3 rgb) +{ + return mix(a * pow(rgb, vec3(1.0 / (2.4 + 1.0 - pp.gamma))) - b, d * rgb / pp.gamma, lessThan(rgb, vec3(cutoff))); +} + void main() { vec4 color_linear = texture(texSampler, uv); - color = pow(color_linear, vec4(1.0/(2.2 + 1.0 - pp.gamma))); + color = vec4(gamma(color_linear.rgb), color_linear.a); } diff --git a/src/video_core/renderer_vulkan/vk_presenter.cpp b/src/video_core/renderer_vulkan/vk_presenter.cpp index b7d829316..139fd962d 100644 --- a/src/video_core/renderer_vulkan/vk_presenter.cpp +++ b/src/video_core/renderer_vulkan/vk_presenter.cpp @@ -154,7 +154,7 @@ void Presenter::CreatePostProcessPipeline() { const auto& fs_module = Vulkan::Compile(pp_shaders[1], vk::ShaderStageFlagBits::eFragment, instance.GetDevice()); ASSERT(fs_module); - Vulkan::SetObjectName(instance.GetDevice(), vs_module, "post_process.frag"); + Vulkan::SetObjectName(instance.GetDevice(), fs_module, "post_process.frag"); const std::array shaders_ci{ vk::PipelineShaderStageCreateInfo{ From 7fe4df85abb23a0f9960ed6bdb74dfaa6a71f085 Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Sun, 22 Dec 2024 20:12:43 +0300 Subject: [PATCH 227/549] Clear color attachment if FCE was invoked before any draws (#1851) * Clear RT if FCE was invoked before any draws Co-authored-by: psucien * address review comments --------- Co-authored-by: psucien --- .../renderer_vulkan/vk_rasterizer.cpp | 31 ++++++++++++++++++- .../renderer_vulkan/vk_rasterizer.h | 1 + 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index a0899f7c8..ec099b9f6 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -52,7 +52,8 @@ bool Rasterizer::FilterDraw() { // There are several cases (e.g. FCE, FMask/HTile decompression) where we don't need to do an // actual draw hence can skip pipeline creation. if (regs.color_control.mode == Liverpool::ColorControl::OperationMode::EliminateFastClear) { - LOG_TRACE(Render_Vulkan, "FCE pass skipped"); + // Clears the render target if FCE is launched before any draws + EliminateFastClear(); return false; } if (regs.color_control.mode == Liverpool::ColorControl::OperationMode::FmaskDecompress) { @@ -201,6 +202,34 @@ RenderState Rasterizer::PrepareRenderState(u32 mrt_mask) { return {vertex_offset, instance_offset}; } +void Rasterizer::EliminateFastClear() { + auto& col_buf = liverpool->regs.color_buffers[0]; + if (!col_buf || !col_buf.info.fast_clear) { + return; + } + if (!texture_cache.IsMetaCleared(col_buf.CmaskAddress(), col_buf.view.slice_start)) { + return; + } + for (u32 slice = col_buf.view.slice_start; slice <= col_buf.view.slice_max; ++slice) { + texture_cache.TouchMeta(col_buf.CmaskAddress(), slice, false); + } + const auto& hint = liverpool->last_cb_extent[0]; + VideoCore::TextureCache::RenderTargetDesc desc(col_buf, hint); + const auto& image_view = texture_cache.FindRenderTarget(desc); + const auto& image = texture_cache.GetImage(image_view.image_id); + const vk::ImageSubresourceRange range = { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = col_buf.view.slice_start, + .layerCount = col_buf.view.slice_max - col_buf.view.slice_start + 1, + }; + scheduler.EndRendering(); + scheduler.CommandBuffer().clearColorImage(image.image, vk::ImageLayout::eColorAttachmentOptimal, + LiverpoolToVK::ColorBufferClearValue(col_buf).color, + range); +} + void Rasterizer::Draw(bool is_indexed, u32 index_offset) { RENDERER_TRACE; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 80b22c7d8..1bbb90b6c 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -71,6 +71,7 @@ private: RenderState PrepareRenderState(u32 mrt_mask); void BeginRendering(const GraphicsPipeline& pipeline, RenderState& state); void Resolve(); + void EliminateFastClear(); void UpdateDynamicState(const GraphicsPipeline& pipeline); void UpdateViewportScissorState(); From 8abc43a03decaba8b82b5d507eec22fc6c864f00 Mon Sep 17 00:00:00 2001 From: psucien <168137814+psucien@users.noreply.github.com> Date: Sun, 22 Dec 2024 19:43:44 +0100 Subject: [PATCH 228/549] texture_cache: 32bpp and 64bpp macro detilers (#1852) * added 32bpp macro detiler * added 64bpp macro detiler * consider 3d depth alignment in size calculations --- src/video_core/amdgpu/resource.h | 6 +- src/video_core/host_shaders/CMakeLists.txt | 2 + src/video_core/host_shaders/detile_m32x1.comp | 1 + src/video_core/host_shaders/detile_m32x2.comp | 1 + src/video_core/host_shaders/detile_m32x4.comp | 1 + src/video_core/host_shaders/detile_m8x1.comp | 1 + src/video_core/host_shaders/detile_m8x2.comp | 1 + .../host_shaders/detile_macro32x1.comp | 90 ++++++++ .../host_shaders/detile_macro32x2.comp | 91 ++++++++ src/video_core/texture_cache/image_info.cpp | 3 + src/video_core/texture_cache/tile_manager.cpp | 201 ++++-------------- src/video_core/texture_cache/tile_manager.h | 9 +- 12 files changed, 235 insertions(+), 172 deletions(-) create mode 100644 src/video_core/host_shaders/detile_macro32x1.comp create mode 100644 src/video_core/host_shaders/detile_macro32x2.comp diff --git a/src/video_core/amdgpu/resource.h b/src/video_core/amdgpu/resource.h index 5d7417559..d9a8b7cac 100644 --- a/src/video_core/amdgpu/resource.h +++ b/src/video_core/amdgpu/resource.h @@ -126,6 +126,7 @@ enum class TilingMode : u32 { Display_MacroTiled = 0xAu, Texture_MicroTiled = 0xDu, Texture_MacroTiled = 0xEu, + Texture_Volume = 0x13u, }; constexpr std::string_view NameOf(TilingMode type) { @@ -140,6 +141,8 @@ constexpr std::string_view NameOf(TilingMode type) { return "Texture_MicroTiled"; case TilingMode::Texture_MacroTiled: return "Texture_MacroTiled"; + case TilingMode::Texture_Volume: + return "Texture_Volume"; default: return "Unknown"; } @@ -294,9 +297,6 @@ struct Image { return tiling_index == 5 ? TilingMode::Texture_MicroTiled : TilingMode::Depth_MacroTiled; } - if (tiling_index == 0x13) { - return TilingMode::Texture_MicroTiled; - } return static_cast(tiling_index); } diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index 4ef8bcdba..c2a3b53fd 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -7,6 +7,8 @@ set(SHADER_FILES detile_m32x1.comp detile_m32x2.comp detile_m32x4.comp + detile_macro32x1.comp + detile_macro32x2.comp fs_tri.vert post_process.frag ) diff --git a/src/video_core/host_shaders/detile_m32x1.comp b/src/video_core/host_shaders/detile_m32x1.comp index fecea109b..802f5f531 100644 --- a/src/video_core/host_shaders/detile_m32x1.comp +++ b/src/video_core/host_shaders/detile_m32x1.comp @@ -15,6 +15,7 @@ layout(std430, binding = 1) buffer output_buf { layout(push_constant) uniform image_info { uint num_levels; uint pitch; + uint height; uint sizes[14]; } info; diff --git a/src/video_core/host_shaders/detile_m32x2.comp b/src/video_core/host_shaders/detile_m32x2.comp index c2caa62c2..90063a185 100644 --- a/src/video_core/host_shaders/detile_m32x2.comp +++ b/src/video_core/host_shaders/detile_m32x2.comp @@ -15,6 +15,7 @@ layout(std430, binding = 1) buffer output_buf { layout(push_constant) uniform image_info { uint num_levels; uint pitch; + uint height; uint sizes[14]; } info; diff --git a/src/video_core/host_shaders/detile_m32x4.comp b/src/video_core/host_shaders/detile_m32x4.comp index 113538706..e1b988172 100644 --- a/src/video_core/host_shaders/detile_m32x4.comp +++ b/src/video_core/host_shaders/detile_m32x4.comp @@ -15,6 +15,7 @@ layout(std430, binding = 1) buffer output_buf { layout(push_constant) uniform image_info { uint num_levels; uint pitch; + uint height; uint sizes[14]; } info; diff --git a/src/video_core/host_shaders/detile_m8x1.comp b/src/video_core/host_shaders/detile_m8x1.comp index 3ca2e64bd..39d0aaeb1 100644 --- a/src/video_core/host_shaders/detile_m8x1.comp +++ b/src/video_core/host_shaders/detile_m8x1.comp @@ -18,6 +18,7 @@ layout(std430, binding = 1) buffer output_buf { layout(push_constant) uniform image_info { uint num_levels; uint pitch; + uint height; uint sizes[14]; } info; diff --git a/src/video_core/host_shaders/detile_m8x2.comp b/src/video_core/host_shaders/detile_m8x2.comp index ee9b72810..3f8e5ab33 100644 --- a/src/video_core/host_shaders/detile_m8x2.comp +++ b/src/video_core/host_shaders/detile_m8x2.comp @@ -17,6 +17,7 @@ layout(std430, binding = 1) buffer output_buf { layout(push_constant) uniform image_info { uint num_levels; uint pitch; + uint height; uint sizes[14]; } info; diff --git a/src/video_core/host_shaders/detile_macro32x1.comp b/src/video_core/host_shaders/detile_macro32x1.comp new file mode 100644 index 000000000..086fbcfb5 --- /dev/null +++ b/src/video_core/host_shaders/detile_macro32x1.comp @@ -0,0 +1,90 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#version 450 + +layout (local_size_x = 64, local_size_y = 1, local_size_z = 1) in; + +layout(std430, binding = 0) buffer input_buf { + uint in_data[]; +}; +layout(std430, binding = 1) buffer output_buf { + uint out_data[]; +}; + +layout(push_constant) uniform image_info { + uint num_levels; + uint pitch; + uint height; + uint c0; + uint c1; +} info; + +// Each LUT is 64 bytes, so should fit into K$ given tiled slices locality +const uint lut_32bpp[][64] = { + { + 0x00, 0x01, 0x04, 0x05, 0x40, 0x41, 0x44, 0x45, + 0x02, 0x03, 0x06, 0x07, 0x42, 0x43, 0x46, 0x47, + 0x10, 0x11, 0x14, 0x15, 0x50, 0x51, 0x54, 0x55, + 0x12, 0x13, 0x16, 0x17, 0x52, 0x53, 0x56, 0x57, + 0x80, 0x81, 0x84, 0x85, 0xc0, 0xc1, 0xc4, 0xc5, + 0x82, 0x83, 0x86, 0x87, 0xc2, 0xc3, 0xc6, 0xc7, + 0x90, 0x91, 0x94, 0x95, 0xd0, 0xd1, 0xd4, 0xd5, + 0x92, 0x93, 0x96, 0x97, 0xd2, 0xd3, 0xd6, 0xd7, + }, + { + 0x08, 0x09, 0x0c, 0x0d, 0x48, 0x49, 0x4c, 0x4d, + 0x0a, 0x0b, 0x0e, 0x0f, 0x4a, 0x4b, 0x4e, 0x4f, + 0x18, 0x19, 0x1c, 0x1d, 0x58, 0x59, 0x5c, 0x5d, + 0x1a, 0x1b, 0x1e, 0x1f, 0x5a, 0x5b, 0x5e, 0x5f, + 0x88, 0x89, 0x8c, 0x8d, 0xc8, 0xc9, 0xcc, 0xcd, + 0x8a, 0x8b, 0x8e, 0x8f, 0xca, 0xcb, 0xce, 0xcf, + 0x98, 0x99, 0x9c, 0x9d, 0xd8, 0xd9, 0xdc, 0xdd, + 0x9a, 0x9b, 0x9e, 0x9f, 0xda, 0xdb, 0xde, 0xdf, + }, + { + 0x20, 0x21, 0x24, 0x25, 0x60, 0x61, 0x64, 0x65, + 0x22, 0x23, 0x26, 0x27, 0x62, 0x63, 0x66, 0x67, + 0x30, 0x31, 0x34, 0x35, 0x70, 0x71, 0x74, 0x75, + 0x32, 0x33, 0x36, 0x37, 0x72, 0x73, 0x76, 0x77, + 0xa0, 0xa1, 0xa4, 0xa5, 0xe0, 0xe1, 0xe4, 0xe5, + 0xa2, 0xa3, 0xa6, 0xa7, 0xe2, 0xe3, 0xe6, 0xe7, + 0xb0, 0xb1, 0xb4, 0xb5, 0xf0, 0xf1, 0xf4, 0xf5, + 0xb2, 0xb3, 0xb6, 0xb7, 0xf2, 0xf3, 0xf6, 0xf7, + }, + { + 0x28, 0x29, 0x2c, 0x2d, 0x68, 0x69, 0x6c, 0x6d, + 0x2a, 0x2b, 0x2e, 0x2f, 0x6a, 0x6b, 0x6e, 0x6f, + 0x38, 0x39, 0x3c, 0x3d, 0x78, 0x79, 0x7c, 0x7d, + 0x3a, 0x3b, 0x3e, 0x3f, 0x7a, 0x7b, 0x7e, 0x7f, + 0xa8, 0xa9, 0xac, 0xad, 0xe8, 0xe9, 0xec, 0xed, + 0xaa, 0xab, 0xae, 0xaf, 0xea, 0xeb, 0xee, 0xef, + 0xb8, 0xb9, 0xbc, 0xbd, 0xf8, 0xf9, 0xfc, 0xfd, + 0xba, 0xbb, 0xbe, 0xbf, 0xfa, 0xfb, 0xfe, 0xff, + } +}; + +#define MICRO_TILE_DIM (8) +#define MICRO_TILE_SZ (1024) +#define TEXELS_PER_ELEMENT (1) +#define BPP (32) + +void main() { + uint x = gl_GlobalInvocationID.x % info.pitch; + uint y = (gl_GlobalInvocationID.x / info.pitch) % info.height; + uint z = gl_GlobalInvocationID.x / (info.pitch * info.height); + + uint col = bitfieldExtract(x, 0, 3); + uint row = bitfieldExtract(y, 0, 3); + uint lut = bitfieldExtract(z, 0, 2); + uint idx = lut_32bpp[lut][col + row * MICRO_TILE_DIM]; + + uint slice_offs = (z >> 2u) * info.c1 * MICRO_TILE_SZ; + uint tile_row = y / MICRO_TILE_DIM; + uint tile_column = x / MICRO_TILE_DIM; + uint tile_offs = ((tile_row * info.c0) + tile_column) * MICRO_TILE_SZ; + uint offs = slice_offs + tile_offs + (idx * BPP / 8); + + uint p0 = in_data[offs >> 2u]; + out_data[gl_GlobalInvocationID.x] = p0; +} diff --git a/src/video_core/host_shaders/detile_macro32x2.comp b/src/video_core/host_shaders/detile_macro32x2.comp new file mode 100644 index 000000000..296311c7a --- /dev/null +++ b/src/video_core/host_shaders/detile_macro32x2.comp @@ -0,0 +1,91 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#version 450 + +layout (local_size_x = 64, local_size_y = 1, local_size_z = 1) in; + +layout(std430, binding = 0) buffer input_buf { + uint in_data[]; +}; +layout(std430, binding = 1) buffer output_buf { + uint out_data[]; +}; + +layout(push_constant) uniform image_info { + uint num_levels; + uint pitch; + uint height; + uint c0; + uint c1; +} info; + +const uint lut_64bpp[][64] = { + { + 0x00, 0x01, 0x08, 0x09, 0x40, 0x41, 0x48, 0x49, + 0x02, 0x03, 0x0a, 0x0b, 0x42, 0x43, 0x4a, 0x4b, + 0x10, 0x11, 0x18, 0x19, 0x50, 0x51, 0x58, 0x59, + 0x12, 0x13, 0x1a, 0x1b, 0x52, 0x53, 0x5a, 0x5b, + 0x80, 0x81, 0x88, 0x89, 0xc0, 0xc1, 0xc8, 0xc9, + 0x82, 0x83, 0x8a, 0x8b, 0xc2, 0xc3, 0xca, 0xcb, + 0x90, 0x91, 0x98, 0x99, 0xd0, 0xd1, 0xd8, 0xd9, + 0x92, 0x93, 0x9a, 0x9b, 0xd2, 0xd3, 0xda, 0xdb, + }, + { + 0x04, 0x05, 0x0c, 0x0d, 0x44, 0x45, 0x4c, 0x4d, + 0x06, 0x07, 0x0e, 0x0f, 0x46, 0x47, 0x4e, 0x4f, + 0x14, 0x15, 0x1c, 0x1d, 0x54, 0x55, 0x5c, 0x5d, + 0x16, 0x17, 0x1e, 0x1f, 0x56, 0x57, 0x5e, 0x5f, + 0x84, 0x85, 0x8c, 0x8d, 0xc4, 0xc5, 0xcc, 0xcd, + 0x86, 0x87, 0x8e, 0x8f, 0xc6, 0xc7, 0xce, 0xcf, + 0x94, 0x95, 0x9c, 0x9d, 0xd4, 0xd5, 0xdc, 0xdd, + 0x96, 0x97, 0x9e, 0x9f, 0xd6, 0xd7, 0xde, 0xdf, + }, + { + 0x20, 0x21, 0x28, 0x29, 0x60, 0x61, 0x68, 0x69, + 0x22, 0x23, 0x2a, 0x2b, 0x62, 0x63, 0x6a, 0x6b, + 0x30, 0x31, 0x38, 0x39, 0x70, 0x71, 0x78, 0x79, + 0x32, 0x33, 0x3a, 0x3b, 0x72, 0x73, 0x7a, 0x7b, + 0xa0, 0xa1, 0xa8, 0xa9, 0xe0, 0xe1, 0xe8, 0xe9, + 0xa2, 0xa3, 0xaa, 0xab, 0xe2, 0xe3, 0xea, 0xeb, + 0xb0, 0xb1, 0xb8, 0xb9, 0xf0, 0xf1, 0xf8, 0xf9, + 0xb2, 0xb3, 0xba, 0xbb, 0xf2, 0xf3, 0xfa, 0xfb, + }, + { + 0x24, 0x25, 0x2c, 0x2d, 0x64, 0x65, 0x6c, 0x6d, + 0x26, 0x27, 0x2e, 0x2f, 0x66, 0x67, 0x6e, 0x6f, + 0x34, 0x35, 0x3c, 0x3d, 0x74, 0x75, 0x7c, 0x7d, + 0x36, 0x37, 0x3e, 0x3f, 0x76, 0x77, 0x7e, 0x7f, + 0xa4, 0xa5, 0xac, 0xad, 0xe4, 0xe5, 0xec, 0xed, + 0xa6, 0xa7, 0xae, 0xaf, 0xe6, 0xe7, 0xee, 0xef, + 0xb4, 0xb5, 0xbc, 0xbd, 0xf4, 0xf5, 0xfc, 0xfd, + 0xb6, 0xb7, 0xbe, 0xbf, 0xf6, 0xf7, 0xfe, 0xff, + }, +}; + +#define MICRO_TILE_DIM (8) +#define MICRO_TILE_SZ (2048) +#define TEXELS_PER_ELEMENT (1) +#define BPP (64) + +void main() { + uint x = gl_GlobalInvocationID.x % info.pitch; + uint y = (gl_GlobalInvocationID.x / info.pitch) % info.height; + uint z = gl_GlobalInvocationID.x / (info.pitch * info.height); + + uint col = bitfieldExtract(x, 0, 3); + uint row = bitfieldExtract(y, 0, 3); + uint lut = bitfieldExtract(z, 0, 2); + uint idx = lut_64bpp[lut][col + row * MICRO_TILE_DIM]; + + uint slice_offs = (z >> 2u) * info.c1 * MICRO_TILE_SZ; + uint tile_row = y / MICRO_TILE_DIM; + uint tile_column = x / MICRO_TILE_DIM; + uint tile_offs = ((tile_row * info.c0) + tile_column) * MICRO_TILE_SZ; + uint offs = slice_offs + tile_offs + (idx * BPP / 8); + + uint p0 = in_data[(offs >> 2) + 0]; + uint p1 = in_data[(offs >> 2) + 1]; + out_data[2 * gl_GlobalInvocationID.x + 0] = p0; + out_data[2 * gl_GlobalInvocationID.x + 1] = p1; +} diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index 606ede558..2cc4aab38 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -366,6 +366,9 @@ void ImageInfo::UpdateSize() { mip_info.height = mip_h; break; } + case AmdGpu::TilingMode::Texture_Volume: + mip_d += (-mip_d) & 3u; + [[fallthrough]]; case AmdGpu::TilingMode::Texture_MicroTiled: { std::tie(mip_info.pitch, mip_info.size) = ImageSizeMicroTiled(mip_w, mip_h, bpp, num_samples); diff --git a/src/video_core/texture_cache/tile_manager.cpp b/src/video_core/texture_cache/tile_manager.cpp index 94d37c993..6a51bae2c 100644 --- a/src/video_core/texture_cache/tile_manager.cpp +++ b/src/video_core/texture_cache/tile_manager.cpp @@ -12,6 +12,8 @@ #include "video_core/host_shaders/detile_m32x4_comp.h" #include "video_core/host_shaders/detile_m8x1_comp.h" #include "video_core/host_shaders/detile_m8x2_comp.h" +#include "video_core/host_shaders/detile_macro32x1_comp.h" +#include "video_core/host_shaders/detile_macro32x2_comp.h" #include #include @@ -19,158 +21,7 @@ namespace VideoCore { -class TileManager32 { -public: - u32 m_macro_tile_height = 0; - u32 m_bank_height = 0; - u32 m_num_banks = 0; - u32 m_num_pipes = 0; - u32 m_padded_width = 0; - u32 m_padded_height = 0; - u32 m_pipe_bits = 0; - u32 m_bank_bits = 0; - - void Init(u32 width, u32 height, bool is_neo) { - m_macro_tile_height = (is_neo ? 128 : 64); - m_bank_height = is_neo ? 2 : 1; - m_num_banks = is_neo ? 8 : 16; - m_num_pipes = is_neo ? 16 : 8; - m_padded_width = width; - if (height == 1080) { - m_padded_height = is_neo ? 1152 : 1088; - } - if (height == 720) { - m_padded_height = 768; - } - m_pipe_bits = is_neo ? 4 : 3; - m_bank_bits = is_neo ? 3 : 4; - } - - static u32 getElementIdx(u32 x, u32 y) { - u32 elem = 0; - elem |= ((x >> 0u) & 0x1u) << 0u; - elem |= ((x >> 1u) & 0x1u) << 1u; - elem |= ((y >> 0u) & 0x1u) << 2u; - elem |= ((x >> 2u) & 0x1u) << 3u; - elem |= ((y >> 1u) & 0x1u) << 4u; - elem |= ((y >> 2u) & 0x1u) << 5u; - - return elem; - } - - static u32 getPipeIdx(u32 x, u32 y, bool is_neo) { - u32 pipe = 0; - - if (!is_neo) { - pipe |= (((x >> 3u) ^ (y >> 3u) ^ (x >> 4u)) & 0x1u) << 0u; - pipe |= (((x >> 4u) ^ (y >> 4u)) & 0x1u) << 1u; - pipe |= (((x >> 5u) ^ (y >> 5u)) & 0x1u) << 2u; - } else { - pipe |= (((x >> 3u) ^ (y >> 3u) ^ (x >> 4u)) & 0x1u) << 0u; - pipe |= (((x >> 4u) ^ (y >> 4u)) & 0x1u) << 1u; - pipe |= (((x >> 5u) ^ (y >> 5u)) & 0x1u) << 2u; - pipe |= (((x >> 6u) ^ (y >> 5u)) & 0x1u) << 3u; - } - - return pipe; - } - - static u32 getBankIdx(u32 x, u32 y, u32 bank_width, u32 bank_height, u32 num_banks, - u32 num_pipes) { - const u32 x_shift_offset = std::bit_width(bank_width * num_pipes) - 1; - const u32 y_shift_offset = std::bit_width(bank_height) - 1; - const u32 xs = x >> x_shift_offset; - const u32 ys = y >> y_shift_offset; - u32 bank = 0; - switch (num_banks) { - case 8: - bank |= (((xs >> 3u) ^ (ys >> 5u)) & 0x1u) << 0u; - bank |= (((xs >> 4u) ^ (ys >> 4u) ^ (ys >> 5u)) & 0x1u) << 1u; - bank |= (((xs >> 5u) ^ (ys >> 3u)) & 0x1u) << 2u; - break; - case 16: - bank |= (((xs >> 3u) ^ (ys >> 6u)) & 0x1u) << 0u; - bank |= (((xs >> 4u) ^ (ys >> 5u) ^ (ys >> 6u)) & 0x1u) << 1u; - bank |= (((xs >> 5u) ^ (ys >> 4u)) & 0x1u) << 2u; - bank |= (((xs >> 6u) ^ (ys >> 3u)) & 0x1u) << 3u; - break; - default:; - } - - return bank; - } - - u64 getTiledOffs(u32 x, u32 y, bool is_neo) const { - u64 element_index = getElementIdx(x, y); - - u32 xh = x; - u32 yh = y; - u64 pipe = getPipeIdx(xh, yh, is_neo); - u64 bank = getBankIdx(xh, yh, 1, m_bank_height, m_num_banks, m_num_pipes); - u32 tile_bytes = (8 * 8 * 32 + 7) / 8; - u64 element_offset = (element_index * 32); - u64 tile_split_slice = 0; - - if (tile_bytes > 512) { - tile_split_slice = element_offset / (static_cast(512) * 8); - element_offset %= (static_cast(512) * 8); - tile_bytes = 512; - } - - u64 macro_tile_bytes = - (128 / 8) * (m_macro_tile_height / 8) * tile_bytes / (m_num_pipes * m_num_banks); - u64 macro_tiles_per_row = m_padded_width / 128; - u64 macro_tile_row_index = y / m_macro_tile_height; - u64 macro_tile_column_index = x / 128; - u64 macro_tile_index = - (macro_tile_row_index * macro_tiles_per_row) + macro_tile_column_index; - u64 macro_tile_offset = macro_tile_index * macro_tile_bytes; - u64 macro_tiles_per_slice = macro_tiles_per_row * (m_padded_height / m_macro_tile_height); - u64 slice_bytes = macro_tiles_per_slice * macro_tile_bytes; - u64 slice_offset = tile_split_slice * slice_bytes; - u64 tile_row_index = (y / 8) % m_bank_height; - u64 tile_index = tile_row_index; - u64 tile_offset = tile_index * tile_bytes; - - u64 tile_split_slice_rotation = ((m_num_banks / 2) + 1) * tile_split_slice; - bank ^= tile_split_slice_rotation; - bank &= (m_num_banks - 1); - - u64 total_offset = (slice_offset + macro_tile_offset + tile_offset) * 8 + element_offset; - u64 bit_offset = total_offset & 0x7u; - total_offset /= 8; - - u64 pipe_interleave_offset = total_offset & 0xffu; - u64 offset = total_offset >> 8u; - u64 byte_offset = pipe_interleave_offset | (pipe << (8u)) | (bank << (8u + m_pipe_bits)) | - (offset << (8u + m_pipe_bits + m_bank_bits)); - - return ((byte_offset << 3u) | bit_offset) / 8; - } -}; - -void ConvertTileToLinear(u8* dst, const u8* src, u32 width, u32 height, bool is_neo) { - TileManager32 t; - t.Init(width, height, is_neo); - - for (u32 y = 0; y < height; y++) { - u32 x = 0; - u64 linear_offset = y * width * 4; - - for (; x + 1 < width; x += 2) { - auto tiled_offset = t.getTiledOffs(x, y, is_neo); - - std::memcpy(dst + linear_offset, src + tiled_offset, sizeof(u64)); - linear_offset += 8; - } - if (x < width) { - auto tiled_offset = t.getTiledOffs(x, y, is_neo); - std::memcpy(dst + linear_offset, src + tiled_offset, sizeof(u32)); - } - } -} - -vk::Format DemoteImageFormatForDetiling(vk::Format format) { +static vk::Format DemoteImageFormatForDetiling(vk::Format format) { switch (format) { case vk::Format::eR8Uint: case vk::Format::eR8Unorm: @@ -233,7 +84,8 @@ vk::Format DemoteImageFormatForDetiling(vk::Format format) { const DetilerContext* TileManager::GetDetiler(const Image& image) const { const auto format = DemoteImageFormatForDetiling(image.info.pixel_format); - if (image.info.tiling_mode == AmdGpu::TilingMode::Texture_MicroTiled) { + switch (image.info.tiling_mode) { + case AmdGpu::TilingMode::Texture_MicroTiled: switch (format) { case vk::Format::eR8Uint: return &detilers[DetilerType::Micro8x1]; @@ -248,22 +100,35 @@ const DetilerContext* TileManager::GetDetiler(const Image& image) const { default: return nullptr; } + case AmdGpu::TilingMode::Texture_Volume: + switch (format) { + case vk::Format::eR32Uint: + return &detilers[DetilerType::Macro32x1]; + case vk::Format::eR32G32Uint: + return &detilers[DetilerType::Macro32x2]; + default: + return nullptr; + } + break; + default: + return nullptr; } - return nullptr; } struct DetilerParams { u32 num_levels; u32 pitch0; + u32 height; u32 sizes[14]; }; TileManager::TileManager(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler) : instance{instance}, scheduler{scheduler} { static const std::array detiler_shaders{ - HostShaders::DETILE_M8X1_COMP, HostShaders::DETILE_M8X2_COMP, - HostShaders::DETILE_M32X1_COMP, HostShaders::DETILE_M32X2_COMP, - HostShaders::DETILE_M32X4_COMP, + HostShaders::DETILE_M8X1_COMP, HostShaders::DETILE_M8X2_COMP, + HostShaders::DETILE_M32X1_COMP, HostShaders::DETILE_M32X2_COMP, + HostShaders::DETILE_M32X4_COMP, HostShaders::DETILE_MACRO32X1_COMP, + HostShaders::DETILE_MACRO32X2_COMP, }; boost::container::static_vector bindings{ @@ -447,14 +312,24 @@ std::pair TileManager::TryDetile(vk::Buffer in_buffer, u32 in_o set_writes); DetilerParams params; - params.pitch0 = image.info.pitch >> (image.info.props.is_block ? 2u : 0u); params.num_levels = image.info.resources.levels; + params.pitch0 = image.info.pitch >> (image.info.props.is_block ? 2u : 0u); + params.height = image.info.size.height; + if (image.info.tiling_mode == AmdGpu::TilingMode::Texture_Volume) { + ASSERT(image.info.resources.levels == 1); + ASSERT(image.info.num_bits >= 32); + const auto tiles_per_row = image.info.pitch / 8u; + const auto tiles_per_slice = tiles_per_row * ((image.info.size.height + 7u) / 8u); + params.sizes[0] = tiles_per_row; + params.sizes[1] = tiles_per_slice; + } else { - ASSERT(image.info.resources.levels <= 14); - std::memset(¶ms.sizes, 0, sizeof(params.sizes)); - for (int m = 0; m < image.info.resources.levels; ++m) { - params.sizes[m] = image.info.mips_layout[m].size * image.info.resources.layers + - (m > 0 ? params.sizes[m - 1] : 0); + ASSERT(image.info.resources.levels <= 14); + std::memset(¶ms.sizes, 0, sizeof(params.sizes)); + for (int m = 0; m < image.info.resources.levels; ++m) { + params.sizes[m] = image.info.mips_layout[m].size * image.info.resources.layers + + (m > 0 ? params.sizes[m - 1] : 0); + } } cmdbuf.pushConstants(*detiler->pl_layout, vk::ShaderStageFlagBits::eCompute, 0u, sizeof(params), diff --git a/src/video_core/texture_cache/tile_manager.h b/src/video_core/texture_cache/tile_manager.h index ed7e32c44..72860bca0 100644 --- a/src/video_core/texture_cache/tile_manager.h +++ b/src/video_core/texture_cache/tile_manager.h @@ -11,12 +11,6 @@ namespace VideoCore { class TextureCache; -/// Converts tiled texture data to linear format. -void ConvertTileToLinear(u8* dst, const u8* src, u32 width, u32 height, bool neo); - -/// Converts image format to the one used internally by detiler. -vk::Format DemoteImageFormatForDetiling(vk::Format format); - enum DetilerType : u32 { Micro8x1, Micro8x2, @@ -24,6 +18,9 @@ enum DetilerType : u32 { Micro32x2, Micro32x4, + Macro32x1, + Macro32x2, + Max }; From 2dc5755799c92a74fddbe5516decd4cb4346c53b Mon Sep 17 00:00:00 2001 From: psucien Date: Sun, 22 Dec 2024 22:51:48 +0100 Subject: [PATCH 229/549] build: exclude Tracy from release builds --- externals/CMakeLists.txt | 6 +++++- src/common/debug.h | 4 ++++ src/video_core/renderer_vulkan/vk_scheduler.cpp | 8 ++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index dbe6794d8..4350948b7 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -189,7 +189,11 @@ add_library(Dear_ImGui target_include_directories(Dear_ImGui INTERFACE dear_imgui/) # Tracy -option(TRACY_ENABLE "" ON) +if (CMAKE_BUILD_TYPE STREQUAL "Release") + option(TRACY_ENABLE "" OFF) +else() + option(TRACY_ENABLE "" ON) +endif() option(TRACY_NO_CRASH_HANDLER "" ON) # Otherwise texture cache exceptions will be treaten as a crash option(TRACY_ON_DEMAND "" ON) option(TRACY_NO_FRAME_IMAGE "" ON) diff --git a/src/common/debug.h b/src/common/debug.h index 882e9e5c4..92bf72550 100644 --- a/src/common/debug.h +++ b/src/common/debug.h @@ -14,7 +14,11 @@ #include static inline bool IsProfilerConnected() { +#if TRACY_ENABLE return tracy::GetProfiler().IsConnected(); +#else + return false; +#endif } #define TRACY_GPU_ENABLED 0 diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index f6b0edda4..fd84c54ed 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -14,12 +14,16 @@ std::mutex Scheduler::submit_mutex; Scheduler::Scheduler(const Instance& instance) : instance{instance}, master_semaphore{instance}, command_pool{instance, &master_semaphore} { +#if TRACY_GPU_ENABLED profiler_scope = reinterpret_cast(std::malloc(sizeof(tracy::VkCtxScope))); +#endif AllocateWorkerCommandBuffers(); } Scheduler::~Scheduler() { +#if TRACY_GPU_ENABLED std::free(profiler_scope); +#endif } void Scheduler::BeginRendering(const RenderState& new_state) { @@ -93,23 +97,27 @@ void Scheduler::AllocateWorkerCommandBuffers() { ASSERT_MSG(begin_result == vk::Result::eSuccess, "Failed to begin command buffer: {}", vk::to_string(begin_result)); +#if TRACY_GPU_ENABLED auto* profiler_ctx = instance.GetProfilerContext(); if (profiler_ctx) { static const auto scope_loc = GPU_SCOPE_LOCATION("Guest Frame", MarkersPalette::GpuMarkerColor); new (profiler_scope) tracy::VkCtxScope{profiler_ctx, &scope_loc, current_cmdbuf, true}; } +#endif } void Scheduler::SubmitExecution(SubmitInfo& info) { std::scoped_lock lk{submit_mutex}; const u64 signal_value = master_semaphore.NextTick(); +#if TRACY_GPU_ENABLED auto* profiler_ctx = instance.GetProfilerContext(); if (profiler_ctx) { profiler_scope->~VkCtxScope(); TracyVkCollect(profiler_ctx, current_cmdbuf); } +#endif EndRendering(); auto end_result = current_cmdbuf.end(); From 43fd8fa44a4ea7e8bdfba86106e72e57d7e678d5 Mon Sep 17 00:00:00 2001 From: psucien Date: Sun, 22 Dec 2024 23:11:16 +0100 Subject: [PATCH 230/549] hot-fix: macos release build --- src/core/libraries/kernel/equeue.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/libraries/kernel/equeue.cpp b/src/core/libraries/kernel/equeue.cpp index 6543cc319..42a8eed89 100644 --- a/src/core/libraries/kernel/equeue.cpp +++ b/src/core/libraries/kernel/equeue.cpp @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include + #include "common/assert.h" #include "common/debug.h" #include "common/logging/log.h" From dfdd819e3e020104d325aeed435eda7ff1d6cb5c Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Mon, 23 Dec 2024 04:29:30 -0300 Subject: [PATCH 231/549] Fix AutoUpdate Issue with Non-Latin Usernames/Directories (#1853) * Fix updater for non-Latin user directories * QStandardPaths --- src/qt_gui/check_update.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/qt_gui/check_update.cpp b/src/qt_gui/check_update.cpp index d713f67fb..bb07baaf5 100644 --- a/src/qt_gui/check_update.cpp +++ b/src/qt_gui/check_update.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -348,7 +349,9 @@ void CheckUpdate::DownloadUpdate(const QString& url) { QString userPath; Common::FS::PathToQString(userPath, Common::FS::GetUserPath(Common::FS::PathType::UserDir)); #ifdef Q_OS_WIN - QString tempDownloadPath = QString(getenv("LOCALAPPDATA")) + "/Temp/temp_download_update"; + QString tempDownloadPath = + QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + + "/Temp/temp_download_update"; #else QString tempDownloadPath = userPath + "/temp_download_update"; #endif @@ -397,10 +400,11 @@ void CheckUpdate::Install() { QString processCommand; #ifdef Q_OS_WIN - // On windows, overwrite tempDirPath with AppData/Local/Temp folder + // On windows, overwrite tempDirPath with AppData/Roaming/shadps4/Temp folder // due to PowerShell Expand-Archive not being able to handle correctly // paths in square brackets (ie: ./[shadps4]) - tempDirPath = QString(getenv("LOCALAPPDATA")) + "/Temp/temp_download_update"; + tempDirPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + + "/Temp/temp_download_update"; // Windows Batch Script scriptFileName = tempDirPath + "/update.ps1"; @@ -536,6 +540,7 @@ void CheckUpdate::Install() { QFile scriptFile(scriptFileName); if (scriptFile.open(QIODevice::WriteOnly | QIODevice::Text)) { QTextStream out(&scriptFile); + scriptFile.write("\xEF\xBB\xBF"); #ifdef Q_OS_WIN out << scriptContent.arg(binaryStartingUpdate).arg(tempDirPath).arg(rootPath); #endif From 94f861588d2f9905147586e3d863f0f7411c71dc Mon Sep 17 00:00:00 2001 From: Emulator-Team-2 <38318848+Emulator-Team-2@users.noreply.github.com> Date: Mon, 23 Dec 2024 14:52:29 +0100 Subject: [PATCH 232/549] added B5G6R5UnormPack16 format (#1856) --- src/video_core/texture_cache/tile_manager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/video_core/texture_cache/tile_manager.cpp b/src/video_core/texture_cache/tile_manager.cpp index 6a51bae2c..ef80f2418 100644 --- a/src/video_core/texture_cache/tile_manager.cpp +++ b/src/video_core/texture_cache/tile_manager.cpp @@ -27,6 +27,7 @@ static vk::Format DemoteImageFormatForDetiling(vk::Format format) { case vk::Format::eR8Unorm: return vk::Format::eR8Uint; case vk::Format::eR4G4B4A4UnormPack16: + case vk::Format::eB5G6R5UnormPack16: case vk::Format::eR5G5B5A1UnormPack16: case vk::Format::eR8G8Unorm: case vk::Format::eR16Sfloat: From 400da1aa8da376d38b3c4a086bc7f656f590de2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quang=20Ng=C3=B4?= Date: Mon, 23 Dec 2024 21:21:48 +0700 Subject: [PATCH 233/549] Handle swapchain recreation (#1830) --- src/video_core/renderer_vulkan/vk_presenter.cpp | 10 +++++++--- src/video_core/renderer_vulkan/vk_swapchain.cpp | 4 +++- src/video_core/renderer_vulkan/vk_swapchain.h | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_presenter.cpp b/src/video_core/renderer_vulkan/vk_presenter.cpp index 139fd962d..bc55cde23 100644 --- a/src/video_core/renderer_vulkan/vk_presenter.cpp +++ b/src/video_core/renderer_vulkan/vk_presenter.cpp @@ -634,9 +634,11 @@ void Presenter::Present(Frame* frame) { swapchain.Recreate(window.GetWidth(), window.GetHeight()); } - ImGui::Core::NewFrame(); + if (!swapchain.AcquireNextImage()) { + swapchain.Recreate(window.GetWidth(), window.GetHeight()); + } - swapchain.AcquireNextImage(); + ImGui::Core::NewFrame(); const vk::Image swapchain_image = swapchain.Image(); @@ -731,7 +733,9 @@ void Presenter::Present(Frame* frame) { // Present to swapchain. std::scoped_lock submit_lock{Scheduler::submit_mutex}; - swapchain.Present(); + if (!swapchain.Present()) { + swapchain.Recreate(window.GetWidth(), window.GetHeight()); + } // Free the frame for reuse std::scoped_lock fl{free_mutex}; diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index d0bc7ebdc..380660a2f 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -112,7 +112,7 @@ bool Swapchain::AcquireNextImage() { return !needs_recreation; } -void Swapchain::Present() { +bool Swapchain::Present() { const vk::PresentInfoKHR present_info = { .waitSemaphoreCount = 1, @@ -131,6 +131,8 @@ void Swapchain::Present() { } frame_index = (frame_index + 1) % image_count; + + return !needs_recreation; } void Swapchain::FindPresentFormat() { diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h index a41b3ca76..19ae9b2d2 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.h +++ b/src/video_core/renderer_vulkan/vk_swapchain.h @@ -32,7 +32,7 @@ public: bool AcquireNextImage(); /// Presents the current image and move to the next one - void Present(); + bool Present(); vk::SurfaceKHR GetSurface() const { return surface; From c2e9c877dd82e9bf79c8ede7bc8cffb591a31c76 Mon Sep 17 00:00:00 2001 From: psucien Date: Mon, 23 Dec 2024 18:20:37 +0100 Subject: [PATCH 234/549] hot-fix: missing fce barrier --- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index ec099b9f6..417b6d4b6 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -216,7 +216,7 @@ void Rasterizer::EliminateFastClear() { const auto& hint = liverpool->last_cb_extent[0]; VideoCore::TextureCache::RenderTargetDesc desc(col_buf, hint); const auto& image_view = texture_cache.FindRenderTarget(desc); - const auto& image = texture_cache.GetImage(image_view.image_id); + auto& image = texture_cache.GetImage(image_view.image_id); const vk::ImageSubresourceRange range = { .aspectMask = vk::ImageAspectFlagBits::eColor, .baseMipLevel = 0, @@ -225,7 +225,8 @@ void Rasterizer::EliminateFastClear() { .layerCount = col_buf.view.slice_max - col_buf.view.slice_start + 1, }; scheduler.EndRendering(); - scheduler.CommandBuffer().clearColorImage(image.image, vk::ImageLayout::eColorAttachmentOptimal, + image.Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite, {}); + scheduler.CommandBuffer().clearColorImage(image.image, image.last_state.layout, LiverpoolToVK::ColorBufferClearValue(col_buf).color, range); } From 092d42e981f23fd439226ad49fa48739821b254b Mon Sep 17 00:00:00 2001 From: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> Date: Tue, 24 Dec 2024 13:28:47 +0200 Subject: [PATCH 235/549] renderer_vulkan: Implement rectlist emulation with tessellation (#1857) * renderer_vulkan: Implement rectlist emulation with tessellation * clang format * renderer_vulkan: Use tessellation for quad primitive as well * vk_rasterizer: Handle viewport enable flags * review * shader_recompiler: Fix quad/rect list FS passthrough semantics. * spirv: Bump to 1.5 * remove pragma --------- Co-authored-by: squidbus <175574877+squidbus@users.noreply.github.com> --- CMakeLists.txt | 2 + src/common/io_file.h | 2 +- .../backend/spirv/emit_spirv.cpp | 1 + .../backend/spirv/emit_spirv_quad_rect.cpp | 329 ++++++++++++++++++ .../backend/spirv/emit_spirv_quad_rect.h | 24 ++ src/shader_recompiler/runtime_info.h | 2 +- src/video_core/buffer_cache/buffer_cache.cpp | 47 +-- .../renderer_vulkan/liverpool_to_vk.cpp | 4 +- .../renderer_vulkan/liverpool_to_vk.h | 28 -- .../renderer_vulkan/vk_graphics_pipeline.cpp | 51 ++- .../renderer_vulkan/vk_graphics_pipeline.h | 8 +- .../renderer_vulkan/vk_pipeline_cache.cpp | 11 +- .../renderer_vulkan/vk_pipeline_cache.h | 3 +- .../renderer_vulkan/vk_rasterizer.cpp | 33 +- .../renderer_vulkan/vk_shader_util.cpp | 4 + 15 files changed, 426 insertions(+), 123 deletions(-) create mode 100644 src/shader_recompiler/backend/spirv/emit_spirv_quad_rect.cpp create mode 100644 src/shader_recompiler/backend/spirv/emit_spirv_quad_rect.h diff --git a/CMakeLists.txt b/CMakeLists.txt index d0c27c503..3f5832d23 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -630,6 +630,8 @@ set(SHADER_RECOMPILER src/shader_recompiler/exception.h src/shader_recompiler/backend/spirv/emit_spirv_instructions.h src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp src/shader_recompiler/backend/spirv/emit_spirv_logical.cpp + src/shader_recompiler/backend/spirv/emit_spirv_quad_rect.cpp + src/shader_recompiler/backend/spirv/emit_spirv_quad_rect.h src/shader_recompiler/backend/spirv/emit_spirv_select.cpp src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp src/shader_recompiler/backend/spirv/emit_spirv_special.cpp diff --git a/src/common/io_file.h b/src/common/io_file.h index feb2110ac..45787a092 100644 --- a/src/common/io_file.h +++ b/src/common/io_file.h @@ -207,7 +207,7 @@ public: return WriteSpan(string); } - static size_t WriteBytes(const std::filesystem::path path, std::span data) { + static size_t WriteBytes(const std::filesystem::path path, const auto& data) { IOFile out(path, FileAccessMode::Write); return out.Write(data); } diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index e545e8e36..0ce9eea7c 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -1,5 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later + #include #include #include diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_quad_rect.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_quad_rect.cpp new file mode 100644 index 000000000..74a807c57 --- /dev/null +++ b/src/shader_recompiler/backend/spirv/emit_spirv_quad_rect.cpp @@ -0,0 +1,329 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include "shader_recompiler/backend/spirv/emit_spirv_quad_rect.h" +#include "shader_recompiler/runtime_info.h" + +namespace Shader::Backend::SPIRV { + +using Sirit::Id; + +constexpr u32 SPIRV_VERSION_1_5 = 0x00010500; + +struct QuadRectListEmitter : public Sirit::Module { + explicit QuadRectListEmitter(const FragmentRuntimeInfo& fs_info_) + : Sirit::Module{SPIRV_VERSION_1_5}, fs_info{fs_info_}, inputs{fs_info_.num_inputs}, + outputs{fs_info_.num_inputs} { + void_id = TypeVoid(); + bool_id = TypeBool(); + float_id = TypeFloat(32); + uint_id = TypeUInt(32U); + int_id = TypeInt(32U, true); + bvec2_id = TypeVector(bool_id, 2); + vec2_id = TypeVector(float_id, 2); + vec3_id = TypeVector(float_id, 3); + vec4_id = TypeVector(float_id, 4); + + float_one = Constant(float_id, 1.0f); + float_min_one = Constant(float_id, -1.0f); + int_zero = Constant(int_id, 0); + + const Id float_arr{TypeArray(float_id, Constant(uint_id, 1U))}; + gl_per_vertex_type = TypeStruct(vec4_id, float_id, float_arr, float_arr); + Decorate(gl_per_vertex_type, spv::Decoration::Block); + MemberDecorate(gl_per_vertex_type, 0U, spv::Decoration::BuiltIn, + static_cast(spv::BuiltIn::Position)); + MemberDecorate(gl_per_vertex_type, 1U, spv::Decoration::BuiltIn, + static_cast(spv::BuiltIn::PointSize)); + MemberDecorate(gl_per_vertex_type, 2U, spv::Decoration::BuiltIn, + static_cast(spv::BuiltIn::ClipDistance)); + MemberDecorate(gl_per_vertex_type, 3U, spv::Decoration::BuiltIn, + static_cast(spv::BuiltIn::CullDistance)); + } + + /// Emits tessellation control shader for interpolating the 4th vertex of rectange primitive + void EmitRectListTCS() { + DefineEntry(spv::ExecutionModel::TessellationControl); + + // Set passthrough tessellation factors + const Id output_float_id{TypePointer(spv::StorageClass::Output, float_id)}; + for (int i = 0; i < 4; i++) { + const Id ptr{OpAccessChain(output_float_id, gl_tess_level_outer, Int(i))}; + OpStore(ptr, float_one); + } + for (int i = 0; i < 2; i++) { + const Id ptr{OpAccessChain(output_float_id, gl_tess_level_inner, Int(i))}; + OpStore(ptr, float_one); + } + + const Id input_vec4{TypePointer(spv::StorageClass::Input, vec4_id)}; + const Id output_vec4{TypePointer(spv::StorageClass::Output, vec4_id)}; + + // Emit interpolation block of the 4th vertex in rect. + // Load positions + std::array pos; + for (int i = 0; i < 3; i++) { + pos[i] = OpLoad(vec4_id, OpAccessChain(input_vec4, gl_in, Int(i), int_zero)); + } + + std::array point_coord_equal; + for (int i = 0; i < 3; i++) { + // point_coord_equal[i] = equal(gl_in[i].gl_Position.xy, gl_in[(i + 1) % + // 3].gl_Position.xy); + const Id pos_l_xy{OpVectorShuffle(vec2_id, pos[i], pos[i], 0, 1)}; + const Id pos_r_xy{OpVectorShuffle(vec2_id, pos[(i + 1) % 3], pos[(i + 1) % 3], 0, 1)}; + point_coord_equal[i] = OpFOrdEqual(bvec2_id, pos_l_xy, pos_r_xy); + } + + std::array bary_coord; + std::array is_edge_vertex; + for (int i = 0; i < 3; i++) { + // bool xy_equal = point_coord_equal[i].x && point_coord_equal[(i + 2) % 3].y; + const Id xy_equal{ + OpLogicalAnd(bool_id, OpCompositeExtract(bool_id, point_coord_equal[i], 0), + OpCompositeExtract(bool_id, point_coord_equal[(i + 2) % 3], 1))}; + // bool yx_equal = point_coord_equal[i].y && point_coord_equal[(i + 2) % 3].x; + const Id yx_equal{ + OpLogicalAnd(bool_id, OpCompositeExtract(bool_id, point_coord_equal[i], 1), + OpCompositeExtract(bool_id, point_coord_equal[(i + 2) % 3], 0))}; + // bary_coord[i] = (xy_equal || yx_equal) ? -1.f : 1.f; + is_edge_vertex[i] = OpLogicalOr(bool_id, xy_equal, yx_equal); + bary_coord[i] = OpSelect(float_id, is_edge_vertex[i], float_min_one, float_one); + } + + const auto interpolate = [&](Id v0, Id v1, Id v2) { + // return v0 * bary_coord.x + v1 * bary_coord.y + v2 * bary_coord.z; + const Id p0{OpVectorTimesScalar(vec4_id, v0, bary_coord[0])}; + const Id p1{OpVectorTimesScalar(vec4_id, v1, bary_coord[1])}; + const Id p2{OpVectorTimesScalar(vec4_id, v2, bary_coord[2])}; + return OpFAdd(vec4_id, p0, OpFAdd(vec4_id, p1, p2)); + }; + + // int vertex_index_id = is_edge_vertex[1] ? 1 : (is_edge_vertex[2] ? 2 : 0); + Id vertex_index{OpSelect(int_id, is_edge_vertex[2], Int(2), Int(0))}; + vertex_index = OpSelect(int_id, is_edge_vertex[1], Int(1), vertex_index); + + // int index = (vertex_index_id + gl_InvocationID) % 3; + const Id invocation_id{OpLoad(int_id, gl_invocation_id)}; + const Id invocation_3{OpIEqual(bool_id, invocation_id, Int(3))}; + const Id index{OpSMod(int_id, OpIAdd(int_id, vertex_index, invocation_id), Int(3))}; + + // gl_out[gl_InvocationID].gl_Position = gl_InvocationID == 3 ? pos3 : + // gl_in[index].gl_Position; + const Id pos3{interpolate(pos[0], pos[1], pos[2])}; + const Id in_ptr{OpAccessChain(input_vec4, gl_in, index, Int(0))}; + const Id position{OpSelect(vec4_id, invocation_3, pos3, OpLoad(vec4_id, in_ptr))}; + OpStore(OpAccessChain(output_vec4, gl_out, invocation_id, Int(0)), position); + + // Set attributes + for (int i = 0; i < inputs.size(); i++) { + // vec4 in_paramN3 = interpolate(bary_coord, in_paramN[0], in_paramN[1], in_paramN[2]); + const Id v0{OpLoad(vec4_id, OpAccessChain(input_vec4, inputs[i], Int(0)))}; + const Id v1{OpLoad(vec4_id, OpAccessChain(input_vec4, inputs[i], Int(1)))}; + const Id v2{OpLoad(vec4_id, OpAccessChain(input_vec4, inputs[i], Int(2)))}; + const Id in_param3{interpolate(v0, v1, v2)}; + // out_paramN[gl_InvocationID] = gl_InvocationID == 3 ? in_paramN3 : in_paramN[index]; + const Id in_param{OpLoad(vec4_id, OpAccessChain(input_vec4, inputs[i], index))}; + const Id out_param{OpSelect(vec4_id, invocation_3, in_param3, in_param)}; + OpStore(OpAccessChain(output_vec4, outputs[i], invocation_id), out_param); + } + + OpReturn(); + OpFunctionEnd(); + } + + /// Emits a passthrough quad tessellation control shader that outputs 4 control points. + void EmitQuadListTCS() { + DefineEntry(spv::ExecutionModel::TessellationControl); + const Id array_type{TypeArray(int_id, Int(4))}; + const Id values{ConstantComposite(array_type, Int(1), Int(2), Int(0), Int(3))}; + const Id indices{AddLocalVariable(TypePointer(spv::StorageClass::Function, array_type), + spv::StorageClass::Function, values)}; + + // Set passthrough tessellation factors + const Id output_float{TypePointer(spv::StorageClass::Output, float_id)}; + for (int i = 0; i < 4; i++) { + const Id ptr{OpAccessChain(output_float, gl_tess_level_outer, Int(i))}; + OpStore(ptr, float_one); + } + for (int i = 0; i < 2; i++) { + const Id ptr{OpAccessChain(output_float, gl_tess_level_inner, Int(i))}; + OpStore(ptr, float_one); + } + + const Id input_vec4{TypePointer(spv::StorageClass::Input, vec4_id)}; + const Id output_vec4{TypePointer(spv::StorageClass::Output, vec4_id)}; + const Id func_int{TypePointer(spv::StorageClass::Function, int_id)}; + const Id invocation_id{OpLoad(int_id, gl_invocation_id)}; + const Id index{OpLoad(int_id, OpAccessChain(func_int, indices, invocation_id))}; + + // gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; + const Id in_position{OpLoad(vec4_id, OpAccessChain(input_vec4, gl_in, index, Int(0)))}; + OpStore(OpAccessChain(output_vec4, gl_out, invocation_id, Int(0)), in_position); + + for (int i = 0; i < inputs.size(); i++) { + // out_paramN[gl_InvocationID] = in_paramN[gl_InvocationID]; + const Id in_param{OpLoad(vec4_id, OpAccessChain(input_vec4, inputs[i], index))}; + OpStore(OpAccessChain(output_vec4, outputs[i], invocation_id), in_param); + } + + OpReturn(); + OpFunctionEnd(); + } + + /// Emits a passthrough quad tessellation evaluation shader that outputs 4 control points. + void EmitPassthroughTES() { + DefineEntry(spv::ExecutionModel::TessellationEvaluation); + + // const int index = int(gl_TessCoord.y) * 2 + int(gl_TessCoord.x); + const Id input_float{TypePointer(spv::StorageClass::Input, float_id)}; + const Id tess_coord_x{OpLoad(float_id, OpAccessChain(input_float, gl_tess_coord, Int(0)))}; + const Id tess_coord_y{OpLoad(float_id, OpAccessChain(input_float, gl_tess_coord, Int(1)))}; + const Id index{OpIAdd(int_id, OpIMul(int_id, OpConvertFToS(int_id, tess_coord_y), Int(2)), + OpConvertFToS(int_id, tess_coord_x))}; + + // gl_Position = gl_in[index].gl_Position; + const Id input_vec4{TypePointer(spv::StorageClass::Input, vec4_id)}; + const Id output_vec4{TypePointer(spv::StorageClass::Output, vec4_id)}; + const Id position{OpLoad(vec4_id, OpAccessChain(input_vec4, gl_in, index, Int(0)))}; + OpStore(OpAccessChain(output_vec4, gl_per_vertex, Int(0)), position); + + // out_paramN = in_paramN[index]; + for (int i = 0; i < inputs.size(); i++) { + const Id param{OpLoad(vec4_id, OpAccessChain(input_vec4, inputs[i], index))}; + OpStore(outputs[i], param); + } + + OpReturn(); + OpFunctionEnd(); + } + +private: + Id Int(s32 value) { + return Constant(int_id, value); + } + + Id AddInput(Id type) { + const Id input{AddGlobalVariable(TypePointer(spv::StorageClass::Input, type), + spv::StorageClass::Input)}; + interfaces.push_back(input); + return input; + } + + Id AddOutput(Id type) { + const Id output{AddGlobalVariable(TypePointer(spv::StorageClass::Output, type), + spv::StorageClass::Output)}; + interfaces.push_back(output); + return output; + } + + void DefineEntry(spv::ExecutionModel model) { + AddCapability(spv::Capability::Shader); + AddCapability(spv::Capability::Tessellation); + const Id void_function{TypeFunction(void_id)}; + main = OpFunction(void_id, spv::FunctionControlMask::MaskNone, void_function); + if (model == spv::ExecutionModel::TessellationControl) { + AddExecutionMode(main, spv::ExecutionMode::OutputVertices, 4U); + } else { + AddExecutionMode(main, spv::ExecutionMode::Quads); + AddExecutionMode(main, spv::ExecutionMode::SpacingEqual); + AddExecutionMode(main, spv::ExecutionMode::VertexOrderCw); + } + DefineInputs(model); + DefineOutputs(model); + AddEntryPoint(model, main, "main", interfaces); + AddLabel(OpLabel()); + } + + void DefineOutputs(spv::ExecutionModel model) { + if (model == spv::ExecutionModel::TessellationControl) { + const Id gl_per_vertex_array{TypeArray(gl_per_vertex_type, Constant(uint_id, 4U))}; + gl_out = AddOutput(gl_per_vertex_array); + + const Id arr2_id{TypeArray(float_id, Constant(uint_id, 2U))}; + gl_tess_level_inner = AddOutput(arr2_id); + Decorate(gl_tess_level_inner, spv::Decoration::BuiltIn, spv::BuiltIn::TessLevelInner); + Decorate(gl_tess_level_inner, spv::Decoration::Patch); + + const Id arr4_id{TypeArray(float_id, Constant(uint_id, 4U))}; + gl_tess_level_outer = AddOutput(arr4_id); + Decorate(gl_tess_level_outer, spv::Decoration::BuiltIn, spv::BuiltIn::TessLevelOuter); + Decorate(gl_tess_level_outer, spv::Decoration::Patch); + } else { + gl_per_vertex = AddOutput(gl_per_vertex_type); + } + for (int i = 0; i < fs_info.num_inputs; i++) { + outputs[i] = AddOutput(model == spv::ExecutionModel::TessellationControl + ? TypeArray(vec4_id, Int(4)) + : vec4_id); + Decorate(outputs[i], spv::Decoration::Location, fs_info.inputs[i].param_index); + } + } + + void DefineInputs(spv::ExecutionModel model) { + if (model == spv::ExecutionModel::TessellationEvaluation) { + gl_tess_coord = AddInput(vec3_id); + Decorate(gl_tess_coord, spv::Decoration::BuiltIn, spv::BuiltIn::TessCoord); + } else { + gl_invocation_id = AddInput(int_id); + Decorate(gl_invocation_id, spv::Decoration::BuiltIn, spv::BuiltIn::InvocationId); + } + const Id gl_per_vertex_array{TypeArray(gl_per_vertex_type, Constant(uint_id, 32U))}; + gl_in = AddInput(gl_per_vertex_array); + const Id float_arr{TypeArray(vec4_id, Int(32))}; + for (int i = 0; i < fs_info.num_inputs; i++) { + inputs[i] = AddInput(float_arr); + Decorate(inputs[i], spv::Decoration::Location, fs_info.inputs[i].param_index); + } + } + +private: + FragmentRuntimeInfo fs_info; + Id main; + Id void_id; + Id bool_id; + Id float_id; + Id uint_id; + Id int_id; + Id bvec2_id; + Id vec2_id; + Id vec3_id; + Id vec4_id; + Id float_one; + Id float_min_one; + Id int_zero; + Id gl_per_vertex_type; + Id gl_in; + union { + Id gl_out; + Id gl_per_vertex; + }; + Id gl_tess_level_inner; + Id gl_tess_level_outer; + union { + Id gl_tess_coord; + Id gl_invocation_id; + }; + std::vector inputs; + std::vector outputs; + std::vector interfaces; +}; + +std::vector EmitAuxilaryTessShader(AuxShaderType type, const FragmentRuntimeInfo& fs_info) { + QuadRectListEmitter ctx{fs_info}; + switch (type) { + case AuxShaderType::RectListTCS: + ctx.EmitRectListTCS(); + break; + case AuxShaderType::QuadListTCS: + ctx.EmitQuadListTCS(); + break; + case AuxShaderType::PassthroughTES: + ctx.EmitPassthroughTES(); + break; + } + return ctx.Assemble(); +} + +} // namespace Shader::Backend::SPIRV \ No newline at end of file diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_quad_rect.h b/src/shader_recompiler/backend/spirv/emit_spirv_quad_rect.h new file mode 100644 index 000000000..c6c970ec3 --- /dev/null +++ b/src/shader_recompiler/backend/spirv/emit_spirv_quad_rect.h @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "common/types.h" + +namespace Shader { +struct FragmentRuntimeInfo; +} + +namespace Shader::Backend::SPIRV { + +enum class AuxShaderType : u32 { + RectListTCS, + QuadListTCS, + PassthroughTES, +}; + +[[nodiscard]] std::vector EmitAuxilaryTessShader(AuxShaderType type, + const FragmentRuntimeInfo& fs_info); + +} // namespace Shader::Backend::SPIRV diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index 23e23c118..bbf74f5d3 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -227,7 +227,7 @@ struct RuntimeInfo { ComputeRuntimeInfo cs_info; }; - RuntimeInfo(Stage stage_) { + void Initialize(Stage stage_) { memset(this, 0, sizeof(*this)); stage = stage_; } diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index f265fb68d..75f06365f 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -238,32 +238,14 @@ u32 BufferCache::BindIndexBuffer(bool& is_indexed, u32 index_offset) { // Emulate QuadList and Polygon primitive types with CPU made index buffer. const auto& regs = liverpool->regs; if (!is_indexed) { - bool needs_index_buffer = false; - if (regs.primitive_type == AmdGpu::PrimitiveType::QuadList || - regs.primitive_type == AmdGpu::PrimitiveType::Polygon) { - needs_index_buffer = true; - } - - if (!needs_index_buffer) { + if (regs.primitive_type != AmdGpu::PrimitiveType::Polygon) { return regs.num_indices; } // Emit indices. const u32 index_size = 3 * regs.num_indices; const auto [data, offset] = stream_buffer.Map(index_size); - - switch (regs.primitive_type) { - case AmdGpu::PrimitiveType::QuadList: - Vulkan::LiverpoolToVK::EmitQuadToTriangleListIndices(data, regs.num_indices); - break; - case AmdGpu::PrimitiveType::Polygon: - Vulkan::LiverpoolToVK::EmitPolygonToTriangleListIndices(data, regs.num_indices); - break; - default: - UNREACHABLE(); - break; - } - + Vulkan::LiverpoolToVK::EmitPolygonToTriangleListIndices(data, regs.num_indices); stream_buffer.Commit(); // Bind index buffer. @@ -282,31 +264,6 @@ u32 BufferCache::BindIndexBuffer(bool& is_indexed, u32 index_offset) { VAddr index_address = regs.index_base_address.Address(); index_address += index_offset * index_size; - if (regs.primitive_type == AmdGpu::PrimitiveType::QuadList) { - // Convert indices. - const u32 new_index_size = regs.num_indices * index_size * 6 / 4; - const auto [data, offset] = stream_buffer.Map(new_index_size); - const auto index_ptr = reinterpret_cast(index_address); - switch (index_type) { - case vk::IndexType::eUint16: - Vulkan::LiverpoolToVK::ConvertQuadToTriangleListIndices(data, index_ptr, - regs.num_indices); - break; - case vk::IndexType::eUint32: - Vulkan::LiverpoolToVK::ConvertQuadToTriangleListIndices(data, index_ptr, - regs.num_indices); - break; - default: - UNREACHABLE_MSG("Unsupported QuadList index type {}", vk::to_string(index_type)); - break; - } - stream_buffer.Commit(); - - // Bind index buffer. - const auto cmdbuf = scheduler.CommandBuffer(); - cmdbuf.bindIndexBuffer(stream_buffer.Handle(), offset, index_type); - return new_index_size / index_size; - } if (regs.primitive_type == AmdGpu::PrimitiveType::Polygon) { UNREACHABLE(); } diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp index 6df89dbae..25ff88b9d 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp @@ -116,12 +116,12 @@ vk::PrimitiveTopology PrimitiveType(AmdGpu::PrimitiveType type) { return vk::PrimitiveTopology::eTriangleStripWithAdjacency; case AmdGpu::PrimitiveType::PatchPrimitive: return vk::PrimitiveTopology::ePatchList; - case AmdGpu::PrimitiveType::QuadList: case AmdGpu::PrimitiveType::Polygon: // Needs to generate index buffer on the fly. return vk::PrimitiveTopology::eTriangleList; + case AmdGpu::PrimitiveType::QuadList: case AmdGpu::PrimitiveType::RectList: - return vk::PrimitiveTopology::eTriangleStrip; + return vk::PrimitiveTopology::ePatchList; default: UNREACHABLE(); return vk::PrimitiveTopology::eTriangleList; diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.h b/src/video_core/renderer_vulkan/liverpool_to_vk.h index 72bddc6b6..d5f8e693b 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.h +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.h @@ -70,34 +70,6 @@ vk::ClearValue ColorBufferClearValue(const AmdGpu::Liverpool::ColorBuffer& color vk::SampleCountFlagBits NumSamples(u32 num_samples, vk::SampleCountFlags supported_flags); -static constexpr u16 NumVerticesPerQuad = 4; - -inline void EmitQuadToTriangleListIndices(u8* out_ptr, u32 num_vertices) { - u16* out_data = reinterpret_cast(out_ptr); - for (u16 i = 0; i < num_vertices; i += NumVerticesPerQuad) { - *out_data++ = i; - *out_data++ = i + 1; - *out_data++ = i + 2; - *out_data++ = i; - *out_data++ = i + 2; - *out_data++ = i + 3; - } -} - -template -void ConvertQuadToTriangleListIndices(u8* out_ptr, const u8* in_ptr, u32 num_vertices) { - T* out_data = reinterpret_cast(out_ptr); - const T* in_data = reinterpret_cast(in_ptr); - for (u16 i = 0; i < num_vertices; i += NumVerticesPerQuad) { - *out_data++ = in_data[i]; - *out_data++ = in_data[i + 1]; - *out_data++ = in_data[i + 2]; - *out_data++ = in_data[i]; - *out_data++ = in_data[i + 2]; - *out_data++ = in_data[i + 3]; - } -} - inline void EmitPolygonToTriangleListIndices(u8* out_ptr, u32 num_vertices) { u16* out_data = reinterpret_cast(out_ptr); for (u16 i = 1; i < num_vertices - 1; i++) { diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 222ffb5a9..3cba7a7e4 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -7,25 +7,30 @@ #include #include "common/assert.h" +#include "common/io_file.h" #include "common/scope_exit.h" +#include "shader_recompiler/backend/spirv/emit_spirv_quad_rect.h" +#include "shader_recompiler/frontend/fetch_shader.h" #include "shader_recompiler/runtime_info.h" #include "video_core/amdgpu/resource.h" #include "video_core/buffer_cache/buffer_cache.h" #include "video_core/renderer_vulkan/vk_graphics_pipeline.h" - -#include "shader_recompiler/frontend/fetch_shader.h" #include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_scheduler.h" +#include "video_core/renderer_vulkan/vk_shader_util.h" #include "video_core/texture_cache/texture_cache.h" namespace Vulkan { -GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& scheduler_, - DescriptorHeap& desc_heap_, const GraphicsPipelineKey& key_, - vk::PipelineCache pipeline_cache, - std::span infos, - std::optional fetch_shader_, - std::span modules) +using Shader::Backend::SPIRV::AuxShaderType; + +GraphicsPipeline::GraphicsPipeline( + const Instance& instance_, Scheduler& scheduler_, DescriptorHeap& desc_heap_, + const GraphicsPipelineKey& key_, vk::PipelineCache pipeline_cache, + std::span infos, + std::span runtime_infos, + std::optional fetch_shader_, + std::span modules) : Pipeline{instance_, scheduler_, desc_heap_, pipeline_cache}, key{key_}, fetch_shader{std::move(fetch_shader_)} { const vk::Device device = instance.GetDevice(); @@ -88,11 +93,6 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul .pVertexAttributeDescriptions = vertex_attributes.data(), }; - if (key.prim_type == AmdGpu::PrimitiveType::RectList && !IsEmbeddedVs()) { - LOG_WARNING(Render_Vulkan, - "Rectangle List primitive type is only supported for embedded VS"); - } - auto prim_restart = key.enable_primitive_restart != 0; if (prim_restart && IsPrimitiveListTopology() && !instance.IsListRestartSupported()) { LOG_WARNING(Render_Vulkan, @@ -106,9 +106,11 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul ASSERT_MSG(!prim_restart || key.primitive_restart_index == 0xFFFF || key.primitive_restart_index == 0xFFFFFFFF, "Primitive restart index other than -1 is not supported yet"); - + const bool is_rect_list = key.prim_type == AmdGpu::PrimitiveType::RectList; + const bool is_quad_list = key.prim_type == AmdGpu::PrimitiveType::QuadList; + const auto& fs_info = runtime_infos[u32(Shader::LogicalStage::Fragment)].fs_info; const vk::PipelineTessellationStateCreateInfo tessellation_state = { - .patchControlPoints = key.patch_control_points, + .patchControlPoints = is_rect_list ? 3U : (is_quad_list ? 4U : key.patch_control_points), }; const vk::PipelineRasterizationStateCreateInfo raster_state = { @@ -232,6 +234,14 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul .module = modules[stage], .pName = "main", }); + } else if (is_rect_list || is_quad_list) { + const auto type = is_quad_list ? AuxShaderType::QuadListTCS : AuxShaderType::RectListTCS; + auto tcs = Shader::Backend::SPIRV::EmitAuxilaryTessShader(type, fs_info); + shader_stages.emplace_back(vk::PipelineShaderStageCreateInfo{ + .stage = vk::ShaderStageFlagBits::eTessellationControl, + .module = CompileSPV(tcs, instance.GetDevice()), + .pName = "main", + }); } stage = u32(Shader::LogicalStage::TessellationEval); if (infos[stage]) { @@ -240,6 +250,14 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul .module = modules[stage], .pName = "main", }); + } else if (is_rect_list || is_quad_list) { + auto tes = + Shader::Backend::SPIRV::EmitAuxilaryTessShader(AuxShaderType::PassthroughTES, fs_info); + shader_stages.emplace_back(vk::PipelineShaderStageCreateInfo{ + .stage = vk::ShaderStageFlagBits::eTessellationEvaluation, + .module = CompileSPV(tes, instance.GetDevice()), + .pName = "main", + }); } stage = u32(Shader::LogicalStage::Fragment); if (infos[stage]) { @@ -322,8 +340,7 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul .pStages = shader_stages.data(), .pVertexInputState = !instance.IsVertexInputDynamicState() ? &vertex_input_info : nullptr, .pInputAssemblyState = &input_assembly, - .pTessellationState = - stages[u32(Shader::LogicalStage::TessellationControl)] ? &tessellation_state : nullptr, + .pTessellationState = &tessellation_state, .pViewportState = &viewport_info, .pRasterizationState = &raster_state, .pMultisampleState = &multisampling, diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index f25341bbb..5a1e3c27f 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -18,7 +18,7 @@ class TextureCache; namespace Vulkan { -static constexpr u32 MaxShaderStages = 5; +static constexpr u32 MaxShaderStages = static_cast(Shader::LogicalStage::NumLogicalStages); static constexpr u32 MaxVertexBufferCount = 32; class Instance; @@ -64,6 +64,7 @@ public: GraphicsPipeline(const Instance& instance, Scheduler& scheduler, DescriptorHeap& desc_heap, const GraphicsPipelineKey& key, vk::PipelineCache pipeline_cache, std::span stages, + std::span runtime_infos, std::optional fetch_shader, std::span modules); ~GraphicsPipeline(); @@ -72,11 +73,6 @@ public: return fetch_shader; } - bool IsEmbeddedVs() const noexcept { - static constexpr size_t EmbeddedVsHash = 0x9b2da5cf47f8c29f; - return key.stage_hashes[u32(Shader::LogicalStage::Vertex)] == EmbeddedVsHash; - } - auto GetWriteMasks() const { return key.write_masks; } diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 43e02dd9d..a7d786688 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -80,8 +80,8 @@ void GatherVertexOutputs(Shader::VertexRuntimeInfo& info, : (ctl.IsCullDistEnabled(7) ? VsOutput::CullDist7 : VsOutput::None)); } -Shader::RuntimeInfo PipelineCache::BuildRuntimeInfo(Stage stage, LogicalStage l_stage) { - auto info = Shader::RuntimeInfo{stage}; +const Shader::RuntimeInfo& PipelineCache::BuildRuntimeInfo(Stage stage, LogicalStage l_stage) { + auto& info = runtime_infos[u32(l_stage)]; const auto& regs = liverpool->regs; const auto BuildCommon = [&](const auto& program) { info.num_user_data = program.settings.num_user_regs; @@ -90,6 +90,7 @@ Shader::RuntimeInfo PipelineCache::BuildRuntimeInfo(Stage stage, LogicalStage l_ info.fp_denorm_mode32 = program.settings.fp_denorm_mode32; info.fp_round_mode32 = program.settings.fp_round_mode32; }; + info.Initialize(stage); switch (stage) { case Stage::Local: { BuildCommon(regs.ls_program); @@ -220,9 +221,9 @@ const GraphicsPipeline* PipelineCache::GetGraphicsPipeline() { } const auto [it, is_new] = graphics_pipelines.try_emplace(graphics_key); if (is_new) { - it.value() = - std::make_unique(instance, scheduler, desc_heap, graphics_key, - *pipeline_cache, infos, fetch_shader, modules); + it.value() = std::make_unique(instance, scheduler, desc_heap, + graphics_key, *pipeline_cache, infos, + runtime_infos, fetch_shader, modules); if (Config::collectShadersForDebug()) { for (auto stage = 0; stage < MaxShaderStages; ++stage) { if (infos[stage]) { diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index ec4406448..d4a51efdb 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h @@ -76,7 +76,7 @@ private: vk::ShaderModule CompileModule(Shader::Info& info, Shader::RuntimeInfo& runtime_info, std::span code, size_t perm_idx, Shader::Backend::Bindings& binding); - Shader::RuntimeInfo BuildRuntimeInfo(Shader::Stage stage, Shader::LogicalStage l_stage); + const Shader::RuntimeInfo& BuildRuntimeInfo(Shader::Stage stage, Shader::LogicalStage l_stage); private: const Instance& instance; @@ -90,6 +90,7 @@ private: tsl::robin_map> program_cache; tsl::robin_map> compute_pipelines; tsl::robin_map> graphics_pipelines; + std::array runtime_infos{}; std::array infos{}; std::array modules{}; std::optional fetch_shader{}; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 417b6d4b6..44772b4c4 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -245,7 +245,6 @@ void Rasterizer::Draw(bool is_indexed, u32 index_offset) { } auto state = PrepareRenderState(pipeline->GetMrtMask()); - if (!BindResources(pipeline)) { return; } @@ -267,10 +266,7 @@ void Rasterizer::Draw(bool is_indexed, u32 index_offset) { cmdbuf.drawIndexed(num_indices, regs.num_instances.NumInstances(), 0, s32(vertex_offset), instance_offset); } else { - const u32 num_vertices = - regs.primitive_type == AmdGpu::PrimitiveType::RectList ? 4 : regs.num_indices; - cmdbuf.draw(num_vertices, regs.num_instances.NumInstances(), vertex_offset, - instance_offset); + cmdbuf.draw(num_indices, regs.num_instances.NumInstances(), vertex_offset, instance_offset); } ResetBindings(); @@ -285,18 +281,14 @@ void Rasterizer::DrawIndirect(bool is_indexed, VAddr arg_address, u32 offset, u3 } const auto& regs = liverpool->regs; - if (regs.primitive_type == AmdGpu::PrimitiveType::QuadList || - regs.primitive_type == AmdGpu::PrimitiveType::Polygon) { - // We use a generated index buffer to convert quad lists and polygons to triangles. Since it + if (regs.primitive_type == AmdGpu::PrimitiveType::Polygon) { + // We use a generated index buffer to convert polygons to triangles. Since it // changes type of the draw, arguments are not valid for this case. We need to run a // conversion pass to repack the indirect arguments buffer first. LOG_WARNING(Render_Vulkan, "Primitive type is not supported for indirect draw"); return; } - ASSERT_MSG(regs.primitive_type != AmdGpu::PrimitiveType::RectList, - "Unsupported primitive type for indirect draw"); - const GraphicsPipeline* pipeline = pipeline_cache.GetGraphicsPipeline(); if (!pipeline) { return; @@ -1009,19 +1001,26 @@ void Rasterizer::UpdateViewportScissorState() { regs.clipper_control.clip_space == AmdGpu::Liverpool::ClipSpace::MinusWToW ? 1.0f : 0.0f; + const auto vp_ctl = regs.viewport_control; for (u32 i = 0; i < Liverpool::NumViewports; i++) { const auto& vp = regs.viewports[i]; const auto& vp_d = regs.viewport_depths[i]; if (vp.xscale == 0) { continue; } + const auto xoffset = vp_ctl.xoffset_enable ? vp.xoffset : 0.f; + const auto xscale = vp_ctl.xscale_enable ? vp.xscale : 1.f; + const auto yoffset = vp_ctl.yoffset_enable ? vp.yoffset : 0.f; + const auto yscale = vp_ctl.yscale_enable ? vp.yscale : 1.f; + const auto zoffset = vp_ctl.zoffset_enable ? vp.zoffset : 0.f; + const auto zscale = vp_ctl.zscale_enable ? vp.zscale : 1.f; viewports.push_back({ - .x = vp.xoffset - vp.xscale, - .y = vp.yoffset - vp.yscale, - .width = vp.xscale * 2.0f, - .height = vp.yscale * 2.0f, - .minDepth = vp.zoffset - vp.zscale * reduce_z, - .maxDepth = vp.zscale + vp.zoffset, + .x = xoffset - xscale, + .y = yoffset - yscale, + .width = xscale * 2.0f, + .height = yscale * 2.0f, + .minDepth = zoffset - zscale * reduce_z, + .maxDepth = zscale + zoffset, }); } diff --git a/src/video_core/renderer_vulkan/vk_shader_util.cpp b/src/video_core/renderer_vulkan/vk_shader_util.cpp index f9347d6e6..08703c3de 100644 --- a/src/video_core/renderer_vulkan/vk_shader_util.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_util.cpp @@ -126,6 +126,10 @@ EShLanguage ToEshShaderStage(vk::ShaderStageFlagBits stage) { return EShLanguage::EShLangVertex; case vk::ShaderStageFlagBits::eGeometry: return EShLanguage::EShLangGeometry; + case vk::ShaderStageFlagBits::eTessellationControl: + return EShLanguage::EShLangTessControl; + case vk::ShaderStageFlagBits::eTessellationEvaluation: + return EShLanguage::EShLangTessEvaluation; case vk::ShaderStageFlagBits::eFragment: return EShLanguage::EShLangFragment; case vk::ShaderStageFlagBits::eCompute: From 0a4453b9127ccfae8fef856fc205b3ddf360288e Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Tue, 24 Dec 2024 03:45:11 -0800 Subject: [PATCH 236/549] renderer_vulkan: Simplify depth pipeline state and move stencil to dynamic state. (#1854) * renderer_vulkan: Simplify depth pipeline state and move stencil to dynamic state. * Change graphics key depth-stencil flags to bitfields. --- .../renderer_vulkan/vk_graphics_pipeline.cpp | 33 ++++--------------- .../renderer_vulkan/vk_graphics_pipeline.h | 14 ++++++-- .../renderer_vulkan/vk_pipeline_cache.cpp | 14 ++++---- .../renderer_vulkan/vk_rasterizer.cpp | 28 ++++++++++++++++ 4 files changed, 54 insertions(+), 35 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 3cba7a7e4..265c7bd3d 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -123,7 +123,7 @@ GraphicsPipeline::GraphicsPipeline( .frontFace = key.front_face == Liverpool::FrontFace::Clockwise ? vk::FrontFace::eClockwise : vk::FrontFace::eCounterClockwise, - .depthBiasEnable = bool(key.depth_bias_enable), + .depthBiasEnable = key.depth_bias_enable, .lineWidth = 1.0f, }; @@ -164,6 +164,7 @@ GraphicsPipeline::GraphicsPipeline( vk::DynamicState::eBlendConstants, vk::DynamicState::eDepthBounds, vk::DynamicState::eDepthBias, vk::DynamicState::eStencilReference, vk::DynamicState::eStencilCompareMask, vk::DynamicState::eStencilWriteMask, + vk::DynamicState::eStencilOpEXT, }; if (instance.IsColorWriteEnableSupported()) { @@ -182,31 +183,11 @@ GraphicsPipeline::GraphicsPipeline( }; const vk::PipelineDepthStencilStateCreateInfo depth_info = { - .depthTestEnable = key.depth_stencil.depth_enable, - .depthWriteEnable = key.depth_stencil.depth_write_enable, - .depthCompareOp = LiverpoolToVK::CompareOp(key.depth_stencil.depth_func), - .depthBoundsTestEnable = key.depth_stencil.depth_bounds_enable, - .stencilTestEnable = key.depth_stencil.stencil_enable, - .front{ - .failOp = LiverpoolToVK::StencilOp(key.stencil.stencil_fail_front), - .passOp = LiverpoolToVK::StencilOp(key.stencil.stencil_zpass_front), - .depthFailOp = LiverpoolToVK::StencilOp(key.stencil.stencil_zfail_front), - .compareOp = LiverpoolToVK::CompareOp(key.depth_stencil.stencil_ref_func), - }, - .back{ - .failOp = LiverpoolToVK::StencilOp(key.depth_stencil.backface_enable - ? key.stencil.stencil_fail_back.Value() - : key.stencil.stencil_fail_front.Value()), - .passOp = LiverpoolToVK::StencilOp(key.depth_stencil.backface_enable - ? key.stencil.stencil_zpass_back.Value() - : key.stencil.stencil_zpass_front.Value()), - .depthFailOp = LiverpoolToVK::StencilOp(key.depth_stencil.backface_enable - ? key.stencil.stencil_zfail_back.Value() - : key.stencil.stencil_zfail_front.Value()), - .compareOp = LiverpoolToVK::CompareOp(key.depth_stencil.backface_enable - ? key.depth_stencil.stencil_bf_func.Value() - : key.depth_stencil.stencil_ref_func.Value()), - }, + .depthTestEnable = key.depth_test_enable, + .depthWriteEnable = key.depth_write_enable, + .depthCompareOp = key.depth_compare_op, + .depthBoundsTestEnable = key.depth_bounds_test_enable, + .stencilTestEnable = key.stencil_test_enable, }; boost::container::static_vector diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index 5a1e3c27f..ee8afa3e6 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -36,11 +36,19 @@ struct GraphicsPipelineKey { vk::Format depth_format; vk::Format stencil_format; - Liverpool::DepthControl depth_stencil; - u32 depth_bias_enable; + struct { + bool depth_test_enable : 1; + bool depth_write_enable : 1; + bool depth_bounds_test_enable : 1; + bool depth_bias_enable : 1; + bool stencil_test_enable : 1; + // Must be named to be zero-initialized. + u8 _unused : 3; + }; + vk::CompareOp depth_compare_op; + u32 num_samples; u32 mrt_mask; - Liverpool::StencilControl stencil; AmdGpu::PrimitiveType prim_type; u32 enable_primitive_restart; u32 primitive_restart_index; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index a7d786688..e774b1d42 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -258,11 +258,13 @@ bool PipelineCache::RefreshGraphicsKey() { auto& regs = liverpool->regs; auto& key = graphics_key; - key.depth_stencil = regs.depth_control; - key.stencil = regs.stencil_control; - key.depth_stencil.depth_write_enable.Assign(regs.depth_control.depth_write_enable.Value() && - !regs.depth_render_control.depth_clear_enable); + key.depth_test_enable = regs.depth_control.depth_enable; + key.depth_write_enable = + regs.depth_control.depth_write_enable && !regs.depth_render_control.depth_clear_enable; + key.depth_bounds_test_enable = regs.depth_control.depth_bounds_enable; key.depth_bias_enable = regs.polygon_control.NeedsBias(); + key.depth_compare_op = LiverpoolToVK::CompareOp(regs.depth_control.depth_func); + key.stencil_test_enable = regs.depth_control.stencil_enable; const auto depth_format = instance.GetSupportedFormat( LiverpoolToVK::DepthFormat(regs.depth_buffer.z_info.format, @@ -272,13 +274,13 @@ bool PipelineCache::RefreshGraphicsKey() { key.depth_format = depth_format; } else { key.depth_format = vk::Format::eUndefined; - key.depth_stencil.depth_enable.Assign(false); + key.depth_test_enable = false; } if (regs.depth_buffer.StencilValid()) { key.stencil_format = depth_format; } else { key.stencil_format = vk::Format::eUndefined; - key.depth_stencil.stencil_enable.Assign(false); + key.stencil_test_enable = false; } key.prim_type = regs.primitive_type; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 44772b4c4..55f861013 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -963,7 +963,33 @@ void Rasterizer::UpdateDynamicState(const GraphicsPipeline& pipeline) { cmdbuf.setDepthBias(regs.poly_offset.back_offset, regs.poly_offset.depth_bias, regs.poly_offset.back_scale / 16.f); } + if (regs.depth_control.stencil_enable) { + const auto front_fail_op = + LiverpoolToVK::StencilOp(regs.stencil_control.stencil_fail_front); + const auto front_pass_op = + LiverpoolToVK::StencilOp(regs.stencil_control.stencil_zpass_front); + const auto front_depth_fail_op = + LiverpoolToVK::StencilOp(regs.stencil_control.stencil_zfail_front); + const auto front_compare_op = LiverpoolToVK::CompareOp(regs.depth_control.stencil_ref_func); + if (regs.depth_control.backface_enable) { + const auto back_fail_op = + LiverpoolToVK::StencilOp(regs.stencil_control.stencil_fail_back); + const auto back_pass_op = + LiverpoolToVK::StencilOp(regs.stencil_control.stencil_zpass_back); + const auto back_depth_fail_op = + LiverpoolToVK::StencilOp(regs.stencil_control.stencil_zfail_back); + const auto back_compare_op = + LiverpoolToVK::CompareOp(regs.depth_control.stencil_bf_func); + cmdbuf.setStencilOpEXT(vk::StencilFaceFlagBits::eFront, front_fail_op, front_pass_op, + front_depth_fail_op, front_compare_op); + cmdbuf.setStencilOpEXT(vk::StencilFaceFlagBits::eBack, back_fail_op, back_pass_op, + back_depth_fail_op, back_compare_op); + } else { + cmdbuf.setStencilOpEXT(vk::StencilFaceFlagBits::eFrontAndBack, front_fail_op, + front_pass_op, front_depth_fail_op, front_compare_op); + } + const auto front = regs.stencil_ref_front; const auto back = regs.stencil_ref_back; if (front.stencil_test_val == back.stencil_test_val) { @@ -973,6 +999,7 @@ void Rasterizer::UpdateDynamicState(const GraphicsPipeline& pipeline) { cmdbuf.setStencilReference(vk::StencilFaceFlagBits::eFront, front.stencil_test_val); cmdbuf.setStencilReference(vk::StencilFaceFlagBits::eBack, back.stencil_test_val); } + if (front.stencil_write_mask == back.stencil_write_mask) { cmdbuf.setStencilWriteMask(vk::StencilFaceFlagBits::eFrontAndBack, front.stencil_write_mask); @@ -980,6 +1007,7 @@ void Rasterizer::UpdateDynamicState(const GraphicsPipeline& pipeline) { cmdbuf.setStencilWriteMask(vk::StencilFaceFlagBits::eFront, front.stencil_write_mask); cmdbuf.setStencilWriteMask(vk::StencilFaceFlagBits::eBack, back.stencil_write_mask); } + if (front.stencil_mask == back.stencil_mask) { cmdbuf.setStencilCompareMask(vk::StencilFaceFlagBits::eFrontAndBack, front.stencil_mask); From 2c0f986c5274a34902a1e3e10c6fc578d0b85989 Mon Sep 17 00:00:00 2001 From: "Daniel R." <47796739+polybiusproxy@users.noreply.github.com> Date: Tue, 24 Dec 2024 13:33:00 +0100 Subject: [PATCH 237/549] core/libraries: HLE fiber reimplementation (#1836) --- CMakeLists.txt | 5 +- src/core/libraries/fiber/fiber.cpp | 509 +++++++++++++++------- src/core/libraries/fiber/fiber.h | 116 +++-- src/core/libraries/fiber/fiber_context.s | 121 +++++ src/core/libraries/kernel/threads/tcb.cpp | 1 + src/core/tls.h | 5 + 6 files changed, 564 insertions(+), 193 deletions(-) create mode 100644 src/core/libraries/fiber/fiber_context.s diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f5832d23..dac3b19ab 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,7 @@ if (NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release) endif() -project(shadPS4) +project(shadPS4 CXX C ASM) # Forcing PIE makes sure that the base address is high enough so that it doesn't clash with the PS4 memory. if(UNIX AND NOT APPLE) @@ -392,7 +392,8 @@ set(USBD_LIB src/core/libraries/usbd/usbd.cpp src/core/libraries/usbd/usbd.h ) -set(FIBER_LIB src/core/libraries/fiber/fiber.cpp +set(FIBER_LIB src/core/libraries/fiber/fiber_context.s + src/core/libraries/fiber/fiber.cpp src/core/libraries/fiber/fiber.h src/core/libraries/fiber/fiber_error.h ) diff --git a/src/core/libraries/fiber/fiber.cpp b/src/core/libraries/fiber/fiber.cpp index c28d8d734..278099841 100644 --- a/src/core/libraries/fiber/fiber.cpp +++ b/src/core/libraries/fiber/fiber.cpp @@ -8,253 +8,455 @@ #include "core/libraries/libs.h" #include "core/tls.h" -#ifdef _WIN64 -#include -#endif - namespace Libraries::Fiber { -static constexpr u64 kFiberSignature = 0x054ad954; +static constexpr u32 kFiberSignature0 = 0xdef1649c; +static constexpr u32 kFiberSignature1 = 0xb37592a0; +static constexpr u32 kFiberOptSignature = 0xbb40e64d; +static constexpr u64 kFiberStackSignature = 0x7149f2ca7149f2ca; +static constexpr u64 kFiberStackSizeCheck = 0xdeadbeefdeadbeef; -thread_local SceFiber* gCurrentFiber = nullptr; -thread_local void* gFiberThread = nullptr; +static std::atomic context_size_check = false; -void FiberEntry(void* param) { - SceFiber* fiber = static_cast(param); - u64 argRun = 0; - u64 argRet = 0; - - gCurrentFiber = fiber; - - if (fiber->pArgRun != nullptr) { - argRun = *fiber->pArgRun; - } - - Core::ExecuteGuest(fiber->entry, fiber->argOnInitialize, argRun); - UNREACHABLE(); +OrbisFiberContext* GetFiberContext() { + return Core::GetTcbBase()->tcb_fiber; } -s32 PS4_SYSV_ABI sceFiberInitialize(SceFiber* fiber, const char* name, SceFiberEntry entry, - u64 argOnInitialize, void* addrContext, u64 sizeContext, - const SceFiberOptParam* optParam) { - LOG_INFO(Lib_Fiber, "called: name = {}", name); +extern "C" s32 PS4_SYSV_ABI _sceFiberSetJmp(OrbisFiberContext* ctx) asm("_sceFiberSetJmp"); +extern "C" s32 PS4_SYSV_ABI _sceFiberLongJmp(OrbisFiberContext* ctx) asm("_sceFiberLongJmp"); +extern "C" void PS4_SYSV_ABI _sceFiberSwitchEntry(OrbisFiberData* data, + bool set_fpu) asm("_sceFiberSwitchEntry"); +extern "C" void PS4_SYSV_ABI _sceFiberForceQuit(u64 ret) asm("_sceFiberForceQuit"); +extern "C" void PS4_SYSV_ABI _sceFiberForceQuit(u64 ret) { + OrbisFiberContext* g_ctx = GetFiberContext(); + g_ctx->return_val = ret; + _sceFiberLongJmp(g_ctx); +} + +void PS4_SYSV_ABI _sceFiberCheckStackOverflow(OrbisFiberContext* ctx) { + u64* stack_base = reinterpret_cast(ctx->current_fiber->addr_context); + if (stack_base && *stack_base != kFiberStackSignature) { + UNREACHABLE_MSG("Stack overflow detected in fiber."); + } +} + +void PS4_SYSV_ABI _sceFiberSwitchToFiber(OrbisFiber* fiber, u64 arg_on_run_to, + OrbisFiberContext* ctx) { + OrbisFiberContext* fiber_ctx = fiber->context; + if (fiber_ctx) { + ctx->arg_on_run_to = arg_on_run_to; + _sceFiberLongJmp(fiber_ctx); + __builtin_trap(); + } + + OrbisFiberData data{}; + if (ctx->prev_fiber) { + OrbisFiber* prev_fiber = ctx->prev_fiber; + ctx->prev_fiber = nullptr; + data.state = reinterpret_cast(&prev_fiber->state); + } else { + data.state = nullptr; + } + + data.entry = fiber->entry; + data.arg_on_initialize = fiber->arg_on_initialize; + data.arg_on_run_to = arg_on_run_to; + data.stack_addr = + reinterpret_cast(reinterpret_cast(fiber->addr_context) + fiber->size_context); + if (fiber->flags & FiberFlags::SetFpuRegs) { + data.fpucw = 0x037f; + data.mxcsr = 0x9fc0; + _sceFiberSwitchEntry(&data, true); + } else { + _sceFiberSwitchEntry(&data, false); + } + + __builtin_trap(); +} + +void PS4_SYSV_ABI _sceFiberSwitch(OrbisFiber* cur_fiber, OrbisFiber* fiber, u64 arg_on_run_to, + OrbisFiberContext* ctx) { + ctx->prev_fiber = cur_fiber; + ctx->current_fiber = fiber; + + if (fiber->addr_context == nullptr) { + ctx->prev_fiber = nullptr; + + OrbisFiberData data{}; + data.entry = fiber->entry; + data.arg_on_initialize = fiber->arg_on_initialize; + data.arg_on_run_to = arg_on_run_to; + data.stack_addr = reinterpret_cast(ctx->rsp & ~15); + data.state = reinterpret_cast(&cur_fiber->state); + + if (fiber->flags & FiberFlags::SetFpuRegs) { + data.fpucw = 0x037f; + data.mxcsr = 0x9fc0; + _sceFiberSwitchEntry(&data, true); + } else { + _sceFiberSwitchEntry(&data, false); + } + + __builtin_trap(); + } + + _sceFiberSwitchToFiber(fiber, arg_on_run_to, ctx); + __builtin_trap(); +} + +void PS4_SYSV_ABI _sceFiberTerminate(OrbisFiber* fiber, u64 arg_on_return, OrbisFiberContext* ctx) { + ctx->arg_on_return = arg_on_return; + _sceFiberLongJmp(ctx); + __builtin_trap(); +} + +s32 PS4_SYSV_ABI sceFiberInitialize(OrbisFiber* fiber, const char* name, OrbisFiberEntry entry, + u64 arg_on_initialize, void* addr_context, u64 size_context, + const OrbisFiberOptParam* opt_param, u32 build_ver) { if (!fiber || !name || !entry) { return ORBIS_FIBER_ERROR_NULL; } + if ((u64)fiber & 7 || (u64)addr_context & 15) { + return ORBIS_FIBER_ERROR_ALIGNMENT; + } + if (opt_param && (u64)opt_param & 7) { + return ORBIS_FIBER_ERROR_ALIGNMENT; + } + if (size_context && size_context < ORBIS_FIBER_CONTEXT_MINIMUM_SIZE) { + return ORBIS_FIBER_ERROR_RANGE; + } + if (size_context & 15) { + return ORBIS_FIBER_ERROR_INVALID; + } + if (!addr_context && size_context) { + return ORBIS_FIBER_ERROR_INVALID; + } + if (addr_context && !size_context) { + return ORBIS_FIBER_ERROR_INVALID; + } + if (opt_param && opt_param->magic != kFiberOptSignature) { + return ORBIS_FIBER_ERROR_INVALID; + } - fiber->signature = kFiberSignature; + u32 flags = FiberFlags::None; + if (build_ver >= 0x3500000) { + flags |= FiberFlags::SetFpuRegs; + } + if (context_size_check) { + flags |= FiberFlags::ContextSizeCheck; + } - fiber->entry = entry; - fiber->argOnInitialize = argOnInitialize; - - fiber->argRun = 0; - fiber->pArgRun = &fiber->argRun; - fiber->argReturn = 0; - fiber->pArgReturn = &fiber->argReturn; - - fiber->sizeContext = sizeContext; - - fiber->state = FiberState::Init; -#ifdef _WIN64 - fiber->handle = CreateFiber(sizeContext, FiberEntry, fiber); -#else - UNREACHABLE_MSG("Missing implementation"); -#endif strncpy(fiber->name, name, ORBIS_FIBER_MAX_NAME_LENGTH); - return ORBIS_OK; -} + fiber->entry = entry; + fiber->arg_on_initialize = arg_on_initialize; + fiber->addr_context = addr_context; + fiber->size_context = size_context; + fiber->context = nullptr; + fiber->flags = flags; -s32 PS4_SYSV_ABI sceFiberOptParamInitialize(SceFiberOptParam* optParam) { - LOG_ERROR(Lib_Fiber, "called"); - - if (!optParam) { - return ORBIS_FIBER_ERROR_NULL; + /* + A low stack area is problematic, as we can easily + cause a stack overflow with our HLE. + */ + if (size_context && size_context <= 4096) { + LOG_WARNING(Lib_Fiber, "Fiber initialized with small stack area."); } + fiber->magic_start = kFiberSignature0; + fiber->magic_end = kFiberSignature1; + + if (addr_context != nullptr) { + fiber->context_start = addr_context; + fiber->context_end = + reinterpret_cast(reinterpret_cast(addr_context) + size_context); + + /* Apply signature to start of stack */ + *(u64*)addr_context = kFiberStackSignature; + + if (flags & FiberFlags::ContextSizeCheck) { + u64* stack_start = reinterpret_cast(fiber->context_start); + u64* stack_end = reinterpret_cast(fiber->context_end); + + u64* stack_ptr = stack_start + 1; + while (stack_ptr < stack_end) { + *stack_ptr++ = kFiberStackSizeCheck; + } + } + } + + fiber->state = FiberState::Idle; return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFiberFinalize(SceFiber* fiber) { - LOG_TRACE(Lib_Fiber, "called"); +s32 PS4_SYSV_ABI sceFiberOptParamInitialize(OrbisFiberOptParam* opt_param) { + if (!opt_param) { + return ORBIS_FIBER_ERROR_NULL; + } + if ((u64)opt_param & 7) { + return ORBIS_FIBER_ERROR_ALIGNMENT; + } + opt_param->magic = kFiberOptSignature; + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFiberFinalize(OrbisFiber* fiber) { if (!fiber) { return ORBIS_FIBER_ERROR_NULL; } - if ((u64)fiber % 8 != 0) { + if ((u64)fiber & 7) { return ORBIS_FIBER_ERROR_ALIGNMENT; } - if (fiber->signature != kFiberSignature) { + if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) { return ORBIS_FIBER_ERROR_INVALID; } - if (fiber->state != FiberState::Run) { + + FiberState expected = FiberState::Idle; + if (!fiber->state.compare_exchange_strong(expected, FiberState::Terminated)) { return ORBIS_FIBER_ERROR_STATE; } - fiber->signature = 0; - fiber->state = FiberState::None; - -#ifdef _WIN64 - DeleteFiber(fiber->handle); -#else - UNREACHABLE_MSG("Missing implementation"); -#endif return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFiberRun(SceFiber* fiber, u64 argOnRunTo, u64* argOnReturn) { - LOG_TRACE(Lib_Fiber, "called"); - +s32 PS4_SYSV_ABI sceFiberRun(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_return) { if (!fiber) { return ORBIS_FIBER_ERROR_NULL; } - if ((u64)fiber % 8 != 0) { + if ((u64)fiber & 7) { return ORBIS_FIBER_ERROR_ALIGNMENT; } - if (fiber->signature != kFiberSignature) { + if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) { return ORBIS_FIBER_ERROR_INVALID; } - if (fiber->state == FiberState::Run) { + + Core::Tcb* tcb = Core::GetTcbBase(); + if (tcb->tcb_fiber) { + return ORBIS_FIBER_ERROR_PERMISSION; + } + + FiberState expected = FiberState::Idle; + if (!fiber->state.compare_exchange_strong(expected, FiberState::Run)) { return ORBIS_FIBER_ERROR_STATE; } - if (gFiberThread == nullptr) { -#ifdef _WIN64 - gFiberThread = ConvertThreadToFiber(nullptr); -#else - UNREACHABLE_MSG("Missing implementation"); -#endif + OrbisFiberContext ctx{}; + ctx.current_fiber = fiber; + ctx.prev_fiber = nullptr; + ctx.return_val = 0; + + tcb->tcb_fiber = &ctx; + + s32 jmp = _sceFiberSetJmp(&ctx); + if (!jmp) { + if (fiber->addr_context) { + _sceFiberSwitchToFiber(fiber, arg_on_run_to, &ctx); + __builtin_trap(); + } + + OrbisFiberData data{}; + data.entry = fiber->entry; + data.arg_on_initialize = fiber->arg_on_initialize; + data.arg_on_run_to = arg_on_run_to; + data.stack_addr = reinterpret_cast(ctx.rsp & ~15); + data.state = nullptr; + if (fiber->flags & FiberFlags::SetFpuRegs) { + data.fpucw = 0x037f; + data.mxcsr = 0x9fc0; + _sceFiberSwitchEntry(&data, true); + } else { + _sceFiberSwitchEntry(&data, false); + } } - gCurrentFiber = fiber; + OrbisFiber* cur_fiber = ctx.current_fiber; + ctx.current_fiber = nullptr; + cur_fiber->state = FiberState::Idle; - if (fiber->pArgRun != nullptr) { - *fiber->pArgRun = argOnRunTo; + if (ctx.return_val != 0) { + /* Fiber entry returned! This should never happen. */ + UNREACHABLE_MSG("Fiber entry function returned."); } - fiber->pArgReturn = argOnReturn; - fiber->state = FiberState::Run; -#ifdef _WIN64 - SwitchToFiber(fiber->handle); -#else - UNREACHABLE_MSG("Missing implementation"); -#endif + if (arg_on_return) { + *arg_on_return = ctx.arg_on_return; + } + + tcb->tcb_fiber = nullptr; return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFiberSwitch(SceFiber* fiber, u64 argOnRunTo, u64* argOnRun) { - LOG_TRACE(Lib_Fiber, "called"); - +s32 PS4_SYSV_ABI sceFiberSwitch(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_run) { if (!fiber) { return ORBIS_FIBER_ERROR_NULL; } - if ((u64)fiber % 8 != 0) { + if ((u64)fiber & 7) { return ORBIS_FIBER_ERROR_ALIGNMENT; } - if (fiber->signature != kFiberSignature) { + if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) { return ORBIS_FIBER_ERROR_INVALID; } - if (gCurrentFiber == nullptr) { + + OrbisFiberContext* g_ctx = GetFiberContext(); + if (!g_ctx) { return ORBIS_FIBER_ERROR_PERMISSION; } - if (fiber->state == FiberState::Run) { + + FiberState expected = FiberState::Idle; + if (!fiber->state.compare_exchange_strong(expected, FiberState::Run)) { return ORBIS_FIBER_ERROR_STATE; } - gCurrentFiber->state = FiberState::Suspend; + OrbisFiber* cur_fiber = g_ctx->current_fiber; + if (cur_fiber->addr_context == nullptr) { + _sceFiberSwitch(cur_fiber, fiber, arg_on_run_to, g_ctx); + __builtin_trap(); + } - // TODO: argOnRun + OrbisFiberContext ctx{}; + s32 jmp = _sceFiberSetJmp(&ctx); + if (!jmp) { + cur_fiber->context = &ctx; + _sceFiberCheckStackOverflow(g_ctx); + _sceFiberSwitch(cur_fiber, fiber, arg_on_run_to, g_ctx); + __builtin_trap(); + } - *fiber->pArgRun = argOnRunTo; - fiber->state = FiberState::Run; + g_ctx = GetFiberContext(); + if (g_ctx->prev_fiber) { + g_ctx->prev_fiber->state = FiberState::Idle; + g_ctx->prev_fiber = nullptr; + } + + if (arg_on_run) { + *arg_on_run = g_ctx->arg_on_run_to; + } - gCurrentFiber = fiber; -#ifdef _WIN64 - SwitchToFiber(fiber->handle); -#else - UNREACHABLE_MSG("Missing implementation"); -#endif return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFiberGetSelf(SceFiber** fiber) { - LOG_TRACE(Lib_Fiber, "called"); - - if (!fiber || !gCurrentFiber) { - return ORBIS_FIBER_ERROR_NULL; - } - if (gCurrentFiber->signature != kFiberSignature) { - return ORBIS_FIBER_ERROR_PERMISSION; - } - - *fiber = gCurrentFiber; - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceFiberReturnToThread(u64 argOnReturn, u64* argOnRun) { - LOG_TRACE(Lib_Fiber, "called"); - - if (gCurrentFiber->signature != kFiberSignature) { - return ORBIS_FIBER_ERROR_PERMISSION; - } - - if (gCurrentFiber->pArgReturn != nullptr) { - *gCurrentFiber->pArgReturn = argOnReturn; - } - - // TODO: argOnRun - gCurrentFiber->state = FiberState::Suspend; - gCurrentFiber = nullptr; -#ifdef _WIN64 - SwitchToFiber(gFiberThread); -#else - UNREACHABLE_MSG("Missing implementation"); -#endif - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceFiberGetInfo(SceFiber* fiber, SceFiberInfo* fiberInfo) { - LOG_INFO(Lib_Fiber, "called"); - - if (!fiber || !fiberInfo) { +s32 PS4_SYSV_ABI sceFiberGetSelf(OrbisFiber** fiber) { + if (!fiber) { return ORBIS_FIBER_ERROR_NULL; } - fiberInfo->entry = fiber->entry; - fiberInfo->argOnInitialize = fiber->argOnInitialize; - fiberInfo->addrContext = nullptr; - fiberInfo->sizeContext = fiber->sizeContext; - fiberInfo->sizeContextMargin = 0; + OrbisFiberContext* g_ctx = GetFiberContext(); + if (!g_ctx) { + return ORBIS_FIBER_ERROR_PERMISSION; + } + + *fiber = g_ctx->current_fiber; + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFiberReturnToThread(u64 arg_on_return, u64* arg_on_run) { + OrbisFiberContext* g_ctx = GetFiberContext(); + if (!g_ctx) { + return ORBIS_FIBER_ERROR_PERMISSION; + } + + OrbisFiber* cur_fiber = g_ctx->current_fiber; + if (cur_fiber->addr_context) { + OrbisFiberContext ctx{}; + s32 jmp = _sceFiberSetJmp(&ctx); + if (jmp) { + g_ctx = GetFiberContext(); + if (g_ctx->prev_fiber) { + g_ctx->prev_fiber->state = FiberState::Idle; + g_ctx->prev_fiber = nullptr; + } + if (arg_on_run) { + *arg_on_run = g_ctx->arg_on_run_to; + } + return ORBIS_OK; + } + + cur_fiber->context = &ctx; + _sceFiberCheckStackOverflow(g_ctx); + } + + _sceFiberTerminate(cur_fiber, arg_on_return, g_ctx); + __builtin_trap(); +} + +s32 PS4_SYSV_ABI sceFiberGetInfo(OrbisFiber* fiber, OrbisFiberInfo* fiber_info) { + if (!fiber || !fiber_info) { + return ORBIS_FIBER_ERROR_NULL; + } + if ((u64)fiber & 7 || (u64)fiber_info & 7) { + return ORBIS_FIBER_ERROR_ALIGNMENT; + } + if (fiber_info->size != sizeof(OrbisFiberInfo)) { + return ORBIS_FIBER_ERROR_INVALID; + } + if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) { + return ORBIS_FIBER_ERROR_INVALID; + } + + fiber_info->entry = fiber->entry; + fiber_info->arg_on_initialize = fiber->arg_on_initialize; + fiber_info->addr_context = fiber->addr_context; + fiber_info->size_context = fiber->size_context; + strncpy(fiber_info->name, fiber->name, ORBIS_FIBER_MAX_NAME_LENGTH); + + fiber_info->size_context_margin = -1; + if (fiber->flags & FiberFlags::ContextSizeCheck && fiber->addr_context != nullptr) { + u64 stack_margin = 0; + u64* stack_start = reinterpret_cast(fiber->context_start); + u64* stack_end = reinterpret_cast(fiber->context_end); + + if (*stack_start == kFiberStackSignature) { + u64* stack_ptr = stack_start + 1; + while (stack_ptr < stack_end) { + if (*stack_ptr == kFiberStackSizeCheck) { + stack_ptr++; + } + } + + stack_margin = + reinterpret_cast(stack_ptr) - reinterpret_cast(stack_start + 1); + } + + fiber_info->size_context_margin = stack_margin; + } - strncpy(fiberInfo->name, fiber->name, ORBIS_FIBER_MAX_NAME_LENGTH); return ORBIS_OK; } s32 PS4_SYSV_ABI sceFiberStartContextSizeCheck(u32 flags) { - LOG_ERROR(Lib_Fiber, "called"); - if (flags != 0) { return ORBIS_FIBER_ERROR_INVALID; } + u32 expected = 0; + if (!context_size_check.compare_exchange_strong(expected, 1u)) { + return ORBIS_FIBER_ERROR_STATE; + } + return ORBIS_OK; } s32 PS4_SYSV_ABI sceFiberStopContextSizeCheck() { - LOG_ERROR(Lib_Fiber, "called"); + u32 expected = 1; + if (!context_size_check.compare_exchange_strong(expected, 0u)) { + return ORBIS_FIBER_ERROR_STATE; + } + return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFiberRename(SceFiber* fiber, const char* name) { - LOG_INFO(Lib_Fiber, "called, name = {}", name); - +s32 PS4_SYSV_ABI sceFiberRename(OrbisFiber* fiber, const char* name) { if (!fiber || !name) { return ORBIS_FIBER_ERROR_NULL; } - if ((u64)fiber % 8 != 0) { + if ((u64)fiber & 7) { return ORBIS_FIBER_ERROR_ALIGNMENT; } + if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) { + return ORBIS_FIBER_ERROR_INVALID; + } strncpy(fiber->name, name, ORBIS_FIBER_MAX_NAME_LENGTH); return ORBIS_OK; @@ -262,6 +464,7 @@ s32 PS4_SYSV_ABI sceFiberRename(SceFiber* fiber, const char* name) { void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("hVYD7Ou2pCQ", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberInitialize); + LIB_FUNCTION("7+OJIpko9RY", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberInitialize); LIB_FUNCTION("asjUJJ+aa8s", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberOptParamInitialize); LIB_FUNCTION("JeNX5F-NzQU", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberFinalize); diff --git a/src/core/libraries/fiber/fiber.h b/src/core/libraries/fiber/fiber.h index 00099f93b..325522fca 100644 --- a/src/core/libraries/fiber/fiber.h +++ b/src/core/libraries/fiber/fiber.h @@ -6,73 +6,113 @@ #include "common/assert.h" #include "common/types.h" +#include + namespace Core::Loader { class SymbolsResolver; } namespace Libraries::Fiber { #define ORBIS_FIBER_MAX_NAME_LENGTH (31) +#define ORBIS_FIBER_CONTEXT_MINIMUM_SIZE (512) -typedef void PS4_SYSV_ABI (*SceFiberEntry)(u64 argOnInitialize, u64 argOnRun); +typedef void PS4_SYSV_ABI (*OrbisFiberEntry)(u64 arg_on_initialize, u64 arg_on_run); enum FiberState : u32 { - None = 0u, - Init = 1u, - Run = 2u, - Suspend = 3u, + Run = 1u, + Idle = 2u, + Terminated = 3u, }; -struct SceFiber { - u64 signature; - FiberState state; - SceFiberEntry entry; - u64 argOnInitialize; - u64 argRun; - u64* pArgRun; - u64 argReturn; - u64* pArgReturn; - u64 sizeContext; - char name[ORBIS_FIBER_MAX_NAME_LENGTH]; - void* handle; +enum FiberFlags : u32 { + None = 0x0, + NoUlobjmgr = 0x1, + ContextSizeCheck = 0x10, + SetFpuRegs = 0x100, }; -static_assert(sizeof(SceFiber) <= 256); -struct SceFiberInfo { - u64 size; - SceFiberEntry entry; - u64 argOnInitialize; - void* addrContext; - u64 sizeContext; +struct OrbisFiber; + +struct OrbisFiberContext { + struct { + u64 rax, rcx, rdx, rbx, rsp, rbp, r8, r9, r10, r11, r12, r13, r14, r15; + u16 fpucw; + u32 mxcsr; + }; + OrbisFiber* current_fiber; + OrbisFiber* prev_fiber; + u64 arg_on_run_to; + u64 arg_on_return; + u64 return_val; +}; + +struct OrbisFiberData { + OrbisFiberEntry entry; + u64 arg_on_initialize; + u64 arg_on_run_to; + void* stack_addr; + u32* state; + u16 fpucw; + s8 pad[2]; + u32 mxcsr; +}; + +struct OrbisFiber { + u32 magic_start; + std::atomic state; + OrbisFiberEntry entry; + u64 arg_on_initialize; + void* addr_context; + u64 size_context; char name[ORBIS_FIBER_MAX_NAME_LENGTH + 1]; - u64 sizeContextMargin; + OrbisFiberContext* context; + u32 flags; + void* context_start; + void* context_end; + u32 magic_end; }; -static_assert(sizeof(SceFiberInfo) <= 128); +static_assert(sizeof(OrbisFiber) <= 256); -using SceFiberOptParam = void*; +struct OrbisFiberInfo { + u64 size; + OrbisFiberEntry entry; + u64 arg_on_initialize; + void* addr_context; + u64 size_context; + char name[ORBIS_FIBER_MAX_NAME_LENGTH + 1]; + u64 size_context_margin; + u8 pad[48]; +}; +static_assert(sizeof(OrbisFiberInfo) == 128); -s32 PS4_SYSV_ABI sceFiberInitialize(SceFiber* fiber, const char* name, SceFiberEntry entry, - u64 argOnInitialize, void* addrContext, u64 sizeContext, - const SceFiberOptParam* optParam); +struct OrbisFiberOptParam { + u32 magic; +}; +static_assert(sizeof(OrbisFiberOptParam) <= 128); -s32 PS4_SYSV_ABI sceFiberOptParamInitialize(SceFiberOptParam* optParam); +s32 PS4_SYSV_ABI sceFiberInitialize(OrbisFiber* fiber, const char* name, OrbisFiberEntry entry, + u64 arg_on_initialize, void* addr_context, u64 size_context, + const OrbisFiberOptParam* opt_param, u32 build_version); -s32 PS4_SYSV_ABI sceFiberFinalize(SceFiber* fiber); +s32 PS4_SYSV_ABI sceFiberOptParamInitialize(OrbisFiberOptParam* opt_param); -s32 PS4_SYSV_ABI sceFiberRun(SceFiber* fiber, u64 argOnRunTo, u64* argOnReturn); +s32 PS4_SYSV_ABI sceFiberFinalize(OrbisFiber* fiber); -s32 PS4_SYSV_ABI sceFiberSwitch(SceFiber* fiber, u64 argOnRunTo, u64* argOnRun); +s32 PS4_SYSV_ABI sceFiberRun(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_return); -s32 PS4_SYSV_ABI sceFiberGetSelf(SceFiber** fiber); +s32 PS4_SYSV_ABI sceFiberSwitch(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_run); -s32 PS4_SYSV_ABI sceFiberReturnToThread(u64 argOnReturn, u64* argOnRun); +s32 PS4_SYSV_ABI sceFiberGetSelf(OrbisFiber** fiber); -s32 PS4_SYSV_ABI sceFiberGetInfo(SceFiber* fiber, SceFiberInfo* fiberInfo); +s32 PS4_SYSV_ABI sceFiberReturnToThread(u64 arg_on_return, u64* arg_on_run); + +s32 PS4_SYSV_ABI sceFiberGetInfo(OrbisFiber* fiber, OrbisFiberInfo* fiber_info); s32 PS4_SYSV_ABI sceFiberStartContextSizeCheck(u32 flags); s32 PS4_SYSV_ABI sceFiberStopContextSizeCheck(void); -s32 PS4_SYSV_ABI sceFiberRename(SceFiber* fiber, const char* name); +s32 PS4_SYSV_ABI sceFiberRename(OrbisFiber* fiber, const char* name); void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::Fiber \ No newline at end of file diff --git a/src/core/libraries/fiber/fiber_context.s b/src/core/libraries/fiber/fiber_context.s new file mode 100644 index 000000000..44e51d38c --- /dev/null +++ b/src/core/libraries/fiber/fiber_context.s @@ -0,0 +1,121 @@ +# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +.global _sceFiberSetJmp +_sceFiberSetJmp: + movq %rax, 0x0(%rdi) + + movq (%rsp), %rdx + movq %rdx, 0x10(%rdi) + + movq %rcx, 0x08(%rdi) + movq %rbx, 0x18(%rdi) + movq %rsp, 0x20(%rdi) + movq %rbp, 0x28(%rdi) + + movq %r8, 0x30(%rdi) + movq %r9, 0x38(%rdi) + movq %r10, 0x40(%rdi) + movq %r11, 0x48(%rdi) + movq %r12, 0x50(%rdi) + movq %r13, 0x58(%rdi) + movq %r14, 0x60(%rdi) + movq %r15, 0x68(%rdi) + + fnstcw 0x70(%rdi) + stmxcsr 0x72(%rdi) + + xor %eax, %eax + ret + +.global _sceFiberLongJmp +_sceFiberLongJmp: + # MXCSR = (MXCSR & 0x3f) ^ (ctx->mxcsr & ~0x3f) + stmxcsr -0x4(%rsp) + movl 0x72(%rdi), %eax + andl $0xffffffc0, %eax + movl -0x4(%rsp), %ecx + andl $0x3f, %ecx + xorl %eax, %ecx + movl %ecx, -0x4(%rsp) + ldmxcsr -0x4(%rsp) + + movq 0x00(%rdi), %rax + movq 0x08(%rdi), %rcx + movq 0x10(%rdi), %rdx + movq 0x18(%rdi), %rbx + movq 0x20(%rdi), %rsp + movq 0x28(%rdi), %rbp + + movq 0x30(%rdi), %r8 + movq 0x38(%rdi), %r9 + movq 0x40(%rdi), %r10 + movq 0x48(%rdi), %r11 + movq 0x50(%rdi), %r12 + movq 0x58(%rdi), %r13 + movq 0x60(%rdi), %r14 + movq 0x68(%rdi), %r15 + + fldcw 0x70(%rdi) + + # Make the jump and return 1 + movq %rdx, 0x00(%rsp) + movl $0x1, %eax + ret + +.global _sceFiberSwitchEntry +_sceFiberSwitchEntry: + mov %rdi, %r11 + + # Set stack address to provided stack + movq 0x18(%r11), %rsp + xorl %ebp, %ebp + + movq 0x20(%r11), %r10 # data->state + + # Set previous fiber state to Idle + test %r10, %r10 + jz .clear_regs + movl $2, (%r10) + +.clear_regs: + test %esi, %esi + jz .skip_fpu_regs + + ldmxcsr 0x2c(%r11) + fldcw 0x28(%r11) + +.skip_fpu_regs: + movq 0x08(%r11), %rdi # data->arg_on_initialize + movq 0x10(%r11), %rsi # data->arg_on_run_to + movq 0x00(%r11), %r11 # data->entry + + xorl %eax, %eax + xorl %ebx, %ebx + xorl %ecx, %ecx + xorl %edx, %edx + xorq %r8, %r8 + xorq %r9, %r9 + xorq %r10, %r10 + xorq %r12, %r12 + xorq %r13, %r13 + xorq %r14, %r14 + xorq %r15, %r15 + pxor %mm0, %mm0 + pxor %mm1, %mm1 + pxor %mm2, %mm2 + pxor %mm3, %mm3 + pxor %mm4, %mm4 + pxor %mm5, %mm5 + pxor %mm6, %mm6 + pxor %mm7, %mm7 + emms + vzeroall + + # Call the fiber's entry function: entry(arg_on_initialize, arg_on_run_to) + call *%r11 + + # Fiber returned, not good + movl $1, %edi + call _sceFiberForceQuit + ret \ No newline at end of file diff --git a/src/core/libraries/kernel/threads/tcb.cpp b/src/core/libraries/kernel/threads/tcb.cpp index e5a158216..e3e8b90c3 100644 --- a/src/core/libraries/kernel/threads/tcb.cpp +++ b/src/core/libraries/kernel/threads/tcb.cpp @@ -53,6 +53,7 @@ Core::Tcb* TcbCtor(Pthread* thread, int initial) { if (tcb) { tcb->tcb_thread = thread; + tcb->tcb_fiber = nullptr; } return tcb; } diff --git a/src/core/tls.h b/src/core/tls.h index 4df9e4ace..6edd6a297 100644 --- a/src/core/tls.h +++ b/src/core/tls.h @@ -9,6 +9,10 @@ namespace Xbyak { class CodeGenerator; } +namespace Libraries::Fiber { +struct OrbisFiberContext; +} + namespace Core { union DtvEntry { @@ -20,6 +24,7 @@ struct Tcb { Tcb* tcb_self; DtvEntry* tcb_dtv; void* tcb_thread; + ::Libraries::Fiber::OrbisFiberContext* tcb_fiber; }; #ifdef _WIN32 From c284cf72e102a6f3a18e08b63f0f833811847d5a Mon Sep 17 00:00:00 2001 From: "Daniel R." <47796739+polybiusproxy@users.noreply.github.com> Date: Tue, 24 Dec 2024 13:56:31 +0100 Subject: [PATCH 238/549] Switch remaining CRLF terminated files to LF --- src/common/div_ceil.h | 50 +- src/common/ntapi.cpp | 60 +- src/common/ntapi.h | 1108 ++++++------- src/common/spin_lock.cpp | 106 +- src/common/spin_lock.h | 66 +- src/common/unique_function.h | 122 +- src/core/libraries/fiber/fiber.cpp | 968 ++++++------ src/core/libraries/fiber/fiber.h | 234 +-- src/core/libraries/fiber/fiber_context.s | 240 +-- src/core/libraries/ime/ime_common.h | 366 ++--- src/core/libraries/ime/ime_ui.cpp | 506 +++--- src/core/libraries/ime/ime_ui.h | 150 +- src/core/libraries/kernel/sync/mutex.cpp | 102 +- src/core/libraries/kernel/sync/mutex.h | 158 +- src/core/libraries/kernel/sync/semaphore.h | 332 ++-- src/core/libraries/videodec/videodec2.cpp | 398 ++--- src/core/libraries/videodec/videodec2.h | 276 ++-- src/core/libraries/videodec/videodec2_avc.h | 118 +- .../libraries/videodec/videodec2_impl.cpp | 458 +++--- src/core/libraries/videodec/videodec2_impl.h | 76 +- src/core/thread.cpp | 302 ++-- src/core/thread.h | 88 +- .../ir/breadth_first_search.h | 148 +- src/video_core/buffer_cache/buffer.h | 406 ++--- src/video_core/buffer_cache/buffer_cache.cpp | 1392 ++++++++--------- src/video_core/buffer_cache/buffer_cache.h | 336 ++-- .../buffer_cache/memory_tracker_base.h | 350 ++--- src/video_core/buffer_cache/word_manager.h | 796 +++++----- 28 files changed, 4856 insertions(+), 4856 deletions(-) diff --git a/src/common/div_ceil.h b/src/common/div_ceil.h index de275e76f..c12477d42 100755 --- a/src/common/div_ceil.h +++ b/src/common/div_ceil.h @@ -1,25 +1,25 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include - -namespace Common { - -/// Ceiled integer division. -template - requires std::is_integral_v && std::is_unsigned_v -[[nodiscard]] constexpr N DivCeil(N number, D divisor) { - return static_cast((static_cast(number) + divisor - 1) / divisor); -} - -/// Ceiled integer division with logarithmic divisor in base 2 -template - requires std::is_integral_v && std::is_unsigned_v -[[nodiscard]] constexpr N DivCeilLog2(N value, D alignment_log2) { - return static_cast((static_cast(value) + (D(1) << alignment_log2) - 1) >> alignment_log2); -} - -} // namespace Common +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +namespace Common { + +/// Ceiled integer division. +template + requires std::is_integral_v && std::is_unsigned_v +[[nodiscard]] constexpr N DivCeil(N number, D divisor) { + return static_cast((static_cast(number) + divisor - 1) / divisor); +} + +/// Ceiled integer division with logarithmic divisor in base 2 +template + requires std::is_integral_v && std::is_unsigned_v +[[nodiscard]] constexpr N DivCeilLog2(N value, D alignment_log2) { + return static_cast((static_cast(value) + (D(1) << alignment_log2) - 1) >> alignment_log2); +} + +} // namespace Common diff --git a/src/common/ntapi.cpp b/src/common/ntapi.cpp index c76c4657e..46ec57e0a 100644 --- a/src/common/ntapi.cpp +++ b/src/common/ntapi.cpp @@ -1,30 +1,30 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#ifdef _WIN32 - -#include "ntapi.h" - -NtClose_t NtClose = nullptr; -NtSetInformationFile_t NtSetInformationFile = nullptr; -NtCreateThread_t NtCreateThread = nullptr; -NtTerminateThread_t NtTerminateThread = nullptr; -NtQueueApcThreadEx_t NtQueueApcThreadEx = nullptr; - -namespace Common::NtApi { - -void Initialize() { - HMODULE nt_handle = GetModuleHandleA("ntdll.dll"); - - // http://stackoverflow.com/a/31411628/4725495 - NtClose = (NtClose_t)GetProcAddress(nt_handle, "NtClose"); - NtSetInformationFile = - (NtSetInformationFile_t)GetProcAddress(nt_handle, "NtSetInformationFile"); - NtCreateThread = (NtCreateThread_t)GetProcAddress(nt_handle, "NtCreateThread"); - NtTerminateThread = (NtTerminateThread_t)GetProcAddress(nt_handle, "NtTerminateThread"); - NtQueueApcThreadEx = (NtQueueApcThreadEx_t)GetProcAddress(nt_handle, "NtQueueApcThreadEx"); -} - -} // namespace Common::NtApi - -#endif +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifdef _WIN32 + +#include "ntapi.h" + +NtClose_t NtClose = nullptr; +NtSetInformationFile_t NtSetInformationFile = nullptr; +NtCreateThread_t NtCreateThread = nullptr; +NtTerminateThread_t NtTerminateThread = nullptr; +NtQueueApcThreadEx_t NtQueueApcThreadEx = nullptr; + +namespace Common::NtApi { + +void Initialize() { + HMODULE nt_handle = GetModuleHandleA("ntdll.dll"); + + // http://stackoverflow.com/a/31411628/4725495 + NtClose = (NtClose_t)GetProcAddress(nt_handle, "NtClose"); + NtSetInformationFile = + (NtSetInformationFile_t)GetProcAddress(nt_handle, "NtSetInformationFile"); + NtCreateThread = (NtCreateThread_t)GetProcAddress(nt_handle, "NtCreateThread"); + NtTerminateThread = (NtTerminateThread_t)GetProcAddress(nt_handle, "NtTerminateThread"); + NtQueueApcThreadEx = (NtQueueApcThreadEx_t)GetProcAddress(nt_handle, "NtQueueApcThreadEx"); +} + +} // namespace Common::NtApi + +#endif diff --git a/src/common/ntapi.h b/src/common/ntapi.h index daab8440d..c876af58f 100644 --- a/src/common/ntapi.h +++ b/src/common/ntapi.h @@ -1,554 +1,554 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#ifdef _WIN32 - -#include -#include "common/types.h" - -typedef enum _FILE_INFORMATION_CLASS { - FileDirectoryInformation = 1, - FileFullDirectoryInformation = 2, - FileBothDirectoryInformation = 3, - FileBasicInformation = 4, - FileStandardInformation = 5, - FileInternalInformation = 6, - FileEaInformation = 7, - FileAccessInformation = 8, - FileNameInformation = 9, - FileRenameInformation = 10, - FileLinkInformation = 11, - FileNamesInformation = 12, - FileDispositionInformation = 13, - FilePositionInformation = 14, - FileFullEaInformation = 15, - FileModeInformation = 16, - FileAlignmentInformation = 17, - FileAllInformation = 18, - FileAllocationInformation = 19, - FileEndOfFileInformation = 20, - FileAlternateNameInformation = 21, - FileStreamInformation = 22, - FilePipeInformation = 23, - FilePipeLocalInformation = 24, - FilePipeRemoteInformation = 25, - FileMailslotQueryInformation = 26, - FileMailslotSetInformation = 27, - FileCompressionInformation = 28, - FileObjectIdInformation = 29, - FileCompletionInformation = 30, - FileMoveClusterInformation = 31, - FileQuotaInformation = 32, - FileReparsePointInformation = 33, - FileNetworkOpenInformation = 34, - FileAttributeTagInformation = 35, - FileTrackingInformation = 36, - FileIdBothDirectoryInformation = 37, - FileIdFullDirectoryInformation = 38, - FileValidDataLengthInformation = 39, - FileShortNameInformation = 40, - FileIoCompletionNotificationInformation = 41, - FileIoStatusBlockRangeInformation = 42, - FileIoPriorityHintInformation = 43, - FileSfioReserveInformation = 44, - FileSfioVolumeInformation = 45, - FileHardLinkInformation = 46, - FileProcessIdsUsingFileInformation = 47, - FileNormalizedNameInformation = 48, - FileNetworkPhysicalNameInformation = 49, - FileIdGlobalTxDirectoryInformation = 50, - FileIsRemoteDeviceInformation = 51, - FileUnusedInformation = 52, - FileNumaNodeInformation = 53, - FileStandardLinkInformation = 54, - FileRemoteProtocolInformation = 55, - FileRenameInformationBypassAccessCheck = 56, - FileLinkInformationBypassAccessCheck = 57, - FileVolumeNameInformation = 58, - FileIdInformation = 59, - FileIdExtdDirectoryInformation = 60, - FileReplaceCompletionInformation = 61, - FileHardLinkFullIdInformation = 62, - FileIdExtdBothDirectoryInformation = 63, - FileDispositionInformationEx = 64, - FileRenameInformationEx = 65, - FileRenameInformationExBypassAccessCheck = 66, - FileDesiredStorageClassInformation = 67, - FileStatInformation = 68, - FileMemoryPartitionInformation = 69, - FileStatLxInformation = 70, - FileCaseSensitiveInformation = 71, - FileLinkInformationEx = 72, - FileLinkInformationExBypassAccessCheck = 73, - FileStorageReserveIdInformation = 74, - FileCaseSensitiveInformationForceAccessCheck = 75, - FileKnownFolderInformation = 76, - FileStatBasicInformation = 77, - FileId64ExtdDirectoryInformation = 78, - FileId64ExtdBothDirectoryInformation = 79, - FileIdAllExtdDirectoryInformation = 80, - FileIdAllExtdBothDirectoryInformation = 81, - FileStreamReservationInformation, - FileMupProviderInfo, - FileMaximumInformation -} FILE_INFORMATION_CLASS, - *PFILE_INFORMATION_CLASS; - -typedef struct _IO_STATUS_BLOCK { - union { - u32 Status; - PVOID Pointer; - }; - ULONG_PTR Information; -} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; - -typedef struct _FILE_DISPOSITION_INFORMATION { - BOOLEAN DeleteFile; -} FILE_DISPOSITION_INFORMATION, *PFILE_DISPOSITION_INFORMATION; - -typedef struct _UNICODE_STRING { - USHORT Length; - USHORT MaximumLength; - PWCH Buffer; -} UNICODE_STRING, *PUNICODE_STRING; - -typedef const UNICODE_STRING* PCUNICODE_STRING; - -typedef struct _OBJECT_ATTRIBUTES { - ULONG Length; - HANDLE RootDirectory; - PCUNICODE_STRING ObjectName; - ULONG Attributes; - PVOID SecurityDescriptor; // PSECURITY_DESCRIPTOR; - PVOID SecurityQualityOfService; // PSECURITY_QUALITY_OF_SERVICE -} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES; - -typedef const OBJECT_ATTRIBUTES* PCOBJECT_ATTRIBUTES; - -typedef struct _CLIENT_ID { - HANDLE UniqueProcess; - HANDLE UniqueThread; -} CLIENT_ID, *PCLIENT_ID; - -typedef struct _INITIAL_TEB { - struct { - PVOID OldStackBase; - PVOID OldStackLimit; - } OldInitialTeb; - PVOID StackBase; - PVOID StackLimit; - PVOID StackAllocationBase; -} INITIAL_TEB, *PINITIAL_TEB; - -typedef struct _PEB_LDR_DATA { - ULONG Length; - BOOLEAN Initialized; - PVOID SsHandle; - LIST_ENTRY InLoadOrderModuleList; - LIST_ENTRY InMemoryOrderModuleList; - LIST_ENTRY InInitializationOrderModuleList; - PVOID EntryInProgress; - BOOLEAN ShutdownInProgress; - HANDLE ShutdownThreadId; -} PEB_LDR_DATA, *PPEB_LDR_DATA; - -typedef struct _CURDIR { - UNICODE_STRING DosPath; - PVOID Handle; -} CURDIR, *PCURDIR; - -typedef struct RTL_DRIVE_LETTER_CURDIR { - USHORT Flags; - USHORT Length; - ULONG TimeStamp; - UNICODE_STRING DosPath; -} RTL_DRIVE_LETTER_CURDIR, *PRTL_DRIVE_LETTER_CURDIR; - -typedef struct _RTL_USER_PROCESS_PARAMETERS { - ULONG AllocationSize; - ULONG Size; - ULONG Flags; - ULONG DebugFlags; - HANDLE ConsoleHandle; - ULONG ConsoleFlags; - HANDLE hStdInput; - HANDLE hStdOutput; - HANDLE hStdError; - CURDIR CurrentDirectory; - UNICODE_STRING DllPath; - UNICODE_STRING ImagePathName; - UNICODE_STRING CommandLine; - PWSTR Environment; - ULONG dwX; - ULONG dwY; - ULONG dwXSize; - ULONG dwYSize; - ULONG dwXCountChars; - ULONG dwYCountChars; - ULONG dwFillAttribute; - ULONG dwFlags; - ULONG wShowWindow; - UNICODE_STRING WindowTitle; - UNICODE_STRING Desktop; - UNICODE_STRING ShellInfo; - UNICODE_STRING RuntimeInfo; - RTL_DRIVE_LETTER_CURDIR DLCurrentDirectory[0x20]; - ULONG_PTR EnvironmentSize; - ULONG_PTR EnvironmentVersion; - PVOID PackageDependencyData; - ULONG ProcessGroupId; - ULONG LoaderThreads; -} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS; - -typedef struct tagRTL_BITMAP { - ULONG SizeOfBitMap; - PULONG Buffer; -} RTL_BITMAP, *PRTL_BITMAP; - -typedef struct { - UINT next; - UINT id; - ULONGLONG addr; - ULONGLONG size; - UINT args[4]; -} CROSS_PROCESS_WORK_ENTRY; - -typedef union { - struct { - UINT first; - UINT counter; - }; - volatile LONGLONG hdr; -} CROSS_PROCESS_WORK_HDR; - -typedef struct { - CROSS_PROCESS_WORK_HDR free_list; - CROSS_PROCESS_WORK_HDR work_list; - ULONGLONG unknown[4]; - CROSS_PROCESS_WORK_ENTRY entries[1]; -} CROSS_PROCESS_WORK_LIST; - -typedef struct _CHPEV2_PROCESS_INFO { - ULONG Wow64ExecuteFlags; /* 000 */ - USHORT NativeMachineType; /* 004 */ - USHORT EmulatedMachineType; /* 006 */ - HANDLE SectionHandle; /* 008 */ - CROSS_PROCESS_WORK_LIST* CrossProcessWorkList; /* 010 */ - void* unknown; /* 018 */ -} CHPEV2_PROCESS_INFO, *PCHPEV2_PROCESS_INFO; - -typedef u64(__stdcall* KERNEL_CALLBACK_PROC)(void*, ULONG); - -typedef struct _PEB { /* win32/win64 */ - BOOLEAN InheritedAddressSpace; /* 000/000 */ - BOOLEAN ReadImageFileExecOptions; /* 001/001 */ - BOOLEAN BeingDebugged; /* 002/002 */ - UCHAR ImageUsedLargePages : 1; /* 003/003 */ - UCHAR IsProtectedProcess : 1; - UCHAR IsImageDynamicallyRelocated : 1; - UCHAR SkipPatchingUser32Forwarders : 1; - UCHAR IsPackagedProcess : 1; - UCHAR IsAppContainer : 1; - UCHAR IsProtectedProcessLight : 1; - UCHAR IsLongPathAwareProcess : 1; - HANDLE Mutant; /* 004/008 */ - HMODULE ImageBaseAddress; /* 008/010 */ - PPEB_LDR_DATA LdrData; /* 00c/018 */ - RTL_USER_PROCESS_PARAMETERS* ProcessParameters; /* 010/020 */ - PVOID SubSystemData; /* 014/028 */ - HANDLE ProcessHeap; /* 018/030 */ - PRTL_CRITICAL_SECTION FastPebLock; /* 01c/038 */ - PVOID AtlThunkSListPtr; /* 020/040 */ - PVOID IFEOKey; /* 024/048 */ - ULONG ProcessInJob : 1; /* 028/050 */ - ULONG ProcessInitializing : 1; - ULONG ProcessUsingVEH : 1; - ULONG ProcessUsingVCH : 1; - ULONG ProcessUsingFTH : 1; - ULONG ProcessPreviouslyThrottled : 1; - ULONG ProcessCurrentlyThrottled : 1; - ULONG ProcessImagesHotPatched : 1; - ULONG ReservedBits0 : 24; - KERNEL_CALLBACK_PROC* KernelCallbackTable; /* 02c/058 */ - ULONG Reserved; /* 030/060 */ - ULONG AtlThunkSListPtr32; /* 034/064 */ - PVOID ApiSetMap; /* 038/068 */ - ULONG TlsExpansionCounter; /* 03c/070 */ - PRTL_BITMAP TlsBitmap; /* 040/078 */ - ULONG TlsBitmapBits[2]; /* 044/080 */ - PVOID ReadOnlySharedMemoryBase; /* 04c/088 */ - PVOID SharedData; /* 050/090 */ - PVOID* ReadOnlyStaticServerData; /* 054/098 */ - PVOID AnsiCodePageData; /* 058/0a0 */ - PVOID OemCodePageData; /* 05c/0a8 */ - PVOID UnicodeCaseTableData; /* 060/0b0 */ - ULONG NumberOfProcessors; /* 064/0b8 */ - ULONG NtGlobalFlag; /* 068/0bc */ - LARGE_INTEGER CriticalSectionTimeout; /* 070/0c0 */ - SIZE_T HeapSegmentReserve; /* 078/0c8 */ - SIZE_T HeapSegmentCommit; /* 07c/0d0 */ - SIZE_T HeapDeCommitTotalFreeThreshold; /* 080/0d8 */ - SIZE_T HeapDeCommitFreeBlockThreshold; /* 084/0e0 */ - ULONG NumberOfHeaps; /* 088/0e8 */ - ULONG MaximumNumberOfHeaps; /* 08c/0ec */ - PVOID* ProcessHeaps; /* 090/0f0 */ - PVOID GdiSharedHandleTable; /* 094/0f8 */ - PVOID ProcessStarterHelper; /* 098/100 */ - PVOID GdiDCAttributeList; /* 09c/108 */ - PVOID LoaderLock; /* 0a0/110 */ - ULONG OSMajorVersion; /* 0a4/118 */ - ULONG OSMinorVersion; /* 0a8/11c */ - ULONG OSBuildNumber; /* 0ac/120 */ - ULONG OSPlatformId; /* 0b0/124 */ - ULONG ImageSubSystem; /* 0b4/128 */ - ULONG ImageSubSystemMajorVersion; /* 0b8/12c */ - ULONG ImageSubSystemMinorVersion; /* 0bc/130 */ - KAFFINITY ActiveProcessAffinityMask; /* 0c0/138 */ -#ifdef _WIN64 - ULONG GdiHandleBuffer[60]; /* /140 */ -#else - ULONG GdiHandleBuffer[34]; /* 0c4/ */ -#endif - PVOID PostProcessInitRoutine; /* 14c/230 */ - PRTL_BITMAP TlsExpansionBitmap; /* 150/238 */ - ULONG TlsExpansionBitmapBits[32]; /* 154/240 */ - ULONG SessionId; /* 1d4/2c0 */ - ULARGE_INTEGER AppCompatFlags; /* 1d8/2c8 */ - ULARGE_INTEGER AppCompatFlagsUser; /* 1e0/2d0 */ - PVOID ShimData; /* 1e8/2d8 */ - PVOID AppCompatInfo; /* 1ec/2e0 */ - UNICODE_STRING CSDVersion; /* 1f0/2e8 */ - PVOID ActivationContextData; /* 1f8/2f8 */ - PVOID ProcessAssemblyStorageMap; /* 1fc/300 */ - PVOID SystemDefaultActivationData; /* 200/308 */ - PVOID SystemAssemblyStorageMap; /* 204/310 */ - SIZE_T MinimumStackCommit; /* 208/318 */ - PVOID* FlsCallback; /* 20c/320 */ - LIST_ENTRY FlsListHead; /* 210/328 */ - union { - PRTL_BITMAP FlsBitmap; /* 218/338 */ -#ifdef _WIN64 - CHPEV2_PROCESS_INFO* ChpeV2ProcessInfo; /* /338 */ -#endif - }; - ULONG FlsBitmapBits[4]; /* 21c/340 */ - ULONG FlsHighIndex; /* 22c/350 */ - PVOID WerRegistrationData; /* 230/358 */ - PVOID WerShipAssertPtr; /* 234/360 */ - PVOID EcCodeBitMap; /* 238/368 */ - PVOID pImageHeaderHash; /* 23c/370 */ - ULONG HeapTracingEnabled : 1; /* 240/378 */ - ULONG CritSecTracingEnabled : 1; - ULONG LibLoaderTracingEnabled : 1; - ULONG SpareTracingBits : 29; - ULONGLONG CsrServerReadOnlySharedMemoryBase; /* 248/380 */ - ULONG TppWorkerpListLock; /* 250/388 */ - LIST_ENTRY TppWorkerpList; /* 254/390 */ - PVOID WaitOnAddressHashTable[0x80]; /* 25c/3a0 */ - PVOID TelemetryCoverageHeader; /* 45c/7a0 */ - ULONG CloudFileFlags; /* 460/7a8 */ - ULONG CloudFileDiagFlags; /* 464/7ac */ - CHAR PlaceholderCompatibilityMode; /* 468/7b0 */ - CHAR PlaceholderCompatibilityModeReserved[7]; /* 469/7b1 */ - PVOID LeapSecondData; /* 470/7b8 */ - ULONG LeapSecondFlags; /* 474/7c0 */ - ULONG NtGlobalFlag2; /* 478/7c4 */ -} PEB, *PPEB; - -typedef struct _RTL_ACTIVATION_CONTEXT_STACK_FRAME { - struct _RTL_ACTIVATION_CONTEXT_STACK_FRAME* Previous; - struct _ACTIVATION_CONTEXT* ActivationContext; - ULONG Flags; -} RTL_ACTIVATION_CONTEXT_STACK_FRAME, *PRTL_ACTIVATION_CONTEXT_STACK_FRAME; - -typedef struct _ACTIVATION_CONTEXT_STACK { - RTL_ACTIVATION_CONTEXT_STACK_FRAME* ActiveFrame; - LIST_ENTRY FrameListCache; - ULONG Flags; - ULONG NextCookieSequenceNumber; - ULONG_PTR StackId; -} ACTIVATION_CONTEXT_STACK, *PACTIVATION_CONTEXT_STACK; - -typedef struct _GDI_TEB_BATCH { - ULONG Offset; - HANDLE HDC; - ULONG Buffer[0x136]; -} GDI_TEB_BATCH; - -typedef struct _TEB_ACTIVE_FRAME_CONTEXT { - ULONG Flags; - const char* FrameName; -} TEB_ACTIVE_FRAME_CONTEXT, *PTEB_ACTIVE_FRAME_CONTEXT; - -typedef struct _TEB_ACTIVE_FRAME { - ULONG Flags; - struct _TEB_ACTIVE_FRAME* Previous; - TEB_ACTIVE_FRAME_CONTEXT* Context; -} TEB_ACTIVE_FRAME, *PTEB_ACTIVE_FRAME; - -typedef struct _TEB { /* win32/win64 */ - NT_TIB Tib; /* 000/0000 */ - PVOID EnvironmentPointer; /* 01c/0038 */ - CLIENT_ID ClientId; /* 020/0040 */ - PVOID ActiveRpcHandle; /* 028/0050 */ - PVOID ThreadLocalStoragePointer; /* 02c/0058 */ - PPEB Peb; /* 030/0060 */ - ULONG LastErrorValue; /* 034/0068 */ - ULONG CountOfOwnedCriticalSections; /* 038/006c */ - PVOID CsrClientThread; /* 03c/0070 */ - PVOID Win32ThreadInfo; /* 040/0078 */ - ULONG User32Reserved[26]; /* 044/0080 */ - ULONG UserReserved[5]; /* 0ac/00e8 */ - PVOID WOW32Reserved; /* 0c0/0100 */ - ULONG CurrentLocale; /* 0c4/0108 */ - ULONG FpSoftwareStatusRegister; /* 0c8/010c */ - PVOID ReservedForDebuggerInstrumentation[16]; /* 0cc/0110 */ -#ifdef _WIN64 - PVOID SystemReserved1[30]; /* /0190 */ -#else - PVOID SystemReserved1[26]; /* 10c/ */ -#endif - char PlaceholderCompatibilityMode; /* 174/0280 */ - BOOLEAN PlaceholderHydrationAlwaysExplicit; /* 175/0281 */ - char PlaceholderReserved[10]; /* 176/0282 */ - DWORD ProxiedProcessId; /* 180/028c */ - ACTIVATION_CONTEXT_STACK ActivationContextStack; /* 184/0290 */ - UCHAR WorkingOnBehalfOfTicket[8]; /* 19c/02b8 */ - LONG ExceptionCode; /* 1a4/02c0 */ - ACTIVATION_CONTEXT_STACK* ActivationContextStackPointer; /* 1a8/02c8 */ - ULONG_PTR InstrumentationCallbackSp; /* 1ac/02d0 */ - ULONG_PTR InstrumentationCallbackPreviousPc; /* 1b0/02d8 */ - ULONG_PTR InstrumentationCallbackPreviousSp; /* 1b4/02e0 */ -#ifdef _WIN64 - ULONG TxFsContext; /* /02e8 */ - BOOLEAN InstrumentationCallbackDisabled; /* /02ec */ - BOOLEAN UnalignedLoadStoreExceptions; /* /02ed */ -#else - BOOLEAN InstrumentationCallbackDisabled; /* 1b8/ */ - BYTE SpareBytes1[23]; /* 1b9/ */ - ULONG TxFsContext; /* 1d0/ */ -#endif - GDI_TEB_BATCH GdiTebBatch; /* 1d4/02f0 */ - CLIENT_ID RealClientId; /* 6b4/07d8 */ - HANDLE GdiCachedProcessHandle; /* 6bc/07e8 */ - ULONG GdiClientPID; /* 6c0/07f0 */ - ULONG GdiClientTID; /* 6c4/07f4 */ - PVOID GdiThreadLocaleInfo; /* 6c8/07f8 */ - ULONG_PTR Win32ClientInfo[62]; /* 6cc/0800 */ - PVOID glDispatchTable[233]; /* 7c4/09f0 */ - PVOID glReserved1[29]; /* b68/1138 */ - PVOID glReserved2; /* bdc/1220 */ - PVOID glSectionInfo; /* be0/1228 */ - PVOID glSection; /* be4/1230 */ - PVOID glTable; /* be8/1238 */ - PVOID glCurrentRC; /* bec/1240 */ - PVOID glContext; /* bf0/1248 */ - ULONG LastStatusValue; /* bf4/1250 */ - UNICODE_STRING StaticUnicodeString; /* bf8/1258 */ - WCHAR StaticUnicodeBuffer[261]; /* c00/1268 */ - PVOID DeallocationStack; /* e0c/1478 */ - PVOID TlsSlots[64]; /* e10/1480 */ - LIST_ENTRY TlsLinks; /* f10/1680 */ - PVOID Vdm; /* f18/1690 */ - PVOID ReservedForNtRpc; /* f1c/1698 */ - PVOID DbgSsReserved[2]; /* f20/16a0 */ - ULONG HardErrorMode; /* f28/16b0 */ -#ifdef _WIN64 - PVOID Instrumentation[11]; /* /16b8 */ -#else - PVOID Instrumentation[9]; /* f2c/ */ -#endif - GUID ActivityId; /* f50/1710 */ - PVOID SubProcessTag; /* f60/1720 */ - PVOID PerflibData; /* f64/1728 */ - PVOID EtwTraceData; /* f68/1730 */ - PVOID WinSockData; /* f6c/1738 */ - ULONG GdiBatchCount; /* f70/1740 */ - ULONG IdealProcessorValue; /* f74/1744 */ - ULONG GuaranteedStackBytes; /* f78/1748 */ - PVOID ReservedForPerf; /* f7c/1750 */ - PVOID ReservedForOle; /* f80/1758 */ - ULONG WaitingOnLoaderLock; /* f84/1760 */ - PVOID SavedPriorityState; /* f88/1768 */ - ULONG_PTR ReservedForCodeCoverage; /* f8c/1770 */ - PVOID ThreadPoolData; /* f90/1778 */ - PVOID* TlsExpansionSlots; /* f94/1780 */ -#ifdef _WIN64 - union { - PVOID DeallocationBStore; /* /1788 */ - PVOID* ChpeV2CpuAreaInfo; /* /1788 */ - } DUMMYUNIONNAME; - PVOID BStoreLimit; /* /1790 */ -#endif - ULONG MuiGeneration; /* f98/1798 */ - ULONG IsImpersonating; /* f9c/179c */ - PVOID NlsCache; /* fa0/17a0 */ - PVOID ShimData; /* fa4/17a8 */ - ULONG HeapVirtualAffinity; /* fa8/17b0 */ - PVOID CurrentTransactionHandle; /* fac/17b8 */ - TEB_ACTIVE_FRAME* ActiveFrame; /* fb0/17c0 */ - PVOID* FlsSlots; /* fb4/17c8 */ - PVOID PreferredLanguages; /* fb8/17d0 */ - PVOID UserPrefLanguages; /* fbc/17d8 */ - PVOID MergedPrefLanguages; /* fc0/17e0 */ - ULONG MuiImpersonation; /* fc4/17e8 */ - USHORT CrossTebFlags; /* fc8/17ec */ - USHORT SameTebFlags; /* fca/17ee */ - PVOID TxnScopeEnterCallback; /* fcc/17f0 */ - PVOID TxnScopeExitCallback; /* fd0/17f8 */ - PVOID TxnScopeContext; /* fd4/1800 */ - ULONG LockCount; /* fd8/1808 */ - LONG WowTebOffset; /* fdc/180c */ - PVOID ResourceRetValue; /* fe0/1810 */ - PVOID ReservedForWdf; /* fe4/1818 */ - ULONGLONG ReservedForCrt; /* fe8/1820 */ - GUID EffectiveContainerId; /* ff0/1828 */ -} TEB, *PTEB; -static_assert(offsetof(TEB, DeallocationStack) == - 0x1478); /* The only member we care about at the moment */ - -typedef enum _QUEUE_USER_APC_FLAGS { - QueueUserApcFlagsNone, - QueueUserApcFlagsSpecialUserApc, - QueueUserApcFlagsMaxValue -} QUEUE_USER_APC_FLAGS; - -typedef union _USER_APC_OPTION { - ULONG_PTR UserApcFlags; - HANDLE MemoryReserveHandle; -} USER_APC_OPTION, *PUSER_APC_OPTION; - -using PPS_APC_ROUTINE = void (*)(PVOID ApcArgument1, PVOID ApcArgument2, PVOID ApcArgument3, - PCONTEXT Context); - -typedef u64(__stdcall* NtClose_t)(HANDLE Handle); - -typedef u64(__stdcall* NtSetInformationFile_t)(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, - PVOID FileInformation, ULONG Length, - FILE_INFORMATION_CLASS FileInformationClass); - -typedef u64(__stdcall* NtCreateThread_t)(PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess, - PCOBJECT_ATTRIBUTES ObjectAttributes, HANDLE ProcessHandle, - PCLIENT_ID ClientId, PCONTEXT ThreadContext, - PINITIAL_TEB InitialTeb, BOOLEAN CreateSuspended); - -typedef u64(__stdcall* NtTerminateThread_t)(HANDLE ThreadHandle, u64 ExitStatus); - -typedef u64(__stdcall* NtQueueApcThreadEx_t)(HANDLE ThreadHandle, - USER_APC_OPTION UserApcReserveHandle, - PPS_APC_ROUTINE ApcRoutine, PVOID ApcArgument1, - PVOID ApcArgument2, PVOID ApcArgument3); - -extern NtClose_t NtClose; -extern NtSetInformationFile_t NtSetInformationFile; -extern NtCreateThread_t NtCreateThread; -extern NtTerminateThread_t NtTerminateThread; -extern NtQueueApcThreadEx_t NtQueueApcThreadEx; - -namespace Common::NtApi { -void Initialize(); -} - -#endif +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#ifdef _WIN32 + +#include +#include "common/types.h" + +typedef enum _FILE_INFORMATION_CLASS { + FileDirectoryInformation = 1, + FileFullDirectoryInformation = 2, + FileBothDirectoryInformation = 3, + FileBasicInformation = 4, + FileStandardInformation = 5, + FileInternalInformation = 6, + FileEaInformation = 7, + FileAccessInformation = 8, + FileNameInformation = 9, + FileRenameInformation = 10, + FileLinkInformation = 11, + FileNamesInformation = 12, + FileDispositionInformation = 13, + FilePositionInformation = 14, + FileFullEaInformation = 15, + FileModeInformation = 16, + FileAlignmentInformation = 17, + FileAllInformation = 18, + FileAllocationInformation = 19, + FileEndOfFileInformation = 20, + FileAlternateNameInformation = 21, + FileStreamInformation = 22, + FilePipeInformation = 23, + FilePipeLocalInformation = 24, + FilePipeRemoteInformation = 25, + FileMailslotQueryInformation = 26, + FileMailslotSetInformation = 27, + FileCompressionInformation = 28, + FileObjectIdInformation = 29, + FileCompletionInformation = 30, + FileMoveClusterInformation = 31, + FileQuotaInformation = 32, + FileReparsePointInformation = 33, + FileNetworkOpenInformation = 34, + FileAttributeTagInformation = 35, + FileTrackingInformation = 36, + FileIdBothDirectoryInformation = 37, + FileIdFullDirectoryInformation = 38, + FileValidDataLengthInformation = 39, + FileShortNameInformation = 40, + FileIoCompletionNotificationInformation = 41, + FileIoStatusBlockRangeInformation = 42, + FileIoPriorityHintInformation = 43, + FileSfioReserveInformation = 44, + FileSfioVolumeInformation = 45, + FileHardLinkInformation = 46, + FileProcessIdsUsingFileInformation = 47, + FileNormalizedNameInformation = 48, + FileNetworkPhysicalNameInformation = 49, + FileIdGlobalTxDirectoryInformation = 50, + FileIsRemoteDeviceInformation = 51, + FileUnusedInformation = 52, + FileNumaNodeInformation = 53, + FileStandardLinkInformation = 54, + FileRemoteProtocolInformation = 55, + FileRenameInformationBypassAccessCheck = 56, + FileLinkInformationBypassAccessCheck = 57, + FileVolumeNameInformation = 58, + FileIdInformation = 59, + FileIdExtdDirectoryInformation = 60, + FileReplaceCompletionInformation = 61, + FileHardLinkFullIdInformation = 62, + FileIdExtdBothDirectoryInformation = 63, + FileDispositionInformationEx = 64, + FileRenameInformationEx = 65, + FileRenameInformationExBypassAccessCheck = 66, + FileDesiredStorageClassInformation = 67, + FileStatInformation = 68, + FileMemoryPartitionInformation = 69, + FileStatLxInformation = 70, + FileCaseSensitiveInformation = 71, + FileLinkInformationEx = 72, + FileLinkInformationExBypassAccessCheck = 73, + FileStorageReserveIdInformation = 74, + FileCaseSensitiveInformationForceAccessCheck = 75, + FileKnownFolderInformation = 76, + FileStatBasicInformation = 77, + FileId64ExtdDirectoryInformation = 78, + FileId64ExtdBothDirectoryInformation = 79, + FileIdAllExtdDirectoryInformation = 80, + FileIdAllExtdBothDirectoryInformation = 81, + FileStreamReservationInformation, + FileMupProviderInfo, + FileMaximumInformation +} FILE_INFORMATION_CLASS, + *PFILE_INFORMATION_CLASS; + +typedef struct _IO_STATUS_BLOCK { + union { + u32 Status; + PVOID Pointer; + }; + ULONG_PTR Information; +} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; + +typedef struct _FILE_DISPOSITION_INFORMATION { + BOOLEAN DeleteFile; +} FILE_DISPOSITION_INFORMATION, *PFILE_DISPOSITION_INFORMATION; + +typedef struct _UNICODE_STRING { + USHORT Length; + USHORT MaximumLength; + PWCH Buffer; +} UNICODE_STRING, *PUNICODE_STRING; + +typedef const UNICODE_STRING* PCUNICODE_STRING; + +typedef struct _OBJECT_ATTRIBUTES { + ULONG Length; + HANDLE RootDirectory; + PCUNICODE_STRING ObjectName; + ULONG Attributes; + PVOID SecurityDescriptor; // PSECURITY_DESCRIPTOR; + PVOID SecurityQualityOfService; // PSECURITY_QUALITY_OF_SERVICE +} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES; + +typedef const OBJECT_ATTRIBUTES* PCOBJECT_ATTRIBUTES; + +typedef struct _CLIENT_ID { + HANDLE UniqueProcess; + HANDLE UniqueThread; +} CLIENT_ID, *PCLIENT_ID; + +typedef struct _INITIAL_TEB { + struct { + PVOID OldStackBase; + PVOID OldStackLimit; + } OldInitialTeb; + PVOID StackBase; + PVOID StackLimit; + PVOID StackAllocationBase; +} INITIAL_TEB, *PINITIAL_TEB; + +typedef struct _PEB_LDR_DATA { + ULONG Length; + BOOLEAN Initialized; + PVOID SsHandle; + LIST_ENTRY InLoadOrderModuleList; + LIST_ENTRY InMemoryOrderModuleList; + LIST_ENTRY InInitializationOrderModuleList; + PVOID EntryInProgress; + BOOLEAN ShutdownInProgress; + HANDLE ShutdownThreadId; +} PEB_LDR_DATA, *PPEB_LDR_DATA; + +typedef struct _CURDIR { + UNICODE_STRING DosPath; + PVOID Handle; +} CURDIR, *PCURDIR; + +typedef struct RTL_DRIVE_LETTER_CURDIR { + USHORT Flags; + USHORT Length; + ULONG TimeStamp; + UNICODE_STRING DosPath; +} RTL_DRIVE_LETTER_CURDIR, *PRTL_DRIVE_LETTER_CURDIR; + +typedef struct _RTL_USER_PROCESS_PARAMETERS { + ULONG AllocationSize; + ULONG Size; + ULONG Flags; + ULONG DebugFlags; + HANDLE ConsoleHandle; + ULONG ConsoleFlags; + HANDLE hStdInput; + HANDLE hStdOutput; + HANDLE hStdError; + CURDIR CurrentDirectory; + UNICODE_STRING DllPath; + UNICODE_STRING ImagePathName; + UNICODE_STRING CommandLine; + PWSTR Environment; + ULONG dwX; + ULONG dwY; + ULONG dwXSize; + ULONG dwYSize; + ULONG dwXCountChars; + ULONG dwYCountChars; + ULONG dwFillAttribute; + ULONG dwFlags; + ULONG wShowWindow; + UNICODE_STRING WindowTitle; + UNICODE_STRING Desktop; + UNICODE_STRING ShellInfo; + UNICODE_STRING RuntimeInfo; + RTL_DRIVE_LETTER_CURDIR DLCurrentDirectory[0x20]; + ULONG_PTR EnvironmentSize; + ULONG_PTR EnvironmentVersion; + PVOID PackageDependencyData; + ULONG ProcessGroupId; + ULONG LoaderThreads; +} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS; + +typedef struct tagRTL_BITMAP { + ULONG SizeOfBitMap; + PULONG Buffer; +} RTL_BITMAP, *PRTL_BITMAP; + +typedef struct { + UINT next; + UINT id; + ULONGLONG addr; + ULONGLONG size; + UINT args[4]; +} CROSS_PROCESS_WORK_ENTRY; + +typedef union { + struct { + UINT first; + UINT counter; + }; + volatile LONGLONG hdr; +} CROSS_PROCESS_WORK_HDR; + +typedef struct { + CROSS_PROCESS_WORK_HDR free_list; + CROSS_PROCESS_WORK_HDR work_list; + ULONGLONG unknown[4]; + CROSS_PROCESS_WORK_ENTRY entries[1]; +} CROSS_PROCESS_WORK_LIST; + +typedef struct _CHPEV2_PROCESS_INFO { + ULONG Wow64ExecuteFlags; /* 000 */ + USHORT NativeMachineType; /* 004 */ + USHORT EmulatedMachineType; /* 006 */ + HANDLE SectionHandle; /* 008 */ + CROSS_PROCESS_WORK_LIST* CrossProcessWorkList; /* 010 */ + void* unknown; /* 018 */ +} CHPEV2_PROCESS_INFO, *PCHPEV2_PROCESS_INFO; + +typedef u64(__stdcall* KERNEL_CALLBACK_PROC)(void*, ULONG); + +typedef struct _PEB { /* win32/win64 */ + BOOLEAN InheritedAddressSpace; /* 000/000 */ + BOOLEAN ReadImageFileExecOptions; /* 001/001 */ + BOOLEAN BeingDebugged; /* 002/002 */ + UCHAR ImageUsedLargePages : 1; /* 003/003 */ + UCHAR IsProtectedProcess : 1; + UCHAR IsImageDynamicallyRelocated : 1; + UCHAR SkipPatchingUser32Forwarders : 1; + UCHAR IsPackagedProcess : 1; + UCHAR IsAppContainer : 1; + UCHAR IsProtectedProcessLight : 1; + UCHAR IsLongPathAwareProcess : 1; + HANDLE Mutant; /* 004/008 */ + HMODULE ImageBaseAddress; /* 008/010 */ + PPEB_LDR_DATA LdrData; /* 00c/018 */ + RTL_USER_PROCESS_PARAMETERS* ProcessParameters; /* 010/020 */ + PVOID SubSystemData; /* 014/028 */ + HANDLE ProcessHeap; /* 018/030 */ + PRTL_CRITICAL_SECTION FastPebLock; /* 01c/038 */ + PVOID AtlThunkSListPtr; /* 020/040 */ + PVOID IFEOKey; /* 024/048 */ + ULONG ProcessInJob : 1; /* 028/050 */ + ULONG ProcessInitializing : 1; + ULONG ProcessUsingVEH : 1; + ULONG ProcessUsingVCH : 1; + ULONG ProcessUsingFTH : 1; + ULONG ProcessPreviouslyThrottled : 1; + ULONG ProcessCurrentlyThrottled : 1; + ULONG ProcessImagesHotPatched : 1; + ULONG ReservedBits0 : 24; + KERNEL_CALLBACK_PROC* KernelCallbackTable; /* 02c/058 */ + ULONG Reserved; /* 030/060 */ + ULONG AtlThunkSListPtr32; /* 034/064 */ + PVOID ApiSetMap; /* 038/068 */ + ULONG TlsExpansionCounter; /* 03c/070 */ + PRTL_BITMAP TlsBitmap; /* 040/078 */ + ULONG TlsBitmapBits[2]; /* 044/080 */ + PVOID ReadOnlySharedMemoryBase; /* 04c/088 */ + PVOID SharedData; /* 050/090 */ + PVOID* ReadOnlyStaticServerData; /* 054/098 */ + PVOID AnsiCodePageData; /* 058/0a0 */ + PVOID OemCodePageData; /* 05c/0a8 */ + PVOID UnicodeCaseTableData; /* 060/0b0 */ + ULONG NumberOfProcessors; /* 064/0b8 */ + ULONG NtGlobalFlag; /* 068/0bc */ + LARGE_INTEGER CriticalSectionTimeout; /* 070/0c0 */ + SIZE_T HeapSegmentReserve; /* 078/0c8 */ + SIZE_T HeapSegmentCommit; /* 07c/0d0 */ + SIZE_T HeapDeCommitTotalFreeThreshold; /* 080/0d8 */ + SIZE_T HeapDeCommitFreeBlockThreshold; /* 084/0e0 */ + ULONG NumberOfHeaps; /* 088/0e8 */ + ULONG MaximumNumberOfHeaps; /* 08c/0ec */ + PVOID* ProcessHeaps; /* 090/0f0 */ + PVOID GdiSharedHandleTable; /* 094/0f8 */ + PVOID ProcessStarterHelper; /* 098/100 */ + PVOID GdiDCAttributeList; /* 09c/108 */ + PVOID LoaderLock; /* 0a0/110 */ + ULONG OSMajorVersion; /* 0a4/118 */ + ULONG OSMinorVersion; /* 0a8/11c */ + ULONG OSBuildNumber; /* 0ac/120 */ + ULONG OSPlatformId; /* 0b0/124 */ + ULONG ImageSubSystem; /* 0b4/128 */ + ULONG ImageSubSystemMajorVersion; /* 0b8/12c */ + ULONG ImageSubSystemMinorVersion; /* 0bc/130 */ + KAFFINITY ActiveProcessAffinityMask; /* 0c0/138 */ +#ifdef _WIN64 + ULONG GdiHandleBuffer[60]; /* /140 */ +#else + ULONG GdiHandleBuffer[34]; /* 0c4/ */ +#endif + PVOID PostProcessInitRoutine; /* 14c/230 */ + PRTL_BITMAP TlsExpansionBitmap; /* 150/238 */ + ULONG TlsExpansionBitmapBits[32]; /* 154/240 */ + ULONG SessionId; /* 1d4/2c0 */ + ULARGE_INTEGER AppCompatFlags; /* 1d8/2c8 */ + ULARGE_INTEGER AppCompatFlagsUser; /* 1e0/2d0 */ + PVOID ShimData; /* 1e8/2d8 */ + PVOID AppCompatInfo; /* 1ec/2e0 */ + UNICODE_STRING CSDVersion; /* 1f0/2e8 */ + PVOID ActivationContextData; /* 1f8/2f8 */ + PVOID ProcessAssemblyStorageMap; /* 1fc/300 */ + PVOID SystemDefaultActivationData; /* 200/308 */ + PVOID SystemAssemblyStorageMap; /* 204/310 */ + SIZE_T MinimumStackCommit; /* 208/318 */ + PVOID* FlsCallback; /* 20c/320 */ + LIST_ENTRY FlsListHead; /* 210/328 */ + union { + PRTL_BITMAP FlsBitmap; /* 218/338 */ +#ifdef _WIN64 + CHPEV2_PROCESS_INFO* ChpeV2ProcessInfo; /* /338 */ +#endif + }; + ULONG FlsBitmapBits[4]; /* 21c/340 */ + ULONG FlsHighIndex; /* 22c/350 */ + PVOID WerRegistrationData; /* 230/358 */ + PVOID WerShipAssertPtr; /* 234/360 */ + PVOID EcCodeBitMap; /* 238/368 */ + PVOID pImageHeaderHash; /* 23c/370 */ + ULONG HeapTracingEnabled : 1; /* 240/378 */ + ULONG CritSecTracingEnabled : 1; + ULONG LibLoaderTracingEnabled : 1; + ULONG SpareTracingBits : 29; + ULONGLONG CsrServerReadOnlySharedMemoryBase; /* 248/380 */ + ULONG TppWorkerpListLock; /* 250/388 */ + LIST_ENTRY TppWorkerpList; /* 254/390 */ + PVOID WaitOnAddressHashTable[0x80]; /* 25c/3a0 */ + PVOID TelemetryCoverageHeader; /* 45c/7a0 */ + ULONG CloudFileFlags; /* 460/7a8 */ + ULONG CloudFileDiagFlags; /* 464/7ac */ + CHAR PlaceholderCompatibilityMode; /* 468/7b0 */ + CHAR PlaceholderCompatibilityModeReserved[7]; /* 469/7b1 */ + PVOID LeapSecondData; /* 470/7b8 */ + ULONG LeapSecondFlags; /* 474/7c0 */ + ULONG NtGlobalFlag2; /* 478/7c4 */ +} PEB, *PPEB; + +typedef struct _RTL_ACTIVATION_CONTEXT_STACK_FRAME { + struct _RTL_ACTIVATION_CONTEXT_STACK_FRAME* Previous; + struct _ACTIVATION_CONTEXT* ActivationContext; + ULONG Flags; +} RTL_ACTIVATION_CONTEXT_STACK_FRAME, *PRTL_ACTIVATION_CONTEXT_STACK_FRAME; + +typedef struct _ACTIVATION_CONTEXT_STACK { + RTL_ACTIVATION_CONTEXT_STACK_FRAME* ActiveFrame; + LIST_ENTRY FrameListCache; + ULONG Flags; + ULONG NextCookieSequenceNumber; + ULONG_PTR StackId; +} ACTIVATION_CONTEXT_STACK, *PACTIVATION_CONTEXT_STACK; + +typedef struct _GDI_TEB_BATCH { + ULONG Offset; + HANDLE HDC; + ULONG Buffer[0x136]; +} GDI_TEB_BATCH; + +typedef struct _TEB_ACTIVE_FRAME_CONTEXT { + ULONG Flags; + const char* FrameName; +} TEB_ACTIVE_FRAME_CONTEXT, *PTEB_ACTIVE_FRAME_CONTEXT; + +typedef struct _TEB_ACTIVE_FRAME { + ULONG Flags; + struct _TEB_ACTIVE_FRAME* Previous; + TEB_ACTIVE_FRAME_CONTEXT* Context; +} TEB_ACTIVE_FRAME, *PTEB_ACTIVE_FRAME; + +typedef struct _TEB { /* win32/win64 */ + NT_TIB Tib; /* 000/0000 */ + PVOID EnvironmentPointer; /* 01c/0038 */ + CLIENT_ID ClientId; /* 020/0040 */ + PVOID ActiveRpcHandle; /* 028/0050 */ + PVOID ThreadLocalStoragePointer; /* 02c/0058 */ + PPEB Peb; /* 030/0060 */ + ULONG LastErrorValue; /* 034/0068 */ + ULONG CountOfOwnedCriticalSections; /* 038/006c */ + PVOID CsrClientThread; /* 03c/0070 */ + PVOID Win32ThreadInfo; /* 040/0078 */ + ULONG User32Reserved[26]; /* 044/0080 */ + ULONG UserReserved[5]; /* 0ac/00e8 */ + PVOID WOW32Reserved; /* 0c0/0100 */ + ULONG CurrentLocale; /* 0c4/0108 */ + ULONG FpSoftwareStatusRegister; /* 0c8/010c */ + PVOID ReservedForDebuggerInstrumentation[16]; /* 0cc/0110 */ +#ifdef _WIN64 + PVOID SystemReserved1[30]; /* /0190 */ +#else + PVOID SystemReserved1[26]; /* 10c/ */ +#endif + char PlaceholderCompatibilityMode; /* 174/0280 */ + BOOLEAN PlaceholderHydrationAlwaysExplicit; /* 175/0281 */ + char PlaceholderReserved[10]; /* 176/0282 */ + DWORD ProxiedProcessId; /* 180/028c */ + ACTIVATION_CONTEXT_STACK ActivationContextStack; /* 184/0290 */ + UCHAR WorkingOnBehalfOfTicket[8]; /* 19c/02b8 */ + LONG ExceptionCode; /* 1a4/02c0 */ + ACTIVATION_CONTEXT_STACK* ActivationContextStackPointer; /* 1a8/02c8 */ + ULONG_PTR InstrumentationCallbackSp; /* 1ac/02d0 */ + ULONG_PTR InstrumentationCallbackPreviousPc; /* 1b0/02d8 */ + ULONG_PTR InstrumentationCallbackPreviousSp; /* 1b4/02e0 */ +#ifdef _WIN64 + ULONG TxFsContext; /* /02e8 */ + BOOLEAN InstrumentationCallbackDisabled; /* /02ec */ + BOOLEAN UnalignedLoadStoreExceptions; /* /02ed */ +#else + BOOLEAN InstrumentationCallbackDisabled; /* 1b8/ */ + BYTE SpareBytes1[23]; /* 1b9/ */ + ULONG TxFsContext; /* 1d0/ */ +#endif + GDI_TEB_BATCH GdiTebBatch; /* 1d4/02f0 */ + CLIENT_ID RealClientId; /* 6b4/07d8 */ + HANDLE GdiCachedProcessHandle; /* 6bc/07e8 */ + ULONG GdiClientPID; /* 6c0/07f0 */ + ULONG GdiClientTID; /* 6c4/07f4 */ + PVOID GdiThreadLocaleInfo; /* 6c8/07f8 */ + ULONG_PTR Win32ClientInfo[62]; /* 6cc/0800 */ + PVOID glDispatchTable[233]; /* 7c4/09f0 */ + PVOID glReserved1[29]; /* b68/1138 */ + PVOID glReserved2; /* bdc/1220 */ + PVOID glSectionInfo; /* be0/1228 */ + PVOID glSection; /* be4/1230 */ + PVOID glTable; /* be8/1238 */ + PVOID glCurrentRC; /* bec/1240 */ + PVOID glContext; /* bf0/1248 */ + ULONG LastStatusValue; /* bf4/1250 */ + UNICODE_STRING StaticUnicodeString; /* bf8/1258 */ + WCHAR StaticUnicodeBuffer[261]; /* c00/1268 */ + PVOID DeallocationStack; /* e0c/1478 */ + PVOID TlsSlots[64]; /* e10/1480 */ + LIST_ENTRY TlsLinks; /* f10/1680 */ + PVOID Vdm; /* f18/1690 */ + PVOID ReservedForNtRpc; /* f1c/1698 */ + PVOID DbgSsReserved[2]; /* f20/16a0 */ + ULONG HardErrorMode; /* f28/16b0 */ +#ifdef _WIN64 + PVOID Instrumentation[11]; /* /16b8 */ +#else + PVOID Instrumentation[9]; /* f2c/ */ +#endif + GUID ActivityId; /* f50/1710 */ + PVOID SubProcessTag; /* f60/1720 */ + PVOID PerflibData; /* f64/1728 */ + PVOID EtwTraceData; /* f68/1730 */ + PVOID WinSockData; /* f6c/1738 */ + ULONG GdiBatchCount; /* f70/1740 */ + ULONG IdealProcessorValue; /* f74/1744 */ + ULONG GuaranteedStackBytes; /* f78/1748 */ + PVOID ReservedForPerf; /* f7c/1750 */ + PVOID ReservedForOle; /* f80/1758 */ + ULONG WaitingOnLoaderLock; /* f84/1760 */ + PVOID SavedPriorityState; /* f88/1768 */ + ULONG_PTR ReservedForCodeCoverage; /* f8c/1770 */ + PVOID ThreadPoolData; /* f90/1778 */ + PVOID* TlsExpansionSlots; /* f94/1780 */ +#ifdef _WIN64 + union { + PVOID DeallocationBStore; /* /1788 */ + PVOID* ChpeV2CpuAreaInfo; /* /1788 */ + } DUMMYUNIONNAME; + PVOID BStoreLimit; /* /1790 */ +#endif + ULONG MuiGeneration; /* f98/1798 */ + ULONG IsImpersonating; /* f9c/179c */ + PVOID NlsCache; /* fa0/17a0 */ + PVOID ShimData; /* fa4/17a8 */ + ULONG HeapVirtualAffinity; /* fa8/17b0 */ + PVOID CurrentTransactionHandle; /* fac/17b8 */ + TEB_ACTIVE_FRAME* ActiveFrame; /* fb0/17c0 */ + PVOID* FlsSlots; /* fb4/17c8 */ + PVOID PreferredLanguages; /* fb8/17d0 */ + PVOID UserPrefLanguages; /* fbc/17d8 */ + PVOID MergedPrefLanguages; /* fc0/17e0 */ + ULONG MuiImpersonation; /* fc4/17e8 */ + USHORT CrossTebFlags; /* fc8/17ec */ + USHORT SameTebFlags; /* fca/17ee */ + PVOID TxnScopeEnterCallback; /* fcc/17f0 */ + PVOID TxnScopeExitCallback; /* fd0/17f8 */ + PVOID TxnScopeContext; /* fd4/1800 */ + ULONG LockCount; /* fd8/1808 */ + LONG WowTebOffset; /* fdc/180c */ + PVOID ResourceRetValue; /* fe0/1810 */ + PVOID ReservedForWdf; /* fe4/1818 */ + ULONGLONG ReservedForCrt; /* fe8/1820 */ + GUID EffectiveContainerId; /* ff0/1828 */ +} TEB, *PTEB; +static_assert(offsetof(TEB, DeallocationStack) == + 0x1478); /* The only member we care about at the moment */ + +typedef enum _QUEUE_USER_APC_FLAGS { + QueueUserApcFlagsNone, + QueueUserApcFlagsSpecialUserApc, + QueueUserApcFlagsMaxValue +} QUEUE_USER_APC_FLAGS; + +typedef union _USER_APC_OPTION { + ULONG_PTR UserApcFlags; + HANDLE MemoryReserveHandle; +} USER_APC_OPTION, *PUSER_APC_OPTION; + +using PPS_APC_ROUTINE = void (*)(PVOID ApcArgument1, PVOID ApcArgument2, PVOID ApcArgument3, + PCONTEXT Context); + +typedef u64(__stdcall* NtClose_t)(HANDLE Handle); + +typedef u64(__stdcall* NtSetInformationFile_t)(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, + PVOID FileInformation, ULONG Length, + FILE_INFORMATION_CLASS FileInformationClass); + +typedef u64(__stdcall* NtCreateThread_t)(PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess, + PCOBJECT_ATTRIBUTES ObjectAttributes, HANDLE ProcessHandle, + PCLIENT_ID ClientId, PCONTEXT ThreadContext, + PINITIAL_TEB InitialTeb, BOOLEAN CreateSuspended); + +typedef u64(__stdcall* NtTerminateThread_t)(HANDLE ThreadHandle, u64 ExitStatus); + +typedef u64(__stdcall* NtQueueApcThreadEx_t)(HANDLE ThreadHandle, + USER_APC_OPTION UserApcReserveHandle, + PPS_APC_ROUTINE ApcRoutine, PVOID ApcArgument1, + PVOID ApcArgument2, PVOID ApcArgument3); + +extern NtClose_t NtClose; +extern NtSetInformationFile_t NtSetInformationFile; +extern NtCreateThread_t NtCreateThread; +extern NtTerminateThread_t NtTerminateThread; +extern NtQueueApcThreadEx_t NtQueueApcThreadEx; + +namespace Common::NtApi { +void Initialize(); +} + +#endif diff --git a/src/common/spin_lock.cpp b/src/common/spin_lock.cpp index 9d4cfe36b..b2ef4ea1d 100755 --- a/src/common/spin_lock.cpp +++ b/src/common/spin_lock.cpp @@ -1,53 +1,53 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/spin_lock.h" - -#if _MSC_VER -#include -#if _M_AMD64 -#define __x86_64__ 1 -#endif -#if _M_ARM64 -#define __aarch64__ 1 -#endif -#else -#if __x86_64__ -#include -#endif -#endif - -namespace { - -void ThreadPause() { -#if __x86_64__ - _mm_pause(); -#elif __aarch64__ && _MSC_VER - __yield(); -#elif __aarch64__ - asm("yield"); -#endif -} - -} // Anonymous namespace - -namespace Common { - -void SpinLock::lock() { - while (lck.test_and_set(std::memory_order_acquire)) { - ThreadPause(); - } -} - -void SpinLock::unlock() { - lck.clear(std::memory_order_release); -} - -bool SpinLock::try_lock() { - if (lck.test_and_set(std::memory_order_acquire)) { - return false; - } - return true; -} - -} // namespace Common +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/spin_lock.h" + +#if _MSC_VER +#include +#if _M_AMD64 +#define __x86_64__ 1 +#endif +#if _M_ARM64 +#define __aarch64__ 1 +#endif +#else +#if __x86_64__ +#include +#endif +#endif + +namespace { + +void ThreadPause() { +#if __x86_64__ + _mm_pause(); +#elif __aarch64__ && _MSC_VER + __yield(); +#elif __aarch64__ + asm("yield"); +#endif +} + +} // Anonymous namespace + +namespace Common { + +void SpinLock::lock() { + while (lck.test_and_set(std::memory_order_acquire)) { + ThreadPause(); + } +} + +void SpinLock::unlock() { + lck.clear(std::memory_order_release); +} + +bool SpinLock::try_lock() { + if (lck.test_and_set(std::memory_order_acquire)) { + return false; + } + return true; +} + +} // namespace Common diff --git a/src/common/spin_lock.h b/src/common/spin_lock.h index 3229a8c6a..a83274851 100755 --- a/src/common/spin_lock.h +++ b/src/common/spin_lock.h @@ -1,33 +1,33 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include - -namespace Common { - -/** - * SpinLock class - * a lock similar to mutex that forces a thread to spin wait instead calling the - * supervisor. Should be used on short sequences of code. - */ -class SpinLock { -public: - SpinLock() = default; - - SpinLock(const SpinLock&) = delete; - SpinLock& operator=(const SpinLock&) = delete; - - SpinLock(SpinLock&&) = delete; - SpinLock& operator=(SpinLock&&) = delete; - - void lock(); - void unlock(); - [[nodiscard]] bool try_lock(); - -private: - std::atomic_flag lck = ATOMIC_FLAG_INIT; -}; - -} // namespace Common +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +namespace Common { + +/** + * SpinLock class + * a lock similar to mutex that forces a thread to spin wait instead calling the + * supervisor. Should be used on short sequences of code. + */ +class SpinLock { +public: + SpinLock() = default; + + SpinLock(const SpinLock&) = delete; + SpinLock& operator=(const SpinLock&) = delete; + + SpinLock(SpinLock&&) = delete; + SpinLock& operator=(SpinLock&&) = delete; + + void lock(); + void unlock(); + [[nodiscard]] bool try_lock(); + +private: + std::atomic_flag lck = ATOMIC_FLAG_INIT; +}; + +} // namespace Common diff --git a/src/common/unique_function.h b/src/common/unique_function.h index 1891ec3c6..c15d88349 100755 --- a/src/common/unique_function.h +++ b/src/common/unique_function.h @@ -1,61 +1,61 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include - -namespace Common { - -/// General purpose function wrapper similar to std::function. -/// Unlike std::function, the captured values don't have to be copyable. -/// This class can be moved but not copied. -template -class UniqueFunction { - class CallableBase { - public: - virtual ~CallableBase() = default; - virtual ResultType operator()(Args&&...) = 0; - }; - - template - class Callable final : public CallableBase { - public: - Callable(Functor&& functor_) : functor{std::move(functor_)} {} - ~Callable() override = default; - - ResultType operator()(Args&&... args) override { - return functor(std::forward(args)...); - } - - private: - Functor functor; - }; - -public: - UniqueFunction() = default; - - template - UniqueFunction(Functor&& functor) - : callable{std::make_unique>(std::move(functor))} {} - - UniqueFunction& operator=(UniqueFunction&& rhs) noexcept = default; - UniqueFunction(UniqueFunction&& rhs) noexcept = default; - - UniqueFunction& operator=(const UniqueFunction&) = delete; - UniqueFunction(const UniqueFunction&) = delete; - - ResultType operator()(Args&&... args) const { - return (*callable)(std::forward(args)...); - } - - explicit operator bool() const noexcept { - return static_cast(callable); - } - -private: - std::unique_ptr callable; -}; - -} // namespace Common +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +namespace Common { + +/// General purpose function wrapper similar to std::function. +/// Unlike std::function, the captured values don't have to be copyable. +/// This class can be moved but not copied. +template +class UniqueFunction { + class CallableBase { + public: + virtual ~CallableBase() = default; + virtual ResultType operator()(Args&&...) = 0; + }; + + template + class Callable final : public CallableBase { + public: + Callable(Functor&& functor_) : functor{std::move(functor_)} {} + ~Callable() override = default; + + ResultType operator()(Args&&... args) override { + return functor(std::forward(args)...); + } + + private: + Functor functor; + }; + +public: + UniqueFunction() = default; + + template + UniqueFunction(Functor&& functor) + : callable{std::make_unique>(std::move(functor))} {} + + UniqueFunction& operator=(UniqueFunction&& rhs) noexcept = default; + UniqueFunction(UniqueFunction&& rhs) noexcept = default; + + UniqueFunction& operator=(const UniqueFunction&) = delete; + UniqueFunction(const UniqueFunction&) = delete; + + ResultType operator()(Args&&... args) const { + return (*callable)(std::forward(args)...); + } + + explicit operator bool() const noexcept { + return static_cast(callable); + } + +private: + std::unique_ptr callable; +}; + +} // namespace Common diff --git a/src/core/libraries/fiber/fiber.cpp b/src/core/libraries/fiber/fiber.cpp index 278099841..7bb81b61e 100644 --- a/src/core/libraries/fiber/fiber.cpp +++ b/src/core/libraries/fiber/fiber.cpp @@ -1,484 +1,484 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "fiber.h" - -#include "common/logging/log.h" -#include "core/libraries/fiber/fiber_error.h" -#include "core/libraries/libs.h" -#include "core/tls.h" - -namespace Libraries::Fiber { - -static constexpr u32 kFiberSignature0 = 0xdef1649c; -static constexpr u32 kFiberSignature1 = 0xb37592a0; -static constexpr u32 kFiberOptSignature = 0xbb40e64d; -static constexpr u64 kFiberStackSignature = 0x7149f2ca7149f2ca; -static constexpr u64 kFiberStackSizeCheck = 0xdeadbeefdeadbeef; - -static std::atomic context_size_check = false; - -OrbisFiberContext* GetFiberContext() { - return Core::GetTcbBase()->tcb_fiber; -} - -extern "C" s32 PS4_SYSV_ABI _sceFiberSetJmp(OrbisFiberContext* ctx) asm("_sceFiberSetJmp"); -extern "C" s32 PS4_SYSV_ABI _sceFiberLongJmp(OrbisFiberContext* ctx) asm("_sceFiberLongJmp"); -extern "C" void PS4_SYSV_ABI _sceFiberSwitchEntry(OrbisFiberData* data, - bool set_fpu) asm("_sceFiberSwitchEntry"); -extern "C" void PS4_SYSV_ABI _sceFiberForceQuit(u64 ret) asm("_sceFiberForceQuit"); - -extern "C" void PS4_SYSV_ABI _sceFiberForceQuit(u64 ret) { - OrbisFiberContext* g_ctx = GetFiberContext(); - g_ctx->return_val = ret; - _sceFiberLongJmp(g_ctx); -} - -void PS4_SYSV_ABI _sceFiberCheckStackOverflow(OrbisFiberContext* ctx) { - u64* stack_base = reinterpret_cast(ctx->current_fiber->addr_context); - if (stack_base && *stack_base != kFiberStackSignature) { - UNREACHABLE_MSG("Stack overflow detected in fiber."); - } -} - -void PS4_SYSV_ABI _sceFiberSwitchToFiber(OrbisFiber* fiber, u64 arg_on_run_to, - OrbisFiberContext* ctx) { - OrbisFiberContext* fiber_ctx = fiber->context; - if (fiber_ctx) { - ctx->arg_on_run_to = arg_on_run_to; - _sceFiberLongJmp(fiber_ctx); - __builtin_trap(); - } - - OrbisFiberData data{}; - if (ctx->prev_fiber) { - OrbisFiber* prev_fiber = ctx->prev_fiber; - ctx->prev_fiber = nullptr; - data.state = reinterpret_cast(&prev_fiber->state); - } else { - data.state = nullptr; - } - - data.entry = fiber->entry; - data.arg_on_initialize = fiber->arg_on_initialize; - data.arg_on_run_to = arg_on_run_to; - data.stack_addr = - reinterpret_cast(reinterpret_cast(fiber->addr_context) + fiber->size_context); - if (fiber->flags & FiberFlags::SetFpuRegs) { - data.fpucw = 0x037f; - data.mxcsr = 0x9fc0; - _sceFiberSwitchEntry(&data, true); - } else { - _sceFiberSwitchEntry(&data, false); - } - - __builtin_trap(); -} - -void PS4_SYSV_ABI _sceFiberSwitch(OrbisFiber* cur_fiber, OrbisFiber* fiber, u64 arg_on_run_to, - OrbisFiberContext* ctx) { - ctx->prev_fiber = cur_fiber; - ctx->current_fiber = fiber; - - if (fiber->addr_context == nullptr) { - ctx->prev_fiber = nullptr; - - OrbisFiberData data{}; - data.entry = fiber->entry; - data.arg_on_initialize = fiber->arg_on_initialize; - data.arg_on_run_to = arg_on_run_to; - data.stack_addr = reinterpret_cast(ctx->rsp & ~15); - data.state = reinterpret_cast(&cur_fiber->state); - - if (fiber->flags & FiberFlags::SetFpuRegs) { - data.fpucw = 0x037f; - data.mxcsr = 0x9fc0; - _sceFiberSwitchEntry(&data, true); - } else { - _sceFiberSwitchEntry(&data, false); - } - - __builtin_trap(); - } - - _sceFiberSwitchToFiber(fiber, arg_on_run_to, ctx); - __builtin_trap(); -} - -void PS4_SYSV_ABI _sceFiberTerminate(OrbisFiber* fiber, u64 arg_on_return, OrbisFiberContext* ctx) { - ctx->arg_on_return = arg_on_return; - _sceFiberLongJmp(ctx); - __builtin_trap(); -} - -s32 PS4_SYSV_ABI sceFiberInitialize(OrbisFiber* fiber, const char* name, OrbisFiberEntry entry, - u64 arg_on_initialize, void* addr_context, u64 size_context, - const OrbisFiberOptParam* opt_param, u32 build_ver) { - if (!fiber || !name || !entry) { - return ORBIS_FIBER_ERROR_NULL; - } - if ((u64)fiber & 7 || (u64)addr_context & 15) { - return ORBIS_FIBER_ERROR_ALIGNMENT; - } - if (opt_param && (u64)opt_param & 7) { - return ORBIS_FIBER_ERROR_ALIGNMENT; - } - if (size_context && size_context < ORBIS_FIBER_CONTEXT_MINIMUM_SIZE) { - return ORBIS_FIBER_ERROR_RANGE; - } - if (size_context & 15) { - return ORBIS_FIBER_ERROR_INVALID; - } - if (!addr_context && size_context) { - return ORBIS_FIBER_ERROR_INVALID; - } - if (addr_context && !size_context) { - return ORBIS_FIBER_ERROR_INVALID; - } - if (opt_param && opt_param->magic != kFiberOptSignature) { - return ORBIS_FIBER_ERROR_INVALID; - } - - u32 flags = FiberFlags::None; - if (build_ver >= 0x3500000) { - flags |= FiberFlags::SetFpuRegs; - } - if (context_size_check) { - flags |= FiberFlags::ContextSizeCheck; - } - - strncpy(fiber->name, name, ORBIS_FIBER_MAX_NAME_LENGTH); - - fiber->entry = entry; - fiber->arg_on_initialize = arg_on_initialize; - fiber->addr_context = addr_context; - fiber->size_context = size_context; - fiber->context = nullptr; - fiber->flags = flags; - - /* - A low stack area is problematic, as we can easily - cause a stack overflow with our HLE. - */ - if (size_context && size_context <= 4096) { - LOG_WARNING(Lib_Fiber, "Fiber initialized with small stack area."); - } - - fiber->magic_start = kFiberSignature0; - fiber->magic_end = kFiberSignature1; - - if (addr_context != nullptr) { - fiber->context_start = addr_context; - fiber->context_end = - reinterpret_cast(reinterpret_cast(addr_context) + size_context); - - /* Apply signature to start of stack */ - *(u64*)addr_context = kFiberStackSignature; - - if (flags & FiberFlags::ContextSizeCheck) { - u64* stack_start = reinterpret_cast(fiber->context_start); - u64* stack_end = reinterpret_cast(fiber->context_end); - - u64* stack_ptr = stack_start + 1; - while (stack_ptr < stack_end) { - *stack_ptr++ = kFiberStackSizeCheck; - } - } - } - - fiber->state = FiberState::Idle; - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceFiberOptParamInitialize(OrbisFiberOptParam* opt_param) { - if (!opt_param) { - return ORBIS_FIBER_ERROR_NULL; - } - if ((u64)opt_param & 7) { - return ORBIS_FIBER_ERROR_ALIGNMENT; - } - - opt_param->magic = kFiberOptSignature; - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceFiberFinalize(OrbisFiber* fiber) { - if (!fiber) { - return ORBIS_FIBER_ERROR_NULL; - } - if ((u64)fiber & 7) { - return ORBIS_FIBER_ERROR_ALIGNMENT; - } - if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) { - return ORBIS_FIBER_ERROR_INVALID; - } - - FiberState expected = FiberState::Idle; - if (!fiber->state.compare_exchange_strong(expected, FiberState::Terminated)) { - return ORBIS_FIBER_ERROR_STATE; - } - - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceFiberRun(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_return) { - if (!fiber) { - return ORBIS_FIBER_ERROR_NULL; - } - if ((u64)fiber & 7) { - return ORBIS_FIBER_ERROR_ALIGNMENT; - } - if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) { - return ORBIS_FIBER_ERROR_INVALID; - } - - Core::Tcb* tcb = Core::GetTcbBase(); - if (tcb->tcb_fiber) { - return ORBIS_FIBER_ERROR_PERMISSION; - } - - FiberState expected = FiberState::Idle; - if (!fiber->state.compare_exchange_strong(expected, FiberState::Run)) { - return ORBIS_FIBER_ERROR_STATE; - } - - OrbisFiberContext ctx{}; - ctx.current_fiber = fiber; - ctx.prev_fiber = nullptr; - ctx.return_val = 0; - - tcb->tcb_fiber = &ctx; - - s32 jmp = _sceFiberSetJmp(&ctx); - if (!jmp) { - if (fiber->addr_context) { - _sceFiberSwitchToFiber(fiber, arg_on_run_to, &ctx); - __builtin_trap(); - } - - OrbisFiberData data{}; - data.entry = fiber->entry; - data.arg_on_initialize = fiber->arg_on_initialize; - data.arg_on_run_to = arg_on_run_to; - data.stack_addr = reinterpret_cast(ctx.rsp & ~15); - data.state = nullptr; - if (fiber->flags & FiberFlags::SetFpuRegs) { - data.fpucw = 0x037f; - data.mxcsr = 0x9fc0; - _sceFiberSwitchEntry(&data, true); - } else { - _sceFiberSwitchEntry(&data, false); - } - } - - OrbisFiber* cur_fiber = ctx.current_fiber; - ctx.current_fiber = nullptr; - cur_fiber->state = FiberState::Idle; - - if (ctx.return_val != 0) { - /* Fiber entry returned! This should never happen. */ - UNREACHABLE_MSG("Fiber entry function returned."); - } - - if (arg_on_return) { - *arg_on_return = ctx.arg_on_return; - } - - tcb->tcb_fiber = nullptr; - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceFiberSwitch(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_run) { - if (!fiber) { - return ORBIS_FIBER_ERROR_NULL; - } - if ((u64)fiber & 7) { - return ORBIS_FIBER_ERROR_ALIGNMENT; - } - if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) { - return ORBIS_FIBER_ERROR_INVALID; - } - - OrbisFiberContext* g_ctx = GetFiberContext(); - if (!g_ctx) { - return ORBIS_FIBER_ERROR_PERMISSION; - } - - FiberState expected = FiberState::Idle; - if (!fiber->state.compare_exchange_strong(expected, FiberState::Run)) { - return ORBIS_FIBER_ERROR_STATE; - } - - OrbisFiber* cur_fiber = g_ctx->current_fiber; - if (cur_fiber->addr_context == nullptr) { - _sceFiberSwitch(cur_fiber, fiber, arg_on_run_to, g_ctx); - __builtin_trap(); - } - - OrbisFiberContext ctx{}; - s32 jmp = _sceFiberSetJmp(&ctx); - if (!jmp) { - cur_fiber->context = &ctx; - _sceFiberCheckStackOverflow(g_ctx); - _sceFiberSwitch(cur_fiber, fiber, arg_on_run_to, g_ctx); - __builtin_trap(); - } - - g_ctx = GetFiberContext(); - if (g_ctx->prev_fiber) { - g_ctx->prev_fiber->state = FiberState::Idle; - g_ctx->prev_fiber = nullptr; - } - - if (arg_on_run) { - *arg_on_run = g_ctx->arg_on_run_to; - } - - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceFiberGetSelf(OrbisFiber** fiber) { - if (!fiber) { - return ORBIS_FIBER_ERROR_NULL; - } - - OrbisFiberContext* g_ctx = GetFiberContext(); - if (!g_ctx) { - return ORBIS_FIBER_ERROR_PERMISSION; - } - - *fiber = g_ctx->current_fiber; - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceFiberReturnToThread(u64 arg_on_return, u64* arg_on_run) { - OrbisFiberContext* g_ctx = GetFiberContext(); - if (!g_ctx) { - return ORBIS_FIBER_ERROR_PERMISSION; - } - - OrbisFiber* cur_fiber = g_ctx->current_fiber; - if (cur_fiber->addr_context) { - OrbisFiberContext ctx{}; - s32 jmp = _sceFiberSetJmp(&ctx); - if (jmp) { - g_ctx = GetFiberContext(); - if (g_ctx->prev_fiber) { - g_ctx->prev_fiber->state = FiberState::Idle; - g_ctx->prev_fiber = nullptr; - } - if (arg_on_run) { - *arg_on_run = g_ctx->arg_on_run_to; - } - return ORBIS_OK; - } - - cur_fiber->context = &ctx; - _sceFiberCheckStackOverflow(g_ctx); - } - - _sceFiberTerminate(cur_fiber, arg_on_return, g_ctx); - __builtin_trap(); -} - -s32 PS4_SYSV_ABI sceFiberGetInfo(OrbisFiber* fiber, OrbisFiberInfo* fiber_info) { - if (!fiber || !fiber_info) { - return ORBIS_FIBER_ERROR_NULL; - } - if ((u64)fiber & 7 || (u64)fiber_info & 7) { - return ORBIS_FIBER_ERROR_ALIGNMENT; - } - if (fiber_info->size != sizeof(OrbisFiberInfo)) { - return ORBIS_FIBER_ERROR_INVALID; - } - if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) { - return ORBIS_FIBER_ERROR_INVALID; - } - - fiber_info->entry = fiber->entry; - fiber_info->arg_on_initialize = fiber->arg_on_initialize; - fiber_info->addr_context = fiber->addr_context; - fiber_info->size_context = fiber->size_context; - strncpy(fiber_info->name, fiber->name, ORBIS_FIBER_MAX_NAME_LENGTH); - - fiber_info->size_context_margin = -1; - if (fiber->flags & FiberFlags::ContextSizeCheck && fiber->addr_context != nullptr) { - u64 stack_margin = 0; - u64* stack_start = reinterpret_cast(fiber->context_start); - u64* stack_end = reinterpret_cast(fiber->context_end); - - if (*stack_start == kFiberStackSignature) { - u64* stack_ptr = stack_start + 1; - while (stack_ptr < stack_end) { - if (*stack_ptr == kFiberStackSizeCheck) { - stack_ptr++; - } - } - - stack_margin = - reinterpret_cast(stack_ptr) - reinterpret_cast(stack_start + 1); - } - - fiber_info->size_context_margin = stack_margin; - } - - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceFiberStartContextSizeCheck(u32 flags) { - if (flags != 0) { - return ORBIS_FIBER_ERROR_INVALID; - } - - u32 expected = 0; - if (!context_size_check.compare_exchange_strong(expected, 1u)) { - return ORBIS_FIBER_ERROR_STATE; - } - - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceFiberStopContextSizeCheck() { - u32 expected = 1; - if (!context_size_check.compare_exchange_strong(expected, 0u)) { - return ORBIS_FIBER_ERROR_STATE; - } - - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceFiberRename(OrbisFiber* fiber, const char* name) { - if (!fiber || !name) { - return ORBIS_FIBER_ERROR_NULL; - } - if ((u64)fiber & 7) { - return ORBIS_FIBER_ERROR_ALIGNMENT; - } - if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) { - return ORBIS_FIBER_ERROR_INVALID; - } - - strncpy(fiber->name, name, ORBIS_FIBER_MAX_NAME_LENGTH); - return ORBIS_OK; -} - -void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym) { - LIB_FUNCTION("hVYD7Ou2pCQ", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberInitialize); - LIB_FUNCTION("7+OJIpko9RY", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberInitialize); - LIB_FUNCTION("asjUJJ+aa8s", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberOptParamInitialize); - LIB_FUNCTION("JeNX5F-NzQU", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberFinalize); - - LIB_FUNCTION("a0LLrZWac0M", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberRun); - LIB_FUNCTION("PFT2S-tJ7Uk", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberSwitch); - LIB_FUNCTION("p+zLIOg27zU", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberGetSelf); - LIB_FUNCTION("B0ZX2hx9DMw", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberReturnToThread); - - LIB_FUNCTION("uq2Y5BFz0PE", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberGetInfo); - LIB_FUNCTION("Lcqty+QNWFc", "libSceFiber", 1, "libSceFiber", 1, 1, - sceFiberStartContextSizeCheck); - LIB_FUNCTION("Kj4nXMpnM8Y", "libSceFiber", 1, "libSceFiber", 1, 1, - sceFiberStopContextSizeCheck); - LIB_FUNCTION("JzyT91ucGDc", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberRename); -} - -} // namespace Libraries::Fiber +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "fiber.h" + +#include "common/logging/log.h" +#include "core/libraries/fiber/fiber_error.h" +#include "core/libraries/libs.h" +#include "core/tls.h" + +namespace Libraries::Fiber { + +static constexpr u32 kFiberSignature0 = 0xdef1649c; +static constexpr u32 kFiberSignature1 = 0xb37592a0; +static constexpr u32 kFiberOptSignature = 0xbb40e64d; +static constexpr u64 kFiberStackSignature = 0x7149f2ca7149f2ca; +static constexpr u64 kFiberStackSizeCheck = 0xdeadbeefdeadbeef; + +static std::atomic context_size_check = false; + +OrbisFiberContext* GetFiberContext() { + return Core::GetTcbBase()->tcb_fiber; +} + +extern "C" s32 PS4_SYSV_ABI _sceFiberSetJmp(OrbisFiberContext* ctx) asm("_sceFiberSetJmp"); +extern "C" s32 PS4_SYSV_ABI _sceFiberLongJmp(OrbisFiberContext* ctx) asm("_sceFiberLongJmp"); +extern "C" void PS4_SYSV_ABI _sceFiberSwitchEntry(OrbisFiberData* data, + bool set_fpu) asm("_sceFiberSwitchEntry"); +extern "C" void PS4_SYSV_ABI _sceFiberForceQuit(u64 ret) asm("_sceFiberForceQuit"); + +extern "C" void PS4_SYSV_ABI _sceFiberForceQuit(u64 ret) { + OrbisFiberContext* g_ctx = GetFiberContext(); + g_ctx->return_val = ret; + _sceFiberLongJmp(g_ctx); +} + +void PS4_SYSV_ABI _sceFiberCheckStackOverflow(OrbisFiberContext* ctx) { + u64* stack_base = reinterpret_cast(ctx->current_fiber->addr_context); + if (stack_base && *stack_base != kFiberStackSignature) { + UNREACHABLE_MSG("Stack overflow detected in fiber."); + } +} + +void PS4_SYSV_ABI _sceFiberSwitchToFiber(OrbisFiber* fiber, u64 arg_on_run_to, + OrbisFiberContext* ctx) { + OrbisFiberContext* fiber_ctx = fiber->context; + if (fiber_ctx) { + ctx->arg_on_run_to = arg_on_run_to; + _sceFiberLongJmp(fiber_ctx); + __builtin_trap(); + } + + OrbisFiberData data{}; + if (ctx->prev_fiber) { + OrbisFiber* prev_fiber = ctx->prev_fiber; + ctx->prev_fiber = nullptr; + data.state = reinterpret_cast(&prev_fiber->state); + } else { + data.state = nullptr; + } + + data.entry = fiber->entry; + data.arg_on_initialize = fiber->arg_on_initialize; + data.arg_on_run_to = arg_on_run_to; + data.stack_addr = + reinterpret_cast(reinterpret_cast(fiber->addr_context) + fiber->size_context); + if (fiber->flags & FiberFlags::SetFpuRegs) { + data.fpucw = 0x037f; + data.mxcsr = 0x9fc0; + _sceFiberSwitchEntry(&data, true); + } else { + _sceFiberSwitchEntry(&data, false); + } + + __builtin_trap(); +} + +void PS4_SYSV_ABI _sceFiberSwitch(OrbisFiber* cur_fiber, OrbisFiber* fiber, u64 arg_on_run_to, + OrbisFiberContext* ctx) { + ctx->prev_fiber = cur_fiber; + ctx->current_fiber = fiber; + + if (fiber->addr_context == nullptr) { + ctx->prev_fiber = nullptr; + + OrbisFiberData data{}; + data.entry = fiber->entry; + data.arg_on_initialize = fiber->arg_on_initialize; + data.arg_on_run_to = arg_on_run_to; + data.stack_addr = reinterpret_cast(ctx->rsp & ~15); + data.state = reinterpret_cast(&cur_fiber->state); + + if (fiber->flags & FiberFlags::SetFpuRegs) { + data.fpucw = 0x037f; + data.mxcsr = 0x9fc0; + _sceFiberSwitchEntry(&data, true); + } else { + _sceFiberSwitchEntry(&data, false); + } + + __builtin_trap(); + } + + _sceFiberSwitchToFiber(fiber, arg_on_run_to, ctx); + __builtin_trap(); +} + +void PS4_SYSV_ABI _sceFiberTerminate(OrbisFiber* fiber, u64 arg_on_return, OrbisFiberContext* ctx) { + ctx->arg_on_return = arg_on_return; + _sceFiberLongJmp(ctx); + __builtin_trap(); +} + +s32 PS4_SYSV_ABI sceFiberInitialize(OrbisFiber* fiber, const char* name, OrbisFiberEntry entry, + u64 arg_on_initialize, void* addr_context, u64 size_context, + const OrbisFiberOptParam* opt_param, u32 build_ver) { + if (!fiber || !name || !entry) { + return ORBIS_FIBER_ERROR_NULL; + } + if ((u64)fiber & 7 || (u64)addr_context & 15) { + return ORBIS_FIBER_ERROR_ALIGNMENT; + } + if (opt_param && (u64)opt_param & 7) { + return ORBIS_FIBER_ERROR_ALIGNMENT; + } + if (size_context && size_context < ORBIS_FIBER_CONTEXT_MINIMUM_SIZE) { + return ORBIS_FIBER_ERROR_RANGE; + } + if (size_context & 15) { + return ORBIS_FIBER_ERROR_INVALID; + } + if (!addr_context && size_context) { + return ORBIS_FIBER_ERROR_INVALID; + } + if (addr_context && !size_context) { + return ORBIS_FIBER_ERROR_INVALID; + } + if (opt_param && opt_param->magic != kFiberOptSignature) { + return ORBIS_FIBER_ERROR_INVALID; + } + + u32 flags = FiberFlags::None; + if (build_ver >= 0x3500000) { + flags |= FiberFlags::SetFpuRegs; + } + if (context_size_check) { + flags |= FiberFlags::ContextSizeCheck; + } + + strncpy(fiber->name, name, ORBIS_FIBER_MAX_NAME_LENGTH); + + fiber->entry = entry; + fiber->arg_on_initialize = arg_on_initialize; + fiber->addr_context = addr_context; + fiber->size_context = size_context; + fiber->context = nullptr; + fiber->flags = flags; + + /* + A low stack area is problematic, as we can easily + cause a stack overflow with our HLE. + */ + if (size_context && size_context <= 4096) { + LOG_WARNING(Lib_Fiber, "Fiber initialized with small stack area."); + } + + fiber->magic_start = kFiberSignature0; + fiber->magic_end = kFiberSignature1; + + if (addr_context != nullptr) { + fiber->context_start = addr_context; + fiber->context_end = + reinterpret_cast(reinterpret_cast(addr_context) + size_context); + + /* Apply signature to start of stack */ + *(u64*)addr_context = kFiberStackSignature; + + if (flags & FiberFlags::ContextSizeCheck) { + u64* stack_start = reinterpret_cast(fiber->context_start); + u64* stack_end = reinterpret_cast(fiber->context_end); + + u64* stack_ptr = stack_start + 1; + while (stack_ptr < stack_end) { + *stack_ptr++ = kFiberStackSizeCheck; + } + } + } + + fiber->state = FiberState::Idle; + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFiberOptParamInitialize(OrbisFiberOptParam* opt_param) { + if (!opt_param) { + return ORBIS_FIBER_ERROR_NULL; + } + if ((u64)opt_param & 7) { + return ORBIS_FIBER_ERROR_ALIGNMENT; + } + + opt_param->magic = kFiberOptSignature; + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFiberFinalize(OrbisFiber* fiber) { + if (!fiber) { + return ORBIS_FIBER_ERROR_NULL; + } + if ((u64)fiber & 7) { + return ORBIS_FIBER_ERROR_ALIGNMENT; + } + if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) { + return ORBIS_FIBER_ERROR_INVALID; + } + + FiberState expected = FiberState::Idle; + if (!fiber->state.compare_exchange_strong(expected, FiberState::Terminated)) { + return ORBIS_FIBER_ERROR_STATE; + } + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFiberRun(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_return) { + if (!fiber) { + return ORBIS_FIBER_ERROR_NULL; + } + if ((u64)fiber & 7) { + return ORBIS_FIBER_ERROR_ALIGNMENT; + } + if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) { + return ORBIS_FIBER_ERROR_INVALID; + } + + Core::Tcb* tcb = Core::GetTcbBase(); + if (tcb->tcb_fiber) { + return ORBIS_FIBER_ERROR_PERMISSION; + } + + FiberState expected = FiberState::Idle; + if (!fiber->state.compare_exchange_strong(expected, FiberState::Run)) { + return ORBIS_FIBER_ERROR_STATE; + } + + OrbisFiberContext ctx{}; + ctx.current_fiber = fiber; + ctx.prev_fiber = nullptr; + ctx.return_val = 0; + + tcb->tcb_fiber = &ctx; + + s32 jmp = _sceFiberSetJmp(&ctx); + if (!jmp) { + if (fiber->addr_context) { + _sceFiberSwitchToFiber(fiber, arg_on_run_to, &ctx); + __builtin_trap(); + } + + OrbisFiberData data{}; + data.entry = fiber->entry; + data.arg_on_initialize = fiber->arg_on_initialize; + data.arg_on_run_to = arg_on_run_to; + data.stack_addr = reinterpret_cast(ctx.rsp & ~15); + data.state = nullptr; + if (fiber->flags & FiberFlags::SetFpuRegs) { + data.fpucw = 0x037f; + data.mxcsr = 0x9fc0; + _sceFiberSwitchEntry(&data, true); + } else { + _sceFiberSwitchEntry(&data, false); + } + } + + OrbisFiber* cur_fiber = ctx.current_fiber; + ctx.current_fiber = nullptr; + cur_fiber->state = FiberState::Idle; + + if (ctx.return_val != 0) { + /* Fiber entry returned! This should never happen. */ + UNREACHABLE_MSG("Fiber entry function returned."); + } + + if (arg_on_return) { + *arg_on_return = ctx.arg_on_return; + } + + tcb->tcb_fiber = nullptr; + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFiberSwitch(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_run) { + if (!fiber) { + return ORBIS_FIBER_ERROR_NULL; + } + if ((u64)fiber & 7) { + return ORBIS_FIBER_ERROR_ALIGNMENT; + } + if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) { + return ORBIS_FIBER_ERROR_INVALID; + } + + OrbisFiberContext* g_ctx = GetFiberContext(); + if (!g_ctx) { + return ORBIS_FIBER_ERROR_PERMISSION; + } + + FiberState expected = FiberState::Idle; + if (!fiber->state.compare_exchange_strong(expected, FiberState::Run)) { + return ORBIS_FIBER_ERROR_STATE; + } + + OrbisFiber* cur_fiber = g_ctx->current_fiber; + if (cur_fiber->addr_context == nullptr) { + _sceFiberSwitch(cur_fiber, fiber, arg_on_run_to, g_ctx); + __builtin_trap(); + } + + OrbisFiberContext ctx{}; + s32 jmp = _sceFiberSetJmp(&ctx); + if (!jmp) { + cur_fiber->context = &ctx; + _sceFiberCheckStackOverflow(g_ctx); + _sceFiberSwitch(cur_fiber, fiber, arg_on_run_to, g_ctx); + __builtin_trap(); + } + + g_ctx = GetFiberContext(); + if (g_ctx->prev_fiber) { + g_ctx->prev_fiber->state = FiberState::Idle; + g_ctx->prev_fiber = nullptr; + } + + if (arg_on_run) { + *arg_on_run = g_ctx->arg_on_run_to; + } + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFiberGetSelf(OrbisFiber** fiber) { + if (!fiber) { + return ORBIS_FIBER_ERROR_NULL; + } + + OrbisFiberContext* g_ctx = GetFiberContext(); + if (!g_ctx) { + return ORBIS_FIBER_ERROR_PERMISSION; + } + + *fiber = g_ctx->current_fiber; + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFiberReturnToThread(u64 arg_on_return, u64* arg_on_run) { + OrbisFiberContext* g_ctx = GetFiberContext(); + if (!g_ctx) { + return ORBIS_FIBER_ERROR_PERMISSION; + } + + OrbisFiber* cur_fiber = g_ctx->current_fiber; + if (cur_fiber->addr_context) { + OrbisFiberContext ctx{}; + s32 jmp = _sceFiberSetJmp(&ctx); + if (jmp) { + g_ctx = GetFiberContext(); + if (g_ctx->prev_fiber) { + g_ctx->prev_fiber->state = FiberState::Idle; + g_ctx->prev_fiber = nullptr; + } + if (arg_on_run) { + *arg_on_run = g_ctx->arg_on_run_to; + } + return ORBIS_OK; + } + + cur_fiber->context = &ctx; + _sceFiberCheckStackOverflow(g_ctx); + } + + _sceFiberTerminate(cur_fiber, arg_on_return, g_ctx); + __builtin_trap(); +} + +s32 PS4_SYSV_ABI sceFiberGetInfo(OrbisFiber* fiber, OrbisFiberInfo* fiber_info) { + if (!fiber || !fiber_info) { + return ORBIS_FIBER_ERROR_NULL; + } + if ((u64)fiber & 7 || (u64)fiber_info & 7) { + return ORBIS_FIBER_ERROR_ALIGNMENT; + } + if (fiber_info->size != sizeof(OrbisFiberInfo)) { + return ORBIS_FIBER_ERROR_INVALID; + } + if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) { + return ORBIS_FIBER_ERROR_INVALID; + } + + fiber_info->entry = fiber->entry; + fiber_info->arg_on_initialize = fiber->arg_on_initialize; + fiber_info->addr_context = fiber->addr_context; + fiber_info->size_context = fiber->size_context; + strncpy(fiber_info->name, fiber->name, ORBIS_FIBER_MAX_NAME_LENGTH); + + fiber_info->size_context_margin = -1; + if (fiber->flags & FiberFlags::ContextSizeCheck && fiber->addr_context != nullptr) { + u64 stack_margin = 0; + u64* stack_start = reinterpret_cast(fiber->context_start); + u64* stack_end = reinterpret_cast(fiber->context_end); + + if (*stack_start == kFiberStackSignature) { + u64* stack_ptr = stack_start + 1; + while (stack_ptr < stack_end) { + if (*stack_ptr == kFiberStackSizeCheck) { + stack_ptr++; + } + } + + stack_margin = + reinterpret_cast(stack_ptr) - reinterpret_cast(stack_start + 1); + } + + fiber_info->size_context_margin = stack_margin; + } + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFiberStartContextSizeCheck(u32 flags) { + if (flags != 0) { + return ORBIS_FIBER_ERROR_INVALID; + } + + u32 expected = 0; + if (!context_size_check.compare_exchange_strong(expected, 1u)) { + return ORBIS_FIBER_ERROR_STATE; + } + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFiberStopContextSizeCheck() { + u32 expected = 1; + if (!context_size_check.compare_exchange_strong(expected, 0u)) { + return ORBIS_FIBER_ERROR_STATE; + } + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFiberRename(OrbisFiber* fiber, const char* name) { + if (!fiber || !name) { + return ORBIS_FIBER_ERROR_NULL; + } + if ((u64)fiber & 7) { + return ORBIS_FIBER_ERROR_ALIGNMENT; + } + if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) { + return ORBIS_FIBER_ERROR_INVALID; + } + + strncpy(fiber->name, name, ORBIS_FIBER_MAX_NAME_LENGTH); + return ORBIS_OK; +} + +void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("hVYD7Ou2pCQ", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberInitialize); + LIB_FUNCTION("7+OJIpko9RY", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberInitialize); + LIB_FUNCTION("asjUJJ+aa8s", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberOptParamInitialize); + LIB_FUNCTION("JeNX5F-NzQU", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberFinalize); + + LIB_FUNCTION("a0LLrZWac0M", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberRun); + LIB_FUNCTION("PFT2S-tJ7Uk", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberSwitch); + LIB_FUNCTION("p+zLIOg27zU", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberGetSelf); + LIB_FUNCTION("B0ZX2hx9DMw", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberReturnToThread); + + LIB_FUNCTION("uq2Y5BFz0PE", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberGetInfo); + LIB_FUNCTION("Lcqty+QNWFc", "libSceFiber", 1, "libSceFiber", 1, 1, + sceFiberStartContextSizeCheck); + LIB_FUNCTION("Kj4nXMpnM8Y", "libSceFiber", 1, "libSceFiber", 1, 1, + sceFiberStopContextSizeCheck); + LIB_FUNCTION("JzyT91ucGDc", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberRename); +} + +} // namespace Libraries::Fiber diff --git a/src/core/libraries/fiber/fiber.h b/src/core/libraries/fiber/fiber.h index 325522fca..3c4e3b70e 100644 --- a/src/core/libraries/fiber/fiber.h +++ b/src/core/libraries/fiber/fiber.h @@ -1,118 +1,118 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/assert.h" -#include "common/types.h" - -#include - -namespace Core::Loader { -class SymbolsResolver; -} -namespace Libraries::Fiber { - -#define ORBIS_FIBER_MAX_NAME_LENGTH (31) -#define ORBIS_FIBER_CONTEXT_MINIMUM_SIZE (512) - -typedef void PS4_SYSV_ABI (*OrbisFiberEntry)(u64 arg_on_initialize, u64 arg_on_run); - -enum FiberState : u32 { - Run = 1u, - Idle = 2u, - Terminated = 3u, -}; - -enum FiberFlags : u32 { - None = 0x0, - NoUlobjmgr = 0x1, - ContextSizeCheck = 0x10, - SetFpuRegs = 0x100, -}; - -struct OrbisFiber; - -struct OrbisFiberContext { - struct { - u64 rax, rcx, rdx, rbx, rsp, rbp, r8, r9, r10, r11, r12, r13, r14, r15; - u16 fpucw; - u32 mxcsr; - }; - OrbisFiber* current_fiber; - OrbisFiber* prev_fiber; - u64 arg_on_run_to; - u64 arg_on_return; - u64 return_val; -}; - -struct OrbisFiberData { - OrbisFiberEntry entry; - u64 arg_on_initialize; - u64 arg_on_run_to; - void* stack_addr; - u32* state; - u16 fpucw; - s8 pad[2]; - u32 mxcsr; -}; - -struct OrbisFiber { - u32 magic_start; - std::atomic state; - OrbisFiberEntry entry; - u64 arg_on_initialize; - void* addr_context; - u64 size_context; - char name[ORBIS_FIBER_MAX_NAME_LENGTH + 1]; - OrbisFiberContext* context; - u32 flags; - void* context_start; - void* context_end; - u32 magic_end; -}; -static_assert(sizeof(OrbisFiber) <= 256); - -struct OrbisFiberInfo { - u64 size; - OrbisFiberEntry entry; - u64 arg_on_initialize; - void* addr_context; - u64 size_context; - char name[ORBIS_FIBER_MAX_NAME_LENGTH + 1]; - u64 size_context_margin; - u8 pad[48]; -}; -static_assert(sizeof(OrbisFiberInfo) == 128); - -struct OrbisFiberOptParam { - u32 magic; -}; -static_assert(sizeof(OrbisFiberOptParam) <= 128); - -s32 PS4_SYSV_ABI sceFiberInitialize(OrbisFiber* fiber, const char* name, OrbisFiberEntry entry, - u64 arg_on_initialize, void* addr_context, u64 size_context, - const OrbisFiberOptParam* opt_param, u32 build_version); - -s32 PS4_SYSV_ABI sceFiberOptParamInitialize(OrbisFiberOptParam* opt_param); - -s32 PS4_SYSV_ABI sceFiberFinalize(OrbisFiber* fiber); - -s32 PS4_SYSV_ABI sceFiberRun(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_return); - -s32 PS4_SYSV_ABI sceFiberSwitch(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_run); - -s32 PS4_SYSV_ABI sceFiberGetSelf(OrbisFiber** fiber); - -s32 PS4_SYSV_ABI sceFiberReturnToThread(u64 arg_on_return, u64* arg_on_run); - -s32 PS4_SYSV_ABI sceFiberGetInfo(OrbisFiber* fiber, OrbisFiberInfo* fiber_info); - -s32 PS4_SYSV_ABI sceFiberStartContextSizeCheck(u32 flags); - -s32 PS4_SYSV_ABI sceFiberStopContextSizeCheck(void); - -s32 PS4_SYSV_ABI sceFiberRename(OrbisFiber* fiber, const char* name); - -void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym); +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/assert.h" +#include "common/types.h" + +#include + +namespace Core::Loader { +class SymbolsResolver; +} +namespace Libraries::Fiber { + +#define ORBIS_FIBER_MAX_NAME_LENGTH (31) +#define ORBIS_FIBER_CONTEXT_MINIMUM_SIZE (512) + +typedef void PS4_SYSV_ABI (*OrbisFiberEntry)(u64 arg_on_initialize, u64 arg_on_run); + +enum FiberState : u32 { + Run = 1u, + Idle = 2u, + Terminated = 3u, +}; + +enum FiberFlags : u32 { + None = 0x0, + NoUlobjmgr = 0x1, + ContextSizeCheck = 0x10, + SetFpuRegs = 0x100, +}; + +struct OrbisFiber; + +struct OrbisFiberContext { + struct { + u64 rax, rcx, rdx, rbx, rsp, rbp, r8, r9, r10, r11, r12, r13, r14, r15; + u16 fpucw; + u32 mxcsr; + }; + OrbisFiber* current_fiber; + OrbisFiber* prev_fiber; + u64 arg_on_run_to; + u64 arg_on_return; + u64 return_val; +}; + +struct OrbisFiberData { + OrbisFiberEntry entry; + u64 arg_on_initialize; + u64 arg_on_run_to; + void* stack_addr; + u32* state; + u16 fpucw; + s8 pad[2]; + u32 mxcsr; +}; + +struct OrbisFiber { + u32 magic_start; + std::atomic state; + OrbisFiberEntry entry; + u64 arg_on_initialize; + void* addr_context; + u64 size_context; + char name[ORBIS_FIBER_MAX_NAME_LENGTH + 1]; + OrbisFiberContext* context; + u32 flags; + void* context_start; + void* context_end; + u32 magic_end; +}; +static_assert(sizeof(OrbisFiber) <= 256); + +struct OrbisFiberInfo { + u64 size; + OrbisFiberEntry entry; + u64 arg_on_initialize; + void* addr_context; + u64 size_context; + char name[ORBIS_FIBER_MAX_NAME_LENGTH + 1]; + u64 size_context_margin; + u8 pad[48]; +}; +static_assert(sizeof(OrbisFiberInfo) == 128); + +struct OrbisFiberOptParam { + u32 magic; +}; +static_assert(sizeof(OrbisFiberOptParam) <= 128); + +s32 PS4_SYSV_ABI sceFiberInitialize(OrbisFiber* fiber, const char* name, OrbisFiberEntry entry, + u64 arg_on_initialize, void* addr_context, u64 size_context, + const OrbisFiberOptParam* opt_param, u32 build_version); + +s32 PS4_SYSV_ABI sceFiberOptParamInitialize(OrbisFiberOptParam* opt_param); + +s32 PS4_SYSV_ABI sceFiberFinalize(OrbisFiber* fiber); + +s32 PS4_SYSV_ABI sceFiberRun(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_return); + +s32 PS4_SYSV_ABI sceFiberSwitch(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_run); + +s32 PS4_SYSV_ABI sceFiberGetSelf(OrbisFiber** fiber); + +s32 PS4_SYSV_ABI sceFiberReturnToThread(u64 arg_on_return, u64* arg_on_run); + +s32 PS4_SYSV_ABI sceFiberGetInfo(OrbisFiber* fiber, OrbisFiberInfo* fiber_info); + +s32 PS4_SYSV_ABI sceFiberStartContextSizeCheck(u32 flags); + +s32 PS4_SYSV_ABI sceFiberStopContextSizeCheck(void); + +s32 PS4_SYSV_ABI sceFiberRename(OrbisFiber* fiber, const char* name); + +void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::Fiber \ No newline at end of file diff --git a/src/core/libraries/fiber/fiber_context.s b/src/core/libraries/fiber/fiber_context.s index 44e51d38c..04fe93581 100644 --- a/src/core/libraries/fiber/fiber_context.s +++ b/src/core/libraries/fiber/fiber_context.s @@ -1,121 +1,121 @@ -# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -# SPDX-License-Identifier: GPL-2.0-or-later - -.global _sceFiberSetJmp -_sceFiberSetJmp: - movq %rax, 0x0(%rdi) - - movq (%rsp), %rdx - movq %rdx, 0x10(%rdi) - - movq %rcx, 0x08(%rdi) - movq %rbx, 0x18(%rdi) - movq %rsp, 0x20(%rdi) - movq %rbp, 0x28(%rdi) - - movq %r8, 0x30(%rdi) - movq %r9, 0x38(%rdi) - movq %r10, 0x40(%rdi) - movq %r11, 0x48(%rdi) - movq %r12, 0x50(%rdi) - movq %r13, 0x58(%rdi) - movq %r14, 0x60(%rdi) - movq %r15, 0x68(%rdi) - - fnstcw 0x70(%rdi) - stmxcsr 0x72(%rdi) - - xor %eax, %eax - ret - -.global _sceFiberLongJmp -_sceFiberLongJmp: - # MXCSR = (MXCSR & 0x3f) ^ (ctx->mxcsr & ~0x3f) - stmxcsr -0x4(%rsp) - movl 0x72(%rdi), %eax - andl $0xffffffc0, %eax - movl -0x4(%rsp), %ecx - andl $0x3f, %ecx - xorl %eax, %ecx - movl %ecx, -0x4(%rsp) - ldmxcsr -0x4(%rsp) - - movq 0x00(%rdi), %rax - movq 0x08(%rdi), %rcx - movq 0x10(%rdi), %rdx - movq 0x18(%rdi), %rbx - movq 0x20(%rdi), %rsp - movq 0x28(%rdi), %rbp - - movq 0x30(%rdi), %r8 - movq 0x38(%rdi), %r9 - movq 0x40(%rdi), %r10 - movq 0x48(%rdi), %r11 - movq 0x50(%rdi), %r12 - movq 0x58(%rdi), %r13 - movq 0x60(%rdi), %r14 - movq 0x68(%rdi), %r15 - - fldcw 0x70(%rdi) - - # Make the jump and return 1 - movq %rdx, 0x00(%rsp) - movl $0x1, %eax - ret - -.global _sceFiberSwitchEntry -_sceFiberSwitchEntry: - mov %rdi, %r11 - - # Set stack address to provided stack - movq 0x18(%r11), %rsp - xorl %ebp, %ebp - - movq 0x20(%r11), %r10 # data->state - - # Set previous fiber state to Idle - test %r10, %r10 - jz .clear_regs - movl $2, (%r10) - -.clear_regs: - test %esi, %esi - jz .skip_fpu_regs - - ldmxcsr 0x2c(%r11) - fldcw 0x28(%r11) - -.skip_fpu_regs: - movq 0x08(%r11), %rdi # data->arg_on_initialize - movq 0x10(%r11), %rsi # data->arg_on_run_to - movq 0x00(%r11), %r11 # data->entry - - xorl %eax, %eax - xorl %ebx, %ebx - xorl %ecx, %ecx - xorl %edx, %edx - xorq %r8, %r8 - xorq %r9, %r9 - xorq %r10, %r10 - xorq %r12, %r12 - xorq %r13, %r13 - xorq %r14, %r14 - xorq %r15, %r15 - pxor %mm0, %mm0 - pxor %mm1, %mm1 - pxor %mm2, %mm2 - pxor %mm3, %mm3 - pxor %mm4, %mm4 - pxor %mm5, %mm5 - pxor %mm6, %mm6 - pxor %mm7, %mm7 - emms - vzeroall - - # Call the fiber's entry function: entry(arg_on_initialize, arg_on_run_to) - call *%r11 - - # Fiber returned, not good - movl $1, %edi - call _sceFiberForceQuit +# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +.global _sceFiberSetJmp +_sceFiberSetJmp: + movq %rax, 0x0(%rdi) + + movq (%rsp), %rdx + movq %rdx, 0x10(%rdi) + + movq %rcx, 0x08(%rdi) + movq %rbx, 0x18(%rdi) + movq %rsp, 0x20(%rdi) + movq %rbp, 0x28(%rdi) + + movq %r8, 0x30(%rdi) + movq %r9, 0x38(%rdi) + movq %r10, 0x40(%rdi) + movq %r11, 0x48(%rdi) + movq %r12, 0x50(%rdi) + movq %r13, 0x58(%rdi) + movq %r14, 0x60(%rdi) + movq %r15, 0x68(%rdi) + + fnstcw 0x70(%rdi) + stmxcsr 0x72(%rdi) + + xor %eax, %eax + ret + +.global _sceFiberLongJmp +_sceFiberLongJmp: + # MXCSR = (MXCSR & 0x3f) ^ (ctx->mxcsr & ~0x3f) + stmxcsr -0x4(%rsp) + movl 0x72(%rdi), %eax + andl $0xffffffc0, %eax + movl -0x4(%rsp), %ecx + andl $0x3f, %ecx + xorl %eax, %ecx + movl %ecx, -0x4(%rsp) + ldmxcsr -0x4(%rsp) + + movq 0x00(%rdi), %rax + movq 0x08(%rdi), %rcx + movq 0x10(%rdi), %rdx + movq 0x18(%rdi), %rbx + movq 0x20(%rdi), %rsp + movq 0x28(%rdi), %rbp + + movq 0x30(%rdi), %r8 + movq 0x38(%rdi), %r9 + movq 0x40(%rdi), %r10 + movq 0x48(%rdi), %r11 + movq 0x50(%rdi), %r12 + movq 0x58(%rdi), %r13 + movq 0x60(%rdi), %r14 + movq 0x68(%rdi), %r15 + + fldcw 0x70(%rdi) + + # Make the jump and return 1 + movq %rdx, 0x00(%rsp) + movl $0x1, %eax + ret + +.global _sceFiberSwitchEntry +_sceFiberSwitchEntry: + mov %rdi, %r11 + + # Set stack address to provided stack + movq 0x18(%r11), %rsp + xorl %ebp, %ebp + + movq 0x20(%r11), %r10 # data->state + + # Set previous fiber state to Idle + test %r10, %r10 + jz .clear_regs + movl $2, (%r10) + +.clear_regs: + test %esi, %esi + jz .skip_fpu_regs + + ldmxcsr 0x2c(%r11) + fldcw 0x28(%r11) + +.skip_fpu_regs: + movq 0x08(%r11), %rdi # data->arg_on_initialize + movq 0x10(%r11), %rsi # data->arg_on_run_to + movq 0x00(%r11), %r11 # data->entry + + xorl %eax, %eax + xorl %ebx, %ebx + xorl %ecx, %ecx + xorl %edx, %edx + xorq %r8, %r8 + xorq %r9, %r9 + xorq %r10, %r10 + xorq %r12, %r12 + xorq %r13, %r13 + xorq %r14, %r14 + xorq %r15, %r15 + pxor %mm0, %mm0 + pxor %mm1, %mm1 + pxor %mm2, %mm2 + pxor %mm3, %mm3 + pxor %mm4, %mm4 + pxor %mm5, %mm5 + pxor %mm6, %mm6 + pxor %mm7, %mm7 + emms + vzeroall + + # Call the fiber's entry function: entry(arg_on_initialize, arg_on_run_to) + call *%r11 + + # Fiber returned, not good + movl $1, %edi + call _sceFiberForceQuit ret \ No newline at end of file diff --git a/src/core/libraries/ime/ime_common.h b/src/core/libraries/ime/ime_common.h index 6d4afd81d..96f073dc5 100644 --- a/src/core/libraries/ime/ime_common.h +++ b/src/core/libraries/ime/ime_common.h @@ -1,183 +1,183 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/types.h" -#include "core/libraries/rtc/rtc.h" - -enum class OrbisImeType : u32 { - Default = 0, - BasicLatin = 1, - Url = 2, - Mail = 3, - Number = 4, -}; - -enum class OrbisImeHorizontalAlignment : u32 { - Left = 0, - Center = 1, - Right = 2, -}; - -enum class OrbisImeVerticalAlignment : u32 { - Top = 0, - Center = 1, - Bottom = 2, -}; - -enum class OrbisImeEnterLabel : u32 { - Default = 0, - Send = 1, - Search = 2, - Go = 3, -}; - -enum class OrbisImeInputMethod : u32 { - Default = 0, -}; - -enum class OrbisImeEventId : u32 { - Open = 0, - UpdateText = 1, - UpdateCaret = 2, - PressClose = 4, - PressEnter = 5, - Abort = 6, - CandidateListStart = 7, - CandidateListEnd = 8, - CandidateWord = 9, - CandidateIndex = 10, - CandidateDone = 11, - CandidateCancel = 12, - ChangeDevice = 14, - ChangeInputMethodState = 18, - - KeyboardOpen = 256, - KeyboardKeycodeDoen = 257, - KeyboardKeycodeUp = 258, - KeyboardKeycodeRepeat = 259, - KeyboardConnection = 260, - KeyboardDisconnection = 261, - KeyboardAbort = 262, -}; - -enum class OrbisImeKeyboardType : u32 { - NONE = 0, - DANISH = 1, - GERMAN = 2, - GERMAN_SW = 3, - ENGLISH_US = 4, - ENGLISH_GB = 5, - SPANISH = 6, - SPANISH_LA = 7, - FINNISH = 8, - FRENCH = 9, - FRENCH_BR = 10, - FRENCH_CA = 11, - FRENCH_SW = 12, - ITALIAN = 13, - DUTCH = 14, - NORWEGIAN = 15, - POLISH = 16, - PORTUGUESE_BR = 17, - PORTUGUESE_PT = 18, - RUSSIAN = 19, - SWEDISH = 20, - TURKISH = 21, - JAPANESE_ROMAN = 22, - JAPANESE_KANA = 23, - KOREAN = 24, - SM_CHINESE = 25, - TR_CHINESE_ZY = 26, - TR_CHINESE_PY_HK = 27, - TR_CHINESE_PY_TW = 28, - TR_CHINESE_CG = 29, - ARABIC_AR = 30, - THAI = 31, - CZECH = 32, - GREEK = 33, - INDONESIAN = 34, - VIETNAMESE = 35, - ROMANIAN = 36, - HUNGARIAN = 37, -}; - -enum class OrbisImeDeviceType : u32 { - None = 0, - Controller = 1, - ExtKeyboard = 2, - RemoteOsk = 3, -}; - -struct OrbisImeRect { - f32 x; - f32 y; - u32 width; - u32 height; -}; - -struct OrbisImeTextAreaProperty { - u32 mode; // OrbisImeTextAreaMode - u32 index; - s32 length; -}; - -struct OrbisImeEditText { - char16_t* str; - u32 caret_index; - u32 area_num; - OrbisImeTextAreaProperty text_area[4]; -}; - -struct OrbisImeKeycode { - u16 keycode; - char16_t character; - u32 status; - OrbisImeKeyboardType type; - s32 user_id; - u32 resource_id; - Libraries::Rtc::OrbisRtcTick timestamp; -}; - -struct OrbisImeKeyboardResourceIdArray { - s32 userId; - u32 resourceId[5]; -}; - -enum class OrbisImeCaretMovementDirection : u32 { - Still = 0, - Left = 1, - Right = 2, - Up = 3, - Down = 4, - Home = 5, - End = 6, - PageUp = 7, - PageDown = 8, - Top = 9, - Bottom = 10, -}; - -union OrbisImeEventParam { - OrbisImeRect rect; - OrbisImeEditText text; - OrbisImeCaretMovementDirection caret_move; - OrbisImeKeycode keycode; - OrbisImeKeyboardResourceIdArray resource_id_array; - char16_t* candidate_word; - s32 candidate_index; - OrbisImeDeviceType device_type; - u32 input_method_state; - s8 reserved[64]; -}; - -struct OrbisImeEvent { - OrbisImeEventId id; - OrbisImeEventParam param; -}; - -using OrbisImeTextFilter = PS4_SYSV_ABI int (*)(char16_t* outText, u32* outTextLength, - const char16_t* srcText, u32 srcTextLength); - -using OrbisImeEventHandler = PS4_SYSV_ABI void (*)(void* arg, const OrbisImeEvent* e); +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" +#include "core/libraries/rtc/rtc.h" + +enum class OrbisImeType : u32 { + Default = 0, + BasicLatin = 1, + Url = 2, + Mail = 3, + Number = 4, +}; + +enum class OrbisImeHorizontalAlignment : u32 { + Left = 0, + Center = 1, + Right = 2, +}; + +enum class OrbisImeVerticalAlignment : u32 { + Top = 0, + Center = 1, + Bottom = 2, +}; + +enum class OrbisImeEnterLabel : u32 { + Default = 0, + Send = 1, + Search = 2, + Go = 3, +}; + +enum class OrbisImeInputMethod : u32 { + Default = 0, +}; + +enum class OrbisImeEventId : u32 { + Open = 0, + UpdateText = 1, + UpdateCaret = 2, + PressClose = 4, + PressEnter = 5, + Abort = 6, + CandidateListStart = 7, + CandidateListEnd = 8, + CandidateWord = 9, + CandidateIndex = 10, + CandidateDone = 11, + CandidateCancel = 12, + ChangeDevice = 14, + ChangeInputMethodState = 18, + + KeyboardOpen = 256, + KeyboardKeycodeDoen = 257, + KeyboardKeycodeUp = 258, + KeyboardKeycodeRepeat = 259, + KeyboardConnection = 260, + KeyboardDisconnection = 261, + KeyboardAbort = 262, +}; + +enum class OrbisImeKeyboardType : u32 { + NONE = 0, + DANISH = 1, + GERMAN = 2, + GERMAN_SW = 3, + ENGLISH_US = 4, + ENGLISH_GB = 5, + SPANISH = 6, + SPANISH_LA = 7, + FINNISH = 8, + FRENCH = 9, + FRENCH_BR = 10, + FRENCH_CA = 11, + FRENCH_SW = 12, + ITALIAN = 13, + DUTCH = 14, + NORWEGIAN = 15, + POLISH = 16, + PORTUGUESE_BR = 17, + PORTUGUESE_PT = 18, + RUSSIAN = 19, + SWEDISH = 20, + TURKISH = 21, + JAPANESE_ROMAN = 22, + JAPANESE_KANA = 23, + KOREAN = 24, + SM_CHINESE = 25, + TR_CHINESE_ZY = 26, + TR_CHINESE_PY_HK = 27, + TR_CHINESE_PY_TW = 28, + TR_CHINESE_CG = 29, + ARABIC_AR = 30, + THAI = 31, + CZECH = 32, + GREEK = 33, + INDONESIAN = 34, + VIETNAMESE = 35, + ROMANIAN = 36, + HUNGARIAN = 37, +}; + +enum class OrbisImeDeviceType : u32 { + None = 0, + Controller = 1, + ExtKeyboard = 2, + RemoteOsk = 3, +}; + +struct OrbisImeRect { + f32 x; + f32 y; + u32 width; + u32 height; +}; + +struct OrbisImeTextAreaProperty { + u32 mode; // OrbisImeTextAreaMode + u32 index; + s32 length; +}; + +struct OrbisImeEditText { + char16_t* str; + u32 caret_index; + u32 area_num; + OrbisImeTextAreaProperty text_area[4]; +}; + +struct OrbisImeKeycode { + u16 keycode; + char16_t character; + u32 status; + OrbisImeKeyboardType type; + s32 user_id; + u32 resource_id; + Libraries::Rtc::OrbisRtcTick timestamp; +}; + +struct OrbisImeKeyboardResourceIdArray { + s32 userId; + u32 resourceId[5]; +}; + +enum class OrbisImeCaretMovementDirection : u32 { + Still = 0, + Left = 1, + Right = 2, + Up = 3, + Down = 4, + Home = 5, + End = 6, + PageUp = 7, + PageDown = 8, + Top = 9, + Bottom = 10, +}; + +union OrbisImeEventParam { + OrbisImeRect rect; + OrbisImeEditText text; + OrbisImeCaretMovementDirection caret_move; + OrbisImeKeycode keycode; + OrbisImeKeyboardResourceIdArray resource_id_array; + char16_t* candidate_word; + s32 candidate_index; + OrbisImeDeviceType device_type; + u32 input_method_state; + s8 reserved[64]; +}; + +struct OrbisImeEvent { + OrbisImeEventId id; + OrbisImeEventParam param; +}; + +using OrbisImeTextFilter = PS4_SYSV_ABI int (*)(char16_t* outText, u32* outTextLength, + const char16_t* srcText, u32 srcTextLength); + +using OrbisImeEventHandler = PS4_SYSV_ABI void (*)(void* arg, const OrbisImeEvent* e); diff --git a/src/core/libraries/ime/ime_ui.cpp b/src/core/libraries/ime/ime_ui.cpp index 8eaa48178..37f25e200 100644 --- a/src/core/libraries/ime/ime_ui.cpp +++ b/src/core/libraries/ime/ime_ui.cpp @@ -1,253 +1,253 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "ime_ui.h" -#include "imgui/imgui_std.h" - -namespace Libraries::Ime { - -using namespace ImGui; - -static constexpr ImVec2 BUTTON_SIZE{100.0f, 30.0f}; - -ImeState::ImeState(const OrbisImeParam* param) { - if (!param) { - return; - } - - work_buffer = param->work; - text_buffer = param->inputTextBuffer; - - std::size_t text_len = std::char_traits::length(text_buffer); - if (!ConvertOrbisToUTF8(text_buffer, text_len, current_text.begin(), - ORBIS_IME_MAX_TEXT_LENGTH * 4)) { - LOG_ERROR(Lib_ImeDialog, "Failed to convert text to utf8 encoding"); - } -} - -ImeState::ImeState(ImeState&& other) noexcept - : work_buffer(other.work_buffer), text_buffer(other.text_buffer), - current_text(std::move(other.current_text)), event_queue(std::move(other.event_queue)) { - other.text_buffer = nullptr; -} - -ImeState& ImeState::operator=(ImeState&& other) noexcept { - if (this != &other) { - work_buffer = other.work_buffer; - text_buffer = other.text_buffer; - current_text = std::move(other.current_text); - event_queue = std::move(other.event_queue); - - other.text_buffer = nullptr; - } - return *this; -} - -void ImeState::SendEvent(OrbisImeEvent* event) { - std::unique_lock lock{queue_mutex}; - event_queue.push(*event); -} - -void ImeState::SendEnterEvent() { - OrbisImeEvent enterEvent{}; - enterEvent.id = OrbisImeEventId::PressEnter; - SendEvent(&enterEvent); -} - -void ImeState::SendCloseEvent() { - OrbisImeEvent closeEvent{}; - closeEvent.id = OrbisImeEventId::PressClose; - closeEvent.param.text.str = reinterpret_cast(work_buffer); - SendEvent(&closeEvent); -} - -void ImeState::SetText(const char16_t* text, u32 length) {} - -void ImeState::SetCaret(u32 position) {} - -bool ImeState::ConvertOrbisToUTF8(const char16_t* orbis_text, std::size_t orbis_text_len, - char* utf8_text, std::size_t utf8_text_len) { - std::fill(utf8_text, utf8_text + utf8_text_len, '\0'); - const ImWchar* orbis_text_ptr = reinterpret_cast(orbis_text); - ImTextStrToUtf8(utf8_text, utf8_text_len, orbis_text_ptr, orbis_text_ptr + orbis_text_len); - - return true; -} - -bool ImeState::ConvertUTF8ToOrbis(const char* utf8_text, std::size_t utf8_text_len, - char16_t* orbis_text, std::size_t orbis_text_len) { - std::fill(orbis_text, orbis_text + orbis_text_len, u'\0'); - ImTextStrFromUtf8(reinterpret_cast(orbis_text), orbis_text_len, utf8_text, nullptr); - - return true; -} - -ImeUi::ImeUi(ImeState* state, const OrbisImeParam* param) : state(state), ime_param(param) { - if (param) { - AddLayer(this); - } -} - -ImeUi::~ImeUi() { - std::scoped_lock lock(draw_mutex); - Free(); -} - -ImeUi& ImeUi::operator=(ImeUi&& other) { - std::scoped_lock lock(draw_mutex, other.draw_mutex); - Free(); - - state = other.state; - ime_param = other.ime_param; - first_render = other.first_render; - other.state = nullptr; - other.ime_param = nullptr; - - AddLayer(this); - return *this; -} - -void ImeUi::Draw() { - std::unique_lock lock{draw_mutex}; - - if (!state) { - return; - } - - const auto& ctx = *GetCurrentContext(); - const auto& io = ctx.IO; - - // TODO: Figure out how to properly translate the positions - - // for example, if a game wants to center the IME panel, - // we have to translate the panel position in a way that it - // still becomes centered, as the game normally calculates - // the position assuming a it's running on a 1920x1080 screen, - // whereas we are running on a 1280x720 window size (by default). - // - // e.g. Panel position calculation from a game: - // param.posx = (1920 / 2) - (panelWidth / 2); - // param.posy = (1080 / 2) - (panelHeight / 2); - const auto size = GetIO().DisplaySize; - f32 pos_x = (ime_param->posx / 1920.0f * (float)size.x); - f32 pos_y = (ime_param->posy / 1080.0f * (float)size.y); - - ImVec2 window_pos = {pos_x, pos_y}; - ImVec2 window_size = {500.0f, 100.0f}; - - // SetNextWindowPos(window_pos); - SetNextWindowPos(ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f), - ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f)); - SetNextWindowSize(window_size); - SetNextWindowCollapsed(false); - - if (first_render || !io.NavActive) { - SetNextWindowFocus(); - } - - if (Begin("IME##Ime", nullptr, - ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | - ImGuiWindowFlags_NoSavedSettings)) { - DrawPrettyBackground(); - - DrawInputText(); - SetCursorPosY(GetCursorPosY() + 10.0f); - - const char* button_text; - button_text = "Done##ImeDone"; - - float button_spacing = 10.0f; - float total_button_width = BUTTON_SIZE.x * 2 + button_spacing; - float button_start_pos = (window_size.x - total_button_width) / 2.0f; - - SetCursorPosX(button_start_pos); - - if (Button(button_text, BUTTON_SIZE) || (IsKeyPressed(ImGuiKey_Enter))) { - state->SendEnterEvent(); - } - - SameLine(0.0f, button_spacing); - - if (Button("Close##ImeClose", BUTTON_SIZE)) { - state->SendCloseEvent(); - } - } - End(); - - first_render = false; -} - -void ImeUi::DrawInputText() { - ImVec2 input_size = {GetWindowWidth() - 40.0f, 0.0f}; - SetCursorPosX(20.0f); - if (first_render) { - SetKeyboardFocusHere(); - } - if (InputTextEx("##ImeInput", nullptr, state->current_text.begin(), ime_param->maxTextLength, - input_size, ImGuiInputTextFlags_CallbackAlways, InputTextCallback, this)) { - } -} - -int ImeUi::InputTextCallback(ImGuiInputTextCallbackData* data) { - ImeUi* ui = static_cast(data->UserData); - ASSERT(ui); - - static std::string lastText; - std::string currentText(data->Buf, data->BufTextLen); - if (currentText != lastText) { - OrbisImeEditText eventParam{}; - eventParam.str = reinterpret_cast(ui->ime_param->work); - eventParam.caret_index = data->CursorPos; - eventParam.area_num = 1; - - eventParam.text_area[0].mode = 1; // Edit mode - eventParam.text_area[0].index = data->CursorPos; - eventParam.text_area[0].length = data->BufTextLen; - - if (!ui->state->ConvertUTF8ToOrbis(data->Buf, data->BufTextLen, eventParam.str, - ui->ime_param->maxTextLength)) { - LOG_ERROR(Lib_ImeDialog, "Failed to convert Orbis char to UTF-8"); - return 0; - } - - if (!ui->state->ConvertUTF8ToOrbis(data->Buf, data->BufTextLen, - ui->ime_param->inputTextBuffer, - ui->ime_param->maxTextLength)) { - LOG_ERROR(Lib_ImeDialog, "Failed to convert Orbis char to UTF-8"); - return 0; - } - - OrbisImeEvent event{}; - event.id = OrbisImeEventId::UpdateText; - event.param.text = eventParam; - - lastText = currentText; - ui->state->SendEvent(&event); - } - - static int lastCaretPos = -1; - if (lastCaretPos == -1) { - lastCaretPos = data->CursorPos; - } else if (data->CursorPos != lastCaretPos) { - OrbisImeCaretMovementDirection caretDirection = OrbisImeCaretMovementDirection::Still; - if (data->CursorPos < lastCaretPos) { - caretDirection = OrbisImeCaretMovementDirection::Left; - } else if (data->CursorPos > lastCaretPos) { - caretDirection = OrbisImeCaretMovementDirection::Right; - } - - OrbisImeEvent event{}; - event.id = OrbisImeEventId::UpdateCaret; - event.param.caret_move = caretDirection; - - lastCaretPos = data->CursorPos; - ui->state->SendEvent(&event); - } - - return 0; -} - -void ImeUi::Free() { - RemoveLayer(this); -} - -}; // namespace Libraries::Ime +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "ime_ui.h" +#include "imgui/imgui_std.h" + +namespace Libraries::Ime { + +using namespace ImGui; + +static constexpr ImVec2 BUTTON_SIZE{100.0f, 30.0f}; + +ImeState::ImeState(const OrbisImeParam* param) { + if (!param) { + return; + } + + work_buffer = param->work; + text_buffer = param->inputTextBuffer; + + std::size_t text_len = std::char_traits::length(text_buffer); + if (!ConvertOrbisToUTF8(text_buffer, text_len, current_text.begin(), + ORBIS_IME_MAX_TEXT_LENGTH * 4)) { + LOG_ERROR(Lib_ImeDialog, "Failed to convert text to utf8 encoding"); + } +} + +ImeState::ImeState(ImeState&& other) noexcept + : work_buffer(other.work_buffer), text_buffer(other.text_buffer), + current_text(std::move(other.current_text)), event_queue(std::move(other.event_queue)) { + other.text_buffer = nullptr; +} + +ImeState& ImeState::operator=(ImeState&& other) noexcept { + if (this != &other) { + work_buffer = other.work_buffer; + text_buffer = other.text_buffer; + current_text = std::move(other.current_text); + event_queue = std::move(other.event_queue); + + other.text_buffer = nullptr; + } + return *this; +} + +void ImeState::SendEvent(OrbisImeEvent* event) { + std::unique_lock lock{queue_mutex}; + event_queue.push(*event); +} + +void ImeState::SendEnterEvent() { + OrbisImeEvent enterEvent{}; + enterEvent.id = OrbisImeEventId::PressEnter; + SendEvent(&enterEvent); +} + +void ImeState::SendCloseEvent() { + OrbisImeEvent closeEvent{}; + closeEvent.id = OrbisImeEventId::PressClose; + closeEvent.param.text.str = reinterpret_cast(work_buffer); + SendEvent(&closeEvent); +} + +void ImeState::SetText(const char16_t* text, u32 length) {} + +void ImeState::SetCaret(u32 position) {} + +bool ImeState::ConvertOrbisToUTF8(const char16_t* orbis_text, std::size_t orbis_text_len, + char* utf8_text, std::size_t utf8_text_len) { + std::fill(utf8_text, utf8_text + utf8_text_len, '\0'); + const ImWchar* orbis_text_ptr = reinterpret_cast(orbis_text); + ImTextStrToUtf8(utf8_text, utf8_text_len, orbis_text_ptr, orbis_text_ptr + orbis_text_len); + + return true; +} + +bool ImeState::ConvertUTF8ToOrbis(const char* utf8_text, std::size_t utf8_text_len, + char16_t* orbis_text, std::size_t orbis_text_len) { + std::fill(orbis_text, orbis_text + orbis_text_len, u'\0'); + ImTextStrFromUtf8(reinterpret_cast(orbis_text), orbis_text_len, utf8_text, nullptr); + + return true; +} + +ImeUi::ImeUi(ImeState* state, const OrbisImeParam* param) : state(state), ime_param(param) { + if (param) { + AddLayer(this); + } +} + +ImeUi::~ImeUi() { + std::scoped_lock lock(draw_mutex); + Free(); +} + +ImeUi& ImeUi::operator=(ImeUi&& other) { + std::scoped_lock lock(draw_mutex, other.draw_mutex); + Free(); + + state = other.state; + ime_param = other.ime_param; + first_render = other.first_render; + other.state = nullptr; + other.ime_param = nullptr; + + AddLayer(this); + return *this; +} + +void ImeUi::Draw() { + std::unique_lock lock{draw_mutex}; + + if (!state) { + return; + } + + const auto& ctx = *GetCurrentContext(); + const auto& io = ctx.IO; + + // TODO: Figure out how to properly translate the positions - + // for example, if a game wants to center the IME panel, + // we have to translate the panel position in a way that it + // still becomes centered, as the game normally calculates + // the position assuming a it's running on a 1920x1080 screen, + // whereas we are running on a 1280x720 window size (by default). + // + // e.g. Panel position calculation from a game: + // param.posx = (1920 / 2) - (panelWidth / 2); + // param.posy = (1080 / 2) - (panelHeight / 2); + const auto size = GetIO().DisplaySize; + f32 pos_x = (ime_param->posx / 1920.0f * (float)size.x); + f32 pos_y = (ime_param->posy / 1080.0f * (float)size.y); + + ImVec2 window_pos = {pos_x, pos_y}; + ImVec2 window_size = {500.0f, 100.0f}; + + // SetNextWindowPos(window_pos); + SetNextWindowPos(ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f), + ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f)); + SetNextWindowSize(window_size); + SetNextWindowCollapsed(false); + + if (first_render || !io.NavActive) { + SetNextWindowFocus(); + } + + if (Begin("IME##Ime", nullptr, + ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | + ImGuiWindowFlags_NoSavedSettings)) { + DrawPrettyBackground(); + + DrawInputText(); + SetCursorPosY(GetCursorPosY() + 10.0f); + + const char* button_text; + button_text = "Done##ImeDone"; + + float button_spacing = 10.0f; + float total_button_width = BUTTON_SIZE.x * 2 + button_spacing; + float button_start_pos = (window_size.x - total_button_width) / 2.0f; + + SetCursorPosX(button_start_pos); + + if (Button(button_text, BUTTON_SIZE) || (IsKeyPressed(ImGuiKey_Enter))) { + state->SendEnterEvent(); + } + + SameLine(0.0f, button_spacing); + + if (Button("Close##ImeClose", BUTTON_SIZE)) { + state->SendCloseEvent(); + } + } + End(); + + first_render = false; +} + +void ImeUi::DrawInputText() { + ImVec2 input_size = {GetWindowWidth() - 40.0f, 0.0f}; + SetCursorPosX(20.0f); + if (first_render) { + SetKeyboardFocusHere(); + } + if (InputTextEx("##ImeInput", nullptr, state->current_text.begin(), ime_param->maxTextLength, + input_size, ImGuiInputTextFlags_CallbackAlways, InputTextCallback, this)) { + } +} + +int ImeUi::InputTextCallback(ImGuiInputTextCallbackData* data) { + ImeUi* ui = static_cast(data->UserData); + ASSERT(ui); + + static std::string lastText; + std::string currentText(data->Buf, data->BufTextLen); + if (currentText != lastText) { + OrbisImeEditText eventParam{}; + eventParam.str = reinterpret_cast(ui->ime_param->work); + eventParam.caret_index = data->CursorPos; + eventParam.area_num = 1; + + eventParam.text_area[0].mode = 1; // Edit mode + eventParam.text_area[0].index = data->CursorPos; + eventParam.text_area[0].length = data->BufTextLen; + + if (!ui->state->ConvertUTF8ToOrbis(data->Buf, data->BufTextLen, eventParam.str, + ui->ime_param->maxTextLength)) { + LOG_ERROR(Lib_ImeDialog, "Failed to convert Orbis char to UTF-8"); + return 0; + } + + if (!ui->state->ConvertUTF8ToOrbis(data->Buf, data->BufTextLen, + ui->ime_param->inputTextBuffer, + ui->ime_param->maxTextLength)) { + LOG_ERROR(Lib_ImeDialog, "Failed to convert Orbis char to UTF-8"); + return 0; + } + + OrbisImeEvent event{}; + event.id = OrbisImeEventId::UpdateText; + event.param.text = eventParam; + + lastText = currentText; + ui->state->SendEvent(&event); + } + + static int lastCaretPos = -1; + if (lastCaretPos == -1) { + lastCaretPos = data->CursorPos; + } else if (data->CursorPos != lastCaretPos) { + OrbisImeCaretMovementDirection caretDirection = OrbisImeCaretMovementDirection::Still; + if (data->CursorPos < lastCaretPos) { + caretDirection = OrbisImeCaretMovementDirection::Left; + } else if (data->CursorPos > lastCaretPos) { + caretDirection = OrbisImeCaretMovementDirection::Right; + } + + OrbisImeEvent event{}; + event.id = OrbisImeEventId::UpdateCaret; + event.param.caret_move = caretDirection; + + lastCaretPos = data->CursorPos; + ui->state->SendEvent(&event); + } + + return 0; +} + +void ImeUi::Free() { + RemoveLayer(this); +} + +}; // namespace Libraries::Ime diff --git a/src/core/libraries/ime/ime_ui.h b/src/core/libraries/ime/ime_ui.h index a2a806bb9..3eea22b8c 100644 --- a/src/core/libraries/ime/ime_ui.h +++ b/src/core/libraries/ime/ime_ui.h @@ -1,76 +1,76 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include -#include "imgui/imgui_layer.h" - -#include "common/cstring.h" -#include "common/types.h" - -#include "ime.h" - -namespace Libraries::Ime { - -class ImeHandler; -class ImeUi; - -class ImeState { - friend class ImeHandler; - friend class ImeUi; - - void* work_buffer{}; - char16_t* text_buffer{}; - - // A character can hold up to 4 bytes in UTF-8 - Common::CString current_text; - - std::queue event_queue; - std::mutex queue_mutex; - -public: - ImeState(const OrbisImeParam* param = nullptr); - ImeState(ImeState&& other) noexcept; - ImeState& operator=(ImeState&& other) noexcept; - - void SendEvent(OrbisImeEvent* event); - void SendEnterEvent(); - void SendCloseEvent(); - - void SetText(const char16_t* text, u32 length); - void SetCaret(u32 position); - -private: - bool ConvertOrbisToUTF8(const char16_t* orbis_text, std::size_t orbis_text_len, char* utf8_text, - std::size_t native_text_len); - bool ConvertUTF8ToOrbis(const char* native_text, std::size_t utf8_text_len, - char16_t* orbis_text, std::size_t orbis_text_len); -}; - -class ImeUi : public ImGui::Layer { - ImeState* state{}; - const OrbisImeParam* ime_param{}; - - bool first_render = true; - std::mutex draw_mutex; - -public: - explicit ImeUi(ImeState* state = nullptr, const OrbisImeParam* param = nullptr); - ~ImeUi() override; - ImeUi(const ImeUi& other) = delete; - ImeUi& operator=(ImeUi&& other); - - void Draw() override; - -private: - void Free(); - - void DrawInputText(); - - static int InputTextCallback(ImGuiInputTextCallbackData* data); -}; - +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include "imgui/imgui_layer.h" + +#include "common/cstring.h" +#include "common/types.h" + +#include "ime.h" + +namespace Libraries::Ime { + +class ImeHandler; +class ImeUi; + +class ImeState { + friend class ImeHandler; + friend class ImeUi; + + void* work_buffer{}; + char16_t* text_buffer{}; + + // A character can hold up to 4 bytes in UTF-8 + Common::CString current_text; + + std::queue event_queue; + std::mutex queue_mutex; + +public: + ImeState(const OrbisImeParam* param = nullptr); + ImeState(ImeState&& other) noexcept; + ImeState& operator=(ImeState&& other) noexcept; + + void SendEvent(OrbisImeEvent* event); + void SendEnterEvent(); + void SendCloseEvent(); + + void SetText(const char16_t* text, u32 length); + void SetCaret(u32 position); + +private: + bool ConvertOrbisToUTF8(const char16_t* orbis_text, std::size_t orbis_text_len, char* utf8_text, + std::size_t native_text_len); + bool ConvertUTF8ToOrbis(const char* native_text, std::size_t utf8_text_len, + char16_t* orbis_text, std::size_t orbis_text_len); +}; + +class ImeUi : public ImGui::Layer { + ImeState* state{}; + const OrbisImeParam* ime_param{}; + + bool first_render = true; + std::mutex draw_mutex; + +public: + explicit ImeUi(ImeState* state = nullptr, const OrbisImeParam* param = nullptr); + ~ImeUi() override; + ImeUi(const ImeUi& other) = delete; + ImeUi& operator=(ImeUi&& other); + + void Draw() override; + +private: + void Free(); + + void DrawInputText(); + + static int InputTextCallback(ImGuiInputTextCallbackData* data); +}; + }; // namespace Libraries::Ime \ No newline at end of file diff --git a/src/core/libraries/kernel/sync/mutex.cpp b/src/core/libraries/kernel/sync/mutex.cpp index c5e3eba1d..b9bb33036 100644 --- a/src/core/libraries/kernel/sync/mutex.cpp +++ b/src/core/libraries/kernel/sync/mutex.cpp @@ -1,52 +1,52 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "mutex.h" - -#include "common/assert.h" - -namespace Libraries::Kernel { - -TimedMutex::TimedMutex() { -#ifdef _WIN64 - mtx = CreateMutex(nullptr, false, nullptr); - ASSERT(mtx); -#endif -} - -TimedMutex::~TimedMutex() { -#ifdef _WIN64 - CloseHandle(mtx); -#endif -} - -void TimedMutex::lock() { -#ifdef _WIN64 - for (;;) { - u64 res = WaitForSingleObjectEx(mtx, INFINITE, true); - if (res == WAIT_OBJECT_0) { - return; - } - } -#else - mtx.lock(); -#endif -} - -bool TimedMutex::try_lock() { -#ifdef _WIN64 - return WaitForSingleObjectEx(mtx, 0, true) == WAIT_OBJECT_0; -#else - return mtx.try_lock(); -#endif -} - -void TimedMutex::unlock() { -#ifdef _WIN64 - ReleaseMutex(mtx); -#else - mtx.unlock(); -#endif -} - +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "mutex.h" + +#include "common/assert.h" + +namespace Libraries::Kernel { + +TimedMutex::TimedMutex() { +#ifdef _WIN64 + mtx = CreateMutex(nullptr, false, nullptr); + ASSERT(mtx); +#endif +} + +TimedMutex::~TimedMutex() { +#ifdef _WIN64 + CloseHandle(mtx); +#endif +} + +void TimedMutex::lock() { +#ifdef _WIN64 + for (;;) { + u64 res = WaitForSingleObjectEx(mtx, INFINITE, true); + if (res == WAIT_OBJECT_0) { + return; + } + } +#else + mtx.lock(); +#endif +} + +bool TimedMutex::try_lock() { +#ifdef _WIN64 + return WaitForSingleObjectEx(mtx, 0, true) == WAIT_OBJECT_0; +#else + return mtx.try_lock(); +#endif +} + +void TimedMutex::unlock() { +#ifdef _WIN64 + ReleaseMutex(mtx); +#else + mtx.unlock(); +#endif +} + } // namespace Libraries::Kernel \ No newline at end of file diff --git a/src/core/libraries/kernel/sync/mutex.h b/src/core/libraries/kernel/sync/mutex.h index f14a920b4..d26c984ef 100644 --- a/src/core/libraries/kernel/sync/mutex.h +++ b/src/core/libraries/kernel/sync/mutex.h @@ -1,80 +1,80 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include - -#include "common/types.h" - -#ifdef _WIN64 -#include -#else -#include -#endif - -namespace Libraries::Kernel { - -class TimedMutex { -public: - TimedMutex(); - ~TimedMutex(); - - void lock(); - bool try_lock(); - - void unlock(); - - template - bool try_lock_for(const std::chrono::duration& rel_time) { -#ifdef _WIN64 - constexpr auto zero = std::chrono::duration::zero(); - const auto now = std::chrono::steady_clock::now(); - - std::chrono::steady_clock::time_point abs_time = now; - if (rel_time > zero) { - constexpr auto max = (std::chrono::steady_clock::time_point::max)(); - if (abs_time < max - rel_time) { - abs_time += rel_time; - } else { - abs_time = max; - } - } - - return try_lock_until(abs_time); -#else - return mtx.try_lock_for(rel_time); -#endif - } - - template - bool try_lock_until(const std::chrono::time_point& abs_time) { -#ifdef _WIN64 - for (;;) { - const auto now = Clock::now(); - if (abs_time <= now) { - return false; - } - - const auto rel_ms = std::chrono::ceil(abs_time - now); - u64 res = WaitForSingleObjectEx(mtx, static_cast(rel_ms.count()), true); - if (res == WAIT_OBJECT_0) { - return true; - } else if (res == WAIT_TIMEOUT) { - return false; - } - } -#else - return mtx.try_lock_until(abs_time); -#endif - } - -private: -#ifdef _WIN64 - HANDLE mtx; -#else - std::timed_mutex mtx; -#endif -}; - +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "common/types.h" + +#ifdef _WIN64 +#include +#else +#include +#endif + +namespace Libraries::Kernel { + +class TimedMutex { +public: + TimedMutex(); + ~TimedMutex(); + + void lock(); + bool try_lock(); + + void unlock(); + + template + bool try_lock_for(const std::chrono::duration& rel_time) { +#ifdef _WIN64 + constexpr auto zero = std::chrono::duration::zero(); + const auto now = std::chrono::steady_clock::now(); + + std::chrono::steady_clock::time_point abs_time = now; + if (rel_time > zero) { + constexpr auto max = (std::chrono::steady_clock::time_point::max)(); + if (abs_time < max - rel_time) { + abs_time += rel_time; + } else { + abs_time = max; + } + } + + return try_lock_until(abs_time); +#else + return mtx.try_lock_for(rel_time); +#endif + } + + template + bool try_lock_until(const std::chrono::time_point& abs_time) { +#ifdef _WIN64 + for (;;) { + const auto now = Clock::now(); + if (abs_time <= now) { + return false; + } + + const auto rel_ms = std::chrono::ceil(abs_time - now); + u64 res = WaitForSingleObjectEx(mtx, static_cast(rel_ms.count()), true); + if (res == WAIT_OBJECT_0) { + return true; + } else if (res == WAIT_TIMEOUT) { + return false; + } + } +#else + return mtx.try_lock_until(abs_time); +#endif + } + +private: +#ifdef _WIN64 + HANDLE mtx; +#else + std::timed_mutex mtx; +#endif +}; + } // namespace Libraries::Kernel \ No newline at end of file diff --git a/src/core/libraries/kernel/sync/semaphore.h b/src/core/libraries/kernel/sync/semaphore.h index 48a5dc0d8..314e2cfa9 100644 --- a/src/core/libraries/kernel/sync/semaphore.h +++ b/src/core/libraries/kernel/sync/semaphore.h @@ -1,167 +1,167 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include - -#include "common/assert.h" -#include "common/types.h" - -#ifdef _WIN64 -#include -#elif defined(__APPLE__) -#include -#else -#include -#endif - -namespace Libraries::Kernel { - -template -class Semaphore { -public: - Semaphore(s32 initialCount) -#if !defined(_WIN64) && !defined(__APPLE__) - : sem{initialCount} -#endif - { -#ifdef _WIN64 - sem = CreateSemaphore(nullptr, initialCount, max, nullptr); - ASSERT(sem); -#elif defined(__APPLE__) - sem = dispatch_semaphore_create(initialCount); - ASSERT(sem); -#endif - } - - ~Semaphore() { -#ifdef _WIN64 - CloseHandle(sem); -#elif defined(__APPLE__) - dispatch_release(sem); -#endif - } - - void release() { -#ifdef _WIN64 - ReleaseSemaphore(sem, 1, nullptr); -#elif defined(__APPLE__) - dispatch_semaphore_signal(sem); -#else - sem.release(); -#endif - } - - void acquire() { -#ifdef _WIN64 - for (;;) { - u64 res = WaitForSingleObjectEx(sem, INFINITE, true); - if (res == WAIT_OBJECT_0) { - return; - } - } -#elif defined(__APPLE__) - for (;;) { - const auto res = dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); - if (res == 0) { - return; - } - } -#else - sem.acquire(); -#endif - } - - bool try_acquire() { -#ifdef _WIN64 - return WaitForSingleObjectEx(sem, 0, true) == WAIT_OBJECT_0; -#elif defined(__APPLE__) - return dispatch_semaphore_wait(sem, DISPATCH_TIME_NOW) == 0; -#else - return sem.try_acquire(); -#endif - } - - template - bool try_acquire_for(const std::chrono::duration& rel_time) { -#ifdef _WIN64 - const auto start_time = std::chrono::high_resolution_clock::now(); - auto rel_time_ms = std::chrono::ceil(rel_time); - - while (rel_time_ms.count() > 0) { - u64 timeout_ms = static_cast(rel_time_ms.count()); - u64 res = WaitForSingleObjectEx(sem, timeout_ms, true); - if (res == WAIT_OBJECT_0) { - return true; - } else if (res == WAIT_IO_COMPLETION) { - auto elapsed_time = std::chrono::high_resolution_clock::now() - start_time; - rel_time_ms -= std::chrono::duration_cast(elapsed_time); - } else { - return false; - } - } - - return false; -#elif defined(__APPLE__) - const auto rel_time_ns = std::chrono::ceil(rel_time).count(); - const auto timeout = dispatch_time(DISPATCH_TIME_NOW, rel_time_ns); - return dispatch_semaphore_wait(sem, timeout) == 0; -#else - return sem.try_acquire_for(rel_time); -#endif - } - - template - bool try_acquire_until(const std::chrono::time_point& abs_time) { -#ifdef _WIN64 - const auto start_time = Clock::now(); - if (start_time >= abs_time) { - return false; - } - - auto rel_time = std::chrono::ceil(abs_time - start_time); - while (rel_time.count() > 0) { - u64 timeout_ms = static_cast(rel_time.count()); - u64 res = WaitForSingleObjectEx(sem, timeout_ms, true); - if (res == WAIT_OBJECT_0) { - return true; - } else if (res == WAIT_IO_COMPLETION) { - auto elapsed_time = Clock::now() - start_time; - rel_time -= std::chrono::duration_cast(elapsed_time); - } else { - return false; - } - } - - return false; -#elif defined(__APPLE__) - auto abs_s = std::chrono::time_point_cast(abs_time); - auto abs_ns = std::chrono::time_point_cast(abs_time) - - std::chrono::time_point_cast(abs_s); - const timespec abs_timespec = { - .tv_sec = abs_s.time_since_epoch().count(), - .tv_nsec = abs_ns.count(), - }; - const auto timeout = dispatch_walltime(&abs_timespec, 0); - return dispatch_semaphore_wait(sem, timeout) == 0; -#else - return sem.try_acquire_until(abs_time); -#endif - } - -private: -#ifdef _WIN64 - HANDLE sem; -#elif defined(__APPLE__) - dispatch_semaphore_t sem; -#else - std::counting_semaphore sem; -#endif -}; - -using BinarySemaphore = Semaphore<1>; -using CountingSemaphore = Semaphore<0x7FFFFFFF /*ORBIS_KERNEL_SEM_VALUE_MAX*/>; - +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#include "common/assert.h" +#include "common/types.h" + +#ifdef _WIN64 +#include +#elif defined(__APPLE__) +#include +#else +#include +#endif + +namespace Libraries::Kernel { + +template +class Semaphore { +public: + Semaphore(s32 initialCount) +#if !defined(_WIN64) && !defined(__APPLE__) + : sem{initialCount} +#endif + { +#ifdef _WIN64 + sem = CreateSemaphore(nullptr, initialCount, max, nullptr); + ASSERT(sem); +#elif defined(__APPLE__) + sem = dispatch_semaphore_create(initialCount); + ASSERT(sem); +#endif + } + + ~Semaphore() { +#ifdef _WIN64 + CloseHandle(sem); +#elif defined(__APPLE__) + dispatch_release(sem); +#endif + } + + void release() { +#ifdef _WIN64 + ReleaseSemaphore(sem, 1, nullptr); +#elif defined(__APPLE__) + dispatch_semaphore_signal(sem); +#else + sem.release(); +#endif + } + + void acquire() { +#ifdef _WIN64 + for (;;) { + u64 res = WaitForSingleObjectEx(sem, INFINITE, true); + if (res == WAIT_OBJECT_0) { + return; + } + } +#elif defined(__APPLE__) + for (;;) { + const auto res = dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + if (res == 0) { + return; + } + } +#else + sem.acquire(); +#endif + } + + bool try_acquire() { +#ifdef _WIN64 + return WaitForSingleObjectEx(sem, 0, true) == WAIT_OBJECT_0; +#elif defined(__APPLE__) + return dispatch_semaphore_wait(sem, DISPATCH_TIME_NOW) == 0; +#else + return sem.try_acquire(); +#endif + } + + template + bool try_acquire_for(const std::chrono::duration& rel_time) { +#ifdef _WIN64 + const auto start_time = std::chrono::high_resolution_clock::now(); + auto rel_time_ms = std::chrono::ceil(rel_time); + + while (rel_time_ms.count() > 0) { + u64 timeout_ms = static_cast(rel_time_ms.count()); + u64 res = WaitForSingleObjectEx(sem, timeout_ms, true); + if (res == WAIT_OBJECT_0) { + return true; + } else if (res == WAIT_IO_COMPLETION) { + auto elapsed_time = std::chrono::high_resolution_clock::now() - start_time; + rel_time_ms -= std::chrono::duration_cast(elapsed_time); + } else { + return false; + } + } + + return false; +#elif defined(__APPLE__) + const auto rel_time_ns = std::chrono::ceil(rel_time).count(); + const auto timeout = dispatch_time(DISPATCH_TIME_NOW, rel_time_ns); + return dispatch_semaphore_wait(sem, timeout) == 0; +#else + return sem.try_acquire_for(rel_time); +#endif + } + + template + bool try_acquire_until(const std::chrono::time_point& abs_time) { +#ifdef _WIN64 + const auto start_time = Clock::now(); + if (start_time >= abs_time) { + return false; + } + + auto rel_time = std::chrono::ceil(abs_time - start_time); + while (rel_time.count() > 0) { + u64 timeout_ms = static_cast(rel_time.count()); + u64 res = WaitForSingleObjectEx(sem, timeout_ms, true); + if (res == WAIT_OBJECT_0) { + return true; + } else if (res == WAIT_IO_COMPLETION) { + auto elapsed_time = Clock::now() - start_time; + rel_time -= std::chrono::duration_cast(elapsed_time); + } else { + return false; + } + } + + return false; +#elif defined(__APPLE__) + auto abs_s = std::chrono::time_point_cast(abs_time); + auto abs_ns = std::chrono::time_point_cast(abs_time) - + std::chrono::time_point_cast(abs_s); + const timespec abs_timespec = { + .tv_sec = abs_s.time_since_epoch().count(), + .tv_nsec = abs_ns.count(), + }; + const auto timeout = dispatch_walltime(&abs_timespec, 0); + return dispatch_semaphore_wait(sem, timeout) == 0; +#else + return sem.try_acquire_until(abs_time); +#endif + } + +private: +#ifdef _WIN64 + HANDLE sem; +#elif defined(__APPLE__) + dispatch_semaphore_t sem; +#else + std::counting_semaphore sem; +#endif +}; + +using BinarySemaphore = Semaphore<1>; +using CountingSemaphore = Semaphore<0x7FFFFFFF /*ORBIS_KERNEL_SEM_VALUE_MAX*/>; + } // namespace Libraries::Kernel \ No newline at end of file diff --git a/src/core/libraries/videodec/videodec2.cpp b/src/core/libraries/videodec/videodec2.cpp index 0bd68afbd..07f7a274b 100644 --- a/src/core/libraries/videodec/videodec2.cpp +++ b/src/core/libraries/videodec/videodec2.cpp @@ -1,199 +1,199 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/assert.h" -#include "common/logging/log.h" -#include "core/libraries/libs.h" -#include "core/libraries/videodec/videodec2.h" -#include "core/libraries/videodec/videodec2_impl.h" -#include "core/libraries/videodec/videodec_error.h" - -namespace Libraries::Vdec2 { - -static constexpr u64 kMinimumMemorySize = 32_MB; ///> Fake minimum memory size for querying - -s32 PS4_SYSV_ABI -sceVideodec2QueryComputeMemoryInfo(OrbisVideodec2ComputeMemoryInfo* computeMemInfo) { - LOG_INFO(Lib_Vdec2, "called"); - - if (!computeMemInfo) { - return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; - } - if (computeMemInfo->thisSize != sizeof(OrbisVideodec2ComputeMemoryInfo)) { - return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; - } - - computeMemInfo->cpuGpuMemory = nullptr; - computeMemInfo->cpuGpuMemorySize = kMinimumMemorySize; - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI -sceVideodec2AllocateComputeQueue(const OrbisVideodec2ComputeConfigInfo* computeCfgInfo, - const OrbisVideodec2ComputeMemoryInfo* computeMemInfo, - OrbisVideodec2ComputeQueue* computeQueue) { - LOG_INFO(Lib_Vdec2, "called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceVideodec2ReleaseComputeQueue(OrbisVideodec2ComputeQueue computeQueue) { - LOG_INFO(Lib_Vdec2, "called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI -sceVideodec2QueryDecoderMemoryInfo(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo, - OrbisVideodec2DecoderMemoryInfo* decoderMemInfo) { - LOG_INFO(Lib_Vdec2, "called"); - - if (!decoderCfgInfo || !decoderMemInfo) { - return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; - } - if (decoderCfgInfo->thisSize != sizeof(OrbisVideodec2DecoderConfigInfo) || - decoderMemInfo->thisSize != sizeof(OrbisVideodec2DecoderMemoryInfo)) { - return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; - } - - decoderMemInfo->cpuMemory = nullptr; - decoderMemInfo->gpuMemory = nullptr; - decoderMemInfo->cpuGpuMemory = nullptr; - - decoderMemInfo->cpuGpuMemorySize = kMinimumMemorySize; - decoderMemInfo->cpuMemorySize = kMinimumMemorySize; - decoderMemInfo->gpuMemorySize = kMinimumMemorySize; - - decoderMemInfo->maxFrameBufferSize = kMinimumMemorySize; - decoderMemInfo->frameBufferAlignment = 0x100; - - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceVideodec2CreateDecoder(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo, - const OrbisVideodec2DecoderMemoryInfo* decoderMemInfo, - OrbisVideodec2Decoder* decoder) { - LOG_INFO(Lib_Vdec2, "called"); - - if (!decoderCfgInfo || !decoderMemInfo || !decoder) { - return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; - } - if (decoderCfgInfo->thisSize != sizeof(OrbisVideodec2DecoderConfigInfo) || - decoderMemInfo->thisSize != sizeof(OrbisVideodec2DecoderMemoryInfo)) { - return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; - } - - *decoder = new VdecDecoder(*decoderCfgInfo, *decoderMemInfo); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceVideodec2DeleteDecoder(OrbisVideodec2Decoder decoder) { - LOG_INFO(Lib_Vdec2, "called"); - - if (!decoder) { - return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE; - } - - delete decoder; - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceVideodec2Decode(OrbisVideodec2Decoder decoder, - const OrbisVideodec2InputData* inputData, - OrbisVideodec2FrameBuffer* frameBuffer, - OrbisVideodec2OutputInfo* outputInfo) { - LOG_TRACE(Lib_Vdec2, "called"); - - if (!decoder) { - return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE; - } - if (!inputData || !frameBuffer || !outputInfo) { - return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; - } - if (inputData->thisSize != sizeof(OrbisVideodec2InputData) || - frameBuffer->thisSize != sizeof(OrbisVideodec2FrameBuffer)) { - return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; - } - - return decoder->Decode(*inputData, *frameBuffer, *outputInfo); -} - -s32 PS4_SYSV_ABI sceVideodec2Flush(OrbisVideodec2Decoder decoder, - OrbisVideodec2FrameBuffer* frameBuffer, - OrbisVideodec2OutputInfo* outputInfo) { - LOG_INFO(Lib_Vdec2, "called"); - - if (!decoder) { - return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE; - } - if (!frameBuffer || !outputInfo) { - return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; - } - if (frameBuffer->thisSize != sizeof(OrbisVideodec2FrameBuffer) || - outputInfo->thisSize != sizeof(OrbisVideodec2OutputInfo)) { - return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; - } - - return decoder->Flush(*frameBuffer, *outputInfo); -} - -s32 PS4_SYSV_ABI sceVideodec2Reset(OrbisVideodec2Decoder decoder) { - LOG_INFO(Lib_Vdec2, "called"); - - if (!decoder) { - return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE; - } - - return decoder->Reset(); -} - -s32 PS4_SYSV_ABI sceVideodec2GetPictureInfo(const OrbisVideodec2OutputInfo* outputInfo, - void* p1stPictureInfoOut, void* p2ndPictureInfoOut) { - LOG_TRACE(Lib_Vdec2, "called"); - - if (!outputInfo) { - return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; - } - if (outputInfo->thisSize != sizeof(OrbisVideodec2OutputInfo)) { - return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; - } - if (outputInfo->pictureCount == 0 || gPictureInfos.empty()) { - return ORBIS_OK; - } - - if (p1stPictureInfoOut) { - OrbisVideodec2AvcPictureInfo* picInfo = - static_cast(p1stPictureInfoOut); - if (picInfo->thisSize != sizeof(OrbisVideodec2AvcPictureInfo)) { - return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; - } - *picInfo = gPictureInfos.back(); - } - - if (outputInfo->pictureCount > 1) { - UNREACHABLE(); - } - - return ORBIS_OK; -} - -void RegisterlibSceVdec2(Core::Loader::SymbolsResolver* sym) { - LIB_FUNCTION("RnDibcGCPKw", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, - sceVideodec2QueryComputeMemoryInfo); - LIB_FUNCTION("eD+X2SmxUt4", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, - sceVideodec2AllocateComputeQueue); - LIB_FUNCTION("UvtA3FAiF4Y", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, - sceVideodec2ReleaseComputeQueue); - - LIB_FUNCTION("qqMCwlULR+E", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, - sceVideodec2QueryDecoderMemoryInfo); - LIB_FUNCTION("CNNRoRYd8XI", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, - sceVideodec2CreateDecoder); - LIB_FUNCTION("jwImxXRGSKA", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, - sceVideodec2DeleteDecoder); - LIB_FUNCTION("852F5+q6+iM", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, sceVideodec2Decode); - LIB_FUNCTION("l1hXwscLuCY", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, sceVideodec2Flush); - LIB_FUNCTION("wJXikG6QFN8", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, sceVideodec2Reset); - LIB_FUNCTION("NtXRa3dRzU0", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, - sceVideodec2GetPictureInfo); -} - -} // namespace Libraries::Vdec2 +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/libraries/libs.h" +#include "core/libraries/videodec/videodec2.h" +#include "core/libraries/videodec/videodec2_impl.h" +#include "core/libraries/videodec/videodec_error.h" + +namespace Libraries::Vdec2 { + +static constexpr u64 kMinimumMemorySize = 32_MB; ///> Fake minimum memory size for querying + +s32 PS4_SYSV_ABI +sceVideodec2QueryComputeMemoryInfo(OrbisVideodec2ComputeMemoryInfo* computeMemInfo) { + LOG_INFO(Lib_Vdec2, "called"); + + if (!computeMemInfo) { + return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; + } + if (computeMemInfo->thisSize != sizeof(OrbisVideodec2ComputeMemoryInfo)) { + return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; + } + + computeMemInfo->cpuGpuMemory = nullptr; + computeMemInfo->cpuGpuMemorySize = kMinimumMemorySize; + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI +sceVideodec2AllocateComputeQueue(const OrbisVideodec2ComputeConfigInfo* computeCfgInfo, + const OrbisVideodec2ComputeMemoryInfo* computeMemInfo, + OrbisVideodec2ComputeQueue* computeQueue) { + LOG_INFO(Lib_Vdec2, "called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVideodec2ReleaseComputeQueue(OrbisVideodec2ComputeQueue computeQueue) { + LOG_INFO(Lib_Vdec2, "called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI +sceVideodec2QueryDecoderMemoryInfo(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo, + OrbisVideodec2DecoderMemoryInfo* decoderMemInfo) { + LOG_INFO(Lib_Vdec2, "called"); + + if (!decoderCfgInfo || !decoderMemInfo) { + return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; + } + if (decoderCfgInfo->thisSize != sizeof(OrbisVideodec2DecoderConfigInfo) || + decoderMemInfo->thisSize != sizeof(OrbisVideodec2DecoderMemoryInfo)) { + return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; + } + + decoderMemInfo->cpuMemory = nullptr; + decoderMemInfo->gpuMemory = nullptr; + decoderMemInfo->cpuGpuMemory = nullptr; + + decoderMemInfo->cpuGpuMemorySize = kMinimumMemorySize; + decoderMemInfo->cpuMemorySize = kMinimumMemorySize; + decoderMemInfo->gpuMemorySize = kMinimumMemorySize; + + decoderMemInfo->maxFrameBufferSize = kMinimumMemorySize; + decoderMemInfo->frameBufferAlignment = 0x100; + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVideodec2CreateDecoder(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo, + const OrbisVideodec2DecoderMemoryInfo* decoderMemInfo, + OrbisVideodec2Decoder* decoder) { + LOG_INFO(Lib_Vdec2, "called"); + + if (!decoderCfgInfo || !decoderMemInfo || !decoder) { + return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; + } + if (decoderCfgInfo->thisSize != sizeof(OrbisVideodec2DecoderConfigInfo) || + decoderMemInfo->thisSize != sizeof(OrbisVideodec2DecoderMemoryInfo)) { + return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; + } + + *decoder = new VdecDecoder(*decoderCfgInfo, *decoderMemInfo); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVideodec2DeleteDecoder(OrbisVideodec2Decoder decoder) { + LOG_INFO(Lib_Vdec2, "called"); + + if (!decoder) { + return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE; + } + + delete decoder; + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVideodec2Decode(OrbisVideodec2Decoder decoder, + const OrbisVideodec2InputData* inputData, + OrbisVideodec2FrameBuffer* frameBuffer, + OrbisVideodec2OutputInfo* outputInfo) { + LOG_TRACE(Lib_Vdec2, "called"); + + if (!decoder) { + return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE; + } + if (!inputData || !frameBuffer || !outputInfo) { + return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; + } + if (inputData->thisSize != sizeof(OrbisVideodec2InputData) || + frameBuffer->thisSize != sizeof(OrbisVideodec2FrameBuffer)) { + return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; + } + + return decoder->Decode(*inputData, *frameBuffer, *outputInfo); +} + +s32 PS4_SYSV_ABI sceVideodec2Flush(OrbisVideodec2Decoder decoder, + OrbisVideodec2FrameBuffer* frameBuffer, + OrbisVideodec2OutputInfo* outputInfo) { + LOG_INFO(Lib_Vdec2, "called"); + + if (!decoder) { + return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE; + } + if (!frameBuffer || !outputInfo) { + return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; + } + if (frameBuffer->thisSize != sizeof(OrbisVideodec2FrameBuffer) || + outputInfo->thisSize != sizeof(OrbisVideodec2OutputInfo)) { + return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; + } + + return decoder->Flush(*frameBuffer, *outputInfo); +} + +s32 PS4_SYSV_ABI sceVideodec2Reset(OrbisVideodec2Decoder decoder) { + LOG_INFO(Lib_Vdec2, "called"); + + if (!decoder) { + return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE; + } + + return decoder->Reset(); +} + +s32 PS4_SYSV_ABI sceVideodec2GetPictureInfo(const OrbisVideodec2OutputInfo* outputInfo, + void* p1stPictureInfoOut, void* p2ndPictureInfoOut) { + LOG_TRACE(Lib_Vdec2, "called"); + + if (!outputInfo) { + return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; + } + if (outputInfo->thisSize != sizeof(OrbisVideodec2OutputInfo)) { + return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; + } + if (outputInfo->pictureCount == 0 || gPictureInfos.empty()) { + return ORBIS_OK; + } + + if (p1stPictureInfoOut) { + OrbisVideodec2AvcPictureInfo* picInfo = + static_cast(p1stPictureInfoOut); + if (picInfo->thisSize != sizeof(OrbisVideodec2AvcPictureInfo)) { + return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; + } + *picInfo = gPictureInfos.back(); + } + + if (outputInfo->pictureCount > 1) { + UNREACHABLE(); + } + + return ORBIS_OK; +} + +void RegisterlibSceVdec2(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("RnDibcGCPKw", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, + sceVideodec2QueryComputeMemoryInfo); + LIB_FUNCTION("eD+X2SmxUt4", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, + sceVideodec2AllocateComputeQueue); + LIB_FUNCTION("UvtA3FAiF4Y", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, + sceVideodec2ReleaseComputeQueue); + + LIB_FUNCTION("qqMCwlULR+E", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, + sceVideodec2QueryDecoderMemoryInfo); + LIB_FUNCTION("CNNRoRYd8XI", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, + sceVideodec2CreateDecoder); + LIB_FUNCTION("jwImxXRGSKA", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, + sceVideodec2DeleteDecoder); + LIB_FUNCTION("852F5+q6+iM", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, sceVideodec2Decode); + LIB_FUNCTION("l1hXwscLuCY", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, sceVideodec2Flush); + LIB_FUNCTION("wJXikG6QFN8", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, sceVideodec2Reset); + LIB_FUNCTION("NtXRa3dRzU0", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, + sceVideodec2GetPictureInfo); +} + +} // namespace Libraries::Vdec2 diff --git a/src/core/libraries/videodec/videodec2.h b/src/core/libraries/videodec/videodec2.h index 4617e1c20..abc8f8ab5 100644 --- a/src/core/libraries/videodec/videodec2.h +++ b/src/core/libraries/videodec/videodec2.h @@ -1,139 +1,139 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/types.h" - -#include "videodec2_avc.h" - -namespace Core::Loader { -class SymbolsResolver; -} -namespace Libraries::Vdec2 { - -class VdecDecoder; - -using OrbisVideodec2Decoder = VdecDecoder*; -using OrbisVideodec2ComputeQueue = void*; - -struct OrbisVideodec2DecoderConfigInfo { - u64 thisSize; - u32 resourceType; - u32 codecType; - u32 profile; - u32 maxLevel; - s32 maxFrameWidth; - s32 maxFrameHeight; - s32 maxDpbFrameCount; - u32 decodePipelineDepth; - OrbisVideodec2ComputeQueue computeQueue; - u64 cpuAffinityMask; - s32 cpuThreadPriority; - bool optimizeProgressiveVideo; - bool checkMemoryType; - u8 reserved0; - u8 reserved1; - void* extraConfigInfo; -}; -static_assert(sizeof(OrbisVideodec2DecoderConfigInfo) == 0x48); - -struct OrbisVideodec2DecoderMemoryInfo { - u64 thisSize; - u64 cpuMemorySize; - void* cpuMemory; - u64 gpuMemorySize; - void* gpuMemory; - u64 cpuGpuMemorySize; - void* cpuGpuMemory; - u64 maxFrameBufferSize; - u32 frameBufferAlignment; - u32 reserved0; -}; -static_assert(sizeof(OrbisVideodec2DecoderMemoryInfo) == 0x48); - -struct OrbisVideodec2InputData { - u64 thisSize; - void* auData; - u64 auSize; - u64 ptsData; - u64 dtsData; - u64 attachedData; -}; -static_assert(sizeof(OrbisVideodec2InputData) == 0x30); - -struct OrbisVideodec2OutputInfo { - u64 thisSize; - bool isValid; - bool isErrorFrame; - u8 pictureCount; - u32 codecType; - u32 frameWidth; - u32 framePitch; - u32 frameHeight; - void* frameBuffer; - u64 frameBufferSize; -}; -static_assert(sizeof(OrbisVideodec2OutputInfo) == 0x30); - -struct OrbisVideodec2FrameBuffer { - u64 thisSize; - void* frameBuffer; - u64 frameBufferSize; - bool isAccepted; -}; -static_assert(sizeof(OrbisVideodec2FrameBuffer) == 0x20); - -struct OrbisVideodec2ComputeMemoryInfo { - u64 thisSize; - u64 cpuGpuMemorySize; - void* cpuGpuMemory; -}; -static_assert(sizeof(OrbisVideodec2ComputeMemoryInfo) == 0x18); - -struct OrbisVideodec2ComputeConfigInfo { - u64 thisSize; - u16 computePipeId; - u16 computeQueueId; - bool checkMemoryType; - u8 reserved0; - u16 reserved1; -}; -static_assert(sizeof(OrbisVideodec2ComputeConfigInfo) == 0x10); - -s32 PS4_SYSV_ABI -sceVideodec2QueryComputeMemoryInfo(OrbisVideodec2ComputeMemoryInfo* computeMemInfo); - -s32 PS4_SYSV_ABI -sceVideodec2AllocateComputeQueue(const OrbisVideodec2ComputeConfigInfo* computeCfgInfo, - const OrbisVideodec2ComputeMemoryInfo* computeMemInfo, - OrbisVideodec2ComputeQueue* computeQueue); - -s32 PS4_SYSV_ABI sceVideodec2ReleaseComputeQueue(OrbisVideodec2ComputeQueue computeQueue); - -s32 PS4_SYSV_ABI -sceVideodec2QueryDecoderMemoryInfo(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo, - OrbisVideodec2DecoderMemoryInfo* decoderMemInfo); - -s32 PS4_SYSV_ABI sceVideodec2CreateDecoder(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo, - const OrbisVideodec2DecoderMemoryInfo* decoderMemInfo, - OrbisVideodec2Decoder* decoder); - -s32 PS4_SYSV_ABI sceVideodec2DeleteDecoder(OrbisVideodec2Decoder decoder); - -s32 PS4_SYSV_ABI sceVideodec2Decode(OrbisVideodec2Decoder decoder, - const OrbisVideodec2InputData* inputData, - OrbisVideodec2FrameBuffer* frameBuffer, - OrbisVideodec2OutputInfo* outputInfo); - -s32 PS4_SYSV_ABI sceVideodec2Flush(OrbisVideodec2Decoder decoder, - OrbisVideodec2FrameBuffer* frameBuffer, - OrbisVideodec2OutputInfo* outputInfo); - -s32 PS4_SYSV_ABI sceVideodec2Reset(OrbisVideodec2Decoder decoder); - -s32 PS4_SYSV_ABI sceVideodec2GetPictureInfo(const OrbisVideodec2OutputInfo* outputInfo, - void* p1stPictureInfo, void* p2ndPictureInfo); - -void RegisterlibSceVdec2(Core::Loader::SymbolsResolver* sym); +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +#include "videodec2_avc.h" + +namespace Core::Loader { +class SymbolsResolver; +} +namespace Libraries::Vdec2 { + +class VdecDecoder; + +using OrbisVideodec2Decoder = VdecDecoder*; +using OrbisVideodec2ComputeQueue = void*; + +struct OrbisVideodec2DecoderConfigInfo { + u64 thisSize; + u32 resourceType; + u32 codecType; + u32 profile; + u32 maxLevel; + s32 maxFrameWidth; + s32 maxFrameHeight; + s32 maxDpbFrameCount; + u32 decodePipelineDepth; + OrbisVideodec2ComputeQueue computeQueue; + u64 cpuAffinityMask; + s32 cpuThreadPriority; + bool optimizeProgressiveVideo; + bool checkMemoryType; + u8 reserved0; + u8 reserved1; + void* extraConfigInfo; +}; +static_assert(sizeof(OrbisVideodec2DecoderConfigInfo) == 0x48); + +struct OrbisVideodec2DecoderMemoryInfo { + u64 thisSize; + u64 cpuMemorySize; + void* cpuMemory; + u64 gpuMemorySize; + void* gpuMemory; + u64 cpuGpuMemorySize; + void* cpuGpuMemory; + u64 maxFrameBufferSize; + u32 frameBufferAlignment; + u32 reserved0; +}; +static_assert(sizeof(OrbisVideodec2DecoderMemoryInfo) == 0x48); + +struct OrbisVideodec2InputData { + u64 thisSize; + void* auData; + u64 auSize; + u64 ptsData; + u64 dtsData; + u64 attachedData; +}; +static_assert(sizeof(OrbisVideodec2InputData) == 0x30); + +struct OrbisVideodec2OutputInfo { + u64 thisSize; + bool isValid; + bool isErrorFrame; + u8 pictureCount; + u32 codecType; + u32 frameWidth; + u32 framePitch; + u32 frameHeight; + void* frameBuffer; + u64 frameBufferSize; +}; +static_assert(sizeof(OrbisVideodec2OutputInfo) == 0x30); + +struct OrbisVideodec2FrameBuffer { + u64 thisSize; + void* frameBuffer; + u64 frameBufferSize; + bool isAccepted; +}; +static_assert(sizeof(OrbisVideodec2FrameBuffer) == 0x20); + +struct OrbisVideodec2ComputeMemoryInfo { + u64 thisSize; + u64 cpuGpuMemorySize; + void* cpuGpuMemory; +}; +static_assert(sizeof(OrbisVideodec2ComputeMemoryInfo) == 0x18); + +struct OrbisVideodec2ComputeConfigInfo { + u64 thisSize; + u16 computePipeId; + u16 computeQueueId; + bool checkMemoryType; + u8 reserved0; + u16 reserved1; +}; +static_assert(sizeof(OrbisVideodec2ComputeConfigInfo) == 0x10); + +s32 PS4_SYSV_ABI +sceVideodec2QueryComputeMemoryInfo(OrbisVideodec2ComputeMemoryInfo* computeMemInfo); + +s32 PS4_SYSV_ABI +sceVideodec2AllocateComputeQueue(const OrbisVideodec2ComputeConfigInfo* computeCfgInfo, + const OrbisVideodec2ComputeMemoryInfo* computeMemInfo, + OrbisVideodec2ComputeQueue* computeQueue); + +s32 PS4_SYSV_ABI sceVideodec2ReleaseComputeQueue(OrbisVideodec2ComputeQueue computeQueue); + +s32 PS4_SYSV_ABI +sceVideodec2QueryDecoderMemoryInfo(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo, + OrbisVideodec2DecoderMemoryInfo* decoderMemInfo); + +s32 PS4_SYSV_ABI sceVideodec2CreateDecoder(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo, + const OrbisVideodec2DecoderMemoryInfo* decoderMemInfo, + OrbisVideodec2Decoder* decoder); + +s32 PS4_SYSV_ABI sceVideodec2DeleteDecoder(OrbisVideodec2Decoder decoder); + +s32 PS4_SYSV_ABI sceVideodec2Decode(OrbisVideodec2Decoder decoder, + const OrbisVideodec2InputData* inputData, + OrbisVideodec2FrameBuffer* frameBuffer, + OrbisVideodec2OutputInfo* outputInfo); + +s32 PS4_SYSV_ABI sceVideodec2Flush(OrbisVideodec2Decoder decoder, + OrbisVideodec2FrameBuffer* frameBuffer, + OrbisVideodec2OutputInfo* outputInfo); + +s32 PS4_SYSV_ABI sceVideodec2Reset(OrbisVideodec2Decoder decoder); + +s32 PS4_SYSV_ABI sceVideodec2GetPictureInfo(const OrbisVideodec2OutputInfo* outputInfo, + void* p1stPictureInfo, void* p2ndPictureInfo); + +void RegisterlibSceVdec2(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::Vdec2 \ No newline at end of file diff --git a/src/core/libraries/videodec/videodec2_avc.h b/src/core/libraries/videodec/videodec2_avc.h index 4109b5fd2..22293ee93 100644 --- a/src/core/libraries/videodec/videodec2_avc.h +++ b/src/core/libraries/videodec/videodec2_avc.h @@ -1,60 +1,60 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/types.h" - -namespace Libraries::Vdec2 { - -struct OrbisVideodec2AvcPictureInfo { - u64 thisSize; - - bool isValid; - - u64 ptsData; - u64 dtsData; - u64 attachedData; - - u8 idrPictureflag; - - u8 profile_idc; - u8 level_idc; - u32 pic_width_in_mbs_minus1; - u32 pic_height_in_map_units_minus1; - u8 frame_mbs_only_flag; - - u8 frame_cropping_flag; - u32 frameCropLeftOffset; - u32 frameCropRightOffset; - u32 frameCropTopOffset; - u32 frameCropBottomOffset; - - u8 aspect_ratio_info_present_flag; - u8 aspect_ratio_idc; - u16 sar_width; - u16 sar_height; - - u8 video_signal_type_present_flag; - u8 video_format; - u8 video_full_range_flag; - u8 colour_description_present_flag; - u8 colour_primaries; - u8 transfer_characteristics; - u8 matrix_coefficients; - - u8 timing_info_present_flag; - u32 num_units_in_tick; - u32 time_scale; - u8 fixed_frame_rate_flag; - - u8 bitstream_restriction_flag; - u8 max_dec_frame_buffering; - - u8 pic_struct_present_flag; - u8 pic_struct; - u8 field_pic_flag; - u8 bottom_field_flag; -}; - +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Libraries::Vdec2 { + +struct OrbisVideodec2AvcPictureInfo { + u64 thisSize; + + bool isValid; + + u64 ptsData; + u64 dtsData; + u64 attachedData; + + u8 idrPictureflag; + + u8 profile_idc; + u8 level_idc; + u32 pic_width_in_mbs_minus1; + u32 pic_height_in_map_units_minus1; + u8 frame_mbs_only_flag; + + u8 frame_cropping_flag; + u32 frameCropLeftOffset; + u32 frameCropRightOffset; + u32 frameCropTopOffset; + u32 frameCropBottomOffset; + + u8 aspect_ratio_info_present_flag; + u8 aspect_ratio_idc; + u16 sar_width; + u16 sar_height; + + u8 video_signal_type_present_flag; + u8 video_format; + u8 video_full_range_flag; + u8 colour_description_present_flag; + u8 colour_primaries; + u8 transfer_characteristics; + u8 matrix_coefficients; + + u8 timing_info_present_flag; + u32 num_units_in_tick; + u32 time_scale; + u8 fixed_frame_rate_flag; + + u8 bitstream_restriction_flag; + u8 max_dec_frame_buffering; + + u8 pic_struct_present_flag; + u8 pic_struct; + u8 field_pic_flag; + u8 bottom_field_flag; +}; + } // namespace Libraries::Vdec2 \ No newline at end of file diff --git a/src/core/libraries/videodec/videodec2_impl.cpp b/src/core/libraries/videodec/videodec2_impl.cpp index 138d78af3..22b17c86c 100644 --- a/src/core/libraries/videodec/videodec2_impl.cpp +++ b/src/core/libraries/videodec/videodec2_impl.cpp @@ -1,229 +1,229 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "videodec2_impl.h" - -#include "common/assert.h" -#include "common/logging/log.h" -#include "core/libraries/videodec/videodec_error.h" - -#include "common/support/avdec.h" - -namespace Libraries::Vdec2 { - -std::vector gPictureInfos; - -static inline void CopyNV12Data(u8* dst, const AVFrame& src) { - std::memcpy(dst, src.data[0], src.width * src.height); - std::memcpy(dst + (src.width * src.height), src.data[1], (src.width * src.height) / 2); -} - -VdecDecoder::VdecDecoder(const OrbisVideodec2DecoderConfigInfo& configInfo, - const OrbisVideodec2DecoderMemoryInfo& memoryInfo) { - ASSERT(configInfo.codecType == 1); /* AVC */ - - const AVCodec* codec = avcodec_find_decoder(AV_CODEC_ID_H264); - ASSERT(codec); - - mCodecContext = avcodec_alloc_context3(codec); - ASSERT(mCodecContext); - mCodecContext->width = configInfo.maxFrameWidth; - mCodecContext->height = configInfo.maxFrameHeight; - - avcodec_open2(mCodecContext, codec, nullptr); -} - -VdecDecoder::~VdecDecoder() { - avcodec_free_context(&mCodecContext); - sws_freeContext(mSwsContext); - - gPictureInfos.clear(); -} - -s32 VdecDecoder::Decode(const OrbisVideodec2InputData& inputData, - OrbisVideodec2FrameBuffer& frameBuffer, - OrbisVideodec2OutputInfo& outputInfo) { - frameBuffer.isAccepted = false; - outputInfo.thisSize = sizeof(OrbisVideodec2OutputInfo); - outputInfo.isValid = false; - outputInfo.isErrorFrame = true; - outputInfo.pictureCount = 0; - - if (!inputData.auData) { - return ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_POINTER; - } - if (inputData.auSize == 0) { - return ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_SIZE; - } - - AVPacket* packet = av_packet_alloc(); - if (!packet) { - LOG_ERROR(Lib_Vdec2, "Failed to allocate packet"); - return ORBIS_VIDEODEC2_ERROR_API_FAIL; - } - - packet->data = (u8*)inputData.auData; - packet->size = inputData.auSize; - packet->pts = inputData.ptsData; - packet->dts = inputData.dtsData; - - int ret = avcodec_send_packet(mCodecContext, packet); - if (ret < 0) { - LOG_ERROR(Lib_Vdec2, "Error sending packet to decoder: {}", ret); - av_packet_free(&packet); - return ORBIS_VIDEODEC2_ERROR_API_FAIL; - } - - AVFrame* frame = av_frame_alloc(); - if (frame == nullptr) { - LOG_ERROR(Lib_Vdec2, "Failed to allocate frame"); - av_packet_free(&packet); - return ORBIS_VIDEODEC2_ERROR_API_FAIL; - } - - while (true) { - ret = avcodec_receive_frame(mCodecContext, frame); - if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { - break; - } else if (ret < 0) { - LOG_ERROR(Lib_Vdec2, "Error receiving frame from decoder: {}", ret); - av_packet_free(&packet); - av_frame_free(&frame); - return ORBIS_VIDEODEC2_ERROR_API_FAIL; - } - - if (frame->format != AV_PIX_FMT_NV12) { - AVFrame* nv12_frame = ConvertNV12Frame(*frame); - ASSERT(nv12_frame); - av_frame_free(&frame); - frame = nv12_frame; - } - - CopyNV12Data((u8*)frameBuffer.frameBuffer, *frame); - frameBuffer.isAccepted = true; - - outputInfo.codecType = 1; // FIXME: Hardcoded to AVC - outputInfo.frameWidth = frame->width; - outputInfo.frameHeight = frame->height; - outputInfo.framePitch = frame->linesize[0]; - outputInfo.frameBufferSize = frameBuffer.frameBufferSize; - outputInfo.frameBuffer = frameBuffer.frameBuffer; - - outputInfo.isValid = true; - outputInfo.isErrorFrame = false; - outputInfo.pictureCount = 1; // TODO: 2 pictures for interlaced video - - if (outputInfo.isValid) { - OrbisVideodec2AvcPictureInfo pictureInfo = {}; - - pictureInfo.thisSize = sizeof(OrbisVideodec2AvcPictureInfo); - pictureInfo.isValid = true; - - pictureInfo.ptsData = inputData.ptsData; - pictureInfo.dtsData = inputData.dtsData; - pictureInfo.attachedData = inputData.attachedData; - - pictureInfo.frameCropLeftOffset = frame->crop_left; - pictureInfo.frameCropRightOffset = frame->crop_right; - pictureInfo.frameCropTopOffset = frame->crop_top; - pictureInfo.frameCropBottomOffset = frame->crop_bottom; - - gPictureInfos.push_back(pictureInfo); - } - } - - av_packet_free(&packet); - av_frame_free(&frame); - return ORBIS_OK; -} - -s32 VdecDecoder::Flush(OrbisVideodec2FrameBuffer& frameBuffer, - OrbisVideodec2OutputInfo& outputInfo) { - frameBuffer.isAccepted = false; - outputInfo.thisSize = sizeof(OrbisVideodec2OutputInfo); - outputInfo.isValid = false; - outputInfo.isErrorFrame = true; - outputInfo.pictureCount = 0; - - AVFrame* frame = av_frame_alloc(); - if (!frame) { - LOG_ERROR(Lib_Vdec2, "Failed to allocate frame"); - return ORBIS_VIDEODEC2_ERROR_API_FAIL; - } - - while (true) { - int ret = avcodec_receive_frame(mCodecContext, frame); - if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { - break; - } else if (ret < 0) { - LOG_ERROR(Lib_Vdec2, "Error receiving frame from decoder: {}", ret); - av_frame_free(&frame); - return ORBIS_VIDEODEC2_ERROR_API_FAIL; - } - - if (frame->format != AV_PIX_FMT_NV12) { - AVFrame* nv12_frame = ConvertNV12Frame(*frame); - ASSERT(nv12_frame); - av_frame_free(&frame); - frame = nv12_frame; - } - - CopyNV12Data((u8*)frameBuffer.frameBuffer, *frame); - frameBuffer.isAccepted = true; - - outputInfo.codecType = 1; // FIXME: Hardcoded to AVC - outputInfo.frameWidth = frame->width; - outputInfo.frameHeight = frame->height; - outputInfo.framePitch = frame->linesize[0]; - outputInfo.frameBufferSize = frameBuffer.frameBufferSize; - outputInfo.frameBuffer = frameBuffer.frameBuffer; - - outputInfo.isValid = true; - outputInfo.isErrorFrame = false; - outputInfo.pictureCount = 1; // TODO: 2 pictures for interlaced video - - // FIXME: Should we add picture info here too? - } - - av_frame_free(&frame); - return ORBIS_OK; -} - -s32 VdecDecoder::Reset() { - avcodec_flush_buffers(mCodecContext); - gPictureInfos.clear(); - return ORBIS_OK; -} - -AVFrame* VdecDecoder::ConvertNV12Frame(AVFrame& frame) { - AVFrame* nv12_frame = av_frame_alloc(); - nv12_frame->pts = frame.pts; - nv12_frame->pkt_dts = frame.pkt_dts < 0 ? 0 : frame.pkt_dts; - nv12_frame->format = AV_PIX_FMT_NV12; - nv12_frame->width = frame.width; - nv12_frame->height = frame.height; - nv12_frame->sample_aspect_ratio = frame.sample_aspect_ratio; - nv12_frame->crop_top = frame.crop_top; - nv12_frame->crop_bottom = frame.crop_bottom; - nv12_frame->crop_left = frame.crop_left; - nv12_frame->crop_right = frame.crop_right; - - av_frame_get_buffer(nv12_frame, 0); - - if (mSwsContext == nullptr) { - mSwsContext = sws_getContext(frame.width, frame.height, AVPixelFormat(frame.format), - nv12_frame->width, nv12_frame->height, AV_PIX_FMT_NV12, - SWS_FAST_BILINEAR, nullptr, nullptr, nullptr); - } - - const auto res = sws_scale(mSwsContext, frame.data, frame.linesize, 0, frame.height, - nv12_frame->data, nv12_frame->linesize); - if (res < 0) { - LOG_ERROR(Lib_Vdec2, "Could not convert to NV12: {}", av_err2str(res)); - return nullptr; - } - - return nv12_frame; -} - -} // namespace Libraries::Vdec2 +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "videodec2_impl.h" + +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/libraries/videodec/videodec_error.h" + +#include "common/support/avdec.h" + +namespace Libraries::Vdec2 { + +std::vector gPictureInfos; + +static inline void CopyNV12Data(u8* dst, const AVFrame& src) { + std::memcpy(dst, src.data[0], src.width * src.height); + std::memcpy(dst + (src.width * src.height), src.data[1], (src.width * src.height) / 2); +} + +VdecDecoder::VdecDecoder(const OrbisVideodec2DecoderConfigInfo& configInfo, + const OrbisVideodec2DecoderMemoryInfo& memoryInfo) { + ASSERT(configInfo.codecType == 1); /* AVC */ + + const AVCodec* codec = avcodec_find_decoder(AV_CODEC_ID_H264); + ASSERT(codec); + + mCodecContext = avcodec_alloc_context3(codec); + ASSERT(mCodecContext); + mCodecContext->width = configInfo.maxFrameWidth; + mCodecContext->height = configInfo.maxFrameHeight; + + avcodec_open2(mCodecContext, codec, nullptr); +} + +VdecDecoder::~VdecDecoder() { + avcodec_free_context(&mCodecContext); + sws_freeContext(mSwsContext); + + gPictureInfos.clear(); +} + +s32 VdecDecoder::Decode(const OrbisVideodec2InputData& inputData, + OrbisVideodec2FrameBuffer& frameBuffer, + OrbisVideodec2OutputInfo& outputInfo) { + frameBuffer.isAccepted = false; + outputInfo.thisSize = sizeof(OrbisVideodec2OutputInfo); + outputInfo.isValid = false; + outputInfo.isErrorFrame = true; + outputInfo.pictureCount = 0; + + if (!inputData.auData) { + return ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_POINTER; + } + if (inputData.auSize == 0) { + return ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_SIZE; + } + + AVPacket* packet = av_packet_alloc(); + if (!packet) { + LOG_ERROR(Lib_Vdec2, "Failed to allocate packet"); + return ORBIS_VIDEODEC2_ERROR_API_FAIL; + } + + packet->data = (u8*)inputData.auData; + packet->size = inputData.auSize; + packet->pts = inputData.ptsData; + packet->dts = inputData.dtsData; + + int ret = avcodec_send_packet(mCodecContext, packet); + if (ret < 0) { + LOG_ERROR(Lib_Vdec2, "Error sending packet to decoder: {}", ret); + av_packet_free(&packet); + return ORBIS_VIDEODEC2_ERROR_API_FAIL; + } + + AVFrame* frame = av_frame_alloc(); + if (frame == nullptr) { + LOG_ERROR(Lib_Vdec2, "Failed to allocate frame"); + av_packet_free(&packet); + return ORBIS_VIDEODEC2_ERROR_API_FAIL; + } + + while (true) { + ret = avcodec_receive_frame(mCodecContext, frame); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + break; + } else if (ret < 0) { + LOG_ERROR(Lib_Vdec2, "Error receiving frame from decoder: {}", ret); + av_packet_free(&packet); + av_frame_free(&frame); + return ORBIS_VIDEODEC2_ERROR_API_FAIL; + } + + if (frame->format != AV_PIX_FMT_NV12) { + AVFrame* nv12_frame = ConvertNV12Frame(*frame); + ASSERT(nv12_frame); + av_frame_free(&frame); + frame = nv12_frame; + } + + CopyNV12Data((u8*)frameBuffer.frameBuffer, *frame); + frameBuffer.isAccepted = true; + + outputInfo.codecType = 1; // FIXME: Hardcoded to AVC + outputInfo.frameWidth = frame->width; + outputInfo.frameHeight = frame->height; + outputInfo.framePitch = frame->linesize[0]; + outputInfo.frameBufferSize = frameBuffer.frameBufferSize; + outputInfo.frameBuffer = frameBuffer.frameBuffer; + + outputInfo.isValid = true; + outputInfo.isErrorFrame = false; + outputInfo.pictureCount = 1; // TODO: 2 pictures for interlaced video + + if (outputInfo.isValid) { + OrbisVideodec2AvcPictureInfo pictureInfo = {}; + + pictureInfo.thisSize = sizeof(OrbisVideodec2AvcPictureInfo); + pictureInfo.isValid = true; + + pictureInfo.ptsData = inputData.ptsData; + pictureInfo.dtsData = inputData.dtsData; + pictureInfo.attachedData = inputData.attachedData; + + pictureInfo.frameCropLeftOffset = frame->crop_left; + pictureInfo.frameCropRightOffset = frame->crop_right; + pictureInfo.frameCropTopOffset = frame->crop_top; + pictureInfo.frameCropBottomOffset = frame->crop_bottom; + + gPictureInfos.push_back(pictureInfo); + } + } + + av_packet_free(&packet); + av_frame_free(&frame); + return ORBIS_OK; +} + +s32 VdecDecoder::Flush(OrbisVideodec2FrameBuffer& frameBuffer, + OrbisVideodec2OutputInfo& outputInfo) { + frameBuffer.isAccepted = false; + outputInfo.thisSize = sizeof(OrbisVideodec2OutputInfo); + outputInfo.isValid = false; + outputInfo.isErrorFrame = true; + outputInfo.pictureCount = 0; + + AVFrame* frame = av_frame_alloc(); + if (!frame) { + LOG_ERROR(Lib_Vdec2, "Failed to allocate frame"); + return ORBIS_VIDEODEC2_ERROR_API_FAIL; + } + + while (true) { + int ret = avcodec_receive_frame(mCodecContext, frame); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + break; + } else if (ret < 0) { + LOG_ERROR(Lib_Vdec2, "Error receiving frame from decoder: {}", ret); + av_frame_free(&frame); + return ORBIS_VIDEODEC2_ERROR_API_FAIL; + } + + if (frame->format != AV_PIX_FMT_NV12) { + AVFrame* nv12_frame = ConvertNV12Frame(*frame); + ASSERT(nv12_frame); + av_frame_free(&frame); + frame = nv12_frame; + } + + CopyNV12Data((u8*)frameBuffer.frameBuffer, *frame); + frameBuffer.isAccepted = true; + + outputInfo.codecType = 1; // FIXME: Hardcoded to AVC + outputInfo.frameWidth = frame->width; + outputInfo.frameHeight = frame->height; + outputInfo.framePitch = frame->linesize[0]; + outputInfo.frameBufferSize = frameBuffer.frameBufferSize; + outputInfo.frameBuffer = frameBuffer.frameBuffer; + + outputInfo.isValid = true; + outputInfo.isErrorFrame = false; + outputInfo.pictureCount = 1; // TODO: 2 pictures for interlaced video + + // FIXME: Should we add picture info here too? + } + + av_frame_free(&frame); + return ORBIS_OK; +} + +s32 VdecDecoder::Reset() { + avcodec_flush_buffers(mCodecContext); + gPictureInfos.clear(); + return ORBIS_OK; +} + +AVFrame* VdecDecoder::ConvertNV12Frame(AVFrame& frame) { + AVFrame* nv12_frame = av_frame_alloc(); + nv12_frame->pts = frame.pts; + nv12_frame->pkt_dts = frame.pkt_dts < 0 ? 0 : frame.pkt_dts; + nv12_frame->format = AV_PIX_FMT_NV12; + nv12_frame->width = frame.width; + nv12_frame->height = frame.height; + nv12_frame->sample_aspect_ratio = frame.sample_aspect_ratio; + nv12_frame->crop_top = frame.crop_top; + nv12_frame->crop_bottom = frame.crop_bottom; + nv12_frame->crop_left = frame.crop_left; + nv12_frame->crop_right = frame.crop_right; + + av_frame_get_buffer(nv12_frame, 0); + + if (mSwsContext == nullptr) { + mSwsContext = sws_getContext(frame.width, frame.height, AVPixelFormat(frame.format), + nv12_frame->width, nv12_frame->height, AV_PIX_FMT_NV12, + SWS_FAST_BILINEAR, nullptr, nullptr, nullptr); + } + + const auto res = sws_scale(mSwsContext, frame.data, frame.linesize, 0, frame.height, + nv12_frame->data, nv12_frame->linesize); + if (res < 0) { + LOG_ERROR(Lib_Vdec2, "Could not convert to NV12: {}", av_err2str(res)); + return nullptr; + } + + return nv12_frame; +} + +} // namespace Libraries::Vdec2 diff --git a/src/core/libraries/videodec/videodec2_impl.h b/src/core/libraries/videodec/videodec2_impl.h index 1bcece6e1..c8e8ea253 100644 --- a/src/core/libraries/videodec/videodec2_impl.h +++ b/src/core/libraries/videodec/videodec2_impl.h @@ -1,39 +1,39 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include - -#include "videodec2.h" - -extern "C" { -#include -#include -#include -} - -namespace Libraries::Vdec2 { - -extern std::vector gPictureInfos; - -class VdecDecoder { -public: - VdecDecoder(const OrbisVideodec2DecoderConfigInfo& configInfo, - const OrbisVideodec2DecoderMemoryInfo& memoryInfo); - ~VdecDecoder(); - - s32 Decode(const OrbisVideodec2InputData& inputData, OrbisVideodec2FrameBuffer& frameBuffer, - OrbisVideodec2OutputInfo& outputInfo); - s32 Flush(OrbisVideodec2FrameBuffer& frameBuffer, OrbisVideodec2OutputInfo& outputInfo); - s32 Reset(); - -private: - AVFrame* ConvertNV12Frame(AVFrame& frame); - -private: - AVCodecContext* mCodecContext = nullptr; - SwsContext* mSwsContext = nullptr; -}; - +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "videodec2.h" + +extern "C" { +#include +#include +#include +} + +namespace Libraries::Vdec2 { + +extern std::vector gPictureInfos; + +class VdecDecoder { +public: + VdecDecoder(const OrbisVideodec2DecoderConfigInfo& configInfo, + const OrbisVideodec2DecoderMemoryInfo& memoryInfo); + ~VdecDecoder(); + + s32 Decode(const OrbisVideodec2InputData& inputData, OrbisVideodec2FrameBuffer& frameBuffer, + OrbisVideodec2OutputInfo& outputInfo); + s32 Flush(OrbisVideodec2FrameBuffer& frameBuffer, OrbisVideodec2OutputInfo& outputInfo); + s32 Reset(); + +private: + AVFrame* ConvertNV12Frame(AVFrame& frame); + +private: + AVCodecContext* mCodecContext = nullptr; + SwsContext* mSwsContext = nullptr; +}; + } // namespace Libraries::Vdec2 \ No newline at end of file diff --git a/src/core/thread.cpp b/src/core/thread.cpp index 07681e6b9..8a0a4cfd1 100644 --- a/src/core/thread.cpp +++ b/src/core/thread.cpp @@ -1,151 +1,151 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/alignment.h" -#include "core/libraries/kernel/threads/pthread.h" -#include "thread.h" - -#ifdef _WIN64 -#include -#include "common/ntapi.h" -#else -#include -#include -#endif - -namespace Core { - -#ifdef _WIN64 -#define KGDT64_R3_DATA (0x28) -#define KGDT64_R3_CODE (0x30) -#define KGDT64_R3_CMTEB (0x50) -#define RPL_MASK (0x03) - -#define INITIAL_FPUCW (0x037f) -#define INITIAL_MXCSR_MASK (0xffbf) -#define EFLAGS_INTERRUPT_MASK (0x200) - -void InitializeTeb(INITIAL_TEB* teb, const ::Libraries::Kernel::PthreadAttr* attr) { - teb->StackBase = (void*)((u64)attr->stackaddr_attr + attr->stacksize_attr); - teb->StackLimit = nullptr; - teb->StackAllocationBase = attr->stackaddr_attr; -} - -void InitializeContext(CONTEXT* ctx, ThreadFunc func, void* arg, - const ::Libraries::Kernel::PthreadAttr* attr) { - /* Note: The stack has to be reversed */ - ctx->Rsp = (u64)attr->stackaddr_attr + attr->stacksize_attr; - ctx->Rbp = (u64)attr->stackaddr_attr + attr->stacksize_attr; - ctx->Rcx = (u64)arg; - ctx->Rip = (u64)func; - - ctx->SegGs = KGDT64_R3_DATA | RPL_MASK; - ctx->SegEs = KGDT64_R3_DATA | RPL_MASK; - ctx->SegDs = KGDT64_R3_DATA | RPL_MASK; - ctx->SegCs = KGDT64_R3_CODE | RPL_MASK; - ctx->SegSs = KGDT64_R3_DATA | RPL_MASK; - ctx->SegFs = KGDT64_R3_CMTEB | RPL_MASK; - - ctx->EFlags = 0x3000 | EFLAGS_INTERRUPT_MASK; - ctx->MxCsr = INITIAL_MXCSR; - - ctx->FltSave.ControlWord = INITIAL_FPUCW; - ctx->FltSave.MxCsr = INITIAL_MXCSR; - ctx->FltSave.MxCsr_Mask = INITIAL_MXCSR_MASK; - - ctx->ContextFlags = - CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT; -} -#endif - -NativeThread::NativeThread() : native_handle{0} {} - -NativeThread::~NativeThread() {} - -int NativeThread::Create(ThreadFunc func, void* arg, const ::Libraries::Kernel::PthreadAttr* attr) { -#ifndef _WIN64 - pthread_t* pthr = reinterpret_cast(&native_handle); - pthread_attr_t pattr; - pthread_attr_init(&pattr); - pthread_attr_setstack(&pattr, attr->stackaddr_attr, attr->stacksize_attr); - return pthread_create(pthr, &pattr, (PthreadFunc)func, arg); -#else - CLIENT_ID clientId{}; - INITIAL_TEB teb{}; - CONTEXT ctx{}; - - clientId.UniqueProcess = GetCurrentProcess(); - clientId.UniqueThread = GetCurrentThread(); - - InitializeTeb(&teb, attr); - InitializeContext(&ctx, func, arg, attr); - - return NtCreateThread(&native_handle, THREAD_ALL_ACCESS, nullptr, GetCurrentProcess(), - &clientId, &ctx, &teb, false); -#endif -} - -void NativeThread::Exit() { - if (!native_handle) { - return; - } - - tid = 0; - -#ifdef _WIN64 - NtClose(native_handle); - native_handle = nullptr; - - /* The Windows kernel will free the stack - given at thread creation via INITIAL_TEB - (StackAllocationBase) upon thread termination. - - In earlier Windows versions (NT4 to Windows Server 2003), - you could get around this via disabling FreeStackOnTermination - on the TEB. This has been removed since then. - - To avoid this, we must forcefully set the TEB - deallocation stack pointer to NULL so ZwFreeVirtualMemory fails - in the kernel and our stack is not freed. - */ - auto* teb = reinterpret_cast(NtCurrentTeb()); - teb->DeallocationStack = nullptr; - - NtTerminateThread(nullptr, 0); -#else - // Disable and free the signal stack. - constexpr stack_t sig_stack = { - .ss_flags = SS_DISABLE, - }; - sigaltstack(&sig_stack, nullptr); - - if (sig_stack_ptr) { - free(sig_stack_ptr); - sig_stack_ptr = nullptr; - } - - pthread_exit(nullptr); -#endif -} - -void NativeThread::Initialize() { -#if _WIN64 - tid = GetCurrentThreadId(); -#else - tid = (u64)pthread_self(); - - // Set up an alternate signal handler stack to avoid overflowing small thread stacks. - const size_t page_size = getpagesize(); - const size_t sig_stack_size = Common::AlignUp(std::max(64_KB, MINSIGSTKSZ), page_size); - ASSERT_MSG(posix_memalign(&sig_stack_ptr, page_size, sig_stack_size) == 0, - "Failed to allocate signal stack: {}", errno); - - stack_t sig_stack; - sig_stack.ss_sp = sig_stack_ptr; - sig_stack.ss_size = sig_stack_size; - sig_stack.ss_flags = 0; - ASSERT_MSG(sigaltstack(&sig_stack, nullptr) == 0, "Failed to set signal stack: {}", errno); -#endif -} - -} // namespace Core +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/alignment.h" +#include "core/libraries/kernel/threads/pthread.h" +#include "thread.h" + +#ifdef _WIN64 +#include +#include "common/ntapi.h" +#else +#include +#include +#endif + +namespace Core { + +#ifdef _WIN64 +#define KGDT64_R3_DATA (0x28) +#define KGDT64_R3_CODE (0x30) +#define KGDT64_R3_CMTEB (0x50) +#define RPL_MASK (0x03) + +#define INITIAL_FPUCW (0x037f) +#define INITIAL_MXCSR_MASK (0xffbf) +#define EFLAGS_INTERRUPT_MASK (0x200) + +void InitializeTeb(INITIAL_TEB* teb, const ::Libraries::Kernel::PthreadAttr* attr) { + teb->StackBase = (void*)((u64)attr->stackaddr_attr + attr->stacksize_attr); + teb->StackLimit = nullptr; + teb->StackAllocationBase = attr->stackaddr_attr; +} + +void InitializeContext(CONTEXT* ctx, ThreadFunc func, void* arg, + const ::Libraries::Kernel::PthreadAttr* attr) { + /* Note: The stack has to be reversed */ + ctx->Rsp = (u64)attr->stackaddr_attr + attr->stacksize_attr; + ctx->Rbp = (u64)attr->stackaddr_attr + attr->stacksize_attr; + ctx->Rcx = (u64)arg; + ctx->Rip = (u64)func; + + ctx->SegGs = KGDT64_R3_DATA | RPL_MASK; + ctx->SegEs = KGDT64_R3_DATA | RPL_MASK; + ctx->SegDs = KGDT64_R3_DATA | RPL_MASK; + ctx->SegCs = KGDT64_R3_CODE | RPL_MASK; + ctx->SegSs = KGDT64_R3_DATA | RPL_MASK; + ctx->SegFs = KGDT64_R3_CMTEB | RPL_MASK; + + ctx->EFlags = 0x3000 | EFLAGS_INTERRUPT_MASK; + ctx->MxCsr = INITIAL_MXCSR; + + ctx->FltSave.ControlWord = INITIAL_FPUCW; + ctx->FltSave.MxCsr = INITIAL_MXCSR; + ctx->FltSave.MxCsr_Mask = INITIAL_MXCSR_MASK; + + ctx->ContextFlags = + CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT; +} +#endif + +NativeThread::NativeThread() : native_handle{0} {} + +NativeThread::~NativeThread() {} + +int NativeThread::Create(ThreadFunc func, void* arg, const ::Libraries::Kernel::PthreadAttr* attr) { +#ifndef _WIN64 + pthread_t* pthr = reinterpret_cast(&native_handle); + pthread_attr_t pattr; + pthread_attr_init(&pattr); + pthread_attr_setstack(&pattr, attr->stackaddr_attr, attr->stacksize_attr); + return pthread_create(pthr, &pattr, (PthreadFunc)func, arg); +#else + CLIENT_ID clientId{}; + INITIAL_TEB teb{}; + CONTEXT ctx{}; + + clientId.UniqueProcess = GetCurrentProcess(); + clientId.UniqueThread = GetCurrentThread(); + + InitializeTeb(&teb, attr); + InitializeContext(&ctx, func, arg, attr); + + return NtCreateThread(&native_handle, THREAD_ALL_ACCESS, nullptr, GetCurrentProcess(), + &clientId, &ctx, &teb, false); +#endif +} + +void NativeThread::Exit() { + if (!native_handle) { + return; + } + + tid = 0; + +#ifdef _WIN64 + NtClose(native_handle); + native_handle = nullptr; + + /* The Windows kernel will free the stack + given at thread creation via INITIAL_TEB + (StackAllocationBase) upon thread termination. + + In earlier Windows versions (NT4 to Windows Server 2003), + you could get around this via disabling FreeStackOnTermination + on the TEB. This has been removed since then. + + To avoid this, we must forcefully set the TEB + deallocation stack pointer to NULL so ZwFreeVirtualMemory fails + in the kernel and our stack is not freed. + */ + auto* teb = reinterpret_cast(NtCurrentTeb()); + teb->DeallocationStack = nullptr; + + NtTerminateThread(nullptr, 0); +#else + // Disable and free the signal stack. + constexpr stack_t sig_stack = { + .ss_flags = SS_DISABLE, + }; + sigaltstack(&sig_stack, nullptr); + + if (sig_stack_ptr) { + free(sig_stack_ptr); + sig_stack_ptr = nullptr; + } + + pthread_exit(nullptr); +#endif +} + +void NativeThread::Initialize() { +#if _WIN64 + tid = GetCurrentThreadId(); +#else + tid = (u64)pthread_self(); + + // Set up an alternate signal handler stack to avoid overflowing small thread stacks. + const size_t page_size = getpagesize(); + const size_t sig_stack_size = Common::AlignUp(std::max(64_KB, MINSIGSTKSZ), page_size); + ASSERT_MSG(posix_memalign(&sig_stack_ptr, page_size, sig_stack_size) == 0, + "Failed to allocate signal stack: {}", errno); + + stack_t sig_stack; + sig_stack.ss_sp = sig_stack_ptr; + sig_stack.ss_size = sig_stack_size; + sig_stack.ss_flags = 0; + ASSERT_MSG(sigaltstack(&sig_stack, nullptr) == 0, "Failed to set signal stack: {}", errno); +#endif +} + +} // namespace Core diff --git a/src/core/thread.h b/src/core/thread.h index bd777a2e6..6ea7dfad4 100644 --- a/src/core/thread.h +++ b/src/core/thread.h @@ -1,45 +1,45 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/types.h" - -namespace Libraries::Kernel { -struct PthreadAttr; -} // namespace Libraries::Kernel - -namespace Core { - -using ThreadFunc = void (*)(void*); -using PthreadFunc = void* (*)(void*); - -class NativeThread { -public: - NativeThread(); - ~NativeThread(); - - int Create(ThreadFunc func, void* arg, const ::Libraries::Kernel::PthreadAttr* attr); - void Exit(); - - void Initialize(); - - uintptr_t GetHandle() { - return reinterpret_cast(native_handle); - } - - u64 GetTid() { - return tid; - } - -private: -#ifdef _WIN64 - void* native_handle; -#else - uintptr_t native_handle; - void* sig_stack_ptr; -#endif - u64 tid; -}; - +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Libraries::Kernel { +struct PthreadAttr; +} // namespace Libraries::Kernel + +namespace Core { + +using ThreadFunc = void (*)(void*); +using PthreadFunc = void* (*)(void*); + +class NativeThread { +public: + NativeThread(); + ~NativeThread(); + + int Create(ThreadFunc func, void* arg, const ::Libraries::Kernel::PthreadAttr* attr); + void Exit(); + + void Initialize(); + + uintptr_t GetHandle() { + return reinterpret_cast(native_handle); + } + + u64 GetTid() { + return tid; + } + +private: +#ifdef _WIN64 + void* native_handle; +#else + uintptr_t native_handle; + void* sig_stack_ptr; +#endif + u64 tid; +}; + } // namespace Core \ No newline at end of file diff --git a/src/shader_recompiler/ir/breadth_first_search.h b/src/shader_recompiler/ir/breadth_first_search.h index b3ed87204..390dffb5c 100644 --- a/src/shader_recompiler/ir/breadth_first_search.h +++ b/src/shader_recompiler/ir/breadth_first_search.h @@ -1,74 +1,74 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include -#include -#include "shader_recompiler/ir/value.h" - -namespace Shader::IR { - -// Use typename Instruction so the function can be used to return either const or mutable -// Insts depending on the context. -template -auto BreadthFirstSearch(Instruction* inst, - Pred&& pred) -> std::invoke_result_t { - // Most often case the instruction is the desired already. - if (std::optional result = pred(inst)) { - return result; - } - - // Breadth-first search visiting the right most arguments first - boost::container::small_vector visited; - std::queue queue; - queue.push(inst); - - while (!queue.empty()) { - // Pop one instruction from the queue - Instruction* inst{queue.front()}; - queue.pop(); - if (std::optional result = pred(inst)) { - // This is the instruction we were looking for - return result; - } - // Visit the right most arguments first - for (size_t arg = inst->NumArgs(); arg--;) { - Value arg_value{inst->Arg(arg)}; - if (arg_value.IsImmediate()) { - continue; - } - // Queue instruction if it hasn't been visited - Instruction* arg_inst{arg_value.InstRecursive()}; - if (std::ranges::find(visited, arg_inst) == visited.end()) { - visited.push_back(arg_inst); - queue.push(arg_inst); - } - } - } - // SSA tree has been traversed and the result hasn't been found - return std::nullopt; -} - -template -auto BreadthFirstSearch(const Value& value, - Pred&& pred) -> std::invoke_result_t { - if (value.IsImmediate()) { - // Nothing to do with immediates - return std::nullopt; - } - return BreadthFirstSearch(value.InstRecursive(), pred); -} - -template -auto BreadthFirstSearch(Value value, Pred&& pred) -> std::invoke_result_t { - if (value.IsImmediate()) { - // Nothing to do with immediates - return std::nullopt; - } - return BreadthFirstSearch(value.InstRecursive(), pred); -} - -} // namespace Shader::IR +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include "shader_recompiler/ir/value.h" + +namespace Shader::IR { + +// Use typename Instruction so the function can be used to return either const or mutable +// Insts depending on the context. +template +auto BreadthFirstSearch(Instruction* inst, + Pred&& pred) -> std::invoke_result_t { + // Most often case the instruction is the desired already. + if (std::optional result = pred(inst)) { + return result; + } + + // Breadth-first search visiting the right most arguments first + boost::container::small_vector visited; + std::queue queue; + queue.push(inst); + + while (!queue.empty()) { + // Pop one instruction from the queue + Instruction* inst{queue.front()}; + queue.pop(); + if (std::optional result = pred(inst)) { + // This is the instruction we were looking for + return result; + } + // Visit the right most arguments first + for (size_t arg = inst->NumArgs(); arg--;) { + Value arg_value{inst->Arg(arg)}; + if (arg_value.IsImmediate()) { + continue; + } + // Queue instruction if it hasn't been visited + Instruction* arg_inst{arg_value.InstRecursive()}; + if (std::ranges::find(visited, arg_inst) == visited.end()) { + visited.push_back(arg_inst); + queue.push(arg_inst); + } + } + } + // SSA tree has been traversed and the result hasn't been found + return std::nullopt; +} + +template +auto BreadthFirstSearch(const Value& value, + Pred&& pred) -> std::invoke_result_t { + if (value.IsImmediate()) { + // Nothing to do with immediates + return std::nullopt; + } + return BreadthFirstSearch(value.InstRecursive(), pred); +} + +template +auto BreadthFirstSearch(Value value, Pred&& pred) -> std::invoke_result_t { + if (value.IsImmediate()) { + // Nothing to do with immediates + return std::nullopt; + } + return BreadthFirstSearch(value.InstRecursive(), pred); +} + +} // namespace Shader::IR diff --git a/src/video_core/buffer_cache/buffer.h b/src/video_core/buffer_cache/buffer.h index f67278f64..feeafd9bd 100644 --- a/src/video_core/buffer_cache/buffer.h +++ b/src/video_core/buffer_cache/buffer.h @@ -1,203 +1,203 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include -#include -#include "common/types.h" -#include "video_core/amdgpu/resource.h" -#include "video_core/renderer_vulkan/vk_common.h" - -namespace Vulkan { -class Instance; -class Scheduler; -} // namespace Vulkan - -VK_DEFINE_HANDLE(VmaAllocation) -VK_DEFINE_HANDLE(VmaAllocator) - -struct VmaAllocationInfo; - -namespace VideoCore { - -/// Hints and requirements for the backing memory type of a commit -enum class MemoryUsage { - DeviceLocal, ///< Requests device local buffer. - Upload, ///< Requires a host visible memory type optimized for CPU to GPU uploads - Download, ///< Requires a host visible memory type optimized for GPU to CPU readbacks - Stream, ///< Requests device local host visible buffer, falling back host memory. -}; - -constexpr vk::BufferUsageFlags ReadFlags = - vk::BufferUsageFlagBits::eTransferSrc | vk::BufferUsageFlagBits::eUniformTexelBuffer | - vk::BufferUsageFlagBits::eUniformBuffer | vk::BufferUsageFlagBits::eIndexBuffer | - vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eIndirectBuffer; - -constexpr vk::BufferUsageFlags AllFlags = ReadFlags | vk::BufferUsageFlagBits::eTransferDst | - vk::BufferUsageFlagBits::eStorageTexelBuffer | - vk::BufferUsageFlagBits::eStorageBuffer; - -struct UniqueBuffer { - explicit UniqueBuffer(vk::Device device, VmaAllocator allocator); - ~UniqueBuffer(); - - UniqueBuffer(const UniqueBuffer&) = delete; - UniqueBuffer& operator=(const UniqueBuffer&) = delete; - - UniqueBuffer(UniqueBuffer&& other) - : allocator{std::exchange(other.allocator, VK_NULL_HANDLE)}, - allocation{std::exchange(other.allocation, VK_NULL_HANDLE)}, - buffer{std::exchange(other.buffer, VK_NULL_HANDLE)} {} - UniqueBuffer& operator=(UniqueBuffer&& other) { - buffer = std::exchange(other.buffer, VK_NULL_HANDLE); - allocator = std::exchange(other.allocator, VK_NULL_HANDLE); - allocation = std::exchange(other.allocation, VK_NULL_HANDLE); - return *this; - } - - void Create(const vk::BufferCreateInfo& image_ci, MemoryUsage usage, - VmaAllocationInfo* out_alloc_info); - - operator vk::Buffer() const { - return buffer; - } - - vk::Device device; - VmaAllocator allocator; - VmaAllocation allocation; - vk::Buffer buffer{}; -}; - -class Buffer { -public: - explicit Buffer(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler, - MemoryUsage usage, VAddr cpu_addr_, vk::BufferUsageFlags flags, - u64 size_bytes_); - - Buffer& operator=(const Buffer&) = delete; - Buffer(const Buffer&) = delete; - - Buffer& operator=(Buffer&&) = default; - Buffer(Buffer&&) = default; - - vk::BufferView View(u32 offset, u32 size, bool is_written, AmdGpu::DataFormat dfmt, - AmdGpu::NumberFormat nfmt); - - /// Increases the likeliness of this being a stream buffer - void IncreaseStreamScore(int score) noexcept { - stream_score += score; - } - - /// Returns the likeliness of this being a stream buffer - [[nodiscard]] int StreamScore() const noexcept { - return stream_score; - } - - /// Returns true when vaddr -> vaddr+size is fully contained in the buffer - [[nodiscard]] bool IsInBounds(VAddr addr, u64 size) const noexcept { - return addr >= cpu_addr && addr + size <= cpu_addr + SizeBytes(); - } - - /// Returns the base CPU address of the buffer - [[nodiscard]] VAddr CpuAddr() const noexcept { - return cpu_addr; - } - - /// Returns the offset relative to the given CPU address - [[nodiscard]] u32 Offset(VAddr other_cpu_addr) const noexcept { - return static_cast(other_cpu_addr - cpu_addr); - } - - size_t SizeBytes() const { - return size_bytes; - } - - vk::Buffer Handle() const noexcept { - return buffer; - } - - std::optional GetBarrier(vk::AccessFlagBits2 dst_acess_mask, - vk::PipelineStageFlagBits2 dst_stage) { - if (dst_acess_mask == access_mask && stage == dst_stage) { - return {}; - } - - auto barrier = vk::BufferMemoryBarrier2{ - .srcStageMask = stage, - .srcAccessMask = access_mask, - .dstStageMask = dst_stage, - .dstAccessMask = dst_acess_mask, - .buffer = buffer.buffer, - .size = size_bytes, - }; - access_mask = dst_acess_mask; - stage = dst_stage; - return barrier; - } - -public: - VAddr cpu_addr = 0; - bool is_picked{}; - bool is_coherent{}; - bool is_deleted{}; - int stream_score = 0; - size_t size_bytes = 0; - std::span mapped_data; - const Vulkan::Instance* instance; - Vulkan::Scheduler* scheduler; - MemoryUsage usage; - UniqueBuffer buffer; - vk::AccessFlagBits2 access_mask{vk::AccessFlagBits2::eNone}; - vk::PipelineStageFlagBits2 stage{vk::PipelineStageFlagBits2::eNone}; -}; - -class StreamBuffer : public Buffer { -public: - explicit StreamBuffer(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler, - MemoryUsage usage, u64 size_bytes_); - - /// Reserves a region of memory from the stream buffer. - std::pair Map(u64 size, u64 alignment = 0); - - /// Ensures that reserved bytes of memory are available to the GPU. - void Commit(); - - /// Maps and commits a memory region with user provided data - u64 Copy(VAddr src, size_t size, size_t alignment = 0) { - const auto [data, offset] = Map(size, alignment); - std::memcpy(data, reinterpret_cast(src), size); - Commit(); - return offset; - } - - u64 GetFreeSize() const { - return size_bytes - offset - mapped_size; - } - -private: - struct Watch { - u64 tick{}; - u64 upper_bound{}; - }; - - /// Increases the amount of watches available. - void ReserveWatches(std::vector& watches, std::size_t grow_size); - - /// Waits pending watches until requested upper bound. - void WaitPendingOperations(u64 requested_upper_bound); - -private: - u64 offset{}; - u64 mapped_size{}; - std::vector current_watches; - std::size_t current_watch_cursor{}; - std::optional invalidation_mark; - std::vector previous_watches; - std::size_t wait_cursor{}; - u64 wait_bound{}; -}; - -} // namespace VideoCore +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include "common/types.h" +#include "video_core/amdgpu/resource.h" +#include "video_core/renderer_vulkan/vk_common.h" + +namespace Vulkan { +class Instance; +class Scheduler; +} // namespace Vulkan + +VK_DEFINE_HANDLE(VmaAllocation) +VK_DEFINE_HANDLE(VmaAllocator) + +struct VmaAllocationInfo; + +namespace VideoCore { + +/// Hints and requirements for the backing memory type of a commit +enum class MemoryUsage { + DeviceLocal, ///< Requests device local buffer. + Upload, ///< Requires a host visible memory type optimized for CPU to GPU uploads + Download, ///< Requires a host visible memory type optimized for GPU to CPU readbacks + Stream, ///< Requests device local host visible buffer, falling back host memory. +}; + +constexpr vk::BufferUsageFlags ReadFlags = + vk::BufferUsageFlagBits::eTransferSrc | vk::BufferUsageFlagBits::eUniformTexelBuffer | + vk::BufferUsageFlagBits::eUniformBuffer | vk::BufferUsageFlagBits::eIndexBuffer | + vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eIndirectBuffer; + +constexpr vk::BufferUsageFlags AllFlags = ReadFlags | vk::BufferUsageFlagBits::eTransferDst | + vk::BufferUsageFlagBits::eStorageTexelBuffer | + vk::BufferUsageFlagBits::eStorageBuffer; + +struct UniqueBuffer { + explicit UniqueBuffer(vk::Device device, VmaAllocator allocator); + ~UniqueBuffer(); + + UniqueBuffer(const UniqueBuffer&) = delete; + UniqueBuffer& operator=(const UniqueBuffer&) = delete; + + UniqueBuffer(UniqueBuffer&& other) + : allocator{std::exchange(other.allocator, VK_NULL_HANDLE)}, + allocation{std::exchange(other.allocation, VK_NULL_HANDLE)}, + buffer{std::exchange(other.buffer, VK_NULL_HANDLE)} {} + UniqueBuffer& operator=(UniqueBuffer&& other) { + buffer = std::exchange(other.buffer, VK_NULL_HANDLE); + allocator = std::exchange(other.allocator, VK_NULL_HANDLE); + allocation = std::exchange(other.allocation, VK_NULL_HANDLE); + return *this; + } + + void Create(const vk::BufferCreateInfo& image_ci, MemoryUsage usage, + VmaAllocationInfo* out_alloc_info); + + operator vk::Buffer() const { + return buffer; + } + + vk::Device device; + VmaAllocator allocator; + VmaAllocation allocation; + vk::Buffer buffer{}; +}; + +class Buffer { +public: + explicit Buffer(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler, + MemoryUsage usage, VAddr cpu_addr_, vk::BufferUsageFlags flags, + u64 size_bytes_); + + Buffer& operator=(const Buffer&) = delete; + Buffer(const Buffer&) = delete; + + Buffer& operator=(Buffer&&) = default; + Buffer(Buffer&&) = default; + + vk::BufferView View(u32 offset, u32 size, bool is_written, AmdGpu::DataFormat dfmt, + AmdGpu::NumberFormat nfmt); + + /// Increases the likeliness of this being a stream buffer + void IncreaseStreamScore(int score) noexcept { + stream_score += score; + } + + /// Returns the likeliness of this being a stream buffer + [[nodiscard]] int StreamScore() const noexcept { + return stream_score; + } + + /// Returns true when vaddr -> vaddr+size is fully contained in the buffer + [[nodiscard]] bool IsInBounds(VAddr addr, u64 size) const noexcept { + return addr >= cpu_addr && addr + size <= cpu_addr + SizeBytes(); + } + + /// Returns the base CPU address of the buffer + [[nodiscard]] VAddr CpuAddr() const noexcept { + return cpu_addr; + } + + /// Returns the offset relative to the given CPU address + [[nodiscard]] u32 Offset(VAddr other_cpu_addr) const noexcept { + return static_cast(other_cpu_addr - cpu_addr); + } + + size_t SizeBytes() const { + return size_bytes; + } + + vk::Buffer Handle() const noexcept { + return buffer; + } + + std::optional GetBarrier(vk::AccessFlagBits2 dst_acess_mask, + vk::PipelineStageFlagBits2 dst_stage) { + if (dst_acess_mask == access_mask && stage == dst_stage) { + return {}; + } + + auto barrier = vk::BufferMemoryBarrier2{ + .srcStageMask = stage, + .srcAccessMask = access_mask, + .dstStageMask = dst_stage, + .dstAccessMask = dst_acess_mask, + .buffer = buffer.buffer, + .size = size_bytes, + }; + access_mask = dst_acess_mask; + stage = dst_stage; + return barrier; + } + +public: + VAddr cpu_addr = 0; + bool is_picked{}; + bool is_coherent{}; + bool is_deleted{}; + int stream_score = 0; + size_t size_bytes = 0; + std::span mapped_data; + const Vulkan::Instance* instance; + Vulkan::Scheduler* scheduler; + MemoryUsage usage; + UniqueBuffer buffer; + vk::AccessFlagBits2 access_mask{vk::AccessFlagBits2::eNone}; + vk::PipelineStageFlagBits2 stage{vk::PipelineStageFlagBits2::eNone}; +}; + +class StreamBuffer : public Buffer { +public: + explicit StreamBuffer(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler, + MemoryUsage usage, u64 size_bytes_); + + /// Reserves a region of memory from the stream buffer. + std::pair Map(u64 size, u64 alignment = 0); + + /// Ensures that reserved bytes of memory are available to the GPU. + void Commit(); + + /// Maps and commits a memory region with user provided data + u64 Copy(VAddr src, size_t size, size_t alignment = 0) { + const auto [data, offset] = Map(size, alignment); + std::memcpy(data, reinterpret_cast(src), size); + Commit(); + return offset; + } + + u64 GetFreeSize() const { + return size_bytes - offset - mapped_size; + } + +private: + struct Watch { + u64 tick{}; + u64 upper_bound{}; + }; + + /// Increases the amount of watches available. + void ReserveWatches(std::vector& watches, std::size_t grow_size); + + /// Waits pending watches until requested upper bound. + void WaitPendingOperations(u64 requested_upper_bound); + +private: + u64 offset{}; + u64 mapped_size{}; + std::vector current_watches; + std::size_t current_watch_cursor{}; + std::optional invalidation_mark; + std::vector previous_watches; + std::size_t wait_cursor{}; + u64 wait_bound{}; +}; + +} // namespace VideoCore diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index 75f06365f..59c1e0bc3 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -1,696 +1,696 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include "common/alignment.h" -#include "common/scope_exit.h" -#include "common/types.h" -#include "shader_recompiler/frontend/fetch_shader.h" -#include "shader_recompiler/info.h" -#include "video_core/amdgpu/liverpool.h" -#include "video_core/buffer_cache/buffer_cache.h" -#include "video_core/renderer_vulkan/liverpool_to_vk.h" -#include "video_core/renderer_vulkan/vk_instance.h" -#include "video_core/renderer_vulkan/vk_scheduler.h" -#include "video_core/texture_cache/texture_cache.h" - -namespace VideoCore { - -static constexpr size_t NumVertexBuffers = 32; -static constexpr size_t GdsBufferSize = 64_KB; -static constexpr size_t StagingBufferSize = 1_GB; -static constexpr size_t UboStreamBufferSize = 64_MB; - -BufferCache::BufferCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_, - AmdGpu::Liverpool* liverpool_, TextureCache& texture_cache_, - PageManager& tracker_) - : instance{instance_}, scheduler{scheduler_}, liverpool{liverpool_}, - texture_cache{texture_cache_}, tracker{tracker_}, - staging_buffer{instance, scheduler, MemoryUsage::Upload, StagingBufferSize}, - stream_buffer{instance, scheduler, MemoryUsage::Stream, UboStreamBufferSize}, - gds_buffer{instance, scheduler, MemoryUsage::Stream, 0, AllFlags, GdsBufferSize}, - memory_tracker{&tracker} { - Vulkan::SetObjectName(instance.GetDevice(), gds_buffer.Handle(), "GDS Buffer"); - - // Ensure the first slot is used for the null buffer - const auto null_id = - slot_buffers.insert(instance, scheduler, MemoryUsage::DeviceLocal, 0, ReadFlags, 1); - ASSERT(null_id.index == 0); - const vk::Buffer& null_buffer = slot_buffers[null_id].buffer; - Vulkan::SetObjectName(instance.GetDevice(), null_buffer, "Null Buffer"); - - const vk::BufferViewCreateInfo null_view_ci = { - .buffer = null_buffer, - .format = vk::Format::eR8Unorm, - .offset = 0, - .range = VK_WHOLE_SIZE, - }; - const auto [null_view_result, null_view] = instance.GetDevice().createBufferView(null_view_ci); - ASSERT_MSG(null_view_result == vk::Result::eSuccess, "Failed to create null buffer view."); - null_buffer_view = null_view; - Vulkan::SetObjectName(instance.GetDevice(), null_buffer_view, "Null Buffer View"); -} - -BufferCache::~BufferCache() = default; - -void BufferCache::InvalidateMemory(VAddr device_addr, u64 size) { - std::scoped_lock lk{mutex}; - const bool is_tracked = IsRegionRegistered(device_addr, size); - if (!is_tracked) { - return; - } - // Mark the page as CPU modified to stop tracking writes. - SCOPE_EXIT { - memory_tracker.MarkRegionAsCpuModified(device_addr, size); - }; - if (!memory_tracker.IsRegionGpuModified(device_addr, size)) { - // Page has not been modified by the GPU, nothing to do. - return; - } -} - -void BufferCache::DownloadBufferMemory(Buffer& buffer, VAddr device_addr, u64 size) { - boost::container::small_vector copies; - u64 total_size_bytes = 0; - memory_tracker.ForEachDownloadRange( - device_addr, size, [&](u64 device_addr_out, u64 range_size) { - const VAddr buffer_addr = buffer.CpuAddr(); - const auto add_download = [&](VAddr start, VAddr end) { - const u64 new_offset = start - buffer_addr; - const u64 new_size = end - start; - copies.push_back(vk::BufferCopy{ - .srcOffset = new_offset, - .dstOffset = total_size_bytes, - .size = new_size, - }); - total_size_bytes += new_size; - }; - gpu_modified_ranges.ForEachInRange(device_addr_out, range_size, add_download); - gpu_modified_ranges.Subtract(device_addr_out, range_size); - }); - if (total_size_bytes == 0) { - return; - } - const auto [staging, offset] = staging_buffer.Map(total_size_bytes); - for (auto& copy : copies) { - // Modify copies to have the staging offset in mind - copy.dstOffset += offset; - } - staging_buffer.Commit(); - scheduler.EndRendering(); - const auto cmdbuf = scheduler.CommandBuffer(); - cmdbuf.copyBuffer(buffer.buffer, staging_buffer.Handle(), copies); - scheduler.Finish(); - for (const auto& copy : copies) { - const VAddr copy_device_addr = buffer.CpuAddr() + copy.srcOffset; - const u64 dst_offset = copy.dstOffset - offset; - std::memcpy(std::bit_cast(copy_device_addr), staging + dst_offset, copy.size); - } -} - -bool BufferCache::BindVertexBuffers( - const Shader::Info& vs_info, const std::optional& fetch_shader) { - boost::container::small_vector attributes; - boost::container::small_vector bindings; - SCOPE_EXIT { - if (instance.IsVertexInputDynamicState()) { - const auto cmdbuf = scheduler.CommandBuffer(); - cmdbuf.setVertexInputEXT(bindings, attributes); - } else if (bindings.empty()) { - // Required to call bindVertexBuffers2EXT at least once in the current command buffer - // with non-null strides without a non-dynamic stride pipeline in between. Thus even - // when nothing is bound we still need to make a dummy call. Non-null strides in turn - // requires a count greater than 0. - const auto cmdbuf = scheduler.CommandBuffer(); - const std::array null_buffers = {GetBuffer(NULL_BUFFER_ID).buffer.buffer}; - constexpr std::array null_offsets = {static_cast(0)}; - cmdbuf.bindVertexBuffers2EXT(0, null_buffers, null_offsets, null_offsets, null_offsets); - } - }; - - if (!fetch_shader || fetch_shader->attributes.empty()) { - return false; - } - - std::array host_buffers; - std::array host_offsets; - std::array host_sizes; - std::array host_strides; - boost::container::static_vector guest_buffers; - - struct BufferRange { - VAddr base_address; - VAddr end_address; - vk::Buffer vk_buffer; - u64 offset; - - size_t GetSize() const { - return end_address - base_address; - } - }; - - // Calculate buffers memory overlaps - bool has_step_rate = false; - boost::container::static_vector ranges{}; - for (const auto& attrib : fetch_shader->attributes) { - if (attrib.UsesStepRates()) { - has_step_rate = true; - continue; - } - - const auto& buffer = attrib.GetSharp(vs_info); - if (buffer.GetSize() == 0) { - continue; - } - guest_buffers.emplace_back(buffer); - ranges.emplace_back(buffer.base_address, buffer.base_address + buffer.GetSize()); - attributes.push_back({ - .location = attrib.semantic, - .binding = attrib.semantic, - .format = - Vulkan::LiverpoolToVK::SurfaceFormat(buffer.GetDataFmt(), buffer.GetNumberFmt()), - .offset = 0, - }); - bindings.push_back({ - .binding = attrib.semantic, - .stride = buffer.GetStride(), - .inputRate = attrib.GetStepRate() == Shader::Gcn::VertexAttribute::InstanceIdType::None - ? vk::VertexInputRate::eVertex - : vk::VertexInputRate::eInstance, - .divisor = 1, - }); - } - if (ranges.empty()) { - return false; - } - - std::ranges::sort(ranges, [](const BufferRange& lhv, const BufferRange& rhv) { - return lhv.base_address < rhv.base_address; - }); - - boost::container::static_vector ranges_merged{ranges[0]}; - for (auto range : ranges) { - auto& prev_range = ranges_merged.back(); - if (prev_range.end_address < range.base_address) { - ranges_merged.emplace_back(range); - } else { - prev_range.end_address = std::max(prev_range.end_address, range.end_address); - } - } - - // Map buffers - for (auto& range : ranges_merged) { - const auto [buffer, offset] = ObtainBuffer(range.base_address, range.GetSize(), false); - range.vk_buffer = buffer->buffer; - range.offset = offset; - } - - // Bind vertex buffers - const size_t num_buffers = guest_buffers.size(); - for (u32 i = 0; i < num_buffers; ++i) { - const auto& buffer = guest_buffers[i]; - const auto host_buffer = std::ranges::find_if(ranges_merged, [&](const BufferRange& range) { - return (buffer.base_address >= range.base_address && - buffer.base_address < range.end_address); - }); - ASSERT(host_buffer != ranges_merged.cend()); - - host_buffers[i] = host_buffer->vk_buffer; - host_offsets[i] = host_buffer->offset + buffer.base_address - host_buffer->base_address; - host_sizes[i] = buffer.GetSize(); - host_strides[i] = buffer.GetStride(); - } - - if (num_buffers > 0) { - const auto cmdbuf = scheduler.CommandBuffer(); - if (instance.IsVertexInputDynamicState()) { - cmdbuf.bindVertexBuffers(0, num_buffers, host_buffers.data(), host_offsets.data()); - } else { - cmdbuf.bindVertexBuffers2EXT(0, num_buffers, host_buffers.data(), host_offsets.data(), - host_sizes.data(), host_strides.data()); - } - } - - return has_step_rate; -} - -u32 BufferCache::BindIndexBuffer(bool& is_indexed, u32 index_offset) { - // Emulate QuadList and Polygon primitive types with CPU made index buffer. - const auto& regs = liverpool->regs; - if (!is_indexed) { - if (regs.primitive_type != AmdGpu::PrimitiveType::Polygon) { - return regs.num_indices; - } - - // Emit indices. - const u32 index_size = 3 * regs.num_indices; - const auto [data, offset] = stream_buffer.Map(index_size); - Vulkan::LiverpoolToVK::EmitPolygonToTriangleListIndices(data, regs.num_indices); - stream_buffer.Commit(); - - // Bind index buffer. - is_indexed = true; - - const auto cmdbuf = scheduler.CommandBuffer(); - cmdbuf.bindIndexBuffer(stream_buffer.Handle(), offset, vk::IndexType::eUint16); - return index_size / sizeof(u16); - } - - // Figure out index type and size. - const bool is_index16 = - regs.index_buffer_type.index_type == AmdGpu::Liverpool::IndexType::Index16; - const vk::IndexType index_type = is_index16 ? vk::IndexType::eUint16 : vk::IndexType::eUint32; - const u32 index_size = is_index16 ? sizeof(u16) : sizeof(u32); - VAddr index_address = regs.index_base_address.Address(); - index_address += index_offset * index_size; - - if (regs.primitive_type == AmdGpu::PrimitiveType::Polygon) { - UNREACHABLE(); - } - - // Bind index buffer. - const u32 index_buffer_size = regs.num_indices * index_size; - const auto [vk_buffer, offset] = ObtainBuffer(index_address, index_buffer_size, false); - const auto cmdbuf = scheduler.CommandBuffer(); - cmdbuf.bindIndexBuffer(vk_buffer->Handle(), offset, index_type); - return regs.num_indices; -} - -void BufferCache::InlineData(VAddr address, const void* value, u32 num_bytes, bool is_gds) { - ASSERT_MSG(address % 4 == 0, "GDS offset must be dword aligned"); - if (!is_gds && !IsRegionRegistered(address, num_bytes)) { - memcpy(std::bit_cast(address), value, num_bytes); - return; - } - scheduler.EndRendering(); - const auto cmdbuf = scheduler.CommandBuffer(); - const Buffer* buffer = [&] { - if (is_gds) { - return &gds_buffer; - } - const BufferId buffer_id = FindBuffer(address, num_bytes); - return &slot_buffers[buffer_id]; - }(); - const vk::BufferMemoryBarrier2 buf_barrier = { - .srcStageMask = vk::PipelineStageFlagBits2::eTransfer, - .srcAccessMask = vk::AccessFlagBits2::eTransferWrite, - .dstStageMask = vk::PipelineStageFlagBits2::eAllCommands, - .dstAccessMask = vk::AccessFlagBits2::eMemoryRead, - .buffer = buffer->Handle(), - .offset = buffer->Offset(address), - .size = num_bytes, - }; - cmdbuf.pipelineBarrier2(vk::DependencyInfo{ - .dependencyFlags = vk::DependencyFlagBits::eByRegion, - .bufferMemoryBarrierCount = 1, - .pBufferMemoryBarriers = &buf_barrier, - }); - cmdbuf.updateBuffer(buffer->Handle(), buf_barrier.offset, num_bytes, value); -} - -std::pair BufferCache::ObtainHostUBO(std::span data) { - static constexpr u64 StreamThreshold = CACHING_PAGESIZE; - ASSERT(data.size_bytes() <= StreamThreshold); - const u64 offset = stream_buffer.Copy(reinterpret_cast(data.data()), data.size_bytes(), - instance.UniformMinAlignment()); - return {&stream_buffer, offset}; -} - -std::pair BufferCache::ObtainBuffer(VAddr device_addr, u32 size, bool is_written, - bool is_texel_buffer, BufferId buffer_id) { - // For small uniform buffers that have not been modified by gpu - // use device local stream buffer to reduce renderpass breaks. - static constexpr u64 StreamThreshold = CACHING_PAGESIZE; - const bool is_gpu_dirty = memory_tracker.IsRegionGpuModified(device_addr, size); - if (!is_written && size <= StreamThreshold && !is_gpu_dirty) { - const u64 offset = stream_buffer.Copy(device_addr, size, instance.UniformMinAlignment()); - return {&stream_buffer, offset}; - } - - if (!buffer_id || slot_buffers[buffer_id].is_deleted) { - buffer_id = FindBuffer(device_addr, size); - } - Buffer& buffer = slot_buffers[buffer_id]; - SynchronizeBuffer(buffer, device_addr, size, is_texel_buffer); - if (is_written) { - memory_tracker.MarkRegionAsGpuModified(device_addr, size); - gpu_modified_ranges.Add(device_addr, size); - } - return {&buffer, buffer.Offset(device_addr)}; -} - -std::pair BufferCache::ObtainViewBuffer(VAddr gpu_addr, u32 size, bool prefer_gpu) { - // Check if any buffer contains the full requested range. - const u64 page = gpu_addr >> CACHING_PAGEBITS; - const BufferId buffer_id = page_table[page]; - if (buffer_id) { - Buffer& buffer = slot_buffers[buffer_id]; - if (buffer.IsInBounds(gpu_addr, size)) { - SynchronizeBuffer(buffer, gpu_addr, size, false); - return {&buffer, buffer.Offset(gpu_addr)}; - } - } - // If no buffer contains the full requested range but some buffer within was GPU-modified, - // fall back to ObtainBuffer to create a full buffer and avoid losing GPU modifications. - // This is only done if the request prefers to use GPU memory, otherwise we can skip it. - if (prefer_gpu && memory_tracker.IsRegionGpuModified(gpu_addr, size)) { - return ObtainBuffer(gpu_addr, size, false, false); - } - // In all other cases, just do a CPU copy to the staging buffer. - const u32 offset = staging_buffer.Copy(gpu_addr, size, 16); - return {&staging_buffer, offset}; -} - -bool BufferCache::IsRegionRegistered(VAddr addr, size_t size) { - const VAddr end_addr = addr + size; - const u64 page_end = Common::DivCeil(end_addr, CACHING_PAGESIZE); - for (u64 page = addr >> CACHING_PAGEBITS; page < page_end;) { - const BufferId buffer_id = page_table[page]; - if (!buffer_id) { - ++page; - continue; - } - Buffer& buffer = slot_buffers[buffer_id]; - const VAddr buf_start_addr = buffer.CpuAddr(); - const VAddr buf_end_addr = buf_start_addr + buffer.SizeBytes(); - if (buf_start_addr < end_addr && addr < buf_end_addr) { - return true; - } - page = Common::DivCeil(buf_end_addr, CACHING_PAGESIZE); - } - return false; -} - -bool BufferCache::IsRegionCpuModified(VAddr addr, size_t size) { - return memory_tracker.IsRegionCpuModified(addr, size); -} - -bool BufferCache::IsRegionGpuModified(VAddr addr, size_t size) { - return memory_tracker.IsRegionGpuModified(addr, size); -} - -BufferId BufferCache::FindBuffer(VAddr device_addr, u32 size) { - if (device_addr == 0) { - return NULL_BUFFER_ID; - } - const u64 page = device_addr >> CACHING_PAGEBITS; - const BufferId buffer_id = page_table[page]; - if (!buffer_id) { - return CreateBuffer(device_addr, size); - } - const Buffer& buffer = slot_buffers[buffer_id]; - if (buffer.IsInBounds(device_addr, size)) { - return buffer_id; - } - return CreateBuffer(device_addr, size); -} - -BufferCache::OverlapResult BufferCache::ResolveOverlaps(VAddr device_addr, u32 wanted_size) { - static constexpr int STREAM_LEAP_THRESHOLD = 16; - boost::container::small_vector overlap_ids; - VAddr begin = device_addr; - VAddr end = device_addr + wanted_size; - int stream_score = 0; - bool has_stream_leap = false; - const auto expand_begin = [&](VAddr add_value) { - static constexpr VAddr min_page = CACHING_PAGESIZE + DEVICE_PAGESIZE; - if (add_value > begin - min_page) { - begin = min_page; - device_addr = DEVICE_PAGESIZE; - return; - } - begin -= add_value; - device_addr = begin - CACHING_PAGESIZE; - }; - const auto expand_end = [&](VAddr add_value) { - static constexpr VAddr max_page = 1ULL << MemoryTracker::MAX_CPU_PAGE_BITS; - if (add_value > max_page - end) { - end = max_page; - return; - } - end += add_value; - }; - if (begin == 0) { - return OverlapResult{ - .ids = std::move(overlap_ids), - .begin = begin, - .end = end, - .has_stream_leap = has_stream_leap, - }; - } - for (; device_addr >> CACHING_PAGEBITS < Common::DivCeil(end, CACHING_PAGESIZE); - device_addr += CACHING_PAGESIZE) { - const BufferId overlap_id = page_table[device_addr >> CACHING_PAGEBITS]; - if (!overlap_id) { - continue; - } - Buffer& overlap = slot_buffers[overlap_id]; - if (overlap.is_picked) { - continue; - } - overlap_ids.push_back(overlap_id); - overlap.is_picked = true; - const VAddr overlap_device_addr = overlap.CpuAddr(); - const bool expands_left = overlap_device_addr < begin; - if (expands_left) { - begin = overlap_device_addr; - } - const VAddr overlap_end = overlap_device_addr + overlap.SizeBytes(); - const bool expands_right = overlap_end > end; - if (overlap_end > end) { - end = overlap_end; - } - stream_score += overlap.StreamScore(); - if (stream_score > STREAM_LEAP_THRESHOLD && !has_stream_leap) { - // When this memory region has been joined a bunch of times, we assume it's being used - // as a stream buffer. Increase the size to skip constantly recreating buffers. - has_stream_leap = true; - if (expands_right) { - expand_begin(CACHING_PAGESIZE * 128); - } - if (expands_left) { - expand_end(CACHING_PAGESIZE * 128); - } - } - } - return OverlapResult{ - .ids = std::move(overlap_ids), - .begin = begin, - .end = end, - .has_stream_leap = has_stream_leap, - }; -} - -void BufferCache::JoinOverlap(BufferId new_buffer_id, BufferId overlap_id, - bool accumulate_stream_score) { - Buffer& new_buffer = slot_buffers[new_buffer_id]; - Buffer& overlap = slot_buffers[overlap_id]; - if (accumulate_stream_score) { - new_buffer.IncreaseStreamScore(overlap.StreamScore() + 1); - } - const size_t dst_base_offset = overlap.CpuAddr() - new_buffer.CpuAddr(); - const vk::BufferCopy copy = { - .srcOffset = 0, - .dstOffset = dst_base_offset, - .size = overlap.SizeBytes(), - }; - scheduler.EndRendering(); - const auto cmdbuf = scheduler.CommandBuffer(); - static constexpr vk::MemoryBarrier READ_BARRIER{ - .srcAccessMask = vk::AccessFlagBits::eMemoryWrite, - .dstAccessMask = vk::AccessFlagBits::eTransferRead | vk::AccessFlagBits::eTransferWrite, - }; - static constexpr vk::MemoryBarrier WRITE_BARRIER{ - .srcAccessMask = vk::AccessFlagBits::eTransferWrite, - .dstAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite, - }; - cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands, - vk::PipelineStageFlagBits::eTransfer, vk::DependencyFlagBits::eByRegion, - READ_BARRIER, {}, {}); - cmdbuf.copyBuffer(overlap.buffer, new_buffer.buffer, copy); - cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, - vk::PipelineStageFlagBits::eAllCommands, - vk::DependencyFlagBits::eByRegion, WRITE_BARRIER, {}, {}); - DeleteBuffer(overlap_id); -} - -BufferId BufferCache::CreateBuffer(VAddr device_addr, u32 wanted_size) { - const VAddr device_addr_end = Common::AlignUp(device_addr + wanted_size, CACHING_PAGESIZE); - device_addr = Common::AlignDown(device_addr, CACHING_PAGESIZE); - wanted_size = static_cast(device_addr_end - device_addr); - const OverlapResult overlap = ResolveOverlaps(device_addr, wanted_size); - const u32 size = static_cast(overlap.end - overlap.begin); - const BufferId new_buffer_id = slot_buffers.insert( - instance, scheduler, MemoryUsage::DeviceLocal, overlap.begin, AllFlags, size); - auto& new_buffer = slot_buffers[new_buffer_id]; - const size_t size_bytes = new_buffer.SizeBytes(); - const auto cmdbuf = scheduler.CommandBuffer(); - scheduler.EndRendering(); - cmdbuf.fillBuffer(new_buffer.buffer, 0, size_bytes, 0); - for (const BufferId overlap_id : overlap.ids) { - JoinOverlap(new_buffer_id, overlap_id, !overlap.has_stream_leap); - } - Register(new_buffer_id); - return new_buffer_id; -} - -void BufferCache::Register(BufferId buffer_id) { - ChangeRegister(buffer_id); -} - -void BufferCache::Unregister(BufferId buffer_id) { - ChangeRegister(buffer_id); -} - -template -void BufferCache::ChangeRegister(BufferId buffer_id) { - Buffer& buffer = slot_buffers[buffer_id]; - const auto size = buffer.SizeBytes(); - const VAddr device_addr_begin = buffer.CpuAddr(); - const VAddr device_addr_end = device_addr_begin + size; - const u64 page_begin = device_addr_begin / CACHING_PAGESIZE; - const u64 page_end = Common::DivCeil(device_addr_end, CACHING_PAGESIZE); - for (u64 page = page_begin; page != page_end; ++page) { - if constexpr (insert) { - page_table[page] = buffer_id; - } else { - page_table[page] = BufferId{}; - } - } -} - -void BufferCache::SynchronizeBuffer(Buffer& buffer, VAddr device_addr, u32 size, - bool is_texel_buffer) { - std::scoped_lock lk{mutex}; - boost::container::small_vector copies; - u64 total_size_bytes = 0; - u64 largest_copy = 0; - VAddr buffer_start = buffer.CpuAddr(); - memory_tracker.ForEachUploadRange(device_addr, size, [&](u64 device_addr_out, u64 range_size) { - copies.push_back(vk::BufferCopy{ - .srcOffset = total_size_bytes, - .dstOffset = device_addr_out - buffer_start, - .size = range_size, - }); - total_size_bytes += range_size; - largest_copy = std::max(largest_copy, range_size); - }); - SCOPE_EXIT { - if (is_texel_buffer) { - SynchronizeBufferFromImage(buffer, device_addr, size); - } - }; - if (total_size_bytes == 0) { - return; - } - vk::Buffer src_buffer = staging_buffer.Handle(); - if (total_size_bytes < StagingBufferSize) { - const auto [staging, offset] = staging_buffer.Map(total_size_bytes); - for (auto& copy : copies) { - u8* const src_pointer = staging + copy.srcOffset; - const VAddr device_addr = buffer.CpuAddr() + copy.dstOffset; - std::memcpy(src_pointer, std::bit_cast(device_addr), copy.size); - // Apply the staging offset - copy.srcOffset += offset; - } - staging_buffer.Commit(); - } else { - // For large one time transfers use a temporary host buffer. - // RenderDoc can lag quite a bit if the stream buffer is too large. - Buffer temp_buffer{instance, - scheduler, - MemoryUsage::Upload, - 0, - vk::BufferUsageFlagBits::eTransferSrc, - total_size_bytes}; - src_buffer = temp_buffer.Handle(); - u8* const staging = temp_buffer.mapped_data.data(); - for (auto& copy : copies) { - u8* const src_pointer = staging + copy.srcOffset; - const VAddr device_addr = buffer.CpuAddr() + copy.dstOffset; - std::memcpy(src_pointer, std::bit_cast(device_addr), copy.size); - } - scheduler.DeferOperation([buffer = std::move(temp_buffer)]() mutable {}); - } - scheduler.EndRendering(); - const auto cmdbuf = scheduler.CommandBuffer(); - static constexpr vk::MemoryBarrier READ_BARRIER{ - .srcAccessMask = vk::AccessFlagBits::eMemoryWrite, - .dstAccessMask = vk::AccessFlagBits::eTransferRead | vk::AccessFlagBits::eTransferWrite, - }; - static constexpr vk::MemoryBarrier WRITE_BARRIER{ - .srcAccessMask = vk::AccessFlagBits::eTransferWrite, - .dstAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite, - }; - cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands, - vk::PipelineStageFlagBits::eTransfer, vk::DependencyFlagBits::eByRegion, - READ_BARRIER, {}, {}); - cmdbuf.copyBuffer(src_buffer, buffer.buffer, copies); - cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, - vk::PipelineStageFlagBits::eAllCommands, - vk::DependencyFlagBits::eByRegion, WRITE_BARRIER, {}, {}); -} - -bool BufferCache::SynchronizeBufferFromImage(Buffer& buffer, VAddr device_addr, u32 size) { - static constexpr FindFlags find_flags = - FindFlags::NoCreate | FindFlags::RelaxDim | FindFlags::RelaxFmt | FindFlags::RelaxSize; - TextureCache::BaseDesc desc{}; - desc.info.guest_address = device_addr; - desc.info.guest_size_bytes = size; - const ImageId image_id = texture_cache.FindImage(desc, find_flags); - if (!image_id) { - return false; - } - Image& image = texture_cache.GetImage(image_id); - if (False(image.flags & ImageFlagBits::GpuModified)) { - return false; - } - ASSERT_MSG(device_addr == image.info.guest_address, - "Texel buffer aliases image subresources {:x} : {:x}", device_addr, - image.info.guest_address); - boost::container::small_vector copies; - u32 offset = buffer.Offset(image.info.guest_address); - const u32 num_layers = image.info.resources.layers; - const u32 max_offset = offset + size; - for (u32 m = 0; m < image.info.resources.levels; m++) { - const u32 width = std::max(image.info.size.width >> m, 1u); - const u32 height = std::max(image.info.size.height >> m, 1u); - const u32 depth = - image.info.props.is_volume ? std::max(image.info.size.depth >> m, 1u) : 1u; - const auto& [mip_size, mip_pitch, mip_height, mip_ofs] = image.info.mips_layout[m]; - offset += mip_ofs * num_layers; - if (offset + (mip_size * num_layers) > max_offset) { - break; - } - copies.push_back({ - .bufferOffset = offset, - .bufferRowLength = static_cast(mip_pitch), - .bufferImageHeight = static_cast(mip_height), - .imageSubresource{ - .aspectMask = image.aspect_mask & ~vk::ImageAspectFlagBits::eStencil, - .mipLevel = m, - .baseArrayLayer = 0, - .layerCount = num_layers, - }, - .imageOffset = {0, 0, 0}, - .imageExtent = {width, height, depth}, - }); - } - if (!copies.empty()) { - scheduler.EndRendering(); - image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, {}); - const auto cmdbuf = scheduler.CommandBuffer(); - cmdbuf.copyImageToBuffer(image.image, vk::ImageLayout::eTransferSrcOptimal, buffer.buffer, - copies); - } - return true; -} - -void BufferCache::DeleteBuffer(BufferId buffer_id) { - Buffer& buffer = slot_buffers[buffer_id]; - Unregister(buffer_id); - scheduler.DeferOperation([this, buffer_id] { slot_buffers.erase(buffer_id); }); - buffer.is_deleted = true; -} - -} // namespace VideoCore +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include "common/alignment.h" +#include "common/scope_exit.h" +#include "common/types.h" +#include "shader_recompiler/frontend/fetch_shader.h" +#include "shader_recompiler/info.h" +#include "video_core/amdgpu/liverpool.h" +#include "video_core/buffer_cache/buffer_cache.h" +#include "video_core/renderer_vulkan/liverpool_to_vk.h" +#include "video_core/renderer_vulkan/vk_instance.h" +#include "video_core/renderer_vulkan/vk_scheduler.h" +#include "video_core/texture_cache/texture_cache.h" + +namespace VideoCore { + +static constexpr size_t NumVertexBuffers = 32; +static constexpr size_t GdsBufferSize = 64_KB; +static constexpr size_t StagingBufferSize = 1_GB; +static constexpr size_t UboStreamBufferSize = 64_MB; + +BufferCache::BufferCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_, + AmdGpu::Liverpool* liverpool_, TextureCache& texture_cache_, + PageManager& tracker_) + : instance{instance_}, scheduler{scheduler_}, liverpool{liverpool_}, + texture_cache{texture_cache_}, tracker{tracker_}, + staging_buffer{instance, scheduler, MemoryUsage::Upload, StagingBufferSize}, + stream_buffer{instance, scheduler, MemoryUsage::Stream, UboStreamBufferSize}, + gds_buffer{instance, scheduler, MemoryUsage::Stream, 0, AllFlags, GdsBufferSize}, + memory_tracker{&tracker} { + Vulkan::SetObjectName(instance.GetDevice(), gds_buffer.Handle(), "GDS Buffer"); + + // Ensure the first slot is used for the null buffer + const auto null_id = + slot_buffers.insert(instance, scheduler, MemoryUsage::DeviceLocal, 0, ReadFlags, 1); + ASSERT(null_id.index == 0); + const vk::Buffer& null_buffer = slot_buffers[null_id].buffer; + Vulkan::SetObjectName(instance.GetDevice(), null_buffer, "Null Buffer"); + + const vk::BufferViewCreateInfo null_view_ci = { + .buffer = null_buffer, + .format = vk::Format::eR8Unorm, + .offset = 0, + .range = VK_WHOLE_SIZE, + }; + const auto [null_view_result, null_view] = instance.GetDevice().createBufferView(null_view_ci); + ASSERT_MSG(null_view_result == vk::Result::eSuccess, "Failed to create null buffer view."); + null_buffer_view = null_view; + Vulkan::SetObjectName(instance.GetDevice(), null_buffer_view, "Null Buffer View"); +} + +BufferCache::~BufferCache() = default; + +void BufferCache::InvalidateMemory(VAddr device_addr, u64 size) { + std::scoped_lock lk{mutex}; + const bool is_tracked = IsRegionRegistered(device_addr, size); + if (!is_tracked) { + return; + } + // Mark the page as CPU modified to stop tracking writes. + SCOPE_EXIT { + memory_tracker.MarkRegionAsCpuModified(device_addr, size); + }; + if (!memory_tracker.IsRegionGpuModified(device_addr, size)) { + // Page has not been modified by the GPU, nothing to do. + return; + } +} + +void BufferCache::DownloadBufferMemory(Buffer& buffer, VAddr device_addr, u64 size) { + boost::container::small_vector copies; + u64 total_size_bytes = 0; + memory_tracker.ForEachDownloadRange( + device_addr, size, [&](u64 device_addr_out, u64 range_size) { + const VAddr buffer_addr = buffer.CpuAddr(); + const auto add_download = [&](VAddr start, VAddr end) { + const u64 new_offset = start - buffer_addr; + const u64 new_size = end - start; + copies.push_back(vk::BufferCopy{ + .srcOffset = new_offset, + .dstOffset = total_size_bytes, + .size = new_size, + }); + total_size_bytes += new_size; + }; + gpu_modified_ranges.ForEachInRange(device_addr_out, range_size, add_download); + gpu_modified_ranges.Subtract(device_addr_out, range_size); + }); + if (total_size_bytes == 0) { + return; + } + const auto [staging, offset] = staging_buffer.Map(total_size_bytes); + for (auto& copy : copies) { + // Modify copies to have the staging offset in mind + copy.dstOffset += offset; + } + staging_buffer.Commit(); + scheduler.EndRendering(); + const auto cmdbuf = scheduler.CommandBuffer(); + cmdbuf.copyBuffer(buffer.buffer, staging_buffer.Handle(), copies); + scheduler.Finish(); + for (const auto& copy : copies) { + const VAddr copy_device_addr = buffer.CpuAddr() + copy.srcOffset; + const u64 dst_offset = copy.dstOffset - offset; + std::memcpy(std::bit_cast(copy_device_addr), staging + dst_offset, copy.size); + } +} + +bool BufferCache::BindVertexBuffers( + const Shader::Info& vs_info, const std::optional& fetch_shader) { + boost::container::small_vector attributes; + boost::container::small_vector bindings; + SCOPE_EXIT { + if (instance.IsVertexInputDynamicState()) { + const auto cmdbuf = scheduler.CommandBuffer(); + cmdbuf.setVertexInputEXT(bindings, attributes); + } else if (bindings.empty()) { + // Required to call bindVertexBuffers2EXT at least once in the current command buffer + // with non-null strides without a non-dynamic stride pipeline in between. Thus even + // when nothing is bound we still need to make a dummy call. Non-null strides in turn + // requires a count greater than 0. + const auto cmdbuf = scheduler.CommandBuffer(); + const std::array null_buffers = {GetBuffer(NULL_BUFFER_ID).buffer.buffer}; + constexpr std::array null_offsets = {static_cast(0)}; + cmdbuf.bindVertexBuffers2EXT(0, null_buffers, null_offsets, null_offsets, null_offsets); + } + }; + + if (!fetch_shader || fetch_shader->attributes.empty()) { + return false; + } + + std::array host_buffers; + std::array host_offsets; + std::array host_sizes; + std::array host_strides; + boost::container::static_vector guest_buffers; + + struct BufferRange { + VAddr base_address; + VAddr end_address; + vk::Buffer vk_buffer; + u64 offset; + + size_t GetSize() const { + return end_address - base_address; + } + }; + + // Calculate buffers memory overlaps + bool has_step_rate = false; + boost::container::static_vector ranges{}; + for (const auto& attrib : fetch_shader->attributes) { + if (attrib.UsesStepRates()) { + has_step_rate = true; + continue; + } + + const auto& buffer = attrib.GetSharp(vs_info); + if (buffer.GetSize() == 0) { + continue; + } + guest_buffers.emplace_back(buffer); + ranges.emplace_back(buffer.base_address, buffer.base_address + buffer.GetSize()); + attributes.push_back({ + .location = attrib.semantic, + .binding = attrib.semantic, + .format = + Vulkan::LiverpoolToVK::SurfaceFormat(buffer.GetDataFmt(), buffer.GetNumberFmt()), + .offset = 0, + }); + bindings.push_back({ + .binding = attrib.semantic, + .stride = buffer.GetStride(), + .inputRate = attrib.GetStepRate() == Shader::Gcn::VertexAttribute::InstanceIdType::None + ? vk::VertexInputRate::eVertex + : vk::VertexInputRate::eInstance, + .divisor = 1, + }); + } + if (ranges.empty()) { + return false; + } + + std::ranges::sort(ranges, [](const BufferRange& lhv, const BufferRange& rhv) { + return lhv.base_address < rhv.base_address; + }); + + boost::container::static_vector ranges_merged{ranges[0]}; + for (auto range : ranges) { + auto& prev_range = ranges_merged.back(); + if (prev_range.end_address < range.base_address) { + ranges_merged.emplace_back(range); + } else { + prev_range.end_address = std::max(prev_range.end_address, range.end_address); + } + } + + // Map buffers + for (auto& range : ranges_merged) { + const auto [buffer, offset] = ObtainBuffer(range.base_address, range.GetSize(), false); + range.vk_buffer = buffer->buffer; + range.offset = offset; + } + + // Bind vertex buffers + const size_t num_buffers = guest_buffers.size(); + for (u32 i = 0; i < num_buffers; ++i) { + const auto& buffer = guest_buffers[i]; + const auto host_buffer = std::ranges::find_if(ranges_merged, [&](const BufferRange& range) { + return (buffer.base_address >= range.base_address && + buffer.base_address < range.end_address); + }); + ASSERT(host_buffer != ranges_merged.cend()); + + host_buffers[i] = host_buffer->vk_buffer; + host_offsets[i] = host_buffer->offset + buffer.base_address - host_buffer->base_address; + host_sizes[i] = buffer.GetSize(); + host_strides[i] = buffer.GetStride(); + } + + if (num_buffers > 0) { + const auto cmdbuf = scheduler.CommandBuffer(); + if (instance.IsVertexInputDynamicState()) { + cmdbuf.bindVertexBuffers(0, num_buffers, host_buffers.data(), host_offsets.data()); + } else { + cmdbuf.bindVertexBuffers2EXT(0, num_buffers, host_buffers.data(), host_offsets.data(), + host_sizes.data(), host_strides.data()); + } + } + + return has_step_rate; +} + +u32 BufferCache::BindIndexBuffer(bool& is_indexed, u32 index_offset) { + // Emulate QuadList and Polygon primitive types with CPU made index buffer. + const auto& regs = liverpool->regs; + if (!is_indexed) { + if (regs.primitive_type != AmdGpu::PrimitiveType::Polygon) { + return regs.num_indices; + } + + // Emit indices. + const u32 index_size = 3 * regs.num_indices; + const auto [data, offset] = stream_buffer.Map(index_size); + Vulkan::LiverpoolToVK::EmitPolygonToTriangleListIndices(data, regs.num_indices); + stream_buffer.Commit(); + + // Bind index buffer. + is_indexed = true; + + const auto cmdbuf = scheduler.CommandBuffer(); + cmdbuf.bindIndexBuffer(stream_buffer.Handle(), offset, vk::IndexType::eUint16); + return index_size / sizeof(u16); + } + + // Figure out index type and size. + const bool is_index16 = + regs.index_buffer_type.index_type == AmdGpu::Liverpool::IndexType::Index16; + const vk::IndexType index_type = is_index16 ? vk::IndexType::eUint16 : vk::IndexType::eUint32; + const u32 index_size = is_index16 ? sizeof(u16) : sizeof(u32); + VAddr index_address = regs.index_base_address.Address(); + index_address += index_offset * index_size; + + if (regs.primitive_type == AmdGpu::PrimitiveType::Polygon) { + UNREACHABLE(); + } + + // Bind index buffer. + const u32 index_buffer_size = regs.num_indices * index_size; + const auto [vk_buffer, offset] = ObtainBuffer(index_address, index_buffer_size, false); + const auto cmdbuf = scheduler.CommandBuffer(); + cmdbuf.bindIndexBuffer(vk_buffer->Handle(), offset, index_type); + return regs.num_indices; +} + +void BufferCache::InlineData(VAddr address, const void* value, u32 num_bytes, bool is_gds) { + ASSERT_MSG(address % 4 == 0, "GDS offset must be dword aligned"); + if (!is_gds && !IsRegionRegistered(address, num_bytes)) { + memcpy(std::bit_cast(address), value, num_bytes); + return; + } + scheduler.EndRendering(); + const auto cmdbuf = scheduler.CommandBuffer(); + const Buffer* buffer = [&] { + if (is_gds) { + return &gds_buffer; + } + const BufferId buffer_id = FindBuffer(address, num_bytes); + return &slot_buffers[buffer_id]; + }(); + const vk::BufferMemoryBarrier2 buf_barrier = { + .srcStageMask = vk::PipelineStageFlagBits2::eTransfer, + .srcAccessMask = vk::AccessFlagBits2::eTransferWrite, + .dstStageMask = vk::PipelineStageFlagBits2::eAllCommands, + .dstAccessMask = vk::AccessFlagBits2::eMemoryRead, + .buffer = buffer->Handle(), + .offset = buffer->Offset(address), + .size = num_bytes, + }; + cmdbuf.pipelineBarrier2(vk::DependencyInfo{ + .dependencyFlags = vk::DependencyFlagBits::eByRegion, + .bufferMemoryBarrierCount = 1, + .pBufferMemoryBarriers = &buf_barrier, + }); + cmdbuf.updateBuffer(buffer->Handle(), buf_barrier.offset, num_bytes, value); +} + +std::pair BufferCache::ObtainHostUBO(std::span data) { + static constexpr u64 StreamThreshold = CACHING_PAGESIZE; + ASSERT(data.size_bytes() <= StreamThreshold); + const u64 offset = stream_buffer.Copy(reinterpret_cast(data.data()), data.size_bytes(), + instance.UniformMinAlignment()); + return {&stream_buffer, offset}; +} + +std::pair BufferCache::ObtainBuffer(VAddr device_addr, u32 size, bool is_written, + bool is_texel_buffer, BufferId buffer_id) { + // For small uniform buffers that have not been modified by gpu + // use device local stream buffer to reduce renderpass breaks. + static constexpr u64 StreamThreshold = CACHING_PAGESIZE; + const bool is_gpu_dirty = memory_tracker.IsRegionGpuModified(device_addr, size); + if (!is_written && size <= StreamThreshold && !is_gpu_dirty) { + const u64 offset = stream_buffer.Copy(device_addr, size, instance.UniformMinAlignment()); + return {&stream_buffer, offset}; + } + + if (!buffer_id || slot_buffers[buffer_id].is_deleted) { + buffer_id = FindBuffer(device_addr, size); + } + Buffer& buffer = slot_buffers[buffer_id]; + SynchronizeBuffer(buffer, device_addr, size, is_texel_buffer); + if (is_written) { + memory_tracker.MarkRegionAsGpuModified(device_addr, size); + gpu_modified_ranges.Add(device_addr, size); + } + return {&buffer, buffer.Offset(device_addr)}; +} + +std::pair BufferCache::ObtainViewBuffer(VAddr gpu_addr, u32 size, bool prefer_gpu) { + // Check if any buffer contains the full requested range. + const u64 page = gpu_addr >> CACHING_PAGEBITS; + const BufferId buffer_id = page_table[page]; + if (buffer_id) { + Buffer& buffer = slot_buffers[buffer_id]; + if (buffer.IsInBounds(gpu_addr, size)) { + SynchronizeBuffer(buffer, gpu_addr, size, false); + return {&buffer, buffer.Offset(gpu_addr)}; + } + } + // If no buffer contains the full requested range but some buffer within was GPU-modified, + // fall back to ObtainBuffer to create a full buffer and avoid losing GPU modifications. + // This is only done if the request prefers to use GPU memory, otherwise we can skip it. + if (prefer_gpu && memory_tracker.IsRegionGpuModified(gpu_addr, size)) { + return ObtainBuffer(gpu_addr, size, false, false); + } + // In all other cases, just do a CPU copy to the staging buffer. + const u32 offset = staging_buffer.Copy(gpu_addr, size, 16); + return {&staging_buffer, offset}; +} + +bool BufferCache::IsRegionRegistered(VAddr addr, size_t size) { + const VAddr end_addr = addr + size; + const u64 page_end = Common::DivCeil(end_addr, CACHING_PAGESIZE); + for (u64 page = addr >> CACHING_PAGEBITS; page < page_end;) { + const BufferId buffer_id = page_table[page]; + if (!buffer_id) { + ++page; + continue; + } + Buffer& buffer = slot_buffers[buffer_id]; + const VAddr buf_start_addr = buffer.CpuAddr(); + const VAddr buf_end_addr = buf_start_addr + buffer.SizeBytes(); + if (buf_start_addr < end_addr && addr < buf_end_addr) { + return true; + } + page = Common::DivCeil(buf_end_addr, CACHING_PAGESIZE); + } + return false; +} + +bool BufferCache::IsRegionCpuModified(VAddr addr, size_t size) { + return memory_tracker.IsRegionCpuModified(addr, size); +} + +bool BufferCache::IsRegionGpuModified(VAddr addr, size_t size) { + return memory_tracker.IsRegionGpuModified(addr, size); +} + +BufferId BufferCache::FindBuffer(VAddr device_addr, u32 size) { + if (device_addr == 0) { + return NULL_BUFFER_ID; + } + const u64 page = device_addr >> CACHING_PAGEBITS; + const BufferId buffer_id = page_table[page]; + if (!buffer_id) { + return CreateBuffer(device_addr, size); + } + const Buffer& buffer = slot_buffers[buffer_id]; + if (buffer.IsInBounds(device_addr, size)) { + return buffer_id; + } + return CreateBuffer(device_addr, size); +} + +BufferCache::OverlapResult BufferCache::ResolveOverlaps(VAddr device_addr, u32 wanted_size) { + static constexpr int STREAM_LEAP_THRESHOLD = 16; + boost::container::small_vector overlap_ids; + VAddr begin = device_addr; + VAddr end = device_addr + wanted_size; + int stream_score = 0; + bool has_stream_leap = false; + const auto expand_begin = [&](VAddr add_value) { + static constexpr VAddr min_page = CACHING_PAGESIZE + DEVICE_PAGESIZE; + if (add_value > begin - min_page) { + begin = min_page; + device_addr = DEVICE_PAGESIZE; + return; + } + begin -= add_value; + device_addr = begin - CACHING_PAGESIZE; + }; + const auto expand_end = [&](VAddr add_value) { + static constexpr VAddr max_page = 1ULL << MemoryTracker::MAX_CPU_PAGE_BITS; + if (add_value > max_page - end) { + end = max_page; + return; + } + end += add_value; + }; + if (begin == 0) { + return OverlapResult{ + .ids = std::move(overlap_ids), + .begin = begin, + .end = end, + .has_stream_leap = has_stream_leap, + }; + } + for (; device_addr >> CACHING_PAGEBITS < Common::DivCeil(end, CACHING_PAGESIZE); + device_addr += CACHING_PAGESIZE) { + const BufferId overlap_id = page_table[device_addr >> CACHING_PAGEBITS]; + if (!overlap_id) { + continue; + } + Buffer& overlap = slot_buffers[overlap_id]; + if (overlap.is_picked) { + continue; + } + overlap_ids.push_back(overlap_id); + overlap.is_picked = true; + const VAddr overlap_device_addr = overlap.CpuAddr(); + const bool expands_left = overlap_device_addr < begin; + if (expands_left) { + begin = overlap_device_addr; + } + const VAddr overlap_end = overlap_device_addr + overlap.SizeBytes(); + const bool expands_right = overlap_end > end; + if (overlap_end > end) { + end = overlap_end; + } + stream_score += overlap.StreamScore(); + if (stream_score > STREAM_LEAP_THRESHOLD && !has_stream_leap) { + // When this memory region has been joined a bunch of times, we assume it's being used + // as a stream buffer. Increase the size to skip constantly recreating buffers. + has_stream_leap = true; + if (expands_right) { + expand_begin(CACHING_PAGESIZE * 128); + } + if (expands_left) { + expand_end(CACHING_PAGESIZE * 128); + } + } + } + return OverlapResult{ + .ids = std::move(overlap_ids), + .begin = begin, + .end = end, + .has_stream_leap = has_stream_leap, + }; +} + +void BufferCache::JoinOverlap(BufferId new_buffer_id, BufferId overlap_id, + bool accumulate_stream_score) { + Buffer& new_buffer = slot_buffers[new_buffer_id]; + Buffer& overlap = slot_buffers[overlap_id]; + if (accumulate_stream_score) { + new_buffer.IncreaseStreamScore(overlap.StreamScore() + 1); + } + const size_t dst_base_offset = overlap.CpuAddr() - new_buffer.CpuAddr(); + const vk::BufferCopy copy = { + .srcOffset = 0, + .dstOffset = dst_base_offset, + .size = overlap.SizeBytes(), + }; + scheduler.EndRendering(); + const auto cmdbuf = scheduler.CommandBuffer(); + static constexpr vk::MemoryBarrier READ_BARRIER{ + .srcAccessMask = vk::AccessFlagBits::eMemoryWrite, + .dstAccessMask = vk::AccessFlagBits::eTransferRead | vk::AccessFlagBits::eTransferWrite, + }; + static constexpr vk::MemoryBarrier WRITE_BARRIER{ + .srcAccessMask = vk::AccessFlagBits::eTransferWrite, + .dstAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite, + }; + cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands, + vk::PipelineStageFlagBits::eTransfer, vk::DependencyFlagBits::eByRegion, + READ_BARRIER, {}, {}); + cmdbuf.copyBuffer(overlap.buffer, new_buffer.buffer, copy); + cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, + vk::PipelineStageFlagBits::eAllCommands, + vk::DependencyFlagBits::eByRegion, WRITE_BARRIER, {}, {}); + DeleteBuffer(overlap_id); +} + +BufferId BufferCache::CreateBuffer(VAddr device_addr, u32 wanted_size) { + const VAddr device_addr_end = Common::AlignUp(device_addr + wanted_size, CACHING_PAGESIZE); + device_addr = Common::AlignDown(device_addr, CACHING_PAGESIZE); + wanted_size = static_cast(device_addr_end - device_addr); + const OverlapResult overlap = ResolveOverlaps(device_addr, wanted_size); + const u32 size = static_cast(overlap.end - overlap.begin); + const BufferId new_buffer_id = slot_buffers.insert( + instance, scheduler, MemoryUsage::DeviceLocal, overlap.begin, AllFlags, size); + auto& new_buffer = slot_buffers[new_buffer_id]; + const size_t size_bytes = new_buffer.SizeBytes(); + const auto cmdbuf = scheduler.CommandBuffer(); + scheduler.EndRendering(); + cmdbuf.fillBuffer(new_buffer.buffer, 0, size_bytes, 0); + for (const BufferId overlap_id : overlap.ids) { + JoinOverlap(new_buffer_id, overlap_id, !overlap.has_stream_leap); + } + Register(new_buffer_id); + return new_buffer_id; +} + +void BufferCache::Register(BufferId buffer_id) { + ChangeRegister(buffer_id); +} + +void BufferCache::Unregister(BufferId buffer_id) { + ChangeRegister(buffer_id); +} + +template +void BufferCache::ChangeRegister(BufferId buffer_id) { + Buffer& buffer = slot_buffers[buffer_id]; + const auto size = buffer.SizeBytes(); + const VAddr device_addr_begin = buffer.CpuAddr(); + const VAddr device_addr_end = device_addr_begin + size; + const u64 page_begin = device_addr_begin / CACHING_PAGESIZE; + const u64 page_end = Common::DivCeil(device_addr_end, CACHING_PAGESIZE); + for (u64 page = page_begin; page != page_end; ++page) { + if constexpr (insert) { + page_table[page] = buffer_id; + } else { + page_table[page] = BufferId{}; + } + } +} + +void BufferCache::SynchronizeBuffer(Buffer& buffer, VAddr device_addr, u32 size, + bool is_texel_buffer) { + std::scoped_lock lk{mutex}; + boost::container::small_vector copies; + u64 total_size_bytes = 0; + u64 largest_copy = 0; + VAddr buffer_start = buffer.CpuAddr(); + memory_tracker.ForEachUploadRange(device_addr, size, [&](u64 device_addr_out, u64 range_size) { + copies.push_back(vk::BufferCopy{ + .srcOffset = total_size_bytes, + .dstOffset = device_addr_out - buffer_start, + .size = range_size, + }); + total_size_bytes += range_size; + largest_copy = std::max(largest_copy, range_size); + }); + SCOPE_EXIT { + if (is_texel_buffer) { + SynchronizeBufferFromImage(buffer, device_addr, size); + } + }; + if (total_size_bytes == 0) { + return; + } + vk::Buffer src_buffer = staging_buffer.Handle(); + if (total_size_bytes < StagingBufferSize) { + const auto [staging, offset] = staging_buffer.Map(total_size_bytes); + for (auto& copy : copies) { + u8* const src_pointer = staging + copy.srcOffset; + const VAddr device_addr = buffer.CpuAddr() + copy.dstOffset; + std::memcpy(src_pointer, std::bit_cast(device_addr), copy.size); + // Apply the staging offset + copy.srcOffset += offset; + } + staging_buffer.Commit(); + } else { + // For large one time transfers use a temporary host buffer. + // RenderDoc can lag quite a bit if the stream buffer is too large. + Buffer temp_buffer{instance, + scheduler, + MemoryUsage::Upload, + 0, + vk::BufferUsageFlagBits::eTransferSrc, + total_size_bytes}; + src_buffer = temp_buffer.Handle(); + u8* const staging = temp_buffer.mapped_data.data(); + for (auto& copy : copies) { + u8* const src_pointer = staging + copy.srcOffset; + const VAddr device_addr = buffer.CpuAddr() + copy.dstOffset; + std::memcpy(src_pointer, std::bit_cast(device_addr), copy.size); + } + scheduler.DeferOperation([buffer = std::move(temp_buffer)]() mutable {}); + } + scheduler.EndRendering(); + const auto cmdbuf = scheduler.CommandBuffer(); + static constexpr vk::MemoryBarrier READ_BARRIER{ + .srcAccessMask = vk::AccessFlagBits::eMemoryWrite, + .dstAccessMask = vk::AccessFlagBits::eTransferRead | vk::AccessFlagBits::eTransferWrite, + }; + static constexpr vk::MemoryBarrier WRITE_BARRIER{ + .srcAccessMask = vk::AccessFlagBits::eTransferWrite, + .dstAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite, + }; + cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands, + vk::PipelineStageFlagBits::eTransfer, vk::DependencyFlagBits::eByRegion, + READ_BARRIER, {}, {}); + cmdbuf.copyBuffer(src_buffer, buffer.buffer, copies); + cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, + vk::PipelineStageFlagBits::eAllCommands, + vk::DependencyFlagBits::eByRegion, WRITE_BARRIER, {}, {}); +} + +bool BufferCache::SynchronizeBufferFromImage(Buffer& buffer, VAddr device_addr, u32 size) { + static constexpr FindFlags find_flags = + FindFlags::NoCreate | FindFlags::RelaxDim | FindFlags::RelaxFmt | FindFlags::RelaxSize; + TextureCache::BaseDesc desc{}; + desc.info.guest_address = device_addr; + desc.info.guest_size_bytes = size; + const ImageId image_id = texture_cache.FindImage(desc, find_flags); + if (!image_id) { + return false; + } + Image& image = texture_cache.GetImage(image_id); + if (False(image.flags & ImageFlagBits::GpuModified)) { + return false; + } + ASSERT_MSG(device_addr == image.info.guest_address, + "Texel buffer aliases image subresources {:x} : {:x}", device_addr, + image.info.guest_address); + boost::container::small_vector copies; + u32 offset = buffer.Offset(image.info.guest_address); + const u32 num_layers = image.info.resources.layers; + const u32 max_offset = offset + size; + for (u32 m = 0; m < image.info.resources.levels; m++) { + const u32 width = std::max(image.info.size.width >> m, 1u); + const u32 height = std::max(image.info.size.height >> m, 1u); + const u32 depth = + image.info.props.is_volume ? std::max(image.info.size.depth >> m, 1u) : 1u; + const auto& [mip_size, mip_pitch, mip_height, mip_ofs] = image.info.mips_layout[m]; + offset += mip_ofs * num_layers; + if (offset + (mip_size * num_layers) > max_offset) { + break; + } + copies.push_back({ + .bufferOffset = offset, + .bufferRowLength = static_cast(mip_pitch), + .bufferImageHeight = static_cast(mip_height), + .imageSubresource{ + .aspectMask = image.aspect_mask & ~vk::ImageAspectFlagBits::eStencil, + .mipLevel = m, + .baseArrayLayer = 0, + .layerCount = num_layers, + }, + .imageOffset = {0, 0, 0}, + .imageExtent = {width, height, depth}, + }); + } + if (!copies.empty()) { + scheduler.EndRendering(); + image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, {}); + const auto cmdbuf = scheduler.CommandBuffer(); + cmdbuf.copyImageToBuffer(image.image, vk::ImageLayout::eTransferSrcOptimal, buffer.buffer, + copies); + } + return true; +} + +void BufferCache::DeleteBuffer(BufferId buffer_id) { + Buffer& buffer = slot_buffers[buffer_id]; + Unregister(buffer_id); + scheduler.DeferOperation([this, buffer_id] { slot_buffers.erase(buffer_id); }); + buffer.is_deleted = true; +} + +} // namespace VideoCore diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index e62913413..bcbaa45dc 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -1,168 +1,168 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include -#include -#include "common/div_ceil.h" -#include "common/slot_vector.h" -#include "common/types.h" -#include "video_core/buffer_cache/buffer.h" -#include "video_core/buffer_cache/memory_tracker_base.h" -#include "video_core/buffer_cache/range_set.h" -#include "video_core/multi_level_page_table.h" - -namespace AmdGpu { -struct Liverpool; -} - -namespace Shader { -namespace Gcn { -struct FetchShaderData; -} -struct Info; -} // namespace Shader - -namespace VideoCore { - -using BufferId = Common::SlotId; - -static constexpr BufferId NULL_BUFFER_ID{0}; - -class TextureCache; - -class BufferCache { -public: - static constexpr u32 CACHING_PAGEBITS = 12; - static constexpr u64 CACHING_PAGESIZE = u64{1} << CACHING_PAGEBITS; - static constexpr u64 DEVICE_PAGESIZE = 4_KB; - - struct Traits { - using Entry = BufferId; - static constexpr size_t AddressSpaceBits = 40; - static constexpr size_t FirstLevelBits = 14; - static constexpr size_t PageBits = CACHING_PAGEBITS; - }; - using PageTable = MultiLevelPageTable; - - struct OverlapResult { - boost::container::small_vector ids; - VAddr begin; - VAddr end; - bool has_stream_leap = false; - }; - -public: - explicit BufferCache(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler, - AmdGpu::Liverpool* liverpool, TextureCache& texture_cache, - PageManager& tracker); - ~BufferCache(); - - /// Returns a pointer to GDS device local buffer. - [[nodiscard]] const Buffer* GetGdsBuffer() const noexcept { - return &gds_buffer; - } - - /// Retrieves the buffer with the specified id. - [[nodiscard]] Buffer& GetBuffer(BufferId id) { - return slot_buffers[id]; - } - - [[nodiscard]] vk::BufferView& NullBufferView() { - return null_buffer_view; - } - - /// Invalidates any buffer in the logical page range. - void InvalidateMemory(VAddr device_addr, u64 size); - - /// Binds host vertex buffers for the current draw. - bool BindVertexBuffers(const Shader::Info& vs_info, - const std::optional& fetch_shader); - - /// Bind host index buffer for the current draw. - u32 BindIndexBuffer(bool& is_indexed, u32 index_offset); - - /// Writes a value to GPU buffer. - void InlineData(VAddr address, const void* value, u32 num_bytes, bool is_gds); - - [[nodiscard]] std::pair ObtainHostUBO(std::span data); - - /// Obtains a buffer for the specified region. - [[nodiscard]] std::pair ObtainBuffer(VAddr gpu_addr, u32 size, bool is_written, - bool is_texel_buffer = false, - BufferId buffer_id = {}); - - /// Attempts to obtain a buffer without modifying the cache contents. - [[nodiscard]] std::pair ObtainViewBuffer(VAddr gpu_addr, u32 size, - bool prefer_gpu); - - /// Return true when a region is registered on the cache - [[nodiscard]] bool IsRegionRegistered(VAddr addr, size_t size); - - /// Return true when a CPU region is modified from the CPU - [[nodiscard]] bool IsRegionCpuModified(VAddr addr, size_t size); - - /// Return true when a CPU region is modified from the GPU - [[nodiscard]] bool IsRegionGpuModified(VAddr addr, size_t size); - - [[nodiscard]] BufferId FindBuffer(VAddr device_addr, u32 size); - -private: - template - void ForEachBufferInRange(VAddr device_addr, u64 size, Func&& func) { - const u64 page_end = Common::DivCeil(device_addr + size, CACHING_PAGESIZE); - for (u64 page = device_addr >> CACHING_PAGEBITS; page < page_end;) { - const BufferId buffer_id = page_table[page]; - if (!buffer_id) { - ++page; - continue; - } - Buffer& buffer = slot_buffers[buffer_id]; - func(buffer_id, buffer); - - const VAddr end_addr = buffer.CpuAddr() + buffer.SizeBytes(); - page = Common::DivCeil(end_addr, CACHING_PAGESIZE); - } - } - - void DownloadBufferMemory(Buffer& buffer, VAddr device_addr, u64 size); - - [[nodiscard]] OverlapResult ResolveOverlaps(VAddr device_addr, u32 wanted_size); - - void JoinOverlap(BufferId new_buffer_id, BufferId overlap_id, bool accumulate_stream_score); - - [[nodiscard]] BufferId CreateBuffer(VAddr device_addr, u32 wanted_size); - - void Register(BufferId buffer_id); - - void Unregister(BufferId buffer_id); - - template - void ChangeRegister(BufferId buffer_id); - - void SynchronizeBuffer(Buffer& buffer, VAddr device_addr, u32 size, bool is_texel_buffer); - - bool SynchronizeBufferFromImage(Buffer& buffer, VAddr device_addr, u32 size); - - void DeleteBuffer(BufferId buffer_id); - - const Vulkan::Instance& instance; - Vulkan::Scheduler& scheduler; - AmdGpu::Liverpool* liverpool; - TextureCache& texture_cache; - PageManager& tracker; - StreamBuffer staging_buffer; - StreamBuffer stream_buffer; - Buffer gds_buffer; - std::mutex mutex; - Common::SlotVector slot_buffers; - RangeSet gpu_modified_ranges; - vk::BufferView null_buffer_view; - MemoryTracker memory_tracker; - PageTable page_table; -}; - -} // namespace VideoCore +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include "common/div_ceil.h" +#include "common/slot_vector.h" +#include "common/types.h" +#include "video_core/buffer_cache/buffer.h" +#include "video_core/buffer_cache/memory_tracker_base.h" +#include "video_core/buffer_cache/range_set.h" +#include "video_core/multi_level_page_table.h" + +namespace AmdGpu { +struct Liverpool; +} + +namespace Shader { +namespace Gcn { +struct FetchShaderData; +} +struct Info; +} // namespace Shader + +namespace VideoCore { + +using BufferId = Common::SlotId; + +static constexpr BufferId NULL_BUFFER_ID{0}; + +class TextureCache; + +class BufferCache { +public: + static constexpr u32 CACHING_PAGEBITS = 12; + static constexpr u64 CACHING_PAGESIZE = u64{1} << CACHING_PAGEBITS; + static constexpr u64 DEVICE_PAGESIZE = 4_KB; + + struct Traits { + using Entry = BufferId; + static constexpr size_t AddressSpaceBits = 40; + static constexpr size_t FirstLevelBits = 14; + static constexpr size_t PageBits = CACHING_PAGEBITS; + }; + using PageTable = MultiLevelPageTable; + + struct OverlapResult { + boost::container::small_vector ids; + VAddr begin; + VAddr end; + bool has_stream_leap = false; + }; + +public: + explicit BufferCache(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler, + AmdGpu::Liverpool* liverpool, TextureCache& texture_cache, + PageManager& tracker); + ~BufferCache(); + + /// Returns a pointer to GDS device local buffer. + [[nodiscard]] const Buffer* GetGdsBuffer() const noexcept { + return &gds_buffer; + } + + /// Retrieves the buffer with the specified id. + [[nodiscard]] Buffer& GetBuffer(BufferId id) { + return slot_buffers[id]; + } + + [[nodiscard]] vk::BufferView& NullBufferView() { + return null_buffer_view; + } + + /// Invalidates any buffer in the logical page range. + void InvalidateMemory(VAddr device_addr, u64 size); + + /// Binds host vertex buffers for the current draw. + bool BindVertexBuffers(const Shader::Info& vs_info, + const std::optional& fetch_shader); + + /// Bind host index buffer for the current draw. + u32 BindIndexBuffer(bool& is_indexed, u32 index_offset); + + /// Writes a value to GPU buffer. + void InlineData(VAddr address, const void* value, u32 num_bytes, bool is_gds); + + [[nodiscard]] std::pair ObtainHostUBO(std::span data); + + /// Obtains a buffer for the specified region. + [[nodiscard]] std::pair ObtainBuffer(VAddr gpu_addr, u32 size, bool is_written, + bool is_texel_buffer = false, + BufferId buffer_id = {}); + + /// Attempts to obtain a buffer without modifying the cache contents. + [[nodiscard]] std::pair ObtainViewBuffer(VAddr gpu_addr, u32 size, + bool prefer_gpu); + + /// Return true when a region is registered on the cache + [[nodiscard]] bool IsRegionRegistered(VAddr addr, size_t size); + + /// Return true when a CPU region is modified from the CPU + [[nodiscard]] bool IsRegionCpuModified(VAddr addr, size_t size); + + /// Return true when a CPU region is modified from the GPU + [[nodiscard]] bool IsRegionGpuModified(VAddr addr, size_t size); + + [[nodiscard]] BufferId FindBuffer(VAddr device_addr, u32 size); + +private: + template + void ForEachBufferInRange(VAddr device_addr, u64 size, Func&& func) { + const u64 page_end = Common::DivCeil(device_addr + size, CACHING_PAGESIZE); + for (u64 page = device_addr >> CACHING_PAGEBITS; page < page_end;) { + const BufferId buffer_id = page_table[page]; + if (!buffer_id) { + ++page; + continue; + } + Buffer& buffer = slot_buffers[buffer_id]; + func(buffer_id, buffer); + + const VAddr end_addr = buffer.CpuAddr() + buffer.SizeBytes(); + page = Common::DivCeil(end_addr, CACHING_PAGESIZE); + } + } + + void DownloadBufferMemory(Buffer& buffer, VAddr device_addr, u64 size); + + [[nodiscard]] OverlapResult ResolveOverlaps(VAddr device_addr, u32 wanted_size); + + void JoinOverlap(BufferId new_buffer_id, BufferId overlap_id, bool accumulate_stream_score); + + [[nodiscard]] BufferId CreateBuffer(VAddr device_addr, u32 wanted_size); + + void Register(BufferId buffer_id); + + void Unregister(BufferId buffer_id); + + template + void ChangeRegister(BufferId buffer_id); + + void SynchronizeBuffer(Buffer& buffer, VAddr device_addr, u32 size, bool is_texel_buffer); + + bool SynchronizeBufferFromImage(Buffer& buffer, VAddr device_addr, u32 size); + + void DeleteBuffer(BufferId buffer_id); + + const Vulkan::Instance& instance; + Vulkan::Scheduler& scheduler; + AmdGpu::Liverpool* liverpool; + TextureCache& texture_cache; + PageManager& tracker; + StreamBuffer staging_buffer; + StreamBuffer stream_buffer; + Buffer gds_buffer; + std::mutex mutex; + Common::SlotVector slot_buffers; + RangeSet gpu_modified_ranges; + vk::BufferView null_buffer_view; + MemoryTracker memory_tracker; + PageTable page_table; +}; + +} // namespace VideoCore diff --git a/src/video_core/buffer_cache/memory_tracker_base.h b/src/video_core/buffer_cache/memory_tracker_base.h index a59bcfff5..ae61b55f2 100644 --- a/src/video_core/buffer_cache/memory_tracker_base.h +++ b/src/video_core/buffer_cache/memory_tracker_base.h @@ -1,175 +1,175 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include -#include -#include "common/types.h" -#include "video_core/buffer_cache/word_manager.h" - -namespace VideoCore { - -class MemoryTracker { -public: - static constexpr size_t MAX_CPU_PAGE_BITS = 40; - static constexpr size_t HIGHER_PAGE_BITS = 22; - static constexpr size_t HIGHER_PAGE_SIZE = 1ULL << HIGHER_PAGE_BITS; - static constexpr size_t HIGHER_PAGE_MASK = HIGHER_PAGE_SIZE - 1ULL; - static constexpr size_t NUM_HIGH_PAGES = 1ULL << (MAX_CPU_PAGE_BITS - HIGHER_PAGE_BITS); - static constexpr size_t MANAGER_POOL_SIZE = 32; - static constexpr size_t WORDS_STACK_NEEDED = HIGHER_PAGE_SIZE / BYTES_PER_WORD; - using Manager = WordManager; - -public: - explicit MemoryTracker(PageManager* tracker_) : tracker{tracker_} {} - ~MemoryTracker() = default; - - /// Returns true if a region has been modified from the CPU - [[nodiscard]] bool IsRegionCpuModified(VAddr query_cpu_addr, u64 query_size) noexcept { - return IteratePages( - query_cpu_addr, query_size, [](Manager* manager, u64 offset, size_t size) { - return manager->template IsRegionModified(offset, size); - }); - } - - /// Returns true if a region has been modified from the GPU - [[nodiscard]] bool IsRegionGpuModified(VAddr query_cpu_addr, u64 query_size) noexcept { - return IteratePages( - query_cpu_addr, query_size, [](Manager* manager, u64 offset, size_t size) { - return manager->template IsRegionModified(offset, size); - }); - } - - /// Mark region as CPU modified, notifying the device_tracker about this change - void MarkRegionAsCpuModified(VAddr dirty_cpu_addr, u64 query_size) { - IteratePages(dirty_cpu_addr, query_size, - [](Manager* manager, u64 offset, size_t size) { - manager->template ChangeRegionState( - manager->GetCpuAddr() + offset, size); - }); - } - - /// Unmark region as CPU modified, notifying the device_tracker about this change - void UnmarkRegionAsCpuModified(VAddr dirty_cpu_addr, u64 query_size) { - IteratePages(dirty_cpu_addr, query_size, - [](Manager* manager, u64 offset, size_t size) { - manager->template ChangeRegionState( - manager->GetCpuAddr() + offset, size); - }); - } - - /// Mark region as modified from the host GPU - void MarkRegionAsGpuModified(VAddr dirty_cpu_addr, u64 query_size) noexcept { - IteratePages(dirty_cpu_addr, query_size, - [](Manager* manager, u64 offset, size_t size) { - manager->template ChangeRegionState( - manager->GetCpuAddr() + offset, size); - }); - } - - /// Unmark region as modified from the host GPU - void UnmarkRegionAsGpuModified(VAddr dirty_cpu_addr, u64 query_size) noexcept { - IteratePages(dirty_cpu_addr, query_size, - [](Manager* manager, u64 offset, size_t size) { - manager->template ChangeRegionState( - manager->GetCpuAddr() + offset, size); - }); - } - - /// Call 'func' for each CPU modified range and unmark those pages as CPU modified - template - void ForEachUploadRange(VAddr query_cpu_range, u64 query_size, Func&& func) { - IteratePages(query_cpu_range, query_size, - [&func](Manager* manager, u64 offset, size_t size) { - manager->template ForEachModifiedRange( - manager->GetCpuAddr() + offset, size, func); - }); - } - - /// Call 'func' for each GPU modified range and unmark those pages as GPU modified - template - void ForEachDownloadRange(VAddr query_cpu_range, u64 query_size, Func&& func) { - IteratePages(query_cpu_range, query_size, - [&func](Manager* manager, u64 offset, size_t size) { - if constexpr (clear) { - manager->template ForEachModifiedRange( - manager->GetCpuAddr() + offset, size, func); - } else { - manager->template ForEachModifiedRange( - manager->GetCpuAddr() + offset, size, func); - } - }); - } - -private: - /** - * @brief IteratePages Iterates L2 word manager page table. - * @param cpu_address Start byte cpu address - * @param size Size in bytes of the region of iterate. - * @param func Callback for each word manager. - * @return - */ - template - bool IteratePages(VAddr cpu_address, size_t size, Func&& func) { - using FuncReturn = typename std::invoke_result::type; - static constexpr bool BOOL_BREAK = std::is_same_v; - std::size_t remaining_size{size}; - std::size_t page_index{cpu_address >> HIGHER_PAGE_BITS}; - u64 page_offset{cpu_address & HIGHER_PAGE_MASK}; - while (remaining_size > 0) { - const std::size_t copy_amount{ - std::min(HIGHER_PAGE_SIZE - page_offset, remaining_size)}; - auto* manager{top_tier[page_index]}; - if (manager) { - if constexpr (BOOL_BREAK) { - if (func(manager, page_offset, copy_amount)) { - return true; - } - } else { - func(manager, page_offset, copy_amount); - } - } else if constexpr (create_region_on_fail) { - CreateRegion(page_index); - manager = top_tier[page_index]; - if constexpr (BOOL_BREAK) { - if (func(manager, page_offset, copy_amount)) { - return true; - } - } else { - func(manager, page_offset, copy_amount); - } - } - page_index++; - page_offset = 0; - remaining_size -= copy_amount; - } - return false; - } - - void CreateRegion(std::size_t page_index) { - const VAddr base_cpu_addr = page_index << HIGHER_PAGE_BITS; - if (free_managers.empty()) { - manager_pool.emplace_back(); - auto& last_pool = manager_pool.back(); - for (size_t i = 0; i < MANAGER_POOL_SIZE; i++) { - std::construct_at(&last_pool[i], tracker, 0, HIGHER_PAGE_SIZE); - free_managers.push_back(&last_pool[i]); - } - } - // Each manager tracks a 4_MB virtual address space. - auto* new_manager = free_managers.back(); - new_manager->SetCpuAddress(base_cpu_addr); - free_managers.pop_back(); - top_tier[page_index] = new_manager; - } - - PageManager* tracker; - std::deque> manager_pool; - std::vector free_managers; - std::array top_tier{}; -}; - -} // namespace VideoCore +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include "common/types.h" +#include "video_core/buffer_cache/word_manager.h" + +namespace VideoCore { + +class MemoryTracker { +public: + static constexpr size_t MAX_CPU_PAGE_BITS = 40; + static constexpr size_t HIGHER_PAGE_BITS = 22; + static constexpr size_t HIGHER_PAGE_SIZE = 1ULL << HIGHER_PAGE_BITS; + static constexpr size_t HIGHER_PAGE_MASK = HIGHER_PAGE_SIZE - 1ULL; + static constexpr size_t NUM_HIGH_PAGES = 1ULL << (MAX_CPU_PAGE_BITS - HIGHER_PAGE_BITS); + static constexpr size_t MANAGER_POOL_SIZE = 32; + static constexpr size_t WORDS_STACK_NEEDED = HIGHER_PAGE_SIZE / BYTES_PER_WORD; + using Manager = WordManager; + +public: + explicit MemoryTracker(PageManager* tracker_) : tracker{tracker_} {} + ~MemoryTracker() = default; + + /// Returns true if a region has been modified from the CPU + [[nodiscard]] bool IsRegionCpuModified(VAddr query_cpu_addr, u64 query_size) noexcept { + return IteratePages( + query_cpu_addr, query_size, [](Manager* manager, u64 offset, size_t size) { + return manager->template IsRegionModified(offset, size); + }); + } + + /// Returns true if a region has been modified from the GPU + [[nodiscard]] bool IsRegionGpuModified(VAddr query_cpu_addr, u64 query_size) noexcept { + return IteratePages( + query_cpu_addr, query_size, [](Manager* manager, u64 offset, size_t size) { + return manager->template IsRegionModified(offset, size); + }); + } + + /// Mark region as CPU modified, notifying the device_tracker about this change + void MarkRegionAsCpuModified(VAddr dirty_cpu_addr, u64 query_size) { + IteratePages(dirty_cpu_addr, query_size, + [](Manager* manager, u64 offset, size_t size) { + manager->template ChangeRegionState( + manager->GetCpuAddr() + offset, size); + }); + } + + /// Unmark region as CPU modified, notifying the device_tracker about this change + void UnmarkRegionAsCpuModified(VAddr dirty_cpu_addr, u64 query_size) { + IteratePages(dirty_cpu_addr, query_size, + [](Manager* manager, u64 offset, size_t size) { + manager->template ChangeRegionState( + manager->GetCpuAddr() + offset, size); + }); + } + + /// Mark region as modified from the host GPU + void MarkRegionAsGpuModified(VAddr dirty_cpu_addr, u64 query_size) noexcept { + IteratePages(dirty_cpu_addr, query_size, + [](Manager* manager, u64 offset, size_t size) { + manager->template ChangeRegionState( + manager->GetCpuAddr() + offset, size); + }); + } + + /// Unmark region as modified from the host GPU + void UnmarkRegionAsGpuModified(VAddr dirty_cpu_addr, u64 query_size) noexcept { + IteratePages(dirty_cpu_addr, query_size, + [](Manager* manager, u64 offset, size_t size) { + manager->template ChangeRegionState( + manager->GetCpuAddr() + offset, size); + }); + } + + /// Call 'func' for each CPU modified range and unmark those pages as CPU modified + template + void ForEachUploadRange(VAddr query_cpu_range, u64 query_size, Func&& func) { + IteratePages(query_cpu_range, query_size, + [&func](Manager* manager, u64 offset, size_t size) { + manager->template ForEachModifiedRange( + manager->GetCpuAddr() + offset, size, func); + }); + } + + /// Call 'func' for each GPU modified range and unmark those pages as GPU modified + template + void ForEachDownloadRange(VAddr query_cpu_range, u64 query_size, Func&& func) { + IteratePages(query_cpu_range, query_size, + [&func](Manager* manager, u64 offset, size_t size) { + if constexpr (clear) { + manager->template ForEachModifiedRange( + manager->GetCpuAddr() + offset, size, func); + } else { + manager->template ForEachModifiedRange( + manager->GetCpuAddr() + offset, size, func); + } + }); + } + +private: + /** + * @brief IteratePages Iterates L2 word manager page table. + * @param cpu_address Start byte cpu address + * @param size Size in bytes of the region of iterate. + * @param func Callback for each word manager. + * @return + */ + template + bool IteratePages(VAddr cpu_address, size_t size, Func&& func) { + using FuncReturn = typename std::invoke_result::type; + static constexpr bool BOOL_BREAK = std::is_same_v; + std::size_t remaining_size{size}; + std::size_t page_index{cpu_address >> HIGHER_PAGE_BITS}; + u64 page_offset{cpu_address & HIGHER_PAGE_MASK}; + while (remaining_size > 0) { + const std::size_t copy_amount{ + std::min(HIGHER_PAGE_SIZE - page_offset, remaining_size)}; + auto* manager{top_tier[page_index]}; + if (manager) { + if constexpr (BOOL_BREAK) { + if (func(manager, page_offset, copy_amount)) { + return true; + } + } else { + func(manager, page_offset, copy_amount); + } + } else if constexpr (create_region_on_fail) { + CreateRegion(page_index); + manager = top_tier[page_index]; + if constexpr (BOOL_BREAK) { + if (func(manager, page_offset, copy_amount)) { + return true; + } + } else { + func(manager, page_offset, copy_amount); + } + } + page_index++; + page_offset = 0; + remaining_size -= copy_amount; + } + return false; + } + + void CreateRegion(std::size_t page_index) { + const VAddr base_cpu_addr = page_index << HIGHER_PAGE_BITS; + if (free_managers.empty()) { + manager_pool.emplace_back(); + auto& last_pool = manager_pool.back(); + for (size_t i = 0; i < MANAGER_POOL_SIZE; i++) { + std::construct_at(&last_pool[i], tracker, 0, HIGHER_PAGE_SIZE); + free_managers.push_back(&last_pool[i]); + } + } + // Each manager tracks a 4_MB virtual address space. + auto* new_manager = free_managers.back(); + new_manager->SetCpuAddress(base_cpu_addr); + free_managers.pop_back(); + top_tier[page_index] = new_manager; + } + + PageManager* tracker; + std::deque> manager_pool; + std::vector free_managers; + std::array top_tier{}; +}; + +} // namespace VideoCore diff --git a/src/video_core/buffer_cache/word_manager.h b/src/video_core/buffer_cache/word_manager.h index 549d2a9ed..ae85d1eb1 100644 --- a/src/video_core/buffer_cache/word_manager.h +++ b/src/video_core/buffer_cache/word_manager.h @@ -1,398 +1,398 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include -#include "common/div_ceil.h" -#include "common/types.h" -#include "video_core/page_manager.h" - -namespace VideoCore { - -constexpr u64 PAGES_PER_WORD = 64; -constexpr u64 BYTES_PER_PAGE = 4_KB; -constexpr u64 BYTES_PER_WORD = PAGES_PER_WORD * BYTES_PER_PAGE; - -enum class Type { - CPU, - GPU, - Untracked, -}; - -/// Vector tracking modified pages tightly packed with small vector optimization -template -struct WordsArray { - /// Returns the pointer to the words state - [[nodiscard]] const u64* Pointer(bool is_short) const noexcept { - return is_short ? stack.data() : heap; - } - - /// Returns the pointer to the words state - [[nodiscard]] u64* Pointer(bool is_short) noexcept { - return is_short ? stack.data() : heap; - } - - std::array stack{}; ///< Small buffers storage - u64* heap; ///< Not-small buffers pointer to the storage -}; - -template -struct Words { - explicit Words() = default; - explicit Words(u64 size_bytes_) : size_bytes{size_bytes_} { - num_words = Common::DivCeil(size_bytes, BYTES_PER_WORD); - if (IsShort()) { - cpu.stack.fill(~u64{0}); - gpu.stack.fill(0); - untracked.stack.fill(~u64{0}); - } else { - // Share allocation between CPU and GPU pages and set their default values - u64* const alloc = new u64[num_words * 3]; - cpu.heap = alloc; - gpu.heap = alloc + num_words; - untracked.heap = alloc + num_words * 2; - std::fill_n(cpu.heap, num_words, ~u64{0}); - std::fill_n(gpu.heap, num_words, 0); - std::fill_n(untracked.heap, num_words, ~u64{0}); - } - // Clean up tailing bits - const u64 last_word_size = size_bytes % BYTES_PER_WORD; - const u64 last_local_page = Common::DivCeil(last_word_size, BYTES_PER_PAGE); - const u64 shift = (PAGES_PER_WORD - last_local_page) % PAGES_PER_WORD; - const u64 last_word = (~u64{0} << shift) >> shift; - cpu.Pointer(IsShort())[NumWords() - 1] = last_word; - untracked.Pointer(IsShort())[NumWords() - 1] = last_word; - } - - ~Words() { - Release(); - } - - Words& operator=(Words&& rhs) noexcept { - Release(); - size_bytes = rhs.size_bytes; - num_words = rhs.num_words; - cpu = rhs.cpu; - gpu = rhs.gpu; - untracked = rhs.untracked; - rhs.cpu.heap = nullptr; - return *this; - } - - Words(Words&& rhs) noexcept - : size_bytes{rhs.size_bytes}, num_words{rhs.num_words}, cpu{rhs.cpu}, gpu{rhs.gpu}, - untracked{rhs.untracked} { - rhs.cpu.heap = nullptr; - } - - Words& operator=(const Words&) = delete; - Words(const Words&) = delete; - - /// Returns true when the buffer fits in the small vector optimization - [[nodiscard]] bool IsShort() const noexcept { - return num_words <= stack_words; - } - - /// Returns the number of words of the buffer - [[nodiscard]] size_t NumWords() const noexcept { - return num_words; - } - - /// Release buffer resources - void Release() { - if (!IsShort()) { - // CPU written words is the base for the heap allocation - delete[] cpu.heap; - } - } - - template - std::span Span() noexcept { - if constexpr (type == Type::CPU) { - return std::span(cpu.Pointer(IsShort()), num_words); - } else if constexpr (type == Type::GPU) { - return std::span(gpu.Pointer(IsShort()), num_words); - } else if constexpr (type == Type::Untracked) { - return std::span(untracked.Pointer(IsShort()), num_words); - } - } - - template - std::span Span() const noexcept { - if constexpr (type == Type::CPU) { - return std::span(cpu.Pointer(IsShort()), num_words); - } else if constexpr (type == Type::GPU) { - return std::span(gpu.Pointer(IsShort()), num_words); - } else if constexpr (type == Type::Untracked) { - return std::span(untracked.Pointer(IsShort()), num_words); - } - } - - u64 size_bytes = 0; - size_t num_words = 0; - WordsArray cpu; - WordsArray gpu; - WordsArray untracked; -}; - -template -class WordManager { -public: - explicit WordManager(PageManager* tracker_, VAddr cpu_addr_, u64 size_bytes) - : tracker{tracker_}, cpu_addr{cpu_addr_}, words{size_bytes} {} - - explicit WordManager() = default; - - void SetCpuAddress(VAddr new_cpu_addr) { - cpu_addr = new_cpu_addr; - } - - VAddr GetCpuAddr() const { - return cpu_addr; - } - - static u64 ExtractBits(u64 word, size_t page_start, size_t page_end) { - constexpr size_t number_bits = sizeof(u64) * 8; - const size_t limit_page_end = number_bits - std::min(page_end, number_bits); - u64 bits = (word >> page_start) << page_start; - bits = (bits << limit_page_end) >> limit_page_end; - return bits; - } - - static std::pair GetWordPage(VAddr address) { - const size_t converted_address = static_cast(address); - const size_t word_number = converted_address / BYTES_PER_WORD; - const size_t amount_pages = converted_address % BYTES_PER_WORD; - return std::make_pair(word_number, amount_pages / BYTES_PER_PAGE); - } - - template - void IterateWords(size_t offset, size_t size, Func&& func) const { - using FuncReturn = std::invoke_result_t; - static constexpr bool BOOL_BREAK = std::is_same_v; - const size_t start = static_cast(std::max(static_cast(offset), 0LL)); - const size_t end = static_cast(std::max(static_cast(offset + size), 0LL)); - if (start >= SizeBytes() || end <= start) { - return; - } - auto [start_word, start_page] = GetWordPage(start); - auto [end_word, end_page] = GetWordPage(end + BYTES_PER_PAGE - 1ULL); - const size_t num_words = NumWords(); - start_word = std::min(start_word, num_words); - end_word = std::min(end_word, num_words); - const size_t diff = end_word - start_word; - end_word += (end_page + PAGES_PER_WORD - 1ULL) / PAGES_PER_WORD; - end_word = std::min(end_word, num_words); - end_page += diff * PAGES_PER_WORD; - constexpr u64 base_mask{~0ULL}; - for (size_t word_index = start_word; word_index < end_word; word_index++) { - const u64 mask = ExtractBits(base_mask, start_page, end_page); - start_page = 0; - end_page -= PAGES_PER_WORD; - if constexpr (BOOL_BREAK) { - if (func(word_index, mask)) { - return; - } - } else { - func(word_index, mask); - } - } - } - - template - void IteratePages(u64 mask, Func&& func) const { - size_t offset = 0; - while (mask != 0) { - const size_t empty_bits = std::countr_zero(mask); - offset += empty_bits; - mask = mask >> empty_bits; - - const size_t continuous_bits = std::countr_one(mask); - func(offset, continuous_bits); - mask = continuous_bits < PAGES_PER_WORD ? (mask >> continuous_bits) : 0; - offset += continuous_bits; - } - } - - /** - * Change the state of a range of pages - * - * @param dirty_addr Base address to mark or unmark as modified - * @param size Size in bytes to mark or unmark as modified - */ - template - void ChangeRegionState(u64 dirty_addr, u64 size) noexcept(type == Type::GPU) { - std::span state_words = words.template Span(); - [[maybe_unused]] std::span untracked_words = words.template Span(); - IterateWords(dirty_addr - cpu_addr, size, [&](size_t index, u64 mask) { - if constexpr (type == Type::CPU) { - NotifyPageTracker(index, untracked_words[index], mask); - } - if constexpr (enable) { - state_words[index] |= mask; - if constexpr (type == Type::CPU) { - untracked_words[index] |= mask; - } - } else { - state_words[index] &= ~mask; - if constexpr (type == Type::CPU) { - untracked_words[index] &= ~mask; - } - } - }); - } - - /** - * Loop over each page in the given range, turn off those bits and notify the tracker if - * needed. Call the given function on each turned off range. - * - * @param query_cpu_range Base CPU address to loop over - * @param size Size in bytes of the CPU range to loop over - * @param func Function to call for each turned off region - */ - template - void ForEachModifiedRange(VAddr query_cpu_range, s64 size, Func&& func) { - static_assert(type != Type::Untracked); - - std::span state_words = words.template Span(); - [[maybe_unused]] std::span untracked_words = words.template Span(); - const size_t offset = query_cpu_range - cpu_addr; - bool pending = false; - size_t pending_offset{}; - size_t pending_pointer{}; - const auto release = [&]() { - func(cpu_addr + pending_offset * BYTES_PER_PAGE, - (pending_pointer - pending_offset) * BYTES_PER_PAGE); - }; - IterateWords(offset, size, [&](size_t index, u64 mask) { - if constexpr (type == Type::GPU) { - mask &= ~untracked_words[index]; - } - const u64 word = state_words[index] & mask; - if constexpr (clear) { - if constexpr (type == Type::CPU) { - NotifyPageTracker(index, untracked_words[index], mask); - } - state_words[index] &= ~mask; - if constexpr (type == Type::CPU) { - untracked_words[index] &= ~mask; - } - } - const size_t base_offset = index * PAGES_PER_WORD; - IteratePages(word, [&](size_t pages_offset, size_t pages_size) { - const auto reset = [&]() { - pending_offset = base_offset + pages_offset; - pending_pointer = base_offset + pages_offset + pages_size; - }; - if (!pending) { - reset(); - pending = true; - return; - } - if (pending_pointer == base_offset + pages_offset) { - pending_pointer += pages_size; - return; - } - release(); - reset(); - }); - }); - if (pending) { - release(); - } - } - - /** - * Returns true when a region has been modified - * - * @param offset Offset in bytes from the start of the buffer - * @param size Size in bytes of the region to query for modifications - */ - template - [[nodiscard]] bool IsRegionModified(u64 offset, u64 size) const noexcept { - static_assert(type != Type::Untracked); - - const std::span state_words = words.template Span(); - [[maybe_unused]] const std::span untracked_words = - words.template Span(); - bool result = false; - IterateWords(offset, size, [&](size_t index, u64 mask) { - if constexpr (type == Type::GPU) { - mask &= ~untracked_words[index]; - } - const u64 word = state_words[index] & mask; - if (word != 0) { - result = true; - return true; - } - return false; - }); - return result; - } - - /// Returns the number of words of the manager - [[nodiscard]] size_t NumWords() const noexcept { - return words.NumWords(); - } - - /// Returns the size in bytes of the manager - [[nodiscard]] u64 SizeBytes() const noexcept { - return words.size_bytes; - } - - /// Returns true when the buffer fits in the small vector optimization - [[nodiscard]] bool IsShort() const noexcept { - return words.IsShort(); - } - -private: - template - u64* Array() noexcept { - if constexpr (type == Type::CPU) { - return words.cpu.Pointer(IsShort()); - } else if constexpr (type == Type::GPU) { - return words.gpu.Pointer(IsShort()); - } else if constexpr (type == Type::Untracked) { - return words.untracked.Pointer(IsShort()); - } - } - - template - const u64* Array() const noexcept { - if constexpr (type == Type::CPU) { - return words.cpu.Pointer(IsShort()); - } else if constexpr (type == Type::GPU) { - return words.gpu.Pointer(IsShort()); - } else if constexpr (type == Type::Untracked) { - return words.untracked.Pointer(IsShort()); - } - } - - /** - * Notify tracker about changes in the CPU tracking state of a word in the buffer - * - * @param word_index Index to the word to notify to the tracker - * @param current_bits Current state of the word - * @param new_bits New state of the word - * - * @tparam add_to_tracker True when the tracker should start tracking the new pages - */ - template - void NotifyPageTracker(u64 word_index, u64 current_bits, u64 new_bits) const { - u64 changed_bits = (add_to_tracker ? current_bits : ~current_bits) & new_bits; - VAddr addr = cpu_addr + word_index * BYTES_PER_WORD; - IteratePages(changed_bits, [&](size_t offset, size_t size) { - tracker->UpdatePagesCachedCount(addr + offset * BYTES_PER_PAGE, size * BYTES_PER_PAGE, - add_to_tracker ? 1 : -1); - }); - } - - PageManager* tracker; - VAddr cpu_addr = 0; - Words words; -}; - -} // namespace VideoCore +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include "common/div_ceil.h" +#include "common/types.h" +#include "video_core/page_manager.h" + +namespace VideoCore { + +constexpr u64 PAGES_PER_WORD = 64; +constexpr u64 BYTES_PER_PAGE = 4_KB; +constexpr u64 BYTES_PER_WORD = PAGES_PER_WORD * BYTES_PER_PAGE; + +enum class Type { + CPU, + GPU, + Untracked, +}; + +/// Vector tracking modified pages tightly packed with small vector optimization +template +struct WordsArray { + /// Returns the pointer to the words state + [[nodiscard]] const u64* Pointer(bool is_short) const noexcept { + return is_short ? stack.data() : heap; + } + + /// Returns the pointer to the words state + [[nodiscard]] u64* Pointer(bool is_short) noexcept { + return is_short ? stack.data() : heap; + } + + std::array stack{}; ///< Small buffers storage + u64* heap; ///< Not-small buffers pointer to the storage +}; + +template +struct Words { + explicit Words() = default; + explicit Words(u64 size_bytes_) : size_bytes{size_bytes_} { + num_words = Common::DivCeil(size_bytes, BYTES_PER_WORD); + if (IsShort()) { + cpu.stack.fill(~u64{0}); + gpu.stack.fill(0); + untracked.stack.fill(~u64{0}); + } else { + // Share allocation between CPU and GPU pages and set their default values + u64* const alloc = new u64[num_words * 3]; + cpu.heap = alloc; + gpu.heap = alloc + num_words; + untracked.heap = alloc + num_words * 2; + std::fill_n(cpu.heap, num_words, ~u64{0}); + std::fill_n(gpu.heap, num_words, 0); + std::fill_n(untracked.heap, num_words, ~u64{0}); + } + // Clean up tailing bits + const u64 last_word_size = size_bytes % BYTES_PER_WORD; + const u64 last_local_page = Common::DivCeil(last_word_size, BYTES_PER_PAGE); + const u64 shift = (PAGES_PER_WORD - last_local_page) % PAGES_PER_WORD; + const u64 last_word = (~u64{0} << shift) >> shift; + cpu.Pointer(IsShort())[NumWords() - 1] = last_word; + untracked.Pointer(IsShort())[NumWords() - 1] = last_word; + } + + ~Words() { + Release(); + } + + Words& operator=(Words&& rhs) noexcept { + Release(); + size_bytes = rhs.size_bytes; + num_words = rhs.num_words; + cpu = rhs.cpu; + gpu = rhs.gpu; + untracked = rhs.untracked; + rhs.cpu.heap = nullptr; + return *this; + } + + Words(Words&& rhs) noexcept + : size_bytes{rhs.size_bytes}, num_words{rhs.num_words}, cpu{rhs.cpu}, gpu{rhs.gpu}, + untracked{rhs.untracked} { + rhs.cpu.heap = nullptr; + } + + Words& operator=(const Words&) = delete; + Words(const Words&) = delete; + + /// Returns true when the buffer fits in the small vector optimization + [[nodiscard]] bool IsShort() const noexcept { + return num_words <= stack_words; + } + + /// Returns the number of words of the buffer + [[nodiscard]] size_t NumWords() const noexcept { + return num_words; + } + + /// Release buffer resources + void Release() { + if (!IsShort()) { + // CPU written words is the base for the heap allocation + delete[] cpu.heap; + } + } + + template + std::span Span() noexcept { + if constexpr (type == Type::CPU) { + return std::span(cpu.Pointer(IsShort()), num_words); + } else if constexpr (type == Type::GPU) { + return std::span(gpu.Pointer(IsShort()), num_words); + } else if constexpr (type == Type::Untracked) { + return std::span(untracked.Pointer(IsShort()), num_words); + } + } + + template + std::span Span() const noexcept { + if constexpr (type == Type::CPU) { + return std::span(cpu.Pointer(IsShort()), num_words); + } else if constexpr (type == Type::GPU) { + return std::span(gpu.Pointer(IsShort()), num_words); + } else if constexpr (type == Type::Untracked) { + return std::span(untracked.Pointer(IsShort()), num_words); + } + } + + u64 size_bytes = 0; + size_t num_words = 0; + WordsArray cpu; + WordsArray gpu; + WordsArray untracked; +}; + +template +class WordManager { +public: + explicit WordManager(PageManager* tracker_, VAddr cpu_addr_, u64 size_bytes) + : tracker{tracker_}, cpu_addr{cpu_addr_}, words{size_bytes} {} + + explicit WordManager() = default; + + void SetCpuAddress(VAddr new_cpu_addr) { + cpu_addr = new_cpu_addr; + } + + VAddr GetCpuAddr() const { + return cpu_addr; + } + + static u64 ExtractBits(u64 word, size_t page_start, size_t page_end) { + constexpr size_t number_bits = sizeof(u64) * 8; + const size_t limit_page_end = number_bits - std::min(page_end, number_bits); + u64 bits = (word >> page_start) << page_start; + bits = (bits << limit_page_end) >> limit_page_end; + return bits; + } + + static std::pair GetWordPage(VAddr address) { + const size_t converted_address = static_cast(address); + const size_t word_number = converted_address / BYTES_PER_WORD; + const size_t amount_pages = converted_address % BYTES_PER_WORD; + return std::make_pair(word_number, amount_pages / BYTES_PER_PAGE); + } + + template + void IterateWords(size_t offset, size_t size, Func&& func) const { + using FuncReturn = std::invoke_result_t; + static constexpr bool BOOL_BREAK = std::is_same_v; + const size_t start = static_cast(std::max(static_cast(offset), 0LL)); + const size_t end = static_cast(std::max(static_cast(offset + size), 0LL)); + if (start >= SizeBytes() || end <= start) { + return; + } + auto [start_word, start_page] = GetWordPage(start); + auto [end_word, end_page] = GetWordPage(end + BYTES_PER_PAGE - 1ULL); + const size_t num_words = NumWords(); + start_word = std::min(start_word, num_words); + end_word = std::min(end_word, num_words); + const size_t diff = end_word - start_word; + end_word += (end_page + PAGES_PER_WORD - 1ULL) / PAGES_PER_WORD; + end_word = std::min(end_word, num_words); + end_page += diff * PAGES_PER_WORD; + constexpr u64 base_mask{~0ULL}; + for (size_t word_index = start_word; word_index < end_word; word_index++) { + const u64 mask = ExtractBits(base_mask, start_page, end_page); + start_page = 0; + end_page -= PAGES_PER_WORD; + if constexpr (BOOL_BREAK) { + if (func(word_index, mask)) { + return; + } + } else { + func(word_index, mask); + } + } + } + + template + void IteratePages(u64 mask, Func&& func) const { + size_t offset = 0; + while (mask != 0) { + const size_t empty_bits = std::countr_zero(mask); + offset += empty_bits; + mask = mask >> empty_bits; + + const size_t continuous_bits = std::countr_one(mask); + func(offset, continuous_bits); + mask = continuous_bits < PAGES_PER_WORD ? (mask >> continuous_bits) : 0; + offset += continuous_bits; + } + } + + /** + * Change the state of a range of pages + * + * @param dirty_addr Base address to mark or unmark as modified + * @param size Size in bytes to mark or unmark as modified + */ + template + void ChangeRegionState(u64 dirty_addr, u64 size) noexcept(type == Type::GPU) { + std::span state_words = words.template Span(); + [[maybe_unused]] std::span untracked_words = words.template Span(); + IterateWords(dirty_addr - cpu_addr, size, [&](size_t index, u64 mask) { + if constexpr (type == Type::CPU) { + NotifyPageTracker(index, untracked_words[index], mask); + } + if constexpr (enable) { + state_words[index] |= mask; + if constexpr (type == Type::CPU) { + untracked_words[index] |= mask; + } + } else { + state_words[index] &= ~mask; + if constexpr (type == Type::CPU) { + untracked_words[index] &= ~mask; + } + } + }); + } + + /** + * Loop over each page in the given range, turn off those bits and notify the tracker if + * needed. Call the given function on each turned off range. + * + * @param query_cpu_range Base CPU address to loop over + * @param size Size in bytes of the CPU range to loop over + * @param func Function to call for each turned off region + */ + template + void ForEachModifiedRange(VAddr query_cpu_range, s64 size, Func&& func) { + static_assert(type != Type::Untracked); + + std::span state_words = words.template Span(); + [[maybe_unused]] std::span untracked_words = words.template Span(); + const size_t offset = query_cpu_range - cpu_addr; + bool pending = false; + size_t pending_offset{}; + size_t pending_pointer{}; + const auto release = [&]() { + func(cpu_addr + pending_offset * BYTES_PER_PAGE, + (pending_pointer - pending_offset) * BYTES_PER_PAGE); + }; + IterateWords(offset, size, [&](size_t index, u64 mask) { + if constexpr (type == Type::GPU) { + mask &= ~untracked_words[index]; + } + const u64 word = state_words[index] & mask; + if constexpr (clear) { + if constexpr (type == Type::CPU) { + NotifyPageTracker(index, untracked_words[index], mask); + } + state_words[index] &= ~mask; + if constexpr (type == Type::CPU) { + untracked_words[index] &= ~mask; + } + } + const size_t base_offset = index * PAGES_PER_WORD; + IteratePages(word, [&](size_t pages_offset, size_t pages_size) { + const auto reset = [&]() { + pending_offset = base_offset + pages_offset; + pending_pointer = base_offset + pages_offset + pages_size; + }; + if (!pending) { + reset(); + pending = true; + return; + } + if (pending_pointer == base_offset + pages_offset) { + pending_pointer += pages_size; + return; + } + release(); + reset(); + }); + }); + if (pending) { + release(); + } + } + + /** + * Returns true when a region has been modified + * + * @param offset Offset in bytes from the start of the buffer + * @param size Size in bytes of the region to query for modifications + */ + template + [[nodiscard]] bool IsRegionModified(u64 offset, u64 size) const noexcept { + static_assert(type != Type::Untracked); + + const std::span state_words = words.template Span(); + [[maybe_unused]] const std::span untracked_words = + words.template Span(); + bool result = false; + IterateWords(offset, size, [&](size_t index, u64 mask) { + if constexpr (type == Type::GPU) { + mask &= ~untracked_words[index]; + } + const u64 word = state_words[index] & mask; + if (word != 0) { + result = true; + return true; + } + return false; + }); + return result; + } + + /// Returns the number of words of the manager + [[nodiscard]] size_t NumWords() const noexcept { + return words.NumWords(); + } + + /// Returns the size in bytes of the manager + [[nodiscard]] u64 SizeBytes() const noexcept { + return words.size_bytes; + } + + /// Returns true when the buffer fits in the small vector optimization + [[nodiscard]] bool IsShort() const noexcept { + return words.IsShort(); + } + +private: + template + u64* Array() noexcept { + if constexpr (type == Type::CPU) { + return words.cpu.Pointer(IsShort()); + } else if constexpr (type == Type::GPU) { + return words.gpu.Pointer(IsShort()); + } else if constexpr (type == Type::Untracked) { + return words.untracked.Pointer(IsShort()); + } + } + + template + const u64* Array() const noexcept { + if constexpr (type == Type::CPU) { + return words.cpu.Pointer(IsShort()); + } else if constexpr (type == Type::GPU) { + return words.gpu.Pointer(IsShort()); + } else if constexpr (type == Type::Untracked) { + return words.untracked.Pointer(IsShort()); + } + } + + /** + * Notify tracker about changes in the CPU tracking state of a word in the buffer + * + * @param word_index Index to the word to notify to the tracker + * @param current_bits Current state of the word + * @param new_bits New state of the word + * + * @tparam add_to_tracker True when the tracker should start tracking the new pages + */ + template + void NotifyPageTracker(u64 word_index, u64 current_bits, u64 new_bits) const { + u64 changed_bits = (add_to_tracker ? current_bits : ~current_bits) & new_bits; + VAddr addr = cpu_addr + word_index * BYTES_PER_WORD; + IteratePages(changed_bits, [&](size_t offset, size_t size) { + tracker->UpdatePagesCachedCount(addr + offset * BYTES_PER_PAGE, size * BYTES_PER_PAGE, + add_to_tracker ? 1 : -1); + }); + } + + PageManager* tracker; + VAddr cpu_addr = 0; + Words words; +}; + +} // namespace VideoCore From 8f7eb2d0e9fcdc1e192c2df623afff3f7e7d1178 Mon Sep 17 00:00:00 2001 From: IndecisiveTurtle <47210458+raphaelthegreat@users.noreply.github.com> Date: Tue, 24 Dec 2024 15:39:17 +0200 Subject: [PATCH 239/549] semaphore: Attempt to acquire before checking timeout * The posix specification says that if the object can be acquired immediately, timeout doesnt matter --- src/core/libraries/kernel/sync/semaphore.h | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/core/libraries/kernel/sync/semaphore.h b/src/core/libraries/kernel/sync/semaphore.h index 314e2cfa9..738df37d8 100644 --- a/src/core/libraries/kernel/sync/semaphore.h +++ b/src/core/libraries/kernel/sync/semaphore.h @@ -90,7 +90,7 @@ public: const auto start_time = std::chrono::high_resolution_clock::now(); auto rel_time_ms = std::chrono::ceil(rel_time); - while (rel_time_ms.count() > 0) { + do { u64 timeout_ms = static_cast(rel_time_ms.count()); u64 res = WaitForSingleObjectEx(sem, timeout_ms, true); if (res == WAIT_OBJECT_0) { @@ -101,7 +101,7 @@ public: } else { return false; } - } + } while (rel_time_ms.count() > 0); return false; #elif defined(__APPLE__) @@ -117,12 +117,9 @@ public: bool try_acquire_until(const std::chrono::time_point& abs_time) { #ifdef _WIN64 const auto start_time = Clock::now(); - if (start_time >= abs_time) { - return false; - } auto rel_time = std::chrono::ceil(abs_time - start_time); - while (rel_time.count() > 0) { + do { u64 timeout_ms = static_cast(rel_time.count()); u64 res = WaitForSingleObjectEx(sem, timeout_ms, true); if (res == WAIT_OBJECT_0) { @@ -133,7 +130,7 @@ public: } else { return false; } - } + } while (rel_time.count() > 0); return false; #elif defined(__APPLE__) From 7b24b42711287733b1f8732ecb22b85068c107f4 Mon Sep 17 00:00:00 2001 From: IndecisiveTurtle <47210458+raphaelthegreat@users.noreply.github.com> Date: Tue, 24 Dec 2024 16:04:30 +0200 Subject: [PATCH 240/549] data_share: Emit barrier before reads * Fixes artifacts in TLG when using NVIDIA gpus. When LDS is written and read in the same basic block, the barrier pass wont handle it properly, so insert a barrier before reads --- src/shader_recompiler/frontend/translate/data_share.cpp | 8 ++++++++ src/shader_recompiler/frontend/translate/translate.h | 1 + 2 files changed, 9 insertions(+) diff --git a/src/shader_recompiler/frontend/translate/data_share.cpp b/src/shader_recompiler/frontend/translate/data_share.cpp index 116935b94..4408cae28 100644 --- a/src/shader_recompiler/frontend/translate/data_share.cpp +++ b/src/shader_recompiler/frontend/translate/data_share.cpp @@ -1,7 +1,9 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later + #include "shader_recompiler/frontend/translate/translate.h" #include "shader_recompiler/ir/reg.h" +#include "shader_recompiler/profile.h" #include "shader_recompiler/runtime_info.h" namespace Shader::Gcn { @@ -203,6 +205,7 @@ void Translator::DS_WRITE(int bit_size, bool is_signed, bool is_pair, bool strid addr, ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0))); ir.WriteShared(bit_size, ir.GetVectorReg(data0), addr0); } + emit_ds_read_barrier = true; } void Translator::DS_SWIZZLE_B32(const GcnInst& inst) { @@ -219,6 +222,11 @@ void Translator::DS_SWIZZLE_B32(const GcnInst& inst) { void Translator::DS_READ(int bit_size, bool is_signed, bool is_pair, bool stride64, const GcnInst& inst) { + if (emit_ds_read_barrier && profile.needs_lds_barriers) { + ir.Barrier(); + emit_ds_read_barrier = false; + } + const IR::U32 addr{ir.GetVectorReg(IR::VectorReg(inst.src[0].code))}; IR::VectorReg dst_reg{inst.dst[0].code}; if (is_pair) { diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index 218b66d74..fd4d8d86a 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -306,6 +306,7 @@ private: const RuntimeInfo& runtime_info; const Profile& profile; bool opcode_missing = false; + bool emit_ds_read_barrier = false; }; void Translate(IR::Block* block, u32 block_base, std::span inst_list, Info& info, From 6d728ec7ed8809ecb131c80bdbe2ae534348db97 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Tue, 24 Dec 2024 13:03:04 -0800 Subject: [PATCH 241/549] renderer_vulkan: Enable LDS barriers for MoltenVK (#1866) --- src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index e774b1d42..74ae6b61c 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -205,7 +205,8 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_, .supports_image_load_store_lod = instance_.IsImageLoadStoreLodSupported(), .needs_manual_interpolation = instance.IsFragmentShaderBarycentricSupported() && instance.GetDriverID() == vk::DriverId::eNvidiaProprietary, - .needs_lds_barriers = instance.GetDriverID() == vk::DriverId::eNvidiaProprietary, + .needs_lds_barriers = instance.GetDriverID() == vk::DriverId::eNvidiaProprietary || + instance.GetDriverID() == vk::DriverId::eMoltenvk, }; auto [cache_result, cache] = instance.GetDevice().createPipelineCacheUnique({}); ASSERT_MSG(cache_result == vk::Result::eSuccess, "Failed to create pipeline cache: {}", From 95638d5ca55d3c481083dd6406780137fb2b1446 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Tue, 24 Dec 2024 14:21:32 -0800 Subject: [PATCH 242/549] exception: Fix raising exceptions on POSIX systems (#1868) --- src/core/libraries/kernel/threads/exception.cpp | 12 ++++++++---- src/core/thread.cpp | 1 + 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/core/libraries/kernel/threads/exception.cpp b/src/core/libraries/kernel/threads/exception.cpp index 017984e0d..cc391e928 100644 --- a/src/core/libraries/kernel/threads/exception.cpp +++ b/src/core/libraries/kernel/threads/exception.cpp @@ -22,7 +22,7 @@ void SigactionHandler(int signum, siginfo_t* inf, ucontext_t* raw_context) { if (handler) { auto ctx = Ucontext{}; #ifdef __APPLE__ - auto& regs = raw_context->uc_mcontext->__ss; + const auto& regs = raw_context->uc_mcontext->__ss; ctx.uc_mcontext.mc_r8 = regs.__r8; ctx.uc_mcontext.mc_r9 = regs.__r9; ctx.uc_mcontext.mc_r10 = regs.__r10; @@ -42,7 +42,7 @@ void SigactionHandler(int signum, siginfo_t* inf, ucontext_t* raw_context) { ctx.uc_mcontext.mc_fs = regs.__fs; ctx.uc_mcontext.mc_gs = regs.__gs; #else - auto& regs = raw_context->uc_mcontext.gregs; + const auto& regs = raw_context->uc_mcontext.gregs; ctx.uc_mcontext.mc_r8 = regs[REG_R8]; ctx.uc_mcontext.mc_r9 = regs[REG_R9]; ctx.uc_mcontext.mc_r10 = regs[REG_R10]; @@ -131,8 +131,12 @@ int PS4_SYSV_ABI sceKernelRaiseException(PthreadT thread, int signum) { LOG_WARNING(Lib_Kernel, "Raising exception on thread '{}'", thread->name); ASSERT_MSG(signum == POSIX_SIGUSR1, "Attempting to raise non user defined signal!"); #ifndef _WIN64 - pthread_t pthr = *reinterpret_cast(thread->native_thr.GetHandle()); - pthread_kill(pthr, SIGUSR2); + const auto pthr = reinterpret_cast(thread->native_thr.GetHandle()); + const auto ret = pthread_kill(pthr, SIGUSR2); + if (ret != 0) { + LOG_ERROR(Kernel, "Failed to send exception signal to thread '{}': {}", thread->name, + strerror(ret)); + } #else USER_APC_OPTION option; option.UserApcFlags = QueueUserApcFlagsSpecialUserApc; diff --git a/src/core/thread.cpp b/src/core/thread.cpp index 8a0a4cfd1..e154530d5 100644 --- a/src/core/thread.cpp +++ b/src/core/thread.cpp @@ -11,6 +11,7 @@ #else #include #include +#include #endif namespace Core { From a89c29c2ca90f56f18bde10fd9dd471e44961036 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Tue, 24 Dec 2024 15:13:32 -0800 Subject: [PATCH 243/549] shader_recompiler: Rework image read/write emit. (#1819) --- .../backend/spirv/emit_spirv_image.cpp | 45 +++++++++--------- .../backend/spirv/emit_spirv_instructions.h | 7 ++- .../backend/spirv/spirv_emit_context.cpp | 7 +-- .../frontend/translate/vector_memory.cpp | 6 +-- src/shader_recompiler/info.h | 16 +++++-- src/shader_recompiler/ir/ir_emitter.cpp | 13 ++--- src/shader_recompiler/ir/ir_emitter.h | 9 ++-- src/shader_recompiler/ir/opcodes.inc | 5 +- .../ir/passes/resource_tracking_pass.cpp | 47 +++++++++---------- src/shader_recompiler/specialization.h | 5 +- .../renderer_vulkan/vk_compute_pipeline.cpp | 5 +- .../renderer_vulkan/vk_graphics_pipeline.cpp | 5 +- .../renderer_vulkan/vk_rasterizer.cpp | 2 +- src/video_core/texture_cache/image_view.cpp | 2 +- src/video_core/texture_cache/texture_cache.h | 2 +- 15 files changed, 88 insertions(+), 88 deletions(-) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index e5d4f3077..2946edab3 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp @@ -168,22 +168,6 @@ Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, return texture.is_integer ? ctx.OpBitcast(ctx.F32[4], texels) : texels; } -Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id lod, - const IR::Value& offset, Id ms) { - const auto& texture = ctx.images[handle & 0xFFFF]; - const Id image = ctx.OpLoad(texture.image_type, texture.id); - const Id result_type = texture.data_types->Get(4); - ImageOperands operands; - operands.AddOffset(ctx, offset); - operands.Add(spv::ImageOperandsMask::Lod, lod); - operands.Add(spv::ImageOperandsMask::Sample, ms); - const Id texel = - texture.is_storage - ? ctx.OpImageRead(result_type, image, coords, operands.mask, operands.operands) - : ctx.OpImageFetch(result_type, image, coords, operands.mask, operands.operands); - return texture.is_integer ? ctx.OpBitcast(ctx.F32[4], texel) : texel; -} - Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, u32 handle, Id lod, bool has_mips) { const auto& texture = ctx.images[handle & 0xFFFF]; const Id image = ctx.OpLoad(texture.image_type, texture.id); @@ -236,15 +220,34 @@ Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id return texture.is_integer ? ctx.OpBitcast(ctx.F32[4], sample) : sample; } -Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id lod) { - UNREACHABLE_MSG("SPIR-V Instruction"); -} - -void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id lod, Id color) { +Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id lod, Id ms) { const auto& texture = ctx.images[handle & 0xFFFF]; const Id image = ctx.OpLoad(texture.image_type, texture.id); const Id color_type = texture.data_types->Get(4); ImageOperands operands; + operands.Add(spv::ImageOperandsMask::Sample, ms); + Id texel; + if (!texture.is_storage) { + operands.Add(spv::ImageOperandsMask::Lod, lod); + texel = ctx.OpImageFetch(color_type, image, coords, operands.mask, operands.operands); + } else { + if (ctx.profile.supports_image_load_store_lod) { + operands.Add(spv::ImageOperandsMask::Lod, lod); + } else if (Sirit::ValidId(lod)) { + LOG_WARNING(Render, "Image read with LOD not supported by driver"); + } + texel = ctx.OpImageRead(color_type, image, coords, operands.mask, operands.operands); + } + return !texture.is_integer ? ctx.OpBitcast(ctx.U32[4], texel) : texel; +} + +void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id lod, Id ms, + Id color) { + const auto& texture = ctx.images[handle & 0xFFFF]; + const Id image = ctx.OpLoad(texture.image_type, texture.id); + const Id color_type = texture.data_types->Get(4); + ImageOperands operands; + operands.Add(spv::ImageOperandsMask::Sample, ms); if (ctx.profile.supports_image_load_store_lod) { operands.Add(spv::ImageOperandsMask::Lod, lod); } else if (Sirit::ValidId(lod)) { diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index f71c61af6..2f606eb45 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h @@ -395,14 +395,13 @@ Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, const IR::Value& offset); Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, const IR::Value& offset, Id dref); -Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id lod, - const IR::Value& offset, Id ms); Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, u32 handle, Id lod, bool skip_mips); Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords); Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id derivatives_dx, Id derivatives_dy, const IR::Value& offset, const IR::Value& lod_clamp); -Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id lod); -void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id lod, Id color); +Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id lod, Id ms); +void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id lod, Id ms, + Id color); Id EmitImageAtomicIAdd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value); Id EmitImageAtomicSMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value); diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 255a3e2b2..d8bafccd9 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -772,7 +772,7 @@ Id ImageType(EmitContext& ctx, const ImageResource& desc, Id sampled_type) { const auto image = desc.GetSharp(ctx.info); const auto format = desc.is_atomic ? GetFormat(image) : spv::ImageFormat::Unknown; const auto type = image.GetBoundType(); - const u32 sampled = desc.is_storage ? 2 : 1; + const u32 sampled = desc.IsStorage(image) ? 2 : 1; switch (type) { case AmdGpu::ImageType::Color1D: return ctx.TypeImage(sampled_type, spv::Dim::Dim1D, false, false, false, sampled, format); @@ -800,6 +800,7 @@ void EmitContext::DefineImagesAndSamplers() { const auto sharp = image_desc.GetSharp(info); const auto nfmt = sharp.GetNumberFmt(); const bool is_integer = AmdGpu::IsInteger(nfmt); + const bool is_storage = image_desc.IsStorage(sharp); const VectorIds& data_types = GetAttributeType(*this, nfmt); const Id sampled_type = data_types[1]; const Id image_type{ImageType(*this, image_desc, sampled_type)}; @@ -811,11 +812,11 @@ void EmitContext::DefineImagesAndSamplers() { images.push_back({ .data_types = &data_types, .id = id, - .sampled_type = image_desc.is_storage ? sampled_type : TypeSampledImage(image_type), + .sampled_type = is_storage ? sampled_type : TypeSampledImage(image_type), .pointer_type = pointer_type, .image_type = image_type, .is_integer = is_integer, - .is_storage = image_desc.is_storage, + .is_storage = is_storage, }); interfaces.push_back(id); } diff --git a/src/shader_recompiler/frontend/translate/vector_memory.cpp b/src/shader_recompiler/frontend/translate/vector_memory.cpp index 072b1f88e..48cb79610 100644 --- a/src/shader_recompiler/frontend/translate/vector_memory.cpp +++ b/src/shader_recompiler/frontend/translate/vector_memory.cpp @@ -420,13 +420,13 @@ void Translator::IMAGE_LOAD(bool has_mip, const GcnInst& inst) { IR::TextureInstInfo info{}; info.has_lod.Assign(has_mip); - const IR::Value texel = ir.ImageFetch(handle, body, {}, {}, {}, info); + const IR::Value texel = ir.ImageRead(handle, body, {}, {}, info); for (u32 i = 0; i < 4; i++) { if (((mimg.dmask >> i) & 1) == 0) { continue; } - IR::F32 value = IR::F32{ir.CompositeExtract(texel, i)}; + IR::U32 value = IR::U32{ir.CompositeExtract(texel, i)}; ir.SetVectorReg(dest_reg++, value); } } @@ -454,7 +454,7 @@ void Translator::IMAGE_STORE(bool has_mip, const GcnInst& inst) { comps.push_back(ir.GetVectorReg(data_reg++)); } const IR::Value value = ir.CompositeConstruct(comps[0], comps[1], comps[2], comps[3]); - ir.ImageWrite(handle, body, {}, value, info); + ir.ImageWrite(handle, body, {}, {}, value, info); } void Translator::IMAGE_GET_RESINFO(const GcnInst& inst) { diff --git a/src/shader_recompiler/info.h b/src/shader_recompiler/info.h index dbea2af8a..b6ac12785 100644 --- a/src/shader_recompiler/info.h +++ b/src/shader_recompiler/info.h @@ -49,11 +49,11 @@ struct BufferResource { u8 instance_attrib{}; bool is_written{}; - bool IsStorage(AmdGpu::Buffer buffer) const noexcept { + [[nodiscard]] bool IsStorage(const AmdGpu::Buffer& buffer) const noexcept { return buffer.GetSize() > MaxUboSize || is_written || is_gds_buffer; } - constexpr AmdGpu::Buffer GetSharp(const Info& info) const noexcept; + [[nodiscard]] constexpr AmdGpu::Buffer GetSharp(const Info& info) const noexcept; }; using BufferResourceList = boost::container::small_vector; @@ -61,18 +61,24 @@ struct TextureBufferResource { u32 sharp_idx; bool is_written{}; - constexpr AmdGpu::Buffer GetSharp(const Info& info) const noexcept; + [[nodiscard]] constexpr AmdGpu::Buffer GetSharp(const Info& info) const noexcept; }; using TextureBufferResourceList = boost::container::small_vector; struct ImageResource { u32 sharp_idx; - bool is_storage{}; bool is_depth{}; bool is_atomic{}; bool is_array{}; + bool is_read{}; + bool is_written{}; - constexpr AmdGpu::Image GetSharp(const Info& info) const noexcept; + [[nodiscard]] bool IsStorage(const AmdGpu::Image& image) const noexcept { + // Need cube as storage when used with ImageRead. + return is_written || (is_read && image.GetBoundType() == AmdGpu::ImageType::Cube); + } + + [[nodiscard]] constexpr AmdGpu::Image GetSharp(const Info& info) const noexcept; }; using ImageResourceList = boost::container::small_vector; diff --git a/src/shader_recompiler/ir/ir_emitter.cpp b/src/shader_recompiler/ir/ir_emitter.cpp index 21df53391..c241ec984 100644 --- a/src/shader_recompiler/ir/ir_emitter.cpp +++ b/src/shader_recompiler/ir/ir_emitter.cpp @@ -1630,11 +1630,6 @@ Value IREmitter::ImageGatherDref(const Value& handle, const Value& coords, const return Inst(Opcode::ImageGatherDref, Flags{info}, handle, coords, offset, dref); } -Value IREmitter::ImageFetch(const Value& handle, const Value& coords, const U32& lod, - const Value& offset, const U32& multisampling, TextureInstInfo info) { - return Inst(Opcode::ImageFetch, Flags{info}, handle, coords, lod, offset, multisampling); -} - Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod, const IR::U1& skip_mips) { return Inst(Opcode::ImageQueryDimensions, handle, lod, skip_mips); @@ -1657,13 +1652,13 @@ Value IREmitter::ImageGradient(const Value& handle, const Value& coords, } Value IREmitter::ImageRead(const Value& handle, const Value& coords, const U32& lod, - TextureInstInfo info) { - return Inst(Opcode::ImageRead, Flags{info}, handle, coords, lod); + const U32& multisampling, TextureInstInfo info) { + return Inst(Opcode::ImageRead, Flags{info}, handle, coords, lod, multisampling); } void IREmitter::ImageWrite(const Value& handle, const Value& coords, const U32& lod, - const Value& color, TextureInstInfo info) { - Inst(Opcode::ImageWrite, Flags{info}, handle, coords, lod, color); + const U32& multisampling, const Value& color, TextureInstInfo info) { + Inst(Opcode::ImageWrite, Flags{info}, handle, coords, lod, multisampling, color); } // Debug print maps to SPIRV's NonSemantic DebugPrintf instruction diff --git a/src/shader_recompiler/ir/ir_emitter.h b/src/shader_recompiler/ir/ir_emitter.h index 95713565b..4cf44107e 100644 --- a/src/shader_recompiler/ir/ir_emitter.h +++ b/src/shader_recompiler/ir/ir_emitter.h @@ -325,17 +325,14 @@ public: TextureInstInfo info); [[nodiscard]] Value ImageGatherDref(const Value& handle, const Value& coords, const Value& offset, const F32& dref, TextureInstInfo info); - [[nodiscard]] Value ImageFetch(const Value& handle, const Value& coords, const U32& lod, - const Value& offset, const U32& multisampling, - TextureInstInfo info); [[nodiscard]] Value ImageGradient(const Value& handle, const Value& coords, const Value& derivatives_dx, const Value& derivatives_dy, const Value& offset, const F32& lod_clamp, TextureInstInfo info); [[nodiscard]] Value ImageRead(const Value& handle, const Value& coords, const U32& lod, - TextureInstInfo info); - void ImageWrite(const Value& handle, const Value& coords, const U32& lod, const Value& color, - TextureInstInfo info); + const U32& multisampling, TextureInstInfo info); + void ImageWrite(const Value& handle, const Value& coords, const U32& lod, + const U32& multisampling, const Value& color, TextureInstInfo info); void EmitVertex(); void EmitPrimitive(); diff --git a/src/shader_recompiler/ir/opcodes.inc b/src/shader_recompiler/ir/opcodes.inc index 470f9fbe5..aafd43ea8 100644 --- a/src/shader_recompiler/ir/opcodes.inc +++ b/src/shader_recompiler/ir/opcodes.inc @@ -338,12 +338,11 @@ OPCODE(ImageSampleDrefImplicitLod, F32x4, Opaq OPCODE(ImageSampleDrefExplicitLod, F32x4, Opaque, Opaque, F32, F32, Opaque, ) OPCODE(ImageGather, F32x4, Opaque, Opaque, Opaque, ) OPCODE(ImageGatherDref, F32x4, Opaque, Opaque, Opaque, F32, ) -OPCODE(ImageFetch, F32x4, Opaque, Opaque, U32, Opaque, Opaque, ) OPCODE(ImageQueryDimensions, U32x4, Opaque, U32, U1, ) OPCODE(ImageQueryLod, F32x4, Opaque, Opaque, ) OPCODE(ImageGradient, F32x4, Opaque, Opaque, Opaque, Opaque, Opaque, F32, ) -OPCODE(ImageRead, U32x4, Opaque, Opaque, U32, ) -OPCODE(ImageWrite, Void, Opaque, Opaque, U32, U32x4, ) +OPCODE(ImageRead, U32x4, Opaque, Opaque, U32, U32, ) +OPCODE(ImageWrite, Void, Opaque, Opaque, U32, U32, U32x4, ) // Image atomic operations OPCODE(ImageAtomicIAdd32, U32, Opaque, Opaque, U32, ) diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index a59398952..db1a2edd2 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -115,25 +115,16 @@ bool IsImageAtomicInstruction(const IR::Inst& inst) { } } -bool IsImageStorageInstruction(const IR::Inst& inst) { - switch (inst.GetOpcode()) { - case IR::Opcode::ImageWrite: - case IR::Opcode::ImageRead: - return true; - default: - return IsImageAtomicInstruction(inst); - } -} - bool IsImageInstruction(const IR::Inst& inst) { switch (inst.GetOpcode()) { - case IR::Opcode::ImageFetch: + case IR::Opcode::ImageRead: + case IR::Opcode::ImageWrite: case IR::Opcode::ImageQueryDimensions: case IR::Opcode::ImageQueryLod: case IR::Opcode::ImageSampleRaw: return true; default: - return IsImageStorageInstruction(inst); + return IsImageAtomicInstruction(inst); } } @@ -201,7 +192,8 @@ public: return desc.sharp_idx == existing.sharp_idx; })}; auto& image = image_resources[index]; - image.is_storage |= desc.is_storage; + image.is_read |= desc.is_read; + image.is_written |= desc.is_written; return index; } @@ -429,9 +421,9 @@ void PatchTextureBufferInstruction(IR::Block& block, IR::Inst& inst, Info& info, } IR::Value PatchCubeCoord(IR::IREmitter& ir, const IR::Value& s, const IR::Value& t, - const IR::Value& z, bool is_storage, bool is_array) { + const IR::Value& z, bool is_written, bool is_array) { // When cubemap is written with imageStore it is treated like 2DArray. - if (is_storage) { + if (is_written) { return ir.CompositeConstruct(s, t, z); } @@ -684,15 +676,16 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip image = AmdGpu::Image::Null(); } ASSERT(image.GetType() != AmdGpu::ImageType::Invalid); - const bool is_storage = IsImageStorageInstruction(inst); + const bool is_read = inst.GetOpcode() == IR::Opcode::ImageRead; + const bool is_written = inst.GetOpcode() == IR::Opcode::ImageWrite; // Patch image instruction if image is FMask. if (image.IsFmask()) { - ASSERT_MSG(!is_storage, "FMask storage instructions are not supported"); + ASSERT_MSG(!is_written, "FMask storage instructions are not supported"); IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; switch (inst.GetOpcode()) { - case IR::Opcode::ImageFetch: + case IR::Opcode::ImageRead: case IR::Opcode::ImageSampleRaw: { IR::F32 fmaskx = ir.BitCast(ir.Imm32(0x76543210)); IR::F32 fmasky = ir.BitCast(ir.Imm32(0xfedcba98)); @@ -721,10 +714,11 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip u32 image_binding = descriptors.Add(ImageResource{ .sharp_idx = tsharp, - .is_storage = is_storage, .is_depth = bool(inst_info.is_depth), .is_atomic = IsImageAtomicInstruction(inst), .is_array = bool(inst_info.is_array), + .is_read = is_read, + .is_written = is_written, }); // Sample instructions must be resolved into a new instruction using address register data. @@ -762,7 +756,7 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip case AmdGpu::ImageType::Color3D: // x, y, z, [lod] return {ir.CompositeConstruct(body->Arg(0), body->Arg(1), body->Arg(2)), body->Arg(3)}; case AmdGpu::ImageType::Cube: // x, y, face, [lod] - return {PatchCubeCoord(ir, body->Arg(0), body->Arg(1), body->Arg(2), is_storage, + return {PatchCubeCoord(ir, body->Arg(0), body->Arg(1), body->Arg(2), is_written, inst_info.is_array), body->Arg(3)}; default: @@ -772,19 +766,20 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip inst.SetArg(1, coords); if (inst.GetOpcode() == IR::Opcode::ImageWrite) { - inst.SetArg(3, SwizzleVector(ir, image, inst.Arg(3))); + inst.SetArg(4, SwizzleVector(ir, image, inst.Arg(4))); } if (inst_info.has_lod) { - ASSERT(inst.GetOpcode() == IR::Opcode::ImageFetch || - inst.GetOpcode() == IR::Opcode::ImageRead || + ASSERT(inst.GetOpcode() == IR::Opcode::ImageRead || inst.GetOpcode() == IR::Opcode::ImageWrite); ASSERT(image.GetType() != AmdGpu::ImageType::Color2DMsaa && image.GetType() != AmdGpu::ImageType::Color2DMsaaArray); inst.SetArg(2, arg); - } else if (image.GetType() == AmdGpu::ImageType::Color2DMsaa || - image.GetType() == AmdGpu::ImageType::Color2DMsaaArray) { - inst.SetArg(4, arg); + } else if ((image.GetType() == AmdGpu::ImageType::Color2DMsaa || + image.GetType() == AmdGpu::ImageType::Color2DMsaaArray) && + (inst.GetOpcode() == IR::Opcode::ImageRead || + inst.GetOpcode() == IR::Opcode::ImageWrite)) { + inst.SetArg(3, arg); } } diff --git a/src/shader_recompiler/specialization.h b/src/shader_recompiler/specialization.h index 5799c4c95..5bf97ee51 100644 --- a/src/shader_recompiler/specialization.h +++ b/src/shader_recompiler/specialization.h @@ -39,10 +39,12 @@ struct TextureBufferSpecialization { struct ImageSpecialization { AmdGpu::ImageType type = AmdGpu::ImageType::Color2D; bool is_integer = false; + bool is_storage = false; u32 dst_select = 0; bool operator==(const ImageSpecialization& other) const { return type == other.type && is_integer == other.is_integer && + is_storage == other.is_storage && (dst_select != 0 ? dst_select == other.dst_select : true); } }; @@ -114,7 +116,8 @@ struct StageSpecialization { [](auto& spec, const auto& desc, AmdGpu::Image sharp) { spec.type = sharp.GetBoundType(); spec.is_integer = AmdGpu::IsInteger(sharp.GetNumberFmt()); - if (desc.is_storage) { + spec.is_storage = desc.IsStorage(sharp); + if (spec.is_storage) { spec.dst_select = sharp.DstSelect(); } }); diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp index a39b18378..71659c06c 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp @@ -58,8 +58,9 @@ ComputePipeline::ComputePipeline(const Instance& instance_, Scheduler& scheduler for (const auto& image : info->images) { bindings.push_back({ .binding = binding++, - .descriptorType = image.is_storage ? vk::DescriptorType::eStorageImage - : vk::DescriptorType::eSampledImage, + .descriptorType = image.IsStorage(image.GetSharp(*info)) + ? vk::DescriptorType::eStorageImage + : vk::DescriptorType::eSampledImage, .descriptorCount = 1, .stageFlags = vk::ShaderStageFlagBits::eCompute, }); diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 265c7bd3d..c01d4cbe8 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -378,8 +378,9 @@ void GraphicsPipeline::BuildDescSetLayout() { for (const auto& image : stage->images) { bindings.push_back({ .binding = binding++, - .descriptorType = image.is_storage ? vk::DescriptorType::eStorageImage - : vk::DescriptorType::eSampledImage, + .descriptorType = image.IsStorage(image.GetSharp(*stage)) + ? vk::DescriptorType::eStorageImage + : vk::DescriptorType::eSampledImage, .descriptorCount = 1, .stageFlags = gp_stage_flags, }); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 55f861013..1e698bf6b 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -655,7 +655,7 @@ void Rasterizer::BindTextures(const Shader::Info& stage, Shader::Backend::Bindin if (image->binding.is_bound) { // The image is already bound. In case if it is about to be used as storage we need // to force general layout on it. - image->binding.force_general |= image_desc.is_storage; + image->binding.force_general |= image_desc.IsStorage(tsharp); } if (image->binding.is_target) { // The image is already bound as target. Since we read and output to it need to force diff --git a/src/video_core/texture_cache/image_view.cpp b/src/video_core/texture_cache/image_view.cpp index ec1fda0d8..9e67b7f73 100644 --- a/src/video_core/texture_cache/image_view.cpp +++ b/src/video_core/texture_cache/image_view.cpp @@ -51,7 +51,7 @@ vk::ComponentSwizzle ConvertComponentSwizzle(u32 dst_sel) { } ImageViewInfo::ImageViewInfo(const AmdGpu::Image& image, const Shader::ImageResource& desc) noexcept - : is_storage{desc.is_storage} { + : is_storage{desc.IsStorage(image)} { const auto dfmt = image.GetDataFmt(); auto nfmt = image.GetNumberFmt(); if (is_storage && nfmt == AmdGpu::NumberFormat::Srgb) { diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 430415ed2..944f021df 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -65,7 +65,7 @@ public: struct TextureDesc : public BaseDesc { TextureDesc() = default; TextureDesc(const AmdGpu::Image& image, const Shader::ImageResource& desc) - : BaseDesc{desc.is_storage ? BindingType::Storage : BindingType::Texture, + : BaseDesc{desc.IsStorage(image) ? BindingType::Storage : BindingType::Texture, ImageInfo{image, desc}, ImageViewInfo{image, desc}} {} }; From d55e41a19e45098f505139fde34cdfc42216c3b7 Mon Sep 17 00:00:00 2001 From: f8ith Date: Wed, 25 Dec 2024 15:30:09 +0800 Subject: [PATCH 244/549] compatibility_data: Added compatibility related settings to GUI (#1855) * compatibility_data: added compatibility related settings to gui * New game compatibility section in settings dialog * Context menu now has a compatibility section * Adjusted minimum size of updater section to 265 * update translations * revert to deprecated statechanged * fix display compatibility data option --- src/qt_gui/compatibility_info.cpp | 10 ++-- src/qt_gui/compatibility_info.h | 2 +- src/qt_gui/game_grid_frame.cpp | 9 ++- src/qt_gui/game_grid_frame.h | 6 +- src/qt_gui/game_list_frame.cpp | 12 ++-- src/qt_gui/gui_context_menus.h | 48 +++++++++++++++- src/qt_gui/main_window.cpp | 12 +++- src/qt_gui/settings_dialog.cpp | 31 ++++++++++- src/qt_gui/settings_dialog.h | 7 ++- src/qt_gui/settings_dialog.ui | 91 ++++++++++++++++++++++++++----- src/qt_gui/translations/en.ts | 65 ++++++++++++++++++++++ 11 files changed, 256 insertions(+), 37 deletions(-) diff --git a/src/qt_gui/compatibility_info.cpp b/src/qt_gui/compatibility_info.cpp index aecac60cd..69fb3e377 100644 --- a/src/qt_gui/compatibility_info.cpp +++ b/src/qt_gui/compatibility_info.cpp @@ -18,9 +18,10 @@ CompatibilityInfoClass::CompatibilityInfoClass() }; CompatibilityInfoClass::~CompatibilityInfoClass() = default; -void CompatibilityInfoClass::UpdateCompatibilityDatabase(QWidget* parent) { - if (LoadCompatibilityFile()) - return; +void CompatibilityInfoClass::UpdateCompatibilityDatabase(QWidget* parent, bool forced) { + if (!forced) + if (LoadCompatibilityFile()) + return; QNetworkReply* reply = FetchPage(1); if (!WaitForReply(reply)) @@ -45,7 +46,8 @@ void CompatibilityInfoClass::UpdateCompatibilityDatabase(QWidget* parent) { QMessageBox::critical(parent, tr("Error"), tr("Unable to update compatibility data! Try again later.")); // Try loading compatibility_file.json again - LoadCompatibilityFile(); + if (!forced) + LoadCompatibilityFile(); return; } diff --git a/src/qt_gui/compatibility_info.h b/src/qt_gui/compatibility_info.h index dcbaef847..0c47c27ff 100644 --- a/src/qt_gui/compatibility_info.h +++ b/src/qt_gui/compatibility_info.h @@ -84,7 +84,7 @@ public: CompatibilityInfoClass(); ~CompatibilityInfoClass(); - void UpdateCompatibilityDatabase(QWidget* parent = nullptr); + void UpdateCompatibilityDatabase(QWidget* parent = nullptr, bool forced = false); bool LoadCompatibilityFile(); CompatibilityEntry GetCompatibilityInfo(const std::string& serial); void ExtractCompatibilityInfo(QByteArray response); diff --git a/src/qt_gui/game_grid_frame.cpp b/src/qt_gui/game_grid_frame.cpp index b932e46c3..2ebb09e5d 100644 --- a/src/qt_gui/game_grid_frame.cpp +++ b/src/qt_gui/game_grid_frame.cpp @@ -3,9 +3,12 @@ #include "common/path_util.h" #include "game_grid_frame.h" +#include "qt_gui/compatibility_info.h" -GameGridFrame::GameGridFrame(std::shared_ptr game_info_get, QWidget* parent) - : QTableWidget(parent), m_game_info(game_info_get) { +GameGridFrame::GameGridFrame(std::shared_ptr game_info_get, + std::shared_ptr compat_info_get, + QWidget* parent) + : QTableWidget(parent), m_game_info(game_info_get), m_compat_info(compat_info_get) { icon_size = Config::getIconSizeGrid(); windowWidth = parent->width(); this->setShowGrid(false); @@ -29,7 +32,7 @@ GameGridFrame::GameGridFrame(std::shared_ptr game_info_get, QWidg connect(this->horizontalScrollBar(), &QScrollBar::valueChanged, this, &GameGridFrame::RefreshGridBackgroundImage); connect(this, &QTableWidget::customContextMenuRequested, this, [=, this](const QPoint& pos) { - m_gui_context_menus.RequestGameMenu(pos, m_game_info->m_games, this, false); + m_gui_context_menus.RequestGameMenu(pos, m_game_info->m_games, m_compat_info, this, false); }); } diff --git a/src/qt_gui/game_grid_frame.h b/src/qt_gui/game_grid_frame.h index c09767684..4825d6daf 100644 --- a/src/qt_gui/game_grid_frame.h +++ b/src/qt_gui/game_grid_frame.h @@ -10,6 +10,7 @@ #include "game_info.h" #include "game_list_utils.h" #include "gui_context_menus.h" +#include "qt_gui/compatibility_info.h" class GameGridFrame : public QTableWidget { Q_OBJECT @@ -29,11 +30,14 @@ private: GameListUtils m_game_list_utils; GuiContextMenus m_gui_context_menus; std::shared_ptr m_game_info; + std::shared_ptr m_compat_info; std::shared_ptr> m_games_shared; bool validCellSelected = false; public: - explicit GameGridFrame(std::shared_ptr game_info_get, QWidget* parent = nullptr); + explicit GameGridFrame(std::shared_ptr game_info_get, + std::shared_ptr compat_info_get, + QWidget* parent = nullptr); void PopulateGameGrid(QVector m_games, bool fromSearch); bool IsValidCellSelected(); diff --git a/src/qt_gui/game_list_frame.cpp b/src/qt_gui/game_list_frame.cpp index 53159d8e7..bba3c2891 100644 --- a/src/qt_gui/game_list_frame.cpp +++ b/src/qt_gui/game_list_frame.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include "common/config.h" #include "common/logging/log.h" #include "common/path_util.h" #include "common/string_util.h" @@ -72,7 +73,7 @@ GameListFrame::GameListFrame(std::shared_ptr game_info_get, }); connect(this, &QTableWidget::customContextMenuRequested, this, [=, this](const QPoint& pos) { - m_gui_context_menus.RequestGameMenu(pos, m_game_info->m_games, this, true); + m_gui_context_menus.RequestGameMenu(pos, m_game_info->m_games, m_compat_info, this, true); }); connect(this, &QTableWidget::cellClicked, this, [=, this](int row, int column) { @@ -80,11 +81,6 @@ GameListFrame::GameListFrame(std::shared_ptr game_info_get, QDesktopServices::openUrl(QUrl(m_game_info->m_games[row].compatibility.url)); } }); - - // Do not show status column if it is not enabled - if (!Config::getCompatibilityEnabled()) { - this->setColumnHidden(2, true); - } } void GameListFrame::onCurrentCellChanged(int currentRow, int currentColumn, int previousRow, @@ -108,6 +104,8 @@ void GameListFrame::PlayBackgroundMusic(QTableWidgetItem* item) { } void GameListFrame::PopulateGameList() { + // Do not show status column if it is not enabled + this->setColumnHidden(2, !Config::getCompatibilityEnabled()); this->setRowCount(m_game_info->m_games.size()); ResizeIcons(icon_size); @@ -241,7 +239,7 @@ void GameListFrame::SetCompatibilityItem(int row, int column, CompatibilityEntry break; case CompatibilityStatus::Nothing: color = QStringLiteral("#212121"); - status_explanation = tr("Games does not initialize properly / crashes the emulator"); + status_explanation = tr("Game does not initialize properly / crashes the emulator"); break; case CompatibilityStatus::Boots: color = QStringLiteral("#828282"); diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h index 3cc12c11e..d26b860c1 100644 --- a/src/qt_gui/gui_context_menus.h +++ b/src/qt_gui/gui_context_menus.h @@ -11,6 +11,9 @@ #include #include "cheats_patches.h" +#include "common/config.h" +#include "common/version.h" +#include "compatibility_info.h" #include "game_info.h" #include "trophy_viewer.h" @@ -27,8 +30,9 @@ class GuiContextMenus : public QObject { Q_OBJECT public: - void RequestGameMenu(const QPoint& pos, QVector m_games, QTableWidget* widget, - bool isList) { + void RequestGameMenu(const QPoint& pos, QVector m_games, + std::shared_ptr m_compat_info, + QTableWidget* widget, bool isList) { QPoint global_pos = widget->viewport()->mapToGlobal(pos); int itemID = 0; if (isList) { @@ -91,6 +95,21 @@ public: menu.addMenu(deleteMenu); + // Compatibility submenu. + QMenu* compatibilityMenu = new QMenu(tr("Compatibility..."), widget); + QAction* updateCompatibility = new QAction(tr("Update database"), widget); + QAction* viewCompatibilityReport = new QAction(tr("View report"), widget); + QAction* submitCompatibilityReport = new QAction(tr("Submit a report"), widget); + + compatibilityMenu->addAction(updateCompatibility); + compatibilityMenu->addAction(viewCompatibilityReport); + compatibilityMenu->addAction(submitCompatibilityReport); + + menu.addMenu(compatibilityMenu); + + compatibilityMenu->setEnabled(Config::getCompatibilityEnabled()); + viewCompatibilityReport->setEnabled(!m_games[itemID].compatibility.url.isEmpty()); + // Show menu. auto selected = menu.exec(global_pos); if (!selected) { @@ -360,6 +379,31 @@ public: } } } + + if (selected == updateCompatibility) { + m_compat_info->UpdateCompatibilityDatabase(widget, true); + } + + if (selected == viewCompatibilityReport) { + if (!m_games[itemID].compatibility.url.isEmpty()) + QDesktopServices::openUrl(QUrl(m_games[itemID].compatibility.url)); + } + + if (selected == submitCompatibilityReport) { + QUrl url = QUrl("https://github.com/shadps4-emu/shadps4-game-compatibility/issues/new"); + QUrlQuery query; + query.addQueryItem("template", QString("game_compatibility.yml")); + query.addQueryItem( + "title", QString("%1 - %2").arg(QString::fromStdString(m_games[itemID].serial), + QString::fromStdString(m_games[itemID].name))); + query.addQueryItem("game-name", QString::fromStdString(m_games[itemID].name)); + query.addQueryItem("game-code", QString::fromStdString(m_games[itemID].serial)); + query.addQueryItem("game-version", QString::fromStdString(m_games[itemID].version)); + query.addQueryItem("emulator-version", QString(Common::VERSION)); + url.setQuery(query); + + QDesktopServices::openUrl(url); + } } int GetRowIndex(QTreeWidget* treeWidget, QTreeWidgetItem* item) { diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index adb42fc26..4a9b4d94a 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -140,7 +140,7 @@ void MainWindow::CreateDockWindows() { m_dock_widget.reset(new QDockWidget(tr("Game List"), this)); m_game_list_frame.reset(new GameListFrame(m_game_info, m_compat_info, this)); m_game_list_frame->setObjectName("gamelist"); - m_game_grid_frame.reset(new GameGridFrame(m_game_info, this)); + m_game_grid_frame.reset(new GameGridFrame(m_game_info, m_compat_info, this)); m_game_grid_frame->setObjectName("gamegridlist"); m_elf_viewer.reset(new ElfViewer(this)); m_elf_viewer->setObjectName("elflist"); @@ -253,20 +253,26 @@ void MainWindow::CreateConnects() { &MainWindow::StartGame); connect(ui->configureAct, &QAction::triggered, this, [this]() { - auto settingsDialog = new SettingsDialog(m_physical_devices, this); + auto settingsDialog = new SettingsDialog(m_physical_devices, m_compat_info, this); connect(settingsDialog, &SettingsDialog::LanguageChanged, this, &MainWindow::OnLanguageChanged); + connect(settingsDialog, &SettingsDialog::CompatibilityChanged, this, + &MainWindow::RefreshGameTable); + settingsDialog->exec(); }); connect(ui->settingsButton, &QPushButton::clicked, this, [this]() { - auto settingsDialog = new SettingsDialog(m_physical_devices, this); + auto settingsDialog = new SettingsDialog(m_physical_devices, m_compat_info, this); connect(settingsDialog, &SettingsDialog::LanguageChanged, this, &MainWindow::OnLanguageChanged); + connect(settingsDialog, &SettingsDialog::CompatibilityChanged, this, + &MainWindow::RefreshGameTable); + settingsDialog->exec(); }); diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index 97c891e4f..f9095d8a0 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -6,6 +6,8 @@ #include #include +#include "common/config.h" +#include "qt_gui/compatibility_info.h" #ifdef ENABLE_DISCORD_RPC #include "common/discord_rpc_handler.h" #endif @@ -54,7 +56,9 @@ QStringList languageNames = {"Arabic", const QVector languageIndexes = {21, 23, 14, 6, 18, 1, 12, 22, 2, 4, 25, 24, 29, 5, 0, 9, 15, 16, 17, 7, 26, 8, 11, 20, 3, 13, 27, 10, 19, 30, 28}; -SettingsDialog::SettingsDialog(std::span physical_devices, QWidget* parent) +SettingsDialog::SettingsDialog(std::span physical_devices, + std::shared_ptr m_compat_info, + QWidget* parent) : QDialog(parent), ui(new Ui::SettingsDialog) { ui->setupUi(this); ui->tabWidgetSettings->setUsesScrollButtons(false); @@ -140,6 +144,16 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge ui->updaterGroupBox->setVisible(false); ui->GUIgroupBox->setMaximumSize(265, 16777215); #endif + connect(ui->updateCompatibilityButton, &QPushButton::clicked, this, + [this, parent, m_compat_info]() { + m_compat_info->UpdateCompatibilityDatabase(this, true); + emit CompatibilityChanged(); + }); + + connect(ui->enableCompatibilityCheckBox, &QCheckBox::stateChanged, this, [this](int state) { + Config::setCompatibilityEnabled(state); + emit CompatibilityChanged(); + }); } // Input TAB @@ -195,6 +209,9 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge #endif ui->GUIgroupBox->installEventFilter(this); ui->disableTrophycheckBox->installEventFilter(this); + ui->enableCompatibilityCheckBox->installEventFilter(this); + ui->checkCompatibilityOnStartupCheckBox->installEventFilter(this); + ui->updateCompatibilityButton->installEventFilter(this); // Input ui->hideCursorGroupBox->installEventFilter(this); @@ -285,6 +302,10 @@ void SettingsDialog::LoadValuesFromConfig() { ui->vkSyncValidationCheckBox->setChecked( toml::find_or(data, "Vulkan", "validation_sync", false)); ui->rdocCheckBox->setChecked(toml::find_or(data, "Vulkan", "rdocEnable", false)); + ui->enableCompatibilityCheckBox->setChecked( + toml::find_or(data, "General", "compatibilityEnabled", false)); + ui->checkCompatibilityOnStartupCheckBox->setChecked( + toml::find_or(data, "General", "checkCompatibilityOnStartup", false)); #ifdef ENABLE_UPDATER ui->updateCheckBox->setChecked(toml::find_or(data, "General", "autoUpdate", false)); @@ -402,6 +423,12 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) { text = tr("GUIgroupBox"); } else if (elementName == "disableTrophycheckBox") { text = tr("disableTrophycheckBox"); + } else if (elementName == "enableCompatibilityCheckBox") { + text = tr("enableCompatibilityCheckBox"); + } else if (elementName == "checkCompatibilityOnStartupCheckBox") { + text = tr("checkCompatibilityOnStartupCheckBox"); + } else if (elementName == "updateCompatibilityButton") { + text = tr("updateCompatibilityButton"); } // Input @@ -515,6 +542,8 @@ void SettingsDialog::UpdateSettings() { Config::setRdocEnabled(ui->rdocCheckBox->isChecked()); Config::setAutoUpdate(ui->updateCheckBox->isChecked()); Config::setUpdateChannel(ui->updateComboBox->currentText().toStdString()); + Config::setCompatibilityEnabled(ui->enableCompatibilityCheckBox->isChecked()); + Config::setCheckCompatibilityOnStartup(ui->checkCompatibilityOnStartupCheckBox->isChecked()); #ifdef ENABLE_DISCORD_RPC auto* rpc = Common::Singleton::Instance(); diff --git a/src/qt_gui/settings_dialog.h b/src/qt_gui/settings_dialog.h index 987b35d45..892e67671 100644 --- a/src/qt_gui/settings_dialog.h +++ b/src/qt_gui/settings_dialog.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include #include @@ -10,6 +11,7 @@ #include "common/config.h" #include "common/path_util.h" +#include "qt_gui/compatibility_info.h" namespace Ui { class SettingsDialog; @@ -18,7 +20,9 @@ class SettingsDialog; class SettingsDialog : public QDialog { Q_OBJECT public: - explicit SettingsDialog(std::span physical_devices, QWidget* parent = nullptr); + explicit SettingsDialog(std::span physical_devices, + std::shared_ptr m_compat_info, + QWidget* parent = nullptr); ~SettingsDialog(); bool eventFilter(QObject* obj, QEvent* event) override; @@ -28,6 +32,7 @@ public: signals: void LanguageChanged(const std::string& locale); + void CompatibilityChanged(); private: void LoadValuesFromConfig(); diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index faa0bf847..af80743d8 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -306,7 +306,7 @@ - 275 + 265 0 @@ -574,25 +574,88 @@ - - - - - Qt::Orientation::Horizontal + + + + + + 0 + 0 + - + - 40 - 20 + 0 + 0 - - - - - + + Game Compatibility + + + + 1 + + + 11 + + + + + Display Compatibility Data + + + + + + + Update Compatibility Database On Startup + + + + + + + 1 + + + 0 + + + + + + 0 + 0 + + + + + 197 + 28 + + + + + 16777215 + 16777215 + + + + Update Compatibility Database + + + + + + + + + + + diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index ddaa4fe0d..9e6487201 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -677,6 +677,21 @@ Play title music Play title music + + + Update Compatibility Database On Startup + Update Compatibility Database On Startup + + + + Display Compatibility Data + Display Compatibility Data + + + + Update Compatibility Database + Update Compatibility Database + Volume @@ -1221,6 +1236,21 @@ backButtonBehaviorGroupBox Back Button Behavior:\nSets the controller's back button to emulate tapping the specified position on the PS4 touchpad. + + + enableCompatibilityCheckBox + Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + + + + checkCompatibilityOnStartupCheckBox + Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + + + + updateCompatibilityButton + Update Compatibility Database:\nImmediately update the compatibility database. + Never @@ -1334,6 +1364,11 @@ Serial Serial + + + Compatibility + Compatibility + Region @@ -1369,6 +1404,36 @@ Never Played Never Played + + + Compatibility is untested + Compatibility is untested + + + + Game does not initialize properly / crashes the emulator + Game does not initialize properly / crashes the emulator + + + + Game boots, but only displays a blank screen + Game boots, but only displays a blank screen + + + + Game displays an image but does not go past the menu + Game displays an image but does not go past the menu + + + + Game has game-breaking glitches or unplayable performance + Game has game-breaking glitches or unplayable performance + + + + Game can be completed with playable performance and no major glitches + Game can be completed with playable performance and no major glitches + CheckUpdate From db43c601fcf90b947624d456f0d84303e67e37e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?= Date: Wed, 25 Dec 2024 15:05:15 +0100 Subject: [PATCH 245/549] README: mention alternative key mappings on Mac (#1876) Co-authored-by: Marcin Mikolajczyk --- README.md | 52 ++++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 7ef5bdf65..7ba13ad47 100644 --- a/README.md +++ b/README.md @@ -86,32 +86,32 @@ F12 | Trigger RenderDoc Capture > [!NOTE] > Xbox and DualShock controllers work out of the box. -| Controller button | Keyboard equivelant | -|-------------|-------------| -LEFT AXIS UP | W | -LEFT AXIS DOWN | S | -LEFT AXIS LEFT | A | -LEFT AXIS RIGHT | D | -RIGHT AXIS UP | I | -RIGHT AXIS DOWN | K | -RIGHT AXIS LEFT | J | -RIGHT AXIS RIGHT | L | -TRIANGLE | Numpad 8 | -CIRCLE | Numpad 6 | -CROSS | Numpad 2 | -SQUARE | Numpad 4 | -PAD UP | UP | -PAD DOWN | DOWN | -PAD LEFT | LEFT | -PAD RIGHT | RIGHT | -OPTIONS | RETURN | -BACK BUTTON / TOUCH PAD | SPACE | -L1 | Q | -R1 | U | -L2 | E | -R2 | O | -L3 | X | -R3 | M | +| Controller button | Keyboard equivelant | Mac alternative | +|-------------|-------------|--------------| +LEFT AXIS UP | W | | +LEFT AXIS DOWN | S | | +LEFT AXIS LEFT | A | | +LEFT AXIS RIGHT | D | | +RIGHT AXIS UP | I | | +RIGHT AXIS DOWN | K | | +RIGHT AXIS LEFT | J | | +RIGHT AXIS RIGHT | L | | +TRIANGLE | Numpad 8 | C | +CIRCLE | Numpad 6 | B | +CROSS | Numpad 2 | N | +SQUARE | Numpad 4 | V | +PAD UP | UP | | +PAD DOWN | DOWN | | +PAD LEFT | LEFT | | +PAD RIGHT | RIGHT | | +OPTIONS | RETURN | | +BACK BUTTON / TOUCH PAD | SPACE | | +L1 | Q | | +R1 | U | | +L2 | E | | +R2 | O | | +L3 | X | | +R3 | M | | # Main team From 3c111202e16eed3abda04409bde2521a63217d0d Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Wed, 25 Dec 2024 06:05:51 -0800 Subject: [PATCH 246/549] renderer_vulkan: Make sure at least one viewport is set (#1859) --- .../renderer_vulkan/vk_graphics_pipeline.cpp | 30 ++---- .../renderer_vulkan/vk_rasterizer.cpp | 97 ++++++++++--------- 2 files changed, 61 insertions(+), 66 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index c01d4cbe8..ffa474a1c 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -133,37 +133,23 @@ GraphicsPipeline::GraphicsPipeline( .sampleShadingEnable = false, }; - const vk::Viewport viewport = { - .x = 0.0f, - .y = 0.0f, - .width = 1.0f, - .height = 1.0f, - .minDepth = 0.0f, - .maxDepth = 1.0f, - }; - - const vk::Rect2D scissor = { - .offset = {0, 0}, - .extent = {1, 1}, - }; - const vk::PipelineViewportDepthClipControlCreateInfoEXT clip_control = { .negativeOneToOne = key.clip_space == Liverpool::ClipSpace::MinusWToW, }; const vk::PipelineViewportStateCreateInfo viewport_info = { .pNext = instance.IsDepthClipControlSupported() ? &clip_control : nullptr, - .viewportCount = 1, - .pViewports = &viewport, - .scissorCount = 1, - .pScissors = &scissor, }; boost::container::static_vector dynamic_states = { - vk::DynamicState::eViewport, vk::DynamicState::eScissor, - vk::DynamicState::eBlendConstants, vk::DynamicState::eDepthBounds, - vk::DynamicState::eDepthBias, vk::DynamicState::eStencilReference, - vk::DynamicState::eStencilCompareMask, vk::DynamicState::eStencilWriteMask, + vk::DynamicState::eViewportWithCountEXT, + vk::DynamicState::eScissorWithCountEXT, + vk::DynamicState::eBlendConstants, + vk::DynamicState::eDepthBounds, + vk::DynamicState::eDepthBias, + vk::DynamicState::eStencilReference, + vk::DynamicState::eStencilCompareMask, + vk::DynamicState::eStencilWriteMask, vk::DynamicState::eStencilOpEXT, }; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 1e698bf6b..d458fa124 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -1019,17 +1019,44 @@ void Rasterizer::UpdateDynamicState(const GraphicsPipeline& pipeline) { } void Rasterizer::UpdateViewportScissorState() { - auto& regs = liverpool->regs; + const auto& regs = liverpool->regs; + + const auto combined_scissor_value_tl = [](s16 scr, s16 win, s16 gen, s16 win_offset) { + return std::max({scr, s16(win + win_offset), s16(gen + win_offset)}); + }; + const auto combined_scissor_value_br = [](s16 scr, s16 win, s16 gen, s16 win_offset) { + return std::min({scr, s16(win + win_offset), s16(gen + win_offset)}); + }; + const bool enable_offset = !regs.window_scissor.window_offset_disable.Value(); + + Liverpool::Scissor scsr{}; + scsr.top_left_x = combined_scissor_value_tl( + regs.screen_scissor.top_left_x, s16(regs.window_scissor.top_left_x.Value()), + s16(regs.generic_scissor.top_left_x.Value()), + enable_offset ? regs.window_offset.window_x_offset : 0); + scsr.top_left_y = combined_scissor_value_tl( + regs.screen_scissor.top_left_y, s16(regs.window_scissor.top_left_y.Value()), + s16(regs.generic_scissor.top_left_y.Value()), + enable_offset ? regs.window_offset.window_y_offset : 0); + scsr.bottom_right_x = combined_scissor_value_br( + regs.screen_scissor.bottom_right_x, regs.window_scissor.bottom_right_x, + regs.generic_scissor.bottom_right_x, + enable_offset ? regs.window_offset.window_x_offset : 0); + scsr.bottom_right_y = combined_scissor_value_br( + regs.screen_scissor.bottom_right_y, regs.window_scissor.bottom_right_y, + regs.generic_scissor.bottom_right_y, + enable_offset ? regs.window_offset.window_y_offset : 0); boost::container::static_vector viewports; boost::container::static_vector scissors; + const auto& vp_ctl = regs.viewport_control; const float reduce_z = instance.IsDepthClipControlSupported() && regs.clipper_control.clip_space == AmdGpu::Liverpool::ClipSpace::MinusWToW ? 1.0f : 0.0f; - const auto vp_ctl = regs.viewport_control; + for (u32 i = 0; i < Liverpool::NumViewports; i++) { const auto& vp = regs.viewports[i]; const auto& vp_d = regs.viewport_depths[i]; @@ -1050,53 +1077,17 @@ void Rasterizer::UpdateViewportScissorState() { .minDepth = zoffset - zscale * reduce_z, .maxDepth = zscale + zoffset, }); - } - const bool enable_offset = !regs.window_scissor.window_offset_disable.Value(); - Liverpool::Scissor scsr{}; - const auto combined_scissor_value_tl = [](s16 scr, s16 win, s16 gen, s16 win_offset) { - return std::max({scr, s16(win + win_offset), s16(gen + win_offset)}); - }; - - scsr.top_left_x = combined_scissor_value_tl( - regs.screen_scissor.top_left_x, s16(regs.window_scissor.top_left_x.Value()), - s16(regs.generic_scissor.top_left_x.Value()), - enable_offset ? regs.window_offset.window_x_offset : 0); - - scsr.top_left_y = combined_scissor_value_tl( - regs.screen_scissor.top_left_y, s16(regs.window_scissor.top_left_y.Value()), - s16(regs.generic_scissor.top_left_y.Value()), - enable_offset ? regs.window_offset.window_y_offset : 0); - - const auto combined_scissor_value_br = [](s16 scr, s16 win, s16 gen, s16 win_offset) { - return std::min({scr, s16(win + win_offset), s16(gen + win_offset)}); - }; - - scsr.bottom_right_x = combined_scissor_value_br( - regs.screen_scissor.bottom_right_x, regs.window_scissor.bottom_right_x, - regs.generic_scissor.bottom_right_x, - enable_offset ? regs.window_offset.window_x_offset : 0); - - scsr.bottom_right_y = combined_scissor_value_br( - regs.screen_scissor.bottom_right_y, regs.window_scissor.bottom_right_y, - regs.generic_scissor.bottom_right_y, - enable_offset ? regs.window_offset.window_y_offset : 0); - - for (u32 idx = 0; idx < Liverpool::NumViewports; idx++) { - if (regs.viewports[idx].xscale == 0) { - // Scissor and viewport counts should be equal. - continue; - } auto vp_scsr = scsr; if (regs.mode_control.vport_scissor_enable) { vp_scsr.top_left_x = - std::max(vp_scsr.top_left_x, s16(regs.viewport_scissors[idx].top_left_x.Value())); + std::max(vp_scsr.top_left_x, s16(regs.viewport_scissors[i].top_left_x.Value())); vp_scsr.top_left_y = - std::max(vp_scsr.top_left_y, s16(regs.viewport_scissors[idx].top_left_y.Value())); + std::max(vp_scsr.top_left_y, s16(regs.viewport_scissors[i].top_left_y.Value())); vp_scsr.bottom_right_x = - std::min(vp_scsr.bottom_right_x, regs.viewport_scissors[idx].bottom_right_x); + std::min(vp_scsr.bottom_right_x, regs.viewport_scissors[i].bottom_right_x); vp_scsr.bottom_right_y = - std::min(vp_scsr.bottom_right_y, regs.viewport_scissors[idx].bottom_right_y); + std::min(vp_scsr.bottom_right_y, regs.viewport_scissors[i].bottom_right_y); } scissors.push_back({ .offset = {vp_scsr.top_left_x, vp_scsr.top_left_y}, @@ -1104,9 +1095,27 @@ void Rasterizer::UpdateViewportScissorState() { }); } + if (viewports.empty()) { + // Vulkan requires providing at least one viewport. + constexpr vk::Viewport empty_viewport = { + .x = 0.0f, + .y = 0.0f, + .width = 1.0f, + .height = 1.0f, + .minDepth = 0.0f, + .maxDepth = 1.0f, + }; + constexpr vk::Rect2D empty_scissor = { + .offset = {0, 0}, + .extent = {1, 1}, + }; + viewports.push_back(empty_viewport); + scissors.push_back(empty_scissor); + } + const auto cmdbuf = scheduler.CommandBuffer(); - cmdbuf.setViewport(0, viewports); - cmdbuf.setScissor(0, scissors); + cmdbuf.setViewportWithCountEXT(viewports); + cmdbuf.setScissorWithCountEXT(scissors); } void Rasterizer::ScopeMarkerBegin(const std::string_view& str) { From 3ab118837aba313bb1d2d53b50347cf5c4e67727 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A5IGA?= <164882787+Xphalnos@users.noreply.github.com> Date: Wed, 25 Dec 2024 15:06:12 +0100 Subject: [PATCH 247/549] Fix for D16Unorm Tiled image (#1863) --- src/video_core/texture_cache/tile_manager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/video_core/texture_cache/tile_manager.cpp b/src/video_core/texture_cache/tile_manager.cpp index ef80f2418..f6b2e2beb 100644 --- a/src/video_core/texture_cache/tile_manager.cpp +++ b/src/video_core/texture_cache/tile_manager.cpp @@ -32,6 +32,7 @@ static vk::Format DemoteImageFormatForDetiling(vk::Format format) { case vk::Format::eR8G8Unorm: case vk::Format::eR16Sfloat: case vk::Format::eR16Unorm: + case vk::Format::eD16Unorm: return vk::Format::eR8G8Uint; case vk::Format::eR8G8B8A8Srgb: case vk::Format::eB8G8R8A8Srgb: From 3d0d1a26dfb25a1aa7ecdf4de881f663aa648726 Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Wed, 25 Dec 2024 17:05:36 -0300 Subject: [PATCH 248/549] Add missing translations (#1879) * Add missing translations * + --- src/qt_gui/settings_dialog.ui | 2 +- src/qt_gui/translations/ar.ts | 80 +++++++++++++++++++++++++++++++ src/qt_gui/translations/da_DK.ts | 80 +++++++++++++++++++++++++++++++ src/qt_gui/translations/de.ts | 80 +++++++++++++++++++++++++++++++ src/qt_gui/translations/el.ts | 80 +++++++++++++++++++++++++++++++ src/qt_gui/translations/en.ts | 10 ++++ src/qt_gui/translations/es_ES.ts | 80 +++++++++++++++++++++++++++++++ src/qt_gui/translations/fa_IR.ts | 80 +++++++++++++++++++++++++++++++ src/qt_gui/translations/fi.ts | 80 +++++++++++++++++++++++++++++++ src/qt_gui/translations/fr.ts | 80 +++++++++++++++++++++++++++++++ src/qt_gui/translations/hu_HU.ts | 82 +++++++++++++++++++++++++++++++- src/qt_gui/translations/id.ts | 80 +++++++++++++++++++++++++++++++ src/qt_gui/translations/it.ts | 82 +++++++++++++++++++++++++++++++- src/qt_gui/translations/ja_JP.ts | 80 +++++++++++++++++++++++++++++++ src/qt_gui/translations/ko_KR.ts | 80 +++++++++++++++++++++++++++++++ src/qt_gui/translations/lt_LT.ts | 80 +++++++++++++++++++++++++++++++ src/qt_gui/translations/nb.ts | 82 +++++++++++++++++++++++++++++++- src/qt_gui/translations/nl.ts | 80 +++++++++++++++++++++++++++++++ src/qt_gui/translations/pl_PL.ts | 80 +++++++++++++++++++++++++++++++ src/qt_gui/translations/pt_BR.ts | 80 +++++++++++++++++++++++++++++++ src/qt_gui/translations/ro_RO.ts | 80 +++++++++++++++++++++++++++++++ src/qt_gui/translations/ru_RU.ts | 80 +++++++++++++++++++++++++++++++ src/qt_gui/translations/sq.ts | 82 +++++++++++++++++++++++++++++++- src/qt_gui/translations/tr_TR.ts | 82 +++++++++++++++++++++++++++++++- src/qt_gui/translations/uk_UA.ts | 80 +++++++++++++++++++++++++++++++ src/qt_gui/translations/vi_VN.ts | 80 +++++++++++++++++++++++++++++++ src/qt_gui/translations/zh_CN.ts | 82 +++++++++++++++++++++++++++++++- src/qt_gui/translations/zh_TW.ts | 80 +++++++++++++++++++++++++++++++ 28 files changed, 2097 insertions(+), 7 deletions(-) diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index af80743d8..e6a9d5a58 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -11,7 +11,7 @@ 0 0 - 854 + 900 660 diff --git a/src/qt_gui/translations/ar.ts b/src/qt_gui/translations/ar.ts index 3f861187e..dad4469cb 100644 --- a/src/qt_gui/translations/ar.ts +++ b/src/qt_gui/translations/ar.ts @@ -672,11 +672,36 @@ GUI Settings إعدادات الواجهة + + + Disable Trophy Pop-ups + Disable Trophy Pop-ups + Play title music تشغيل موسيقى العنوان + + + Update Compatibility Database On Startup + Update Compatibility Database On Startup + + + + Game Compatibility + Game Compatibility + + + + Display Compatibility Data + Display Compatibility Data + + + + Update Compatibility Database + Update Compatibility Database + Volume @@ -1201,6 +1226,11 @@ GUIgroupBox تشغيل موسيقى العنوان:\nإذا كانت اللعبة تدعم ذلك، قم بتمكين تشغيل موسيقى خاصة عند اختيار اللعبة في واجهة المستخدم. + + + disableTrophycheckBox + Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + hideCursorGroupBox @@ -1216,6 +1246,21 @@ backButtonBehaviorGroupBox سلوك زر العودة:\nيضبط زر العودة في وحدة التحكم ليحاكي الضغط على الموضع المحدد على لوحة اللمس في PS4. + + + enableCompatibilityCheckBox + Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + + + + checkCompatibilityOnStartupCheckBox + Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + + + + updateCompatibilityButton + Update Compatibility Database:\nImmediately update the compatibility database. + Never @@ -1329,6 +1374,11 @@ Serial سيريال + + + Compatibility + Compatibility + Region @@ -1364,6 +1414,36 @@ Never Played Never Played + + + Compatibility is untested + Compatibility is untested + + + + Game does not initialize properly / crashes the emulator + Game does not initialize properly / crashes the emulator + + + + Game boots, but only displays a blank screen + Game boots, but only displays a blank screen + + + + Game displays an image but does not go past the menu + Game displays an image but does not go past the menu + + + + Game has game-breaking glitches or unplayable performance + Game has game-breaking glitches or unplayable performance + + + + Game can be completed with playable performance and no major glitches + Game can be completed with playable performance and no major glitches + CheckUpdate diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts index 3539159e2..2448d4426 100644 --- a/src/qt_gui/translations/da_DK.ts +++ b/src/qt_gui/translations/da_DK.ts @@ -672,11 +672,36 @@ GUI Settings GUI-Indstillinger + + + Disable Trophy Pop-ups + Disable Trophy Pop-ups + Play title music Afspil titelsang + + + Update Compatibility Database On Startup + Update Compatibility Database On Startup + + + + Game Compatibility + Game Compatibility + + + + Display Compatibility Data + Display Compatibility Data + + + + Update Compatibility Database + Update Compatibility Database + Volume @@ -1201,6 +1226,11 @@ GUIgroupBox Titelsmusikafspilning:\nHvis spillet understøtter det, aktiver speciel musik, når spillet vælges i brugergrænsefladen. + + + disableTrophycheckBox + Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + hideCursorGroupBox @@ -1216,6 +1246,21 @@ backButtonBehaviorGroupBox Tilbageknap Adfærd:\nIndstiller controllerens tilbageknap til at efterligne tryk på den angivne position på PS4 berøringsflade. + + + enableCompatibilityCheckBox + Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + + + + checkCompatibilityOnStartupCheckBox + Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + + + + updateCompatibilityButton + Update Compatibility Database:\nImmediately update the compatibility database. + Never @@ -1329,6 +1374,11 @@ Serial Seriel + + + Compatibility + Compatibility + Region @@ -1364,6 +1414,36 @@ Never Played Never Played + + + Compatibility is untested + Compatibility is untested + + + + Game does not initialize properly / crashes the emulator + Game does not initialize properly / crashes the emulator + + + + Game boots, but only displays a blank screen + Game boots, but only displays a blank screen + + + + Game displays an image but does not go past the menu + Game displays an image but does not go past the menu + + + + Game has game-breaking glitches or unplayable performance + Game has game-breaking glitches or unplayable performance + + + + Game can be completed with playable performance and no major glitches + Game can be completed with playable performance and no major glitches + CheckUpdate diff --git a/src/qt_gui/translations/de.ts b/src/qt_gui/translations/de.ts index f34402ac9..dd98c209b 100644 --- a/src/qt_gui/translations/de.ts +++ b/src/qt_gui/translations/de.ts @@ -672,11 +672,36 @@ GUI Settings GUI-Einstellungen + + + Disable Trophy Pop-ups + Disable Trophy Pop-ups + Play title music Titelmusik abspielen + + + Update Compatibility Database On Startup + Update Compatibility Database On Startup + + + + Game Compatibility + Game Compatibility + + + + Display Compatibility Data + Display Compatibility Data + + + + Update Compatibility Database + Update Compatibility Database + Volume @@ -1201,6 +1226,11 @@ GUIgroupBox Wiedergabe der Titelmusik:\nWenn das Spiel dies unterstützt, wird beim Auswählen des Spiels in der Benutzeroberfläche spezielle Musik abgespielt. + + + disableTrophycheckBox + Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + hideCursorGroupBox @@ -1216,6 +1246,21 @@ backButtonBehaviorGroupBox Zurück-Button Verhalten:\nStellt die Zurück-Taste des Controllers so ein, dass sie das Antippen der angegebenen Position auf dem PS4-Touchpad emuliert. + + + enableCompatibilityCheckBox + Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + + + + checkCompatibilityOnStartupCheckBox + Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + + + + updateCompatibilityButton + Update Compatibility Database:\nImmediately update the compatibility database. + Never @@ -1329,6 +1374,11 @@ Serial Seriennummer + + + Compatibility + Compatibility + Region @@ -1364,6 +1414,36 @@ Never Played Never Played + + + Compatibility is untested + Compatibility is untested + + + + Game does not initialize properly / crashes the emulator + Game does not initialize properly / crashes the emulator + + + + Game boots, but only displays a blank screen + Game boots, but only displays a blank screen + + + + Game displays an image but does not go past the menu + Game displays an image but does not go past the menu + + + + Game has game-breaking glitches or unplayable performance + Game has game-breaking glitches or unplayable performance + + + + Game can be completed with playable performance and no major glitches + Game can be completed with playable performance and no major glitches + CheckUpdate diff --git a/src/qt_gui/translations/el.ts b/src/qt_gui/translations/el.ts index 65cee641a..afb8ebddf 100644 --- a/src/qt_gui/translations/el.ts +++ b/src/qt_gui/translations/el.ts @@ -672,11 +672,36 @@ GUI Settings Ρυθμίσεις GUI + + + Disable Trophy Pop-ups + Disable Trophy Pop-ups + Play title music Αναπαραγωγή μουσικής τίτλου + + + Update Compatibility Database On Startup + Update Compatibility Database On Startup + + + + Game Compatibility + Game Compatibility + + + + Display Compatibility Data + Display Compatibility Data + + + + Update Compatibility Database + Update Compatibility Database + Volume @@ -1201,6 +1226,11 @@ GUIgroupBox Αναπαραγωγή Μουσικής Τίτλων:\nΕάν το παιχνίδι το υποστηρίζει, ενεργοποιεί ειδική μουσική κατά την επιλογή του παιχνιδιού από τη διεπαφή χρήστη. + + + disableTrophycheckBox + Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + hideCursorGroupBox @@ -1216,6 +1246,21 @@ backButtonBehaviorGroupBox Συμπεριφορά Κουμπιού Επιστροφής:\nΟρίζει το κουμπί επιστροφής του ελεγκτή να προσομοιώνει το πάτημα της καθορισμένης θέσης στην οθόνη αφής PS4. + + + enableCompatibilityCheckBox + Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + + + + checkCompatibilityOnStartupCheckBox + Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + + + + updateCompatibilityButton + Update Compatibility Database:\nImmediately update the compatibility database. + Never @@ -1329,6 +1374,11 @@ Serial Σειριακός αριθμός + + + Compatibility + Compatibility + Region @@ -1364,6 +1414,36 @@ Never Played Never Played + + + Compatibility is untested + Compatibility is untested + + + + Game does not initialize properly / crashes the emulator + Game does not initialize properly / crashes the emulator + + + + Game boots, but only displays a blank screen + Game boots, but only displays a blank screen + + + + Game displays an image but does not go past the menu + Game displays an image but does not go past the menu + + + + Game has game-breaking glitches or unplayable performance + Game has game-breaking glitches or unplayable performance + + + + Game can be completed with playable performance and no major glitches + Game can be completed with playable performance and no major glitches + CheckUpdate diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index 9e6487201..1bbc68e61 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -672,6 +672,11 @@ GUI Settings GUI Settings + + + Disable Trophy Pop-ups + Disable Trophy Pop-ups + Play title music @@ -682,6 +687,11 @@ Update Compatibility Database On Startup Update Compatibility Database On Startup + + + Game Compatibility + Game Compatibility + Display Compatibility Data diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index 3d1f291a6..badb18d2f 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -672,11 +672,36 @@ GUI Settings Configuraciones de la Interfaz + + + Disable Trophy Pop-ups + Disable Trophy Pop-ups + Play title music Reproducir la música de apertura + + + Update Compatibility Database On Startup + Update Compatibility Database On Startup + + + + Game Compatibility + Game Compatibility + + + + Display Compatibility Data + Display Compatibility Data + + + + Update Compatibility Database + Update Compatibility Database + Volume @@ -1201,6 +1226,11 @@ GUIgroupBox Reproducir Música del Título:\nSi un juego lo admite, habilita la reproducción de música especial al seleccionar el juego en la interfaz gráfica. + + + disableTrophycheckBox + Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + hideCursorGroupBox @@ -1216,6 +1246,21 @@ backButtonBehaviorGroupBox Comportamiento del Botón Atrás:\nEstablece el botón atrás del controlador para emular el toque en la posición especificada en el touchpad del PS4. + + + enableCompatibilityCheckBox + Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + + + + checkCompatibilityOnStartupCheckBox + Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + + + + updateCompatibilityButton + Update Compatibility Database:\nImmediately update the compatibility database. + Never @@ -1329,6 +1374,11 @@ Serial Numero de serie + + + Compatibility + Compatibility + Region @@ -1364,6 +1414,36 @@ Never Played Never Played + + + Compatibility is untested + Compatibility is untested + + + + Game does not initialize properly / crashes the emulator + Game does not initialize properly / crashes the emulator + + + + Game boots, but only displays a blank screen + Game boots, but only displays a blank screen + + + + Game displays an image but does not go past the menu + Game displays an image but does not go past the menu + + + + Game has game-breaking glitches or unplayable performance + Game has game-breaking glitches or unplayable performance + + + + Game can be completed with playable performance and no major glitches + Game can be completed with playable performance and no major glitches + CheckUpdate diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index 58de03346..7b6583f45 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -672,11 +672,36 @@ GUI Settings تنظیمات رابط کاربری + + + Disable Trophy Pop-ups + Disable Trophy Pop-ups + Play title music پخش موسیقی عنوان + + + Update Compatibility Database On Startup + Update Compatibility Database On Startup + + + + Game Compatibility + Game Compatibility + + + + Display Compatibility Data + Display Compatibility Data + + + + Update Compatibility Database + Update Compatibility Database + Volume @@ -1201,6 +1226,11 @@ GUIgroupBox Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI. + + + disableTrophycheckBox + Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + hideCursorGroupBox @@ -1216,6 +1246,21 @@ backButtonBehaviorGroupBox رفتار دکمه برگشت:\nدکمه برگشت کنترلر را طوری تنظیم می کند که ضربه زدن روی موقعیت مشخص شده روی صفحه لمسی PS4 را شبیه سازی کند. + + + enableCompatibilityCheckBox + Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + + + + checkCompatibilityOnStartupCheckBox + Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + + + + updateCompatibilityButton + Update Compatibility Database:\nImmediately update the compatibility database. + Never @@ -1329,6 +1374,11 @@ Serial سریال + + + Compatibility + Compatibility + Region @@ -1364,6 +1414,36 @@ Never Played Never Played + + + Compatibility is untested + Compatibility is untested + + + + Game does not initialize properly / crashes the emulator + Game does not initialize properly / crashes the emulator + + + + Game boots, but only displays a blank screen + Game boots, but only displays a blank screen + + + + Game displays an image but does not go past the menu + Game displays an image but does not go past the menu + + + + Game has game-breaking glitches or unplayable performance + Game has game-breaking glitches or unplayable performance + + + + Game can be completed with playable performance and no major glitches + Game can be completed with playable performance and no major glitches + CheckUpdate diff --git a/src/qt_gui/translations/fi.ts b/src/qt_gui/translations/fi.ts index 0a7f2b250..7c933c5ec 100644 --- a/src/qt_gui/translations/fi.ts +++ b/src/qt_gui/translations/fi.ts @@ -672,11 +672,36 @@ GUI Settings GUI-Asetukset + + + Disable Trophy Pop-ups + Disable Trophy Pop-ups + Play title music Soita otsikkomusiikkia + + + Update Compatibility Database On Startup + Update Compatibility Database On Startup + + + + Game Compatibility + Game Compatibility + + + + Display Compatibility Data + Display Compatibility Data + + + + Update Compatibility Database + Update Compatibility Database + Volume @@ -1201,6 +1226,11 @@ GUIgroupBox Soita Otsikkomusiikkia:\nJos peli tukee sitä, ota käyttöön erityisen musiikin soittaminen pelin valinnan yhteydessä käyttöliittymässä. + + + disableTrophycheckBox + Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + hideCursorGroupBox @@ -1216,6 +1246,21 @@ backButtonBehaviorGroupBox Takaisin-napin käyttäytyminen:\nAsettaa ohjaimen takaisin-napin jäljittelemään kosketusta PS4:n kosketuslevyn määritettyyn kohtaan. + + + enableCompatibilityCheckBox + Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + + + + checkCompatibilityOnStartupCheckBox + Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + + + + updateCompatibilityButton + Update Compatibility Database:\nImmediately update the compatibility database. + Never @@ -1329,6 +1374,11 @@ Serial Sarjanumero + + + Compatibility + Compatibility + Region @@ -1364,6 +1414,36 @@ Never Played Never Played + + + Compatibility is untested + Compatibility is untested + + + + Game does not initialize properly / crashes the emulator + Game does not initialize properly / crashes the emulator + + + + Game boots, but only displays a blank screen + Game boots, but only displays a blank screen + + + + Game displays an image but does not go past the menu + Game displays an image but does not go past the menu + + + + Game has game-breaking glitches or unplayable performance + Game has game-breaking glitches or unplayable performance + + + + Game can be completed with playable performance and no major glitches + Game can be completed with playable performance and no major glitches + CheckUpdate diff --git a/src/qt_gui/translations/fr.ts b/src/qt_gui/translations/fr.ts index fad90622a..7a61dc292 100644 --- a/src/qt_gui/translations/fr.ts +++ b/src/qt_gui/translations/fr.ts @@ -672,11 +672,36 @@ GUI Settings Paramètres de l'interface + + + Disable Trophy Pop-ups + Disable Trophy Pop-ups + Play title music Lire la musique du titre + + + Update Compatibility Database On Startup + Update Compatibility Database On Startup + + + + Game Compatibility + Game Compatibility + + + + Display Compatibility Data + Display Compatibility Data + + + + Update Compatibility Database + Update Compatibility Database + Volume @@ -1201,6 +1226,11 @@ GUIgroupBox Jouer de la musique de titre:\nSi le jeu le prend en charge, cela active la musique spéciale lorsque vous sélectionnez le jeu dans l'interface utilisateur. + + + disableTrophycheckBox + Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + hideCursorGroupBox @@ -1216,6 +1246,21 @@ backButtonBehaviorGroupBox Comportement du bouton retour:\nDéfinit le bouton de retour de la manette pour imiter le toucher de la position spécifiée sur le pavé tactile PS4. + + + enableCompatibilityCheckBox + Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + + + + checkCompatibilityOnStartupCheckBox + Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + + + + updateCompatibilityButton + Update Compatibility Database:\nImmediately update the compatibility database. + Never @@ -1329,6 +1374,11 @@ Serial Série + + + Compatibility + Compatibility + Region @@ -1364,6 +1414,36 @@ Never Played Jamais joué + + + Compatibility is untested + Compatibility is untested + + + + Game does not initialize properly / crashes the emulator + Game does not initialize properly / crashes the emulator + + + + Game boots, but only displays a blank screen + Game boots, but only displays a blank screen + + + + Game displays an image but does not go past the menu + Game displays an image but does not go past the menu + + + + Game has game-breaking glitches or unplayable performance + Game has game-breaking glitches or unplayable performance + + + + Game can be completed with playable performance and no major glitches + Game can be completed with playable performance and no major glitches + CheckUpdate diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index 937e3f188..bf0b26988 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -672,11 +672,36 @@ GUI Settings GUI Beállítások + + + Disable Trophy Pop-ups + Disable Trophy Pop-ups + Play title music Címzene lejátszása + + + Update Compatibility Database On Startup + Update Compatibility Database On Startup + + + + Game Compatibility + Game Compatibility + + + + Display Compatibility Data + Display Compatibility Data + + + + Update Compatibility Database + Update Compatibility Database + Volume @@ -1201,6 +1226,11 @@ GUIgroupBox Játék címzene lejátszása:\nHa a játék támogatja, engedélyezze egy speciális zene lejátszását, amikor a játékot kiválasztja a GUI-ban. + + + disableTrophycheckBox + Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + hideCursorGroupBox @@ -1216,6 +1246,21 @@ backButtonBehaviorGroupBox Vissza gomb viselkedés:\nBeállítja a vezérlő vissza gombját, hogy utánozza a PS4 érintőpadján megadott pozíció megérintését. + + + enableCompatibilityCheckBox + Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + + + + checkCompatibilityOnStartupCheckBox + Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + + + + updateCompatibilityButton + Update Compatibility Database:\nImmediately update the compatibility database. + Never @@ -1329,6 +1374,11 @@ Serial Sorozatszám + + + Compatibility + Compatibility + Region @@ -1364,6 +1414,36 @@ Never Played Never Played + + + Compatibility is untested + Compatibility is untested + + + + Game does not initialize properly / crashes the emulator + Game does not initialize properly / crashes the emulator + + + + Game boots, but only displays a blank screen + Game boots, but only displays a blank screen + + + + Game displays an image but does not go past the menu + Game displays an image but does not go past the menu + + + + Game has game-breaking glitches or unplayable performance + Game has game-breaking glitches or unplayable performance + + + + Game can be completed with playable performance and no major glitches + Game can be completed with playable performance and no major glitches + CheckUpdate @@ -1493,4 +1573,4 @@ A frissítési szkript fájl létrehozása nem sikerült - + \ No newline at end of file diff --git a/src/qt_gui/translations/id.ts b/src/qt_gui/translations/id.ts index 80873daa9..098c17bc4 100644 --- a/src/qt_gui/translations/id.ts +++ b/src/qt_gui/translations/id.ts @@ -672,11 +672,36 @@ GUI Settings Pengaturan GUI + + + Disable Trophy Pop-ups + Disable Trophy Pop-ups + Play title music Putar musik judul + + + Update Compatibility Database On Startup + Update Compatibility Database On Startup + + + + Game Compatibility + Game Compatibility + + + + Display Compatibility Data + Display Compatibility Data + + + + Update Compatibility Database + Update Compatibility Database + Volume @@ -1201,6 +1226,11 @@ GUIgroupBox Putar Musik Judul Permainan:\nJika permainan mendukungnya, aktifkan pemutaran musik khusus saat memilih permainan di GUI. + + + disableTrophycheckBox + Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + hideCursorGroupBox @@ -1216,6 +1246,21 @@ backButtonBehaviorGroupBox Perilaku Tombol Kembali:\nMengatur tombol kembali pada pengontrol untuk meniru ketukan di posisi yang ditentukan di touchpad PS4. + + + enableCompatibilityCheckBox + Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + + + + checkCompatibilityOnStartupCheckBox + Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + + + + updateCompatibilityButton + Update Compatibility Database:\nImmediately update the compatibility database. + Never @@ -1329,6 +1374,11 @@ Serial Serial + + + Compatibility + Compatibility + Region @@ -1364,6 +1414,36 @@ Never Played Never Played + + + Compatibility is untested + Compatibility is untested + + + + Game does not initialize properly / crashes the emulator + Game does not initialize properly / crashes the emulator + + + + Game boots, but only displays a blank screen + Game boots, but only displays a blank screen + + + + Game displays an image but does not go past the menu + Game displays an image but does not go past the menu + + + + Game has game-breaking glitches or unplayable performance + Game has game-breaking glitches or unplayable performance + + + + Game can be completed with playable performance and no major glitches + Game can be completed with playable performance and no major glitches + CheckUpdate diff --git a/src/qt_gui/translations/it.ts b/src/qt_gui/translations/it.ts index 9094a7ed5..dfbbf1cf3 100644 --- a/src/qt_gui/translations/it.ts +++ b/src/qt_gui/translations/it.ts @@ -672,11 +672,36 @@ GUI Settings Impostazioni GUI + + + Disable Trophy Pop-ups + Disable Trophy Pop-ups + Play title music Riproduci musica del titolo + + + Update Compatibility Database On Startup + Update Compatibility Database On Startup + + + + Game Compatibility + Game Compatibility + + + + Display Compatibility Data + Display Compatibility Data + + + + Update Compatibility Database + Update Compatibility Database + Volume @@ -1201,6 +1226,11 @@ GUIgroupBox Riproduci Musica del Titolo:\nSe un gioco lo supporta, attiva la riproduzione di musica speciale quando selezioni il gioco nell'interfaccia grafica. + + + disableTrophycheckBox + Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + hideCursorGroupBox @@ -1216,6 +1246,21 @@ backButtonBehaviorGroupBox Comportamento del pulsante Indietro:\nImposta il pulsante Indietro del controller per emulare il tocco sulla posizione specificata sul touchpad PS4. + + + enableCompatibilityCheckBox + Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + + + + checkCompatibilityOnStartupCheckBox + Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + + + + updateCompatibilityButton + Update Compatibility Database:\nImmediately update the compatibility database. + Never @@ -1329,6 +1374,11 @@ Serial Seriale + + + Compatibility + Compatibility + Region @@ -1364,6 +1414,36 @@ Never Played Never Played + + + Compatibility is untested + Compatibility is untested + + + + Game does not initialize properly / crashes the emulator + Game does not initialize properly / crashes the emulator + + + + Game boots, but only displays a blank screen + Game boots, but only displays a blank screen + + + + Game displays an image but does not go past the menu + Game displays an image but does not go past the menu + + + + Game has game-breaking glitches or unplayable performance + Game has game-breaking glitches or unplayable performance + + + + Game can be completed with playable performance and no major glitches + Game can be completed with playable performance and no major glitches + CheckUpdate @@ -1493,4 +1573,4 @@ Impossibile creare il file di script di aggiornamento - + \ No newline at end of file diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index ad1f383fe..c5ee65ce0 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -672,11 +672,36 @@ GUI Settings GUI設定 + + + Disable Trophy Pop-ups + Disable Trophy Pop-ups + Play title music タイトル音楽を再生する + + + Update Compatibility Database On Startup + Update Compatibility Database On Startup + + + + Game Compatibility + Game Compatibility + + + + Display Compatibility Data + Display Compatibility Data + + + + Update Compatibility Database + Update Compatibility Database + Volume @@ -1201,6 +1226,11 @@ GUIgroupBox タイトルミュージックを再生:\nゲームがそれをサポートしている場合、GUIでゲームを選択したときに特別な音楽を再生することを有効にします。 + + + disableTrophycheckBox + Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + hideCursorGroupBox @@ -1216,6 +1246,21 @@ backButtonBehaviorGroupBox 戻るボタンの動作:\nコントローラーの戻るボタンを、PS4のタッチパッドの指定された位置をタッチするように設定します。 + + + enableCompatibilityCheckBox + Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + + + + checkCompatibilityOnStartupCheckBox + Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + + + + updateCompatibilityButton + Update Compatibility Database:\nImmediately update the compatibility database. + Never @@ -1329,6 +1374,11 @@ Serial シリアル + + + Compatibility + Compatibility + Region @@ -1364,6 +1414,36 @@ Never Played Never Played + + + Compatibility is untested + Compatibility is untested + + + + Game does not initialize properly / crashes the emulator + Game does not initialize properly / crashes the emulator + + + + Game boots, but only displays a blank screen + Game boots, but only displays a blank screen + + + + Game displays an image but does not go past the menu + Game displays an image but does not go past the menu + + + + Game has game-breaking glitches or unplayable performance + Game has game-breaking glitches or unplayable performance + + + + Game can be completed with playable performance and no major glitches + Game can be completed with playable performance and no major glitches + CheckUpdate diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts index a528db295..e47db791a 100644 --- a/src/qt_gui/translations/ko_KR.ts +++ b/src/qt_gui/translations/ko_KR.ts @@ -672,11 +672,36 @@ GUI Settings GUI Settings + + + Disable Trophy Pop-ups + Disable Trophy Pop-ups + Play title music Play title music + + + Update Compatibility Database On Startup + Update Compatibility Database On Startup + + + + Game Compatibility + Game Compatibility + + + + Display Compatibility Data + Display Compatibility Data + + + + Update Compatibility Database + Update Compatibility Database + Volume @@ -1201,6 +1226,11 @@ GUIgroupBox Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI. + + + disableTrophycheckBox + Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + hideCursorGroupBox @@ -1216,6 +1246,21 @@ backButtonBehaviorGroupBox Back Button Behavior:\nSets the controller's back button to emulate tapping the specified position on the PS4 touchpad. + + + enableCompatibilityCheckBox + Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + + + + checkCompatibilityOnStartupCheckBox + Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + + + + updateCompatibilityButton + Update Compatibility Database:\nImmediately update the compatibility database. + Never @@ -1329,6 +1374,11 @@ Serial Serial + + + Compatibility + Compatibility + Region @@ -1364,6 +1414,36 @@ Never Played Never Played + + + Compatibility is untested + Compatibility is untested + + + + Game does not initialize properly / crashes the emulator + Game does not initialize properly / crashes the emulator + + + + Game boots, but only displays a blank screen + Game boots, but only displays a blank screen + + + + Game displays an image but does not go past the menu + Game displays an image but does not go past the menu + + + + Game has game-breaking glitches or unplayable performance + Game has game-breaking glitches or unplayable performance + + + + Game can be completed with playable performance and no major glitches + Game can be completed with playable performance and no major glitches + CheckUpdate diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts index 4a2820399..ba5308358 100644 --- a/src/qt_gui/translations/lt_LT.ts +++ b/src/qt_gui/translations/lt_LT.ts @@ -672,11 +672,36 @@ GUI Settings GUI Nustatymai + + + Disable Trophy Pop-ups + Disable Trophy Pop-ups + Play title music Groti antraštės muziką + + + Update Compatibility Database On Startup + Update Compatibility Database On Startup + + + + Game Compatibility + Game Compatibility + + + + Display Compatibility Data + Display Compatibility Data + + + + Update Compatibility Database + Update Compatibility Database + Volume @@ -1201,6 +1226,11 @@ GUIgroupBox Groti antraščių muziką:\nJei žaidimas tai palaiko, įjungia specialios muzikos grojimą, kai pasirinkite žaidimą GUI. + + + disableTrophycheckBox + Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + hideCursorGroupBox @@ -1216,6 +1246,21 @@ backButtonBehaviorGroupBox Atgal mygtuko elgesys:\nNustato valdiklio atgal mygtuką imituoti paspaudimą nurodytoje vietoje PS4 jutiklinėje plokštėje. + + + enableCompatibilityCheckBox + Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + + + + checkCompatibilityOnStartupCheckBox + Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + + + + updateCompatibilityButton + Update Compatibility Database:\nImmediately update the compatibility database. + Never @@ -1329,6 +1374,11 @@ Serial Serijinis numeris + + + Compatibility + Compatibility + Region @@ -1364,6 +1414,36 @@ Never Played Never Played + + + Compatibility is untested + Compatibility is untested + + + + Game does not initialize properly / crashes the emulator + Game does not initialize properly / crashes the emulator + + + + Game boots, but only displays a blank screen + Game boots, but only displays a blank screen + + + + Game displays an image but does not go past the menu + Game displays an image but does not go past the menu + + + + Game has game-breaking glitches or unplayable performance + Game has game-breaking glitches or unplayable performance + + + + Game can be completed with playable performance and no major glitches + Game can be completed with playable performance and no major glitches + CheckUpdate diff --git a/src/qt_gui/translations/nb.ts b/src/qt_gui/translations/nb.ts index 028646740..8fe4cec9b 100644 --- a/src/qt_gui/translations/nb.ts +++ b/src/qt_gui/translations/nb.ts @@ -672,11 +672,36 @@ GUI Settings GUI-Innstillinger + + + Disable Trophy Pop-ups + Disable Trophy Pop-ups + Play title music Spill tittelmusikk + + + Update Compatibility Database On Startup + Update Compatibility Database On Startup + + + + Game Compatibility + Game Compatibility + + + + Display Compatibility Data + Display Compatibility Data + + + + Update Compatibility Database + Update Compatibility Database + Volume @@ -1201,6 +1226,11 @@ GUIgroupBox Spille tittelmusikk:\nHvis et spill støtter det, så aktiveres det spesiell musikk når du velger spillet i menyen. + + + disableTrophycheckBox + Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + hideCursorGroupBox @@ -1216,6 +1246,21 @@ backButtonBehaviorGroupBox Atferd for tilbaketasten:\nSetter tilbaketasten på kontrolleren til å imitere et trykk på den angitte posisjonen på PS4s berøringsplate. + + + enableCompatibilityCheckBox + Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + + + + checkCompatibilityOnStartupCheckBox + Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + + + + updateCompatibilityButton + Update Compatibility Database:\nImmediately update the compatibility database. + Never @@ -1329,6 +1374,11 @@ Serial Serienummer + + + Compatibility + Compatibility + Region @@ -1364,6 +1414,36 @@ Never Played Never Played + + + Compatibility is untested + Compatibility is untested + + + + Game does not initialize properly / crashes the emulator + Game does not initialize properly / crashes the emulator + + + + Game boots, but only displays a blank screen + Game boots, but only displays a blank screen + + + + Game displays an image but does not go past the menu + Game displays an image but does not go past the menu + + + + Game has game-breaking glitches or unplayable performance + Game has game-breaking glitches or unplayable performance + + + + Game can be completed with playable performance and no major glitches + Game can be completed with playable performance and no major glitches + CheckUpdate @@ -1493,4 +1573,4 @@ Kunne ikke opprette oppdateringsskriptfilen - + \ No newline at end of file diff --git a/src/qt_gui/translations/nl.ts b/src/qt_gui/translations/nl.ts index b66cb94e4..4dd8813b0 100644 --- a/src/qt_gui/translations/nl.ts +++ b/src/qt_gui/translations/nl.ts @@ -672,11 +672,36 @@ GUI Settings GUI-Instellingen + + + Disable Trophy Pop-ups + Disable Trophy Pop-ups + Play title music Titelmuziek afspelen + + + Update Compatibility Database On Startup + Update Compatibility Database On Startup + + + + Game Compatibility + Game Compatibility + + + + Display Compatibility Data + Display Compatibility Data + + + + Update Compatibility Database + Update Compatibility Database + Volume @@ -1201,6 +1226,11 @@ GUIgroupBox Speel titelsong:\nAls een game dit ondersteunt, wordt speciale muziek afgespeeld wanneer je het spel in de GUI selecteert. + + + disableTrophycheckBox + Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + hideCursorGroupBox @@ -1216,6 +1246,21 @@ backButtonBehaviorGroupBox Gedrag van de terugknop:\nStelt de terugknop van de controller in om een aanraking op de opgegeven positie op de PS4-touchpad na te bootsen. + + + enableCompatibilityCheckBox + Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + + + + checkCompatibilityOnStartupCheckBox + Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + + + + updateCompatibilityButton + Update Compatibility Database:\nImmediately update the compatibility database. + Never @@ -1329,6 +1374,11 @@ Serial Serienummer + + + Compatibility + Compatibility + Region @@ -1364,6 +1414,36 @@ Never Played Never Played + + + Compatibility is untested + Compatibility is untested + + + + Game does not initialize properly / crashes the emulator + Game does not initialize properly / crashes the emulator + + + + Game boots, but only displays a blank screen + Game boots, but only displays a blank screen + + + + Game displays an image but does not go past the menu + Game displays an image but does not go past the menu + + + + Game has game-breaking glitches or unplayable performance + Game has game-breaking glitches or unplayable performance + + + + Game can be completed with playable performance and no major glitches + Game can be completed with playable performance and no major glitches + CheckUpdate diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index 8236cf720..012f92164 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -672,11 +672,36 @@ GUI Settings Ustawienia Interfejsu + + + Disable Trophy Pop-ups + Disable Trophy Pop-ups + Play title music Odtwórz muzykę tytułową + + + Update Compatibility Database On Startup + Update Compatibility Database On Startup + + + + Game Compatibility + Game Compatibility + + + + Display Compatibility Data + Display Compatibility Data + + + + Update Compatibility Database + Update Compatibility Database + Volume @@ -1201,6 +1226,11 @@ GUIgroupBox Odtwórz muzykę tytułową:\nJeśli gra to obsługuje, aktywuje odtwarzanie specjalnej muzyki podczas wybierania gry w GUI. + + + disableTrophycheckBox + Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + hideCursorGroupBox @@ -1216,6 +1246,21 @@ backButtonBehaviorGroupBox Zachowanie przycisku Wstecz:\nUstawia przycisk Wstecz kontrolera tak, aby emulował dotknięcie określonego miejsca na panelu dotykowym PS4. + + + enableCompatibilityCheckBox + Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + + + + checkCompatibilityOnStartupCheckBox + Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + + + + updateCompatibilityButton + Update Compatibility Database:\nImmediately update the compatibility database. + Never @@ -1329,6 +1374,11 @@ Serial Numer seryjny + + + Compatibility + Compatibility + Region @@ -1364,6 +1414,36 @@ Never Played Never Played + + + Compatibility is untested + Compatibility is untested + + + + Game does not initialize properly / crashes the emulator + Game does not initialize properly / crashes the emulator + + + + Game boots, but only displays a blank screen + Game boots, but only displays a blank screen + + + + Game displays an image but does not go past the menu + Game displays an image but does not go past the menu + + + + Game has game-breaking glitches or unplayable performance + Game has game-breaking glitches or unplayable performance + + + + Game can be completed with playable performance and no major glitches + Game can be completed with playable performance and no major glitches + CheckUpdate diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index 5faccf6c5..2d40781cf 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -672,11 +672,36 @@ GUI Settings Configurações da Interface + + + Disable Trophy Pop-ups + Desabilitar Pop-ups dos Troféus + Play title music Reproduzir música de abertura + + + Update Compatibility Database On Startup + Atualizar Compatibilidade ao Inicializar + + + + Game Compatibility + Compatibilidade dos Jogos + + + + Display Compatibility Data + Exibir Dados de Compatibilidade + + + + Update Compatibility Database + Atualizar Lista de Compatibilidade + Volume @@ -1201,6 +1226,11 @@ GUIgroupBox Reproduzir música de abertura:\nSe o jogo suportar, ativa a reprodução de uma música especial ao selecionar o jogo na interface do menu. + + + disableTrophycheckBox + Desabilitar pop-ups dos troféus:\nDesabilite notificações de troféus no jogo. O progresso do troféu ainda pode ser rastreado usando o Trophy Viewer (clique com o botão direito do mouse no jogo na janela principal). + hideCursorGroupBox @@ -1216,6 +1246,21 @@ backButtonBehaviorGroupBox Comportamento do botão Voltar:\nDefine o botão Voltar do controle para emular o toque na posição especificada no touchpad do PS4. + + + enableCompatibilityCheckBox + Exibir Dados de Compatibilidade:\nExibe informações de compatibilidade dos jogos na janela principal.\nHabilitar "Atualizar Compatibilidade ao Inicializar" para obter informações atualizadas. + + + + checkCompatibilityOnStartupCheckBox + Atualizar Compatibilidade ao inicializar:\nAtualiza automaticamente o banco de dados de compatibilidade quando o SHADPS4 é iniciado. + + + + updateCompatibilityButton + Atualizar Lista de Compatibilidade:\nAtualizar imediatamente o banco de dados de compatibilidade. + Never @@ -1329,6 +1374,11 @@ Serial Serial + + + Compatibility + Compatibilidade + Region @@ -1364,6 +1414,36 @@ Never Played Nunca jogado + + + Compatibility is untested + Compatibilidade não testada + + + + Game does not initialize properly / crashes the emulator + Jogo não inicializa corretamente / trava o emulador + + + + Game boots, but only displays a blank screen + O jogo inicializa, mas exibe apenas uma tela vazia + + + + Game displays an image but does not go past the menu + Jogo exibe imagem mas não passa do menu + + + + Game has game-breaking glitches or unplayable performance + O jogo tem falhas que interrompem o jogo ou desempenho injogável + + + + Game can be completed with playable performance and no major glitches + O jogo pode ser concluído com desempenho jogável e sem grandes falhas + CheckUpdate diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index 2439e69e2..aa72049af 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -672,11 +672,36 @@ GUI Settings Setări GUI + + + Disable Trophy Pop-ups + Disable Trophy Pop-ups + Play title music Redă muzica titlului + + + Update Compatibility Database On Startup + Update Compatibility Database On Startup + + + + Game Compatibility + Game Compatibility + + + + Display Compatibility Data + Display Compatibility Data + + + + Update Compatibility Database + Update Compatibility Database + Volume @@ -1201,6 +1226,11 @@ GUIgroupBox Redă muzica titlului:\nDacă un joc o suportă, activează redarea muzicii speciale când selectezi jocul în GUI. + + + disableTrophycheckBox + Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + hideCursorGroupBox @@ -1216,6 +1246,21 @@ backButtonBehaviorGroupBox Comportamentul butonului înapoi:\nSetează butonul înapoi al controlerului să imite atingerea poziției specificate pe touchpad-ul PS4. + + + enableCompatibilityCheckBox + Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + + + + checkCompatibilityOnStartupCheckBox + Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + + + + updateCompatibilityButton + Update Compatibility Database:\nImmediately update the compatibility database. + Never @@ -1329,6 +1374,11 @@ Serial Serie + + + Compatibility + Compatibility + Region @@ -1364,6 +1414,36 @@ Never Played Never Played + + + Compatibility is untested + Compatibility is untested + + + + Game does not initialize properly / crashes the emulator + Game does not initialize properly / crashes the emulator + + + + Game boots, but only displays a blank screen + Game boots, but only displays a blank screen + + + + Game displays an image but does not go past the menu + Game displays an image but does not go past the menu + + + + Game has game-breaking glitches or unplayable performance + Game has game-breaking glitches or unplayable performance + + + + Game can be completed with playable performance and no major glitches + Game can be completed with playable performance and no major glitches + CheckUpdate diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index ccee34517..67a38eeba 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -672,11 +672,36 @@ GUI Settings Интерфейс + + + Disable Trophy Pop-ups + Disable Trophy Pop-ups + Play title music Играть заглавную музыку + + + Update Compatibility Database On Startup + Update Compatibility Database On Startup + + + + Game Compatibility + Game Compatibility + + + + Display Compatibility Data + Display Compatibility Data + + + + Update Compatibility Database + Update Compatibility Database + Volume @@ -1201,6 +1226,11 @@ GUIgroupBox Играть заглавную музыку:\nВключает воспроизведение специальной музыки при выборе игры в списке, если она это поддерживает. + + + disableTrophycheckBox + Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + hideCursorGroupBox @@ -1216,6 +1246,21 @@ backButtonBehaviorGroupBox Поведение кнопки «Назад»:\nНастраивает кнопку «Назад» контроллера на эмуляцию нажатия на указанную область на сенсорной панели контроллера PS4. + + + enableCompatibilityCheckBox + Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + + + + checkCompatibilityOnStartupCheckBox + Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + + + + updateCompatibilityButton + Update Compatibility Database:\nImmediately update the compatibility database. + Never @@ -1329,6 +1374,11 @@ Serial Серийный номер + + + Compatibility + Compatibility + Region @@ -1364,6 +1414,36 @@ Never Played Never Played + + + Compatibility is untested + Compatibility is untested + + + + Game does not initialize properly / crashes the emulator + Game does not initialize properly / crashes the emulator + + + + Game boots, but only displays a blank screen + Game boots, but only displays a blank screen + + + + Game displays an image but does not go past the menu + Game displays an image but does not go past the menu + + + + Game has game-breaking glitches or unplayable performance + Game has game-breaking glitches or unplayable performance + + + + Game can be completed with playable performance and no major glitches + Game can be completed with playable performance and no major glitches + CheckUpdate diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index 4a02298e8..0e89eaaca 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -672,11 +672,36 @@ GUI Settings Cilësimet e GUI + + + Disable Trophy Pop-ups + Disable Trophy Pop-ups + Play title music Luaj muzikën e titullit + + + Update Compatibility Database On Startup + Update Compatibility Database On Startup + + + + Game Compatibility + Game Compatibility + + + + Display Compatibility Data + Display Compatibility Data + + + + Update Compatibility Database + Update Compatibility Database + Volume @@ -1201,6 +1226,11 @@ GUIgroupBox Luaj muzikën e titullit:\nNëse një lojë e mbështet, aktivizohet luajtja e muzikës të veçantë kur të zgjidhësh lojën në GUI. + + + disableTrophycheckBox + Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + hideCursorGroupBox @@ -1216,6 +1246,21 @@ backButtonBehaviorGroupBox Sjellja e butonit mbrapa:\nLejon të përcaktohet se në cilën pjesë të tastierës prekëse do të imitojë një prekje butoni mprapa. + + + enableCompatibilityCheckBox + Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + + + + checkCompatibilityOnStartupCheckBox + Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + + + + updateCompatibilityButton + Update Compatibility Database:\nImmediately update the compatibility database. + Never @@ -1329,6 +1374,11 @@ Serial Seriku + + + Compatibility + Compatibility + Region @@ -1364,6 +1414,36 @@ Never Played Never Played + + + Compatibility is untested + Compatibility is untested + + + + Game does not initialize properly / crashes the emulator + Game does not initialize properly / crashes the emulator + + + + Game boots, but only displays a blank screen + Game boots, but only displays a blank screen + + + + Game displays an image but does not go past the menu + Game displays an image but does not go past the menu + + + + Game has game-breaking glitches or unplayable performance + Game has game-breaking glitches or unplayable performance + + + + Game can be completed with playable performance and no major glitches + Game can be completed with playable performance and no major glitches + CheckUpdate @@ -1493,4 +1573,4 @@ Krijimi i skedarit skript të përditësimit dështoi - + \ No newline at end of file diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index 4c77bc16a..33e8d0905 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -672,11 +672,36 @@ GUI Settings GUI Ayarları + + + Disable Trophy Pop-ups + Disable Trophy Pop-ups + Play title music Başlık müziğini çal + + + Update Compatibility Database On Startup + Update Compatibility Database On Startup + + + + Game Compatibility + Game Compatibility + + + + Display Compatibility Data + Display Compatibility Data + + + + Update Compatibility Database + Update Compatibility Database + Volume @@ -1201,6 +1226,11 @@ GUIgroupBox Başlık Müziklerini Çal:\nEğer bir oyun bunu destekliyorsa, GUI'de oyunu seçtiğinizde özel müziklerin çalmasını etkinleştirir. + + + disableTrophycheckBox + Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + hideCursorGroupBox @@ -1216,6 +1246,21 @@ backButtonBehaviorGroupBox Geri düğmesi davranışı:\nKontrol cihazındaki geri düğmesini, PS4'ün dokunmatik panelindeki belirlenen noktaya dokunmak için ayarlar. + + + enableCompatibilityCheckBox + Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + + + + checkCompatibilityOnStartupCheckBox + Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + + + + updateCompatibilityButton + Update Compatibility Database:\nImmediately update the compatibility database. + Never @@ -1329,6 +1374,11 @@ Serial Seri Numarası + + + Compatibility + Compatibility + Region @@ -1364,6 +1414,36 @@ Never Played Never Played + + + Compatibility is untested + Compatibility is untested + + + + Game does not initialize properly / crashes the emulator + Game does not initialize properly / crashes the emulator + + + + Game boots, but only displays a blank screen + Game boots, but only displays a blank screen + + + + Game displays an image but does not go past the menu + Game displays an image but does not go past the menu + + + + Game has game-breaking glitches or unplayable performance + Game has game-breaking glitches or unplayable performance + + + + Game can be completed with playable performance and no major glitches + Game can be completed with playable performance and no major glitches + CheckUpdate @@ -1493,4 +1573,4 @@ Güncelleme betiği dosyası oluşturulamadı - + \ No newline at end of file diff --git a/src/qt_gui/translations/uk_UA.ts b/src/qt_gui/translations/uk_UA.ts index 805fff151..88d852474 100644 --- a/src/qt_gui/translations/uk_UA.ts +++ b/src/qt_gui/translations/uk_UA.ts @@ -672,11 +672,36 @@ GUI Settings Інтерфейс + + + Disable Trophy Pop-ups + Disable Trophy Pop-ups + Play title music Програвати заголовну музику + + + Update Compatibility Database On Startup + Update Compatibility Database On Startup + + + + Game Compatibility + Game Compatibility + + + + Display Compatibility Data + Display Compatibility Data + + + + Update Compatibility Database + Update Compatibility Database + Volume @@ -1201,6 +1226,11 @@ GUIgroupBox Грати заголовну музику:\nВмикає відтворення спеціальної музики під час вибору гри в списку, якщо вона це підтримує. + + + disableTrophycheckBox + Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + hideCursorGroupBox @@ -1216,6 +1246,21 @@ backButtonBehaviorGroupBox Поведінка кнопки «Назад»:\nНалаштовує кнопку «Назад» контролера на емуляцію натискання на зазначену область на сенсорній панелі контролера PS4. + + + enableCompatibilityCheckBox + Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + + + + checkCompatibilityOnStartupCheckBox + Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + + + + updateCompatibilityButton + Update Compatibility Database:\nImmediately update the compatibility database. + Never @@ -1329,6 +1374,11 @@ Serial Серійний номер + + + Compatibility + Compatibility + Region @@ -1364,6 +1414,36 @@ Never Played Never Played + + + Compatibility is untested + Compatibility is untested + + + + Game does not initialize properly / crashes the emulator + Game does not initialize properly / crashes the emulator + + + + Game boots, but only displays a blank screen + Game boots, but only displays a blank screen + + + + Game displays an image but does not go past the menu + Game displays an image but does not go past the menu + + + + Game has game-breaking glitches or unplayable performance + Game has game-breaking glitches or unplayable performance + + + + Game can be completed with playable performance and no major glitches + Game can be completed with playable performance and no major glitches + CheckUpdate diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index 1ac3d042d..a0a316df3 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -672,11 +672,36 @@ GUI Settings Cài đặt GUI + + + Disable Trophy Pop-ups + Disable Trophy Pop-ups + Play title music Phát nhạc tiêu đề + + + Update Compatibility Database On Startup + Update Compatibility Database On Startup + + + + Game Compatibility + Game Compatibility + + + + Display Compatibility Data + Display Compatibility Data + + + + Update Compatibility Database + Update Compatibility Database + Volume @@ -1201,6 +1226,11 @@ GUIgroupBox Phát nhạc tiêu đề trò chơi:\nNếu một trò chơi hỗ trợ điều này, hãy kích hoạt phát nhạc đặc biệt khi bạn chọn trò chơi trong GUI. + + + disableTrophycheckBox + Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + hideCursorGroupBox @@ -1216,6 +1246,21 @@ backButtonBehaviorGroupBox Hành vi nút quay lại:\nĐặt nút quay lại của tay cầm để mô phỏng việc chạm vào vị trí đã chỉ định trên touchpad của PS4. + + + enableCompatibilityCheckBox + Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + + + + checkCompatibilityOnStartupCheckBox + Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + + + + updateCompatibilityButton + Update Compatibility Database:\nImmediately update the compatibility database. + Never @@ -1329,6 +1374,11 @@ Serial Số seri + + + Compatibility + Compatibility + Region @@ -1364,6 +1414,36 @@ Never Played Never Played + + + Compatibility is untested + Compatibility is untested + + + + Game does not initialize properly / crashes the emulator + Game does not initialize properly / crashes the emulator + + + + Game boots, but only displays a blank screen + Game boots, but only displays a blank screen + + + + Game displays an image but does not go past the menu + Game displays an image but does not go past the menu + + + + Game has game-breaking glitches or unplayable performance + Game has game-breaking glitches or unplayable performance + + + + Game can be completed with playable performance and no major glitches + Game can be completed with playable performance and no major glitches + CheckUpdate diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index 19fb8edff..fb95ba935 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -672,11 +672,36 @@ GUI Settings 界面设置 + + + Disable Trophy Pop-ups + Disable Trophy Pop-ups + Play title music 播放标题音乐 + + + Update Compatibility Database On Startup + Update Compatibility Database On Startup + + + + Game Compatibility + Game Compatibility + + + + Display Compatibility Data + Display Compatibility Data + + + + Update Compatibility Database + Update Compatibility Database + Volume @@ -1201,6 +1226,11 @@ GUIgroupBox 播放标题音乐:\n如果游戏支持,在图形界面选择游戏时启用播放特殊音乐。 + + + disableTrophycheckBox + Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + hideCursorGroupBox @@ -1216,6 +1246,21 @@ backButtonBehaviorGroupBox 返回按钮行为:\n设置控制器的返回按钮以模拟在 PS4 触控板上指定位置的点击。 + + + enableCompatibilityCheckBox + Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + + + + checkCompatibilityOnStartupCheckBox + Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + + + + updateCompatibilityButton + Update Compatibility Database:\nImmediately update the compatibility database. + Never @@ -1329,6 +1374,11 @@ Serial 序列号 + + + Compatibility + Compatibility + Region @@ -1364,6 +1414,36 @@ Never Played Never Played + + + Compatibility is untested + Compatibility is untested + + + + Game does not initialize properly / crashes the emulator + Game does not initialize properly / crashes the emulator + + + + Game boots, but only displays a blank screen + Game boots, but only displays a blank screen + + + + Game displays an image but does not go past the menu + Game displays an image but does not go past the menu + + + + Game has game-breaking glitches or unplayable performance + Game has game-breaking glitches or unplayable performance + + + + Game can be completed with playable performance and no major glitches + Game can be completed with playable performance and no major glitches + CheckUpdate @@ -1493,4 +1573,4 @@ 无法创建更新脚本文件 - + \ No newline at end of file diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index fbd6d624d..77734bf26 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -672,11 +672,36 @@ GUI Settings 介面設置 + + + Disable Trophy Pop-ups + Disable Trophy Pop-ups + Play title music 播放標題音樂 + + + Update Compatibility Database On Startup + Update Compatibility Database On Startup + + + + Game Compatibility + Game Compatibility + + + + Display Compatibility Data + Display Compatibility Data + + + + Update Compatibility Database + Update Compatibility Database + Volume @@ -1201,6 +1226,11 @@ GUIgroupBox 播放標題音樂:\n如果遊戲支持,啟用在GUI中選擇遊戲時播放特殊音樂。 + + + disableTrophycheckBox + Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + hideCursorGroupBox @@ -1216,6 +1246,21 @@ backButtonBehaviorGroupBox 返回按鈕行為:\n設定控制器的返回按鈕模擬在 PS4 觸控板上指定位置的觸碰。 + + + enableCompatibilityCheckBox + Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + + + + checkCompatibilityOnStartupCheckBox + Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + + + + updateCompatibilityButton + Update Compatibility Database:\nImmediately update the compatibility database. + Never @@ -1329,6 +1374,11 @@ Serial 序號 + + + Compatibility + Compatibility + Region @@ -1364,6 +1414,36 @@ Never Played Never Played + + + Compatibility is untested + Compatibility is untested + + + + Game does not initialize properly / crashes the emulator + Game does not initialize properly / crashes the emulator + + + + Game boots, but only displays a blank screen + Game boots, but only displays a blank screen + + + + Game displays an image but does not go past the menu + Game displays an image but does not go past the menu + + + + Game has game-breaking glitches or unplayable performance + Game has game-breaking glitches or unplayable performance + + + + Game can be completed with playable performance and no major glitches + Game can be completed with playable performance and no major glitches + CheckUpdate From 195929953ba0784c5a676dc95e62aa5ffef9373b Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Wed, 25 Dec 2024 22:08:43 +0200 Subject: [PATCH 249/549] tagged 0.5.0 --- src/common/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/version.h b/src/common/version.h index 5e6599604..faca24f2f 100644 --- a/src/common/version.h +++ b/src/common/version.h @@ -8,7 +8,7 @@ namespace Common { -constexpr char VERSION[] = "0.4.1 WIP"; +constexpr char VERSION[] = "0.5.0"; constexpr bool isRelease = false; } // namespace Common From a1a98966eee07e7ecf3a5e3836b5f2ecde5664b0 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Wed, 25 Dec 2024 22:35:00 +0200 Subject: [PATCH 250/549] retagged 0.5.0 --- src/common/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/version.h b/src/common/version.h index faca24f2f..6ee4bac97 100644 --- a/src/common/version.h +++ b/src/common/version.h @@ -9,6 +9,6 @@ namespace Common { constexpr char VERSION[] = "0.5.0"; -constexpr bool isRelease = false; +constexpr bool isRelease = true; } // namespace Common From 3a1a3fc75e7a80a6e9439f0ef4eba234116ba8ef Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Wed, 25 Dec 2024 22:42:49 +0200 Subject: [PATCH 251/549] 0.5.1 WIP started --- src/common/version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/version.h b/src/common/version.h index 6ee4bac97..c903b1db6 100644 --- a/src/common/version.h +++ b/src/common/version.h @@ -8,7 +8,7 @@ namespace Common { -constexpr char VERSION[] = "0.5.0"; -constexpr bool isRelease = true; +constexpr char VERSION[] = "0.5.1 WIP"; +constexpr bool isRelease = false; } // namespace Common From 0939082aca4e289a6e9804987949ea732eb923b9 Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Wed, 25 Dec 2024 18:58:19 -0300 Subject: [PATCH 252/549] Fix button changelog (#1882) --- src/qt_gui/check_update.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/qt_gui/check_update.cpp b/src/qt_gui/check_update.cpp index bb07baaf5..edd55b804 100644 --- a/src/qt_gui/check_update.cpp +++ b/src/qt_gui/check_update.cpp @@ -213,9 +213,9 @@ void CheckUpdate::setupUI(const QString& downloadUrl, const QString& latestDate, // Don't show changelog button if: // The current version is a pre-release and the version to be downloaded is a release. - bool current_isRelease = currentRev.startsWith('v', Qt::CaseInsensitive); - bool latest_isRelease = latestRev.startsWith('v', Qt::CaseInsensitive); - if (!current_isRelease && latest_isRelease) { + bool current_isWIP = currentRev.endsWith("WIP", Qt::CaseInsensitive); + bool latest_isWIP = latestRev.endsWith("WIP", Qt::CaseInsensitive); + if (current_isWIP && !latest_isWIP) { } else { QTextEdit* textField = new QTextEdit(this); textField->setReadOnly(true); From 5f9abd1db440e05a663946cfa80e7feaf9b4e7ac Mon Sep 17 00:00:00 2001 From: rafael-57 Date: Thu, 26 Dec 2024 00:55:15 +0100 Subject: [PATCH 253/549] Add info for QT Multimedia (#1886) QT Multimedia is mandatory for building on Windows, and it's not mentioned in the guide. --- documents/building-windows.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documents/building-windows.md b/documents/building-windows.md index d01e7b81e..0d523692e 100644 --- a/documents/building-windows.md +++ b/documents/building-windows.md @@ -25,7 +25,7 @@ Once you are within the installer: Beware, this requires you to create a Qt account. If you do not want to do this, please follow the MSYS2/MinGW compilation method instead. -1. Under the current, non beta version of Qt (at the time of writing 6.7.3), select the option `MSVC 2022 64-bit` or similar. +1. Under the current, non beta version of Qt (at the time of writing 6.7.3), select the option `MSVC 2022 64-bit` or similar, as well as `QT Multimedia`. If you are on Windows on ARM / Qualcomm Snapdragon Elite X, select `MSVC 2022 ARM64` instead. Go through the installation normally. If you know what you are doing, you may unselect individual components that eat up too much disk space. From ba9273d1cca5ec8a682dfdcc6235e987bfd3f1f6 Mon Sep 17 00:00:00 2001 From: bigol83 <38129260+bigol83@users.noreply.github.com> Date: Thu, 26 Dec 2024 12:53:20 +0100 Subject: [PATCH 254/549] Update it.ts (#1894) Add missing translation and fixed some typos --- src/qt_gui/translations/it.ts | 38 +++++++++++++++++------------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/qt_gui/translations/it.ts b/src/qt_gui/translations/it.ts index dfbbf1cf3..0bff066ff 100644 --- a/src/qt_gui/translations/it.ts +++ b/src/qt_gui/translations/it.ts @@ -208,7 +208,7 @@ requiresEnableSeparateUpdateFolder_MSG - This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + Questa feature richiede che venga attivata l'opzione "Abilita Cartella Aggiornamenti Separata" per poter funzionare, per favore abilitala. @@ -495,7 +495,7 @@ Enable Separate Update Folder - Abilità Cartella Aggiornamenti Separata + Abilita Cartella Aggiornamenti Separata @@ -675,7 +675,7 @@ Disable Trophy Pop-ups - Disable Trophy Pop-ups + Disabilita Notifica Trofei @@ -685,22 +685,22 @@ Update Compatibility Database On Startup - Update Compatibility Database On Startup + Aggiorna Database Compatibilità all'Avvio Game Compatibility - Game Compatibility + Compatibilità Gioco Display Compatibility Data - Display Compatibility Data + Mostra Dati Compatibilità Update Compatibility Database - Update Compatibility Database + Aggiorna Database Compatibilità @@ -1249,17 +1249,17 @@ enableCompatibilityCheckBox - Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + Mostra Dati Compatibilità:\nMostra informazioni sulla compatibilità del gioco nella visualizzazione lista. Abilita "Aggiorna Compatiblità all'Avvio" per ottenere informazioni aggiornate. checkCompatibilityOnStartupCheckBox - Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + Aggiorna Compatibilità all'Avvio:\nAggiorna automaticamente il database della compatibilità quando si avvia shadps4. updateCompatibilityButton - Update Compatibility Database:\nImmediately update the compatibility database. + Aggiorna Database Compatibilità:\nAggiorna immediatamente il database di compatibilità. @@ -1377,7 +1377,7 @@ Compatibility - Compatibility + Compatibilità @@ -1412,37 +1412,37 @@ Never Played - Never Played + Mai Giocato Compatibility is untested - Compatibility is untested + Nessuna informazione sulla compatibilità Game does not initialize properly / crashes the emulator - Game does not initialize properly / crashes the emulator + Il gioco non si avvia in modo corretto / forza chiusura dell'emulatore Game boots, but only displays a blank screen - Game boots, but only displays a blank screen + Il gioco si avvia, ma mostra solo una schermata nera Game displays an image but does not go past the menu - Game displays an image but does not go past the menu + Il gioco mostra immagini ma non va oltre il menu Game has game-breaking glitches or unplayable performance - Game has game-breaking glitches or unplayable performance + Il gioco ha problemi gravi di emulazione oppure framerate troppo basso Game can be completed with playable performance and no major glitches - Game can be completed with playable performance and no major glitches + Il gioco può essere completato con buone prestazioni e senza problemi gravi @@ -1573,4 +1573,4 @@ Impossibile creare il file di script di aggiornamento - \ No newline at end of file + From e885088632dd9b73a63e712f8facc46a11c94b48 Mon Sep 17 00:00:00 2001 From: F1219R <109141852+F1219R@users.noreply.github.com> Date: Thu, 26 Dec 2024 12:58:42 +0100 Subject: [PATCH 255/549] Update sq translation (#1893) --- src/qt_gui/translations/sq.ts | 40 +++++++++++++++++------------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index 0e89eaaca..7f4b8e6c2 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -675,7 +675,7 @@ Disable Trophy Pop-ups - Disable Trophy Pop-ups + Çaktivizo njoftimet për Trofetë @@ -685,22 +685,22 @@ Update Compatibility Database On Startup - Update Compatibility Database On Startup + Përditëso bazën e të dhënave të përputhshmërisë gjatë nisjes Game Compatibility - Game Compatibility + Përputhshmëria e lojës Display Compatibility Data - Display Compatibility Data + Shfaq të dhënat e përputhshmërisë Update Compatibility Database - Update Compatibility Database + Përditëso bazën e të dhënave të përputhshmërisë @@ -866,7 +866,7 @@ Cheats / Patches for - Cheats / Patches for + Mashtrime / Arna për @@ -1184,7 +1184,7 @@ separateUpdatesCheckBox - Aktivizo dosjen e ndarë të përditësimit:\nAktivizon instalimin e përditësimeve të lojërave në dosje të veçanta për menaxhim më të lehtë. + Aktivizo dosjen e ndarë të përditësimit:\nAktivizon instalimin e përditësimeve të lojërave në dosje të veçanta për menaxhim më të lehtë.\nKjo mund të krijohet manualisht duke shtuar përditësimin e shpaketuar në dosjen e lojës me emrin "CUSA00000-UPDATE" ku ID-ja CUSA përputhet me ID-në e lojës. @@ -1229,7 +1229,7 @@ disableTrophycheckBox - Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + Çaktivizo njoftimet për Trofetë:\nÇaktivizo njoftimet për trofetë gjatë lojës. Përparimi i trofeve mund të ndiqet duke përdorur Shikuesin e Trofeve (kliko me të djathtën mbi lojën në dritaren kryesore). @@ -1249,17 +1249,17 @@ enableCompatibilityCheckBox - Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + Shfaq të dhënat e përputhshmërisë:\nShfaq informacionin e përputhshmërisë së lojës në formë tabele. Aktivizo 'Përditëso përputhshmërinë gjatë nisjes' për të marrë informacion të përditësuar. checkCompatibilityOnStartupCheckBox - Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + Përditëso përputhshmërinë gjatë nisjes:\nPërditëson automatikisht bazën e të dhënave të përputhshmërisë kur shadPS4 niset. updateCompatibilityButton - Update Compatibility Database:\nImmediately update the compatibility database. + Përditëso bazën e të dhënave të përputhshmërisë:\nPërditëso menjëherë bazën e të dhënave të përputhshmërisë. @@ -1377,7 +1377,7 @@ Compatibility - Compatibility + Përputhshmëria @@ -1412,37 +1412,37 @@ Never Played - Never Played + Nuk është luajtur kurrë Compatibility is untested - Compatibility is untested + Përputhshmëria nuk është e testuar Game does not initialize properly / crashes the emulator - Game does not initialize properly / crashes the emulator + Loja nuk niset siç duhet / rrëzon emulatorin Game boots, but only displays a blank screen - Game boots, but only displays a blank screen + Loja niset, por shfaq vetëm një ekran të zbrazët Game displays an image but does not go past the menu - Game displays an image but does not go past the menu + Loja shfaq një imazh, por nuk kalon përtej menysë Game has game-breaking glitches or unplayable performance - Game has game-breaking glitches or unplayable performance + Loja ka probleme kritike ose performancë të papërshtatshme për lojë Game can be completed with playable performance and no major glitches - Game can be completed with playable performance and no major glitches + Loja mund të përfundohet me performancë të luajtshme dhe pa probleme të mëdha @@ -1573,4 +1573,4 @@ Krijimi i skedarit skript të përditësimit dështoi - \ No newline at end of file + From 91d40441625e1406cd0d9a6a66448692c03935fd Mon Sep 17 00:00:00 2001 From: Mohsen Date: Thu, 26 Dec 2024 15:31:31 +0330 Subject: [PATCH 256/549] Translated most of the remaining texts into Persian (fa_IR.ts) and fixed incorrect translations (#1885) --- src/qt_gui/translations/fa_IR.ts | 142 +++++++++++++++---------------- 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index 7b6583f45..e34b04491 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -62,7 +62,7 @@ Select which directory you want to install to. - Select which directory you want to install to. + محلی را که می‌خواهید در آن نصب شود، انتخاب کنید. @@ -98,7 +98,7 @@ Create Shortcut - ساخت شورتکات + ایجاد میانبر @@ -113,7 +113,7 @@ Trophy Viewer - مشاهده تروفی ها + مشاهده جوایز @@ -158,32 +158,32 @@ Delete... - Delete... + حذف... Delete Game - Delete Game + حذف بازی Delete Update - Delete Update + حذف به‌روزرسانی Delete DLC - Delete DLC + حذف محتوای اضافی (DLC) Shortcut creation - سازنده شورتکات + ایجاد میانبر Shortcut created successfully!\n %1 - شورتکات با موفقیت ساخته شد! \n %1 + میانبر با موفقیت ساخته شد! \n %1 @@ -193,7 +193,7 @@ Error creating shortcut!\n %1 - مشکلی در هنگام ساخت شورتکات بوجود آمد!\n %1 + مشکلی در هنگام ساخت میانبر بوجود آمد!\n %1 @@ -203,27 +203,27 @@ Game - Game + بازی requiresEnableSeparateUpdateFolder_MSG - This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + این قابلیت نیازمند فعال‌سازی گزینه تنظیمات «ایجاد پوشه جداگانه برای به‌روزرسانی» است. در صورت تمایل به استفاده از این قابلیت، لطفاً آن را فعال کنید. This game has no update to delete! - This game has no update to delete! + این بازی به‌روزرسانی‌ای برای حذف ندارد! Update - Update + به‌روزرسانی This game has no DLC to delete! - This game has no DLC to delete! + این بازی محتوای اضافی (DLC) برای حذف ندارد! @@ -233,7 +233,7 @@ Delete %1 - Delete %1 + حذف %1 @@ -331,7 +331,7 @@ List View - لیستی + نمایش لیست @@ -341,7 +341,7 @@ Elf Viewer - Elf Viewer + مشاهده گر Elf @@ -452,7 +452,7 @@ Trophy Viewer - تروفی ها + مشاهده جوایز @@ -495,7 +495,7 @@ Enable Separate Update Folder - Enable Separate Update Folder + فعال‌سازی پوشه جداگانه برای به‌روزرسانی @@ -555,7 +555,7 @@ Controller - کنترل کننده + دسته بازی @@ -585,7 +585,7 @@ Vblank Divider - Vblank Divider + تقسیم‌کننده Vblank @@ -595,7 +595,7 @@ Enable Shaders Dumping - Shaders Dumping فعال کردن + فعال‌سازی ذخیره‌سازی شیدرها @@ -625,7 +625,7 @@ Debug - Debug + دیباگ @@ -650,22 +650,22 @@ Update - بروزرسانی + به‌روزرسانی Check for Updates at Startup - بررسی بروزرسانی هنگام شروع + بررسی به‌روزرسانی‌ها در زمان راه‌اندازی Update Channel - کانال بروزرسانی + کانال به‌روزرسانی Check for Updates - به روز رسانی را بررسی کنید + بررسی به‌روزرسانی‌ها @@ -675,7 +675,7 @@ Disable Trophy Pop-ups - Disable Trophy Pop-ups + غیرفعال کردن نمایش جوایز @@ -685,27 +685,27 @@ Update Compatibility Database On Startup - Update Compatibility Database On Startup + به‌روزرسانی پایگاه داده سازگاری هنگام راه‌اندازی Game Compatibility - Game Compatibility + سازگاری بازی با سیستم Display Compatibility Data - Display Compatibility Data + نمایش داده‌های سازگاری Update Compatibility Database - Update Compatibility Database + به‌روزرسانی پایگاه داده سازگاری Volume - صدا + صدا @@ -718,7 +718,7 @@ * Unsupported Vulkan Version - شما پشتیبانی نمیشود Vulkan ورژن* + شما پشتیبانی نمیشود Vulkan ورژن * @@ -866,7 +866,7 @@ Cheats / Patches for - Cheats / Patches for ا + چیت / پچ برای @@ -886,7 +886,7 @@ Version: - ورژن: + نسخه: @@ -911,7 +911,7 @@ Delete File - پاک کردن فایل + حذف فایل @@ -1174,27 +1174,27 @@ emulatorLanguageGroupBox - Emulator Language:\nSets the language of the emulator's user interface. + زبان شبیه‌ساز:\nزبان رابط کاربری شبیه‌ساز را انتخاب می‌کند. fullscreenCheckBox - Enable Full Screen:\nAutomatically puts the game window into full-screen mode.\nThis can be toggled by pressing the F11 key. + فعال‌سازی تمام صفحه:\nپنجره بازی را به‌طور خودکار به حالت تمام صفحه در می‌آورد.\nبرای تغییر این حالت می‌توانید کلید F11 را فشار دهید. separateUpdatesCheckBox - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + فعال‌سازی پوشه جداگانه برای به‌روزرسانی:\nامکان نصب به‌روزرسانی‌های بازی در یک پوشه جداگانه برای مدیریت راحت‌تر را فراهم می‌کند. showSplashCheckBox - Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. + نمایش صفحه شروع:\nصفحه شروع بازی (تصویری ویژه) را هنگام بارگذاری بازی نمایش می‌دهد. ps4proCheckBox - Is PS4 Pro:\nMakes the emulator act as a PS4 PRO, which may enable special features in games that support it. + حالت PS4 Pro:\nشبیه‌ساز را به‌عنوان PS4 Pro شبیه‌سازی می‌کند که ممکن است ویژگی‌های ویژه‌ای را در بازی‌های پشتیبانی‌شده فعال کند. @@ -1204,12 +1204,12 @@ userName - Username:\nSets the PS4's account username, which may be displayed by some games. + نام کاربری:\nنام کاربری حساب PS4 را تنظیم می‌کند که ممکن است توسط برخی بازی‌ها نمایش داده شود. logTypeGroupBox - Log Type:\nSets whether to synchronize the output of the log window for performance. May have adverse effects on emulation. + نوع لاگ:\nتنظیم می‌کند که آیا خروجی پنجره لاگ برای بهبود عملکرد همگام‌سازی شود یا خیر. این ممکن است تأثیر منفی بر شبیه‌سازی داشته باشد. @@ -1219,17 +1219,17 @@ updaterGroupBox - Update:\nRelease: Official versions released every month that may be very outdated, but are more reliable and tested.\nNightly: Development versions that have all the latest features and fixes, but may contain bugs and are less stable. + به‌روزرسانی:\nانتشار: نسخه‌های رسمی که هر ماه منتشر می‌شوند و ممکن است بسیار قدیمی باشند، اما پایدارتر و تست‌ شده‌تر هستند.\nشبانه: نسخه‌های توسعه‌ای که شامل جدیدترین ویژگی‌ها و اصلاحات هستند، اما ممکن است دارای اشکال باشند و کمتر پایدار باشند. GUIgroupBox - Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI. + پخش موسیقی عنوان:\nIدر صورتی که بازی از آن پشتیبانی کند، پخش موسیقی ویژه هنگام انتخاب بازی در رابط کاربری را فعال می‌کند. disableTrophycheckBox - Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + غیرفعال کردن نمایش جوایز:\nنمایش اعلان‌های جوایز درون بازی را غیرفعال می‌کند. پیشرفت جوایز همچنان از طریق نمایشگر جوایز (کلیک راست روی بازی در پنجره اصلی) قابل پیگیری است.. @@ -1249,17 +1249,17 @@ enableCompatibilityCheckBox - Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + نمایش داده‌های سازگاری:\nاطلاعات سازگاری بازی را به صورت جدول نمایش می‌دهد. برای دریافت اطلاعات به‌روز، گزینه "به‌روزرسانی سازگاری هنگام راه‌اندازی" را فعال کنید. checkCompatibilityOnStartupCheckBox - Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + به‌روزرسانی سازگاری هنگام راه‌اندازی:\nبه‌طور خودکار پایگاه داده سازگاری را هنگام راه‌اندازی ShadPS4 به‌روزرسانی می‌کند. updateCompatibilityButton - Update Compatibility Database:\nImmediately update the compatibility database. + به‌روزرسانی پایگاه داده سازگاری:\nپایگاه داده سازگاری را بلافاصله به‌روزرسانی می‌کند. @@ -1279,7 +1279,7 @@ Touchpad Left - پد لمسی سمت چپ + صفحه لمسی سمت چپ @@ -1289,7 +1289,7 @@ Touchpad Center - مرکز تاچ پد + مرکز صفحه لمسی @@ -1299,22 +1299,22 @@ graphicsAdapterGroupBox - Graphics Device:\nOn multiple GPU systems, select the GPU the emulator will use from the drop down list,\nor select "Auto Select" to automatically determine it. + دستگاه گرافیکی:\nدر سیستم‌های با چندین پردازنده گرافیکی، از فهرست کشویی، پردازنده گرافیکی که شبیه‌ساز از آن استفاده می‌کند را انتخاب کنید، یا گزینه "انتخاب خودکار" را انتخاب کنید تا به طور خودکار تعیین شود. resolutionLayout - Width/Height:\nSets the size of the emulator window at launch, which can be resized during gameplay.\nThis is different from the in-game resolution. + عرض/ارتفاع:\nاندازه پنجره شبیه‌ساز را در هنگام راه‌اندازی تنظیم می‌کند، که در حین بازی قابل تغییر اندازه است.\nاین با وضوح داخل بازی متفاوت است. heightDivider - Vblank Divider:\nThe frame rate at which the emulator refreshes at is multiplied by this number. Changing this may have adverse effects, such as increasing the game speed, or breaking critical game functionality that does not expect this to change! + تقسیم‌کننده Vblank:\nمیزان فریم ریت که شبیه‌ساز با آن به‌روزرسانی می‌شود، در این عدد ضرب می‌شود. تغییر این مقدار ممکن است تأثیرات منفی داشته باشد، مانند افزایش سرعت بازی یا خراب شدن عملکردهای حیاتی بازی که انتظار تغییر آن را ندارند! dumpShadersCheckBox - Enable Shaders Dumping:\nFor the sake of technical debugging, saves the games shaders to a folder as they render. + فعال‌سازی ذخیره‌سازی شیدرها:\nبه‌منظور اشکال‌زدایی فنی، شیدرهای بازی را هنگام رندر شدن در یک پوشه ذخیره می‌کند. @@ -1339,7 +1339,7 @@ debugDump - Enable Debug Dumping:\nSaves the import and export symbols and file header information of the currently running PS4 program to a directory. + فعال‌سازی ذخیره‌سازی دیباگ:\nنمادهای import و export و اطلاعات هدر فایل برنامه در حال اجرای PS4 را در یک پوشه ذخیره می‌کند. @@ -1377,7 +1377,7 @@ Compatibility - Compatibility + سازگاری @@ -1387,7 +1387,7 @@ Firmware - فریمور + فریم‌ور @@ -1412,37 +1412,37 @@ Never Played - Never Played + هرگز بازی نشده Compatibility is untested - Compatibility is untested + سازگاری تست نشده است Game does not initialize properly / crashes the emulator - Game does not initialize properly / crashes the emulator + بازی به درستی راه‌اندازی نمی‌شود / شبیه‌ساز کرش می‌کند Game boots, but only displays a blank screen - Game boots, but only displays a blank screen + بازی اجرا می‌شود، اما فقط یک صفحه خالی نمایش داده می‌شود Game displays an image but does not go past the menu - Game displays an image but does not go past the menu + بازی تصویری نمایش می‌دهد، اما از منو فراتر نمی‌رود Game has game-breaking glitches or unplayable performance - Game has game-breaking glitches or unplayable performance + بازی دارای اشکالات بحرانی یا عملکرد غیرقابل بازی است Game can be completed with playable performance and no major glitches - Game can be completed with playable performance and no major glitches + بازی با عملکرد قابل قبول و بدون اشکالات عمده قابل بازی است. @@ -1450,7 +1450,7 @@ Auto Updater - به روز رسانی خودکار + به‌روزرسانی خودکار @@ -1495,7 +1495,7 @@ Update Channel - کانال بروزرسانی + کانال به‌روزرسانی @@ -1520,7 +1520,7 @@ Check for Updates at Startup - بررسی بروزرسانی هنگام شروع + بررسی به‌روزرسانی هنگام شروع @@ -1573,4 +1573,4 @@ فایل اسکریپت به روز رسانی ایجاد نشد - \ No newline at end of file + From cbb13fa39b603cda9526bf4b85c13e0e8557a4b1 Mon Sep 17 00:00:00 2001 From: "Daniel R." <47796739+polybiusproxy@users.noreply.github.com> Date: Thu, 26 Dec 2024 13:18:53 +0100 Subject: [PATCH 257/549] hotfix: fix incorrect appcontent available space size --- src/core/libraries/app_content/app_content.cpp | 8 +++++--- src/core/libraries/app_content/app_content.h | 5 +++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/core/libraries/app_content/app_content.cpp b/src/core/libraries/app_content/app_content.cpp index ca3cdad39..1d23e7f44 100644 --- a/src/core/libraries/app_content/app_content.cpp +++ b/src/core/libraries/app_content/app_content.cpp @@ -145,8 +145,10 @@ int PS4_SYSV_ABI sceAppContentDownloadDataFormat() { return ORBIS_OK; } -int PS4_SYSV_ABI sceAppContentDownloadDataGetAvailableSpaceKb() { +int PS4_SYSV_ABI sceAppContentDownloadDataGetAvailableSpaceKb(OrbisAppContentMountPoint* mountPoint, + u64* availableSpaceKb) { LOG_ERROR(Lib_AppContent, "(STUBBED) called"); + *availableSpaceKb = 1048576; return ORBIS_OK; } @@ -294,9 +296,9 @@ int PS4_SYSV_ABI sceAppContentTemporaryDataFormat() { } int PS4_SYSV_ABI sceAppContentTemporaryDataGetAvailableSpaceKb( - const OrbisAppContentMountPoint* mountPoint, size_t* availableSpaceKb) { + const OrbisAppContentMountPoint* mountPoint, u64* availableSpaceKb) { LOG_ERROR(Lib_AppContent, "(STUBBED) called"); - *availableSpaceKb = 1073741824; + *availableSpaceKb = 1048576; return ORBIS_OK; } diff --git a/src/core/libraries/app_content/app_content.h b/src/core/libraries/app_content/app_content.h index f41f7dccf..05bd3bc49 100644 --- a/src/core/libraries/app_content/app_content.h +++ b/src/core/libraries/app_content/app_content.h @@ -84,7 +84,8 @@ int PS4_SYSV_ABI sceAppContentDownload0Shrink(); int PS4_SYSV_ABI sceAppContentDownload1Expand(); int PS4_SYSV_ABI sceAppContentDownload1Shrink(); int PS4_SYSV_ABI sceAppContentDownloadDataFormat(); -int PS4_SYSV_ABI sceAppContentDownloadDataGetAvailableSpaceKb(); +int PS4_SYSV_ABI sceAppContentDownloadDataGetAvailableSpaceKb(OrbisAppContentMountPoint* mountPoint, + u64* availableSpaceKb); int PS4_SYSV_ABI sceAppContentGetAddcontDownloadProgress(); int PS4_SYSV_ABI sceAppContentGetAddcontInfo(u32 service_label, const OrbisNpUnifiedEntitlementLabel* entitlementLabel, @@ -105,7 +106,7 @@ int PS4_SYSV_ABI sceAppContentSmallSharedDataMount(); int PS4_SYSV_ABI sceAppContentSmallSharedDataUnmount(); int PS4_SYSV_ABI sceAppContentTemporaryDataFormat(); int PS4_SYSV_ABI sceAppContentTemporaryDataGetAvailableSpaceKb( - const OrbisAppContentMountPoint* mountPoint, size_t* availableSpaceKb); + const OrbisAppContentMountPoint* mountPoint, u64* availableSpaceKb); int PS4_SYSV_ABI sceAppContentTemporaryDataMount(); int PS4_SYSV_ABI sceAppContentTemporaryDataMount2(OrbisAppContentTemporaryDataOption option, OrbisAppContentMountPoint* mountPoint); From 0bb1c05aff6af1309eef29545be214d3ac66c355 Mon Sep 17 00:00:00 2001 From: IndecisiveTurtle <47210458+raphaelthegreat@users.noreply.github.com> Date: Thu, 26 Dec 2024 17:38:20 +0200 Subject: [PATCH 258/549] hot-fix: Drop key * Caution is best --- src/core/crypto/crypto.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/core/crypto/crypto.cpp b/src/core/crypto/crypto.cpp index aa1c96724..00f1dea46 100644 --- a/src/core/crypto/crypto.cpp +++ b/src/core/crypto/crypto.cpp @@ -141,12 +141,9 @@ void Crypto::decryptEFSM(std::span NPcommID, std::span efsmIv, std::span ciphertext, std::span decrypted) { - std::vector TrophyKey = {0x21, 0xF4, 0x1A, 0x6B, 0xAD, 0x8A, 0x1D, 0x3E, - 0xCA, 0x7A, 0xD5, 0x86, 0xC1, 0x01, 0xB7, 0xA9}; std::vector TrophyIV = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // step 1: Encrypt NPcommID CryptoPP::CBC_Mode::Encryption encrypt; - encrypt.SetKeyWithIV(TrophyKey.data(), TrophyKey.size(), TrophyIV.data()); std::vector trpKey(16); From edc027a8bcf4c514b071d669e168a4ab31369ef1 Mon Sep 17 00:00:00 2001 From: Vinicius Rangel Date: Thu, 26 Dec 2024 18:08:47 -0300 Subject: [PATCH 259/549] Devtools IV (#1910) * devtools: fix popen in non-windows environment * devtools: fix frame crash assertion when hidden * devtools: add search to shader list * devtools: add copy name to shader list * devtools: frame dump: search by shader name --- src/core/debug_state.cpp | 8 ++ src/core/debug_state.h | 4 + src/core/devtools/widget/cmd_list.cpp | 37 ++++++- src/core/devtools/widget/cmd_list.h | 6 +- src/core/devtools/widget/common.h | 2 +- src/core/devtools/widget/frame_dump.cpp | 11 +- src/core/devtools/widget/frame_dump.h | 2 + src/core/devtools/widget/frame_graph.cpp | 101 +++++++++--------- src/core/devtools/widget/frame_graph.h | 2 + src/core/devtools/widget/reg_view.cpp | 11 ++ src/core/devtools/widget/shader_list.cpp | 11 ++ src/core/devtools/widget/shader_list.h | 2 + .../renderer_vulkan/vk_pipeline_cache.cpp | 14 ++- .../renderer_vulkan/vk_pipeline_cache.h | 3 + 14 files changed, 159 insertions(+), 55 deletions(-) diff --git a/src/core/debug_state.cpp b/src/core/debug_state.cpp index daf614bd9..6508a9875 100644 --- a/src/core/debug_state.cpp +++ b/src/core/debug_state.cpp @@ -11,6 +11,7 @@ #include "libraries/kernel/time.h" #include "libraries/system/msgdialog.h" #include "video_core/amdgpu/pm4_cmds.h" +#include "video_core/renderer_vulkan/vk_pipeline_cache.h" using namespace DebugStateType; @@ -168,8 +169,12 @@ void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr, if ((*dump)->regs.stage_enable.IsStageEnabled(i)) { auto stage = (*dump)->regs.ProgramForStage(i); if (stage->address_lo != 0) { + const auto& info = AmdGpu::Liverpool::SearchBinaryInfo(stage->Address()); auto code = stage->Code(); (*dump)->stages[i] = PipelineShaderProgramDump{ + .name = Vulkan::PipelineCache::GetShaderName(Shader::StageFromIndex(i), + info.shader_hash), + .hash = info.shader_hash, .user_data = *stage, .code = std::vector{code.begin(), code.end()}, }; @@ -191,7 +196,10 @@ void DebugStateImpl::PushRegsDumpCompute(uintptr_t base_addr, uintptr_t header_a auto& cs = (*dump)->regs.cs_program; cs = cs_state; + const auto& info = AmdGpu::Liverpool::SearchBinaryInfo(cs.Address()); (*dump)->cs_data = PipelineComputerProgramDump{ + .name = Vulkan::PipelineCache::GetShaderName(Shader::Stage::Compute, info.shader_hash), + .hash = info.shader_hash, .cs_program = cs, .code = std::vector{cs.Code().begin(), cs.Code().end()}, }; diff --git a/src/core/debug_state.h b/src/core/debug_state.h index a0e428b6b..6a8e15baa 100644 --- a/src/core/debug_state.h +++ b/src/core/debug_state.h @@ -50,11 +50,15 @@ struct QueueDump { }; struct PipelineShaderProgramDump { + std::string name; + u64 hash; Vulkan::Liverpool::ShaderProgram user_data{}; std::vector code{}; }; struct PipelineComputerProgramDump { + std::string name; + u64 hash; Vulkan::Liverpool::ComputeProgram cs_program{}; std::vector code{}; }; diff --git a/src/core/devtools/widget/cmd_list.cpp b/src/core/devtools/widget/cmd_list.cpp index 7c550cf2e..dc3eb9cdd 100644 --- a/src/core/devtools/widget/cmd_list.cpp +++ b/src/core/devtools/widget/cmd_list.cpp @@ -1174,7 +1174,7 @@ CmdListViewer::CmdListViewer(DebugStateType::FrameDump* _frame_dump, } } -void CmdListViewer::Draw(bool only_batches_view) { +void CmdListViewer::Draw(bool only_batches_view, CmdListFilter& filter) { const auto& ctx = *GetCurrentContext(); if (batch_view.open) { @@ -1285,6 +1285,41 @@ void CmdListViewer::Draw(bool only_batches_view) { } auto& batch = std::get(event); + + // filtering + { + bool remove = false; + + if (filter.shader_name[0] != '\0') { + remove = true; + std::string_view shader_name{filter.shader_name}; + const auto& data = frame_dump->regs.find(batch.command_addr); + if (data != frame_dump->regs.end()) { + DebugStateType::RegDump& dump = data->second; + if (dump.is_compute) { + if (dump.cs_data.name.contains(shader_name)) { + remove = false; + break; + } + } else { + for (int i = 0; i < DebugStateType::RegDump::MaxShaderStages; ++i) { + if (dump.regs.stage_enable.IsStageEnabled(i)) { + auto& stage = dump.stages[i]; + if (stage.name.contains(shader_name)) { + remove = false; + break; + } + } + } + } + } + } + + if (remove) { + continue; + } + } + auto const* pm4_hdr = reinterpret_cast(cmdb_addr + batch.start_addr); diff --git a/src/core/devtools/widget/cmd_list.h b/src/core/devtools/widget/cmd_list.h index ed71d0b76..e2c61f6b9 100644 --- a/src/core/devtools/widget/cmd_list.h +++ b/src/core/devtools/widget/cmd_list.h @@ -35,6 +35,10 @@ void ParseDepthControl(u32 value, bool begin_table = true); void ParseEqaa(u32 value, bool begin_table = true); void ParseZInfo(u32 value, bool begin_table = true); +struct CmdListFilter { + char shader_name[128]{}; +}; + class CmdListViewer { DebugStateType::FrameDump* frame_dump; @@ -70,7 +74,7 @@ public: explicit CmdListViewer(DebugStateType::FrameDump* frame_dump, const std::vector& cmd_list, uintptr_t base_addr = 0, std::string name = ""); - void Draw(bool only_batches_view = false); + void Draw(bool only_batches_view, CmdListFilter& filter); }; } // namespace Core::Devtools::Widget diff --git a/src/core/devtools/widget/common.h b/src/core/devtools/widget/common.h index 75eb55301..4684f6e3b 100644 --- a/src/core/devtools/widget/common.h +++ b/src/core/devtools/widget/common.h @@ -117,7 +117,7 @@ static bool IsDrawCall(AmdGpu::PM4ItOpcode opcode) { inline std::optional exec_cli(const char* cli) { std::array buffer{}; std::string output; - const auto f = popen(cli, "rt"); + const auto f = popen(cli, "r"); if (!f) { pclose(f); return {}; diff --git a/src/core/devtools/widget/frame_dump.cpp b/src/core/devtools/widget/frame_dump.cpp index 055ce1333..646ccb6d6 100644 --- a/src/core/devtools/widget/frame_dump.cpp +++ b/src/core/devtools/widget/frame_dump.cpp @@ -132,6 +132,15 @@ void FrameDumpViewer::Draw() { } } EndDisabled(); + SameLine(); + if (BeginMenu("Filter")) { + + TextUnformatted("Shader name"); + SameLine(); + InputText("##filter_shader", filter.shader_name, sizeof(filter.shader_name)); + + ImGui::EndMenu(); + } TextEx("Submit num"); SameLine(); @@ -187,7 +196,7 @@ void FrameDumpViewer::Draw() { EndGroup(); } if (is_showing && selected_cmd != -1) { - cmd_list_viewer[selected_cmd].Draw(is_collapsed); + cmd_list_viewer[selected_cmd].Draw(is_collapsed, filter); } End(); } diff --git a/src/core/devtools/widget/frame_dump.h b/src/core/devtools/widget/frame_dump.h index cc4fe6381..94075112b 100644 --- a/src/core/devtools/widget/frame_dump.h +++ b/src/core/devtools/widget/frame_dump.h @@ -27,6 +27,8 @@ class FrameDumpViewer { s32 selected_queue_num2; s32 selected_cmd = -1; + CmdListFilter filter; + public: bool is_open = true; diff --git a/src/core/devtools/widget/frame_graph.cpp b/src/core/devtools/widget/frame_graph.cpp index 952f50c34..0e170db38 100644 --- a/src/core/devtools/widget/frame_graph.cpp +++ b/src/core/devtools/widget/frame_graph.cpp @@ -19,6 +19,57 @@ constexpr float BAR_HEIGHT_MULT = 1.25f; constexpr float FRAME_GRAPH_PADDING_Y = 3.0f; constexpr static float FRAME_GRAPH_HEIGHT = 50.0f; +void FrameGraph::DrawFrameGraph() { + // Frame graph - inspired by + // https://asawicki.info/news_1758_an_idea_for_visualization_of_frame_times + const float full_width = GetContentRegionAvail().x; + auto pos = GetCursorScreenPos(); + const ImVec2 size{full_width, FRAME_GRAPH_HEIGHT + FRAME_GRAPH_PADDING_Y * 2.0f}; + ItemSize(size); + if (!ItemAdd({pos, pos + size}, GetID("FrameGraph"))) { + return; + } + + float target_dt = 1.0f / (TARGET_FPS * (float)Config::vblankDiv()); + float cur_pos_x = pos.x + full_width; + pos.y += FRAME_GRAPH_PADDING_Y; + const float final_pos_y = pos.y + FRAME_GRAPH_HEIGHT; + + auto& draw_list = *GetWindowDrawList(); + draw_list.AddRectFilled({pos.x, pos.y - FRAME_GRAPH_PADDING_Y}, + {pos.x + full_width, final_pos_y + FRAME_GRAPH_PADDING_Y}, + IM_COL32(0x33, 0x33, 0x33, 0xFF)); + draw_list.PushClipRect({pos.x, pos.y}, {pos.x + full_width, final_pos_y}, true); + for (u32 i = 0; i < FRAME_BUFFER_SIZE; ++i) { + const auto& frame_info = frame_list[(DebugState.GetFrameNum() - i) % FRAME_BUFFER_SIZE]; + const float dt_factor = target_dt / frame_info.delta; + + const float width = std::ceil(BAR_WIDTH_MULT / dt_factor); + const float height = + std::min(std::log2(BAR_HEIGHT_MULT / dt_factor) / 3.0f, 1.0f) * FRAME_GRAPH_HEIGHT; + + ImU32 color; + if (dt_factor >= 0.95f) { // BLUE + color = IM_COL32(0x33, 0x33, 0xFF, 0xFF); + } else if (dt_factor >= 0.5f) { // GREEN <> YELLOW + float t = 1.0f - (dt_factor - 0.5f) * 2.0f; + int r = (int)(0xFF * t); + color = IM_COL32(r, 0xFF, 0, 0xFF); + } else { // YELLOW <> RED + float t = dt_factor * 2.0f; + int g = (int)(0xFF * t); + color = IM_COL32(0xFF, g, 0, 0xFF); + } + draw_list.AddRectFilled({cur_pos_x - width, final_pos_y - height}, {cur_pos_x, final_pos_y}, + color); + cur_pos_x -= width; + if (cur_pos_x < width) { + break; + } + } + draw_list.PopClipRect(); +} + void FrameGraph::Draw() { if (!is_open) { return; @@ -43,55 +94,9 @@ void FrameGraph::Draw() { Text("Frame time: %.3f ms (%.1f FPS)", deltaTime, frameRate); Text("Flip frame: %d Gnm submit frame: %d", DebugState.flip_frame_count.load(), DebugState.gnm_frame_count.load()); + SeparatorText("Frame graph"); - - const float full_width = GetContentRegionAvail().x; - // Frame graph - inspired by - // https://asawicki.info/news_1758_an_idea_for_visualization_of_frame_times - auto pos = GetCursorScreenPos(); - const ImVec2 size{full_width, FRAME_GRAPH_HEIGHT + FRAME_GRAPH_PADDING_Y * 2.0f}; - ItemSize(size); - if (!ItemAdd({pos, pos + size}, GetID("FrameGraph"))) { - return; - } - - float target_dt = 1.0f / (TARGET_FPS * (float)Config::vblankDiv()); - float cur_pos_x = pos.x + full_width; - pos.y += FRAME_GRAPH_PADDING_Y; - const float final_pos_y = pos.y + FRAME_GRAPH_HEIGHT; - - draw_list.AddRectFilled({pos.x, pos.y - FRAME_GRAPH_PADDING_Y}, - {pos.x + full_width, final_pos_y + FRAME_GRAPH_PADDING_Y}, - IM_COL32(0x33, 0x33, 0x33, 0xFF)); - draw_list.PushClipRect({pos.x, pos.y}, {pos.x + full_width, final_pos_y}, true); - for (u32 i = 0; i < FRAME_BUFFER_SIZE; ++i) { - const auto& frame_info = frame_list[(DebugState.GetFrameNum() - i) % FRAME_BUFFER_SIZE]; - const float dt_factor = target_dt / frame_info.delta; - - const float width = std::ceil(BAR_WIDTH_MULT / dt_factor); - const float height = - std::min(std::log2(BAR_HEIGHT_MULT / dt_factor) / 3.0f, 1.0f) * FRAME_GRAPH_HEIGHT; - - ImU32 color; - if (dt_factor >= 0.95f) { // BLUE - color = IM_COL32(0x33, 0x33, 0xFF, 0xFF); - } else if (dt_factor >= 0.5f) { // GREEN <> YELLOW - float t = 1.0f - (dt_factor - 0.5f) * 2.0f; - int r = (int)(0xFF * t); - color = IM_COL32(r, 0xFF, 0, 0xFF); - } else { // YELLOW <> RED - float t = dt_factor * 2.0f; - int g = (int)(0xFF * t); - color = IM_COL32(0xFF, g, 0, 0xFF); - } - draw_list.AddRectFilled({cur_pos_x - width, final_pos_y - height}, - {cur_pos_x, final_pos_y}, color); - cur_pos_x -= width; - if (cur_pos_x < width) { - break; - } - } - draw_list.PopClipRect(); + DrawFrameGraph(); } End(); } diff --git a/src/core/devtools/widget/frame_graph.h b/src/core/devtools/widget/frame_graph.h index 700b6b2a2..40a68ffa7 100644 --- a/src/core/devtools/widget/frame_graph.h +++ b/src/core/devtools/widget/frame_graph.h @@ -16,6 +16,8 @@ class FrameGraph { std::array frame_list{}; + void DrawFrameGraph(); + public: bool is_open = true; diff --git a/src/core/devtools/widget/reg_view.cpp b/src/core/devtools/widget/reg_view.cpp index 79b02a849..a1b7937df 100644 --- a/src/core/devtools/widget/reg_view.cpp +++ b/src/core/devtools/widget/reg_view.cpp @@ -292,6 +292,17 @@ void RegView::Draw() { EndMenuBar(); } + const char* shader_name = "_"; + if (data.is_compute) { + shader_name = data.cs_data.name.c_str(); + } else if (selected_shader >= 0) { + shader_name = data.stages[selected_shader].name.c_str(); + } + + TextUnformatted("Shader: "); + SameLine(); + TextUnformatted(shader_name); + if (!data.is_compute && BeginChild("STAGES", {}, ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeY)) { diff --git a/src/core/devtools/widget/shader_list.cpp b/src/core/devtools/widget/shader_list.cpp index 2c97db7fd..97d01896d 100644 --- a/src/core/devtools/widget/shader_list.cpp +++ b/src/core/devtools/widget/shader_list.cpp @@ -112,6 +112,10 @@ bool ShaderList::Selection::DrawShader(DebugStateType::ShaderDump& value) { ReloadShader(value); } } + SameLine(); + if (Button("Copy name")) { + SetClipboardText(value.name.c_str()); + } if (value.is_patched) { if (BeginCombo("Shader type", showing_bin ? "SPIRV" : "GLSL", @@ -229,9 +233,16 @@ void ShaderList::Draw() { return; } + InputTextEx("##search_shader", "Search by name", search_box, sizeof(search_box), {}, + ImGuiInputTextFlags_None); + auto width = GetContentRegionAvail().x; int i = 0; for (const auto& shader : DebugState.shader_dump_list) { + if (search_box[0] != '\0' && !shader.name.contains(search_box)) { + i++; + continue; + } char name[128]; if (shader.is_patched) { snprintf(name, sizeof(name), "%s (PATCH ON)", shader.name.c_str()); diff --git a/src/core/devtools/widget/shader_list.h b/src/core/devtools/widget/shader_list.h index 2534ded35..fbb8d2070 100644 --- a/src/core/devtools/widget/shader_list.h +++ b/src/core/devtools/widget/shader_list.h @@ -31,6 +31,8 @@ class ShaderList { std::vector open_shaders{}; + char search_box[128]{}; + public: bool open = false; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 74ae6b61c..c880cad70 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -497,7 +497,7 @@ vk::ShaderModule PipelineCache::CompileModule(Shader::Info& info, Shader::Runtim module = CompileSPV(spv, instance.GetDevice()); } - const auto name = fmt::format("{}_{:#018x}_{}", info.stage, info.pgm_hash, perm_idx); + const auto name = GetShaderName(info.stage, info.pgm_hash, perm_idx); Vulkan::SetObjectName(instance.GetDevice(), module, name); if (Config::collectShadersForDebug()) { DebugState.CollectShader(name, info.l_stage, module, spv, code, @@ -572,6 +572,14 @@ std::optional PipelineCache::ReplaceShader(vk::ShaderModule mo return new_module; } +std::string PipelineCache::GetShaderName(Shader::Stage stage, u64 hash, + std::optional perm) { + if (perm) { + return fmt::format("{}_{:#018x}_{}", stage, hash, *perm); + } + return fmt::format("{}_{:#018x}", stage, hash); +} + void PipelineCache::DumpShader(std::span code, u64 hash, Shader::Stage stage, size_t perm_idx, std::string_view ext) { if (!Config::dumpShaders()) { @@ -583,7 +591,7 @@ void PipelineCache::DumpShader(std::span code, u64 hash, Shader::Stag if (!std::filesystem::exists(dump_dir)) { std::filesystem::create_directories(dump_dir); } - const auto filename = fmt::format("{}_{:#018x}_{}.{}", stage, hash, perm_idx, ext); + const auto filename = fmt::format("{}.{}", GetShaderName(stage, hash, perm_idx), ext); const auto file = IOFile{dump_dir / filename, FileAccessMode::Write}; file.WriteSpan(code); } @@ -597,7 +605,7 @@ std::optional> PipelineCache::GetShaderPatch(u64 hash, Shader:: if (!std::filesystem::exists(patch_dir)) { std::filesystem::create_directories(patch_dir); } - const auto filename = fmt::format("{}_{:#018x}_{}.{}", stage, hash, perm_idx, ext); + const auto filename = fmt::format("{}.{}", GetShaderName(stage, hash, perm_idx), ext); const auto filepath = patch_dir / filename; if (!std::filesystem::exists(filepath)) { return {}; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index d4a51efdb..b3bccd513 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h @@ -65,6 +65,9 @@ public: std::optional ReplaceShader(vk::ShaderModule module, std::span spv_code); + static std::string GetShaderName(Shader::Stage stage, u64 hash, + std::optional perm = {}); + private: bool RefreshGraphicsKey(); bool RefreshComputeKey(); From 5dd1e7c32ef191c94e9e06faea27b62b61d81496 Mon Sep 17 00:00:00 2001 From: f8ith Date: Fri, 27 Dec 2024 19:53:41 +0800 Subject: [PATCH 260/549] settings-dialog: use grid layout (#1920) --- src/qt_gui/settings_dialog.ui | 567 ++++++++++++++++++---------------- 1 file changed, 297 insertions(+), 270 deletions(-) diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index e6a9d5a58..4d69ee5b1 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -42,160 +42,42 @@ - + true - - QFrame::Shape::NoFrame + + + 0 + 0 + - - true + + 0 - - + + true - - - 0 - -97 - 815 - 618 - - - - - 0 - 0 - - - - 0 - - - - General - - + + General + + + + + 0 + 0 + 822 + 487 + + + - - - - - - - System - - - - - - Console Language - - - - - - - - - - - - Emulator Language - - - - - - - - - - - - - - - - - - - Emulator - - - - - - - - Enable Fullscreen - - - - - - - Enable Separate Update Folder - - - - - - - Show Splash - - - - - - - Is PS4 Pro - - - - - - - Enable Discord Rich Presence - - - - - - - - - 6 - - - 0 - - - - - - - Username - - - - - - - - - - - - - - - - - - + + + 0 + + @@ -275,12 +157,129 @@ - - - - - + + + + + + System + + + + + + Console Language + + + + + + + + + + + + Emulator Language + + + + + + + + + + + + + + + + + + + Emulator + + + + + + 10 + + + + + Enable Fullscreen + + + + + + + Enable Separate Update Folder + + + + + + + Show Splash + + + + + + + Is PS4 Pro + + + + + + + Enable Discord Rich Presence + + + + + + + + + 6 + + + 0 + + + + + + + Username + + + + + + + + + + + + + + + + + + + + -1 + QLayout::SizeConstraint::SetDefaultConstraint @@ -296,7 +295,7 @@ 0 - + @@ -306,7 +305,7 @@ - 265 + 0 0 @@ -321,7 +320,7 @@ - 5 + 10 1 @@ -343,7 +342,7 @@ 0 - 75 + 0 @@ -404,8 +403,8 @@ - 197 - 28 + 0 + 0 @@ -443,7 +442,7 @@ - + @@ -476,6 +475,19 @@ + + + + + 0 + 0 + + + + Play title music + + + @@ -485,20 +497,7 @@ 0 - - - - 0 - 0 - - - - Play title music - - - - - + Qt::Orientation::Vertical @@ -508,7 +507,7 @@ 20 - 2 + 13 @@ -573,7 +572,7 @@ - + @@ -593,6 +592,9 @@ Game Compatibility + + 10 + 1 @@ -614,53 +616,56 @@ - - - 1 + + + + 0 + 0 + - - 0 + + + 0 + 0 + - - - - - 0 - 0 - - - - - 197 - 28 - - - - - 16777215 - 16777215 - - - - Update Compatibility Database - - - - - - - + + + 16777215 + 16777215 + + + + Update Compatibility Database + + + + + + + + + - - - - - - - Input - + + + + true + + + Input + + + + + 0 + 0 + 396 + 222 + + @@ -769,8 +774,8 @@ - 80 - 30 + 0 + 0 @@ -856,7 +861,7 @@ - 237 + 0 0 @@ -935,10 +940,23 @@ - - - Graphics - + + + + true + + + Graphics + + + + + 0 + 0 + 536 + 192 + + @@ -1173,58 +1191,54 @@ - - - Paths - - + + + + true + + + Paths + + + + + 0 + 0 + 146 + 215 + + + - + Game Folders - - - - 0 - 20 - 401 - 331 - - - - - - - 100 - 360 - 91 - 24 - - - - Add... - - - - - - 199 - 360 - 91 - 24 - - - - Remove - - + + + + + Remove + + + + + + + Add... + + + + + + + - + Qt::Orientation::Horizontal @@ -1243,10 +1257,23 @@ - - - Debug - + + + + true + + + Debug + + + + + 0 + 0 + 288 + 163 + + From 4e8b8887dff612565b1761c6a0175f31fa944bd5 Mon Sep 17 00:00:00 2001 From: bigol83 <38129260+bigol83@users.noreply.github.com> Date: Fri, 27 Dec 2024 12:53:58 +0100 Subject: [PATCH 261/549] Fix typo (#1918) --- documents/building-windows.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documents/building-windows.md b/documents/building-windows.md index 0d523692e..845cdd10f 100644 --- a/documents/building-windows.md +++ b/documents/building-windows.md @@ -64,7 +64,7 @@ Go through the Git for Windows installation as normal Your shadps4.exe will be in `C:\path\to\source\Build\x64-Clang-Release\` To automatically populate the necessary files to run shadPS4.exe, run in a command prompt or terminal: -`C:\Qt\6.7.3\msvc2022_64\bin\windeployqt.exe "C:\path\to\shadps4.exe"` +`C:\Qt\6.7.3\msvc2022_64\bin\windeployqt6.exe "C:\path\to\shadps4.exe"` (Change Qt path if you've installed it to non-default path) ## Option 2: MSYS2/MinGW From 9b8de8d32059d0c124be0b85e102994fc04fb1ef Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Fri, 27 Dec 2024 08:54:31 -0300 Subject: [PATCH 262/549] Fix TR (#1897) --- src/qt_gui/gui_context_menus.h | 8 ++++---- src/qt_gui/translations/ar.ts | 8 ++++---- src/qt_gui/translations/da_DK.ts | 8 ++++---- src/qt_gui/translations/de.ts | 8 ++++---- src/qt_gui/translations/el.ts | 8 ++++---- src/qt_gui/translations/en.ts | 8 ++++---- src/qt_gui/translations/es_ES.ts | 8 ++++---- src/qt_gui/translations/fa_IR.ts | 8 ++++---- src/qt_gui/translations/fi.ts | 8 ++++---- src/qt_gui/translations/fr.ts | 8 ++++---- src/qt_gui/translations/hu_HU.ts | 8 ++++---- src/qt_gui/translations/id.ts | 8 ++++---- src/qt_gui/translations/it.ts | 8 ++++---- src/qt_gui/translations/ja_JP.ts | 8 ++++---- src/qt_gui/translations/ko_KR.ts | 8 ++++---- src/qt_gui/translations/lt_LT.ts | 8 ++++---- src/qt_gui/translations/nb.ts | 8 ++++---- src/qt_gui/translations/nl.ts | 8 ++++---- src/qt_gui/translations/pl_PL.ts | 8 ++++---- src/qt_gui/translations/pt_BR.ts | 8 ++++---- src/qt_gui/translations/ro_RO.ts | 8 ++++---- src/qt_gui/translations/ru_RU.ts | 8 ++++---- src/qt_gui/translations/sq.ts | 8 ++++---- src/qt_gui/translations/tr_TR.ts | 8 ++++---- src/qt_gui/translations/uk_UA.ts | 8 ++++---- src/qt_gui/translations/vi_VN.ts | 8 ++++---- src/qt_gui/translations/zh_CN.ts | 8 ++++---- src/qt_gui/translations/zh_TW.ts | 8 ++++---- 28 files changed, 112 insertions(+), 112 deletions(-) diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h index d26b860c1..6c96ab37f 100644 --- a/src/qt_gui/gui_context_menus.h +++ b/src/qt_gui/gui_context_menus.h @@ -287,11 +287,11 @@ public: #endif QMessageBox::information( nullptr, tr("Shortcut creation"), - QString(tr("Shortcut created successfully!\n %1")).arg(linkPath)); + QString(tr("Shortcut created successfully!") + "\n%1").arg(linkPath)); } else { QMessageBox::critical( nullptr, tr("Error"), - QString(tr("Error creating shortcut!\n %1")).arg(linkPath)); + QString(tr("Error creating shortcut!") + "\n%1").arg(linkPath)); } } else { QMessageBox::critical(nullptr, tr("Error"), tr("Failed to convert icon.")); @@ -305,11 +305,11 @@ public: #endif QMessageBox::information( nullptr, tr("Shortcut creation"), - QString(tr("Shortcut created successfully!\n %1")).arg(linkPath)); + QString(tr("Shortcut created successfully!") + "\n%1").arg(linkPath)); } else { QMessageBox::critical( nullptr, tr("Error"), - QString(tr("Error creating shortcut!\n %1")).arg(linkPath)); + QString(tr("Error creating shortcut!") + "\n%1").arg(linkPath)); } } } diff --git a/src/qt_gui/translations/ar.ts b/src/qt_gui/translations/ar.ts index dad4469cb..cd71e083c 100644 --- a/src/qt_gui/translations/ar.ts +++ b/src/qt_gui/translations/ar.ts @@ -182,8 +182,8 @@ - Shortcut created successfully!\n %1 - تم إنشاء الاختصار بنجاح!\n %1 + Shortcut created successfully! + تم إنشاء الاختصار بنجاح! @@ -192,8 +192,8 @@ - Error creating shortcut!\n %1 - !\n %1 خطأ في إنشاء الاختصار + Error creating shortcut! + خطأ في إنشاء الاختصار diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts index 2448d4426..677789d49 100644 --- a/src/qt_gui/translations/da_DK.ts +++ b/src/qt_gui/translations/da_DK.ts @@ -182,8 +182,8 @@ - Shortcut created successfully!\n %1 - Shortcut created successfully!\n %1 + Shortcut created successfully! + Shortcut created successfully! @@ -192,8 +192,8 @@ - Error creating shortcut!\n %1 - Error creating shortcut!\n %1 + Error creating shortcut! + Error creating shortcut! diff --git a/src/qt_gui/translations/de.ts b/src/qt_gui/translations/de.ts index dd98c209b..64770f6fe 100644 --- a/src/qt_gui/translations/de.ts +++ b/src/qt_gui/translations/de.ts @@ -182,8 +182,8 @@ - Shortcut created successfully!\n %1 - Verknüpfung erfolgreich erstellt!\n %1 + Shortcut created successfully! + Verknüpfung erfolgreich erstellt! @@ -192,8 +192,8 @@ - Error creating shortcut!\n %1 - Fehler beim Erstellen der Verknüpfung!\n %1 + Error creating shortcut! + Fehler beim Erstellen der Verknüpfung! diff --git a/src/qt_gui/translations/el.ts b/src/qt_gui/translations/el.ts index afb8ebddf..05faa7bc9 100644 --- a/src/qt_gui/translations/el.ts +++ b/src/qt_gui/translations/el.ts @@ -182,8 +182,8 @@ - Shortcut created successfully!\n %1 - Shortcut created successfully!\n %1 + Shortcut created successfully! + Shortcut created successfully! @@ -192,8 +192,8 @@ - Error creating shortcut!\n %1 - Error creating shortcut!\n %1 + Error creating shortcut! + Error creating shortcut! diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index 1bbc68e61..b616b889a 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -182,8 +182,8 @@ - Shortcut created successfully!\n %1 - Shortcut created successfully!\n %1 + Shortcut created successfully! + Shortcut created successfully! @@ -192,8 +192,8 @@ - Error creating shortcut!\n %1 - Error creating shortcut!\n %1 + Error creating shortcut! + Error creating shortcut! diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index badb18d2f..9b8b38129 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -182,8 +182,8 @@ - Shortcut created successfully!\n %1 - ¡Acceso directo creado con éxito!\n %1 + Shortcut created successfully! + ¡Acceso directo creado con éxito! @@ -192,8 +192,8 @@ - Error creating shortcut!\n %1 - ¡Error al crear el acceso directo!\n %1 + Error creating shortcut! + ¡Error al crear el acceso directo! diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index e34b04491..66ec0b4c0 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -182,8 +182,8 @@ - Shortcut created successfully!\n %1 - میانبر با موفقیت ساخته شد! \n %1 + Shortcut created successfully! + میانبر با موفقیت ساخته شد! @@ -192,8 +192,8 @@ - Error creating shortcut!\n %1 - مشکلی در هنگام ساخت میانبر بوجود آمد!\n %1 + Error creating shortcut! + مشکلی در هنگام ساخت میانبر بوجود آمد! diff --git a/src/qt_gui/translations/fi.ts b/src/qt_gui/translations/fi.ts index 7c933c5ec..67ea079aa 100644 --- a/src/qt_gui/translations/fi.ts +++ b/src/qt_gui/translations/fi.ts @@ -182,8 +182,8 @@ - Shortcut created successfully!\n %1 - Shortcut created successfully!\n %1 + Shortcut created successfully! + Shortcut created successfully! @@ -192,8 +192,8 @@ - Error creating shortcut!\n %1 - Error creating shortcut!\n %1 + Error creating shortcut! + Error creating shortcut! diff --git a/src/qt_gui/translations/fr.ts b/src/qt_gui/translations/fr.ts index 7a61dc292..c092580a8 100644 --- a/src/qt_gui/translations/fr.ts +++ b/src/qt_gui/translations/fr.ts @@ -182,8 +182,8 @@ - Shortcut created successfully!\n %1 - Raccourci créé avec succès !\n %1 + Shortcut created successfully! + Raccourci créé avec succès ! @@ -192,8 +192,8 @@ - Error creating shortcut!\n %1 - Erreur lors de la création du raccourci !\n %1 + Error creating shortcut! + Erreur lors de la création du raccourci ! diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index bf0b26988..7e60001f6 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -182,8 +182,8 @@ - Shortcut created successfully!\n %1 - Parancsikon sikeresen létrehozva!\n %1 + Shortcut created successfully! + Parancsikon sikeresen létrehozva! @@ -192,8 +192,8 @@ - Error creating shortcut!\n %1 - Hiba a parancsikon létrehozásával!\n %1 + Error creating shortcut! + Hiba a parancsikon létrehozásával! diff --git a/src/qt_gui/translations/id.ts b/src/qt_gui/translations/id.ts index 098c17bc4..31f377341 100644 --- a/src/qt_gui/translations/id.ts +++ b/src/qt_gui/translations/id.ts @@ -182,8 +182,8 @@ - Shortcut created successfully!\n %1 - Shortcut created successfully!\n %1 + Shortcut created successfully! + Shortcut created successfully! @@ -192,8 +192,8 @@ - Error creating shortcut!\n %1 - Error creating shortcut!\n %1 + Error creating shortcut! + Error creating shortcut! diff --git a/src/qt_gui/translations/it.ts b/src/qt_gui/translations/it.ts index 0bff066ff..8dc0817f1 100644 --- a/src/qt_gui/translations/it.ts +++ b/src/qt_gui/translations/it.ts @@ -182,8 +182,8 @@ - Shortcut created successfully!\n %1 - Scorciatoia creata con successo!\n %1 + Shortcut created successfully! + Scorciatoia creata con successo! @@ -192,8 +192,8 @@ - Error creating shortcut!\n %1 - Errore nella creazione della scorciatoia!\n %1 + Error creating shortcut! + Errore nella creazione della scorciatoia! diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index c5ee65ce0..43fb37c2c 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -182,8 +182,8 @@ - Shortcut created successfully!\n %1 - ショートカットが正常に作成されました!\n %1 + Shortcut created successfully! + ショートカットが正常に作成されました! @@ -192,8 +192,8 @@ - Error creating shortcut!\n %1 - ショートカットの作成に失敗しました!\n %1 + Error creating shortcut! + ショートカットの作成に失敗しました! diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts index e47db791a..ffaa8404f 100644 --- a/src/qt_gui/translations/ko_KR.ts +++ b/src/qt_gui/translations/ko_KR.ts @@ -182,8 +182,8 @@ - Shortcut created successfully!\n %1 - Shortcut created successfully!\n %1 + Shortcut created successfully! + Shortcut created successfully! @@ -192,8 +192,8 @@ - Error creating shortcut!\n %1 - Error creating shortcut!\n %1 + Error creating shortcut! + Error creating shortcut! diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts index ba5308358..5cf4d0e6b 100644 --- a/src/qt_gui/translations/lt_LT.ts +++ b/src/qt_gui/translations/lt_LT.ts @@ -182,8 +182,8 @@ - Shortcut created successfully!\n %1 - Shortcut created successfully!\n %1 + Shortcut created successfully! + Shortcut created successfully! @@ -192,8 +192,8 @@ - Error creating shortcut!\n %1 - Error creating shortcut!\n %1 + Error creating shortcut! + Error creating shortcut! diff --git a/src/qt_gui/translations/nb.ts b/src/qt_gui/translations/nb.ts index 8fe4cec9b..ecc323879 100644 --- a/src/qt_gui/translations/nb.ts +++ b/src/qt_gui/translations/nb.ts @@ -182,8 +182,8 @@ - Shortcut created successfully!\n %1 - Snarvei opprettet!\n %1 + Shortcut created successfully! + Snarvei opprettet! @@ -192,8 +192,8 @@ - Error creating shortcut!\n %1 - Feil ved opprettelse av snarvei!\n %1 + Error creating shortcut! + Feil ved opprettelse av snarvei! diff --git a/src/qt_gui/translations/nl.ts b/src/qt_gui/translations/nl.ts index 4dd8813b0..eb49c83ab 100644 --- a/src/qt_gui/translations/nl.ts +++ b/src/qt_gui/translations/nl.ts @@ -182,8 +182,8 @@ - Shortcut created successfully!\n %1 - Shortcut created successfully!\n %1 + Shortcut created successfully! + Shortcut created successfully! @@ -192,8 +192,8 @@ - Error creating shortcut!\n %1 - Error creating shortcut!\n %1 + Error creating shortcut! + Error creating shortcut! diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index 012f92164..cb9e7987d 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -182,8 +182,8 @@ - Shortcut created successfully!\n %1 - Utworzenie skrótu zakończone pomyślnie!\n %1 + Shortcut created successfully! + Utworzenie skrótu zakończone pomyślnie! @@ -192,8 +192,8 @@ - Error creating shortcut!\n %1 - Utworzenie skrótu zakończone niepowodzeniem!\n %1 + Error creating shortcut! + Utworzenie skrótu zakończone niepowodzeniem! diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index 2d40781cf..a668c61d1 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -182,8 +182,8 @@ - Shortcut created successfully!\n %1 - Atalho criado com sucesso!\n %1 + Shortcut created successfully! + Atalho criado com sucesso! @@ -192,8 +192,8 @@ - Error creating shortcut!\n %1 - Erro ao criar atalho!\n %1 + Error creating shortcut! + Erro ao criar atalho! diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index aa72049af..ae7e2efcb 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -182,8 +182,8 @@ - Shortcut created successfully!\n %1 - Shortcut created successfully!\n %1 + Shortcut created successfully! + Shortcut created successfully! @@ -192,8 +192,8 @@ - Error creating shortcut!\n %1 - Error creating shortcut!\n %1 + Error creating shortcut! + Error creating shortcut! diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index 67a38eeba..0349dfce7 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -182,8 +182,8 @@ - Shortcut created successfully!\n %1 - Ярлык создан успешно!\n %1 + Shortcut created successfully! + Ярлык создан успешно! @@ -192,8 +192,8 @@ - Error creating shortcut!\n %1 - Ошибка создания ярлыка!\n %1 + Error creating shortcut! + Ошибка создания ярлыка! diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index 7f4b8e6c2..c3280e0ea 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -182,8 +182,8 @@ - Shortcut created successfully!\n %1 - Shkurtorja u krijua me sukses!\n %1 + Shortcut created successfully! + Shkurtorja u krijua me sukses! @@ -192,8 +192,8 @@ - Error creating shortcut!\n %1 - Gabim në krijimin e shkurtores!\n %1 + Error creating shortcut! + Gabim në krijimin e shkurtores! diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index 33e8d0905..4d644ecfe 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -182,8 +182,8 @@ - Shortcut created successfully!\n %1 - Kısayol başarıyla oluşturuldu!\n %1 + Shortcut created successfully! + Kısayol başarıyla oluşturuldu! @@ -192,8 +192,8 @@ - Error creating shortcut!\n %1 - Kısayol oluşturulurken hata oluştu!\n %1 + Error creating shortcut! + Kısayol oluşturulurken hata oluştu! diff --git a/src/qt_gui/translations/uk_UA.ts b/src/qt_gui/translations/uk_UA.ts index 88d852474..66cbf2bd9 100644 --- a/src/qt_gui/translations/uk_UA.ts +++ b/src/qt_gui/translations/uk_UA.ts @@ -182,8 +182,8 @@ - Shortcut created successfully!\n %1 - Ярлик створений успішно!\n %1 + Shortcut created successfully! + Ярлик створений успішно! @@ -192,8 +192,8 @@ - Error creating shortcut!\n %1 - Помилка при створенні ярлика!\n %1 + Error creating shortcut! + Помилка при створенні ярлика! diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index a0a316df3..9441d1697 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -182,8 +182,8 @@ - Shortcut created successfully!\n %1 - Shortcut created successfully!\n %1 + Shortcut created successfully! + Shortcut created successfully! @@ -192,8 +192,8 @@ - Error creating shortcut!\n %1 - Error creating shortcut!\n %1 + Error creating shortcut! + Error creating shortcut! diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index fb95ba935..92071c5b2 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -182,8 +182,8 @@ - Shortcut created successfully!\n %1 - 创建快捷方式成功!\n %1 + Shortcut created successfully! + 创建快捷方式成功! @@ -192,8 +192,8 @@ - Error creating shortcut!\n %1 - 创建快捷方式出错!\n %1 + Error creating shortcut! + 创建快捷方式出错! diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index 77734bf26..be7cd69ec 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -182,8 +182,8 @@ - Shortcut created successfully!\n %1 - Shortcut created successfully!\n %1 + Shortcut created successfully! + Shortcut created successfully! @@ -192,8 +192,8 @@ - Error creating shortcut!\n %1 - Error creating shortcut!\n %1 + Error creating shortcut! + Error creating shortcut! From 0677d7a2145efe934b6a70a2bc6eb655c37ef503 Mon Sep 17 00:00:00 2001 From: mailwl Date: Fri, 27 Dec 2024 17:43:10 +0300 Subject: [PATCH 263/549] Avoid to access invalid iterator (#1892) --- src/core/memory.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 41db7df4b..aa116fa3d 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -101,13 +101,17 @@ PAddr MemoryManager::Allocate(PAddr search_start, PAddr search_end, size_t size, auto dmem_area = FindDmemArea(search_start); const auto is_suitable = [&] { + if (dmem_area == dmem_map.end()) { + return false; + } const auto aligned_base = Common::AlignUp(dmem_area->second.base, alignment); const auto alignment_size = aligned_base - dmem_area->second.base; const auto remaining_size = dmem_area->second.size >= alignment_size ? dmem_area->second.size - alignment_size : 0; return dmem_area->second.is_free && remaining_size >= size; }; - while (!is_suitable() && dmem_area->second.GetEnd() <= search_end) { + while (dmem_area != dmem_map.end() && !is_suitable() && + dmem_area->second.GetEnd() <= search_end) { ++dmem_area; } ASSERT_MSG(is_suitable(), "Unable to find free direct memory area: size = {:#x}", size); From cf84c46a493303ae22a27942002660b9cd206c57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A5IGA?= <164882787+Xphalnos@users.noreply.github.com> Date: Fri, 27 Dec 2024 15:43:44 +0100 Subject: [PATCH 264/549] Fix for D32Sfloat and R8Snorm Tiled image (#1898) * Fix for D32Sfloat Tiled image * Fix for R8Snorm Tiled image --- src/video_core/texture_cache/tile_manager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/video_core/texture_cache/tile_manager.cpp b/src/video_core/texture_cache/tile_manager.cpp index f6b2e2beb..de108843b 100644 --- a/src/video_core/texture_cache/tile_manager.cpp +++ b/src/video_core/texture_cache/tile_manager.cpp @@ -25,6 +25,7 @@ static vk::Format DemoteImageFormatForDetiling(vk::Format format) { switch (format) { case vk::Format::eR8Uint: case vk::Format::eR8Unorm: + case vk::Format::eR8Snorm: return vk::Format::eR8Uint; case vk::Format::eR4G4B4A4UnormPack16: case vk::Format::eB5G6R5UnormPack16: @@ -41,6 +42,7 @@ static vk::Format DemoteImageFormatForDetiling(vk::Format format) { case vk::Format::eR8G8B8A8Snorm: case vk::Format::eR8G8B8A8Uint: case vk::Format::eR32Sfloat: + case vk::Format::eD32Sfloat: case vk::Format::eR32Uint: case vk::Format::eR16G16Sfloat: case vk::Format::eR16G16Unorm: From 1c5947d93b2031b4a16cbbb0ce69f185d39382da Mon Sep 17 00:00:00 2001 From: mailwl Date: Fri, 27 Dec 2024 17:44:57 +0300 Subject: [PATCH 265/549] Load HLE library if native library can't be loaded (#1899) --- src/emulator.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/emulator.cpp b/src/emulator.cpp index 252a34418..11a9f42f2 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -311,8 +311,9 @@ void Emulator::LoadSystemModules(const std::filesystem::path& file, std::string found_modules, [&](const auto& path) { return path.filename() == module_name; }); if (it != found_modules.end()) { LOG_INFO(Loader, "Loading {}", it->string()); - linker->LoadModule(*it); - continue; + if (linker->LoadModule(*it) != -1) { + continue; + } } if (init_func) { LOG_INFO(Loader, "Can't Load {} switching to HLE", module_name); From b1f74660df317cd3b396353599608b0cb3b5375f Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 27 Dec 2024 06:46:07 -0800 Subject: [PATCH 266/549] shader_recompiler: Implement S_BCNT1_I32_B64 and S_FF1_I32_B64 (#1889) * shader_recompiler: Implement S_BCNT1_I32_B64 * shader_recompiler: Implement S_FF1_I32_B64 * shader_recompiler: Implement IEqual for 64-bit. * shader_recompiler: Fix immediate type in S_FF1_I32_B32 --- .../backend/spirv/emit_spirv_instructions.h | 5 +++- .../backend/spirv/emit_spirv_integer.cpp | 14 +++++++++- .../frontend/translate/scalar_alu.cpp | 17 ++++++++++++ .../frontend/translate/translate.h | 2 ++ src/shader_recompiler/ir/ir_emitter.cpp | 26 +++++++++++++++---- src/shader_recompiler/ir/ir_emitter.h | 4 +-- src/shader_recompiler/ir/opcodes.inc | 5 +++- .../ir/passes/constant_propagation_pass.cpp | 5 +++- .../ir/passes/resource_tracking_pass.cpp | 2 +- 9 files changed, 68 insertions(+), 12 deletions(-) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index 2f606eb45..85bed589b 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h @@ -304,10 +304,12 @@ Id EmitBitFieldSExtract(EmitContext& ctx, IR::Inst* inst, Id base, Id offset, Id Id EmitBitFieldUExtract(EmitContext& ctx, IR::Inst* inst, Id base, Id offset, Id count); Id EmitBitReverse32(EmitContext& ctx, Id value); Id EmitBitCount32(EmitContext& ctx, Id value); +Id EmitBitCount64(EmitContext& ctx, Id value); Id EmitBitwiseNot32(EmitContext& ctx, Id value); Id EmitFindSMsb32(EmitContext& ctx, Id value); Id EmitFindUMsb32(EmitContext& ctx, Id value); Id EmitFindILsb32(EmitContext& ctx, Id value); +Id EmitFindILsb64(EmitContext& ctx, Id value); Id EmitSMin32(EmitContext& ctx, Id a, Id b); Id EmitUMin32(EmitContext& ctx, Id a, Id b); Id EmitSMax32(EmitContext& ctx, Id a, Id b); @@ -318,7 +320,8 @@ Id EmitSLessThan32(EmitContext& ctx, Id lhs, Id rhs); Id EmitSLessThan64(EmitContext& ctx, Id lhs, Id rhs); Id EmitULessThan32(EmitContext& ctx, Id lhs, Id rhs); Id EmitULessThan64(EmitContext& ctx, Id lhs, Id rhs); -Id EmitIEqual(EmitContext& ctx, Id lhs, Id rhs); +Id EmitIEqual32(EmitContext& ctx, Id lhs, Id rhs); +Id EmitIEqual64(EmitContext& ctx, Id lhs, Id rhs); Id EmitSLessThanEqual(EmitContext& ctx, Id lhs, Id rhs); Id EmitULessThanEqual(EmitContext& ctx, Id lhs, Id rhs); Id EmitSGreaterThan(EmitContext& ctx, Id lhs, Id rhs); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp index 02af92385..def1f816e 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp @@ -201,6 +201,10 @@ Id EmitBitCount32(EmitContext& ctx, Id value) { return ctx.OpBitCount(ctx.U32[1], value); } +Id EmitBitCount64(EmitContext& ctx, Id value) { + return ctx.OpBitCount(ctx.U64, value); +} + Id EmitBitwiseNot32(EmitContext& ctx, Id value) { return ctx.OpNot(ctx.U32[1], value); } @@ -217,6 +221,10 @@ Id EmitFindILsb32(EmitContext& ctx, Id value) { return ctx.OpFindILsb(ctx.U32[1], value); } +Id EmitFindILsb64(EmitContext& ctx, Id value) { + return ctx.OpFindILsb(ctx.U64, value); +} + Id EmitSMin32(EmitContext& ctx, Id a, Id b) { return ctx.OpSMin(ctx.U32[1], a, b); } @@ -277,7 +285,11 @@ Id EmitULessThan64(EmitContext& ctx, Id lhs, Id rhs) { return ctx.OpULessThan(ctx.U1[1], lhs, rhs); } -Id EmitIEqual(EmitContext& ctx, Id lhs, Id rhs) { +Id EmitIEqual32(EmitContext& ctx, Id lhs, Id rhs) { + return ctx.OpIEqual(ctx.U1[1], lhs, rhs); +} + +Id EmitIEqual64(EmitContext& ctx, Id lhs, Id rhs) { return ctx.OpIEqual(ctx.U1[1], lhs, rhs); } diff --git a/src/shader_recompiler/frontend/translate/scalar_alu.cpp b/src/shader_recompiler/frontend/translate/scalar_alu.cpp index f96fd0f40..3a2b01a90 100644 --- a/src/shader_recompiler/frontend/translate/scalar_alu.cpp +++ b/src/shader_recompiler/frontend/translate/scalar_alu.cpp @@ -100,8 +100,12 @@ void Translator::EmitScalarAlu(const GcnInst& inst) { return S_BREV_B32(inst); case Opcode::S_BCNT1_I32_B32: return S_BCNT1_I32_B32(inst); + case Opcode::S_BCNT1_I32_B64: + return S_BCNT1_I32_B64(inst); case Opcode::S_FF1_I32_B32: return S_FF1_I32_B32(inst); + case Opcode::S_FF1_I32_B64: + return S_FF1_I32_B64(inst); case Opcode::S_AND_SAVEEXEC_B64: return S_SAVEEXEC_B64(NegateMode::None, false, inst); case Opcode::S_ORN2_SAVEEXEC_B64: @@ -585,12 +589,25 @@ void Translator::S_BCNT1_I32_B32(const GcnInst& inst) { ir.SetScc(ir.INotEqual(result, ir.Imm32(0))); } +void Translator::S_BCNT1_I32_B64(const GcnInst& inst) { + const IR::U32 result = ir.BitCount(GetSrc64(inst.src[0])); + SetDst(inst.dst[0], result); + ir.SetScc(ir.INotEqual(result, ir.Imm32(0))); +} + void Translator::S_FF1_I32_B32(const GcnInst& inst) { const IR::U32 src0{GetSrc(inst.src[0])}; const IR::U32 result{ir.Select(ir.IEqual(src0, ir.Imm32(0U)), ir.Imm32(-1), ir.FindILsb(src0))}; SetDst(inst.dst[0], result); } +void Translator::S_FF1_I32_B64(const GcnInst& inst) { + const IR::U64 src0{GetSrc64(inst.src[0])}; + const IR::U32 result{ + ir.Select(ir.IEqual(src0, ir.Imm64(u64(0))), ir.Imm32(-1), ir.FindILsb(src0))}; + SetDst(inst.dst[0], result); +} + void Translator::S_SAVEEXEC_B64(NegateMode negate, bool is_or, const GcnInst& inst) { // This instruction normally operates on 64-bit data (EXEC, VCC, SGPRs) // However here we flatten it to 1-bit EXEC and 1-bit VCC. For the destination diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index fd4d8d86a..e8584ec2f 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -111,7 +111,9 @@ public: void S_NOT_B64(const GcnInst& inst); void S_BREV_B32(const GcnInst& inst); void S_BCNT1_I32_B32(const GcnInst& inst); + void S_BCNT1_I32_B64(const GcnInst& inst); void S_FF1_I32_B32(const GcnInst& inst); + void S_FF1_I32_B64(const GcnInst& inst); void S_GETPC_B64(u32 pc, const GcnInst& inst); void S_SAVEEXEC_B64(NegateMode negate, bool is_or, const GcnInst& inst); void S_ABS_I32(const GcnInst& inst); diff --git a/src/shader_recompiler/ir/ir_emitter.cpp b/src/shader_recompiler/ir/ir_emitter.cpp index c241ec984..c9d97679f 100644 --- a/src/shader_recompiler/ir/ir_emitter.cpp +++ b/src/shader_recompiler/ir/ir_emitter.cpp @@ -1273,8 +1273,15 @@ U32 IREmitter::BitReverse(const U32& value) { return Inst(Opcode::BitReverse32, value); } -U32 IREmitter::BitCount(const U32& value) { - return Inst(Opcode::BitCount32, value); +U32 IREmitter::BitCount(const U32U64& value) { + switch (value.Type()) { + case Type::U32: + return Inst(Opcode::BitCount32, value); + case Type::U64: + return Inst(Opcode::BitCount64, value); + default: + ThrowInvalidType(value.Type()); + } } U32 IREmitter::BitwiseNot(const U32& value) { @@ -1289,8 +1296,15 @@ U32 IREmitter::FindUMsb(const U32& value) { return Inst(Opcode::FindUMsb32, value); } -U32 IREmitter::FindILsb(const U32& value) { - return Inst(Opcode::FindILsb32, value); +U32 IREmitter::FindILsb(const U32U64& value) { + switch (value.Type()) { + case Type::U32: + return Inst(Opcode::FindILsb32, value); + case Type::U64: + return Inst(Opcode::FindILsb64, value); + default: + ThrowInvalidType(value.Type()); + } } U32 IREmitter::SMin(const U32& a, const U32& b) { @@ -1345,7 +1359,9 @@ U1 IREmitter::IEqual(const U32U64& lhs, const U32U64& rhs) { } switch (lhs.Type()) { case Type::U32: - return Inst(Opcode::IEqual, lhs, rhs); + return Inst(Opcode::IEqual32, lhs, rhs); + case Type::U64: + return Inst(Opcode::IEqual64, lhs, rhs); default: ThrowInvalidType(lhs.Type()); } diff --git a/src/shader_recompiler/ir/ir_emitter.h b/src/shader_recompiler/ir/ir_emitter.h index 4cf44107e..4679a0133 100644 --- a/src/shader_recompiler/ir/ir_emitter.h +++ b/src/shader_recompiler/ir/ir_emitter.h @@ -229,12 +229,12 @@ public: [[nodiscard]] U32 BitFieldExtract(const U32& base, const U32& offset, const U32& count, bool is_signed = false); [[nodiscard]] U32 BitReverse(const U32& value); - [[nodiscard]] U32 BitCount(const U32& value); + [[nodiscard]] U32 BitCount(const U32U64& value); [[nodiscard]] U32 BitwiseNot(const U32& value); [[nodiscard]] U32 FindSMsb(const U32& value); [[nodiscard]] U32 FindUMsb(const U32& value); - [[nodiscard]] U32 FindILsb(const U32& value); + [[nodiscard]] U32 FindILsb(const U32U64& value); [[nodiscard]] U32 SMin(const U32& a, const U32& b); [[nodiscard]] U32 UMin(const U32& a, const U32& b); [[nodiscard]] U32 IMin(const U32& a, const U32& b, bool is_signed); diff --git a/src/shader_recompiler/ir/opcodes.inc b/src/shader_recompiler/ir/opcodes.inc index aafd43ea8..cf2c3b67e 100644 --- a/src/shader_recompiler/ir/opcodes.inc +++ b/src/shader_recompiler/ir/opcodes.inc @@ -284,11 +284,13 @@ OPCODE(BitFieldSExtract, U32, U32, OPCODE(BitFieldUExtract, U32, U32, U32, U32, ) OPCODE(BitReverse32, U32, U32, ) OPCODE(BitCount32, U32, U32, ) +OPCODE(BitCount64, U32, U64, ) OPCODE(BitwiseNot32, U32, U32, ) OPCODE(FindSMsb32, U32, U32, ) OPCODE(FindUMsb32, U32, U32, ) OPCODE(FindILsb32, U32, U32, ) +OPCODE(FindILsb64, U32, U64, ) OPCODE(SMin32, U32, U32, U32, ) OPCODE(UMin32, U32, U32, U32, ) OPCODE(SMax32, U32, U32, U32, ) @@ -299,7 +301,8 @@ OPCODE(SLessThan32, U1, U32, OPCODE(SLessThan64, U1, U64, U64, ) OPCODE(ULessThan32, U1, U32, U32, ) OPCODE(ULessThan64, U1, U64, U64, ) -OPCODE(IEqual, U1, U32, U32, ) +OPCODE(IEqual32, U1, U32, U32, ) +OPCODE(IEqual64, U1, U64, U64, ) OPCODE(SLessThanEqual, U1, U32, U32, ) OPCODE(ULessThanEqual, U1, U32, U32, ) OPCODE(SGreaterThan, U1, U32, U32, ) diff --git a/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp b/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp index 16b07e1a1..fcf2f7d9f 100644 --- a/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp +++ b/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp @@ -391,9 +391,12 @@ void ConstantPropagation(IR::Block& block, IR::Inst& inst) { case IR::Opcode::UGreaterThanEqual: FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a >= b; }); return; - case IR::Opcode::IEqual: + case IR::Opcode::IEqual32: FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a == b; }); return; + case IR::Opcode::IEqual64: + FoldWhenAllImmediates(inst, [](u64 a, u64 b) { return a == b; }); + return; case IR::Opcode::INotEqual: FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a != b; }); return; diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index db1a2edd2..e6d23bfe7 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -249,7 +249,7 @@ std::pair TryDisableAnisoLod0(const IR::Inst* inst) { // Select should be based on zero check const auto* prod0 = inst->Arg(0).InstRecursive(); - if (prod0->GetOpcode() != IR::Opcode::IEqual || + if (prod0->GetOpcode() != IR::Opcode::IEqual32 || !(prod0->Arg(1).IsImmediate() && prod0->Arg(1).U32() == 0u)) { return not_found; } From a86ee7e7f525b10fc19332a4a8b0a39ad67e3e34 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 27 Dec 2024 06:46:31 -0800 Subject: [PATCH 267/549] vk_platform: Enable MoltenVK debug if crash diagnostics is enabled. (#1887) * vk_platform: Enable MoltenVK debug if crash diagnostics is enabled. * build: Make sure MoltenVK gets re-bundled when changed. --- CMakeLists.txt | 10 +++++----- src/video_core/renderer_vulkan/vk_platform.cpp | 12 ++++++++++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dac3b19ab..30a048278 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -901,11 +901,11 @@ endif() if (APPLE) if (ENABLE_QT_GUI) # Include MoltenVK in the app bundle, along with an ICD file so it can be found by the system Vulkan loader if used for loading layers. - target_sources(shadps4 PRIVATE externals/MoltenVK/MoltenVK_icd.json) - set_source_files_properties(externals/MoltenVK/MoltenVK_icd.json - PROPERTIES MACOSX_PACKAGE_LOCATION Resources/vulkan/icd.d) - add_custom_command(TARGET shadps4 POST_BUILD - COMMAND cmake -E copy $ $/Contents/Frameworks/libMoltenVK.dylib) + set(MVK_DYLIB ${CMAKE_CURRENT_BINARY_DIR}/externals/MoltenVK/libMoltenVK.dylib) + set(MVK_ICD ${CMAKE_CURRENT_SOURCE_DIR}/externals/MoltenVK/MoltenVK_icd.json) + target_sources(shadps4 PRIVATE ${MVK_DYLIB} ${MVK_ICD}) + set_source_files_properties(${MVK_DYLIB} PROPERTIES MACOSX_PACKAGE_LOCATION Frameworks) + set_source_files_properties(${MVK_ICD} PROPERTIES MACOSX_PACKAGE_LOCATION Resources/vulkan/icd.d) set_property(TARGET shadps4 APPEND PROPERTY BUILD_RPATH "@executable_path/../Frameworks") else() # For non-bundled SDL build, just do a normal library link. diff --git a/src/video_core/renderer_vulkan/vk_platform.cpp b/src/video_core/renderer_vulkan/vk_platform.cpp index dbdabe0d9..ab61af6a4 100644 --- a/src/video_core/renderer_vulkan/vk_platform.cpp +++ b/src/video_core/renderer_vulkan/vk_platform.cpp @@ -137,6 +137,7 @@ std::vector GetInstanceExtensions(Frontend::WindowSystemType window // Add the windowing system specific extension std::vector extensions; extensions.reserve(7); + extensions.push_back(VK_EXT_LAYER_SETTINGS_EXTENSION_NAME); switch (window_type) { case Frontend::WindowSystemType::Headless: @@ -347,6 +348,17 @@ vk::UniqueInstance CreateInstance(Frontend::WindowSystemType window_type, bool e .valueCount = 1, .pValues = &enable_force_barriers, }, +#ifdef __APPLE__ + // MoltenVK debug mode turns on additional device loss error details, so + // use the crash diagnostic setting as an indicator of whether to turn it on. + vk::LayerSettingEXT{ + .pLayerName = "MoltenVK", + .pSettingName = "MVK_CONFIG_DEBUG", + .type = vk::LayerSettingTypeEXT::eBool32, + .valueCount = 1, + .pValues = &enable_crash_diagnostic, + } +#endif }; vk::StructureChain instance_ci_chain = { From 0351b864d0c065f8814ae7af890c8ef32113dc72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quang=20Ng=C3=B4?= Date: Fri, 27 Dec 2024 21:47:26 +0700 Subject: [PATCH 268/549] texture_cache: Enable anisotropic filtering (#1872) --- src/video_core/amdgpu/resource.h | 27 ++++++++++++++++++++ src/video_core/renderer_vulkan/vk_instance.h | 5 ++++ src/video_core/texture_cache/sampler.cpp | 9 +++++++ 3 files changed, 41 insertions(+) diff --git a/src/video_core/amdgpu/resource.h b/src/video_core/amdgpu/resource.h index d9a8b7cac..58b286e9c 100644 --- a/src/video_core/amdgpu/resource.h +++ b/src/video_core/amdgpu/resource.h @@ -364,6 +364,16 @@ enum class Filter : u64 { AnisoLinear = 3, }; +constexpr bool IsAnisoFilter(const Filter filter) { + switch (filter) { + case Filter::AnisoPoint: + case Filter::AnisoLinear: + return true; + default: + return false; + } +} + enum class MipFilter : u64 { None = 0, Point = 1, @@ -435,6 +445,23 @@ struct Sampler { float MaxLod() const noexcept { return static_cast(max_lod.Value()) / 256.0f; } + + float MaxAniso() const { + switch (max_aniso) { + case AnisoRatio::One: + return 1.0f; + case AnisoRatio::Two: + return 2.0f; + case AnisoRatio::Four: + return 4.0f; + case AnisoRatio::Eight: + return 8.0f; + case AnisoRatio::Sixteen: + return 16.0f; + default: + UNREACHABLE(); + } + } }; } // namespace AmdGpu diff --git a/src/video_core/renderer_vulkan/vk_instance.h b/src/video_core/renderer_vulkan/vk_instance.h index 54a9b9873..62838140c 100644 --- a/src/video_core/renderer_vulkan/vk_instance.h +++ b/src/video_core/renderer_vulkan/vk_instance.h @@ -249,6 +249,11 @@ public: return properties.limits.maxSamplerLodBias; } + /// Returns the maximum sampler anisotropy. + float MaxSamplerAnisotropy() const { + return properties.limits.maxSamplerAnisotropy; + } + /// Returns the maximum number of push descriptors. u32 MaxPushDescriptors() const { return push_descriptor_props.maxPushDescriptors; diff --git a/src/video_core/texture_cache/sampler.cpp b/src/video_core/texture_cache/sampler.cpp index 9f4bc7a7e..5ce10ff0e 100644 --- a/src/video_core/texture_cache/sampler.cpp +++ b/src/video_core/texture_cache/sampler.cpp @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include "video_core/amdgpu/resource.h" #include "video_core/renderer_vulkan/liverpool_to_vk.h" #include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/texture_cache/sampler.h" @@ -12,6 +14,11 @@ Sampler::Sampler(const Vulkan::Instance& instance, const AmdGpu::Sampler& sample LOG_WARNING(Render_Vulkan, "Texture requires gamma correction"); } using namespace Vulkan; + const bool anisotropyEnable = instance.IsAnisotropicFilteringSupported() && + (AmdGpu::IsAnisoFilter(sampler.xy_mag_filter) || + AmdGpu::IsAnisoFilter(sampler.xy_min_filter)); + const float maxAnisotropy = + std::clamp(sampler.MaxAniso(), 1.0f, instance.MaxSamplerAnisotropy()); const vk::SamplerCreateInfo sampler_ci = { .magFilter = LiverpoolToVK::Filter(sampler.xy_mag_filter), .minFilter = LiverpoolToVK::Filter(sampler.xy_min_filter), @@ -20,6 +27,8 @@ Sampler::Sampler(const Vulkan::Instance& instance, const AmdGpu::Sampler& sample .addressModeV = LiverpoolToVK::ClampMode(sampler.clamp_y), .addressModeW = LiverpoolToVK::ClampMode(sampler.clamp_z), .mipLodBias = std::min(sampler.LodBias(), instance.MaxSamplerLodBias()), + .anisotropyEnable = anisotropyEnable, + .maxAnisotropy = maxAnisotropy, .compareEnable = sampler.depth_compare_func != AmdGpu::DepthCompare::Never, .compareOp = LiverpoolToVK::DepthCompare(sampler.depth_compare_func), .minLod = sampler.MinLod(), From ff4d37cbc8c7abc27f457fdba8ba83148a98f982 Mon Sep 17 00:00:00 2001 From: Yury <27062841+f1amy@users.noreply.github.com> Date: Fri, 27 Dec 2024 19:50:46 +0500 Subject: [PATCH 269/549] Update russian translation for 0.5.0 (#1903) --- src/qt_gui/translations/ru_RU.ts | 78 ++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 29 deletions(-) diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index 0349dfce7..8112f9dfd 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -118,7 +118,7 @@ Open Folder... - Открыть Папку... + Открыть папку... @@ -128,12 +128,12 @@ Open Save Data Folder - Открыть Папку Сохранений + Открыть папку сохранений Open Log Folder - Открыть Папку Логов + Открыть папку логов @@ -175,6 +175,26 @@ Delete DLC Удалить DLC + + + Compatibility... + Совместимость... + + + + Update database + Обновить базу данных + + + + View report + Посмотреть отчет + + + + Submit a report + Отправить отчет + Shortcut creation @@ -452,7 +472,7 @@ Trophy Viewer - Трофеи + Просмотр трофеев @@ -550,7 +570,7 @@ Hide Cursor Idle Timeout - Тайм-аут скрытия курсора при бездействии + Время скрытия курсора при бездействии @@ -675,7 +695,7 @@ Disable Trophy Pop-ups - Disable Trophy Pop-ups + Отключить уведомления о трофеях @@ -685,22 +705,22 @@ Update Compatibility Database On Startup - Update Compatibility Database On Startup + Обновлять базу совместимости при запуске Game Compatibility - Game Compatibility + Совместимость игр Display Compatibility Data - Display Compatibility Data + Показывать данные совместимости Update Compatibility Database - Update Compatibility Database + Обновить базу совместимости @@ -763,7 +783,7 @@ ELF files (*.bin *.elf *.oelf) - Файл ELF (*.bin *.elf *.oelf) + Файлы ELF (*.bin *.elf *.oelf) @@ -818,7 +838,7 @@ Would you like to install DLC: %1? - Вы хотите установить DLC: %1?? + Вы хотите установить DLC: %1? @@ -866,7 +886,7 @@ Cheats / Patches for - Cheats / Patches for + Читы и патчи для @@ -1189,7 +1209,7 @@ showSplashCheckBox - Показывать заставку:\nОтображает заставку игры (специальное изображение) во время запуска игры. + Показывать заставку:\nОтображает заставку игры (специальное изображение) во время запуска. @@ -1229,12 +1249,12 @@ disableTrophycheckBox - Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + Отключить уведомления о трофеях:\nОтключает внутриигровые уведомления о трофеях. Прогресс трофеев по прежнему можно отслеживать в меню Просмотр трофеев (правая кнопка мыши по игре в главном окне). hideCursorGroupBox - Скрывать курсор:\nВыберите, когда курсор исчезнет:\nНикогда: Вы всегда будете видеть мышь.\nПри бездействии: Установите время, через которое курсор исчезнет при бездействии.\nВсегда: Вы никогда не будете видеть мышь. + Скрывать курсор:\nВыберите, когда курсор будет скрыт:\nНикогда: Вы всегда будете видеть курсор.\nПри бездействии: Установите время, через которое курсор будет скрыт при бездействии.\nВсегда: Курсор всегда будет скрыт. @@ -1249,17 +1269,17 @@ enableCompatibilityCheckBox - Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + Показывать данные совместимости:\nПоказывает информацию о совместимости игр в таблице. Включите «Обновлять базу совместимости при запуске» для получения актуальной информации. checkCompatibilityOnStartupCheckBox - Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + Обновлять базу совместимости при запуске:\nАвтоматически обновлять базу данных совместимости при запуске shadPS4. updateCompatibilityButton - Update Compatibility Database:\nImmediately update the compatibility database. + Обновить базу совместимости:\nНемедленно обновить базу данных совместимости. @@ -1299,7 +1319,7 @@ graphicsAdapterGroupBox - Графическое устройство:\nВ системах с несколькими GPU выберите GPU, который будет использовать эмулятор из выпадающего списка,\nили выберите "Auto Select", чтобы определить его автоматически. + Графическое устройство:\nВ системах с несколькими GPU выберите GPU, который будет использовать эмулятор.\nВыберите "Auto Select", чтобы определить его автоматически. @@ -1377,7 +1397,7 @@ Compatibility - Compatibility + Совместимость @@ -1412,37 +1432,37 @@ Never Played - Never Played + Вы не играли Compatibility is untested - Compatibility is untested + Совместимость не проверена Game does not initialize properly / crashes the emulator - Game does not initialize properly / crashes the emulator + Игра не иницализируется правильно / крашит эмулятор Game boots, but only displays a blank screen - Game boots, but only displays a blank screen + Игра запускается, но показывает только пустой экран Game displays an image but does not go past the menu - Game displays an image but does not go past the menu + Игра показывает картинку, но не проходит дальше меню Game has game-breaking glitches or unplayable performance - Game has game-breaking glitches or unplayable performance + Игра имеет ломающие игру глюки или плохую производительность Game can be completed with playable performance and no major glitches - Game can be completed with playable performance and no major glitches + Игра может быть пройдена с хорошей производительностью и без серьезных сбоев @@ -1525,7 +1545,7 @@ Update - Обновиться + Обновить From e40ede5db2aede2a51d481f5dc1df0baf8ea9c78 Mon Sep 17 00:00:00 2001 From: Martin <67326368+Martini-141@users.noreply.github.com> Date: Fri, 27 Dec 2024 15:51:23 +0100 Subject: [PATCH 270/549] Update nb translation (#1904) * add more translations and make more consistent nb * typo and make easier to read * fix wording --- src/qt_gui/translations/nb.ts | 158 +++++++++++++++++----------------- 1 file changed, 79 insertions(+), 79 deletions(-) diff --git a/src/qt_gui/translations/nb.ts b/src/qt_gui/translations/nb.ts index ecc323879..4ebe2f0f6 100644 --- a/src/qt_gui/translations/nb.ts +++ b/src/qt_gui/translations/nb.ts @@ -108,32 +108,32 @@ SFO Viewer - SFO Viser + SFO viser Trophy Viewer - Trofé Viser + Trofé viser Open Folder... - Åpne Mappen... + Åpne mappen... Open Game Folder - Åpne Spillmappen + Åpne spillmappen Open Save Data Folder - Åpne Lagrede Data-mappen + Åpne lagrede datamappen Open Log Folder - Åpne Loggmappen + Åpne loggmappen @@ -143,17 +143,17 @@ Copy Name - Kopier Navn + Kopier navn Copy Serial - Kopier Serienummer + Kopier serienummer Copy All - Kopier Alle + Kopier alle @@ -163,12 +163,12 @@ Delete Game - Slett Spill + Slett spill Delete Update - Slett Oppdatering + Slett oppdatering @@ -251,12 +251,12 @@ Install Packages (PKG) - Installer Pakker (PKG) + Installer pakker (PKG) Boot Game - Start Spill + Start spill @@ -281,7 +281,7 @@ Recent Games - Nylige Spill + Nylige spill @@ -301,12 +301,12 @@ Show Game List - Vis Spill-listen + Vis spill-listen Game List Refresh - Oppdater Spill-listen + Oppdater spill-listen @@ -351,17 +351,17 @@ Download Cheats/Patches - Last ned Juks /Programrettelse + Last ned juks/programrettelse Dump Game List - Dump Spill-liste + Dump spill-liste PKG Viewer - PKG Viser + PKG viser @@ -381,12 +381,12 @@ Game List Icons - Spill-liste Ikoner + Spill-liste ikoner Game List Mode - Spill-liste Modus + Spill-liste modus @@ -444,7 +444,7 @@ Open Folder - Åpne Mappe + Åpne mappe @@ -452,7 +452,7 @@ Trophy Viewer - Trofé Viser + Trofé viser @@ -490,17 +490,17 @@ Enable Fullscreen - Aktiver Fullskjerm + Aktiver fullskjerm Enable Separate Update Folder - Aktiver Seperat Oppdateringsmappe + Aktiver seperat oppdateringsmappe Show Splash - Vis Velkomstbilde + Vis velkomstbilde @@ -525,12 +525,12 @@ Log Type - Log Type + Logg type Log Filter - Log Filter + Logg filter @@ -560,7 +560,7 @@ Back Button Behavior - Tilbakeknapp Atferd + Tilbakeknapp atferd @@ -585,7 +585,7 @@ Vblank Divider - Vblank Skillelinje + Vblank skillelinje @@ -595,7 +595,7 @@ Enable Shaders Dumping - Aktiver Skyggelegger Dumping + Aktiver dumping av skyggelegger @@ -630,22 +630,22 @@ Enable Debug Dumping - Aktiver Feilretting Dumping + Aktiver dumping av feilretting Enable Vulkan Validation Layers - Aktiver Vulkan Valideringslag + Aktiver Vulkan valideringslag Enable Vulkan Synchronization Validation - Aktiver Vulkan Synkroniseringslag + Aktiver Vulkan synkroniseringslag Enable RenderDoc Debugging - Aktiver RenderDoc Feilretting + Aktiver RenderDoc feilretting @@ -670,12 +670,12 @@ GUI Settings - GUI-Innstillinger + GUI-innstillinger Disable Trophy Pop-ups - Disable Trophy Pop-ups + Deaktiver trofé hurtigmeny @@ -685,22 +685,22 @@ Update Compatibility Database On Startup - Update Compatibility Database On Startup + Oppdater kompatibilitets-database ved oppstart Game Compatibility - Game Compatibility + Spill kompatibilitet Display Compatibility Data - Display Compatibility Data + Vis kompatibilitets-data Update Compatibility Database - Update Compatibility Database + Oppdater kompatibilitets-database @@ -728,7 +728,7 @@ Download Patches For All Games - Last ned oppdateringer for alle spill + Last ned programrettelser for alle spill @@ -778,7 +778,7 @@ PKG Extraction - PKG-ekstraksjon + PKG-utpakking @@ -788,7 +788,7 @@ PKG and Game versions match: - PKG- og spillversjoner stemmer overens: + PKG og spillversjoner stemmer overens: @@ -813,7 +813,7 @@ DLC Installation - DLC-installasjon + DLC installasjon @@ -833,7 +833,7 @@ PKG is a patch, please install the game first! - PKG er en oppdatering, vennligst installer spillet først! + PKG er en programrettelse, vennligst installer spillet først! @@ -871,7 +871,7 @@ defaultTextEdit_MSG - Juks/programrettelse er eksperimentelle.\nBruk med forsiktighet.\n\nLast ned juks individuelt ved å velge pakkebrønn og klikke på nedlastingsknappen.\nPå fanen programrettelse kan du laste ned alle programrettelser samtidig, velge hvilke du ønsker å bruke, og lagre valget ditt.\n\nSiden vi ikke utvikler Juksene/Programrettelsene,\nvær vennlig å rapportere problemer til jukse/programrettelse utvikleren.\n\nHar du laget en ny juks? Besøk:\nhttps://github.com/shadps4-emu/ps4_cheats + Juks/programrettelse er eksperimentelle.\nBruk med forsiktighet.\n\nLast ned juks individuelt ved å velge pakkebrønn og klikke på nedlastingsknappen.\nPå fanen programrettelse kan du laste ned alle programrettelser samtidig, velge hvilke du ønsker å bruke, og lagre valget ditt.\n\nSiden vi ikke utvikler Juks/Programrettelse,\nvær vennlig å rapportere problemer til juks/programrettelse utvikleren.\n\nHar du laget en ny juks? Besøk:\nhttps://github.com/shadps4-emu/ps4_cheats @@ -921,7 +921,7 @@ You can delete the cheats you don't want after downloading them. - Du kan slette juksene du ikke ønsker etter å ha lastet dem ned. + Du kan slette juks du ikke ønsker etter å ha lastet dem ned. @@ -936,7 +936,7 @@ Download Patches - Last ned programrettelse + Last ned programrettelser @@ -946,7 +946,7 @@ Cheats - Jukser + Juks @@ -1001,7 +1001,7 @@ Invalid Source - Ugyldig Kilde + Ugyldig kilde @@ -1011,7 +1011,7 @@ File Exists - Filen Eksisterer + Filen eksisterer @@ -1031,7 +1031,7 @@ Cheats Not Found - Fant ikke juksene + Fant ikke juks @@ -1041,12 +1041,12 @@ Cheats Downloaded Successfully - Juksene ble lastet ned + Juks ble lastet ned CheatsDownloadedSuccessfully_MSG - Du har lastet ned jukser for denne versjonen av spillet fra den valgte pakkebrønnen. Du kan prøve å laste ned fra en annen pakkebrønn, hvis det er tilgjengelig, vil det også være mulig å bruke det ved å velge filen fra listen. + Du har lastet ned juks for denne versjonen av spillet fra den valgte pakkebrønnen. Du kan prøve å laste ned fra en annen pakkebrønn, hvis det er tilgjengelig, vil det også være mulig å bruke det ved å velge filen fra listen. @@ -1066,7 +1066,7 @@ DownloadComplete_MSG - Programrettelser ble lastet ned! Alle programrettelsene tilgjengelige for alle spill har blitt lastet ned, det er ikke nødvendig å laste dem ned individuelt for hvert spill som skjer med jukser. Hvis programrettelsen ikke vises, kan det hende at den ikke finnes for den spesifikke serienummeret og versjonen av spillet. + Programrettelser ble lastet ned! Alle programrettelsene tilgjengelige for alle spill har blitt lastet ned, det er ikke nødvendig å laste dem ned individuelt for hvert spill som skjer med juks. Hvis programrettelsen ikke vises, kan det hende at den ikke finnes for den spesifikke serienummeret og versjonen av spillet. @@ -1136,7 +1136,7 @@ Can't apply cheats before the game is started - Kan ikke bruke juksene før spillet er startet. + Kan ikke bruke juks før spillet er startet. @@ -1189,7 +1189,7 @@ showSplashCheckBox - Vis Velkomstbilde:\nViser spillets velkomstbilde (et spesialbilde) når spillet starter. + Vis velkomstbilde:\nViser spillets velkomstbilde (et spesialbilde) når spillet starter. @@ -1209,12 +1209,12 @@ logTypeGroupBox - Logtype:\nAngir om loggvinduets utdata skal synkroniseres for ytelse. Kan ha negative effekter for etterligneren. + Logg type:\nAngir om loggvinduets utdata skal synkroniseres for ytelse. Kan ha negative effekter for etterligneren. logFilter - Loggfilter:\nFiltrerer loggen for å kun skrive ut spesifikk informasjon.\nEksempler: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Nivåer: Trace, Debug, Info, Warning, Error, Critical - i denne rekkefølgen, et spesifikt nivå demper alle tidligere nivåer i listen og logger alle nivåer etter det. + Logg filter:\nFiltrerer loggen for å kun skrive ut spesifikk informasjon.\nEksempler: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Nivåer: Trace, Debug, Info, Warning, Error, Critical - i denne rekkefølgen, et spesifikt nivå demper alle tidligere nivåer i listen og logger alle nivåer etter det. @@ -1229,7 +1229,7 @@ disableTrophycheckBox - Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + Deaktiver trofé hurtigmeny:\nDeaktiver trofévarsler i spillet. Trofé-fremgang kan fortsatt ved help av troféviseren (høyreklikk på spillet i hovedvinduet). @@ -1249,17 +1249,17 @@ enableCompatibilityCheckBox - Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + Vis kompatibilitets-data:\nViser informasjon om spillkompatibilitet i tabellvisning. Aktiver "Oppdater kompatibilitets-data ved oppstart" for oppdatert informasjon. checkCompatibilityOnStartupCheckBox - Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + Oppdater kompatibilitets-data ved oppstart:\nOppdaterer kompatibilitets-databasen automatisk når shadPS4 starter. updateCompatibilityButton - Update Compatibility Database:\nImmediately update the compatibility database. + Oppdater kompatibilitets-database:\nOppdater kompatibilitets-databasen nå. @@ -1299,7 +1299,7 @@ graphicsAdapterGroupBox - Grafikkenhet:\nI systemer med flere GPU-er, velg GPU-en etterligneren skal bruke fra rullegardinlisten,\neller velg "Auto Select" for å bestemme den automatisk. + Grafikkenhet:\nI systemer med flere GPU-er, velg GPU-en etterligneren skal bruke fra rullegardinlisten,\neller velg "Auto Select" for å velge automatisk. @@ -1309,12 +1309,12 @@ heightDivider - Vblank Skillelinje:\nBildehastigheten som etterligneren oppdaterer ved, multipliseres med dette tallet. Endring av dette kan ha negative effekter, som å øke hastigheten av spillet, eller ødelegge kritisk spillfunksjonalitet som ikke forventer at dette endres! + Vblank skillelinje:\nBildehastigheten som etterligneren oppdaterer ved, multipliseres med dette tallet. Endring av dette kan ha negative effekter, som å øke hastigheten av spillet, eller ødelegge kritisk spillfunksjonalitet som ikke forventer at dette endres! dumpShadersCheckBox - Aktiver skyggelegger-dumping:\nFor teknisk feilsøking lagrer skyggeleggene fra spillet i en mappe mens de gjengis. + Aktiver dumping av skyggelegger:\nFor teknisk feilsøking lagrer skyggeleggerne fra spillet i en mappe mens de gjengis. @@ -1377,7 +1377,7 @@ Compatibility - Compatibility + Kompatibilitet @@ -1412,37 +1412,37 @@ Never Played - Never Played + Aldri spilt Compatibility is untested - Compatibility is untested + kompatibilitet er utestet Game does not initialize properly / crashes the emulator - Game does not initialize properly / crashes the emulator + Spillet initialiseres ikke riktig / krasjer etterligneren Game boots, but only displays a blank screen - Game boots, but only displays a blank screen + Spillet starter, men viser bare en tom skjerm Game displays an image but does not go past the menu - Game displays an image but does not go past the menu + Spillet viser et bilde, men går ikke forbi menyen Game has game-breaking glitches or unplayable performance - Game has game-breaking glitches or unplayable performance + Spillet har spillbrytende feil eller uspillbar ytelse Game can be completed with playable performance and no major glitches - Game can be completed with playable performance and no major glitches + Spillet kan fullføres med spillbar ytelse og ingen store feil @@ -1450,7 +1450,7 @@ Auto Updater - Automatisk oppdaterering + Automatisk oppdatering @@ -1465,7 +1465,7 @@ Failed to parse update information. - Kunne ikke analysere oppdateringsinformasjonen. + Kunne ikke analysere oppdaterings-informasjonen. @@ -1545,7 +1545,7 @@ Network error occurred while trying to access the URL - Nettverksfeil oppstod mens du prøvde å få tilgang til URL + Nettverksfeil oppstod mens vi prøvde å få tilgang til URL @@ -1573,4 +1573,4 @@ Kunne ikke opprette oppdateringsskriptfilen - \ No newline at end of file + From f95803664be37d8be515433e0e734f4124896478 Mon Sep 17 00:00:00 2001 From: Nenkai Date: Fri, 27 Dec 2024 19:33:45 +0100 Subject: [PATCH 271/549] equeue: sceGnmGetEqEventType/sceKernelGetEventData impl (#1839) --- src/core/libraries/gnmdriver/gnmdriver.cpp | 37 +++++++++++++++------- src/core/libraries/gnmdriver/gnmdriver.h | 2 +- src/core/libraries/kernel/equeue.cpp | 23 ++++++++++---- src/core/libraries/kernel/equeue.h | 11 +++++-- 4 files changed, 51 insertions(+), 22 deletions(-) diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index 1a6007bf8..566f8ce1f 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -29,7 +29,7 @@ namespace Libraries::GnmDriver { using namespace AmdGpu; -enum GnmEventIdents : u64 { +enum GnmEventType : u64 { Compute0RelMem = 0x00, Compute1RelMem = 0x01, Compute2RelMem = 0x02, @@ -337,6 +337,12 @@ static inline u32* ClearContextState(u32* cmdbuf) { return cmdbuf + ClearStateSequence.size(); } +static inline bool IsValidEventType(Platform::InterruptId id) { + return (static_cast(id) >= static_cast(Platform::InterruptId::Compute0RelMem) && + static_cast(id) <= static_cast(Platform::InterruptId::Compute6RelMem)) || + static_cast(id) == static_cast(Platform::InterruptId::GfxEop); +} + s32 PS4_SYSV_ABI sceGnmAddEqEvent(SceKernelEqueue eq, u64 id, void* udata) { LOG_TRACE(Lib_GnmDriver, "called"); @@ -347,8 +353,7 @@ s32 PS4_SYSV_ABI sceGnmAddEqEvent(SceKernelEqueue eq, u64 id, void* udata) { EqueueEvent kernel_event{}; kernel_event.event.ident = id; kernel_event.event.filter = SceKernelEvent::Filter::GraphicsCore; - // The library only sets EV_ADD but it is suspected the kernel driver forces EV_CLEAR - kernel_event.event.flags = SceKernelEvent::Flags::Clear; + kernel_event.event.flags = SceKernelEvent::Flags::Add; kernel_event.event.fflags = 0; kernel_event.event.data = id; kernel_event.event.udata = udata; @@ -357,11 +362,15 @@ s32 PS4_SYSV_ABI sceGnmAddEqEvent(SceKernelEqueue eq, u64 id, void* udata) { Platform::IrqC::Instance()->Register( static_cast(id), [=](Platform::InterruptId irq) { - ASSERT_MSG(irq == static_cast(id), - "An unexpected IRQ occured"); // We need to convert IRQ# to event id and do - // proper filtering in trigger function - eq->TriggerEvent(static_cast(id), SceKernelEvent::Filter::GraphicsCore, - nullptr); + ASSERT_MSG(irq == static_cast(id), "An unexpected IRQ occured"); + + // We need to convert IRQ# to event id + if (!IsValidEventType(irq)) + return; + + // Event data is expected to be an event type as per sceGnmGetEqEventType. + eq->TriggerEvent(static_cast(id), SceKernelEvent::Filter::GraphicsCore, + reinterpret_cast(id)); }, eq); return ORBIS_OK; @@ -476,7 +485,7 @@ s32 PS4_SYSV_ABI sceGnmDeleteEqEvent(SceKernelEqueue eq, u64 id) { return ORBIS_KERNEL_ERROR_EBADF; } - eq->RemoveEvent(id); + eq->RemoveEvent(id, SceKernelEvent::Filter::GraphicsCore); Platform::IrqC::Instance()->Unregister(static_cast(id), eq); return ORBIS_OK; @@ -1000,9 +1009,13 @@ int PS4_SYSV_ABI sceGnmGetDebugTimestamp() { return ORBIS_OK; } -int PS4_SYSV_ABI sceGnmGetEqEventType() { - LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceGnmGetEqEventType(const SceKernelEvent* ev) { + LOG_TRACE(Lib_GnmDriver, "called"); + + auto data = sceKernelGetEventData(ev); + ASSERT(static_cast(data) == GnmEventType::GfxEop); + + return data; } int PS4_SYSV_ABI sceGnmGetEqTimeStamp() { diff --git a/src/core/libraries/gnmdriver/gnmdriver.h b/src/core/libraries/gnmdriver/gnmdriver.h index 017dbe3ad..d15483323 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.h +++ b/src/core/libraries/gnmdriver/gnmdriver.h @@ -85,7 +85,7 @@ int PS4_SYSV_ABI sceGnmGetCoredumpMode(); int PS4_SYSV_ABI sceGnmGetCoredumpProtectionFaultTimestamp(); int PS4_SYSV_ABI sceGnmGetDbgGcHandle(); int PS4_SYSV_ABI sceGnmGetDebugTimestamp(); -int PS4_SYSV_ABI sceGnmGetEqEventType(); +int PS4_SYSV_ABI sceGnmGetEqEventType(const SceKernelEvent* ev); int PS4_SYSV_ABI sceGnmGetEqTimeStamp(); int PS4_SYSV_ABI sceGnmGetGpuBlockStatus(); u32 PS4_SYSV_ABI sceGnmGetGpuCoreClockFrequency(); diff --git a/src/core/libraries/kernel/equeue.cpp b/src/core/libraries/kernel/equeue.cpp index 42a8eed89..3ae77e46b 100644 --- a/src/core/libraries/kernel/equeue.cpp +++ b/src/core/libraries/kernel/equeue.cpp @@ -12,6 +12,8 @@ namespace Libraries::Kernel { +// Events are uniquely identified by id and filter. + bool EqueueInternal::AddEvent(EqueueEvent& event) { std::scoped_lock lock{m_mutex}; @@ -27,12 +29,13 @@ bool EqueueInternal::AddEvent(EqueueEvent& event) { return true; } -bool EqueueInternal::RemoveEvent(u64 id) { +bool EqueueInternal::RemoveEvent(u64 id, s16 filter) { bool has_found = false; std::scoped_lock lock{m_mutex}; - const auto& it = - std::ranges::find_if(m_events, [id](auto& ev) { return ev.event.ident == id; }); + const auto& it = std::ranges::find_if(m_events, [id, filter](auto& ev) { + return ev.event.ident == id && ev.event.filter == filter; + }); if (it != m_events.cend()) { m_events.erase(it); has_found = true; @@ -68,7 +71,7 @@ int EqueueInternal::WaitForEvents(SceKernelEvent* ev, int num, u32 micros) { if (ev->flags & SceKernelEvent::Flags::OneShot) { for (auto ev_id = 0u; ev_id < count; ++ev_id) { - RemoveEvent(ev->ident); + RemoveEvent(ev->ident, ev->filter); } } @@ -94,8 +97,11 @@ int EqueueInternal::GetTriggeredEvents(SceKernelEvent* ev, int num) { int count = 0; for (auto& event : m_events) { if (event.IsTriggered()) { + // Event should not trigger again + event.ResetTriggerState(); + if (event.event.flags & SceKernelEvent::Flags::Clear) { - event.Reset(); + event.Clear(); } ev[count++] = event.event; if (count == num) { @@ -334,7 +340,7 @@ int PS4_SYSV_ABI sceKernelDeleteUserEvent(SceKernelEqueue eq, int id) { return ORBIS_KERNEL_ERROR_EBADF; } - if (!eq->RemoveEvent(id)) { + if (!eq->RemoveEvent(id, SceKernelEvent::Filter::User)) { return ORBIS_KERNEL_ERROR_ENOENT; } return ORBIS_OK; @@ -344,6 +350,10 @@ s16 PS4_SYSV_ABI sceKernelGetEventFilter(const SceKernelEvent* ev) { return ev->filter; } +u64 PS4_SYSV_ABI sceKernelGetEventData(const SceKernelEvent* ev) { + return ev->data; +} + void RegisterEventQueue(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("D0OdFMjp46I", "libkernel", 1, "libkernel", 1, 1, sceKernelCreateEqueue); LIB_FUNCTION("jpFjmgAC5AE", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteEqueue); @@ -356,6 +366,7 @@ void RegisterEventQueue(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("LJDwdSNTnDg", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteUserEvent); LIB_FUNCTION("mJ7aghmgvfc", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventId); LIB_FUNCTION("23CPPI1tyBY", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventFilter); + LIB_FUNCTION("kwGyyjohI50", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventData); } } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/equeue.h b/src/core/libraries/kernel/equeue.h index 5a13bdecd..f8759137c 100644 --- a/src/core/libraries/kernel/equeue.h +++ b/src/core/libraries/kernel/equeue.h @@ -66,8 +66,11 @@ struct EqueueEvent { std::chrono::steady_clock::time_point time_added; std::unique_ptr timer; - void Reset() { + void ResetTriggerState() { is_triggered = false; + } + + void Clear() { event.fflags = 0; event.data = 0; } @@ -83,7 +86,7 @@ struct EqueueEvent { } bool operator==(const EqueueEvent& ev) const { - return ev.event.ident == event.ident; + return ev.event.ident == event.ident && ev.event.filter == event.filter; } private: @@ -99,7 +102,7 @@ public: } bool AddEvent(EqueueEvent& event); - bool RemoveEvent(u64 id); + bool RemoveEvent(u64 id, s16 filter); int WaitForEvents(SceKernelEvent* ev, int num, u32 micros); bool TriggerEvent(u64 ident, s16 filter, void* trigger_data); int GetTriggeredEvents(SceKernelEvent* ev, int num); @@ -122,6 +125,8 @@ private: using SceKernelUseconds = u32; using SceKernelEqueue = EqueueInternal*; +u64 PS4_SYSV_ABI sceKernelGetEventData(const SceKernelEvent* ev); + void RegisterEventQueue(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::Kernel From 333f35ef2565fd24d93194893e04bd21fab32b20 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 27 Dec 2024 11:04:49 -0800 Subject: [PATCH 272/549] audio: Implement cubeb audio out backend. (#1895) * audio: Implement cubeb audio out backend. * cubeb_audio: Add some additional safety checks. * cubeb_audio: Add debug logging callback. * audioout: Refactor backend ports into class. * pthread: Bump minimum stack size to fix cubeb crash. * cubeb_audio: Replace output yield loop with condvar. * common: Rename ring_buffer_base to RingBuffer. --- .gitmodules | 4 + CMakeLists.txt | 9 +- LICENSES/ISC.txt | 7 + externals/CMakeLists.txt | 10 + externals/cubeb | 1 + src/common/config.cpp | 17 + src/common/config.h | 2 + src/common/ringbuffer.h | 374 ++++++++++++++++++ src/core/libraries/audio/audioout.cpp | 59 ++- src/core/libraries/audio/audioout.h | 24 +- src/core/libraries/audio/audioout_backend.h | 33 +- src/core/libraries/audio/cubeb_audio.cpp | 158 ++++++++ src/core/libraries/audio/sdl_audio.cpp | 72 ++-- src/core/libraries/audio/sdl_audio.h | 18 - src/core/libraries/kernel/threads/pthread.cpp | 4 +- src/emulator.cpp | 2 +- src/qt_gui/settings_dialog.cpp | 6 + src/qt_gui/settings_dialog.ui | 23 ++ 18 files changed, 733 insertions(+), 90 deletions(-) create mode 100644 LICENSES/ISC.txt create mode 160000 externals/cubeb create mode 100644 src/common/ringbuffer.h create mode 100644 src/core/libraries/audio/cubeb_audio.cpp delete mode 100644 src/core/libraries/audio/sdl_audio.h diff --git a/.gitmodules b/.gitmodules index 3d0d21c5b..1c05ba6f3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -119,3 +119,7 @@ path = externals/MoltenVK/cereal url = https://github.com/USCiLab/cereal shallow = true +[submodule "externals/cubeb"] + path = externals/cubeb + url = https://github.com/mozilla/cubeb + shallow = true diff --git a/CMakeLists.txt b/CMakeLists.txt index 30a048278..5c2366601 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -127,6 +127,7 @@ find_package(xxHash 0.8.2 MODULE) find_package(ZLIB 1.3 MODULE) find_package(Zydis 5.0.0 CONFIG) find_package(pugixml 1.14 CONFIG) +find_package(cubeb CONFIG) if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR NOT MSVC) find_package(cryptopp 8.9.0 MODULE) @@ -198,9 +199,10 @@ set(AUDIO_LIB src/core/libraries/audio/audioin.cpp src/core/libraries/audio/audioin.h src/core/libraries/audio/audioout.cpp src/core/libraries/audio/audioout.h - src/core/libraries/audio/sdl_audio.cpp - src/core/libraries/audio/sdl_audio.h + src/core/libraries/audio/audioout_backend.h src/core/libraries/audio/audioout_error.h + src/core/libraries/audio/cubeb_audio.cpp + src/core/libraries/audio/sdl_audio.cpp src/core/libraries/ngs2/ngs2.cpp src/core/libraries/ngs2/ngs2.h ) @@ -493,6 +495,7 @@ set(COMMON src/common/logging/backend.cpp src/common/polyfill_thread.h src/common/rdtsc.cpp src/common/rdtsc.h + src/common/ringbuffer.h src/common/signal_context.h src/common/signal_context.cpp src/common/singleton.h @@ -884,7 +887,7 @@ endif() create_target_directory_groups(shadps4) target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG) -target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers) +target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers cubeb::cubeb) target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h") target_compile_definitions(Dear_ImGui PRIVATE IMGUI_USER_CONFIG="${PROJECT_SOURCE_DIR}/src/imgui/imgui_config.h") diff --git a/LICENSES/ISC.txt b/LICENSES/ISC.txt new file mode 100644 index 000000000..b9bcfa3a4 --- /dev/null +++ b/LICENSES/ISC.txt @@ -0,0 +1,7 @@ +ISC License + + + +Permission to use, copy, modify, and /or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 4350948b7..8bdf089f8 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -228,6 +228,16 @@ if (NOT TARGET stb::headers) add_library(stb::headers ALIAS stb) endif() +# cubeb +if (NOT TARGET cubeb::cubeb) + option(BUILD_TESTS "" OFF) + option(BUILD_TOOLS "" OFF) + option(BUNDLE_SPEEX "" ON) + option(USE_SANITIZERS "" OFF) + add_subdirectory(cubeb) + add_library(cubeb::cubeb ALIAS cubeb) +endif() + # Apple-only dependencies if (APPLE) # date diff --git a/externals/cubeb b/externals/cubeb new file mode 160000 index 000000000..9a9d034c5 --- /dev/null +++ b/externals/cubeb @@ -0,0 +1 @@ +Subproject commit 9a9d034c51859a045a34f201334f612c51e6c19d diff --git a/src/common/config.cpp b/src/common/config.cpp index deef0fa88..93627d8c9 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -67,6 +67,7 @@ static int cursorHideTimeout = 5; // 5 seconds (default) static bool separateupdatefolder = false; static bool compatibilityData = false; static bool checkCompatibilityOnStartup = false; +static std::string audioBackend = "cubeb"; // Gui std::vector settings_install_dirs = {}; @@ -239,6 +240,10 @@ bool getCheckCompatibilityOnStartup() { return checkCompatibilityOnStartup; } +std::string getAudioBackend() { + return audioBackend; +} + void setGpuId(s32 selectedGpuId) { gpuId = selectedGpuId; } @@ -371,6 +376,10 @@ void setCheckCompatibilityOnStartup(bool use) { checkCompatibilityOnStartup = use; } +void setAudioBackend(std::string backend) { + audioBackend = backend; +} + void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h) { main_window_geometry_x = x; main_window_geometry_y = y; @@ -611,6 +620,12 @@ void load(const std::filesystem::path& path) { vkCrashDiagnostic = toml::find_or(vk, "crashDiagnostic", false); } + if (data.contains("Audio")) { + const toml::value& audio = data.at("Audio"); + + audioBackend = toml::find_or(audio, "backend", "cubeb"); + } + if (data.contains("Debug")) { const toml::value& debug = data.at("Debug"); @@ -709,6 +724,7 @@ void save(const std::filesystem::path& path) { data["Vulkan"]["rdocEnable"] = rdocEnable; data["Vulkan"]["rdocMarkersEnable"] = vkMarkers; data["Vulkan"]["crashDiagnostic"] = vkCrashDiagnostic; + data["Audio"]["backend"] = audioBackend; data["Debug"]["DebugDump"] = isDebugDump; data["Debug"]["CollectShader"] = isShaderDebug; @@ -812,6 +828,7 @@ void setDefaultValues() { separateupdatefolder = false; compatibilityData = false; checkCompatibilityOnStartup = false; + audioBackend = "cubeb"; } } // namespace Config diff --git a/src/common/config.h b/src/common/config.h index 701aadb12..43ef5024b 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -24,6 +24,7 @@ bool getEnableDiscordRPC(); bool getSeparateUpdateEnabled(); bool getCompatibilityEnabled(); bool getCheckCompatibilityOnStartup(); +std::string getAudioBackend(); std::string getLogFilter(); std::string getLogType(); @@ -75,6 +76,7 @@ void setSeparateUpdateEnabled(bool use); void setGameInstallDirs(const std::vector& settings_install_dirs_config); void setCompatibilityEnabled(bool use); void setCheckCompatibilityOnStartup(bool use); +void setAudioBackend(std::string backend); void setCursorState(s16 cursorState); void setCursorHideTimeout(int newcursorHideTimeout); diff --git a/src/common/ringbuffer.h b/src/common/ringbuffer.h new file mode 100644 index 000000000..6a71c2888 --- /dev/null +++ b/src/common/ringbuffer.h @@ -0,0 +1,374 @@ +// SPDX-FileCopyrightText: Copyright 2016 Mozilla Foundation +// SPDX-License-Identifier: ISC + +#pragma once + +#include +#include +#include +#include +#include +#include "common/assert.h" + +/** + * Single producer single consumer lock-free and wait-free ring buffer. + * + * This data structure allows producing data from one thread, and consuming it + * on another thread, safely and without explicit synchronization. If used on + * two threads, this data structure uses atomics for thread safety. It is + * possible to disable the use of atomics at compile time and only use this data + * structure on one thread. + * + * The role for the producer and the consumer must be constant, i.e., the + * producer should always be on one thread and the consumer should always be on + * another thread. + * + * Some words about the inner workings of this class: + * - Capacity is fixed. Only one allocation is performed, in the constructor. + * When reading and writing, the return value of the method allows checking if + * the ring buffer is empty or full. + * - We always keep the read index at least one element ahead of the write + * index, so we can distinguish between an empty and a full ring buffer: an + * empty ring buffer is when the write index is at the same position as the + * read index. A full buffer is when the write index is exactly one position + * before the read index. + * - We synchronize updates to the read index after having read the data, and + * the write index after having written the data. This means that the each + * thread can only touch a portion of the buffer that is not touched by the + * other thread. + * - Callers are expected to provide buffers. When writing to the queue, + * elements are copied into the internal storage from the buffer passed in. + * When reading from the queue, the user is expected to provide a buffer. + * Because this is a ring buffer, data might not be contiguous in memory, + * providing an external buffer to copy into is an easy way to have linear + * data for further processing. + */ +template +class RingBuffer { +public: + /** + * Constructor for a ring buffer. + * + * This performs an allocation, but is the only allocation that will happen + * for the life time of a `RingBuffer`. + * + * @param capacity The maximum number of element this ring buffer will hold. + */ + RingBuffer(int capacity) + /* One more element to distinguish from empty and full buffer. */ + : capacity_(capacity + 1) { + ASSERT(storage_capacity() < std::numeric_limits::max() / 2 && + "buffer too large for the type of index used."); + ASSERT(capacity_ > 0); + + data_.reset(new T[storage_capacity()]); + /* If this queue is using atomics, initializing those members as the last + * action in the constructor acts as a full barrier, and allow capacity() to + * be thread-safe. */ + write_index_ = 0; + read_index_ = 0; + } + /** + * Push `count` zero or default constructed elements in the array. + * + * Only safely called on the producer thread. + * + * @param count The number of elements to enqueue. + * @return The number of element enqueued. + */ + int enqueue_default(int count) { + return enqueue(nullptr, count); + } + /** + * @brief Put an element in the queue + * + * Only safely called on the producer thread. + * + * @param element The element to put in the queue. + * + * @return 1 if the element was inserted, 0 otherwise. + */ + int enqueue(T& element) { + return enqueue(&element, 1); + } + /** + * Push `count` elements in the ring buffer. + * + * Only safely called on the producer thread. + * + * @param elements a pointer to a buffer containing at least `count` elements. + * If `elements` is nullptr, zero or default constructed elements are + * enqueued. + * @param count The number of elements to read from `elements` + * @return The number of elements successfully coped from `elements` and + * inserted into the ring buffer. + */ + int enqueue(T* elements, int count) { +#ifndef NDEBUG + assert_correct_thread(producer_id); +#endif + + int wr_idx = write_index_.load(std::memory_order_relaxed); + int rd_idx = read_index_.load(std::memory_order_acquire); + + if (full_internal(rd_idx, wr_idx)) { + return 0; + } + + int to_write = std::min(available_write_internal(rd_idx, wr_idx), count); + + /* First part, from the write index to the end of the array. */ + int first_part = std::min(storage_capacity() - wr_idx, to_write); + /* Second part, from the beginning of the array */ + int second_part = to_write - first_part; + + if (elements) { + Copy(data_.get() + wr_idx, elements, first_part); + Copy(data_.get(), elements + first_part, second_part); + } else { + ConstructDefault(data_.get() + wr_idx, first_part); + ConstructDefault(data_.get(), second_part); + } + + write_index_.store(increment_index(wr_idx, to_write), std::memory_order_release); + + return to_write; + } + /** + * Retrieve at most `count` elements from the ring buffer, and copy them to + * `elements`, if non-null. + * + * Only safely called on the consumer side. + * + * @param elements A pointer to a buffer with space for at least `count` + * elements. If `elements` is `nullptr`, `count` element will be discarded. + * @param count The maximum number of elements to dequeue. + * @return The number of elements written to `elements`. + */ + int dequeue(T* elements, int count) { +#ifndef NDEBUG + assert_correct_thread(consumer_id); +#endif + + int rd_idx = read_index_.load(std::memory_order_relaxed); + int wr_idx = write_index_.load(std::memory_order_acquire); + + if (empty_internal(rd_idx, wr_idx)) { + return 0; + } + + int to_read = std::min(available_read_internal(rd_idx, wr_idx), count); + + int first_part = std::min(storage_capacity() - rd_idx, to_read); + int second_part = to_read - first_part; + + if (elements) { + Copy(elements, data_.get() + rd_idx, first_part); + Copy(elements + first_part, data_.get(), second_part); + } + + read_index_.store(increment_index(rd_idx, to_read), std::memory_order_release); + + return to_read; + } + /** + * Get the number of available element for consuming. + * + * Only safely called on the consumer thread. + * + * @return The number of available elements for reading. + */ + int available_read() const { +#ifndef NDEBUG + assert_correct_thread(consumer_id); +#endif + return available_read_internal(read_index_.load(std::memory_order_relaxed), + write_index_.load(std::memory_order_acquire)); + } + /** + * Get the number of available elements for consuming. + * + * Only safely called on the producer thread. + * + * @return The number of empty slots in the buffer, available for writing. + */ + int available_write() const { +#ifndef NDEBUG + assert_correct_thread(producer_id); +#endif + return available_write_internal(read_index_.load(std::memory_order_acquire), + write_index_.load(std::memory_order_relaxed)); + } + /** + * Get the total capacity, for this ring buffer. + * + * Can be called safely on any thread. + * + * @return The maximum capacity of this ring buffer. + */ + int capacity() const { + return storage_capacity() - 1; + } + /** + * Reset the consumer and producer thread identifier, in case the thread are + * being changed. This has to be externally synchronized. This is no-op when + * asserts are disabled. + */ + void reset_thread_ids() { +#ifndef NDEBUG + consumer_id = producer_id = std::thread::id(); +#endif + } + +private: + /** Return true if the ring buffer is empty. + * + * @param read_index the read index to consider + * @param write_index the write index to consider + * @return true if the ring buffer is empty, false otherwise. + **/ + bool empty_internal(int read_index, int write_index) const { + return write_index == read_index; + } + /** Return true if the ring buffer is full. + * + * This happens if the write index is exactly one element behind the read + * index. + * + * @param read_index the read index to consider + * @param write_index the write index to consider + * @return true if the ring buffer is full, false otherwise. + **/ + bool full_internal(int read_index, int write_index) const { + return (write_index + 1) % storage_capacity() == read_index; + } + /** + * Return the size of the storage. It is one more than the number of elements + * that can be stored in the buffer. + * + * @return the number of elements that can be stored in the buffer. + */ + int storage_capacity() const { + return capacity_; + } + /** + * Returns the number of elements available for reading. + * + * @return the number of available elements for reading. + */ + int available_read_internal(int read_index, int write_index) const { + if (write_index >= read_index) { + return write_index - read_index; + } else { + return write_index + storage_capacity() - read_index; + } + } + /** + * Returns the number of empty elements, available for writing. + * + * @return the number of elements that can be written into the array. + */ + int available_write_internal(int read_index, int write_index) const { + /* We substract one element here to always keep at least one sample + * free in the buffer, to distinguish between full and empty array. */ + int rv = read_index - write_index - 1; + if (write_index >= read_index) { + rv += storage_capacity(); + } + return rv; + } + /** + * Increments an index, wrapping it around the storage. + * + * @param index a reference to the index to increment. + * @param increment the number by which `index` is incremented. + * @return the new index. + */ + int increment_index(int index, int increment) const { + ASSERT(increment >= 0); + return (index + increment) % storage_capacity(); + } + /** + * @brief This allows checking that enqueue (resp. dequeue) are always called + * by the right thread. + * + * @param id the id of the thread that has called the calling method first. + */ +#ifndef NDEBUG + static void assert_correct_thread(std::thread::id& id) { + if (id == std::thread::id()) { + id = std::this_thread::get_id(); + return; + } + ASSERT(id == std::this_thread::get_id()); + } +#endif + /** Similar to memcpy, but accounts for the size of an element. */ + template + void PodCopy(CopyT* destination, const CopyT* source, size_t count) { + static_assert(std::is_trivial::value, "Requires trivial type"); + ASSERT(destination && source); + memcpy(destination, source, count * sizeof(CopyT)); + } + /** Similar to a memset to zero, but accounts for the size of an element. */ + template + void PodZero(ZeroT* destination, size_t count) { + static_assert(std::is_trivial::value, "Requires trivial type"); + ASSERT(destination); + memset(destination, 0, count * sizeof(ZeroT)); + } + template + void Copy(CopyT* destination, const CopyT* source, size_t count, Trait) { + for (size_t i = 0; i < count; i++) { + destination[i] = source[i]; + } + } + template + void Copy(CopyT* destination, const CopyT* source, size_t count, std::true_type) { + PodCopy(destination, source, count); + } + /** + * This allows copying a number of elements from a `source` pointer to a + * `destination` pointer, using `memcpy` if it is safe to do so, or a loop that + * calls the constructors and destructors otherwise. + */ + template + void Copy(CopyT* destination, const T* source, size_t count) { + ASSERT(destination && source); + Copy(destination, source, count, typename std::is_trivial::type()); + } + template + void ConstructDefault(ConstructT* destination, size_t count, Trait) { + for (size_t i = 0; i < count; i++) { + destination[i] = ConstructT(); + } + } + template + void ConstructDefault(ConstructT* destination, size_t count, std::true_type) { + PodZero(destination, count); + } + /** + * This allows zeroing (using memset) or default-constructing a number of + * elements calling the constructors and destructors if necessary. + */ + template + void ConstructDefault(ConstructT* destination, size_t count) { + ASSERT(destination); + ConstructDefault(destination, count, typename std::is_arithmetic::type()); + } + /** Index at which the oldest element is at, in samples. */ + std::atomic read_index_; + /** Index at which to write new elements. `write_index` is always at + * least one element ahead of `read_index_`. */ + std::atomic write_index_; + /** Maximum number of elements that can be stored in the ring buffer. */ + const int capacity_; + /** Data storage */ + std::unique_ptr data_; +#ifndef NDEBUG + /** The id of the only thread that is allowed to read from the queue. */ + mutable std::thread::id consumer_id; + /** The id of the only thread that is allowed to write from the queue. */ + mutable std::thread::id producer_id; +#endif +}; diff --git a/src/core/libraries/audio/audioout.cpp b/src/core/libraries/audio/audioout.cpp index db43ee928..89ea1d3f5 100644 --- a/src/core/libraries/audio/audioout.cpp +++ b/src/core/libraries/audio/audioout.cpp @@ -7,26 +7,15 @@ #include #include "common/assert.h" +#include "common/config.h" #include "common/logging/log.h" #include "core/libraries/audio/audioout.h" +#include "core/libraries/audio/audioout_backend.h" #include "core/libraries/audio/audioout_error.h" -#include "core/libraries/audio/sdl_audio.h" #include "core/libraries/libs.h" namespace Libraries::AudioOut { -struct PortOut { - void* impl; - u32 samples_num; - u32 freq; - OrbisAudioOutParamFormat format; - OrbisAudioOutPort type; - int channels_num; - bool is_float; - std::array volume; - u8 sample_size; - bool is_open; -}; std::shared_mutex ports_mutex; std::array ports_out{}; @@ -104,7 +93,7 @@ static bool IsFormatFloat(const OrbisAudioOutParamFormat format) { } } -static int GetFormatNumChannels(const OrbisAudioOutParamFormat format) { +static u8 GetFormatNumChannels(const OrbisAudioOutParamFormat format) { switch (format) { case OrbisAudioOutParamFormat::S16Mono: case OrbisAudioOutParamFormat::FloatMono: @@ -187,13 +176,11 @@ int PS4_SYSV_ABI sceAudioOutClose(s32 handle) { std::scoped_lock lock(ports_mutex); auto& port = ports_out.at(handle - 1); - if (!port.is_open) { + if (!port.impl) { return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; } - audio->Close(port.impl); port.impl = nullptr; - port.is_open = false; return ORBIS_OK; } @@ -264,7 +251,7 @@ int PS4_SYSV_ABI sceAudioOutGetPortState(s32 handle, OrbisAudioOutPortState* sta std::scoped_lock lock(ports_mutex); const auto& port = ports_out.at(handle - 1); - if (!port.is_open) { + if (!port.impl) { return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; } @@ -324,7 +311,16 @@ int PS4_SYSV_ABI sceAudioOutInit() { if (audio != nullptr) { return ORBIS_AUDIO_OUT_ERROR_ALREADY_INIT; } - audio = std::make_unique(); + const auto backend = Config::getAudioBackend(); + if (backend == "cubeb") { + audio = std::make_unique(); + } else if (backend == "sdl") { + audio = std::make_unique(); + } else { + // Cubeb as a default fallback. + LOG_ERROR(Lib_AudioOut, "Invalid audio backend '{}', defaulting to cubeb.", backend); + audio = std::make_unique(); + } return ORBIS_OK; } @@ -399,23 +395,25 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id, } std::scoped_lock lock{ports_mutex}; - const auto port = std::ranges::find(ports_out, false, &PortOut::is_open); + const auto port = + std::ranges::find_if(ports_out, [&](const PortOut& p) { return p.impl == nullptr; }); if (port == ports_out.end()) { LOG_ERROR(Lib_AudioOut, "Audio ports are full"); return ORBIS_AUDIO_OUT_ERROR_PORT_FULL; } - port->is_open = true; port->type = port_type; - port->samples_num = length; - port->freq = sample_rate; port->format = format; port->is_float = IsFormatFloat(format); - port->channels_num = GetFormatNumChannels(format); port->sample_size = GetFormatSampleSize(format); + port->channels_num = GetFormatNumChannels(format); + port->samples_num = length; + port->frame_size = port->sample_size * port->channels_num; + port->buffer_size = port->frame_size * port->samples_num; + port->freq = sample_rate; port->volume.fill(SCE_AUDIO_OUT_VOLUME_0DB); + port->impl = audio->Open(*port); - port->impl = audio->Open(port->is_float, port->channels_num, port->freq); return std::distance(ports_out.begin(), port) + 1; } @@ -424,7 +422,7 @@ int PS4_SYSV_ABI sceAudioOutOpenEx() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceAudioOutOutput(s32 handle, const void* ptr) { +s32 PS4_SYSV_ABI sceAudioOutOutput(s32 handle, void* ptr) { if (handle < 1 || handle > SCE_AUDIO_OUT_NUM_PORTS) { return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; } @@ -434,12 +432,11 @@ s32 PS4_SYSV_ABI sceAudioOutOutput(s32 handle, const void* ptr) { } auto& port = ports_out.at(handle - 1); - if (!port.is_open) { + if (!port.impl) { return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; } - const size_t data_size = port.samples_num * port.sample_size * port.channels_num; - audio->Output(port.impl, ptr, data_size); + port.impl->Output(ptr, port.buffer_size); return ORBIS_OK; } @@ -548,7 +545,7 @@ s32 PS4_SYSV_ABI sceAudioOutSetVolume(s32 handle, s32 flag, s32* vol) { std::scoped_lock lock(ports_mutex); auto& port = ports_out.at(handle - 1); - if (!port.is_open) { + if (!port.impl) { return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; } @@ -579,7 +576,7 @@ s32 PS4_SYSV_ABI sceAudioOutSetVolume(s32 handle, s32 flag, s32* vol) { } } - audio->SetVolume(port.impl, port.volume); + port.impl->SetVolume(port.volume); return ORBIS_OK; } diff --git a/src/core/libraries/audio/audioout.h b/src/core/libraries/audio/audioout.h index c66a0e9f5..58c77db99 100644 --- a/src/core/libraries/audio/audioout.h +++ b/src/core/libraries/audio/audioout.h @@ -3,12 +3,15 @@ #pragma once -#include "common/bit_field.h" +#include +#include "common/bit_field.h" #include "core/libraries/system/userservice.h" namespace Libraries::AudioOut { +class PortBackend; + // Main up to 8 ports, BGM 1 port, voice up to 4 ports, // personal up to 4 ports, padspk up to 5 ports, aux 1 port constexpr int SCE_AUDIO_OUT_NUM_PORTS = 22; @@ -43,7 +46,7 @@ union OrbisAudioOutParamExtendedInformation { struct OrbisAudioOutOutputParam { s32 handle; - const void* ptr; + void* ptr; }; struct OrbisAudioOutPortState { @@ -56,6 +59,21 @@ struct OrbisAudioOutPortState { u64 reserved64[2]; }; +struct PortOut { + std::unique_ptr impl{}; + + OrbisAudioOutPort type; + OrbisAudioOutParamFormat format; + bool is_float; + u8 sample_size; + u8 channels_num; + u32 samples_num; + u32 frame_size; + u32 buffer_size; + u32 freq; + std::array volume; +}; + int PS4_SYSV_ABI sceAudioOutDeviceIdOpen(); int PS4_SYSV_ABI sceAudioDeviceControlGet(); int PS4_SYSV_ABI sceAudioDeviceControlSet(); @@ -94,7 +112,7 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id, OrbisAudioOutPort port_type, s32 index, u32 length, u32 sample_rate, OrbisAudioOutParamExtendedInformation param_type); int PS4_SYSV_ABI sceAudioOutOpenEx(); -s32 PS4_SYSV_ABI sceAudioOutOutput(s32 handle, const void* ptr); +s32 PS4_SYSV_ABI sceAudioOutOutput(s32 handle, void* ptr); s32 PS4_SYSV_ABI sceAudioOutOutputs(OrbisAudioOutOutputParam* param, u32 num); int PS4_SYSV_ABI sceAudioOutPtClose(); int PS4_SYSV_ABI sceAudioOutPtGetLastOutputTime(); diff --git a/src/core/libraries/audio/audioout_backend.h b/src/core/libraries/audio/audioout_backend.h index 238ef0201..ecc4cf7c6 100644 --- a/src/core/libraries/audio/audioout_backend.h +++ b/src/core/libraries/audio/audioout_backend.h @@ -3,17 +3,42 @@ #pragma once +typedef struct cubeb cubeb; + namespace Libraries::AudioOut { +struct PortOut; + +class PortBackend { +public: + virtual ~PortBackend() = default; + + virtual void Output(void* ptr, size_t size) = 0; + virtual void SetVolume(const std::array& ch_volumes) = 0; +}; + class AudioOutBackend { public: AudioOutBackend() = default; virtual ~AudioOutBackend() = default; - virtual void* Open(bool is_float, int num_channels, u32 sample_rate) = 0; - virtual void Close(void* impl) = 0; - virtual void Output(void* impl, const void* ptr, size_t size) = 0; - virtual void SetVolume(void* impl, std::array ch_volumes) = 0; + virtual std::unique_ptr Open(PortOut& port) = 0; +}; + +class CubebAudioOut final : public AudioOutBackend { +public: + CubebAudioOut(); + ~CubebAudioOut() override; + + std::unique_ptr Open(PortOut& port) override; + +private: + cubeb* ctx = nullptr; +}; + +class SDLAudioOut final : public AudioOutBackend { +public: + std::unique_ptr Open(PortOut& port) override; }; } // namespace Libraries::AudioOut diff --git a/src/core/libraries/audio/cubeb_audio.cpp b/src/core/libraries/audio/cubeb_audio.cpp new file mode 100644 index 000000000..ca0a4c3b6 --- /dev/null +++ b/src/core/libraries/audio/cubeb_audio.cpp @@ -0,0 +1,158 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include + +#include "common/assert.h" +#include "common/ringbuffer.h" +#include "core/libraries/audio/audioout.h" +#include "core/libraries/audio/audioout_backend.h" + +namespace Libraries::AudioOut { + +constexpr int AUDIO_STREAM_BUFFER_THRESHOLD = 65536; // Define constant for buffer threshold + +class CubebPortBackend : public PortBackend { +public: + CubebPortBackend(cubeb* ctx, const PortOut& port) + : frame_size(port.frame_size), buffer(static_cast(port.buffer_size) * 4) { + if (!ctx) { + return; + } + const auto get_channel_layout = [&port] -> cubeb_channel_layout { + switch (port.channels_num) { + case 1: + return CUBEB_LAYOUT_MONO; + case 2: + return CUBEB_LAYOUT_STEREO; + case 8: + return CUBEB_LAYOUT_3F4_LFE; + default: + UNREACHABLE(); + } + }; + cubeb_stream_params stream_params = { + .format = port.is_float ? CUBEB_SAMPLE_FLOAT32LE : CUBEB_SAMPLE_S16LE, + .rate = port.freq, + .channels = port.channels_num, + .layout = get_channel_layout(), + .prefs = CUBEB_STREAM_PREF_NONE, + }; + u32 latency_frames = 512; + if (const auto ret = cubeb_get_min_latency(ctx, &stream_params, &latency_frames); + ret != CUBEB_OK) { + LOG_WARNING(Lib_AudioOut, + "Could not get minimum cubeb audio latency, falling back to default: {}", + ret); + } + char stream_name[64]; + snprintf(stream_name, sizeof(stream_name), "shadPS4 stream %p", this); + if (const auto ret = cubeb_stream_init(ctx, &stream, stream_name, nullptr, nullptr, nullptr, + &stream_params, latency_frames, &DataCallback, + &StateCallback, this); + ret != CUBEB_OK) { + LOG_ERROR(Lib_AudioOut, "Failed to create cubeb stream: {}", ret); + return; + } + if (const auto ret = cubeb_stream_start(stream); ret != CUBEB_OK) { + LOG_ERROR(Lib_AudioOut, "Failed to start cubeb stream: {}", ret); + return; + } + } + + ~CubebPortBackend() override { + if (!stream) { + return; + } + if (const auto ret = cubeb_stream_stop(stream); ret != CUBEB_OK) { + LOG_WARNING(Lib_AudioOut, "Failed to stop cubeb stream: {}", ret); + } + cubeb_stream_destroy(stream); + stream = nullptr; + } + + void Output(void* ptr, size_t size) override { + auto* data = static_cast(ptr); + + std::unique_lock lock{buffer_mutex}; + buffer_cv.wait(lock, [&] { return buffer.available_write() >= size; }); + buffer.enqueue(data, static_cast(size)); + } + + void SetVolume(const std::array& ch_volumes) override { + if (!stream) { + return; + } + // Cubeb does not have per-channel volumes, for now just take the maximum of the channels. + const auto vol = *std::ranges::max_element(ch_volumes); + if (const auto ret = + cubeb_stream_set_volume(stream, static_cast(vol) / SCE_AUDIO_OUT_VOLUME_0DB); + ret != CUBEB_OK) { + LOG_WARNING(Lib_AudioOut, "Failed to change cubeb stream volume: {}", ret); + } + } + +private: + static long DataCallback(cubeb_stream* stream, void* user_data, const void* in, void* out, + long num_frames) { + auto* stream_data = static_cast(user_data); + const auto out_data = static_cast(out); + const auto requested_size = static_cast(num_frames * stream_data->frame_size); + + std::unique_lock lock{stream_data->buffer_mutex}; + const auto dequeued_size = stream_data->buffer.dequeue(out_data, requested_size); + lock.unlock(); + stream_data->buffer_cv.notify_one(); + + if (dequeued_size < requested_size) { + // Need to fill remaining space with silence. + std::memset(out_data + dequeued_size, 0, requested_size - dequeued_size); + } + return num_frames; + } + + static void StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state) { + switch (state) { + case CUBEB_STATE_STARTED: + LOG_INFO(Lib_AudioOut, "Cubeb stream started"); + break; + case CUBEB_STATE_STOPPED: + LOG_INFO(Lib_AudioOut, "Cubeb stream stopped"); + break; + case CUBEB_STATE_DRAINED: + LOG_INFO(Lib_AudioOut, "Cubeb stream drained"); + break; + case CUBEB_STATE_ERROR: + LOG_ERROR(Lib_AudioOut, "Cubeb stream encountered an error"); + break; + } + } + + size_t frame_size; + RingBuffer buffer; + std::mutex buffer_mutex; + std::condition_variable buffer_cv; + cubeb_stream* stream{}; +}; + +CubebAudioOut::CubebAudioOut() { + if (const auto ret = cubeb_init(&ctx, "shadPS4", nullptr); ret != CUBEB_OK) { + LOG_CRITICAL(Lib_AudioOut, "Failed to create cubeb context: {}", ret); + } +} + +CubebAudioOut::~CubebAudioOut() { + if (!ctx) { + return; + } + cubeb_destroy(ctx); + ctx = nullptr; +} + +std::unique_ptr CubebAudioOut::Open(PortOut& port) { + return std::make_unique(ctx, port); +} + +} // namespace Libraries::AudioOut diff --git a/src/core/libraries/audio/sdl_audio.cpp b/src/core/libraries/audio/sdl_audio.cpp index ce385ad9c..7d7a7cee5 100644 --- a/src/core/libraries/audio/sdl_audio.cpp +++ b/src/core/libraries/audio/sdl_audio.cpp @@ -1,44 +1,60 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include + #include #include -#include -#include "common/assert.h" -#include "core/libraries/audio/sdl_audio.h" +#include "common/logging/log.h" +#include "core/libraries/audio/audioout.h" +#include "core/libraries/audio/audioout_backend.h" namespace Libraries::AudioOut { constexpr int AUDIO_STREAM_BUFFER_THRESHOLD = 65536; // Define constant for buffer threshold -void* SDLAudioOut::Open(bool is_float, int num_channels, u32 sample_rate) { - SDL_AudioSpec fmt; - SDL_zero(fmt); - fmt.format = is_float ? SDL_AUDIO_F32 : SDL_AUDIO_S16; - fmt.channels = num_channels; - fmt.freq = sample_rate; - - auto* stream = - SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &fmt, nullptr, nullptr); - SDL_ResumeAudioStreamDevice(stream); - return stream; -} - -void SDLAudioOut::Close(void* impl) { - SDL_DestroyAudioStream(static_cast(impl)); -} - -void SDLAudioOut::Output(void* impl, const void* ptr, size_t size) { - auto* stream = static_cast(impl); - SDL_PutAudioStreamData(stream, ptr, size); - while (SDL_GetAudioStreamAvailable(stream) > AUDIO_STREAM_BUFFER_THRESHOLD) { - SDL_Delay(0); +class SDLPortBackend : public PortBackend { +public: + explicit SDLPortBackend(const PortOut& port) { + const SDL_AudioSpec fmt = { + .format = port.is_float ? SDL_AUDIO_F32 : SDL_AUDIO_S16, + .channels = port.channels_num, + .freq = static_cast(port.freq), + }; + stream = + SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &fmt, nullptr, nullptr); + if (stream == nullptr) { + LOG_ERROR(Lib_AudioOut, "Failed to create SDL audio stream: {}", SDL_GetError()); + } + SDL_ResumeAudioStreamDevice(stream); } -} -void SDLAudioOut::SetVolume(void* impl, std::array ch_volumes) { - // Not yet implemented + ~SDLPortBackend() override { + if (stream) { + SDL_DestroyAudioStream(stream); + stream = nullptr; + } + } + + void Output(void* ptr, size_t size) override { + SDL_PutAudioStreamData(stream, ptr, static_cast(size)); + while (SDL_GetAudioStreamAvailable(stream) > AUDIO_STREAM_BUFFER_THRESHOLD) { + // Yield to allow the stream to drain. + std::this_thread::yield(); + } + } + + void SetVolume(const std::array& ch_volumes) override { + // TODO: Not yet implemented + } + +private: + SDL_AudioStream* stream; +}; + +std::unique_ptr SDLAudioOut::Open(PortOut& port) { + return std::make_unique(port); } } // namespace Libraries::AudioOut diff --git a/src/core/libraries/audio/sdl_audio.h b/src/core/libraries/audio/sdl_audio.h deleted file mode 100644 index d55f2f6e3..000000000 --- a/src/core/libraries/audio/sdl_audio.h +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/libraries/audio/audioout_backend.h" - -namespace Libraries::AudioOut { - -class SDLAudioOut final : public AudioOutBackend { -public: - void* Open(bool is_float, int num_channels, u32 sample_rate) override; - void Close(void* impl) override; - void Output(void* impl, const void* ptr, size_t size) override; - void SetVolume(void* impl, std::array ch_volumes) override; -}; - -} // namespace Libraries::AudioOut diff --git a/src/core/libraries/kernel/threads/pthread.cpp b/src/core/libraries/kernel/threads/pthread.cpp index 610e61238..761d13346 100644 --- a/src/core/libraries/kernel/threads/pthread.cpp +++ b/src/core/libraries/kernel/threads/pthread.cpp @@ -244,8 +244,8 @@ int PS4_SYSV_ABI posix_pthread_create_name_np(PthreadT* thread, const PthreadAtt new_thread->tid = ++TidCounter; if (new_thread->attr.stackaddr_attr == 0) { - /* Enforce minimum stack size of 64 KB */ - static constexpr size_t MinimumStack = 64_KB; + /* Enforce minimum stack size of 128 KB */ + static constexpr size_t MinimumStack = 128_KB; auto& stacksize = new_thread->attr.stacksize_attr; stacksize = std::max(stacksize, MinimumStack); } diff --git a/src/emulator.cpp b/src/emulator.cpp index 11a9f42f2..10d17a2db 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -322,7 +322,7 @@ void Emulator::LoadSystemModules(const std::filesystem::path& file, std::string LOG_INFO(Loader, "No HLE available for {} module", module_name); } } - if (std::filesystem::exists(sys_module_path / game_serial)) { + if (!game_serial.empty() && std::filesystem::exists(sys_module_path / game_serial)) { for (const auto& entry : std::filesystem::directory_iterator(sys_module_path / game_serial)) { LOG_INFO(Loader, "Loading {} from game serial file {}", entry.path().string(), diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index f9095d8a0..df802901a 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -212,6 +212,7 @@ SettingsDialog::SettingsDialog(std::span physical_devices, ui->enableCompatibilityCheckBox->installEventFilter(this); ui->checkCompatibilityOnStartupCheckBox->installEventFilter(this); ui->updateCompatibilityButton->installEventFilter(this); + ui->audioBackendComboBox->installEventFilter(this); // Input ui->hideCursorGroupBox->installEventFilter(this); @@ -306,6 +307,8 @@ void SettingsDialog::LoadValuesFromConfig() { toml::find_or(data, "General", "compatibilityEnabled", false)); ui->checkCompatibilityOnStartupCheckBox->setChecked( toml::find_or(data, "General", "checkCompatibilityOnStartup", false)); + ui->audioBackendComboBox->setCurrentText( + QString::fromStdString(toml::find_or(data, "Audio", "backend", "cubeb"))); #ifdef ENABLE_UPDATER ui->updateCheckBox->setChecked(toml::find_or(data, "General", "autoUpdate", false)); @@ -429,6 +432,8 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) { text = tr("checkCompatibilityOnStartupCheckBox"); } else if (elementName == "updateCompatibilityButton") { text = tr("updateCompatibilityButton"); + } else if (elementName == "audioBackendGroupBox") { + text = tr("audioBackendGroupBox"); } // Input @@ -544,6 +549,7 @@ void SettingsDialog::UpdateSettings() { Config::setUpdateChannel(ui->updateComboBox->currentText().toStdString()); Config::setCompatibilityEnabled(ui->enableCompatibilityCheckBox->isChecked()); Config::setCheckCompatibilityOnStartup(ui->checkCompatibilityOnStartupCheckBox->isChecked()); + Config::setAudioBackend(ui->audioBackendComboBox->currentText().toStdString()); #ifdef ENABLE_DISCORD_RPC auto* rpc = Common::Singleton::Instance(); diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index 4d69ee5b1..0af133e3e 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -270,6 +270,29 @@ + + + + Audio Backend + + + + + + + cubeb + + + + + sdl + + + + + + + From 115aa61d040e8ed0f6d68624d3bec0b5f2e945c7 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 27 Dec 2024 12:10:16 -0800 Subject: [PATCH 273/549] build: Fix MoltenVK bundling copy. (#1928) --- CMakeLists.txt | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c2366601..736cf2803 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -904,11 +904,18 @@ endif() if (APPLE) if (ENABLE_QT_GUI) # Include MoltenVK in the app bundle, along with an ICD file so it can be found by the system Vulkan loader if used for loading layers. - set(MVK_DYLIB ${CMAKE_CURRENT_BINARY_DIR}/externals/MoltenVK/libMoltenVK.dylib) set(MVK_ICD ${CMAKE_CURRENT_SOURCE_DIR}/externals/MoltenVK/MoltenVK_icd.json) - target_sources(shadps4 PRIVATE ${MVK_DYLIB} ${MVK_ICD}) - set_source_files_properties(${MVK_DYLIB} PROPERTIES MACOSX_PACKAGE_LOCATION Frameworks) + target_sources(shadps4 PRIVATE ${MVK_ICD}) set_source_files_properties(${MVK_ICD} PROPERTIES MACOSX_PACKAGE_LOCATION Resources/vulkan/icd.d) + + set(MVK_DYLIB_SRC ${CMAKE_CURRENT_BINARY_DIR}/externals/MoltenVK/libMoltenVK.dylib) + set(MVK_DYLIB_DST ${CMAKE_CURRENT_BINARY_DIR}/shadps4.app/Contents/Frameworks/libMoltenVK.dylib) + add_custom_command( + OUTPUT ${MVK_DYLIB_DST} + DEPENDS ${MVK_DYLIB_SRC} + COMMAND cmake -E copy ${MVK_DYLIB_SRC} ${MVK_DYLIB_DST}) + add_custom_target(CopyMoltenVK DEPENDS ${MVK_DYLIB_DST}) + add_dependencies(shadps4 CopyMoltenVK) set_property(TARGET shadps4 APPEND PROPERTY BUILD_RPATH "@executable_path/../Frameworks") else() # For non-bundled SDL build, just do a normal library link. From f4c1d827d5b6bb47c93205d8c239b9bff61afbb9 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 27 Dec 2024 12:20:30 -0800 Subject: [PATCH 274/549] build: Do not generate files in source. (#1929) --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 736cf2803..43e8d7cab 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -106,7 +106,7 @@ git_describe(GIT_DESC --always --long --dirty) git_branch_name(GIT_BRANCH) string(TIMESTAMP BUILD_DATE "%Y-%m-%d %H:%M:%S") -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/common/scm_rev.cpp.in" "${CMAKE_CURRENT_SOURCE_DIR}/src/common/scm_rev.cpp" @ONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/common/scm_rev.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/src/common/scm_rev.cpp" @ONLY) find_package(Boost 1.84.0 CONFIG) find_package(FFmpeg 5.1.2 MODULE) @@ -520,7 +520,7 @@ set(COMMON src/common/logging/backend.cpp src/common/number_utils.cpp src/common/memory_patcher.h src/common/memory_patcher.cpp - src/common/scm_rev.cpp + ${CMAKE_CURRENT_BINARY_DIR}/src/common/scm_rev.cpp src/common/scm_rev.h ) From abe85fd3e005976c64048972d34dea31dd6f7f08 Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Fri, 27 Dec 2024 17:23:11 -0300 Subject: [PATCH 275/549] GUI: Fix TR Units - Size and Time - more... (#1927) * GUI: TR Units - Size and Time Added translation for these units: B, KB, MB, GB, TB h, m, s Added missing text: Games: - text in the lower left corner Compatibility... Update database View report Submit a report Fixed the size of the settings menu * Audio Backend * Fixed the size of the settings menu --- src/qt_gui/game_list_frame.cpp | 6 +-- src/qt_gui/game_list_utils.h | 5 ++- src/qt_gui/main_window.cpp | 4 +- src/qt_gui/settings_dialog.ui | 4 +- src/qt_gui/translations/ar.ts | 73 +++++++++++++++++++++++++++++++ src/qt_gui/translations/da_DK.ts | 73 +++++++++++++++++++++++++++++++ src/qt_gui/translations/de.ts | 73 +++++++++++++++++++++++++++++++ src/qt_gui/translations/el.ts | 73 +++++++++++++++++++++++++++++++ src/qt_gui/translations/en.ts | 73 +++++++++++++++++++++++++++++++ src/qt_gui/translations/es_ES.ts | 73 +++++++++++++++++++++++++++++++ src/qt_gui/translations/fa_IR.ts | 73 +++++++++++++++++++++++++++++++ src/qt_gui/translations/fi.ts | 73 +++++++++++++++++++++++++++++++ src/qt_gui/translations/fr.ts | 73 +++++++++++++++++++++++++++++++ src/qt_gui/translations/hu_HU.ts | 73 +++++++++++++++++++++++++++++++ src/qt_gui/translations/id.ts | 73 +++++++++++++++++++++++++++++++ src/qt_gui/translations/it.ts | 75 +++++++++++++++++++++++++++++++- src/qt_gui/translations/ja_JP.ts | 73 +++++++++++++++++++++++++++++++ src/qt_gui/translations/ko_KR.ts | 73 +++++++++++++++++++++++++++++++ src/qt_gui/translations/lt_LT.ts | 73 +++++++++++++++++++++++++++++++ src/qt_gui/translations/nb.ts | 75 +++++++++++++++++++++++++++++++- src/qt_gui/translations/nl.ts | 73 +++++++++++++++++++++++++++++++ src/qt_gui/translations/pl_PL.ts | 73 +++++++++++++++++++++++++++++++ src/qt_gui/translations/pt_BR.ts | 73 +++++++++++++++++++++++++++++++ src/qt_gui/translations/ro_RO.ts | 73 +++++++++++++++++++++++++++++++ src/qt_gui/translations/ru_RU.ts | 53 ++++++++++++++++++++++ src/qt_gui/translations/sq.ts | 73 +++++++++++++++++++++++++++++++ src/qt_gui/translations/tr_TR.ts | 73 +++++++++++++++++++++++++++++++ src/qt_gui/translations/uk_UA.ts | 73 +++++++++++++++++++++++++++++++ src/qt_gui/translations/vi_VN.ts | 73 +++++++++++++++++++++++++++++++ src/qt_gui/translations/zh_CN.ts | 73 +++++++++++++++++++++++++++++++ src/qt_gui/translations/zh_TW.ts | 73 +++++++++++++++++++++++++++++++ 31 files changed, 1963 insertions(+), 11 deletions(-) diff --git a/src/qt_gui/game_list_frame.cpp b/src/qt_gui/game_list_frame.cpp index bba3c2891..b1cd07216 100644 --- a/src/qt_gui/game_list_frame.cpp +++ b/src/qt_gui/game_list_frame.cpp @@ -133,16 +133,16 @@ void GameListFrame::PopulateGameList() { QString formattedPlayTime; if (hours > 0) { - formattedPlayTime += QString("%1h ").arg(hours); + formattedPlayTime += QString("%1").arg(hours) + tr("h"); } if (minutes > 0) { - formattedPlayTime += QString("%1m ").arg(minutes); + formattedPlayTime += QString("%1").arg(minutes) + tr("m"); } formattedPlayTime = formattedPlayTime.trimmed(); m_game_info->m_games[i].play_time = playTime.toStdString(); if (formattedPlayTime.isEmpty()) { - SetTableItem(i, 8, QString("%1s").arg(seconds)); + SetTableItem(i, 8, QString("%1").arg(seconds) + tr("s")); } else { SetTableItem(i, 8, formattedPlayTime); } diff --git a/src/qt_gui/game_list_utils.h b/src/qt_gui/game_list_utils.h index 16c0307c8..ab9233886 100644 --- a/src/qt_gui/game_list_utils.h +++ b/src/qt_gui/game_list_utils.h @@ -30,10 +30,11 @@ struct GameInfo { CompatibilityEntry compatibility = CompatibilityEntry{CompatibilityStatus::Unknown}; }; -class GameListUtils { +class GameListUtils : public QObject { + Q_OBJECT public: static QString FormatSize(qint64 size) { - static const QStringList suffixes = {"B", "KB", "MB", "GB", "TB"}; + static const QStringList suffixes = {tr("B"), tr("KB"), tr("MB"), tr("GB"), tr("TB")}; int suffixIndex = 0; double gameSize = static_cast(size); diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 4a9b4d94a..724e008ae 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -75,8 +75,8 @@ bool MainWindow::Init() { this->setStatusBar(statusBar.data()); // Update status bar int numGames = m_game_info->m_games.size(); - QString statusMessage = - "Games: " + QString::number(numGames) + " (" + QString::number(duration.count()) + "ms)"; + QString statusMessage = tr("Games: ") + QString::number(numGames) + " (" + + QString::number(duration.count()) + "ms)"; statusBar->showMessage(statusMessage); #ifdef ENABLE_DISCORD_RPC diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index 0af133e3e..c18b70497 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -11,8 +11,8 @@ 0 0 - 900 - 660 + 950 + 780 diff --git a/src/qt_gui/translations/ar.ts b/src/qt_gui/translations/ar.ts index cd71e083c..1f65db04a 100644 --- a/src/qt_gui/translations/ar.ts +++ b/src/qt_gui/translations/ar.ts @@ -175,6 +175,26 @@ Delete DLC Delete DLC + + + Compatibility... + Compatibility... + + + + Update database + Update database + + + + View report + View report + + + + Submit a report + Submit a report + Shortcut creation @@ -552,6 +572,11 @@ Hide Cursor Idle Timeout مهلة إخفاء المؤشر عند الخمول + + + s + s + Controller @@ -707,6 +732,11 @@ Volume الصوت + + + Audio Backend + Audio Backend + MainWindow @@ -1414,6 +1444,21 @@ Never Played Never Played + + + h + h + + + + m + m + + + + s + s + Compatibility is untested @@ -1573,4 +1618,32 @@ فشل في إنشاء ملف سكريبت التحديث + + GameListUtils + + + B + B + + + + KB + KB + + + + MB + MB + + + + GB + GB + + + + TB + TB + + \ No newline at end of file diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts index 677789d49..943e2d092 100644 --- a/src/qt_gui/translations/da_DK.ts +++ b/src/qt_gui/translations/da_DK.ts @@ -175,6 +175,26 @@ Delete DLC Delete DLC + + + Compatibility... + Compatibility... + + + + Update database + Update database + + + + View report + View report + + + + Submit a report + Submit a report + Shortcut creation @@ -552,6 +572,11 @@ Hide Cursor Idle Timeout Timeout for skjul markør ved inaktivitet + + + s + s + Controller @@ -707,6 +732,11 @@ Volume Lydstyrke + + + Audio Backend + Audio Backend + MainWindow @@ -1414,6 +1444,21 @@ Never Played Never Played + + + h + h + + + + m + m + + + + s + s + Compatibility is untested @@ -1573,4 +1618,32 @@ Kunne ikke oprette opdateringsscriptfilen + + GameListUtils + + + B + B + + + + KB + KB + + + + MB + MB + + + + GB + GB + + + + TB + TB + + \ No newline at end of file diff --git a/src/qt_gui/translations/de.ts b/src/qt_gui/translations/de.ts index 64770f6fe..cbbef8215 100644 --- a/src/qt_gui/translations/de.ts +++ b/src/qt_gui/translations/de.ts @@ -175,6 +175,26 @@ Delete DLC Delete DLC + + + Compatibility... + Compatibility... + + + + Update database + Update database + + + + View report + View report + + + + Submit a report + Submit a report + Shortcut creation @@ -552,6 +572,11 @@ Hide Cursor Idle Timeout Inaktivitätszeitüberschreitung zum Ausblenden des Cursors + + + s + s + Controller @@ -707,6 +732,11 @@ Volume Lautstärke + + + Audio Backend + Audio Backend + MainWindow @@ -1414,6 +1444,21 @@ Never Played Never Played + + + h + h + + + + m + m + + + + s + s + Compatibility is untested @@ -1573,4 +1618,32 @@ Fehler beim Erstellen der Aktualisierungs-Skriptdatei + + GameListUtils + + + B + B + + + + KB + KB + + + + MB + MB + + + + GB + GB + + + + TB + TB + + \ No newline at end of file diff --git a/src/qt_gui/translations/el.ts b/src/qt_gui/translations/el.ts index 05faa7bc9..8737f5216 100644 --- a/src/qt_gui/translations/el.ts +++ b/src/qt_gui/translations/el.ts @@ -175,6 +175,26 @@ Delete DLC Delete DLC + + + Compatibility... + Compatibility... + + + + Update database + Update database + + + + View report + View report + + + + Submit a report + Submit a report + Shortcut creation @@ -552,6 +572,11 @@ Hide Cursor Idle Timeout Χρόνος αδράνειας απόκρυψης δείκτη + + + s + s + Controller @@ -707,6 +732,11 @@ Volume ένταση + + + Audio Backend + Audio Backend + MainWindow @@ -1414,6 +1444,21 @@ Never Played Never Played + + + h + h + + + + m + m + + + + s + s + Compatibility is untested @@ -1573,4 +1618,32 @@ Αποτυχία δημιουργίας του αρχείου σεναρίου ενημέρωσης + + GameListUtils + + + B + B + + + + KB + KB + + + + MB + MB + + + + GB + GB + + + + TB + TB + + \ No newline at end of file diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index b616b889a..692aa527e 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -175,6 +175,26 @@ Delete DLC Delete DLC + + + Compatibility... + Compatibility... + + + + Update database + Update database + + + + View report + View report + + + + Submit a report + Submit a report + Shortcut creation @@ -552,6 +572,11 @@ Hide Cursor Idle Timeout Hide Cursor Idle Timeout + + + s + s + Controller @@ -707,6 +732,11 @@ Volume Volume + + + Audio Backend + Audio Backend + MainWindow @@ -1414,6 +1444,21 @@ Never Played Never Played + + + h + h + + + + m + m + + + + s + s + Compatibility is untested @@ -1573,4 +1618,32 @@ Failed to create the update script file + + GameListUtils + + + B + B + + + + KB + KB + + + + MB + MB + + + + GB + GB + + + + TB + TB + + \ No newline at end of file diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index 9b8b38129..70be2253d 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -175,6 +175,26 @@ Delete DLC Delete DLC + + + Compatibility... + Compatibility... + + + + Update database + Update database + + + + View report + View report + + + + Submit a report + Submit a report + Shortcut creation @@ -552,6 +572,11 @@ Hide Cursor Idle Timeout Tiempo de espera para ocultar cursor inactivo + + + s + s + Controller @@ -707,6 +732,11 @@ Volume Volumen + + + Audio Backend + Audio Backend + MainWindow @@ -1414,6 +1444,21 @@ Never Played Never Played + + + h + h + + + + m + m + + + + s + s + Compatibility is untested @@ -1573,4 +1618,32 @@ No se pudo crear el archivo del script de actualización + + GameListUtils + + + B + B + + + + KB + KB + + + + MB + MB + + + + GB + GB + + + + TB + TB + + \ No newline at end of file diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index 66ec0b4c0..54187cf9b 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -175,6 +175,26 @@ Delete DLC حذف محتوای اضافی (DLC) + + + Compatibility... + Compatibility... + + + + Update database + Update database + + + + View report + View report + + + + Submit a report + Submit a report + Shortcut creation @@ -552,6 +572,11 @@ Hide Cursor Idle Timeout مخفی کردن زمان توقف مکان نما + + + s + s + Controller @@ -707,6 +732,11 @@ Volume صدا + + + Audio Backend + Audio Backend + MainWindow @@ -1414,6 +1444,21 @@ Never Played هرگز بازی نشده + + + h + h + + + + m + m + + + + s + s + Compatibility is untested @@ -1573,4 +1618,32 @@ فایل اسکریپت به روز رسانی ایجاد نشد + + GameListUtils + + + B + B + + + + KB + KB + + + + MB + MB + + + + GB + GB + + + + TB + TB + + diff --git a/src/qt_gui/translations/fi.ts b/src/qt_gui/translations/fi.ts index 67ea079aa..bdc1eb703 100644 --- a/src/qt_gui/translations/fi.ts +++ b/src/qt_gui/translations/fi.ts @@ -175,6 +175,26 @@ Delete DLC Delete DLC + + + Compatibility... + Compatibility... + + + + Update database + Update database + + + + View report + View report + + + + Submit a report + Submit a report + Shortcut creation @@ -552,6 +572,11 @@ Hide Cursor Idle Timeout Inaktiivisuuden aikaraja kursorin piilottamiselle + + + s + s + Controller @@ -707,6 +732,11 @@ Volume Äänenvoimakkuus + + + Audio Backend + Audio Backend + MainWindow @@ -1414,6 +1444,21 @@ Never Played Never Played + + + h + h + + + + m + m + + + + s + s + Compatibility is untested @@ -1573,4 +1618,32 @@ Päivitysskripttitiedoston luominen epäonnistui + + GameListUtils + + + B + B + + + + KB + KB + + + + MB + MB + + + + GB + GB + + + + TB + TB + + \ No newline at end of file diff --git a/src/qt_gui/translations/fr.ts b/src/qt_gui/translations/fr.ts index c092580a8..19b0f9358 100644 --- a/src/qt_gui/translations/fr.ts +++ b/src/qt_gui/translations/fr.ts @@ -175,6 +175,26 @@ Delete DLC Supprimer DLC + + + Compatibility... + Compatibility... + + + + Update database + Update database + + + + View report + View report + + + + Submit a report + Submit a report + Shortcut creation @@ -552,6 +572,11 @@ Hide Cursor Idle Timeout Délai d'inactivité pour masquer le curseur + + + s + s + Controller @@ -707,6 +732,11 @@ Volume Volume + + + Audio Backend + Audio Backend + MainWindow @@ -1414,6 +1444,21 @@ Never Played Jamais joué + + + h + h + + + + m + m + + + + s + s + Compatibility is untested @@ -1573,4 +1618,32 @@ Échec de la création du fichier de script de mise à jour + + GameListUtils + + + B + B + + + + KB + KB + + + + MB + MB + + + + GB + GB + + + + TB + TB + + \ No newline at end of file diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index 7e60001f6..bc337f2cd 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -175,6 +175,26 @@ Delete DLC DLC-k törlése + + + Compatibility... + Compatibility... + + + + Update database + Update database + + + + View report + View report + + + + Submit a report + Submit a report + Shortcut creation @@ -552,6 +572,11 @@ Hide Cursor Idle Timeout Kurzor inaktivitási időtúllépés + + + s + s + Controller @@ -707,6 +732,11 @@ Volume Hangerő + + + Audio Backend + Audio Backend + MainWindow @@ -1414,6 +1444,21 @@ Never Played Never Played + + + h + h + + + + m + m + + + + s + s + Compatibility is untested @@ -1573,4 +1618,32 @@ A frissítési szkript fájl létrehozása nem sikerült + + GameListUtils + + + B + B + + + + KB + KB + + + + MB + MB + + + + GB + GB + + + + TB + TB + + \ No newline at end of file diff --git a/src/qt_gui/translations/id.ts b/src/qt_gui/translations/id.ts index 31f377341..7a0bf5d05 100644 --- a/src/qt_gui/translations/id.ts +++ b/src/qt_gui/translations/id.ts @@ -175,6 +175,26 @@ Delete DLC Delete DLC + + + Compatibility... + Compatibility... + + + + Update database + Update database + + + + View report + View report + + + + Submit a report + Submit a report + Shortcut creation @@ -552,6 +572,11 @@ Hide Cursor Idle Timeout Batas waktu sembunyikan kursor tidak aktif + + + s + s + Controller @@ -707,6 +732,11 @@ Volume Volume + + + Audio Backend + Audio Backend + MainWindow @@ -1414,6 +1444,21 @@ Never Played Never Played + + + h + h + + + + m + m + + + + s + s + Compatibility is untested @@ -1573,4 +1618,32 @@ Gagal membuat file skrip pembaruan + + GameListUtils + + + B + B + + + + KB + KB + + + + MB + MB + + + + GB + GB + + + + TB + TB + + \ No newline at end of file diff --git a/src/qt_gui/translations/it.ts b/src/qt_gui/translations/it.ts index 8dc0817f1..1391fbc55 100644 --- a/src/qt_gui/translations/it.ts +++ b/src/qt_gui/translations/it.ts @@ -175,6 +175,26 @@ Delete DLC Elimina DLC + + + Compatibility... + Compatibility... + + + + Update database + Update database + + + + View report + View report + + + + Submit a report + Submit a report + Shortcut creation @@ -552,6 +572,11 @@ Hide Cursor Idle Timeout Timeout inattività per nascondere il cursore + + + s + s + Controller @@ -707,6 +732,11 @@ Volume Volume + + + Audio Backend + Audio Backend + MainWindow @@ -1414,6 +1444,21 @@ Never Played Mai Giocato + + + h + h + + + + m + m + + + + s + s + Compatibility is untested @@ -1573,4 +1618,32 @@ Impossibile creare il file di script di aggiornamento - + + GameListUtils + + + B + B + + + + KB + KB + + + + MB + MB + + + + GB + GB + + + + TB + TB + + + \ No newline at end of file diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index 43fb37c2c..58f213e03 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -175,6 +175,26 @@ Delete DLC Delete DLC + + + Compatibility... + Compatibility... + + + + Update database + Update database + + + + View report + View report + + + + Submit a report + Submit a report + Shortcut creation @@ -552,6 +572,11 @@ Hide Cursor Idle Timeout カーソル非アクティブタイムアウト + + + s + s + Controller @@ -707,6 +732,11 @@ Volume 音量 + + + Audio Backend + Audio Backend + MainWindow @@ -1414,6 +1444,21 @@ Never Played Never Played + + + h + h + + + + m + m + + + + s + s + Compatibility is untested @@ -1573,4 +1618,32 @@ アップデートスクリプトファイルの作成に失敗しました + + GameListUtils + + + B + B + + + + KB + KB + + + + MB + MB + + + + GB + GB + + + + TB + TB + + \ No newline at end of file diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts index ffaa8404f..75a1b53cf 100644 --- a/src/qt_gui/translations/ko_KR.ts +++ b/src/qt_gui/translations/ko_KR.ts @@ -175,6 +175,26 @@ Delete DLC Delete DLC + + + Compatibility... + Compatibility... + + + + Update database + Update database + + + + View report + View report + + + + Submit a report + Submit a report + Shortcut creation @@ -552,6 +572,11 @@ Hide Cursor Idle Timeout Hide Cursor Idle Timeout + + + s + s + Controller @@ -707,6 +732,11 @@ Volume 음량 + + + Audio Backend + Audio Backend + MainWindow @@ -1414,6 +1444,21 @@ Never Played Never Played + + + h + h + + + + m + m + + + + s + s + Compatibility is untested @@ -1573,4 +1618,32 @@ Failed to create the update script file + + GameListUtils + + + B + B + + + + KB + KB + + + + MB + MB + + + + GB + GB + + + + TB + TB + + \ No newline at end of file diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts index 5cf4d0e6b..092521fdf 100644 --- a/src/qt_gui/translations/lt_LT.ts +++ b/src/qt_gui/translations/lt_LT.ts @@ -175,6 +175,26 @@ Delete DLC Delete DLC + + + Compatibility... + Compatibility... + + + + Update database + Update database + + + + View report + View report + + + + Submit a report + Submit a report + Shortcut creation @@ -552,6 +572,11 @@ Hide Cursor Idle Timeout Žymeklio paslėpimo neveikimo laikas + + + s + s + Controller @@ -707,6 +732,11 @@ Volume Garsumas + + + Audio Backend + Audio Backend + MainWindow @@ -1414,6 +1444,21 @@ Never Played Never Played + + + h + h + + + + m + m + + + + s + s + Compatibility is untested @@ -1573,4 +1618,32 @@ Nepavyko sukurti atnaujinimo scenarijaus failo + + GameListUtils + + + B + B + + + + KB + KB + + + + MB + MB + + + + GB + GB + + + + TB + TB + + \ No newline at end of file diff --git a/src/qt_gui/translations/nb.ts b/src/qt_gui/translations/nb.ts index 4ebe2f0f6..cc41573db 100644 --- a/src/qt_gui/translations/nb.ts +++ b/src/qt_gui/translations/nb.ts @@ -175,6 +175,26 @@ Delete DLC Slett DLC + + + Compatibility... + Compatibility... + + + + Update database + Update database + + + + View report + View report + + + + Submit a report + Submit a report + Shortcut creation @@ -552,6 +572,11 @@ Hide Cursor Idle Timeout Skjul musepeker ved inaktivitet + + + s + s + Controller @@ -707,6 +732,11 @@ Volume Volum + + + Audio Backend + Audio Backend + MainWindow @@ -1414,6 +1444,21 @@ Never Played Aldri spilt + + + h + h + + + + m + m + + + + s + s + Compatibility is untested @@ -1573,4 +1618,32 @@ Kunne ikke opprette oppdateringsskriptfilen - + + GameListUtils + + + B + B + + + + KB + KB + + + + MB + MB + + + + GB + GB + + + + TB + TB + + + \ No newline at end of file diff --git a/src/qt_gui/translations/nl.ts b/src/qt_gui/translations/nl.ts index eb49c83ab..5cd4a4224 100644 --- a/src/qt_gui/translations/nl.ts +++ b/src/qt_gui/translations/nl.ts @@ -175,6 +175,26 @@ Delete DLC Delete DLC + + + Compatibility... + Compatibility... + + + + Update database + Update database + + + + View report + View report + + + + Submit a report + Submit a report + Shortcut creation @@ -552,6 +572,11 @@ Hide Cursor Idle Timeout Inactiviteit timeout voor het verbergen van de cursor + + + s + s + Controller @@ -707,6 +732,11 @@ Volume Volume + + + Audio Backend + Audio Backend + MainWindow @@ -1414,6 +1444,21 @@ Never Played Never Played + + + h + h + + + + m + m + + + + s + s + Compatibility is untested @@ -1573,4 +1618,32 @@ Kon het update-scriptbestand niet maken + + GameListUtils + + + B + B + + + + KB + KB + + + + MB + MB + + + + GB + GB + + + + TB + TB + + \ No newline at end of file diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index cb9e7987d..b85393bb0 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -175,6 +175,26 @@ Delete DLC Delete DLC + + + Compatibility... + Compatibility... + + + + Update database + Update database + + + + View report + View report + + + + Submit a report + Submit a report + Shortcut creation @@ -552,6 +572,11 @@ Hide Cursor Idle Timeout Czas oczekiwania na ukrycie kursora przy bezczynności + + + s + s + Controller @@ -707,6 +732,11 @@ Volume Głośność + + + Audio Backend + Audio Backend + MainWindow @@ -1414,6 +1444,21 @@ Never Played Never Played + + + h + h + + + + m + m + + + + s + s + Compatibility is untested @@ -1573,4 +1618,32 @@ Nie udało się utworzyć pliku skryptu aktualizacji + + GameListUtils + + + B + B + + + + KB + KB + + + + MB + MB + + + + GB + GB + + + + TB + TB + + \ No newline at end of file diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index a668c61d1..8ab8db093 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -175,6 +175,26 @@ Delete DLC Deletar DLC + + + Compatibility... + Compatibilidade... + + + + Update database + Atualizar banco de dados + + + + View report + Ver status + + + + Submit a report + Enviar status + Shortcut creation @@ -552,6 +572,11 @@ Hide Cursor Idle Timeout Tempo de Inatividade para Ocultar Cursor + + + s + s + Controller @@ -707,6 +732,11 @@ Volume Volume + + + Audio Backend + Backend de Áudio + MainWindow @@ -1414,6 +1444,21 @@ Never Played Nunca jogado + + + h + h + + + + m + m + + + + s + s + Compatibility is untested @@ -1573,4 +1618,32 @@ Falha ao criar o arquivo de script de atualização + + GameListUtils + + + B + B + + + + KB + KB + + + + MB + MB + + + + GB + GB + + + + TB + TB + + \ No newline at end of file diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index ae7e2efcb..00547d6ba 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -175,6 +175,26 @@ Delete DLC Delete DLC + + + Compatibility... + Compatibility... + + + + Update database + Update database + + + + View report + View report + + + + Submit a report + Submit a report + Shortcut creation @@ -552,6 +572,11 @@ Hide Cursor Idle Timeout Timeout pentru ascunderea cursorului inactiv + + + s + s + Controller @@ -707,6 +732,11 @@ Volume Volum + + + Audio Backend + Audio Backend + MainWindow @@ -1414,6 +1444,21 @@ Never Played Never Played + + + h + h + + + + m + m + + + + s + s + Compatibility is untested @@ -1573,4 +1618,32 @@ Nu s-a putut crea fișierul script de actualizare + + GameListUtils + + + B + B + + + + KB + KB + + + + MB + MB + + + + GB + GB + + + + TB + TB + + \ No newline at end of file diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index 8112f9dfd..505a05a3e 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -572,6 +572,11 @@ Hide Cursor Idle Timeout Время скрытия курсора при бездействии + + + s + сек + Controller @@ -727,6 +732,11 @@ Volume Громкость + + + Audio Backend + Звуковая Подсистема + MainWindow @@ -1434,6 +1444,21 @@ Never Played Вы не играли + + + h + ч + + + + m + м + + + + s + с + Compatibility is untested @@ -1593,4 +1618,32 @@ Не удалось создать файл скрипта обновления + + GameListUtils + + + B + Б + + + + KB + КБ + + + + MB + МБ + + + + GB + ГБ + + + + TB + ТБ + + \ No newline at end of file diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index c3280e0ea..0c318f4f7 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -175,6 +175,26 @@ Delete DLC Fshi DLC-në + + + Compatibility... + Compatibility... + + + + Update database + Update database + + + + View report + View report + + + + Submit a report + Submit a report + Shortcut creation @@ -552,6 +572,11 @@ Hide Cursor Idle Timeout Koha për fshehjen e kursorit joaktiv + + + s + s + Controller @@ -707,6 +732,11 @@ Volume Vëllimi i zërit + + + Audio Backend + Audio Backend + MainWindow @@ -1414,6 +1444,21 @@ Never Played Nuk është luajtur kurrë + + + h + h + + + + m + m + + + + s + s + Compatibility is untested @@ -1573,4 +1618,32 @@ Krijimi i skedarit skript të përditësimit dështoi + + GameListUtils + + + B + B + + + + KB + KB + + + + MB + MB + + + + GB + GB + + + + TB + TB + + diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index 4d644ecfe..2845af462 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -175,6 +175,26 @@ Delete DLC Delete DLC + + + Compatibility... + Compatibility... + + + + Update database + Update database + + + + View report + View report + + + + Submit a report + Submit a report + Shortcut creation @@ -552,6 +572,11 @@ Hide Cursor Idle Timeout İmleç İçin Hareketsizlik Zaman Aşımı + + + s + s + Controller @@ -707,6 +732,11 @@ Volume Ses seviyesi + + + Audio Backend + Audio Backend + MainWindow @@ -1414,6 +1444,21 @@ Never Played Never Played + + + h + h + + + + m + m + + + + s + s + Compatibility is untested @@ -1573,4 +1618,32 @@ Güncelleme betiği dosyası oluşturulamadı + + GameListUtils + + + B + B + + + + KB + KB + + + + MB + MB + + + + GB + GB + + + + TB + TB + + \ No newline at end of file diff --git a/src/qt_gui/translations/uk_UA.ts b/src/qt_gui/translations/uk_UA.ts index 66cbf2bd9..8abfca435 100644 --- a/src/qt_gui/translations/uk_UA.ts +++ b/src/qt_gui/translations/uk_UA.ts @@ -175,6 +175,26 @@ Delete DLC Видалити DLC + + + Compatibility... + Compatibility... + + + + Update database + Update database + + + + View report + View report + + + + Submit a report + Submit a report + Shortcut creation @@ -552,6 +572,11 @@ Hide Cursor Idle Timeout Тайм-аут приховування курсора при бездіяльності + + + s + s + Controller @@ -707,6 +732,11 @@ Volume Гучність + + + Audio Backend + Audio Backend + MainWindow @@ -1414,6 +1444,21 @@ Never Played Never Played + + + h + h + + + + m + m + + + + s + s + Compatibility is untested @@ -1573,4 +1618,32 @@ Не вдалося створити файл скрипта оновлення + + GameListUtils + + + B + B + + + + KB + KB + + + + MB + MB + + + + GB + GB + + + + TB + TB + + \ No newline at end of file diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index 9441d1697..7d0e9a2cd 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -175,6 +175,26 @@ Delete DLC Delete DLC + + + Compatibility... + Compatibility... + + + + Update database + Update database + + + + View report + View report + + + + Submit a report + Submit a report + Shortcut creation @@ -552,6 +572,11 @@ Hide Cursor Idle Timeout Thời gian chờ ẩn con trỏ + + + s + s + Controller @@ -707,6 +732,11 @@ Volume Âm lượng + + + Audio Backend + Audio Backend + MainWindow @@ -1414,6 +1444,21 @@ Never Played Never Played + + + h + h + + + + m + m + + + + s + s + Compatibility is untested @@ -1573,4 +1618,32 @@ Không thể tạo tệp kịch bản cập nhật + + GameListUtils + + + B + B + + + + KB + KB + + + + MB + MB + + + + GB + GB + + + + TB + TB + + \ No newline at end of file diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index 92071c5b2..4ceb91315 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -175,6 +175,26 @@ Delete DLC 删除DLC + + + Compatibility... + Compatibility... + + + + Update database + Update database + + + + View report + View report + + + + Submit a report + Submit a report + Shortcut creation @@ -552,6 +572,11 @@ Hide Cursor Idle Timeout 光标空闲超时隐藏 + + + s + s + Controller @@ -707,6 +732,11 @@ Volume 音量 + + + Audio Backend + Audio Backend + MainWindow @@ -1414,6 +1444,21 @@ Never Played Never Played + + + h + h + + + + m + m + + + + s + s + Compatibility is untested @@ -1573,4 +1618,32 @@ 无法创建更新脚本文件 + + GameListUtils + + + B + B + + + + KB + KB + + + + MB + MB + + + + GB + GB + + + + TB + TB + + \ No newline at end of file diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index be7cd69ec..3d27267b6 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -175,6 +175,26 @@ Delete DLC Delete DLC + + + Compatibility... + Compatibility... + + + + Update database + Update database + + + + View report + View report + + + + Submit a report + Submit a report + Shortcut creation @@ -552,6 +572,11 @@ Hide Cursor Idle Timeout 游標空閒超時隱藏 + + + s + s + Controller @@ -707,6 +732,11 @@ Volume 音量 + + + Audio Backend + Audio Backend + MainWindow @@ -1414,6 +1444,21 @@ Never Played Never Played + + + h + h + + + + m + m + + + + s + s + Compatibility is untested @@ -1573,4 +1618,32 @@ 無法創建更新腳本文件 + + GameListUtils + + + B + B + + + + KB + KB + + + + MB + MB + + + + GB + GB + + + + TB + TB + + \ No newline at end of file From 3218c36b22335e69d558a724627f9876ac70f7b7 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Fri, 27 Dec 2024 23:03:03 +0200 Subject: [PATCH 276/549] knack fixes by niko (#1933) --- src/core/libraries/ajm/ajm_batch.h | 1 + src/shader_recompiler/backend/spirv/emit_spirv.cpp | 1 + src/shader_recompiler/backend/spirv/spirv_emit_context.cpp | 1 + 3 files changed, 3 insertions(+) diff --git a/src/core/libraries/ajm/ajm_batch.h b/src/core/libraries/ajm/ajm_batch.h index 65110ee73..3c586b773 100644 --- a/src/core/libraries/ajm/ajm_batch.h +++ b/src/core/libraries/ajm/ajm_batch.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index 0ce9eea7c..900d40472 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -24,6 +24,7 @@ static constexpr spv::ExecutionMode GetInputPrimitiveType(AmdGpu::PrimitiveType case AmdGpu::PrimitiveType::PointList: return spv::ExecutionMode::InputPoints; case AmdGpu::PrimitiveType::LineList: + case AmdGpu::PrimitiveType::LineStrip: return spv::ExecutionMode::InputLines; case AmdGpu::PrimitiveType::TriangleList: case AmdGpu::PrimitiveType::TriangleStrip: diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index d8bafccd9..281c487af 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -43,6 +43,7 @@ static constexpr u32 NumVertices(AmdGpu::PrimitiveType type) { case AmdGpu::PrimitiveType::PointList: return 1u; case AmdGpu::PrimitiveType::LineList: + case AmdGpu::PrimitiveType::LineStrip: return 2u; case AmdGpu::PrimitiveType::TriangleList: case AmdGpu::PrimitiveType::TriangleStrip: From 49ffb7b120d02286db02a9608b5f6e3f0b149f15 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 27 Dec 2024 14:07:16 -0800 Subject: [PATCH 277/549] sdl_audio: Implement SetVolume and add more error checking. (#1935) --- src/core/libraries/audio/cubeb_audio.cpp | 7 +++++- src/core/libraries/audio/sdl_audio.cpp | 30 ++++++++++++++++++------ 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/core/libraries/audio/cubeb_audio.cpp b/src/core/libraries/audio/cubeb_audio.cpp index ca0a4c3b6..e1195558a 100644 --- a/src/core/libraries/audio/cubeb_audio.cpp +++ b/src/core/libraries/audio/cubeb_audio.cpp @@ -5,7 +5,7 @@ #include #include -#include "common/assert.h" +#include "common/logging/log.h" #include "common/ringbuffer.h" #include "core/libraries/audio/audioout.h" #include "core/libraries/audio/audioout_backend.h" @@ -58,6 +58,8 @@ public: } if (const auto ret = cubeb_stream_start(stream); ret != CUBEB_OK) { LOG_ERROR(Lib_AudioOut, "Failed to start cubeb stream: {}", ret); + cubeb_stream_destroy(stream); + stream = nullptr; return; } } @@ -74,6 +76,9 @@ public: } void Output(void* ptr, size_t size) override { + if (!stream) { + return; + } auto* data = static_cast(ptr); std::unique_lock lock{buffer_mutex}; diff --git a/src/core/libraries/audio/sdl_audio.cpp b/src/core/libraries/audio/sdl_audio.cpp index 7d7a7cee5..598941ba7 100644 --- a/src/core/libraries/audio/sdl_audio.cpp +++ b/src/core/libraries/audio/sdl_audio.cpp @@ -2,9 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include - #include -#include #include "common/logging/log.h" #include "core/libraries/audio/audioout.h" @@ -26,18 +24,28 @@ public: SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &fmt, nullptr, nullptr); if (stream == nullptr) { LOG_ERROR(Lib_AudioOut, "Failed to create SDL audio stream: {}", SDL_GetError()); + return; + } + if (!SDL_ResumeAudioStreamDevice(stream)) { + LOG_ERROR(Lib_AudioOut, "Failed to resume SDL audio stream: {}", SDL_GetError()); + SDL_DestroyAudioStream(stream); + stream = nullptr; + return; } - SDL_ResumeAudioStreamDevice(stream); } ~SDLPortBackend() override { - if (stream) { - SDL_DestroyAudioStream(stream); - stream = nullptr; + if (!stream) { + return; } + SDL_DestroyAudioStream(stream); + stream = nullptr; } void Output(void* ptr, size_t size) override { + if (!stream) { + return; + } SDL_PutAudioStreamData(stream, ptr, static_cast(size)); while (SDL_GetAudioStreamAvailable(stream) > AUDIO_STREAM_BUFFER_THRESHOLD) { // Yield to allow the stream to drain. @@ -46,7 +54,15 @@ public: } void SetVolume(const std::array& ch_volumes) override { - // TODO: Not yet implemented + if (!stream) { + return; + } + // SDL does not have per-channel volumes, for now just take the maximum of the channels. + const auto vol = *std::ranges::max_element(ch_volumes); + if (!SDL_SetAudioStreamGain(stream, static_cast(vol) / SCE_AUDIO_OUT_VOLUME_0DB)) { + LOG_WARNING(Lib_AudioOut, "Failed to change SDL audio stream volume: {}", + SDL_GetError()); + } } private: From 122fe22a320c4a17529d709d863f842a5c9ab107 Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Fri, 27 Dec 2024 18:42:41 -0600 Subject: [PATCH 278/549] Implement IMAGE_GATHER4 and IMAGE_GATHER4_O (#1939) * Implement IMAGE_GATHER4_O Used by The Last of Us Remastered. * Fix type on IMAGE_GATHER4_C_LZ Had a different set of types compared to the other IMAGE_GATHER4 ops. * IMAGE_GATHER4 --- src/shader_recompiler/frontend/format.cpp | 10 +++++----- .../frontend/translate/vector_memory.cpp | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/shader_recompiler/frontend/format.cpp b/src/shader_recompiler/frontend/format.cpp index 4f0922e2e..9677be3e5 100644 --- a/src/shader_recompiler/frontend/format.cpp +++ b/src/shader_recompiler/frontend/format.cpp @@ -3565,8 +3565,8 @@ constexpr std::array InstructionFormatMIMG = {{ {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Float32, ScalarType::Float32}, // 64 = IMAGE_GATHER4 - {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Undefined, - ScalarType::Undefined}, + {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, + ScalarType::Float32}, // 65 = IMAGE_GATHER4_CL {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Undefined, ScalarType::Undefined}, @@ -3603,10 +3603,10 @@ constexpr std::array InstructionFormatMIMG = {{ ScalarType::Undefined}, // 79 = IMAGE_GATHER4_C_LZ {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, - ScalarType::Uint32}, + ScalarType::Float32}, // 80 = IMAGE_GATHER4_O - {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Undefined, - ScalarType::Undefined}, + {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, + ScalarType::Float32}, // 81 = IMAGE_GATHER4_CL_O {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Undefined, ScalarType::Undefined}, diff --git a/src/shader_recompiler/frontend/translate/vector_memory.cpp b/src/shader_recompiler/frontend/translate/vector_memory.cpp index 48cb79610..7c3db9551 100644 --- a/src/shader_recompiler/frontend/translate/vector_memory.cpp +++ b/src/shader_recompiler/frontend/translate/vector_memory.cpp @@ -144,8 +144,10 @@ void Translator::EmitVectorMemory(const GcnInst& inst) { return IMAGE_SAMPLE(inst); // Image gather operations + case Opcode::IMAGE_GATHER4: case Opcode::IMAGE_GATHER4_LZ: case Opcode::IMAGE_GATHER4_C: + case Opcode::IMAGE_GATHER4_O: case Opcode::IMAGE_GATHER4_C_O: case Opcode::IMAGE_GATHER4_C_LZ: case Opcode::IMAGE_GATHER4_LZ_O: From 04933ac863d4777fdbe21810e9cf5e050381aa37 Mon Sep 17 00:00:00 2001 From: psucien Date: Sat, 28 Dec 2024 11:44:11 +0100 Subject: [PATCH 279/549] hot-fix: handle ASC ring wrap --- src/core/libraries/gnmdriver/gnmdriver.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index 566f8ce1f..91a1329e5 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -513,10 +513,14 @@ void PS4_SYSV_ABI sceGnmDingDong(u32 gnm_vqid, u32 next_offs_dw) { auto vqid = gnm_vqid - 1; auto& asc_queue = liverpool->asc_queues[{vqid}]; - const auto& offs_dw = asc_next_offs_dw[vqid]; + auto& offs_dw = asc_next_offs_dw[vqid]; - if (next_offs_dw < offs_dw) { - ASSERT_MSG(next_offs_dw == 0, "ACB submission is split at the end of ring buffer"); + if (next_offs_dw < offs_dw && next_offs_dw != 0) { + // For cases if a submission is split at the end of the ring buffer, we need to submit it in + // two parts to handle the wrap + liverpool->SubmitAsc(gnm_vqid, {reinterpret_cast(asc_queue.map_addr) + offs_dw, + asc_queue.ring_size_dw - offs_dw}); + offs_dw = 0; } const auto* acb_ptr = reinterpret_cast(asc_queue.map_addr) + offs_dw; From 9aabe98e4eb548b12ddbaeb6d60c81e14caf7dae Mon Sep 17 00:00:00 2001 From: DemoJameson Date: Sat, 28 Dec 2024 18:49:44 +0800 Subject: [PATCH 280/549] Fix a translation not working (#1947) --- src/qt_gui/gui_context_menus.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h index 6c96ab37f..bbc84c4fc 100644 --- a/src/qt_gui/gui_context_menus.h +++ b/src/qt_gui/gui_context_menus.h @@ -347,9 +347,8 @@ public: if (selected == deleteUpdate) { if (!std::filesystem::exists(Common::FS::PathFromQString(game_update_path))) { - QMessageBox::critical( - nullptr, tr("Error"), - QString(tr("This game has no separate update to delete!"))); + QMessageBox::critical(nullptr, tr("Error"), + QString(tr("This game has no update to delete!"))); error = true; } else { folder_path = game_update_path; From 55f78a0b94b508cc51ae23bd49079b680ac1a574 Mon Sep 17 00:00:00 2001 From: mailwl Date: Sat, 28 Dec 2024 13:58:37 +0300 Subject: [PATCH 281/549] libraries: Add libSceMove HLE skeleton (#1945) * libraries: Add libSceMove HLE skeleton * Fix clang-format issue --- CMakeLists.txt | 2 ++ src/common/logging/filter.cpp | 1 + src/common/logging/types.h | 3 ++- src/core/libraries/libs.cpp | 2 ++ src/core/libraries/move/move.cpp | 44 ++++++++++++++++++++++++++++++++ src/core/libraries/move/move.h | 21 +++++++++++++++ 6 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 src/core/libraries/move/move.cpp create mode 100644 src/core/libraries/move/move.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 43e8d7cab..cd3894719 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -425,6 +425,8 @@ set(NP_LIBS src/core/libraries/np_manager/np_manager.cpp set(MISC_LIBS src/core/libraries/screenshot/screenshot.cpp src/core/libraries/screenshot/screenshot.h + src/core/libraries/move/move.cpp + src/core/libraries/move/move.h ) set(DEV_TOOLS src/core/devtools/layer.cpp diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index 75c61a188..a2fd2c0a4 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -97,6 +97,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Lib, Http) \ SUB(Lib, Ssl) \ SUB(Lib, SysModule) \ + SUB(Lib, Move) \ SUB(Lib, NpManager) \ SUB(Lib, NpScore) \ SUB(Lib, NpTrophy) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index a0e7d021f..5b496d175 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -57,8 +57,9 @@ enum class Class : u8 { Lib_MsgDlg, ///< The LibSceMsgDialog implementation. Lib_AudioOut, ///< The LibSceAudioOut implementation. Lib_AudioIn, ///< The LibSceAudioIn implementation. + Lib_Move, ///< The LibSceMove implementation. Lib_Net, ///< The LibSceNet implementation. - Lib_NetCtl, ///< The LibSecNetCtl implementation. + Lib_NetCtl, ///< The LibSceNetCtl implementation. Lib_SaveData, ///< The LibSceSaveData implementation. Lib_SaveDataDialog, ///< The LibSceSaveDataDialog implementation. Lib_Ssl, ///< The LibSceSsl implementation. diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index 66cdd5b87..c30c2d7c3 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -18,6 +18,7 @@ #include "core/libraries/libc_internal/libc_internal.h" #include "core/libraries/libpng/pngdec.h" #include "core/libraries/libs.h" +#include "core/libraries/move/move.h" #include "core/libraries/network/http.h" #include "core/libraries/network/net.h" #include "core/libraries/network/netctl.h" @@ -91,6 +92,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::Remoteplay::RegisterlibSceRemoteplay(sym); Libraries::Videodec::RegisterlibSceVideodec(sym); Libraries::RazorCpu::RegisterlibSceRazorCpu(sym); + Libraries::Move::RegisterlibSceMove(sym); } } // namespace Libraries diff --git a/src/core/libraries/move/move.cpp b/src/core/libraries/move/move.cpp new file mode 100644 index 000000000..626fed9b4 --- /dev/null +++ b/src/core/libraries/move/move.cpp @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" +#include "move.h" + +namespace Libraries::Move { + +int PS4_SYSV_ABI sceMoveOpen() { + LOG_ERROR(Lib_Move, "(STUBBED) called"); + return ORBIS_FAIL; +} + +int PS4_SYSV_ABI sceMoveGetDeviceInfo() { + LOG_ERROR(Lib_Move, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceMoveReadStateRecent() { + LOG_TRACE(Lib_Move, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceMoveTerm() { + LOG_ERROR(Lib_Move, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceMoveInit() { + LOG_ERROR(Lib_Move, "(STUBBED) called"); + return ORBIS_OK; +} + +void RegisterlibSceMove(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("HzC60MfjJxU", "libSceMove", 1, "libSceMove", 1, 1, sceMoveOpen); + LIB_FUNCTION("GWXTyxs4QbE", "libSceMove", 1, "libSceMove", 1, 1, sceMoveGetDeviceInfo); + LIB_FUNCTION("f2bcpK6kJfg", "libSceMove", 1, "libSceMove", 1, 1, sceMoveReadStateRecent); + LIB_FUNCTION("tsZi60H4ypY", "libSceMove", 1, "libSceMove", 1, 1, sceMoveTerm); + LIB_FUNCTION("j1ITE-EoJmE", "libSceMove", 1, "libSceMove", 1, 1, sceMoveInit); +}; + +} // namespace Libraries::Move \ No newline at end of file diff --git a/src/core/libraries/move/move.h b/src/core/libraries/move/move.h new file mode 100644 index 000000000..2d7adaba7 --- /dev/null +++ b/src/core/libraries/move/move.h @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::Move { + +int PS4_SYSV_ABI sceMoveOpen(); +int PS4_SYSV_ABI sceMoveGetDeviceInfo(); +int PS4_SYSV_ABI sceMoveReadStateRecent(); +int PS4_SYSV_ABI sceMoveTerm(); +int PS4_SYSV_ABI sceMoveInit(); + +void RegisterlibSceMove(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::Move \ No newline at end of file From 817a62468e9ecf041ba106cab5b1943127057fff Mon Sep 17 00:00:00 2001 From: polybiusproxy <47796739+polybiusproxy@users.noreply.github.com> Date: Sat, 28 Dec 2024 12:03:00 +0100 Subject: [PATCH 282/549] core: better memory configuration (#1896) --- src/core/address_space.cpp | 2 +- src/core/libraries/kernel/memory.h | 12 +++---- src/core/libraries/np_trophy/np_trophy.cpp | 7 ++-- src/core/linker.cpp | 41 ++++++++++++++++------ src/core/linker.h | 3 +- src/core/memory.cpp | 20 ++++++----- src/core/memory.h | 2 +- 7 files changed, 55 insertions(+), 32 deletions(-) diff --git a/src/core/address_space.cpp b/src/core/address_space.cpp index 24f5e9f87..2b7331cbd 100644 --- a/src/core/address_space.cpp +++ b/src/core/address_space.cpp @@ -26,7 +26,7 @@ asm(".zerofill GUEST_SYSTEM,GUEST_SYSTEM,__guest_system,0xFBFC00000"); namespace Core { -static constexpr size_t BackingSize = SCE_KERNEL_MAIN_DMEM_SIZE_PRO; +static constexpr size_t BackingSize = SCE_KERNEL_TOTAL_MEM_PRO; #ifdef _WIN32 diff --git a/src/core/libraries/kernel/memory.h b/src/core/libraries/kernel/memory.h index 2d19ceb49..400b6c3fc 100644 --- a/src/core/libraries/kernel/memory.h +++ b/src/core/libraries/kernel/memory.h @@ -6,9 +6,11 @@ #include "common/bit_field.h" #include "common/types.h" -constexpr u64 SCE_KERNEL_MAIN_DMEM_SIZE = 5056_MB; // ~ 5GB -// TODO: Confirm this value on hardware. -constexpr u64 SCE_KERNEL_MAIN_DMEM_SIZE_PRO = 5568_MB; // ~ 5.5GB +constexpr u64 SCE_KERNEL_TOTAL_MEM = 5248_MB; +constexpr u64 SCE_KERNEL_TOTAL_MEM_PRO = 5888_MB; + +constexpr u64 SCE_FLEXIBLE_MEMORY_BASE = 64_MB; +constexpr u64 SCE_FLEXIBLE_MEMORY_SIZE = 512_MB; namespace Core::Loader { class SymbolsResolver; @@ -129,10 +131,6 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, size_t len, int flags); int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len); -void* Malloc(size_t size); - -void Free(void* ptr); - void RegisterMemory(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::Kernel diff --git a/src/core/libraries/np_trophy/np_trophy.cpp b/src/core/libraries/np_trophy/np_trophy.cpp index 9324ed6bb..ccd8ab710 100644 --- a/src/core/libraries/np_trophy/np_trophy.cpp +++ b/src/core/libraries/np_trophy/np_trophy.cpp @@ -498,7 +498,7 @@ int PS4_SYSV_ABI sceNpTrophyGetTrophyInfo(OrbisNpTrophyContext context, OrbisNpT s32 PS4_SYSV_ABI sceNpTrophyGetTrophyUnlockState(OrbisNpTrophyContext context, OrbisNpTrophyHandle handle, OrbisNpTrophyFlagArray* flags, u32* count) { - LOG_INFO(Lib_NpTrophy, "GetTrophyUnlockState called"); + LOG_INFO(Lib_NpTrophy, "called"); if (context == ORBIS_NP_TROPHY_INVALID_CONTEXT) return ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT; @@ -519,8 +519,9 @@ s32 PS4_SYSV_ABI sceNpTrophyGetTrophyUnlockState(OrbisNpTrophyContext context, pugi::xml_parse_result result = doc.load_file(trophy_file.native().c_str()); if (!result) { - LOG_ERROR(Lib_NpTrophy, "Failed to open trophy xml : {}", result.description()); - return -1; + LOG_ERROR(Lib_NpTrophy, "Failed to open trophy XML: {}", result.description()); + *count = 0; + return ORBIS_OK; } int num_trophies = 0; diff --git a/src/core/linker.cpp b/src/core/linker.cpp index 9cf4198ae..28d2eea7b 100644 --- a/src/core/linker.cpp +++ b/src/core/linker.cpp @@ -5,6 +5,7 @@ #include "common/arch.h" #include "common/assert.h" #include "common/config.h" +#include "common/elf_info.h" #include "common/logging/log.h" #include "common/path_util.h" #include "common/string_util.h" @@ -65,21 +66,41 @@ void Linker::Execute() { Relocate(m.get()); } - // Configure used flexible memory size. - if (const auto* proc_param = GetProcParam()) { - if (proc_param->size >= - offsetof(OrbisProcParam, mem_param) + sizeof(OrbisKernelMemParam*)) { - if (const auto* mem_param = proc_param->mem_param) { - if (mem_param->size >= - offsetof(OrbisKernelMemParam, flexible_memory_size) + sizeof(u64*)) { - if (const auto* flexible_size = mem_param->flexible_memory_size) { - memory->SetupMemoryRegions(*flexible_size); - } + // Configure the direct and flexible memory regions. + u64 fmem_size = SCE_FLEXIBLE_MEMORY_SIZE; + bool use_extended_mem1 = true, use_extended_mem2 = true; + + const auto* proc_param = GetProcParam(); + ASSERT(proc_param); + + Core::OrbisKernelMemParam mem_param{}; + if (proc_param->size >= offsetof(OrbisProcParam, mem_param) + sizeof(OrbisKernelMemParam*)) { + if (proc_param->mem_param) { + mem_param = *proc_param->mem_param; + if (mem_param.size >= + offsetof(OrbisKernelMemParam, flexible_memory_size) + sizeof(u64*)) { + if (const auto* flexible_size = mem_param.flexible_memory_size) { + fmem_size = *flexible_size + SCE_FLEXIBLE_MEMORY_BASE; } } } } + if (mem_param.size < offsetof(OrbisKernelMemParam, extended_memory_1) + sizeof(u64*)) { + mem_param.extended_memory_1 = nullptr; + } + if (mem_param.size < offsetof(OrbisKernelMemParam, extended_memory_2) + sizeof(u64*)) { + mem_param.extended_memory_2 = nullptr; + } + + const u64 sdk_ver = proc_param->sdk_version; + if (sdk_ver < Common::ElfInfo::FW_50) { + use_extended_mem1 = mem_param.extended_memory_1 ? *mem_param.extended_memory_1 : false; + use_extended_mem2 = mem_param.extended_memory_2 ? *mem_param.extended_memory_2 : false; + } + + memory->SetupMemoryRegions(fmem_size, use_extended_mem1, use_extended_mem2); + main_thread.Run([this, module](std::stop_token) { Common::SetCurrentThreadName("GAME_MainThread"); LoadSharedLibraries(); diff --git a/src/core/linker.h b/src/core/linker.h index d6b5d648a..7ef13ae56 100644 --- a/src/core/linker.h +++ b/src/core/linker.h @@ -22,8 +22,9 @@ struct OrbisKernelMemParam { u8* extended_memory_1; u64* extended_gpu_page_table; u8* extended_memory_2; - u64* exnteded_cpu_page_table; + u64* extended_cpu_page_table; }; +static_assert(sizeof(OrbisKernelMemParam) == 0x38); struct OrbisProcParam { u64 size; diff --git a/src/core/memory.cpp b/src/core/memory.cpp index aa116fa3d..0a69ad773 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -12,12 +12,7 @@ namespace Core { -constexpr u64 SCE_DEFAULT_FLEXIBLE_MEMORY_SIZE = 448_MB; - MemoryManager::MemoryManager() { - // Set up the direct and flexible memory regions. - SetupMemoryRegions(SCE_DEFAULT_FLEXIBLE_MEMORY_SIZE); - // Insert a virtual memory area that covers the entire area we manage. const VAddr system_managed_base = impl.SystemManagedVirtualBase(); const size_t system_managed_size = impl.SystemManagedVirtualSize(); @@ -38,10 +33,17 @@ MemoryManager::MemoryManager() { MemoryManager::~MemoryManager() = default; -void MemoryManager::SetupMemoryRegions(u64 flexible_size) { - const auto total_size = - Config::isNeoMode() ? SCE_KERNEL_MAIN_DMEM_SIZE_PRO : SCE_KERNEL_MAIN_DMEM_SIZE; - total_flexible_size = flexible_size; +void MemoryManager::SetupMemoryRegions(u64 flexible_size, bool use_extended_mem1, + bool use_extended_mem2) { + const bool is_neo = Config::isNeoMode(); + auto total_size = is_neo ? SCE_KERNEL_TOTAL_MEM_PRO : SCE_KERNEL_TOTAL_MEM; + if (!use_extended_mem1 && is_neo) { + total_size -= 256_MB; + } + if (!use_extended_mem2 && !is_neo) { + total_size -= 128_MB; + } + total_flexible_size = flexible_size - SCE_FLEXIBLE_MEMORY_BASE; total_direct_size = total_size - flexible_size; // Insert an area that covers direct memory physical block. diff --git a/src/core/memory.h b/src/core/memory.h index a9f2df322..615ecc3eb 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -166,7 +166,7 @@ public: bool TryWriteBacking(void* address, const void* data, u32 num_bytes); - void SetupMemoryRegions(u64 flexible_size); + void SetupMemoryRegions(u64 flexible_size, bool use_extended_mem1, bool use_extended_mem2); PAddr PoolExpand(PAddr search_start, PAddr search_end, size_t size, u64 alignment); From fc2fd9fd3cd4ce584235590772d15904e0c1b9a4 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Sat, 28 Dec 2024 13:05:53 +0200 Subject: [PATCH 283/549] Update README.md with latest (not quite) addition to dev team --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 7ba13ad47..e68fb86f7 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,10 @@ R3 | M | | - [**skmp**](https://github.com/skmp) - [**wheremyfoodat**](https://github.com/wheremyfoodat) - [**raziel1000**](https://github.com/raziel1000) +- [**viniciuslrangel**](https://github.com/viniciuslrangel) +- [**Roamic**](https://github.com/vladmikhalin) +- [**Poly**](https://github.com/polybiusproxy) +- [**squidbus**](https://github.com/squidbus) Logo is done by [**Xphalnos**](https://github.com/Xphalnos) From c8b704e4a364ce64d2f50cda2c7a240fec2f9aa1 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Sat, 28 Dec 2024 13:06:51 +0200 Subject: [PATCH 284/549] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e68fb86f7..4fad47c3d 100644 --- a/README.md +++ b/README.md @@ -122,8 +122,8 @@ R3 | M | | - [**wheremyfoodat**](https://github.com/wheremyfoodat) - [**raziel1000**](https://github.com/raziel1000) - [**viniciuslrangel**](https://github.com/viniciuslrangel) -- [**Roamic**](https://github.com/vladmikhalin) -- [**Poly**](https://github.com/polybiusproxy) +- [**roamic**](https://github.com/vladmikhalin) +- [**poly**](https://github.com/polybiusproxy) - [**squidbus**](https://github.com/squidbus) Logo is done by [**Xphalnos**](https://github.com/Xphalnos) From f0352d2f7db0d64fec53fcb7214de9438285c6cb Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Sat, 28 Dec 2024 13:16:25 +0200 Subject: [PATCH 285/549] Updated dev team (sorry frodo , the ring is yours) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4fad47c3d..fd40d2d63 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,7 @@ R3 | M | | - [**roamic**](https://github.com/vladmikhalin) - [**poly**](https://github.com/polybiusproxy) - [**squidbus**](https://github.com/squidbus) +- [**frodo**](https://github.com/baggins183) Logo is done by [**Xphalnos**](https://github.com/Xphalnos) From 99e1e028c056de16966913d64efac7e7975df40f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quang=20Ng=C3=B4?= Date: Sat, 28 Dec 2024 18:18:56 +0700 Subject: [PATCH 286/549] texture_cache: Don't read max ansio value if not aniso filter (#1942) Fix Sonic Forces. --- src/video_core/amdgpu/resource.h | 2 +- src/video_core/texture_cache/sampler.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/video_core/amdgpu/resource.h b/src/video_core/amdgpu/resource.h index 58b286e9c..6bbe1fb7e 100644 --- a/src/video_core/amdgpu/resource.h +++ b/src/video_core/amdgpu/resource.h @@ -447,7 +447,7 @@ struct Sampler { } float MaxAniso() const { - switch (max_aniso) { + switch (max_aniso.Value()) { case AnisoRatio::One: return 1.0f; case AnisoRatio::Two: diff --git a/src/video_core/texture_cache/sampler.cpp b/src/video_core/texture_cache/sampler.cpp index 5ce10ff0e..8dbdd2912 100644 --- a/src/video_core/texture_cache/sampler.cpp +++ b/src/video_core/texture_cache/sampler.cpp @@ -18,7 +18,8 @@ Sampler::Sampler(const Vulkan::Instance& instance, const AmdGpu::Sampler& sample (AmdGpu::IsAnisoFilter(sampler.xy_mag_filter) || AmdGpu::IsAnisoFilter(sampler.xy_min_filter)); const float maxAnisotropy = - std::clamp(sampler.MaxAniso(), 1.0f, instance.MaxSamplerAnisotropy()); + anisotropyEnable ? std::clamp(sampler.MaxAniso(), 1.0f, instance.MaxSamplerAnisotropy()) + : 1.0f; const vk::SamplerCreateInfo sampler_ci = { .magFilter = LiverpoolToVK::Filter(sampler.xy_mag_filter), .minFilter = LiverpoolToVK::Filter(sampler.xy_min_filter), From 668d5f65dcb1442ff14a5498a242231f36637b69 Mon Sep 17 00:00:00 2001 From: DemoJameson Date: Sat, 28 Dec 2024 19:19:16 +0800 Subject: [PATCH 287/549] Update zh_CN translation (#1946) --- src/qt_gui/translations/zh_CN.ts | 232 +++++++++++++++---------------- 1 file changed, 116 insertions(+), 116 deletions(-) diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index 4ceb91315..32b838fac 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -2,7 +2,7 @@ + SPDX-License-Identifier: GPL-2.0-or-later --> AboutDialog @@ -18,7 +18,7 @@ shadPS4 is an experimental open-source emulator for the PlayStation 4. - shadPS4 是一款实验性质的开源 PlayStation 4模拟器软件。 + shadPS4 是一款实验性质的开源 PlayStation 4 模拟器软件。 @@ -103,7 +103,7 @@ Cheats / Patches - 作弊码 / 补丁 + 作弊码/补丁 @@ -113,7 +113,7 @@ Trophy Viewer - Trophy 查看器 + 奖杯查看器 @@ -128,7 +128,7 @@ Open Save Data Folder - 打开保存数据文件夹 + 打开存档数据文件夹 @@ -173,27 +173,27 @@ Delete DLC - 删除DLC + 删除 DLC Compatibility... - Compatibility... + 兼容性... Update database - Update database + 更新数据库 View report - View report + 查看报告 Submit a report - Submit a report + 提交报告 @@ -203,7 +203,7 @@ Shortcut created successfully! - 创建快捷方式成功! + 创建快捷方式成功! @@ -213,7 +213,7 @@ Error creating shortcut! - 创建快捷方式出错! + 创建快捷方式出错! @@ -228,7 +228,7 @@ requiresEnableSeparateUpdateFolder_MSG - 这个功能需要‘启用单独的更新目录’配置选项才能正常运行,如果你想要使用这个功能,请启用它。 + 这个功能需要“启用单独的更新目录”配置选项才能正常运行,如果你想要使用这个功能,请启用它。 @@ -243,7 +243,7 @@ This game has no DLC to delete! - 这个游戏没有DLC可以删除! + 这个游戏没有 DLC 可以删除! @@ -258,7 +258,7 @@ Are you sure you want to delete %1's %2 directory? - 你确定要删除 %1 的 %2 目录? + 你确定要删除 %1 的%2目录? @@ -266,7 +266,7 @@ Open/Add Elf Folder - 打开/添加Elf文件夹 + 打开/添加 Elf 文件夹 @@ -316,7 +316,7 @@ Exit the application. - 退出应用程序. + 退出应用程序。 @@ -341,12 +341,12 @@ Medium - 中等 + Large - 巨大 + @@ -376,7 +376,7 @@ Dump Game List - 转储游戏列表 + 导出游戏列表 @@ -472,7 +472,7 @@ Trophy Viewer - Trophy 查看器 + 奖杯查看器 @@ -485,7 +485,7 @@ General - 通用 + 常规 @@ -520,12 +520,12 @@ Show Splash - 显示Splash + 显示启动画面 Is PS4 Pro - 是否是 PS4 Pro + 模拟 PS4 Pro @@ -570,17 +570,17 @@ Hide Cursor Idle Timeout - 光标空闲超时隐藏 + 光标隐藏闲置时长 s - s + Controller - 控制器 + 手柄 @@ -595,7 +595,7 @@ Graphics Device - 图像设备 + 图形设备 @@ -700,7 +700,7 @@ Disable Trophy Pop-ups - Disable Trophy Pop-ups + 禁止弹出奖杯 @@ -710,22 +710,22 @@ Update Compatibility Database On Startup - Update Compatibility Database On Startup + 启动时更新兼容性数据库 Game Compatibility - Game Compatibility + 游戏兼容性 Display Compatibility Data - Display Compatibility Data + 显示兼容性数据 Update Compatibility Database - Update Compatibility Database + 更新兼容性数据库 @@ -735,7 +735,7 @@ Audio Backend - Audio Backend + 音频后端 @@ -778,12 +778,12 @@ All Patches available for all games have been downloaded. - 所有游戏的所有补丁都已下载。 + 所有游戏的可用补丁都已下载。 Games: - 游戏: + 游戏: @@ -798,7 +798,7 @@ Game Boot - 游戏启动 + 启动游戏 @@ -818,7 +818,7 @@ PKG and Game versions match: - PKG 和游戏版本匹配: + PKG 和游戏版本匹配: @@ -828,17 +828,17 @@ PKG Version %1 is older than installed version: - PKG 版本 %1 比已安装版本更旧: + PKG 版本 %1 比已安装版本更旧: Game is installed: - 游戏已安装: + 游戏已安装: Would you like to install Patch: - 您想安装补丁吗: + 您想安装补丁吗: @@ -848,12 +848,12 @@ Would you like to install DLC: %1? - 您想安装 DLC: %1 吗? + 您想安装 DLC:%1 吗? DLC already installed: - DLC 已经安装: + DLC 已经安装: @@ -896,42 +896,42 @@ Cheats / Patches for - Cheats / Patches for + 作弊码/补丁: defaultTextEdit_MSG - 作弊/补丁是实验性的。\n请小心使用。\n\n通过选择存储库并点击下载按钮,单独下载作弊程序。\n在“补丁”选项卡中,您可以一次性下载所有补丁,选择要使用的补丁并保存选择。\n\n由于我们不开发作弊程序/补丁,\n请将问题报告给作弊程序的作者。\n\n创建了新的作弊程序?访问:\nhttps://github.com/shadps4-emu/ps4_cheats + 作弊码/补丁是实验性的。\n请小心使用。\n\n通过选择存储库并点击下载按钮,下载该游戏的作弊码。\n在“补丁”选项卡中,您可以一次性下载所有补丁,选择要使用的补丁并保存选择。\n\n由于我们不开发作弊码/补丁,\n请将问题报告给作弊码/补丁的作者。\n\n创建了新的作弊码/补丁?欢迎提交到我们的仓库:\nhttps://github.com/shadps4-emu/ps4_cheats No Image Available - 没有可用的图像 + 没有可用的图片 Serial: - 序列号: + 序列号: Version: - 版本: + 版本: Size: - 大小: + 大小: Select Cheat File: - 选择作弊码文件: + 选择作弊码文件: Repository: - 存储库: + 存储库: @@ -961,7 +961,7 @@ Select Patch File: - 选择补丁文件: + 选择补丁文件: @@ -1016,7 +1016,7 @@ Failed to parse XML: - 解析 XML 失败: + 解析 XML 失败: @@ -1046,17 +1046,17 @@ File already exists. Do you want to replace it? - 文件已存在。您要替换它吗? + 文件已存在,您要替换它吗? Failed to save file: - 保存文件失败: + 保存文件失败: Failed to download file: - 下载文件失败: + 下载文件失败: @@ -1076,17 +1076,17 @@ CheatsDownloadedSuccessfully_MSG - 您已成功下载了该游戏版本的作弊码 从所选存储库中。如果有,您还可以尝试从其他存储库下载,或通过从列表中选择文件来使用它们。 + 您已从所选存储库中成功下载了该游戏版本的作弊码。您还可以尝试从其他存储库下载,或通过从列表中选择文件来使用它们。 Failed to save: - 保存失败: + 保存失败: Failed to download: - 下载失败: + 下载失败: @@ -1096,7 +1096,7 @@ DownloadComplete_MSG - 补丁下载成功!所有可用的补丁已下载完成,无需像作弊码那样单独下载每个游戏的补丁。如果补丁没有出现,可能是该补丁不存在于特定的序列号和游戏版本中。 + 补丁下载成功!所有可用的补丁已下载完成,无需像作弊码那样单独下载每个游戏的补丁。如果补丁没有出现,可能是该补丁不适用于当前游戏的序列号和版本。 @@ -1111,12 +1111,12 @@ The game is in version: %1 - 游戏版本: %1 + 游戏版本:%1 The downloaded patch only works on version: %1 - 下载的补丁仅适用于版本: %1 + 下载的补丁仅适用于版本:%1 @@ -1131,12 +1131,12 @@ Failed to open file: - 无法打开文件: + 无法打开文件: XML ERROR: - XML 错误: + XML 错误: @@ -1146,12 +1146,12 @@ Author: - 作者: + 作者: Directory does not exist: - 目录不存在: + 目录不存在: @@ -1161,12 +1161,12 @@ Name: - 名称: + 名称: Can't apply cheats before the game is started - 在游戏开始之前无法应用作弊。 + 在游戏启动之前无法应用作弊码。 @@ -1199,17 +1199,17 @@ consoleLanguageGroupBox - 控制台语言:\n设置 PS4 游戏中使用的语言。\n建议设置为支持的语言,因为可能因地区而异。 + 主机语言:\n设置 PS4 游戏中使用的语言。\n建议设置为支持的语言,这将因地区而异。 emulatorLanguageGroupBox - 模拟器语言:\n设置模拟器用户界面的语言。 + 模拟器语言:\n设置模拟器用户界面的语言。 fullscreenCheckBox - 启用全屏模式:\n自动将游戏窗口设置为全屏模式。\n您可以按 F11 键禁用此选项。 + 启用全屏:\n以全屏模式启动游戏。\n您可以按 F11 键切换回窗口模式。 @@ -1219,77 +1219,77 @@ showSplashCheckBox - 显示启动画面:\n在游戏启动时显示游戏的启动画面(特殊图像)。 + 显示启动画面:\n在游戏启动时显示游戏的启动画面(特殊图像)。 ps4proCheckBox - 这是 PS4 Pro:\n使模拟器作为 PS4 PRO 运行,可以在支持的游戏中激活特殊功能。 + 模拟 PS4 Pro:\n使模拟器作为 PS4 Pro 运行,可以在支持的游戏中激活特殊功能。 discordRPCCheckbox - 启用 Discord Rich Presence:\n在您的 Discord 个人资料上显示仿真器图标和相关信息。 + 启用 Discord Rich Presence:\n在您的 Discord 个人资料上显示模拟器图标和相关信息。 userName - 用户名:\n设置 PS4 帐户的用户名。某些游戏中可能会显示此名称。 + 用户名:\n设置 PS4 帐户的用户名,某些游戏中可能会显示此名称。 logTypeGroupBox - 日志类型:\n设置是否同步日志窗口的输出以提高性能。这可能会对模拟产生负面影响。 + 日志类型:\n设置日志窗口输出的同步方式以提高性能。可能会对模拟产生不良影响。 logFilter - 日志过滤器:\n过滤日志,仅打印特定信息。\n例如:"Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" 级别: Trace, Debug, Info, Warning, Error, Critical - 按此顺序,特定级别将静默列表中所有先前的级别,并记录所有后续级别。 + 日志过滤器:\n过滤日志,仅打印特定信息。\n例如:"Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" 级别: Trace, Debug, Info, Warning, Error, Critical - 按此顺序,特定级别将静默列表中所有先前的级别,并记录所有后续级别。 updaterGroupBox - 更新:\nRelease: 官方版本,可能非常旧,并且每月发布,但更可靠且经过测试。\nNightly: 开发版本,包含所有最新功能和修复,但可能包含错误且不够稳定。 + 更新:\nRelease:每月发布的官方版本可能非常过时,但更可靠且经过测试。\nNightly:包含所有最新功能和修复的开发版本,但可能包含错误且稳定性较低。 GUIgroupBox - 播放标题音乐:\n如果游戏支持,在图形界面选择游戏时启用播放特殊音乐。 + 播放标题音乐:\n如果游戏支持,在图形界面选择游戏时播放特殊音乐。 disableTrophycheckBox - Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + 禁止弹出奖杯:\n禁用游戏内奖杯通知。可以在奖杯查看器中继续跟踪奖杯进度(在主窗口中右键点击游戏)。 hideCursorGroupBox - 隐藏光标:\n选择光标何时消失:\n从不: 您将始终看到鼠标。\n空闲: 设置光标在空闲后消失的时间。\n始终: 您将永远看不到鼠标。 + 隐藏光标:\n选择光标何时消失:\n从不: 始终显示光标。\闲置: 光标在闲置若干秒后消失。\n始终: 始终显示光标。 idleTimeoutGroupBox - 设置鼠标在空闲后消失的时间。 + 光标隐藏闲置时长:\n光标自动隐藏之前的闲置时长。 backButtonBehaviorGroupBox - 返回按钮行为:\n设置控制器的返回按钮以模拟在 PS4 触控板上指定位置的点击。 + 返回按钮行为:\n设置手柄的返回按钮模拟在 PS4 触控板上指定位置的点击。 enableCompatibilityCheckBox - Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + 显示兼容性数据:\n在列表视图中显示游戏兼容性信息。启用“启动时更新兼容性数据库”以获取最新信息。 checkCompatibilityOnStartupCheckBox - Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + 启动时更新兼容性数据库:\n当 shadPS4 启动时自动更新兼容性数据库。 updateCompatibilityButton - Update Compatibility Database:\nImmediately update the compatibility database. + 更新兼容性数据库:\n立即更新兼容性数据库。 @@ -1299,7 +1299,7 @@ Idle - 空闲 + 闲置 @@ -1329,62 +1329,62 @@ graphicsAdapterGroupBox - 图形设备:\n在具有多个 GPU 的系统中,从下拉列表中选择要使用的 GPU,\n或者选择“自动检测”以自动确定。 + 图形设备:\n在具有多个 GPU 的系统中,从下拉列表中选择要使用的 GPU,\n或者选择“自动选择”由模拟器决定。 resolutionLayout - 宽度/高度:\n设置启动时模拟器的窗口大小,该大小可以在游戏中更改。\n这与游戏中的分辨率不同。 + 宽度/高度:\n设置启动游戏时的窗口大小,游戏过程中可以调整。\n这与游戏内的分辨率不同。 heightDivider - Vblank 除数:\n模拟器更新的帧速率乘以此数字。改变此项可能会导致游戏速度加快,或破坏游戏中不期望此变化的关键功能! + Vblank Divider:\n模拟器刷新的帧率会乘以此数字。改变此项可能会导致游戏速度加快,或破坏游戏中不期望此变化的关键功能! dumpShadersCheckBox - 启用着色器转储:\n为了技术调试,在渲染期间将游戏着色器保存到文件夹中。 + 启用着色器转储:\n用于技术调试,在渲染期间将游戏着色器保存到文件夹中。 nullGpuCheckBox - 启用空 GPU:\n为了技术调试,将游戏渲染禁用,仿佛没有图形卡。 + 启用 NULL GPU:\n用于技术调试,禁用游戏渲染,就像没有显卡一样。 gameFoldersBox - 游戏文件夹:\n检查已安装游戏的文件夹列表。 + 游戏文件夹:\n检查已安装游戏的文件夹列表。 addFolderButton - 添加:\n将文件夹添加到列表。 + 添加:\n将文件夹添加到列表。 removeFolderButton - 移除:\n从列表中移除文件夹。 + 移除:\n从列表中移除文件夹。 debugDump - 启用调试转储:\n将当前正在运行的 PS4 程序的导入和导出符号及文件头信息保存到目录中。 + 启用调试转储:\n将当前正在运行的 PS4 程序的导入和导出符号及文件头信息保存到目录中。 vkValidationCheckBox - 启用 Vulkan 验证层:\n启用验证 Vulkan 渲染器状态并记录内部状态信息的系统。这可能会降低性能,并可能更改模拟行为。 + 启用 Vulkan 验证层:\n启用一个系统来验证 Vulkan 渲染器的状态并记录其内部状态的信息。\n这将降低性能并可能改变模拟的行为。 vkSyncValidationCheckBox - 启用 Vulkan 同步验证:\n启用验证 Vulkan 渲染任务时间的系统。这可能会降低性能,并可能更改模拟行为。 + 启用 Vulkan 同步验证:\n启用一个系统来验证 Vulkan 渲染任务的时间。\n这将降低性能并可能改变模拟的行为。 rdocCheckBox - 启用 RenderDoc 调试:\n如果启用,模拟器将提供与 Renderdoc 的兼容性,允许在渲染过程中捕获和分析当前渲染的帧。 + 启用 RenderDoc 调试:\n启用后模拟器将提供与 Renderdoc 的兼容性,允许在渲染过程中捕获和分析当前渲染的帧。 @@ -1407,7 +1407,7 @@ Compatibility - Compatibility + 兼容性 @@ -1442,52 +1442,52 @@ Never Played - Never Played + 未玩过 h - h + 小时 m - m + 分钟 s - s + Compatibility is untested - Compatibility is untested + 兼容性未经测试 Game does not initialize properly / crashes the emulator - Game does not initialize properly / crashes the emulator + 游戏无法正确初始化/模拟器崩溃 Game boots, but only displays a blank screen - Game boots, but only displays a blank screen + 游戏启动,但只显示白屏 Game displays an image but does not go past the menu - Game displays an image but does not go past the menu + 游戏显示图像但无法通过菜单页面 Game has game-breaking glitches or unplayable performance - Game has game-breaking glitches or unplayable performance + 游戏有严重的 Bug 或太卡无法游玩 Game can be completed with playable performance and no major glitches - Game can be completed with playable performance and no major glitches + 游戏能在可玩的性能下完成且没有重大 Bug @@ -1525,7 +1525,7 @@ No download URL found for the specified asset. - 未找到指定资产的下载 URL。 + 未找到指定资源的下载地址。 @@ -1560,7 +1560,7 @@ Show Changelog - 显示变更日志 + 显示更新日志 @@ -1580,17 +1580,17 @@ Hide Changelog - 隐藏变更日志 + 隐藏更新日志 Changes - 变更 + 更新日志 Network error occurred while trying to access the URL - 尝试访问 URL 时发生网络错误 + 尝试访问网址时发生网络错误 @@ -1646,4 +1646,4 @@ TB - \ No newline at end of file + From 63d2d1ebe8467a58410e9eb82ff2ed5bafee0a4c Mon Sep 17 00:00:00 2001 From: jas0n098 Date: Sat, 28 Dec 2024 11:19:41 +0000 Subject: [PATCH 288/549] Handle RectList primitives in Geometry shaders (#1936) --- src/shader_recompiler/backend/spirv/emit_spirv.cpp | 1 + src/shader_recompiler/backend/spirv/spirv_emit_context.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index 900d40472..f0cf15af0 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -28,6 +28,7 @@ static constexpr spv::ExecutionMode GetInputPrimitiveType(AmdGpu::PrimitiveType return spv::ExecutionMode::InputLines; case AmdGpu::PrimitiveType::TriangleList: case AmdGpu::PrimitiveType::TriangleStrip: + case AmdGpu::PrimitiveType::RectList: return spv::ExecutionMode::Triangles; case AmdGpu::PrimitiveType::AdjTriangleList: return spv::ExecutionMode::InputTrianglesAdjacency; diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 281c487af..575bf91f7 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -47,6 +47,7 @@ static constexpr u32 NumVertices(AmdGpu::PrimitiveType type) { return 2u; case AmdGpu::PrimitiveType::TriangleList: case AmdGpu::PrimitiveType::TriangleStrip: + case AmdGpu::PrimitiveType::RectList: return 3u; case AmdGpu::PrimitiveType::AdjTriangleList: return 6u; From 8447d6ea1c6295b9e499f135a61cdb163a9c4b03 Mon Sep 17 00:00:00 2001 From: rainmakerv2 <30595646+rainmakerv3@users.noreply.github.com> Date: Sat, 28 Dec 2024 21:30:26 +0800 Subject: [PATCH 289/549] Remove PS4 pro mode from GUI, can still be edited in from config file (#1871) --- src/qt_gui/settings_dialog.cpp | 8 +------- src/qt_gui/settings_dialog.ui | 7 ------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index df802901a..6d4de6603 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -199,7 +199,6 @@ SettingsDialog::SettingsDialog(std::span physical_devices, ui->fullscreenCheckBox->installEventFilter(this); ui->separateUpdatesCheckBox->installEventFilter(this); ui->showSplashCheckBox->installEventFilter(this); - ui->ps4proCheckBox->installEventFilter(this); ui->discordRPCCheckbox->installEventFilter(this); ui->userName->installEventFilter(this); ui->logTypeGroupBox->installEventFilter(this); @@ -291,7 +290,6 @@ void SettingsDialog::LoadValuesFromConfig() { ui->separateUpdatesCheckBox->setChecked( toml::find_or(data, "General", "separateUpdateEnabled", false)); ui->showSplashCheckBox->setChecked(toml::find_or(data, "General", "showSplash", false)); - ui->ps4proCheckBox->setChecked(toml::find_or(data, "General", "isPS4Pro", false)); ui->logTypeComboBox->setCurrentText( QString::fromStdString(toml::find_or(data, "General", "logType", "async"))); ui->logFilterLineEdit->setText( @@ -408,8 +406,6 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) { text = tr("separateUpdatesCheckBox"); } else if (elementName == "showSplashCheckBox") { text = tr("showSplashCheckBox"); - } else if (elementName == "ps4proCheckBox") { - text = tr("ps4proCheckBox"); } else if (elementName == "discordRPCCheckbox") { text = tr("discordRPCCheckbox"); } else if (elementName == "userName") { @@ -520,11 +516,9 @@ void SettingsDialog::UpdateSettings() { const QVector TouchPadIndex = {"left", "center", "right", "none"}; Config::setBackButtonBehavior(TouchPadIndex[ui->backButtonBehaviorComboBox->currentIndex()]); - Config::setNeoMode(ui->ps4proCheckBox->isChecked()); Config::setFullscreenMode(ui->fullscreenCheckBox->isChecked()); Config::setisTrophyPopupDisabled(ui->disableTrophycheckBox->isChecked()); Config::setPlayBGM(ui->playBGMCheckBox->isChecked()); - Config::setNeoMode(ui->ps4proCheckBox->isChecked()); Config::setLogType(ui->logTypeComboBox->currentText().toStdString()); Config::setLogFilter(ui->logFilterLineEdit->text().toStdString()); Config::setUserName(ui->userNameLineEdit->text().toStdString()); @@ -590,4 +584,4 @@ void SettingsDialog::ResetInstallFolders() { } Config::setGameInstallDirs(settings_install_dirs_config); } -} +} \ No newline at end of file diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index c18b70497..f2d6b77d2 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -228,13 +228,6 @@ - - - - Is PS4 Pro - - - From a8b4e14bf526b4983eab8cc544263ee3d28412f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quang=20Ng=C3=B4?= Date: Sat, 28 Dec 2024 21:35:12 +0700 Subject: [PATCH 290/549] Fix SDL version cannot launch game using game ID (#1650) * Fix SDL version cannot launch game using game ID Missing from https://github.com/shadps4-emu/shadPS4/pull/1507. * Added --add-game-folder argument Also added "treat the last argument as the game, if it isn't found already" option to the qt version --------- Co-authored-by: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> --- src/main.cpp | 58 +++++++++++++++++++++++++++++++++++++++++---- src/qt_gui/main.cpp | 35 +++++++++++++++++++++++---- 2 files changed, 83 insertions(+), 10 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 17b5c11fe..bdbab89c9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,11 +4,14 @@ #include "functional" #include "iostream" #include "string" +#include "system_error" #include "unordered_map" #include #include "common/config.h" #include "common/memory_patcher.h" +#include "common/path_util.h" +#include "core/file_sys/fs.h" #include "emulator.h" #ifdef _WIN32 @@ -20,6 +23,10 @@ int main(int argc, char* argv[]) { SetConsoleOutputCP(CP_UTF8); #endif + // Load configurations + const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); + Config::load(user_dir / "config.toml"); + bool has_game_argument = false; std::string game_path; @@ -33,6 +40,7 @@ int main(int argc, char* argv[]) { " -p, --patch Apply specified patch file\n" " -f, --fullscreen Specify window initial fullscreen " "state. Does not overwrite the config file.\n" + " --add-game-folder Adds a new game folder to the config.\n" " -h, --help Display this help message\n"; exit(0); }}, @@ -81,6 +89,25 @@ int main(int argc, char* argv[]) { Config::setFullscreenMode(is_fullscreen); }}, {"--fullscreen", [&](int& i) { arg_map["-f"](i); }}, + {"--add-game-folder", + [&](int& i) { + if (++i >= argc) { + std::cerr << "Error: Missing argument for --add-game-folder\n"; + exit(1); + } + std::string config_dir(argv[i]); + std::filesystem::path config_path = std::filesystem::path(config_dir); + std::error_code discard; + if (!std::filesystem::exists(config_path, discard)) { + std::cerr << "Error: File does not exist: " << config_path << "\n"; + exit(1); + } + + Config::addGameInstallDir(config_path); + Config::save(Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "config.toml"); + std::cout << "Game folder successfully saved.\n"; + exit(0); + }}, }; if (argc == 1) { @@ -105,20 +132,41 @@ int main(int argc, char* argv[]) { } } + // If no game directory is set and no command line argument, prompt for it + if (Config::getGameInstallDirs().empty()) { + std::cout << "Warning: No game folder set, please set it by calling shadps4" + " with the --add-game-folder argument"; + } + if (!has_game_argument) { std::cerr << "Error: Please provide a game path or ID.\n"; exit(1); } // Check if the game path or ID exists - if (!std::filesystem::exists(game_path)) { - std::cerr << "Error: Game file not found\n"; - return -1; + std::filesystem::path eboot_path(game_path); + + // Check if the provided path is a valid file + if (!std::filesystem::exists(eboot_path)) { + // If not a file, treat it as a game ID and search in install directories + bool game_found = false; + for (const auto& install_dir : Config::getGameInstallDirs()) { + const auto candidate_path = install_dir / game_path / "eboot.bin"; + if (std::filesystem::exists(candidate_path)) { + eboot_path = candidate_path; + game_found = true; + break; + } + } + if (!game_found) { + std::cerr << "Error: Game ID or file path not found: " << game_path << std::endl; + return 1; + } } - // Run the emulator with the specified game + // Run the emulator with the resolved eboot path Core::Emulator emulator; - emulator.Run(game_path); + emulator.Run(eboot_path); return 0; } diff --git a/src/qt_gui/main.cpp b/src/qt_gui/main.cpp index 318245053..ac731fdce 100644 --- a/src/qt_gui/main.cpp +++ b/src/qt_gui/main.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "iostream" +#include "system_error" #include "unordered_map" #include "common/config.h" @@ -31,7 +32,7 @@ int main(int argc, char* argv[]) { bool has_command_line_argument = argc > 1; bool show_gui = false, has_game_argument = false; - std::string gamePath; + std::string game_path; // Map of argument strings to lambda functions std::unordered_map> arg_map = { @@ -46,6 +47,7 @@ int main(int argc, char* argv[]) { " -s, --show-gui Show the GUI\n" " -f, --fullscreen Specify window initial fullscreen " "state. Does not overwrite the config file.\n" + " --add-game-folder Adds a new game folder to the config.\n" " -h, --help Display this help message\n"; exit(0); }}, @@ -57,7 +59,7 @@ int main(int argc, char* argv[]) { {"-g", [&](int& i) { if (i + 1 < argc) { - gamePath = argv[++i]; + game_path = argv[++i]; has_game_argument = true; } else { std::cerr << "Error: Missing argument for -g/--game\n"; @@ -98,6 +100,25 @@ int main(int argc, char* argv[]) { Config::setFullscreenMode(is_fullscreen); }}, {"--fullscreen", [&](int& i) { arg_map["-f"](i); }}, + {"--add-game-folder", + [&](int& i) { + if (++i >= argc) { + std::cerr << "Error: Missing argument for --add-game-folder\n"; + exit(1); + } + std::string config_dir(argv[i]); + std::filesystem::path config_path = std::filesystem::path(config_dir); + std::error_code discard; + if (!std::filesystem::is_directory(config_path, discard)) { + std::cerr << "Error: Directory does not exist: " << config_path << "\n"; + exit(1); + } + + Config::addGameInstallDir(config_path); + Config::save(Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "config.toml"); + std::cout << "Game folder successfully saved.\n"; + exit(0); + }}, }; // Parse command-line arguments using the map @@ -106,6 +127,10 @@ int main(int argc, char* argv[]) { auto it = arg_map.find(cur_arg); if (it != arg_map.end()) { it->second(i); // Call the associated lambda function + } else if (i == argc - 1 && !has_game_argument) { + // Assume the last argument is the game file if not specified via -g/--game + game_path = argv[i]; + has_game_argument = true; } else { std::cerr << "Unknown argument: " << cur_arg << ", see --help for info.\n"; return 1; @@ -134,14 +159,14 @@ int main(int argc, char* argv[]) { // Process game path or ID if provided if (has_game_argument) { - std::filesystem::path game_file_path(gamePath); + std::filesystem::path game_file_path(game_path); // Check if the provided path is a valid file if (!std::filesystem::exists(game_file_path)) { // If not a file, treat it as a game ID and search in install directories bool game_found = false; for (const auto& install_dir : Config::getGameInstallDirs()) { - auto potential_game_path = install_dir / gamePath / "eboot.bin"; + auto potential_game_path = install_dir / game_path / "eboot.bin"; if (std::filesystem::exists(potential_game_path)) { game_file_path = potential_game_path; game_found = true; @@ -149,7 +174,7 @@ int main(int argc, char* argv[]) { } } if (!game_found) { - std::cerr << "Error: Game ID or file path not found: " << gamePath << std::endl; + std::cerr << "Error: Game ID or file path not found: " << game_path << std::endl; return 1; } } From e8b0fdd6cce138e83043843ad3289cf73684dcd2 Mon Sep 17 00:00:00 2001 From: tomboylover93 <95257311+tomboylover93@users.noreply.github.com> Date: Sat, 28 Dec 2024 12:09:33 -0300 Subject: [PATCH 291/549] style: add Tokyo Night theme (#1811) * style: add Tokyo Night theme * clang-format: Update main_window_themes.h --- src/qt_gui/main_window.cpp | 14 ++++++++++++++ src/qt_gui/main_window_themes.cpp | 22 ++++++++++++++++++++++ src/qt_gui/main_window_themes.h | 2 +- src/qt_gui/main_window_ui.h | 6 ++++++ 4 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 724e008ae..bd3c27809 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -112,6 +112,7 @@ void MainWindow::CreateActions() { m_theme_act_group->addAction(ui->setThemeBlue); m_theme_act_group->addAction(ui->setThemeViolet); m_theme_act_group->addAction(ui->setThemeGruvbox); + m_theme_act_group->addAction(ui->setThemeTokyoNight); } void MainWindow::AddUiWidgets() { @@ -559,6 +560,14 @@ void MainWindow::CreateConnects() { isIconBlack = false; } }); + connect(ui->setThemeTokyoNight, &QAction::triggered, &m_window_themes, [this]() { + m_window_themes.SetWindowTheme(Theme::TokyoNight, ui->mw_searchbar); + Config::setMainWindowTheme(static_cast(Theme::TokyoNight)); + if (isIconBlack) { + SetUiIcons(false); + isIconBlack = false; + } + }); } void MainWindow::StartGame() { @@ -934,6 +943,11 @@ void MainWindow::SetLastUsedTheme() { isIconBlack = false; SetUiIcons(false); break; + case Theme::TokyoNight: + ui->setThemeTokyoNight->setChecked(true); + isIconBlack = false; + SetUiIcons(false); + break; } } diff --git a/src/qt_gui/main_window_themes.cpp b/src/qt_gui/main_window_themes.cpp index a52b4466e..5fffd4c9e 100644 --- a/src/qt_gui/main_window_themes.cpp +++ b/src/qt_gui/main_window_themes.cpp @@ -143,5 +143,27 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { themePalette.setColor(QPalette::HighlightedText, Qt::black); qApp->setPalette(themePalette); break; + case Theme::TokyoNight: + mw_searchbar->setStyleSheet( + "QLineEdit {" + "background-color: #1a1b26; color: #9d7cd8; border: 1px solid #9d7cd8; " + "border-radius: 4px; padding: 5px; }" + "QLineEdit:focus {" + "border: 1px solid #7aa2f7; }"); + themePalette.setColor(QPalette::Window, QColor(31, 35, 53)); + themePalette.setColor(QPalette::WindowText, QColor(192, 202, 245)); + themePalette.setColor(QPalette::Base, QColor(25, 28, 39)); + themePalette.setColor(QPalette::AlternateBase, QColor(36, 40, 59)); + themePalette.setColor(QPalette::ToolTipBase, QColor(192, 202, 245)); + themePalette.setColor(QPalette::ToolTipText, QColor(192, 202, 245)); + themePalette.setColor(QPalette::Text, QColor(192, 202, 245)); + themePalette.setColor(QPalette::Button, QColor(30, 30, 41)); + themePalette.setColor(QPalette::ButtonText, QColor(192, 202, 245)); + themePalette.setColor(QPalette::BrightText, QColor(197, 59, 83)); + themePalette.setColor(QPalette::Link, QColor(79, 214, 190)); + themePalette.setColor(QPalette::Highlight, QColor(79, 214, 190)); + themePalette.setColor(QPalette::HighlightedText, Qt::black); + qApp->setPalette(themePalette); + break; } } \ No newline at end of file diff --git a/src/qt_gui/main_window_themes.h b/src/qt_gui/main_window_themes.h index d162da87b..0ec2cce58 100644 --- a/src/qt_gui/main_window_themes.h +++ b/src/qt_gui/main_window_themes.h @@ -7,7 +7,7 @@ #include #include -enum class Theme : int { Dark, Light, Green, Blue, Violet, Gruvbox }; +enum class Theme : int { Dark, Light, Green, Blue, Violet, Gruvbox, TokyoNight }; class WindowThemes : public QObject { Q_OBJECT diff --git a/src/qt_gui/main_window_ui.h b/src/qt_gui/main_window_ui.h index df64361fd..0d5038d7e 100644 --- a/src/qt_gui/main_window_ui.h +++ b/src/qt_gui/main_window_ui.h @@ -37,6 +37,7 @@ public: QAction* setThemeBlue; QAction* setThemeViolet; QAction* setThemeGruvbox; + QAction* setThemeTokyoNight; QWidget* centralWidget; QLineEdit* mw_searchbar; QPushButton* playButton; @@ -162,6 +163,9 @@ public: setThemeGruvbox = new QAction(MainWindow); setThemeGruvbox->setObjectName("setThemeGruvbox"); setThemeGruvbox->setCheckable(true); + setThemeTokyoNight = new QAction(MainWindow); + setThemeTokyoNight->setObjectName("setThemeTokyoNight"); + setThemeTokyoNight->setCheckable(true); centralWidget = new QWidget(MainWindow); centralWidget->setObjectName("centralWidget"); sizePolicy.setHeightForWidth(centralWidget->sizePolicy().hasHeightForWidth()); @@ -287,6 +291,7 @@ public: menuThemes->addAction(setThemeBlue); menuThemes->addAction(setThemeViolet); menuThemes->addAction(setThemeGruvbox); + menuThemes->addAction(setThemeTokyoNight); menuGame_List_Icons->addAction(setIconSizeTinyAct); menuGame_List_Icons->addAction(setIconSizeSmallAct); menuGame_List_Icons->addAction(setIconSizeMediumAct); @@ -374,6 +379,7 @@ public: setThemeBlue->setText(QCoreApplication::translate("MainWindow", "Blue", nullptr)); setThemeViolet->setText(QCoreApplication::translate("MainWindow", "Violet", nullptr)); setThemeGruvbox->setText("Gruvbox"); + setThemeTokyoNight->setText("Tokyo Night"); toolBar->setWindowTitle(QCoreApplication::translate("MainWindow", "toolBar", nullptr)); } // retranslateUi }; From 851995d4440b65f34b81995c638ce73b4ee92489 Mon Sep 17 00:00:00 2001 From: polybiusproxy <47796739+polybiusproxy@users.noreply.github.com> Date: Sat, 28 Dec 2024 17:33:40 +0100 Subject: [PATCH 292/549] libraries/fiber: implement context switching (#1950) --- src/core/libraries/fiber/fiber.cpp | 98 +++++++++++++++++++++++++++--- src/core/libraries/fiber/fiber.h | 2 + 2 files changed, 91 insertions(+), 9 deletions(-) diff --git a/src/core/libraries/fiber/fiber.cpp b/src/core/libraries/fiber/fiber.cpp index 7bb81b61e..6d3f546f2 100644 --- a/src/core/libraries/fiber/fiber.cpp +++ b/src/core/libraries/fiber/fiber.cpp @@ -41,6 +41,39 @@ void PS4_SYSV_ABI _sceFiberCheckStackOverflow(OrbisFiberContext* ctx) { } } +s32 PS4_SYSV_ABI _sceFiberAttachContext(OrbisFiber* fiber, void* addr_context, u64 size_context) { + if (size_context && size_context < ORBIS_FIBER_CONTEXT_MINIMUM_SIZE) { + return ORBIS_FIBER_ERROR_RANGE; + } + if (size_context & 15) { + return ORBIS_FIBER_ERROR_INVALID; + } + if (!addr_context || !size_context) { + return ORBIS_FIBER_ERROR_INVALID; + } + if (fiber->addr_context) { + return ORBIS_FIBER_ERROR_INVALID; + } + + fiber->addr_context = addr_context; + fiber->size_context = size_context; + fiber->context_start = addr_context; + fiber->context_end = reinterpret_cast(addr_context) + size_context; + + /* Apply signature to start of stack */ + *(u64*)addr_context = kFiberStackSignature; + + if (fiber->flags & FiberFlags::ContextSizeCheck) { + u64* stack_start = reinterpret_cast(fiber->context_start); + u64* stack_end = reinterpret_cast(fiber->context_end); + + u64* stack_ptr = stack_start + 1; + while (stack_ptr < stack_end) { + *stack_ptr++ = kFiberStackSizeCheck; + } + } +} + void PS4_SYSV_ABI _sceFiberSwitchToFiber(OrbisFiber* fiber, u64 arg_on_run_to, OrbisFiberContext* ctx) { OrbisFiberContext* fiber_ctx = fiber->context; @@ -62,8 +95,7 @@ void PS4_SYSV_ABI _sceFiberSwitchToFiber(OrbisFiber* fiber, u64 arg_on_run_to, data.entry = fiber->entry; data.arg_on_initialize = fiber->arg_on_initialize; data.arg_on_run_to = arg_on_run_to; - data.stack_addr = - reinterpret_cast(reinterpret_cast(fiber->addr_context) + fiber->size_context); + data.stack_addr = reinterpret_cast(fiber->addr_context) + fiber->size_context; if (fiber->flags & FiberFlags::SetFpuRegs) { data.fpucw = 0x037f; data.mxcsr = 0x9fc0; @@ -169,8 +201,7 @@ s32 PS4_SYSV_ABI sceFiberInitialize(OrbisFiber* fiber, const char* name, OrbisFi if (addr_context != nullptr) { fiber->context_start = addr_context; - fiber->context_end = - reinterpret_cast(reinterpret_cast(addr_context) + size_context); + fiber->context_end = reinterpret_cast(addr_context) + size_context; /* Apply signature to start of stack */ *(u64*)addr_context = kFiberStackSignature; @@ -221,11 +252,12 @@ s32 PS4_SYSV_ABI sceFiberFinalize(OrbisFiber* fiber) { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFiberRun(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_return) { +s32 PS4_SYSV_ABI sceFiberRunImpl(OrbisFiber* fiber, void* addr_context, u64 size_context, + u64 arg_on_run_to, u64* arg_on_return) { if (!fiber) { return ORBIS_FIBER_ERROR_NULL; } - if ((u64)fiber & 7) { + if ((u64)fiber & 7 || (u64)addr_context & 15) { return ORBIS_FIBER_ERROR_ALIGNMENT; } if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) { @@ -237,6 +269,14 @@ s32 PS4_SYSV_ABI sceFiberRun(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_r return ORBIS_FIBER_ERROR_PERMISSION; } + /* Caller wants to attach context and run. */ + if (addr_context != nullptr || size_context != 0) { + s32 res = _sceFiberAttachContext(fiber, addr_context, size_context); + if (res < 0) { + return res; + } + } + FiberState expected = FiberState::Idle; if (!fiber->state.compare_exchange_strong(expected, FiberState::Run)) { return ORBIS_FIBER_ERROR_STATE; @@ -288,11 +328,12 @@ s32 PS4_SYSV_ABI sceFiberRun(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_r return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFiberSwitch(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_run) { +s32 PS4_SYSV_ABI sceFiberSwitchImpl(OrbisFiber* fiber, void* addr_context, u64 size_context, + u64 arg_on_run_to, u64* arg_on_run) { if (!fiber) { return ORBIS_FIBER_ERROR_NULL; } - if ((u64)fiber & 7) { + if ((u64)fiber & 7 || (u64)addr_context & 15) { return ORBIS_FIBER_ERROR_ALIGNMENT; } if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) { @@ -304,6 +345,14 @@ s32 PS4_SYSV_ABI sceFiberSwitch(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_o return ORBIS_FIBER_ERROR_PERMISSION; } + /* Caller wants to attach context and switch. */ + if (addr_context != nullptr || size_context != 0) { + s32 res = _sceFiberAttachContext(fiber, addr_context, size_context); + if (res < 0) { + return res; + } + } + FiberState expected = FiberState::Idle; if (!fiber->state.compare_exchange_strong(expected, FiberState::Run)) { return ORBIS_FIBER_ERROR_STATE; @@ -462,9 +511,32 @@ s32 PS4_SYSV_ABI sceFiberRename(OrbisFiber* fiber, const char* name) { return ORBIS_OK; } +s32 PS4_SYSV_ABI sceFiberGetThreadFramePointerAddress(u64* addr_frame_pointer) { + if (!addr_frame_pointer) { + return ORBIS_FIBER_ERROR_NULL; + } + + OrbisFiberContext* g_ctx = GetFiberContext(); + if (!g_ctx) { + return ORBIS_FIBER_ERROR_PERMISSION; + } + + *addr_frame_pointer = g_ctx->rbp; + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFiberRun(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_return) { + return sceFiberRunImpl(fiber, nullptr, 0, arg_on_run_to, arg_on_return); +} + +s32 PS4_SYSV_ABI sceFiberSwitch(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_run) { + return sceFiberSwitchImpl(fiber, nullptr, 0, arg_on_run_to, arg_on_run); +} + void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("hVYD7Ou2pCQ", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberInitialize); - LIB_FUNCTION("7+OJIpko9RY", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberInitialize); + LIB_FUNCTION("7+OJIpko9RY", "libSceFiber", 1, "libSceFiber", 1, 1, + sceFiberInitialize); // _sceFiberInitializeWithInternalOptionImpl LIB_FUNCTION("asjUJJ+aa8s", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberOptParamInitialize); LIB_FUNCTION("JeNX5F-NzQU", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberFinalize); @@ -473,12 +545,20 @@ void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("p+zLIOg27zU", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberGetSelf); LIB_FUNCTION("B0ZX2hx9DMw", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberReturnToThread); + LIB_FUNCTION("avfGJ94g36Q", "libSceFiber", 1, "libSceFiber", 1, 1, + sceFiberRunImpl); // _sceFiberAttachContextAndRun + LIB_FUNCTION("ZqhZFuzKT6U", "libSceFiber", 1, "libSceFiber", 1, 1, + sceFiberSwitchImpl); // _sceFiberAttachContextAndSwitch + LIB_FUNCTION("uq2Y5BFz0PE", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberGetInfo); LIB_FUNCTION("Lcqty+QNWFc", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberStartContextSizeCheck); LIB_FUNCTION("Kj4nXMpnM8Y", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberStopContextSizeCheck); LIB_FUNCTION("JzyT91ucGDc", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberRename); + + LIB_FUNCTION("0dy4JtMUcMQ", "libSceFiber", 1, "libSceFiber", 1, 1, + sceFiberGetThreadFramePointerAddress); } } // namespace Libraries::Fiber diff --git a/src/core/libraries/fiber/fiber.h b/src/core/libraries/fiber/fiber.h index 3c4e3b70e..edcd9afe8 100644 --- a/src/core/libraries/fiber/fiber.h +++ b/src/core/libraries/fiber/fiber.h @@ -114,5 +114,7 @@ s32 PS4_SYSV_ABI sceFiberStopContextSizeCheck(void); s32 PS4_SYSV_ABI sceFiberRename(OrbisFiber* fiber, const char* name); +s32 PS4_SYSV_ABI sceFiberGetThreadFramePointerAddress(u64* addr_frame_pointer); + void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::Fiber \ No newline at end of file From ee974414d28b4da47fda57024987057c928e2872 Mon Sep 17 00:00:00 2001 From: polyproxy <47796739+polybiusproxy@users.noreply.github.com> Date: Sat, 28 Dec 2024 17:43:29 +0100 Subject: [PATCH 293/549] hotfix: fix fiber initialization --- src/common/elf_info.h | 1 + src/core/libraries/fiber/fiber.cpp | 29 ++++++++++++++++++++--------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/common/elf_info.h b/src/common/elf_info.h index 5a2c914e0..6eb144e9a 100644 --- a/src/common/elf_info.h +++ b/src/common/elf_info.h @@ -34,6 +34,7 @@ public: static constexpr u32 FW_20 = 0x2000000; static constexpr u32 FW_25 = 0x2500000; static constexpr u32 FW_30 = 0x3000000; + static constexpr u32 FW_35 = 0x3500000; static constexpr u32 FW_40 = 0x4000000; static constexpr u32 FW_45 = 0x4500000; static constexpr u32 FW_50 = 0x5000000; diff --git a/src/core/libraries/fiber/fiber.cpp b/src/core/libraries/fiber/fiber.cpp index 6d3f546f2..b77b5b5b6 100644 --- a/src/core/libraries/fiber/fiber.cpp +++ b/src/core/libraries/fiber/fiber.cpp @@ -3,6 +3,7 @@ #include "fiber.h" +#include "common/elf_info.h" #include "common/logging/log.h" #include "core/libraries/fiber/fiber_error.h" #include "core/libraries/libs.h" @@ -72,6 +73,8 @@ s32 PS4_SYSV_ABI _sceFiberAttachContext(OrbisFiber* fiber, void* addr_context, u *stack_ptr++ = kFiberStackSizeCheck; } } + + return ORBIS_OK; } void PS4_SYSV_ABI _sceFiberSwitchToFiber(OrbisFiber* fiber, u64 arg_on_run_to, @@ -143,9 +146,10 @@ void PS4_SYSV_ABI _sceFiberTerminate(OrbisFiber* fiber, u64 arg_on_return, Orbis __builtin_trap(); } -s32 PS4_SYSV_ABI sceFiberInitialize(OrbisFiber* fiber, const char* name, OrbisFiberEntry entry, - u64 arg_on_initialize, void* addr_context, u64 size_context, - const OrbisFiberOptParam* opt_param, u32 build_ver) { +s32 PS4_SYSV_ABI sceFiberInitializeImpl(OrbisFiber* fiber, const char* name, OrbisFiberEntry entry, + u64 arg_on_initialize, void* addr_context, u64 size_context, + const OrbisFiberOptParam* opt_param, u32 flags, + u32 build_ver) { if (!fiber || !name || !entry) { return ORBIS_FIBER_ERROR_NULL; } @@ -171,12 +175,12 @@ s32 PS4_SYSV_ABI sceFiberInitialize(OrbisFiber* fiber, const char* name, OrbisFi return ORBIS_FIBER_ERROR_INVALID; } - u32 flags = FiberFlags::None; - if (build_ver >= 0x3500000) { - flags |= FiberFlags::SetFpuRegs; + u32 user_flags = flags; + if (build_ver >= Common::ElfInfo::FW_35) { + user_flags |= FiberFlags::SetFpuRegs; } if (context_size_check) { - flags |= FiberFlags::ContextSizeCheck; + user_flags |= FiberFlags::ContextSizeCheck; } strncpy(fiber->name, name, ORBIS_FIBER_MAX_NAME_LENGTH); @@ -186,7 +190,7 @@ s32 PS4_SYSV_ABI sceFiberInitialize(OrbisFiber* fiber, const char* name, OrbisFi fiber->addr_context = addr_context; fiber->size_context = size_context; fiber->context = nullptr; - fiber->flags = flags; + fiber->flags = user_flags; /* A low stack area is problematic, as we can easily @@ -525,6 +529,13 @@ s32 PS4_SYSV_ABI sceFiberGetThreadFramePointerAddress(u64* addr_frame_pointer) { return ORBIS_OK; } +s32 PS4_SYSV_ABI sceFiberInitialize(OrbisFiber* fiber, const char* name, OrbisFiberEntry entry, + u64 arg_on_initialize, void* addr_context, u64 size_context, + const OrbisFiberOptParam* opt_param, u32 build_ver) { + return sceFiberInitializeImpl(fiber, name, entry, arg_on_initialize, addr_context, size_context, + opt_param, 0, build_ver); +} + s32 PS4_SYSV_ABI sceFiberRun(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_return) { return sceFiberRunImpl(fiber, nullptr, 0, arg_on_run_to, arg_on_return); } @@ -536,7 +547,7 @@ s32 PS4_SYSV_ABI sceFiberSwitch(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_o void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("hVYD7Ou2pCQ", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberInitialize); LIB_FUNCTION("7+OJIpko9RY", "libSceFiber", 1, "libSceFiber", 1, 1, - sceFiberInitialize); // _sceFiberInitializeWithInternalOptionImpl + sceFiberInitializeImpl); // _sceFiberInitializeWithInternalOptionImpl LIB_FUNCTION("asjUJJ+aa8s", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberOptParamInitialize); LIB_FUNCTION("JeNX5F-NzQU", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberFinalize); From ab7e794f23881c4fb704ceedcaad6133fc187a7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quang=20Ng=C3=B4?= Date: Sun, 29 Dec 2024 17:35:52 +0700 Subject: [PATCH 294/549] sdl: Limit minimum window size (#1966) --- src/sdl_window.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index 4b13844b8..50c3e93ee 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -92,6 +92,7 @@ WindowSDL::WindowSDL(s32 width_, s32 height_, Input::GameController* controller_ UNREACHABLE_MSG("Failed to create window handle: {}", SDL_GetError()); } + SDL_SetWindowMinimumSize(window, 640, 360); SDL_SetWindowFullscreen(window, Config::isFullscreenMode()); SDL_InitSubSystem(SDL_INIT_GAMEPAD); From 468d7ea80edd5c370e9de67f3a3f3b5b03b3ddf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quang=20Ng=C3=B4?= Date: Sun, 29 Dec 2024 17:36:16 +0700 Subject: [PATCH 295/549] config: Don't load config in the Emulator class (#1965) Allows overriding of configs in frontends. Fix set fullscreen not working when specified in the CLI. --- src/emulator.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/emulator.cpp b/src/emulator.cpp index 10d17a2db..dbe21a141 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -45,10 +45,6 @@ Frontend::WindowSDL* g_window = nullptr; namespace Core { Emulator::Emulator() { - // Read configuration file. - const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); - Config::load(config_dir / "config.toml"); - // Initialize NT API functions and set high priority #ifdef _WIN32 Common::NtApi::Initialize(); From 202c1046a139a413f6b37265c89a9ebc6c24ca97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quang=20Ng=C3=B4?= Date: Sun, 29 Dec 2024 17:36:29 +0700 Subject: [PATCH 296/549] Fix loading RenderDoc in offline mode for Linux (#1968) --- src/video_core/renderdoc.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/video_core/renderdoc.cpp b/src/video_core/renderdoc.cpp index 7e0994992..b082fd1ca 100644 --- a/src/video_core/renderdoc.cpp +++ b/src/video_core/renderdoc.cpp @@ -65,11 +65,18 @@ void LoadRenderDoc() { #else static constexpr const char RENDERDOC_LIB[] = "librenderdoc.so"; #endif - if (void* mod = dlopen(RENDERDOC_LIB, RTLD_NOW | RTLD_NOLOAD)) { - const auto RENDERDOC_GetAPI = - reinterpret_cast(dlsym(mod, "RENDERDOC_GetAPI")); - const s32 ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_6_0, (void**)&rdoc_api); - ASSERT(ret == 1); + // Check if we are running by RDoc GUI + void* mod = dlopen(RENDERDOC_LIB, RTLD_NOW | RTLD_NOLOAD); + if (!mod && Config::isRdocEnabled()) { + // If enabled in config, try to load RDoc runtime in offline mode + if ((mod = dlopen(RENDERDOC_LIB, RTLD_NOW))) { + const auto RENDERDOC_GetAPI = + reinterpret_cast(dlsym(mod, "RENDERDOC_GetAPI")); + const s32 ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_6_0, (void**)&rdoc_api); + ASSERT(ret == 1); + } else { + LOG_ERROR(Render, "Cannot load RenderDoc: {}", dlerror()); + } } #endif if (rdoc_api) { From da9e45b5828a9a7c54b10b6d41e346cde0e2d535 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sun, 29 Dec 2024 02:36:41 -0800 Subject: [PATCH 297/549] build: Update MoltenVK and fix missing add_dependencies for copy. (#1970) * build: Fix missing add_dependencies for MoltenVK copy target. * externals: Update MoltenVK --- CMakeLists.txt | 1 + externals/MoltenVK/MoltenVK | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cd3894719..d63bd1951 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -917,6 +917,7 @@ if (APPLE) DEPENDS ${MVK_DYLIB_SRC} COMMAND cmake -E copy ${MVK_DYLIB_SRC} ${MVK_DYLIB_DST}) add_custom_target(CopyMoltenVK DEPENDS ${MVK_DYLIB_DST}) + add_dependencies(CopyMoltenVK MoltenVK) add_dependencies(shadps4 CopyMoltenVK) set_property(TARGET shadps4 APPEND PROPERTY BUILD_RPATH "@executable_path/../Frameworks") else() diff --git a/externals/MoltenVK/MoltenVK b/externals/MoltenVK/MoltenVK index 5ad3ee5d2..9f0b616d9 160000 --- a/externals/MoltenVK/MoltenVK +++ b/externals/MoltenVK/MoltenVK @@ -1 +1 @@ -Subproject commit 5ad3ee5d2f84342950c3fe93dec97719574d1932 +Subproject commit 9f0b616d9e2c39464d2a859b79dbc655c4a30e7e From 62c47cb1b74c22812c566e7a2aeb2968e7fcb999 Mon Sep 17 00:00:00 2001 From: baggins183 Date: Sun, 29 Dec 2024 02:37:15 -0800 Subject: [PATCH 298/549] recompiler: handle reads of output variables in hull shaders (#1962) * Handle output control point reads in hull shader. Might need additional barriers * output storage class --- .../spirv/emit_spirv_context_get_set.cpp | 15 +++++----- .../backend/spirv/emit_spirv_instructions.h | 2 ++ .../frontend/translate/vector_memory.cpp | 4 --- src/shader_recompiler/ir/ir_emitter.cpp | 6 ++++ src/shader_recompiler/ir/ir_emitter.h | 3 ++ src/shader_recompiler/ir/opcodes.inc | 2 ++ .../ir/passes/hull_shader_transform.cpp | 28 ++++++++++++------- 7 files changed, 38 insertions(+), 22 deletions(-) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp index f3db6af56..4550440bb 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp @@ -217,14 +217,6 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, u32 comp, Id index) { const auto pointer{ ctx.OpAccessChain(component_ptr, ctx.tess_coord, ctx.ConstU32(component))}; return ctx.OpLoad(ctx.F32[1], pointer); - } else if (IR::IsParam(attr)) { - const u32 param_id{u32(attr) - u32(IR::Attribute::Param0)}; - const auto param = ctx.input_params.at(param_id).id; - const auto param_arr_ptr = ctx.TypePointer(spv::StorageClass::Input, ctx.F32[4]); - const auto pointer{ctx.OpAccessChain(param_arr_ptr, param, index)}; - const auto position_comp_ptr = ctx.TypePointer(spv::StorageClass::Input, ctx.F32[1]); - return ctx.OpLoad(ctx.F32[1], - ctx.OpAccessChain(position_comp_ptr, pointer, ctx.ConstU32(comp))); } UNREACHABLE(); } @@ -351,6 +343,13 @@ Id EmitGetTessGenericAttribute(EmitContext& ctx, Id vertex_index, Id attr_index, vertex_index, attr_index, comp_index)); } +Id EmitReadTcsGenericOuputAttribute(EmitContext& ctx, Id vertex_index, Id attr_index, + Id comp_index) { + const auto attr_comp_ptr = ctx.TypePointer(spv::StorageClass::Output, ctx.F32[1]); + return ctx.OpLoad(ctx.F32[1], ctx.OpAccessChain(attr_comp_ptr, ctx.output_attr_array, + vertex_index, attr_index, comp_index)); +} + void EmitSetTcsGenericAttribute(EmitContext& ctx, Id value, Id attr_index, Id comp_index) { // Implied vertex index is invocation_id const auto component_ptr = ctx.TypePointer(spv::StorageClass::Output, ctx.F32[1]); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index 85bed589b..d26cf6662 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h @@ -89,6 +89,8 @@ Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, u32 comp); void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, u32 comp); Id EmitGetTessGenericAttribute(EmitContext& ctx, Id vertex_index, Id attr_index, Id comp_index); void EmitSetTcsGenericAttribute(EmitContext& ctx, Id value, Id attr_index, Id comp_index); +Id EmitReadTcsGenericOuputAttribute(EmitContext& ctx, Id vertex_index, Id attr_index, + Id comp_index); Id EmitGetPatch(EmitContext& ctx, IR::Patch patch); void EmitSetPatch(EmitContext& ctx, IR::Patch patch, Id value); void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, Id value); diff --git a/src/shader_recompiler/frontend/translate/vector_memory.cpp b/src/shader_recompiler/frontend/translate/vector_memory.cpp index 7c3db9551..79d46cd42 100644 --- a/src/shader_recompiler/frontend/translate/vector_memory.cpp +++ b/src/shader_recompiler/frontend/translate/vector_memory.cpp @@ -255,10 +255,6 @@ void Translator::BUFFER_STORE(u32 num_dwords, bool is_typed, const GcnInst& inst "Non immediate offset not supported"); } - if (info.stage == Stage::Hull) { - // printf("here\n"); // break - } - IR::Value address = [&] -> IR::Value { if (is_ring) { return ir.CompositeConstruct(ir.GetVectorReg(vaddr), soffset); diff --git a/src/shader_recompiler/ir/ir_emitter.cpp b/src/shader_recompiler/ir/ir_emitter.cpp index c9d97679f..20e6eae0b 100644 --- a/src/shader_recompiler/ir/ir_emitter.cpp +++ b/src/shader_recompiler/ir/ir_emitter.cpp @@ -288,6 +288,12 @@ void IREmitter::SetTcsGenericAttribute(const F32& value, const U32& attr_index, Inst(Opcode::SetTcsGenericAttribute, value, attr_index, comp_index); } +F32 IREmitter::ReadTcsGenericOuputAttribute(const U32& vertex_index, const U32& attr_index, + const U32& comp_index) { + return Inst(IR::Opcode::ReadTcsGenericOuputAttribute, vertex_index, attr_index, + comp_index); +} + F32 IREmitter::GetPatch(Patch patch) { return Inst(Opcode::GetPatch, patch); } diff --git a/src/shader_recompiler/ir/ir_emitter.h b/src/shader_recompiler/ir/ir_emitter.h index 4679a0133..f65baee2a 100644 --- a/src/shader_recompiler/ir/ir_emitter.h +++ b/src/shader_recompiler/ir/ir_emitter.h @@ -90,6 +90,9 @@ public: const U32& comp_index); void SetTcsGenericAttribute(const F32& value, const U32& attr_index, const U32& comp_index); + [[nodiscard]] F32 ReadTcsGenericOuputAttribute(const U32& vertex_index, const U32& attr_index, + const U32& comp_index); + [[nodiscard]] F32 GetPatch(Patch patch); void SetPatch(Patch patch, const F32& value); diff --git a/src/shader_recompiler/ir/opcodes.inc b/src/shader_recompiler/ir/opcodes.inc index cf2c3b67e..1194c3792 100644 --- a/src/shader_recompiler/ir/opcodes.inc +++ b/src/shader_recompiler/ir/opcodes.inc @@ -64,6 +64,8 @@ OPCODE(GetPatch, F32, Patc OPCODE(SetPatch, Void, Patch, F32, ) OPCODE(GetTessGenericAttribute, F32, U32, U32, U32, ) OPCODE(SetTcsGenericAttribute, Void, F32, U32, U32, ) +OPCODE(ReadTcsGenericOuputAttribute, F32, U32, U32, U32, ) + // Flags OPCODE(GetScc, U1, Void, ) diff --git a/src/shader_recompiler/ir/passes/hull_shader_transform.cpp b/src/shader_recompiler/ir/passes/hull_shader_transform.cpp index 895c9823e..6164fec12 100644 --- a/src/shader_recompiler/ir/passes/hull_shader_transform.cpp +++ b/src/shader_recompiler/ir/passes/hull_shader_transform.cpp @@ -343,8 +343,8 @@ static IR::U32 TryOptimizeAddressModulo(IR::U32 addr, u32 stride, IR::IREmitter& // TODO: can optimize div in control point index similarly to mod // Read a TCS input (InputCP region) or TES input (OutputCP region) -static IR::F32 ReadTessInputComponent(IR::U32 addr, const u32 stride, IR::IREmitter& ir, - u32 off_dw) { +static IR::F32 ReadTessControlPointAttribute(IR::U32 addr, const u32 stride, IR::IREmitter& ir, + u32 off_dw, bool is_output_read_in_tcs) { if (off_dw > 0) { addr = ir.IAdd(addr, ir.Imm32(off_dw)); } @@ -354,7 +354,11 @@ static IR::F32 ReadTessInputComponent(IR::U32 addr, const u32 stride, IR::IREmit ir.ShiftRightLogical(ir.IMod(addr_for_attrs, ir.Imm32(stride)), ir.Imm32(4u)); const IR::U32 comp_index = ir.ShiftRightLogical(ir.BitwiseAnd(addr_for_attrs, ir.Imm32(0xFU)), ir.Imm32(2u)); - return ir.GetTessGenericAttribute(control_point_index, attr_index, comp_index); + if (is_output_read_in_tcs) { + return ir.ReadTcsGenericOuputAttribute(control_point_index, attr_index, comp_index); + } else { + return ir.GetTessGenericAttribute(control_point_index, attr_index, comp_index); + } } } // namespace @@ -481,21 +485,25 @@ void HullShaderTransform(IR::Program& program, RuntimeInfo& runtime_info) { case IR::Opcode::LoadSharedU128: IR::IREmitter ir{*block, IR::Block::InstructionList::s_iterator_to(inst)}; const IR::U32 addr{inst.Arg(0)}; - AttributeRegion region = GetAttributeRegionKind(&inst, info, runtime_info); + const AttributeRegion region = GetAttributeRegionKind(&inst, info, runtime_info); const u32 num_dwords = opcode == IR::Opcode::LoadSharedU32 ? 1 : (opcode == IR::Opcode::LoadSharedU64 ? 2 : 4); - ASSERT_MSG(region == AttributeRegion::InputCP, - "Unhandled read of output or patchconst attribute in hull shader"); + ASSERT_MSG(region == AttributeRegion::InputCP || + region == AttributeRegion::OutputCP, + "Unhandled read of patchconst attribute in hull shader"); + const bool is_tcs_output_read = region == AttributeRegion::OutputCP; + const u32 stride = is_tcs_output_read ? runtime_info.hs_info.hs_output_cp_stride + : runtime_info.hs_info.ls_stride; IR::Value attr_read; if (num_dwords == 1) { attr_read = ir.BitCast( - ReadTessInputComponent(addr, runtime_info.hs_info.ls_stride, ir, 0)); + ReadTessControlPointAttribute(addr, stride, ir, 0, is_tcs_output_read)); } else { boost::container::static_vector read_components; for (auto i = 0; i < num_dwords; i++) { const IR::F32 component = - ReadTessInputComponent(addr, runtime_info.hs_info.ls_stride, ir, i); + ReadTessControlPointAttribute(addr, stride, ir, i, is_tcs_output_read); read_components.push_back(ir.BitCast(component)); } attr_read = ir.CompositeConstruct(read_components); @@ -565,8 +573,8 @@ void DomainShaderTransform(IR::Program& program, RuntimeInfo& runtime_info) { : (opcode == IR::Opcode::LoadSharedU64 ? 2 : 4); const auto GetInput = [&](IR::U32 addr, u32 off_dw) -> IR::F32 { if (region == AttributeRegion::OutputCP) { - return ReadTessInputComponent( - addr, runtime_info.vs_info.hs_output_cp_stride, ir, off_dw); + return ReadTessControlPointAttribute( + addr, runtime_info.vs_info.hs_output_cp_stride, ir, off_dw, false); } else { ASSERT(region == AttributeRegion::PatchConst); return ir.GetPatch(IR::PatchGeneric((addr.U32() >> 2) + off_dw)); From 248220fef333557f6a59450072da9555f68e9109 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sun, 29 Dec 2024 02:37:37 -0800 Subject: [PATCH 299/549] pthread: Change minimum stack for HLE to additional stack. (#1960) --- src/core/libraries/kernel/threads/pthread.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/core/libraries/kernel/threads/pthread.cpp b/src/core/libraries/kernel/threads/pthread.cpp index 761d13346..e81207a0d 100644 --- a/src/core/libraries/kernel/threads/pthread.cpp +++ b/src/core/libraries/kernel/threads/pthread.cpp @@ -244,10 +244,9 @@ int PS4_SYSV_ABI posix_pthread_create_name_np(PthreadT* thread, const PthreadAtt new_thread->tid = ++TidCounter; if (new_thread->attr.stackaddr_attr == 0) { - /* Enforce minimum stack size of 128 KB */ - static constexpr size_t MinimumStack = 128_KB; - auto& stacksize = new_thread->attr.stacksize_attr; - stacksize = std::max(stacksize, MinimumStack); + /* Add additional stack space for HLE */ + static constexpr size_t AdditionalStack = 128_KB; + new_thread->attr.stacksize_attr += AdditionalStack; } if (thread_state->CreateStack(&new_thread->attr) != 0) { From 4b2db61120b893840baeedf1e03500e4d32b1699 Mon Sep 17 00:00:00 2001 From: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> Date: Sun, 29 Dec 2024 12:45:18 +0200 Subject: [PATCH 300/549] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 30 ++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..02017dddf --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,30 @@ +--- +name: Bug report +about: Report a bug in the emulator +title: '' +labels: '' +assignees: '' + +--- + +Checklist: +[ ] I have searched for a similar issue in this repository and did not find one. +[ ] I have asked for support on shadPS4 discord server. +[ ] I am using an official build obtained from [releases](https://github.com/shadps4-emu/shadPS4/releases) or updated it using its in-app updater. +[ ] I have re-dumped the game and performed a clean install without mods. +[ ] I have disabled all patches and cheats. + +Description: + + +Steps To Reproduce: + + +Logs: + + +System Information: + + +Additional Information: + From f8177902a5e4a2f99bd166b9d9f85f468dfce7d5 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sun, 29 Dec 2024 02:46:59 -0800 Subject: [PATCH 301/549] cubeb_audio: Make sure COM is initialized on Windows. (#1958) --- src/core/libraries/audio/audioout_backend.h | 3 +++ src/core/libraries/audio/cubeb_audio.cpp | 23 +++++++++++++++------ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/core/libraries/audio/audioout_backend.h b/src/core/libraries/audio/audioout_backend.h index ecc4cf7c6..f423d4963 100644 --- a/src/core/libraries/audio/audioout_backend.h +++ b/src/core/libraries/audio/audioout_backend.h @@ -34,6 +34,9 @@ public: private: cubeb* ctx = nullptr; +#ifdef _WIN32 + bool owns_com = false; +#endif }; class SDLAudioOut final : public AudioOutBackend { diff --git a/src/core/libraries/audio/cubeb_audio.cpp b/src/core/libraries/audio/cubeb_audio.cpp index e1195558a..4127931b7 100644 --- a/src/core/libraries/audio/cubeb_audio.cpp +++ b/src/core/libraries/audio/cubeb_audio.cpp @@ -10,9 +10,11 @@ #include "core/libraries/audio/audioout.h" #include "core/libraries/audio/audioout_backend.h" -namespace Libraries::AudioOut { +#ifdef _WIN32 +#include +#endif -constexpr int AUDIO_STREAM_BUFFER_THRESHOLD = 65536; // Define constant for buffer threshold +namespace Libraries::AudioOut { class CubebPortBackend : public PortBackend { public: @@ -143,17 +145,26 @@ private: }; CubebAudioOut::CubebAudioOut() { +#ifdef _WIN32 + // Need to initialize COM for this thread on Windows, in case WASAPI backend is used. + owns_com = CoInitializeEx(nullptr, COINIT_MULTITHREADED) == S_OK; +#endif if (const auto ret = cubeb_init(&ctx, "shadPS4", nullptr); ret != CUBEB_OK) { LOG_CRITICAL(Lib_AudioOut, "Failed to create cubeb context: {}", ret); } } CubebAudioOut::~CubebAudioOut() { - if (!ctx) { - return; + if (ctx) { + cubeb_destroy(ctx); + ctx = nullptr; } - cubeb_destroy(ctx); - ctx = nullptr; +#ifdef _WIN32 + if (owns_com) { + CoUninitialize(); + owns_com = false; + } +#endif } std::unique_ptr CubebAudioOut::Open(PortOut& port) { From e952013fe06b6d040d3be091e3c7e50e3456770f Mon Sep 17 00:00:00 2001 From: Mahmoud Adel <94652220+AboMedoz@users.noreply.github.com> Date: Sun, 29 Dec 2024 12:47:15 +0200 Subject: [PATCH 302/549] add EventWrite and DispatchIndirect to ProcessCompute (#1948) * add EventWrite and DispatchIndirect to ProcessCompute helps Alienation go Ingame * apply review changes Co-authored-by: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> --------- Co-authored-by: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> --- src/video_core/amdgpu/liverpool.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index 5dd3edd6d..43adff8d2 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -821,6 +821,24 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, u32 vqid) { } break; } + case PM4ItOpcode::DispatchIndirect: { + const auto* dispatch_indirect = reinterpret_cast(header); + auto& cs_program = GetCsRegs(); + const auto offset = dispatch_indirect->data_offset; + const auto ib_address = mapped_queues[GfxQueueId].indirect_args_addr; + const auto size = sizeof(PM4CmdDispatchIndirect::GroupDimensions); + if (DebugState.DumpingCurrentReg()) { + DebugState.PushRegsDumpCompute(base_addr, reinterpret_cast(header), + cs_program); + } + if (rasterizer && (cs_program.dispatch_initiator & 1)) { + const auto cmd_address = reinterpret_cast(header); + rasterizer->ScopeMarkerBegin(fmt::format("acb:{}:DispatchIndirect", cmd_address)); + rasterizer->DispatchIndirect(ib_address, offset, size); + rasterizer->ScopeMarkerEnd(); + } + break; + } case PM4ItOpcode::WriteData: { const auto* write_data = reinterpret_cast(header); ASSERT(write_data->dst_sel.Value() == 2 || write_data->dst_sel.Value() == 5); @@ -845,6 +863,10 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, u32 vqid) { release_mem->SignalFence(static_cast(queue.pipe_id)); break; } + case PM4ItOpcode::EventWrite: { + // const auto* event = reinterpret_cast(header); + break; + } default: UNREACHABLE_MSG("Unknown PM4 type 3 opcode {:#x} with count {}", static_cast(opcode), count); From f09a95453ee875e0a6492343f7a27ba91525ab23 Mon Sep 17 00:00:00 2001 From: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> Date: Sun, 29 Dec 2024 12:48:45 +0200 Subject: [PATCH 303/549] hot-fix: Correct queue id in dispatch indirect I missed this --- src/video_core/amdgpu/liverpool.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index 43adff8d2..2926bcc69 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -825,7 +825,7 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, u32 vqid) { const auto* dispatch_indirect = reinterpret_cast(header); auto& cs_program = GetCsRegs(); const auto offset = dispatch_indirect->data_offset; - const auto ib_address = mapped_queues[GfxQueueId].indirect_args_addr; + const auto ib_address = mapped_queues[vqid].indirect_args_addr; const auto size = sizeof(PM4CmdDispatchIndirect::GroupDimensions); if (DebugState.DumpingCurrentReg()) { DebugState.PushRegsDumpCompute(base_addr, reinterpret_cast(header), @@ -833,7 +833,7 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, u32 vqid) { } if (rasterizer && (cs_program.dispatch_initiator & 1)) { const auto cmd_address = reinterpret_cast(header); - rasterizer->ScopeMarkerBegin(fmt::format("acb:{}:DispatchIndirect", cmd_address)); + rasterizer->ScopeMarkerBegin(fmt::format("acb[{}]:{}:Dispatch", vqid, cmd_address)); rasterizer->DispatchIndirect(ib_address, offset, size); rasterizer->ScopeMarkerEnd(); } From ee72d99947382dac8f4fbd56f1915a3eb6b65cde Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Sun, 29 Dec 2024 13:53:06 +0300 Subject: [PATCH 304/549] ajm: added stubbed statistics instance (#1924) * ajm: added stubbed statistics instance * fixed a typo, thanks poly * fixed clang-format * removed unused struct * small fixes * fixed typedefs, added per codec statistics --- CMakeLists.txt | 2 + src/core/libraries/ajm/ajm.cpp | 10 +- src/core/libraries/ajm/ajm.h | 49 ++++++- src/core/libraries/ajm/ajm_batch.cpp | 129 ++++++++++++++---- src/core/libraries/ajm/ajm_batch.h | 4 + src/core/libraries/ajm/ajm_context.cpp | 21 +-- src/core/libraries/ajm/ajm_instance.cpp | 4 +- .../libraries/ajm/ajm_instance_statistics.cpp | 37 +++++ .../libraries/ajm/ajm_instance_statistics.h | 17 +++ 9 files changed, 231 insertions(+), 42 deletions(-) create mode 100644 src/core/libraries/ajm/ajm_instance_statistics.cpp create mode 100644 src/core/libraries/ajm/ajm_instance_statistics.h diff --git a/CMakeLists.txt b/CMakeLists.txt index d63bd1951..af811e9fb 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -189,6 +189,8 @@ set(AJM_LIB src/core/libraries/ajm/ajm.cpp src/core/libraries/ajm/ajm_context.cpp src/core/libraries/ajm/ajm_context.h src/core/libraries/ajm/ajm_error.h + src/core/libraries/ajm/ajm_instance_statistics.cpp + src/core/libraries/ajm/ajm_instance_statistics.h src/core/libraries/ajm/ajm_instance.cpp src/core/libraries/ajm/ajm_instance.h src/core/libraries/ajm/ajm_mp3.cpp diff --git a/src/core/libraries/ajm/ajm.cpp b/src/core/libraries/ajm/ajm.cpp index 3184fa64f..5c55d2c06 100644 --- a/src/core/libraries/ajm/ajm.cpp +++ b/src/core/libraries/ajm/ajm.cpp @@ -183,13 +183,15 @@ int PS4_SYSV_ABI sceAjmInstanceSwitch() { return ORBIS_OK; } -int PS4_SYSV_ABI sceAjmMemoryRegister() { - LOG_ERROR(Lib_Ajm, "(STUBBED) called"); +int PS4_SYSV_ABI sceAjmMemoryRegister(u32 context_id, void* ptr, size_t num_pages) { + // All memory is already shared with our implementation since we do not use any hardware. + LOG_TRACE(Lib_Ajm, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceAjmMemoryUnregister() { - LOG_ERROR(Lib_Ajm, "(STUBBED) called"); +int PS4_SYSV_ABI sceAjmMemoryUnregister(u32 context_id, void* ptr) { + // All memory is already shared with our implementation since we do not use any hardware. + LOG_TRACE(Lib_Ajm, "(STUBBED) called"); return ORBIS_OK; } diff --git a/src/core/libraries/ajm/ajm.h b/src/core/libraries/ajm/ajm.h index 1ac7c7629..34aeb9aa4 100644 --- a/src/core/libraries/ajm/ajm.h +++ b/src/core/libraries/ajm/ajm.h @@ -74,6 +74,26 @@ union AjmJobFlags { }; }; +enum class AjmStatisticsFlags : u64 { + Memory = 1 << 0, + EnginePerCodec = 1 << 15, + Engine = 1 << 16, +}; +DECLARE_ENUM_FLAG_OPERATORS(AjmStatisticsFlags) + +union AjmStatisticsJobFlags { + AjmStatisticsJobFlags(AjmJobFlags job_flags) : raw(job_flags.raw) {} + + u64 raw; + struct { + u64 version : 3; + u64 : 12; + AjmStatisticsFlags statistics_flags : 17; + u64 : 32; + }; +}; +static_assert(sizeof(AjmStatisticsJobFlags) == 8); + struct AjmSidebandResult { s32 result; s32 internal_result; @@ -126,6 +146,31 @@ union AjmSidebandInitParameters { u8 reserved[8]; }; +struct AjmSidebandStatisticsEngine { + float usage_batch; + float usage_interval[3]; +}; + +struct AjmSidebandStatisticsEnginePerCodec { + u8 codec_count; + u8 codec_id[3]; + float codec_percentage[3]; +}; + +struct AjmSidebandStatisticsMemory { + u32 instance_free; + u32 buffer_free; + u32 batch_size; + u32 input_size; + u32 output_size; + u32 small_size; +}; + +struct AjmSidebandStatisticsEngineParameters { + u32 interval_count; + float interval[3]; +}; + union AjmInstanceFlags { u64 raw; struct { @@ -178,8 +223,8 @@ int PS4_SYSV_ABI sceAjmInstanceCreate(u32 context, AjmCodecType codec_type, AjmI int PS4_SYSV_ABI sceAjmInstanceDestroy(u32 context, u32 instance); int PS4_SYSV_ABI sceAjmInstanceExtend(); int PS4_SYSV_ABI sceAjmInstanceSwitch(); -int PS4_SYSV_ABI sceAjmMemoryRegister(); -int PS4_SYSV_ABI sceAjmMemoryUnregister(); +int PS4_SYSV_ABI sceAjmMemoryRegister(u32 context_id, void* ptr, size_t num_pages); +int PS4_SYSV_ABI sceAjmMemoryUnregister(u32 context_id, void* ptr); int PS4_SYSV_ABI sceAjmModuleRegister(u32 context, AjmCodecType codec_type, s64 reserved); int PS4_SYSV_ABI sceAjmModuleUnregister(); int PS4_SYSV_ABI sceAjmStrError(); diff --git a/src/core/libraries/ajm/ajm_batch.cpp b/src/core/libraries/ajm/ajm_batch.cpp index b1cec88b3..30e1deb71 100644 --- a/src/core/libraries/ajm/ajm_batch.cpp +++ b/src/core/libraries/ajm/ajm_batch.cpp @@ -54,6 +54,8 @@ public: : m_p_begin(begin), m_p_current(m_p_begin), m_size(size) {} AjmBatchBuffer(std::span data) : m_p_begin(data.data()), m_p_current(m_p_begin), m_size(data.size()) {} + AjmBatchBuffer(AjmChunkBuffer& buffer) + : AjmBatchBuffer(reinterpret_cast(buffer.p_address), buffer.size) {} AjmBatchBuffer SubBuffer(size_t size = s_dynamic_extent) { auto current = m_p_current; @@ -113,6 +115,88 @@ private: size_t m_size{}; }; +AjmJob AjmStatisticsJobFromBatchBuffer(u32 instance_id, AjmBatchBuffer batch_buffer) { + std::optional job_flags = {}; + std::optional input_control_buffer = {}; + std::optional output_control_buffer = {}; + + AjmJob job; + job.instance_id = instance_id; + + while (!batch_buffer.IsEmpty()) { + auto& header = batch_buffer.Peek(); + switch (header.ident) { + case Identifier::AjmIdentInputControlBuf: { + ASSERT_MSG(!input_control_buffer.has_value(), + "Only one instance of input control buffer is allowed per job"); + const auto& buffer = batch_buffer.Consume(); + if (buffer.p_address != nullptr && buffer.size != 0) { + input_control_buffer = buffer; + } + break; + } + case Identifier::AjmIdentControlFlags: { + ASSERT_MSG(!job_flags.has_value(), "Only one instance of job flags is allowed per job"); + auto& chunk = batch_buffer.Consume(); + job_flags = AjmJobFlags{ + .raw = (u64(chunk.header.payload) << 32) + chunk.flags_low, + }; + break; + } + case Identifier::AjmIdentReturnAddressBuf: { + // Ignore return address buffers. + batch_buffer.Skip(); + break; + } + case Identifier::AjmIdentOutputControlBuf: { + ASSERT_MSG(!output_control_buffer.has_value(), + "Only one instance of output control buffer is allowed per job"); + const auto& buffer = batch_buffer.Consume(); + if (buffer.p_address != nullptr && buffer.size != 0) { + output_control_buffer = buffer; + } + break; + } + default: + UNREACHABLE_MSG("Unknown chunk: {}", header.ident); + } + } + + ASSERT(job_flags.has_value()); + job.flags = job_flags.value(); + + AjmStatisticsJobFlags flags(job.flags); + if (input_control_buffer.has_value()) { + AjmBatchBuffer input_batch(input_control_buffer.value()); + if (True(flags.statistics_flags & AjmStatisticsFlags::Engine)) { + job.input.statistics_engine_parameters = + input_batch.Consume(); + } + } + + if (output_control_buffer.has_value()) { + AjmBatchBuffer output_batch(output_control_buffer.value()); + job.output.p_result = &output_batch.Consume(); + *job.output.p_result = AjmSidebandResult{}; + + if (True(flags.statistics_flags & AjmStatisticsFlags::Engine)) { + job.output.p_engine = &output_batch.Consume(); + *job.output.p_engine = AjmSidebandStatisticsEngine{}; + } + if (True(flags.statistics_flags & AjmStatisticsFlags::EnginePerCodec)) { + job.output.p_engine_per_codec = + &output_batch.Consume(); + *job.output.p_engine_per_codec = AjmSidebandStatisticsEnginePerCodec{}; + } + if (True(flags.statistics_flags & AjmStatisticsFlags::Memory)) { + job.output.p_memory = &output_batch.Consume(); + *job.output.p_memory = AjmSidebandStatisticsMemory{}; + } + } + + return job; +} + AjmJob AjmJobFromBatchBuffer(u32 instance_id, AjmBatchBuffer batch_buffer) { std::optional job_flags = {}; std::optional input_control_buffer = {}; @@ -155,15 +239,6 @@ AjmJob AjmJobFromBatchBuffer(u32 instance_id, AjmBatchBuffer batch_buffer) { batch_buffer.Skip(); break; } - case Identifier::AjmIdentInlineBuf: { - ASSERT_MSG(!output_control_buffer.has_value(), - "Only one instance of inline buffer is allowed per job"); - const auto& buffer = batch_buffer.Consume(); - if (buffer.p_address != nullptr && buffer.size != 0) { - inline_buffer = buffer; - } - break; - } case Identifier::AjmIdentOutputRunBuf: { auto& buffer = batch_buffer.Consume(); u8* p_begin = reinterpret_cast(buffer.p_address); @@ -186,13 +261,12 @@ AjmJob AjmJobFromBatchBuffer(u32 instance_id, AjmBatchBuffer batch_buffer) { } } + ASSERT(job_flags.has_value()); job.flags = job_flags.value(); // Initialize sideband input parameters if (input_control_buffer.has_value()) { - AjmBatchBuffer input_batch(reinterpret_cast(input_control_buffer->p_address), - input_control_buffer->size); - + AjmBatchBuffer input_batch(input_control_buffer.value()); const auto sideband_flags = job_flags->sideband_flags; if (True(sideband_flags & AjmJobSidebandFlags::Format) && !input_batch.IsEmpty()) { job.input.format = input_batch.Consume(); @@ -202,6 +276,9 @@ AjmJob AjmJobFromBatchBuffer(u32 instance_id, AjmBatchBuffer batch_buffer) { } const auto control_flags = job_flags.value().control_flags; + if (True(control_flags & AjmJobControlFlags::Resample)) { + job.input.resample_parameters = input_batch.Consume(); + } if (True(control_flags & AjmJobControlFlags::Initialize)) { job.input.init_params = AjmDecAt9InitializeParameters{}; std::memcpy(&job.input.init_params.value(), input_batch.GetCurrent(), @@ -209,21 +286,9 @@ AjmJob AjmJobFromBatchBuffer(u32 instance_id, AjmBatchBuffer batch_buffer) { } } - if (inline_buffer.has_value()) { - AjmBatchBuffer inline_batch(reinterpret_cast(inline_buffer->p_address), - inline_buffer->size); - - const auto control_flags = job_flags.value().control_flags; - if (True(control_flags & AjmJobControlFlags::Resample)) { - job.input.resample_parameters = inline_batch.Consume(); - } - } - // Initialize sideband output parameters if (output_control_buffer.has_value()) { - AjmBatchBuffer output_batch(reinterpret_cast(output_control_buffer->p_address), - output_control_buffer->size); - + AjmBatchBuffer output_batch(output_control_buffer.value()); job.output.p_result = &output_batch.Consume(); *job.output.p_result = AjmSidebandResult{}; @@ -260,9 +325,21 @@ std::shared_ptr AjmBatch::FromBatchBuffer(std::span data) { AjmBatchBuffer buffer(data); while (!buffer.IsEmpty()) { auto& job_chunk = buffer.Consume(); + if (job_chunk.header.ident == AjmIdentInlineBuf) { + // Inline buffers are used to store sideband input data. + // We should just skip them as they do not require any special handling. + buffer.Advance(job_chunk.size); + continue; + } ASSERT(job_chunk.header.ident == AjmIdentJob); auto instance_id = job_chunk.header.payload; - batch->jobs.push_back(AjmJobFromBatchBuffer(instance_id, buffer.SubBuffer(job_chunk.size))); + if (instance_id == AJM_INSTANCE_STATISTICS) { + batch->jobs.push_back( + AjmStatisticsJobFromBatchBuffer(instance_id, buffer.SubBuffer(job_chunk.size))); + } else { + batch->jobs.push_back( + AjmJobFromBatchBuffer(instance_id, buffer.SubBuffer(job_chunk.size))); + } } return batch; diff --git a/src/core/libraries/ajm/ajm_batch.h b/src/core/libraries/ajm/ajm_batch.h index 3c586b773..09daa630d 100644 --- a/src/core/libraries/ajm/ajm_batch.h +++ b/src/core/libraries/ajm/ajm_batch.h @@ -23,6 +23,7 @@ struct AjmJob { struct Input { std::optional init_params; std::optional resample_parameters; + std::optional statistics_engine_parameters; std::optional format; std::optional gapless_decode; std::vector buffer; @@ -33,6 +34,9 @@ struct AjmJob { AjmSidebandResult* p_result = nullptr; AjmSidebandStream* p_stream = nullptr; AjmSidebandFormat* p_format = nullptr; + AjmSidebandStatisticsMemory* p_memory = nullptr; + AjmSidebandStatisticsEnginePerCodec* p_engine_per_codec = nullptr; + AjmSidebandStatisticsEngine* p_engine = nullptr; AjmSidebandGaplessDecode* p_gapless_decode = nullptr; AjmSidebandMFrame* p_mframe = nullptr; u8* p_codec_info = nullptr; diff --git a/src/core/libraries/ajm/ajm_context.cpp b/src/core/libraries/ajm/ajm_context.cpp index 09255110c..8992dd83b 100644 --- a/src/core/libraries/ajm/ajm_context.cpp +++ b/src/core/libraries/ajm/ajm_context.cpp @@ -9,6 +9,7 @@ #include "core/libraries/ajm/ajm_context.h" #include "core/libraries/ajm/ajm_error.h" #include "core/libraries/ajm/ajm_instance.h" +#include "core/libraries/ajm/ajm_instance_statistics.h" #include "core/libraries/ajm/ajm_mp3.h" #include "core/libraries/error_codes.h" @@ -70,15 +71,19 @@ void AjmContext::ProcessBatch(u32 id, std::span jobs) { LOG_TRACE(Lib_Ajm, "Processing job {} for instance {}. flags = {:#x}", id, job.instance_id, job.flags.raw); - std::shared_ptr instance; - { - std::shared_lock lock(instances_mutex); - auto* p_instance = instances.Get(job.instance_id); - ASSERT_MSG(p_instance != nullptr, "Attempting to execute job on null instance"); - instance = *p_instance; - } + if (job.instance_id == AJM_INSTANCE_STATISTICS) { + AjmInstanceStatistics::Getinstance().ExecuteJob(job); + } else { + std::shared_ptr instance; + { + std::shared_lock lock(instances_mutex); + auto* p_instance = instances.Get(job.instance_id); + ASSERT_MSG(p_instance != nullptr, "Attempting to execute job on null instance"); + instance = *p_instance; + } - instance->ExecuteJob(job); + instance->ExecuteJob(job); + } } } diff --git a/src/core/libraries/ajm/ajm_instance.cpp b/src/core/libraries/ajm/ajm_instance.cpp index ea7fd5617..8af105c77 100644 --- a/src/core/libraries/ajm/ajm_instance.cpp +++ b/src/core/libraries/ajm/ajm_instance.cpp @@ -68,11 +68,11 @@ void AjmInstance::ExecuteJob(AjmJob& job) { m_codec->Initialize(¶ms, sizeof(params)); } if (job.input.resample_parameters.has_value()) { - UNREACHABLE_MSG("Unimplemented: resample parameters"); + LOG_ERROR(Lib_Ajm, "Unimplemented: resample parameters"); m_resample_parameters = job.input.resample_parameters.value(); } if (job.input.format.has_value()) { - UNREACHABLE_MSG("Unimplemented: format parameters"); + LOG_ERROR(Lib_Ajm, "Unimplemented: format parameters"); m_format = job.input.format.value(); } if (job.input.gapless_decode.has_value()) { diff --git a/src/core/libraries/ajm/ajm_instance_statistics.cpp b/src/core/libraries/ajm/ajm_instance_statistics.cpp new file mode 100644 index 000000000..c0c1af8bb --- /dev/null +++ b/src/core/libraries/ajm/ajm_instance_statistics.cpp @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/libraries/ajm/ajm.h" +#include "core/libraries/ajm/ajm_instance_statistics.h" + +namespace Libraries::Ajm { + +void AjmInstanceStatistics::ExecuteJob(AjmJob& job) { + if (job.output.p_engine) { + job.output.p_engine->usage_batch = 0.01; + const auto ic = job.input.statistics_engine_parameters->interval_count; + for (u32 idx = 0; idx < ic; ++idx) { + job.output.p_engine->usage_interval[idx] = 0.01; + } + } + if (job.output.p_engine_per_codec) { + job.output.p_engine_per_codec->codec_count = 1; + job.output.p_engine_per_codec->codec_id[0] = static_cast(AjmCodecType::At9Dec); + job.output.p_engine_per_codec->codec_percentage[0] = 0.01; + } + if (job.output.p_memory) { + job.output.p_memory->instance_free = 0x400000; + job.output.p_memory->buffer_free = 0x400000; + job.output.p_memory->batch_size = 0x4200; + job.output.p_memory->input_size = 0x2000; + job.output.p_memory->output_size = 0x2000; + job.output.p_memory->small_size = 0x200; + } +} + +AjmInstanceStatistics& AjmInstanceStatistics::Getinstance() { + static AjmInstanceStatistics instance; + return instance; +} + +} // namespace Libraries::Ajm diff --git a/src/core/libraries/ajm/ajm_instance_statistics.h b/src/core/libraries/ajm/ajm_instance_statistics.h new file mode 100644 index 000000000..ea70c9d56 --- /dev/null +++ b/src/core/libraries/ajm/ajm_instance_statistics.h @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/ajm/ajm_batch.h" + +namespace Libraries::Ajm { + +class AjmInstanceStatistics { +public: + void ExecuteJob(AjmJob& job); + + static AjmInstanceStatistics& Getinstance(); +}; + +} // namespace Libraries::Ajm From 1bc27135e33b057702d0fe673f48b9eb29cc8e36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quang=20Ng=C3=B4?= Date: Sun, 29 Dec 2024 18:22:35 +0700 Subject: [PATCH 305/549] renderer_vulkan: fix deadlock when resizing the SDL window (#1860) * renderer_vulkan: Fix deadlock when resizing the SDL window * Address review comment --- .../renderer_vulkan/vk_presenter.cpp | 27 +++++++++++++------ .../renderer_vulkan/vk_swapchain.cpp | 1 + 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_presenter.cpp b/src/video_core/renderer_vulkan/vk_presenter.cpp index bc55cde23..93129842f 100644 --- a/src/video_core/renderer_vulkan/vk_presenter.cpp +++ b/src/video_core/renderer_vulkan/vk_presenter.cpp @@ -628,6 +628,13 @@ Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop) } void Presenter::Present(Frame* frame) { + // Free the frame for reuse + const auto free_frame = [&] { + std::scoped_lock fl{free_mutex}; + free_queue.push(frame); + free_cv.notify_one(); + }; + // Recreate the swapchain if the window was resized. if (window.GetWidth() != swapchain.GetExtent().width || window.GetHeight() != swapchain.GetExtent().height) { @@ -636,8 +643,19 @@ void Presenter::Present(Frame* frame) { if (!swapchain.AcquireNextImage()) { swapchain.Recreate(window.GetWidth(), window.GetHeight()); + if (!swapchain.AcquireNextImage()) { + // User resizes the window too fast and GPU can't keep up. Skip this frame. + LOG_WARNING(Render_Vulkan, "Skipping frame!"); + free_frame(); + return; + } } + // Reset fence for queue submission. Do it here instead of GetRenderFrame() because we may + // skip frame because of slow swapchain recreation. If a frame skip occurs, we skip signal + // the frame's present fence and future GetRenderFrame() call will hang waiting for this frame. + instance.GetDevice().resetFences(frame->present_done); + ImGui::Core::NewFrame(); const vk::Image swapchain_image = swapchain.Image(); @@ -737,11 +755,7 @@ void Presenter::Present(Frame* frame) { swapchain.Recreate(window.GetWidth(), window.GetHeight()); } - // Free the frame for reuse - std::scoped_lock fl{free_mutex}; - free_queue.push(frame); - free_cv.notify_one(); - + free_frame(); DebugState.IncFlipFrameNum(); } @@ -776,9 +790,6 @@ Frame* Presenter::GetRenderFrame() { } } - // Reset fence for next queue submission. - device.resetFences(frame->present_done); - // If the window dimensions changed, recreate this frame if (frame->width != window.GetWidth() || frame->height != window.GetHeight()) { RecreateFrame(frame, window.GetWidth(), window.GetHeight()); diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index 380660a2f..44f4be6dd 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -84,6 +84,7 @@ void Swapchain::Create(u32 width_, u32 height_, vk::SurfaceKHR surface_) { } void Swapchain::Recreate(u32 width_, u32 height_) { + LOG_DEBUG(Render_Vulkan, "Recreate the swapchain: width={} height={}", width_, height_); Create(width_, height_, surface); } From 38f1cc265295e30c429a1d604d38599eccce5303 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sun, 29 Dec 2024 03:30:37 -0800 Subject: [PATCH 306/549] renderer_vulkan: Render polygons using triangle fans. (#1969) --- src/video_core/buffer_cache/buffer_cache.cpp | 30 ++----------------- src/video_core/buffer_cache/buffer_cache.h | 2 +- .../renderer_vulkan/liverpool_to_vk.cpp | 4 +-- .../renderer_vulkan/liverpool_to_vk.h | 9 ------ .../renderer_vulkan/vk_rasterizer.cpp | 26 +++++++--------- 5 files changed, 15 insertions(+), 56 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index 59c1e0bc3..0088ea4fa 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -234,46 +234,22 @@ bool BufferCache::BindVertexBuffers( return has_step_rate; } -u32 BufferCache::BindIndexBuffer(bool& is_indexed, u32 index_offset) { - // Emulate QuadList and Polygon primitive types with CPU made index buffer. +void BufferCache::BindIndexBuffer(u32 index_offset) { const auto& regs = liverpool->regs; - if (!is_indexed) { - if (regs.primitive_type != AmdGpu::PrimitiveType::Polygon) { - return regs.num_indices; - } - - // Emit indices. - const u32 index_size = 3 * regs.num_indices; - const auto [data, offset] = stream_buffer.Map(index_size); - Vulkan::LiverpoolToVK::EmitPolygonToTriangleListIndices(data, regs.num_indices); - stream_buffer.Commit(); - - // Bind index buffer. - is_indexed = true; - - const auto cmdbuf = scheduler.CommandBuffer(); - cmdbuf.bindIndexBuffer(stream_buffer.Handle(), offset, vk::IndexType::eUint16); - return index_size / sizeof(u16); - } // Figure out index type and size. const bool is_index16 = regs.index_buffer_type.index_type == AmdGpu::Liverpool::IndexType::Index16; const vk::IndexType index_type = is_index16 ? vk::IndexType::eUint16 : vk::IndexType::eUint32; const u32 index_size = is_index16 ? sizeof(u16) : sizeof(u32); - VAddr index_address = regs.index_base_address.Address(); - index_address += index_offset * index_size; - - if (regs.primitive_type == AmdGpu::PrimitiveType::Polygon) { - UNREACHABLE(); - } + const VAddr index_address = + regs.index_base_address.Address() + index_offset * index_size; // Bind index buffer. const u32 index_buffer_size = regs.num_indices * index_size; const auto [vk_buffer, offset] = ObtainBuffer(index_address, index_buffer_size, false); const auto cmdbuf = scheduler.CommandBuffer(); cmdbuf.bindIndexBuffer(vk_buffer->Handle(), offset, index_type); - return regs.num_indices; } void BufferCache::InlineData(VAddr address, const void* value, u32 num_bytes, bool is_gds) { diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index bcbaa45dc..0c70fa10b 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -83,7 +83,7 @@ public: const std::optional& fetch_shader); /// Bind host index buffer for the current draw. - u32 BindIndexBuffer(bool& is_indexed, u32 index_offset); + void BindIndexBuffer(u32 index_offset); /// Writes a value to GPU buffer. void InlineData(VAddr address, const void* value, u32 num_bytes, bool is_gds); diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp index 25ff88b9d..6bd50ab06 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp @@ -103,6 +103,7 @@ vk::PrimitiveTopology PrimitiveType(AmdGpu::PrimitiveType type) { case AmdGpu::PrimitiveType::TriangleList: return vk::PrimitiveTopology::eTriangleList; case AmdGpu::PrimitiveType::TriangleFan: + case AmdGpu::PrimitiveType::Polygon: return vk::PrimitiveTopology::eTriangleFan; case AmdGpu::PrimitiveType::TriangleStrip: return vk::PrimitiveTopology::eTriangleStrip; @@ -116,9 +117,6 @@ vk::PrimitiveTopology PrimitiveType(AmdGpu::PrimitiveType type) { return vk::PrimitiveTopology::eTriangleStripWithAdjacency; case AmdGpu::PrimitiveType::PatchPrimitive: return vk::PrimitiveTopology::ePatchList; - case AmdGpu::PrimitiveType::Polygon: - // Needs to generate index buffer on the fly. - return vk::PrimitiveTopology::eTriangleList; case AmdGpu::PrimitiveType::QuadList: case AmdGpu::PrimitiveType::RectList: return vk::PrimitiveTopology::ePatchList; diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.h b/src/video_core/renderer_vulkan/liverpool_to_vk.h index d5f8e693b..25a27e20e 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.h +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.h @@ -70,15 +70,6 @@ vk::ClearValue ColorBufferClearValue(const AmdGpu::Liverpool::ColorBuffer& color vk::SampleCountFlagBits NumSamples(u32 num_samples, vk::SampleCountFlags supported_flags); -inline void EmitPolygonToTriangleListIndices(u8* out_ptr, u32 num_vertices) { - u16* out_data = reinterpret_cast(out_ptr); - for (u16 i = 1; i < num_vertices - 1; i++) { - *out_data++ = 0; - *out_data++ = i; - *out_data++ = i + 1; - } -} - static inline vk::Format PromoteFormatToDepth(vk::Format fmt) { if (fmt == vk::Format::eR32Sfloat) { return vk::Format::eD32Sfloat; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index d458fa124..4384cdbea 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -12,7 +12,6 @@ #include "video_core/renderer_vulkan/vk_shader_hle.h" #include "video_core/texture_cache/image_view.h" #include "video_core/texture_cache/texture_cache.h" -#include "vk_rasterizer.h" #ifdef MemoryBarrier #undef MemoryBarrier @@ -252,7 +251,9 @@ void Rasterizer::Draw(bool is_indexed, u32 index_offset) { const auto& vs_info = pipeline->GetStage(Shader::LogicalStage::Vertex); const auto& fetch_shader = pipeline->GetFetchShader(); buffer_cache.BindVertexBuffers(vs_info, fetch_shader); - const u32 num_indices = buffer_cache.BindIndexBuffer(is_indexed, index_offset); + if (is_indexed) { + buffer_cache.BindIndexBuffer(index_offset); + } BeginRendering(*pipeline, state); UpdateDynamicState(*pipeline); @@ -263,10 +264,11 @@ void Rasterizer::Draw(bool is_indexed, u32 index_offset) { cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline->Handle()); if (is_indexed) { - cmdbuf.drawIndexed(num_indices, regs.num_instances.NumInstances(), 0, s32(vertex_offset), - instance_offset); + cmdbuf.drawIndexed(regs.num_indices, regs.num_instances.NumInstances(), 0, + s32(vertex_offset), instance_offset); } else { - cmdbuf.draw(num_indices, regs.num_instances.NumInstances(), vertex_offset, instance_offset); + cmdbuf.draw(regs.num_indices, regs.num_instances.NumInstances(), vertex_offset, + instance_offset); } ResetBindings(); @@ -280,22 +282,12 @@ void Rasterizer::DrawIndirect(bool is_indexed, VAddr arg_address, u32 offset, u3 return; } - const auto& regs = liverpool->regs; - if (regs.primitive_type == AmdGpu::PrimitiveType::Polygon) { - // We use a generated index buffer to convert polygons to triangles. Since it - // changes type of the draw, arguments are not valid for this case. We need to run a - // conversion pass to repack the indirect arguments buffer first. - LOG_WARNING(Render_Vulkan, "Primitive type is not supported for indirect draw"); - return; - } - const GraphicsPipeline* pipeline = pipeline_cache.GetGraphicsPipeline(); if (!pipeline) { return; } auto state = PrepareRenderState(pipeline->GetMrtMask()); - if (!BindResources(pipeline)) { return; } @@ -303,7 +295,9 @@ void Rasterizer::DrawIndirect(bool is_indexed, VAddr arg_address, u32 offset, u3 const auto& vs_info = pipeline->GetStage(Shader::LogicalStage::Vertex); const auto& fetch_shader = pipeline->GetFetchShader(); buffer_cache.BindVertexBuffers(vs_info, fetch_shader); - buffer_cache.BindIndexBuffer(is_indexed, 0); + if (is_indexed) { + buffer_cache.BindIndexBuffer(0); + } const auto& [buffer, base] = buffer_cache.ObtainBuffer(arg_address + offset, stride * max_count, false); From ac2e8c26027059af7c80892f6245193890d3d1c2 Mon Sep 17 00:00:00 2001 From: Nenkai Date: Sun, 29 Dec 2024 19:15:04 +0100 Subject: [PATCH 307/549] gnmdriver: remove redundant EqEventType assert (#1975) --- src/core/libraries/gnmdriver/gnmdriver.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index 91a1329e5..805c9124e 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -1015,11 +1015,7 @@ int PS4_SYSV_ABI sceGnmGetDebugTimestamp() { int PS4_SYSV_ABI sceGnmGetEqEventType(const SceKernelEvent* ev) { LOG_TRACE(Lib_GnmDriver, "called"); - - auto data = sceKernelGetEventData(ev); - ASSERT(static_cast(data) == GnmEventType::GfxEop); - - return data; + return sceKernelGetEventData(ev); } int PS4_SYSV_ABI sceGnmGetEqTimeStamp() { From dd3f24614b47986ff2b921e6ff9e45c979ed4ed6 Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Sun, 29 Dec 2024 14:57:32 +0300 Subject: [PATCH 308/549] infra: updated github issue templates --- .github/ISSUE_TEMPLATE/app-bug-report.yaml | 55 +++++++++++++ .github/ISSUE_TEMPLATE/bug_report.md | 30 ------- .github/ISSUE_TEMPLATE/config.yml | 10 +++ .github/ISSUE_TEMPLATE/feature-request.yaml | 54 ++++++++++++ .github/ISSUE_TEMPLATE/game-bug-report.yaml | 91 +++++++++++++++++++++ 5 files changed, 210 insertions(+), 30 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/app-bug-report.yaml delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature-request.yaml create mode 100644 .github/ISSUE_TEMPLATE/game-bug-report.yaml diff --git a/.github/ISSUE_TEMPLATE/app-bug-report.yaml b/.github/ISSUE_TEMPLATE/app-bug-report.yaml new file mode 100644 index 000000000..c38bbb814 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/app-bug-report.yaml @@ -0,0 +1,55 @@ +# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later +# Docs - https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema +name: Application Bug Report +description: Problem with the application itself (ie. bad file path handling, UX issue) +title: "[APP BUG]: " +body: + - type: markdown + attributes: + value: | + ## Important: Read First + + Please do not make support requests on GitHub. Our issue tracker is for tracking bugs and feature requests only + If you need help using the emulator or unsure about your issue please contact us on [discord](https://discord.gg/bFJxfftGW6). + + Please make an effort to make sure your issue isn't already reported. + + Do not create issues involving software piracy, our rules specifically prohibit this. Otherwise your issue will be closed and you will be banned in this repository. + - type: checkboxes + id: checklist + attributes: + label: Checklist + options: + - label: I have searched for a similar issue in this repository and did not find one. + required: true + - label: I am using an official build obtained from [releases](https://github.com/shadps4-emu/shadPS4/releases) or updated one of those builds using its in-app updater. + required: true + - type: textarea + id: desc + attributes: + label: Describe the Bug + description: "A clear and concise description of what the bug is" + validations: + required: true + - type: textarea + id: repro + attributes: + label: Reproduction Steps + description: "Detailed steps to reproduce the behavior" + validations: + required: true + - type: textarea + id: expected + attributes: + label: Expected Behavior + description: "A clear and concise description of what you expected to happen" + validations: + required: false + - type: input + id: os + attributes: + label: Specify OS Version + placeholder: "Example: Windows 11, Arch Linux, MacOS 15" + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 02017dddf..000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -name: Bug report -about: Report a bug in the emulator -title: '' -labels: '' -assignees: '' - ---- - -Checklist: -[ ] I have searched for a similar issue in this repository and did not find one. -[ ] I have asked for support on shadPS4 discord server. -[ ] I am using an official build obtained from [releases](https://github.com/shadps4-emu/shadPS4/releases) or updated it using its in-app updater. -[ ] I have re-dumped the game and performed a clean install without mods. -[ ] I have disabled all patches and cheats. - -Description: - - -Steps To Reproduce: - - -Logs: - - -System Information: - - -Additional Information: - diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..5adcf1437 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later +blank_issues_enabled: false +contact_links: + - name: Discord + url: https://discord.gg/bFJxfftGW6 + about: Get direct support and hang out with us + - name: Wiki + url: https://github.com/shadps4-emu/shadPS4/wiki + about: Information, guides, etc. diff --git a/.github/ISSUE_TEMPLATE/feature-request.yaml b/.github/ISSUE_TEMPLATE/feature-request.yaml new file mode 100644 index 000000000..a1b49362a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.yaml @@ -0,0 +1,54 @@ +# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later +# Docs - https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema +name: Feature Request +description: Suggest a new feature or improve an existing one +title: "[Feature Request]: " +body: + - type: markdown + attributes: + value: | + ## Important: Read First + + Please make an effort to make sure your issue isn't already reported. + + Do not create issues involving software piracy, our rules specifically prohibit this. Otherwise your issue will be closed and you will be banned in this repository. + + - type: checkboxes + id: checklist + attributes: + label: Checklist + options: + - label: I have searched for a similar issue in this repository and did not find one. + required: true + - type: textarea + id: desc + attributes: + label: Description + description: | + A concise description of the feature you want + + Include step by step examples of how the feature should work under various circumstances + validations: + required: true + - type: textarea + id: reason + attributes: + label: Reason + description: | + Give a reason why you want this feature + - How will it make things easier for you? + - How does this feature help your enjoyment of the emulator? + - What does it provide that isn't being provided currently? + validations: + required: true + - type: textarea + id: examples + attributes: + label: Examples + description: | + Provide examples of the feature as implemented by other software + + Include screenshots or video if you like to help demonstrate how you'd like this feature to work + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/game-bug-report.yaml b/.github/ISSUE_TEMPLATE/game-bug-report.yaml new file mode 100644 index 000000000..7eb9441d2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/game-bug-report.yaml @@ -0,0 +1,91 @@ +# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later +# Docs - https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema +name: Game Emulation Bug Report +description: Problem in a game (ie. graphical artifacts, crashes, etc.) +title: "[GAME BUG]: " +body: + - type: markdown + attributes: + value: | + ## Important: Read First + + Please do not make support requests on GitHub. Our issue tracker is for tracking bugs and feature requests only + If you need help using the emulator or unsure about your issue please contact us on [discord](https://discord.gg/bFJxfftGW6). + + You can also check the [Game Compatibility Repository](https://github.com/shadps4-emu/shadps4-game-compatibility) for the information about the status of the game. + + Please make an effort to make sure your issue isn't already reported. + + Do not create issues involving software piracy, our rules specifically prohibit this. Otherwise your issue will be closed and you will be banned in this repository. + - type: checkboxes + id: checklist + attributes: + label: Checklist + options: + - label: I have searched for a similar issue in this repository and did not find one. + required: true + - label: I am using an official build obtained from [releases](https://github.com/shadps4-emu/shadPS4/releases) or updated one of those builds using its in-app updater. + required: true + - label: I have re-dumped the game and performed a clean install without mods. + required: true + - label: I have disabled all patches and cheats. + required: true + - label: I have all the required [system modules](https://github.com/shadps4-emu/shadps4-game-compatibility?tab=readme-ov-file#informations) installed. + required: true + - type: textarea + id: desc + attributes: + label: Describe the Bug + description: "A clear and concise description of what the bug is" + validations: + required: true + - type: textarea + id: repro + attributes: + label: Reproduction Steps + description: "Detailed steps to reproduce the behavior" + validations: + required: true + - type: input + id: os + attributes: + label: Specify OS Version + placeholder: "Example: Windows 11, Arch Linux, MacOS 15" + validations: + required: true + - type: input + id: cpu + attributes: + label: CPU + placeholder: "Example: Intel Core i7-8700" + validations: + required: true + - type: input + id: gpu + attributes: + label: GPU + placeholder: "Example: nVidia GTX 1650" + validations: + required: true + - type: input + id: ram + attributes: + label: Amount of RAM in GB + placeholder: "Example: 16 GB" + validations: + required: true + - type: input + id: vram + attributes: + label: Amount of VRAM in GB + placeholder: "Example: 8 GB" + validations: + required: true + - type: textarea + id: logs + attributes: + label: "Logs" + description: Attach any logs here. Log can be found by right clicking on a game name -> Open Folder... -> Open Log Folder. Make sure that the log type is set to `sync`. + validations: + required: false From 90912233967f97b41d4ed8017eae15667adf1547 Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Mon, 30 Dec 2024 21:59:14 -0600 Subject: [PATCH 309/549] Fix sceKernelGetEventFilter (#1987) --- src/core/libraries/kernel/equeue.cpp | 2 +- src/core/libraries/kernel/equeue.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/libraries/kernel/equeue.cpp b/src/core/libraries/kernel/equeue.cpp index 3ae77e46b..03259cd22 100644 --- a/src/core/libraries/kernel/equeue.cpp +++ b/src/core/libraries/kernel/equeue.cpp @@ -346,7 +346,7 @@ int PS4_SYSV_ABI sceKernelDeleteUserEvent(SceKernelEqueue eq, int id) { return ORBIS_OK; } -s16 PS4_SYSV_ABI sceKernelGetEventFilter(const SceKernelEvent* ev) { +int PS4_SYSV_ABI sceKernelGetEventFilter(const SceKernelEvent* ev) { return ev->filter; } diff --git a/src/core/libraries/kernel/equeue.h b/src/core/libraries/kernel/equeue.h index f8759137c..17900238f 100644 --- a/src/core/libraries/kernel/equeue.h +++ b/src/core/libraries/kernel/equeue.h @@ -21,7 +21,7 @@ class EqueueInternal; struct EqueueEvent; struct SceKernelEvent { - enum Filter : s16 { + enum Filter : int { None = 0, Read = -1, Write = -2, From 62780e4e431b42cacadeb63dfbd360b0891f4928 Mon Sep 17 00:00:00 2001 From: baggins183 Date: Mon, 30 Dec 2024 20:00:52 -0800 Subject: [PATCH 310/549] Initialize V0 to PrimitiveId in hull shader (#1985) --- src/shader_recompiler/frontend/translate/translate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shader_recompiler/frontend/translate/translate.cpp b/src/shader_recompiler/frontend/translate/translate.cpp index a14bff706..237acf309 100644 --- a/src/shader_recompiler/frontend/translate/translate.cpp +++ b/src/shader_recompiler/frontend/translate/translate.cpp @@ -124,12 +124,12 @@ void Translator::EmitPrologue() { } break; case LogicalStage::TessellationControl: { + ir.SetVectorReg(IR::VectorReg::V0, ir.GetAttributeU32(IR::Attribute::PrimitiveId)); // Should be laid out like: // [0:8]: patch id within VGT // [8:12]: output control point id ir.SetVectorReg(IR::VectorReg::V1, ir.GetAttributeU32(IR::Attribute::PackedHullInvocationInfo)); - // TODO PrimitiveId is probably V2 but haven't seen it yet break; } case LogicalStage::TessellationEval: From 284f473a52456917e2cf7fd61a151f39abf09403 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Mon, 30 Dec 2024 20:10:29 -0800 Subject: [PATCH 311/549] shader_recompiler: Fix BitCount64 and FindILsb64 (#1978) --- .../backend/spirv/emit_spirv_integer.cpp | 19 +++++++++++++++++-- .../frontend/translate/scalar_alu.cpp | 5 ++--- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp index def1f816e..70411ecec 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp @@ -202,7 +202,14 @@ Id EmitBitCount32(EmitContext& ctx, Id value) { } Id EmitBitCount64(EmitContext& ctx, Id value) { - return ctx.OpBitCount(ctx.U64, value); + // Vulkan restricts some bitwise operations to 32-bit only, so decompose into + // two 32-bit values and add the result. + const Id unpacked{ctx.OpBitcast(ctx.U32[2], value)}; + const Id lo{ctx.OpCompositeExtract(ctx.U32[1], unpacked, 0U)}; + const Id hi{ctx.OpCompositeExtract(ctx.U32[1], unpacked, 1U)}; + const Id lo_count{ctx.OpBitCount(ctx.U32[1], lo)}; + const Id hi_count{ctx.OpBitCount(ctx.U32[1], hi)}; + return ctx.OpIAdd(ctx.U32[1], lo_count, hi_count); } Id EmitBitwiseNot32(EmitContext& ctx, Id value) { @@ -222,7 +229,15 @@ Id EmitFindILsb32(EmitContext& ctx, Id value) { } Id EmitFindILsb64(EmitContext& ctx, Id value) { - return ctx.OpFindILsb(ctx.U64, value); + // Vulkan restricts some bitwise operations to 32-bit only, so decompose into + // two 32-bit values and select the correct result. + const Id unpacked{ctx.OpBitcast(ctx.U32[2], value)}; + const Id lo{ctx.OpCompositeExtract(ctx.U32[1], unpacked, 0U)}; + const Id hi{ctx.OpCompositeExtract(ctx.U32[1], unpacked, 1U)}; + const Id lo_lsb{ctx.OpFindILsb(ctx.U32[1], lo)}; + const Id hi_lsb{ctx.OpFindILsb(ctx.U32[1], hi)}; + const Id found_lo{ctx.OpINotEqual(ctx.U32[1], lo_lsb, ctx.ConstU32(u32(-1)))}; + return ctx.OpSelect(ctx.U32[1], found_lo, lo_lsb, hi_lsb); } Id EmitSMin32(EmitContext& ctx, Id a, Id b) { diff --git a/src/shader_recompiler/frontend/translate/scalar_alu.cpp b/src/shader_recompiler/frontend/translate/scalar_alu.cpp index 3a2b01a90..e18cda012 100644 --- a/src/shader_recompiler/frontend/translate/scalar_alu.cpp +++ b/src/shader_recompiler/frontend/translate/scalar_alu.cpp @@ -597,14 +597,13 @@ void Translator::S_BCNT1_I32_B64(const GcnInst& inst) { void Translator::S_FF1_I32_B32(const GcnInst& inst) { const IR::U32 src0{GetSrc(inst.src[0])}; - const IR::U32 result{ir.Select(ir.IEqual(src0, ir.Imm32(0U)), ir.Imm32(-1), ir.FindILsb(src0))}; + const IR::U32 result{ir.FindILsb(src0)}; SetDst(inst.dst[0], result); } void Translator::S_FF1_I32_B64(const GcnInst& inst) { const IR::U64 src0{GetSrc64(inst.src[0])}; - const IR::U32 result{ - ir.Select(ir.IEqual(src0, ir.Imm64(u64(0))), ir.Imm32(-1), ir.FindILsb(src0))}; + const IR::U32 result{ir.FindILsb(src0)}; SetDst(inst.dst[0], result); } From 41d64a200dc6ac519b2db0df9d7692afef3a8344 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Mon, 30 Dec 2024 20:14:47 -0800 Subject: [PATCH 312/549] shader_recompiler: Add swizzle support for unsupported formats. (#1869) * shader_recompiler: Add swizzle support for unsupported formats. * renderer_vulkan: Rework MRT swizzles and add unsupported format swizzle support. * shader_recompiler: Clean up swizzle handling and handle ImageRead storage swizzle. * shader_recompiler: Fix type errors * liverpool_to_vk: Remove redundant clear color swizzles. * shader_recompiler: Reduce CompositeConstruct to constants where possible. * shader_recompiler: Fix ImageRead/Write and StoreBufferFormatF32 types. * amdgpu: Add a few more unsupported format remaps. --- CMakeLists.txt | 1 + .../backend/spirv/emit_spirv_composite.cpp | 98 +++++++++-- .../backend/spirv/emit_spirv_image.cpp | 6 +- .../backend/spirv/emit_spirv_instructions.h | 38 ++++- .../frontend/translate/export.cpp | 32 ++-- .../frontend/translate/translate.cpp | 25 +-- .../frontend/translate/vector_memory.cpp | 4 +- src/shader_recompiler/ir/ir_emitter.cpp | 80 +++++++++ src/shader_recompiler/ir/ir_emitter.h | 7 + src/shader_recompiler/ir/opcodes.inc | 18 +- .../ir/passes/resource_tracking_pass.cpp | 100 ++++++----- src/shader_recompiler/ir/reinterpret.h | 24 +++ src/shader_recompiler/runtime_info.h | 2 +- src/shader_recompiler/specialization.h | 10 +- src/video_core/amdgpu/liverpool.h | 50 +++++- src/video_core/amdgpu/resource.h | 156 ++++++++++++------ .../renderer_vulkan/liverpool_to_vk.cpp | 97 ++++------- .../renderer_vulkan/liverpool_to_vk.h | 7 +- .../renderer_vulkan/vk_graphics_pipeline.h | 2 +- .../renderer_vulkan/vk_pipeline_cache.cpp | 14 +- src/video_core/texture_cache/image_info.cpp | 4 +- src/video_core/texture_cache/image_view.cpp | 29 +--- 22 files changed, 522 insertions(+), 282 deletions(-) create mode 100644 src/shader_recompiler/ir/reinterpret.h diff --git a/CMakeLists.txt b/CMakeLists.txt index af811e9fb..833bbe3ce 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -701,6 +701,7 @@ set(SHADER_RECOMPILER src/shader_recompiler/exception.h src/shader_recompiler/ir/post_order.h src/shader_recompiler/ir/program.cpp src/shader_recompiler/ir/program.h + src/shader_recompiler/ir/reinterpret.h src/shader_recompiler/ir/reg.h src/shader_recompiler/ir/type.cpp src/shader_recompiler/ir/type.h diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp index 74e736cf6..d064b5d05 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp @@ -6,16 +6,22 @@ namespace Shader::Backend::SPIRV { -Id EmitCompositeConstructU32x2(EmitContext& ctx, Id e1, Id e2) { - return ctx.OpCompositeConstruct(ctx.U32[2], e1, e2); +template +Id EmitCompositeConstruct(EmitContext& ctx, IR::Inst* inst, Args&&... args) { + return inst->AreAllArgsImmediates() ? ctx.ConstantComposite(args...) + : ctx.OpCompositeConstruct(args...); } -Id EmitCompositeConstructU32x3(EmitContext& ctx, Id e1, Id e2, Id e3) { - return ctx.OpCompositeConstruct(ctx.U32[3], e1, e2, e3); +Id EmitCompositeConstructU32x2(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2) { + return EmitCompositeConstruct(ctx, inst, ctx.U32[2], e1, e2); } -Id EmitCompositeConstructU32x4(EmitContext& ctx, Id e1, Id e2, Id e3, Id e4) { - return ctx.OpCompositeConstruct(ctx.U32[4], e1, e2, e3, e4); +Id EmitCompositeConstructU32x3(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2, Id e3) { + return EmitCompositeConstruct(ctx, inst, ctx.U32[3], e1, e2, e3); +} + +Id EmitCompositeConstructU32x4(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2, Id e3, Id e4) { + return EmitCompositeConstruct(ctx, inst, ctx.U32[4], e1, e2, e3, e4); } Id EmitCompositeExtractU32x2(EmitContext& ctx, Id composite, u32 index) { @@ -42,16 +48,30 @@ Id EmitCompositeInsertU32x4(EmitContext& ctx, Id composite, Id object, u32 index return ctx.OpCompositeInsert(ctx.U32[4], object, composite, index); } -Id EmitCompositeConstructF16x2(EmitContext& ctx, Id e1, Id e2) { - return ctx.OpCompositeConstruct(ctx.F16[2], e1, e2); +Id EmitCompositeShuffleU32x2(EmitContext& ctx, Id composite1, Id composite2, u32 comp0, u32 comp1) { + return ctx.OpVectorShuffle(ctx.U32[2], composite1, composite2, comp0, comp1); } -Id EmitCompositeConstructF16x3(EmitContext& ctx, Id e1, Id e2, Id e3) { - return ctx.OpCompositeConstruct(ctx.F16[3], e1, e2, e3); +Id EmitCompositeShuffleU32x3(EmitContext& ctx, Id composite1, Id composite2, u32 comp0, u32 comp1, + u32 comp2) { + return ctx.OpVectorShuffle(ctx.U32[3], composite1, composite2, comp0, comp1, comp2); } -Id EmitCompositeConstructF16x4(EmitContext& ctx, Id e1, Id e2, Id e3, Id e4) { - return ctx.OpCompositeConstruct(ctx.F16[4], e1, e2, e3, e4); +Id EmitCompositeShuffleU32x4(EmitContext& ctx, Id composite1, Id composite2, u32 comp0, u32 comp1, + u32 comp2, u32 comp3) { + return ctx.OpVectorShuffle(ctx.U32[4], composite1, composite2, comp0, comp1, comp2, comp3); +} + +Id EmitCompositeConstructF16x2(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2) { + return EmitCompositeConstruct(ctx, inst, ctx.F16[2], e1, e2); +} + +Id EmitCompositeConstructF16x3(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2, Id e3) { + return EmitCompositeConstruct(ctx, inst, ctx.F16[3], e1, e2, e3); +} + +Id EmitCompositeConstructF16x4(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2, Id e3, Id e4) { + return EmitCompositeConstruct(ctx, inst, ctx.F16[4], e1, e2, e3, e4); } Id EmitCompositeExtractF16x2(EmitContext& ctx, Id composite, u32 index) { @@ -78,16 +98,30 @@ Id EmitCompositeInsertF16x4(EmitContext& ctx, Id composite, Id object, u32 index return ctx.OpCompositeInsert(ctx.F16[4], object, composite, index); } -Id EmitCompositeConstructF32x2(EmitContext& ctx, Id e1, Id e2) { - return ctx.OpCompositeConstruct(ctx.F32[2], e1, e2); +Id EmitCompositeShuffleF16x2(EmitContext& ctx, Id composite1, Id composite2, u32 comp0, u32 comp1) { + return ctx.OpVectorShuffle(ctx.F16[2], composite1, composite2, comp0, comp1); } -Id EmitCompositeConstructF32x3(EmitContext& ctx, Id e1, Id e2, Id e3) { - return ctx.OpCompositeConstruct(ctx.F32[3], e1, e2, e3); +Id EmitCompositeShuffleF16x3(EmitContext& ctx, Id composite1, Id composite2, u32 comp0, u32 comp1, + u32 comp2) { + return ctx.OpVectorShuffle(ctx.F16[3], composite1, composite2, comp0, comp1, comp2); } -Id EmitCompositeConstructF32x4(EmitContext& ctx, Id e1, Id e2, Id e3, Id e4) { - return ctx.OpCompositeConstruct(ctx.F32[4], e1, e2, e3, e4); +Id EmitCompositeShuffleF16x4(EmitContext& ctx, Id composite1, Id composite2, u32 comp0, u32 comp1, + u32 comp2, u32 comp3) { + return ctx.OpVectorShuffle(ctx.F16[4], composite1, composite2, comp0, comp1, comp2, comp3); +} + +Id EmitCompositeConstructF32x2(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2) { + return EmitCompositeConstruct(ctx, inst, ctx.F32[2], e1, e2); +} + +Id EmitCompositeConstructF32x3(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2, Id e3) { + return EmitCompositeConstruct(ctx, inst, ctx.F32[3], e1, e2, e3); +} + +Id EmitCompositeConstructF32x4(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2, Id e3, Id e4) { + return EmitCompositeConstruct(ctx, inst, ctx.F32[4], e1, e2, e3, e4); } Id EmitCompositeExtractF32x2(EmitContext& ctx, Id composite, u32 index) { @@ -114,6 +148,20 @@ Id EmitCompositeInsertF32x4(EmitContext& ctx, Id composite, Id object, u32 index return ctx.OpCompositeInsert(ctx.F32[4], object, composite, index); } +Id EmitCompositeShuffleF32x2(EmitContext& ctx, Id composite1, Id composite2, u32 comp0, u32 comp1) { + return ctx.OpVectorShuffle(ctx.F32[2], composite1, composite2, comp0, comp1); +} + +Id EmitCompositeShuffleF32x3(EmitContext& ctx, Id composite1, Id composite2, u32 comp0, u32 comp1, + u32 comp2) { + return ctx.OpVectorShuffle(ctx.F32[3], composite1, composite2, comp0, comp1, comp2); +} + +Id EmitCompositeShuffleF32x4(EmitContext& ctx, Id composite1, Id composite2, u32 comp0, u32 comp1, + u32 comp2, u32 comp3) { + return ctx.OpVectorShuffle(ctx.F32[4], composite1, composite2, comp0, comp1, comp2, comp3); +} + void EmitCompositeConstructF64x2(EmitContext&) { UNREACHABLE_MSG("SPIR-V Instruction"); } @@ -150,4 +198,18 @@ Id EmitCompositeInsertF64x4(EmitContext& ctx, Id composite, Id object, u32 index return ctx.OpCompositeInsert(ctx.F64[4], object, composite, index); } +Id EmitCompositeShuffleF64x2(EmitContext& ctx, Id composite1, Id composite2, u32 comp0, u32 comp1) { + return ctx.OpVectorShuffle(ctx.F64[2], composite1, composite2, comp0, comp1); +} + +Id EmitCompositeShuffleF64x3(EmitContext& ctx, Id composite1, Id composite2, u32 comp0, u32 comp1, + u32 comp2) { + return ctx.OpVectorShuffle(ctx.F64[3], composite1, composite2, comp0, comp1, comp2); +} + +Id EmitCompositeShuffleF64x4(EmitContext& ctx, Id composite1, Id composite2, u32 comp0, u32 comp1, + u32 comp2, u32 comp3) { + return ctx.OpVectorShuffle(ctx.F64[4], composite1, composite2, comp0, comp1, comp2, comp3); +} + } // namespace Shader::Backend::SPIRV diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index 2946edab3..c3d937fe7 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp @@ -238,7 +238,7 @@ Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id lod } texel = ctx.OpImageRead(color_type, image, coords, operands.mask, operands.operands); } - return !texture.is_integer ? ctx.OpBitcast(ctx.U32[4], texel) : texel; + return texture.is_integer ? ctx.OpBitcast(ctx.F32[4], texel) : texel; } void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id lod, Id ms, @@ -253,8 +253,8 @@ void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id } else if (Sirit::ValidId(lod)) { LOG_WARNING(Render, "Image write with LOD not supported by driver"); } - ctx.OpImageWrite(image, coords, ctx.OpBitcast(color_type, color), operands.mask, - operands.operands); + const Id texel = texture.is_integer ? ctx.OpBitcast(color_type, color) : color; + ctx.OpImageWrite(image, coords, texel, operands.mask, operands.operands); } } // namespace Shader::Backend::SPIRV diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index d26cf6662..0d9fcff46 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h @@ -120,33 +120,48 @@ Id EmitSharedAtomicSMin32(EmitContext& ctx, Id offset, Id value); Id EmitSharedAtomicAnd32(EmitContext& ctx, Id offset, Id value); Id EmitSharedAtomicOr32(EmitContext& ctx, Id offset, Id value); Id EmitSharedAtomicXor32(EmitContext& ctx, Id offset, Id value); -Id EmitCompositeConstructU32x2(EmitContext& ctx, Id e1, Id e2); -Id EmitCompositeConstructU32x3(EmitContext& ctx, Id e1, Id e2, Id e3); -Id EmitCompositeConstructU32x4(EmitContext& ctx, Id e1, Id e2, Id e3, Id e4); +Id EmitCompositeConstructU32x2(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2); +Id EmitCompositeConstructU32x3(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2, Id e3); +Id EmitCompositeConstructU32x4(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2, Id e3, Id e4); Id EmitCompositeExtractU32x2(EmitContext& ctx, Id composite, u32 index); Id EmitCompositeExtractU32x3(EmitContext& ctx, Id composite, u32 index); Id EmitCompositeExtractU32x4(EmitContext& ctx, Id composite, u32 index); Id EmitCompositeInsertU32x2(EmitContext& ctx, Id composite, Id object, u32 index); Id EmitCompositeInsertU32x3(EmitContext& ctx, Id composite, Id object, u32 index); Id EmitCompositeInsertU32x4(EmitContext& ctx, Id composite, Id object, u32 index); -Id EmitCompositeConstructF16x2(EmitContext& ctx, Id e1, Id e2); -Id EmitCompositeConstructF16x3(EmitContext& ctx, Id e1, Id e2, Id e3); -Id EmitCompositeConstructF16x4(EmitContext& ctx, Id e1, Id e2, Id e3, Id e4); +Id EmitCompositeShuffleU32x2(EmitContext& ctx, Id composite1, Id composite2, u32 comp0, u32 comp1); +Id EmitCompositeShuffleU32x3(EmitContext& ctx, Id composite1, Id composite2, u32 comp0, u32 comp1, + u32 comp2); +Id EmitCompositeShuffleU32x4(EmitContext& ctx, Id composite1, Id composite2, u32 comp0, u32 comp1, + u32 comp2, u32 comp3); +Id EmitCompositeConstructF16x2(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2); +Id EmitCompositeConstructF16x3(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2, Id e3); +Id EmitCompositeConstructF16x4(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2, Id e3, Id e4); Id EmitCompositeExtractF16x2(EmitContext& ctx, Id composite, u32 index); Id EmitCompositeExtractF16x3(EmitContext& ctx, Id composite, u32 index); Id EmitCompositeExtractF16x4(EmitContext& ctx, Id composite, u32 index); Id EmitCompositeInsertF16x2(EmitContext& ctx, Id composite, Id object, u32 index); Id EmitCompositeInsertF16x3(EmitContext& ctx, Id composite, Id object, u32 index); Id EmitCompositeInsertF16x4(EmitContext& ctx, Id composite, Id object, u32 index); -Id EmitCompositeConstructF32x2(EmitContext& ctx, Id e1, Id e2); -Id EmitCompositeConstructF32x3(EmitContext& ctx, Id e1, Id e2, Id e3); -Id EmitCompositeConstructF32x4(EmitContext& ctx, Id e1, Id e2, Id e3, Id e4); +Id EmitCompositeShuffleF16x2(EmitContext& ctx, Id composite1, Id composite2, u32 comp0, u32 comp1); +Id EmitCompositeShuffleF16x3(EmitContext& ctx, Id composite1, Id composite2, u32 comp0, u32 comp1, + u32 comp2); +Id EmitCompositeShuffleF16x4(EmitContext& ctx, Id composite1, Id composite2, u32 comp0, u32 comp1, + u32 comp2, u32 comp3); +Id EmitCompositeConstructF32x2(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2); +Id EmitCompositeConstructF32x3(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2, Id e3); +Id EmitCompositeConstructF32x4(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2, Id e3, Id e4); Id EmitCompositeExtractF32x2(EmitContext& ctx, Id composite, u32 index); Id EmitCompositeExtractF32x3(EmitContext& ctx, Id composite, u32 index); Id EmitCompositeExtractF32x4(EmitContext& ctx, Id composite, u32 index); Id EmitCompositeInsertF32x2(EmitContext& ctx, Id composite, Id object, u32 index); Id EmitCompositeInsertF32x3(EmitContext& ctx, Id composite, Id object, u32 index); Id EmitCompositeInsertF32x4(EmitContext& ctx, Id composite, Id object, u32 index); +Id EmitCompositeShuffleF32x2(EmitContext& ctx, Id composite1, Id composite2, u32 comp0, u32 comp1); +Id EmitCompositeShuffleF32x3(EmitContext& ctx, Id composite1, Id composite2, u32 comp0, u32 comp1, + u32 comp2); +Id EmitCompositeShuffleF32x4(EmitContext& ctx, Id composite1, Id composite2, u32 comp0, u32 comp1, + u32 comp2, u32 comp3); void EmitCompositeConstructF64x2(EmitContext& ctx); void EmitCompositeConstructF64x3(EmitContext& ctx); void EmitCompositeConstructF64x4(EmitContext& ctx); @@ -156,6 +171,11 @@ void EmitCompositeExtractF64x4(EmitContext& ctx); Id EmitCompositeInsertF64x2(EmitContext& ctx, Id composite, Id object, u32 index); Id EmitCompositeInsertF64x3(EmitContext& ctx, Id composite, Id object, u32 index); Id EmitCompositeInsertF64x4(EmitContext& ctx, Id composite, Id object, u32 index); +Id EmitCompositeShuffleF64x2(EmitContext& ctx, Id composite1, Id composite2, u32 comp0, u32 comp1); +Id EmitCompositeShuffleF64x3(EmitContext& ctx, Id composite1, Id composite2, u32 comp0, u32 comp1, + u32 comp2); +Id EmitCompositeShuffleF64x4(EmitContext& ctx, Id composite1, Id composite2, u32 comp0, u32 comp1, + u32 comp2, u32 comp3); Id EmitSelectU1(EmitContext& ctx, Id cond, Id true_value, Id false_value); Id EmitSelectU8(EmitContext& ctx, Id cond, Id true_value, Id false_value); Id EmitSelectU16(EmitContext& ctx, Id cond, Id true_value, Id false_value); diff --git a/src/shader_recompiler/frontend/translate/export.cpp b/src/shader_recompiler/frontend/translate/export.cpp index 5927aa696..83240e17f 100644 --- a/src/shader_recompiler/frontend/translate/export.cpp +++ b/src/shader_recompiler/frontend/translate/export.cpp @@ -25,34 +25,28 @@ void Translator::EmitExport(const GcnInst& inst) { IR::VectorReg(inst.src[3].code), }; - const auto swizzle = [&](u32 comp) { + const auto set_attribute = [&](u32 comp, IR::F32 value) { if (!IR::IsMrt(attrib)) { - return comp; + ir.SetAttribute(attrib, value, comp); + return; } const u32 index = u32(attrib) - u32(IR::Attribute::RenderTarget0); - switch (runtime_info.fs_info.color_buffers[index].mrt_swizzle) { - case MrtSwizzle::Identity: - return comp; - case MrtSwizzle::Alt: - static constexpr std::array AltSwizzle = {2, 1, 0, 3}; - return AltSwizzle[comp]; - case MrtSwizzle::Reverse: - static constexpr std::array RevSwizzle = {3, 2, 1, 0}; - return RevSwizzle[comp]; - case MrtSwizzle::ReverseAlt: - static constexpr std::array AltRevSwizzle = {3, 0, 1, 2}; - return AltRevSwizzle[comp]; - default: - UNREACHABLE(); + const auto [r, g, b, a] = runtime_info.fs_info.color_buffers[index].swizzle; + const std::array swizzle_array = {r, g, b, a}; + const auto swizzled_comp = swizzle_array[comp]; + if (u32(swizzled_comp) < u32(AmdGpu::CompSwizzle::Red)) { + ir.SetAttribute(attrib, value, comp); + return; } + ir.SetAttribute(attrib, value, u32(swizzled_comp) - u32(AmdGpu::CompSwizzle::Red)); }; const auto unpack = [&](u32 idx) { const IR::Value value = ir.UnpackHalf2x16(ir.GetVectorReg(vsrc[idx])); const IR::F32 r = IR::F32{ir.CompositeExtract(value, 0)}; const IR::F32 g = IR::F32{ir.CompositeExtract(value, 1)}; - ir.SetAttribute(attrib, r, swizzle(idx * 2)); - ir.SetAttribute(attrib, g, swizzle(idx * 2 + 1)); + set_attribute(idx * 2, r); + set_attribute(idx * 2 + 1, g); }; // Components are float16 packed into a VGPR @@ -73,7 +67,7 @@ void Translator::EmitExport(const GcnInst& inst) { continue; } const IR::F32 comp = ir.GetVectorReg(vsrc[i]); - ir.SetAttribute(attrib, comp, swizzle(i)); + set_attribute(i, comp); } } if (IR::IsMrt(attrib)) { diff --git a/src/shader_recompiler/frontend/translate/translate.cpp b/src/shader_recompiler/frontend/translate/translate.cpp index 237acf309..7f5504663 100644 --- a/src/shader_recompiler/frontend/translate/translate.cpp +++ b/src/shader_recompiler/frontend/translate/translate.cpp @@ -10,6 +10,7 @@ #include "shader_recompiler/info.h" #include "shader_recompiler/ir/attribute.h" #include "shader_recompiler/ir/reg.h" +#include "shader_recompiler/ir/reinterpret.h" #include "shader_recompiler/runtime_info.h" #include "video_core/amdgpu/resource.h" #include "video_core/amdgpu/types.h" @@ -475,26 +476,12 @@ void Translator::EmitFetch(const GcnInst& inst) { // Read the V# of the attribute to figure out component number and type. const auto buffer = info.ReadUdReg(attrib.sgpr_base, attrib.dword_offset); + const auto values = + ir.CompositeConstruct(ir.GetAttribute(attr, 0), ir.GetAttribute(attr, 1), + ir.GetAttribute(attr, 2), ir.GetAttribute(attr, 3)); + const auto swizzled = ApplySwizzle(ir, values, buffer.DstSelect()); for (u32 i = 0; i < 4; i++) { - const IR::F32 comp = [&] { - switch (buffer.GetSwizzle(i)) { - case AmdGpu::CompSwizzle::One: - return ir.Imm32(1.f); - case AmdGpu::CompSwizzle::Zero: - return ir.Imm32(0.f); - case AmdGpu::CompSwizzle::Red: - return ir.GetAttribute(attr, 0); - case AmdGpu::CompSwizzle::Green: - return ir.GetAttribute(attr, 1); - case AmdGpu::CompSwizzle::Blue: - return ir.GetAttribute(attr, 2); - case AmdGpu::CompSwizzle::Alpha: - return ir.GetAttribute(attr, 3); - default: - UNREACHABLE(); - } - }(); - ir.SetVectorReg(dst_reg++, comp); + ir.SetVectorReg(dst_reg++, IR::F32{ir.CompositeExtract(swizzled, i)}); } // In case of programmable step rates we need to fallback to instance data pulling in diff --git a/src/shader_recompiler/frontend/translate/vector_memory.cpp b/src/shader_recompiler/frontend/translate/vector_memory.cpp index 79d46cd42..c5be08b7d 100644 --- a/src/shader_recompiler/frontend/translate/vector_memory.cpp +++ b/src/shader_recompiler/frontend/translate/vector_memory.cpp @@ -326,7 +326,7 @@ void Translator::BUFFER_STORE_FORMAT(u32 num_dwords, const GcnInst& inst) { const IR::VectorReg src_reg{inst.src[1].code}; - std::array comps{}; + std::array comps{}; for (u32 i = 0; i < num_dwords; i++) { comps[i] = ir.GetVectorReg(src_reg + i); } @@ -424,7 +424,7 @@ void Translator::IMAGE_LOAD(bool has_mip, const GcnInst& inst) { if (((mimg.dmask >> i) & 1) == 0) { continue; } - IR::U32 value = IR::U32{ir.CompositeExtract(texel, i)}; + IR::F32 value = IR::F32{ir.CompositeExtract(texel, i)}; ir.SetVectorReg(dest_reg++, value); } } diff --git a/src/shader_recompiler/ir/ir_emitter.cpp b/src/shader_recompiler/ir/ir_emitter.cpp index 20e6eae0b..823f9bdcd 100644 --- a/src/shader_recompiler/ir/ir_emitter.cpp +++ b/src/shader_recompiler/ir/ir_emitter.cpp @@ -663,6 +663,86 @@ Value IREmitter::CompositeInsert(const Value& vector, const Value& object, size_ } } +Value IREmitter::CompositeShuffle(const Value& vector1, const Value& vector2, size_t comp0, + size_t comp1) { + if (vector1.Type() != vector2.Type()) { + UNREACHABLE_MSG("Mismatching types {} and {}", vector1.Type(), vector2.Type()); + } + if (comp0 >= 4 || comp1 >= 4) { + UNREACHABLE_MSG("One or more out of bounds elements {}, {}", comp0, comp1); + } + const auto shuffle{[&](Opcode opcode) -> Value { + return Inst(opcode, vector1, vector2, Value{static_cast(comp0)}, + Value{static_cast(comp1)}); + }}; + switch (vector1.Type()) { + case Type::U32x4: + return shuffle(Opcode::CompositeShuffleU32x2); + case Type::F16x4: + return shuffle(Opcode::CompositeShuffleF16x2); + case Type::F32x4: + return shuffle(Opcode::CompositeShuffleF32x2); + case Type::F64x4: + return shuffle(Opcode::CompositeShuffleF64x2); + default: + ThrowInvalidType(vector1.Type()); + } +} + +Value IREmitter::CompositeShuffle(const Value& vector1, const Value& vector2, size_t comp0, + size_t comp1, size_t comp2) { + if (vector1.Type() != vector2.Type()) { + UNREACHABLE_MSG("Mismatching types {} and {}", vector1.Type(), vector2.Type()); + } + if (comp0 >= 6 || comp1 >= 6 || comp2 >= 6) { + UNREACHABLE_MSG("One or more out of bounds elements {}, {}, {}", comp0, comp1, comp2); + } + const auto shuffle{[&](Opcode opcode) -> Value { + return Inst(opcode, vector1, vector2, Value{static_cast(comp0)}, + Value{static_cast(comp1)}, Value{static_cast(comp2)}); + }}; + switch (vector1.Type()) { + case Type::U32x4: + return shuffle(Opcode::CompositeShuffleU32x3); + case Type::F16x4: + return shuffle(Opcode::CompositeShuffleF16x3); + case Type::F32x4: + return shuffle(Opcode::CompositeShuffleF32x3); + case Type::F64x4: + return shuffle(Opcode::CompositeShuffleF64x3); + default: + ThrowInvalidType(vector1.Type()); + } +} + +Value IREmitter::CompositeShuffle(const Value& vector1, const Value& vector2, size_t comp0, + size_t comp1, size_t comp2, size_t comp3) { + if (vector1.Type() != vector2.Type()) { + UNREACHABLE_MSG("Mismatching types {} and {}", vector1.Type(), vector2.Type()); + } + if (comp0 >= 8 || comp1 >= 8 || comp2 >= 8 || comp3 >= 8) { + UNREACHABLE_MSG("One or more out of bounds elements {}, {}, {}, {}", comp0, comp1, comp2, + comp3); + } + const auto shuffle{[&](Opcode opcode) -> Value { + return Inst(opcode, vector1, vector2, Value{static_cast(comp0)}, + Value{static_cast(comp1)}, Value{static_cast(comp2)}, + Value{static_cast(comp3)}); + }}; + switch (vector1.Type()) { + case Type::U32x4: + return shuffle(Opcode::CompositeShuffleU32x4); + case Type::F16x4: + return shuffle(Opcode::CompositeShuffleF16x4); + case Type::F32x4: + return shuffle(Opcode::CompositeShuffleF32x4); + case Type::F64x4: + return shuffle(Opcode::CompositeShuffleF64x4); + default: + ThrowInvalidType(vector1.Type()); + } +} + Value IREmitter::Select(const U1& condition, const Value& true_value, const Value& false_value) { if (true_value.Type() != false_value.Type()) { UNREACHABLE_MSG("Mismatching types {} and {}", true_value.Type(), false_value.Type()); diff --git a/src/shader_recompiler/ir/ir_emitter.h b/src/shader_recompiler/ir/ir_emitter.h index f65baee2a..9aab9459b 100644 --- a/src/shader_recompiler/ir/ir_emitter.h +++ b/src/shader_recompiler/ir/ir_emitter.h @@ -155,6 +155,13 @@ public: [[nodiscard]] Value CompositeExtract(const Value& vector, size_t element); [[nodiscard]] Value CompositeInsert(const Value& vector, const Value& object, size_t element); + [[nodiscard]] Value CompositeShuffle(const Value& vector1, const Value& vector2, size_t comp0, + size_t comp1); + [[nodiscard]] Value CompositeShuffle(const Value& vector1, const Value& vector2, size_t comp0, + size_t comp1, size_t comp2); + [[nodiscard]] Value CompositeShuffle(const Value& vector1, const Value& vector2, size_t comp0, + size_t comp1, size_t comp2, size_t comp3); + [[nodiscard]] Value Select(const U1& condition, const Value& true_value, const Value& false_value); diff --git a/src/shader_recompiler/ir/opcodes.inc b/src/shader_recompiler/ir/opcodes.inc index 1194c3792..6242a230e 100644 --- a/src/shader_recompiler/ir/opcodes.inc +++ b/src/shader_recompiler/ir/opcodes.inc @@ -99,7 +99,7 @@ OPCODE(StoreBufferU32, Void, Opaq OPCODE(StoreBufferU32x2, Void, Opaque, Opaque, U32x2, ) OPCODE(StoreBufferU32x3, Void, Opaque, Opaque, U32x3, ) OPCODE(StoreBufferU32x4, Void, Opaque, Opaque, U32x4, ) -OPCODE(StoreBufferFormatF32, Void, Opaque, Opaque, U32x4, ) +OPCODE(StoreBufferFormatF32, Void, Opaque, Opaque, F32x4, ) // Buffer atomic operations OPCODE(BufferAtomicIAdd32, U32, Opaque, Opaque, U32 ) @@ -124,6 +124,9 @@ OPCODE(CompositeExtractU32x4, U32, U32x OPCODE(CompositeInsertU32x2, U32x2, U32x2, U32, U32, ) OPCODE(CompositeInsertU32x3, U32x3, U32x3, U32, U32, ) OPCODE(CompositeInsertU32x4, U32x4, U32x4, U32, U32, ) +OPCODE(CompositeShuffleU32x2, U32x2, U32x2, U32x2, U32, U32, ) +OPCODE(CompositeShuffleU32x3, U32x3, U32x3, U32x3, U32, U32, U32, ) +OPCODE(CompositeShuffleU32x4, U32x4, U32x4, U32x4, U32, U32, U32, U32, ) OPCODE(CompositeConstructF16x2, F16x2, F16, F16, ) OPCODE(CompositeConstructF16x3, F16x3, F16, F16, F16, ) OPCODE(CompositeConstructF16x4, F16x4, F16, F16, F16, F16, ) @@ -133,6 +136,9 @@ OPCODE(CompositeExtractF16x4, F16, F16x OPCODE(CompositeInsertF16x2, F16x2, F16x2, F16, U32, ) OPCODE(CompositeInsertF16x3, F16x3, F16x3, F16, U32, ) OPCODE(CompositeInsertF16x4, F16x4, F16x4, F16, U32, ) +OPCODE(CompositeShuffleF16x2, F16x2, F16x2, F16x2, U32, U32, ) +OPCODE(CompositeShuffleF16x3, F16x3, F16x3, F16x3, U32, U32, U32, ) +OPCODE(CompositeShuffleF16x4, F16x4, F16x4, F16x4, U32, U32, U32, U32, ) OPCODE(CompositeConstructF32x2, F32x2, F32, F32, ) OPCODE(CompositeConstructF32x3, F32x3, F32, F32, F32, ) OPCODE(CompositeConstructF32x4, F32x4, F32, F32, F32, F32, ) @@ -142,6 +148,9 @@ OPCODE(CompositeExtractF32x4, F32, F32x OPCODE(CompositeInsertF32x2, F32x2, F32x2, F32, U32, ) OPCODE(CompositeInsertF32x3, F32x3, F32x3, F32, U32, ) OPCODE(CompositeInsertF32x4, F32x4, F32x4, F32, U32, ) +OPCODE(CompositeShuffleF32x2, F32x2, F32x2, F32x2, U32, U32, ) +OPCODE(CompositeShuffleF32x3, F32x3, F32x3, F32x3, U32, U32, U32, ) +OPCODE(CompositeShuffleF32x4, F32x4, F32x4, F32x4, U32, U32, U32, U32, ) OPCODE(CompositeConstructF64x2, F64x2, F64, F64, ) OPCODE(CompositeConstructF64x3, F64x3, F64, F64, F64, ) OPCODE(CompositeConstructF64x4, F64x4, F64, F64, F64, F64, ) @@ -151,6 +160,9 @@ OPCODE(CompositeExtractF64x4, F64, F64x OPCODE(CompositeInsertF64x2, F64x2, F64x2, F64, U32, ) OPCODE(CompositeInsertF64x3, F64x3, F64x3, F64, U32, ) OPCODE(CompositeInsertF64x4, F64x4, F64x4, F64, U32, ) +OPCODE(CompositeShuffleF64x2, F64x2, F64x2, F64x2, U32, U32, ) +OPCODE(CompositeShuffleF64x3, F64x3, F64x3, F64x3, U32, U32, U32, ) +OPCODE(CompositeShuffleF64x4, F64x4, F64x4, F64x4, U32, U32, U32, U32, ) // Select operations OPCODE(SelectU1, U1, U1, U1, U1, ) @@ -346,8 +358,8 @@ OPCODE(ImageGatherDref, F32x4, Opaq OPCODE(ImageQueryDimensions, U32x4, Opaque, U32, U1, ) OPCODE(ImageQueryLod, F32x4, Opaque, Opaque, ) OPCODE(ImageGradient, F32x4, Opaque, Opaque, Opaque, Opaque, Opaque, F32, ) -OPCODE(ImageRead, U32x4, Opaque, Opaque, U32, U32, ) -OPCODE(ImageWrite, Void, Opaque, Opaque, U32, U32, U32x4, ) +OPCODE(ImageRead, F32x4, Opaque, Opaque, U32, U32, ) +OPCODE(ImageWrite, Void, Opaque, Opaque, U32, U32, F32x4, ) // Image atomic operations OPCODE(ImageAtomicIAdd32, U32, Opaque, Opaque, U32, ) diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index e6d23bfe7..636752912 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -8,6 +8,7 @@ #include "shader_recompiler/ir/breadth_first_search.h" #include "shader_recompiler/ir/ir_emitter.h" #include "shader_recompiler/ir/program.h" +#include "shader_recompiler/ir/reinterpret.h" #include "video_core/amdgpu/resource.h" namespace Shader::Optimization { @@ -128,35 +129,6 @@ bool IsImageInstruction(const IR::Inst& inst) { } } -IR::Value SwizzleVector(IR::IREmitter& ir, auto sharp, IR::Value texel) { - boost::container::static_vector comps; - for (u32 i = 0; i < 4; i++) { - switch (sharp.GetSwizzle(i)) { - case AmdGpu::CompSwizzle::Zero: - comps.emplace_back(ir.Imm32(0.f)); - break; - case AmdGpu::CompSwizzle::One: - comps.emplace_back(ir.Imm32(1.f)); - break; - case AmdGpu::CompSwizzle::Red: - comps.emplace_back(ir.CompositeExtract(texel, 0)); - break; - case AmdGpu::CompSwizzle::Green: - comps.emplace_back(ir.CompositeExtract(texel, 1)); - break; - case AmdGpu::CompSwizzle::Blue: - comps.emplace_back(ir.CompositeExtract(texel, 2)); - break; - case AmdGpu::CompSwizzle::Alpha: - comps.emplace_back(ir.CompositeExtract(texel, 3)); - break; - default: - UNREACHABLE(); - } - } - return ir.CompositeConstruct(comps[0], comps[1], comps[2], comps[3]); -}; - class Descriptors { public: explicit Descriptors(Info& info_) @@ -409,15 +381,6 @@ void PatchTextureBufferInstruction(IR::Block& block, IR::Inst& inst, Info& info, IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; inst.SetArg(0, ir.Imm32(binding)); ASSERT(!buffer.swizzle_enable && !buffer.add_tid_enable); - - // Apply dst_sel swizzle on formatted buffer instructions - if (inst.GetOpcode() == IR::Opcode::StoreBufferFormatF32) { - inst.SetArg(2, SwizzleVector(ir, buffer, inst.Arg(2))); - } else { - const auto inst_info = inst.Flags(); - const auto texel = ir.LoadBufferFormat(inst.Arg(0), inst.Arg(1), inst_info); - inst.ReplaceUsesWith(SwizzleVector(ir, buffer, texel)); - } } IR::Value PatchCubeCoord(IR::IREmitter& ir, const IR::Value& s, const IR::Value& t, @@ -765,10 +728,6 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip }(); inst.SetArg(1, coords); - if (inst.GetOpcode() == IR::Opcode::ImageWrite) { - inst.SetArg(4, SwizzleVector(ir, image, inst.Arg(4))); - } - if (inst_info.has_lod) { ASSERT(inst.GetOpcode() == IR::Opcode::ImageRead || inst.GetOpcode() == IR::Opcode::ImageWrite); @@ -783,6 +742,50 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip } } +void PatchTextureBufferInterpretation(IR::Block& block, IR::Inst& inst, Info& info) { + const auto binding = inst.Arg(0).U32(); + const auto buffer_res = info.texture_buffers[binding]; + const auto buffer = buffer_res.GetSharp(info); + if (!buffer.Valid()) { + // Don't need to swizzle invalid buffer. + return; + } + + IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; + if (inst.GetOpcode() == IR::Opcode::StoreBufferFormatF32) { + inst.SetArg(2, ApplySwizzle(ir, inst.Arg(2), buffer.DstSelect())); + } else if (inst.GetOpcode() == IR::Opcode::LoadBufferFormatF32) { + const auto inst_info = inst.Flags(); + const auto texel = ir.LoadBufferFormat(inst.Arg(0), inst.Arg(1), inst_info); + const auto swizzled = ApplySwizzle(ir, texel, buffer.DstSelect()); + inst.ReplaceUsesWith(swizzled); + } +} + +void PatchImageInterpretation(IR::Block& block, IR::Inst& inst, Info& info) { + const auto binding = inst.Arg(0).U32(); + const auto image_res = info.images[binding & 0xFFFF]; + const auto image = image_res.GetSharp(info); + if (!image.Valid() || !image_res.IsStorage(image)) { + // Don't need to swizzle invalid or non-storage image. + return; + } + + IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; + if (inst.GetOpcode() == IR::Opcode::ImageWrite) { + inst.SetArg(4, ApplySwizzle(ir, inst.Arg(4), image.DstSelect())); + } else if (inst.GetOpcode() == IR::Opcode::ImageRead) { + const auto inst_info = inst.Flags(); + const auto lod = inst.Arg(2); + const auto ms = inst.Arg(3); + const auto texel = + ir.ImageRead(inst.Arg(0), inst.Arg(1), lod.IsEmpty() ? IR::U32{} : IR::U32{lod}, + ms.IsEmpty() ? IR::U32{} : IR::U32{ms}, inst_info); + const auto swizzled = ApplySwizzle(ir, texel, image.DstSelect()); + inst.ReplaceUsesWith(swizzled); + } +} + void PatchDataRingInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descriptors& descriptors) { // Insert gds binding in the shader if it doesn't exist already. @@ -852,6 +855,19 @@ void ResourceTrackingPass(IR::Program& program) { } } } + // Second pass to reinterpret format read/write where needed, since we now know + // the bindings and their properties. + for (IR::Block* const block : program.blocks) { + for (IR::Inst& inst : block->Instructions()) { + if (IsTextureBufferInstruction(inst)) { + PatchTextureBufferInterpretation(*block, inst, info); + continue; + } + if (IsImageInstruction(inst)) { + PatchImageInterpretation(*block, inst, info); + } + } + } } } // namespace Shader::Optimization diff --git a/src/shader_recompiler/ir/reinterpret.h b/src/shader_recompiler/ir/reinterpret.h new file mode 100644 index 000000000..73d587a56 --- /dev/null +++ b/src/shader_recompiler/ir/reinterpret.h @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "shader_recompiler/ir/ir_emitter.h" +#include "video_core/amdgpu/resource.h" + +namespace Shader::IR { + +/// Applies a component swizzle to a vec4. +inline Value ApplySwizzle(IREmitter& ir, const Value& vector, const AmdGpu::CompMapping& swizzle) { + // Constants are indexed as 0 and 1, and components are 4-7. Thus we can apply a swizzle + // using two vectors and a shuffle, using one vector of constants and one of the components. + const auto zero = ir.Imm32(0.f); + const auto one = ir.Imm32(1.f); + const auto constants_vec = ir.CompositeConstruct(zero, one, zero, zero); + const auto swizzled = + ir.CompositeShuffle(constants_vec, vector, size_t(swizzle.r), size_t(swizzle.g), + size_t(swizzle.b), size_t(swizzle.a)); + return swizzled; +} + +} // namespace Shader::IR diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index bbf74f5d3..781a0b14a 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -180,7 +180,7 @@ struct FragmentRuntimeInfo { std::array inputs; struct PsColorBuffer { AmdGpu::NumberFormat num_format; - MrtSwizzle mrt_swizzle; + AmdGpu::CompMapping swizzle; auto operator<=>(const PsColorBuffer&) const noexcept = default; }; diff --git a/src/shader_recompiler/specialization.h b/src/shader_recompiler/specialization.h index 5bf97ee51..f8a86c63b 100644 --- a/src/shader_recompiler/specialization.h +++ b/src/shader_recompiler/specialization.h @@ -31,7 +31,7 @@ struct BufferSpecialization { struct TextureBufferSpecialization { bool is_integer = false; - u32 dst_select = 0; + AmdGpu::CompMapping dst_select{}; auto operator<=>(const TextureBufferSpecialization&) const = default; }; @@ -40,13 +40,9 @@ struct ImageSpecialization { AmdGpu::ImageType type = AmdGpu::ImageType::Color2D; bool is_integer = false; bool is_storage = false; - u32 dst_select = 0; + AmdGpu::CompMapping dst_select{}; - bool operator==(const ImageSpecialization& other) const { - return type == other.type && is_integer == other.is_integer && - is_storage == other.is_storage && - (dst_select != 0 ? dst_select == other.dst_select : true); - } + auto operator<=>(const ImageSpecialization&) const = default; }; struct FMaskSpecialization { diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index 83271a82d..f1607f03e 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -889,10 +889,54 @@ struct Liverpool { return !info.linear_general; } - NumberFormat NumFormat() const { + [[nodiscard]] DataFormat DataFormat() const { + return RemapDataFormat(info.format); + } + + [[nodiscard]] NumberFormat NumFormat() const { // There is a small difference between T# and CB number types, account for it. - return info.number_type == AmdGpu::NumberFormat::SnormNz ? AmdGpu::NumberFormat::Srgb - : info.number_type.Value(); + return RemapNumberFormat(info.number_type == NumberFormat::SnormNz + ? NumberFormat::Srgb + : info.number_type.Value()); + } + + [[nodiscard]] CompMapping Swizzle() const { + // clang-format off + static constexpr std::array, 4> mrt_swizzles{{ + // Standard + std::array{{ + {.r = CompSwizzle::Red, .g = CompSwizzle::Zero, .b = CompSwizzle::Zero, .a = CompSwizzle::Zero}, + {.r = CompSwizzle::Red, .g = CompSwizzle::Green, .b = CompSwizzle::Zero, .a = CompSwizzle::Zero}, + {.r = CompSwizzle::Red, .g = CompSwizzle::Green, .b = CompSwizzle::Blue, .a = CompSwizzle::Zero}, + {.r = CompSwizzle::Red, .g = CompSwizzle::Green, .b = CompSwizzle::Blue, .a = CompSwizzle::Alpha}, + }}, + // Alternate + std::array{{ + {.r = CompSwizzle::Green, .g = CompSwizzle::Zero, .b = CompSwizzle::Zero, .a = CompSwizzle::Zero}, + {.r = CompSwizzle::Red, .g = CompSwizzle::Alpha, .b = CompSwizzle::Zero, .a = CompSwizzle::Zero}, + {.r = CompSwizzle::Red, .g = CompSwizzle::Green, .b = CompSwizzle::Alpha, .a = CompSwizzle::Zero}, + {.r = CompSwizzle::Blue, .g = CompSwizzle::Green, .b = CompSwizzle::Red, .a = CompSwizzle::Alpha}, + }}, + // StandardReverse + std::array{{ + {.r = CompSwizzle::Blue, .g = CompSwizzle::Zero, .b = CompSwizzle::Zero, .a = CompSwizzle::Zero}, + {.r = CompSwizzle::Green, .g = CompSwizzle::Red, .b = CompSwizzle::Zero, .a = CompSwizzle::Zero}, + {.r = CompSwizzle::Blue, .g = CompSwizzle::Green, .b = CompSwizzle::Red, .a = CompSwizzle::Zero}, + {.r = CompSwizzle::Alpha, .g = CompSwizzle::Blue, .b = CompSwizzle::Green, .a = CompSwizzle::Red}, + }}, + // AlternateReverse + std::array{{ + {.r = CompSwizzle::Alpha, .g = CompSwizzle::Zero, .b = CompSwizzle::Zero, .a = CompSwizzle::Zero}, + {.r = CompSwizzle::Alpha, .g = CompSwizzle::Red, .b = CompSwizzle::Zero, .a = CompSwizzle::Zero}, + {.r = CompSwizzle::Alpha, .g = CompSwizzle::Green, .b = CompSwizzle::Red, .a = CompSwizzle::Zero}, + {.r = CompSwizzle::Alpha, .g = CompSwizzle::Red, .b = CompSwizzle::Green, .a = CompSwizzle::Blue}, + }}, + }}; + // clang-format on + const auto swap_idx = static_cast(info.comp_swap.Value()); + const auto components_idx = NumComponents(info.format) - 1; + const auto mrt_swizzle = mrt_swizzles[swap_idx][components_idx]; + return RemapComponents(info.format, mrt_swizzle); } }; diff --git a/src/video_core/amdgpu/resource.h b/src/video_core/amdgpu/resource.h index 6bbe1fb7e..4de25adbf 100644 --- a/src/video_core/amdgpu/resource.h +++ b/src/video_core/amdgpu/resource.h @@ -20,6 +20,85 @@ enum class CompSwizzle : u32 { Alpha = 7, }; +struct CompMapping { + CompSwizzle r : 3; + CompSwizzle g : 3; + CompSwizzle b : 3; + CompSwizzle a : 3; + + auto operator<=>(const CompMapping& other) const = default; + + template + [[nodiscard]] std::array Apply(const std::array& data) const { + return { + ApplySingle(data, r), + ApplySingle(data, g), + ApplySingle(data, b), + ApplySingle(data, a), + }; + } + +private: + template + T ApplySingle(const std::array& data, const CompSwizzle swizzle) const { + switch (swizzle) { + case CompSwizzle::Zero: + return T(0); + case CompSwizzle::One: + return T(1); + case CompSwizzle::Red: + return data[0]; + case CompSwizzle::Green: + return data[1]; + case CompSwizzle::Blue: + return data[2]; + case CompSwizzle::Alpha: + return data[3]; + default: + UNREACHABLE(); + } + } +}; + +inline DataFormat RemapDataFormat(const DataFormat format) { + switch (format) { + case DataFormat::Format11_11_10: + return DataFormat::Format10_11_11; + case DataFormat::Format10_10_10_2: + return DataFormat::Format2_10_10_10; + case DataFormat::Format5_5_5_1: + return DataFormat::Format1_5_5_5; + default: + return format; + } +} + +inline NumberFormat RemapNumberFormat(const NumberFormat format) { + return format; +} + +inline CompMapping RemapComponents(const DataFormat format, const CompMapping components) { + switch (format) { + case DataFormat::Format11_11_10: + return { + .r = components.b, + .g = components.g, + .b = components.r, + .a = components.a, + }; + case DataFormat::Format10_10_10_2: + case DataFormat::Format5_5_5_1: + return { + .r = components.a, + .g = components.b, + .b = components.g, + .a = components.r, + }; + default: + return components; + } +} + // Table 8.5 Buffer Resource Descriptor [Sea Islands Series Instruction Set Architecture] struct Buffer { u64 base_address : 44; @@ -52,21 +131,22 @@ struct Buffer { return std::memcmp(this, &other, sizeof(Buffer)) == 0; } - u32 DstSelect() const { - return dst_sel_x | (dst_sel_y << 3) | (dst_sel_z << 6) | (dst_sel_w << 9); - } - - CompSwizzle GetSwizzle(u32 comp) const noexcept { - const std::array select{dst_sel_x, dst_sel_y, dst_sel_z, dst_sel_w}; - return static_cast(select[comp]); + CompMapping DstSelect() const { + const CompMapping dst_sel{ + .r = CompSwizzle(dst_sel_x), + .g = CompSwizzle(dst_sel_y), + .b = CompSwizzle(dst_sel_z), + .a = CompSwizzle(dst_sel_w), + }; + return RemapComponents(DataFormat(data_format), dst_sel); } NumberFormat GetNumberFmt() const noexcept { - return static_cast(num_format); + return RemapNumberFormat(NumberFormat(num_format)); } DataFormat GetDataFmt() const noexcept { - return static_cast(data_format); + return RemapDataFormat(DataFormat(data_format)); } u32 GetStride() const noexcept { @@ -186,10 +266,11 @@ struct Image { static constexpr Image Null() { Image image{}; image.data_format = u64(DataFormat::Format8_8_8_8); - image.dst_sel_x = 4; - image.dst_sel_y = 5; - image.dst_sel_z = 6; - image.dst_sel_w = 7; + image.num_format = u64(NumberFormat::Unorm); + image.dst_sel_x = u64(CompSwizzle::Red); + image.dst_sel_y = u64(CompSwizzle::Green); + image.dst_sel_z = u64(CompSwizzle::Blue); + image.dst_sel_w = u64(CompSwizzle::Alpha); image.tiling_index = u64(TilingMode::Texture_MicroTiled); image.type = u64(ImageType::Color2D); return image; @@ -207,43 +288,14 @@ struct Image { return base_address != 0; } - u32 DstSelect() const { - return dst_sel_x | (dst_sel_y << 3) | (dst_sel_z << 6) | (dst_sel_w << 9); - } - - CompSwizzle GetSwizzle(u32 comp) const noexcept { - const std::array select{dst_sel_x, dst_sel_y, dst_sel_z, dst_sel_w}; - return static_cast(select[comp]); - } - - static char SelectComp(u32 sel) { - switch (sel) { - case 0: - return '0'; - case 1: - return '1'; - case 4: - return 'R'; - case 5: - return 'G'; - case 6: - return 'B'; - case 7: - return 'A'; - default: - UNREACHABLE(); - } - } - - std::string DstSelectName() const { - std::string result = "["; - u32 dst_sel = DstSelect(); - for (u32 i = 0; i < 4; i++) { - result += SelectComp(dst_sel & 7); - dst_sel >>= 3; - } - result += ']'; - return result; + CompMapping DstSelect() const { + const CompMapping dst_sel{ + .r = CompSwizzle(dst_sel_x), + .g = CompSwizzle(dst_sel_y), + .b = CompSwizzle(dst_sel_z), + .a = CompSwizzle(dst_sel_w), + }; + return RemapComponents(DataFormat(data_format), dst_sel); } u32 Pitch() const { @@ -285,11 +337,11 @@ struct Image { } DataFormat GetDataFmt() const noexcept { - return static_cast(data_format); + return RemapDataFormat(DataFormat(data_format)); } NumberFormat GetNumberFmt() const noexcept { - return static_cast(num_format); + return RemapNumberFormat(NumberFormat(num_format)); } TilingMode GetTilingMode() const { diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp index 6bd50ab06..c41b760ba 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp @@ -324,6 +324,34 @@ vk::BorderColor BorderColor(AmdGpu::BorderColor color) { } } +vk::ComponentSwizzle ComponentSwizzle(AmdGpu::CompSwizzle comp_swizzle) { + switch (comp_swizzle) { + case AmdGpu::CompSwizzle::Zero: + return vk::ComponentSwizzle::eZero; + case AmdGpu::CompSwizzle::One: + return vk::ComponentSwizzle::eOne; + case AmdGpu::CompSwizzle::Red: + return vk::ComponentSwizzle::eR; + case AmdGpu::CompSwizzle::Green: + return vk::ComponentSwizzle::eG; + case AmdGpu::CompSwizzle::Blue: + return vk::ComponentSwizzle::eB; + case AmdGpu::CompSwizzle::Alpha: + return vk::ComponentSwizzle::eA; + default: + UNREACHABLE(); + } +} + +vk::ComponentMapping ComponentMapping(AmdGpu::CompMapping comp_mapping) { + return vk::ComponentMapping{ + .r = ComponentSwizzle(comp_mapping.r), + .g = ComponentSwizzle(comp_mapping.g), + .b = ComponentSwizzle(comp_mapping.b), + .a = ComponentSwizzle(comp_mapping.a), + }; +} + static constexpr vk::FormatFeatureFlags2 BufferRead = vk::FormatFeatureFlagBits2::eUniformTexelBuffer | vk::FormatFeatureFlagBits2::eVertexBuffer; static constexpr vk::FormatFeatureFlags2 BufferWrite = @@ -538,10 +566,8 @@ std::span SurfaceFormats() { // 10_11_11 CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format10_11_11, AmdGpu::NumberFormat::Float, vk::Format::eB10G11R11UfloatPack32), - // 11_11_10 - CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format11_11_10, AmdGpu::NumberFormat::Float, - vk::Format::eB10G11R11UfloatPack32), - // 10_10_10_2 + // 11_11_10 - Remapped to 10_11_11. + // 10_10_10_2 - Remapped to 2_10_10_10. // 2_10_10_10 CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format2_10_10_10, AmdGpu::NumberFormat::Unorm, vk::Format::eA2B10G10R10UnormPack32), @@ -614,7 +640,7 @@ std::span SurfaceFormats() { // 1_5_5_5 CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format1_5_5_5, AmdGpu::NumberFormat::Unorm, vk::Format::eR5G5B5A1UnormPack16), - // 5_5_5_1 + // 5_5_5_1 - Remapped to 1_5_5_5. // 4_4_4_4 CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format4_4_4_4, AmdGpu::NumberFormat::Unorm, vk::Format::eR4G4B4A4UnormPack16), @@ -677,31 +703,6 @@ vk::Format SurfaceFormat(AmdGpu::DataFormat data_format, AmdGpu::NumberFormat nu return format->vk_format; } -vk::Format AdjustColorBufferFormat(vk::Format base_format, - Liverpool::ColorBuffer::SwapMode comp_swap) { - const bool comp_swap_alt = comp_swap == Liverpool::ColorBuffer::SwapMode::Alternate; - const bool comp_swap_reverse = comp_swap == Liverpool::ColorBuffer::SwapMode::StandardReverse; - const bool comp_swap_alt_reverse = - comp_swap == Liverpool::ColorBuffer::SwapMode::AlternateReverse; - if (comp_swap_alt) { - switch (base_format) { - case vk::Format::eR8G8B8A8Unorm: - return vk::Format::eB8G8R8A8Unorm; - case vk::Format::eB8G8R8A8Unorm: - return vk::Format::eR8G8B8A8Unorm; - case vk::Format::eR8G8B8A8Srgb: - return vk::Format::eB8G8R8A8Srgb; - case vk::Format::eB8G8R8A8Srgb: - return vk::Format::eR8G8B8A8Srgb; - case vk::Format::eA2B10G10R10UnormPack32: - return vk::Format::eA2R10G10B10UnormPack32; - default: - break; - } - } - return base_format; -} - static constexpr DepthFormatInfo CreateDepthFormatInfo( const DepthBuffer::ZFormat z_format, const DepthBuffer::StencilFormat stencil_format, const vk::Format vk_format) { @@ -744,21 +745,12 @@ vk::Format DepthFormat(DepthBuffer::ZFormat z_format, DepthBuffer::StencilFormat } vk::ClearValue ColorBufferClearValue(const AmdGpu::Liverpool::ColorBuffer& color_buffer) { - const auto comp_swap = color_buffer.info.comp_swap.Value(); - const auto format = color_buffer.info.format.Value(); - const auto number_type = color_buffer.info.number_type.Value(); + const auto comp_swizzle = color_buffer.Swizzle(); + const auto format = color_buffer.DataFormat(); + const auto number_type = color_buffer.NumFormat(); const auto& c0 = color_buffer.clear_word0; const auto& c1 = color_buffer.clear_word1; - const auto num_bits = AmdGpu::NumBits(color_buffer.info.format); - const auto num_components = AmdGpu::NumComponents(format); - - const bool comp_swap_alt = - comp_swap == AmdGpu::Liverpool::ColorBuffer::SwapMode::Alternate || - comp_swap == AmdGpu::Liverpool::ColorBuffer::SwapMode::AlternateReverse; - const bool comp_swap_reverse = - comp_swap == AmdGpu::Liverpool::ColorBuffer::SwapMode::StandardReverse || - comp_swap == AmdGpu::Liverpool::ColorBuffer::SwapMode::AlternateReverse; vk::ClearColorValue color{}; @@ -1079,26 +1071,7 @@ vk::ClearValue ColorBufferClearValue(const AmdGpu::Liverpool::ColorBuffer& color break; } - if (num_components == 1) { - if (comp_swap != Liverpool::ColorBuffer::SwapMode::Standard) { - color.float32[static_cast(comp_swap)] = color.float32[0]; - color.float32[0] = 0.0f; - } - } else { - if (comp_swap_alt && num_components == 4) { - std::swap(color.float32[0], color.float32[2]); - } - - if (comp_swap_reverse) { - std::reverse(std::begin(color.float32), std::begin(color.float32) + num_components); - } - - if (comp_swap_alt && num_components != 4) { - color.float32[3] = color.float32[num_components - 1]; - color.float32[num_components - 1] = 0.0f; - } - } - + color.float32 = comp_swizzle.Apply(color.float32); return {.color = color}; } diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.h b/src/video_core/renderer_vulkan/liverpool_to_vk.h index 25a27e20e..a68280e7d 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.h +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.h @@ -42,6 +42,10 @@ vk::SamplerMipmapMode MipFilter(AmdGpu::MipFilter filter); vk::BorderColor BorderColor(AmdGpu::BorderColor color); +vk::ComponentSwizzle ComponentSwizzle(AmdGpu::CompSwizzle comp_swizzle); + +vk::ComponentMapping ComponentMapping(AmdGpu::CompMapping comp_mapping); + struct SurfaceFormatInfo { AmdGpu::DataFormat data_format; AmdGpu::NumberFormat number_format; @@ -52,9 +56,6 @@ std::span SurfaceFormats(); vk::Format SurfaceFormat(AmdGpu::DataFormat data_format, AmdGpu::NumberFormat num_format); -vk::Format AdjustColorBufferFormat(vk::Format base_format, - Liverpool::ColorBuffer::SwapMode comp_swap); - struct DepthFormatInfo { Liverpool::DepthBuffer::ZFormat z_format; Liverpool::DepthBuffer::StencilFormat stencil_format; diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index ee8afa3e6..c8f4999b1 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -32,7 +32,7 @@ struct GraphicsPipelineKey { u32 num_color_attachments; std::array color_formats; std::array color_num_formats; - std::array mrt_swizzles; + std::array color_swizzles; vk::Format depth_format; vk::Format stencil_format; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index c880cad70..cd1b42b05 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -168,7 +168,7 @@ const Shader::RuntimeInfo& PipelineCache::BuildRuntimeInfo(Stage stage, LogicalS for (u32 i = 0; i < Shader::MaxColorBuffers; i++) { info.fs_info.color_buffers[i] = { .num_format = graphics_key.color_num_formats[i], - .mrt_swizzle = static_cast(graphics_key.mrt_swizzles[i]), + .swizzle = graphics_key.color_swizzles[i], }; } break; @@ -304,7 +304,7 @@ bool PipelineCache::RefreshGraphicsKey() { key.color_num_formats.fill(AmdGpu::NumberFormat::Unorm); key.blend_controls.fill({}); key.write_masks.fill({}); - key.mrt_swizzles.fill(Liverpool::ColorBuffer::SwapMode::Standard); + key.color_swizzles.fill({}); key.vertex_buffer_formats.fill(vk::Format::eUndefined); key.patch_control_points = 0; @@ -327,14 +327,10 @@ bool PipelineCache::RefreshGraphicsKey() { continue; } - const auto base_format = - LiverpoolToVK::SurfaceFormat(col_buf.info.format, col_buf.NumFormat()); key.color_formats[remapped_cb] = - LiverpoolToVK::AdjustColorBufferFormat(base_format, col_buf.info.comp_swap.Value()); + LiverpoolToVK::SurfaceFormat(col_buf.DataFormat(), col_buf.NumFormat()); key.color_num_formats[remapped_cb] = col_buf.NumFormat(); - if (base_format == key.color_formats[remapped_cb]) { - key.mrt_swizzles[remapped_cb] = col_buf.info.comp_swap.Value(); - } + key.color_swizzles[remapped_cb] = col_buf.Swizzle(); } fetch_shader = std::nullopt; @@ -450,7 +446,7 @@ bool PipelineCache::RefreshGraphicsKey() { // of the latter we need to change format to undefined, and either way we need to // increment the index for the null attachment binding. key.color_formats[remapped_cb] = vk::Format::eUndefined; - key.mrt_swizzles[remapped_cb] = Liverpool::ColorBuffer::SwapMode::Standard; + key.color_swizzles[remapped_cb] = {}; ++remapped_cb; continue; } diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index 2cc4aab38..0559f1be3 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -265,9 +265,9 @@ ImageInfo::ImageInfo(const AmdGpu::Liverpool::ColorBuffer& buffer, const AmdGpu::Liverpool::CbDbExtent& hint /*= {}*/) noexcept { props.is_tiled = buffer.IsTiled(); tiling_mode = buffer.GetTilingMode(); - pixel_format = LiverpoolToVK::SurfaceFormat(buffer.info.format, buffer.NumFormat()); + pixel_format = LiverpoolToVK::SurfaceFormat(buffer.DataFormat(), buffer.NumFormat()); num_samples = buffer.NumSamples(); - num_bits = NumBits(buffer.info.format); + num_bits = NumBits(buffer.DataFormat()); type = vk::ImageType::e2D; size.width = hint.Valid() ? hint.width : buffer.Pitch(); size.height = hint.Valid() ? hint.height : buffer.Height(); diff --git a/src/video_core/texture_cache/image_view.cpp b/src/video_core/texture_cache/image_view.cpp index 9e67b7f73..a9ae41dd1 100644 --- a/src/video_core/texture_cache/image_view.cpp +++ b/src/video_core/texture_cache/image_view.cpp @@ -31,25 +31,6 @@ vk::ImageViewType ConvertImageViewType(AmdGpu::ImageType type) { } } -vk::ComponentSwizzle ConvertComponentSwizzle(u32 dst_sel) { - switch (dst_sel) { - case 0: - return vk::ComponentSwizzle::eZero; - case 1: - return vk::ComponentSwizzle::eOne; - case 4: - return vk::ComponentSwizzle::eR; - case 5: - return vk::ComponentSwizzle::eG; - case 6: - return vk::ComponentSwizzle::eB; - case 7: - return vk::ComponentSwizzle::eA; - default: - UNREACHABLE(); - } -} - ImageViewInfo::ImageViewInfo(const AmdGpu::Image& image, const Shader::ImageResource& desc) noexcept : is_storage{desc.IsStorage(image)} { const auto dfmt = image.GetDataFmt(); @@ -87,21 +68,15 @@ ImageViewInfo::ImageViewInfo(const AmdGpu::Image& image, const Shader::ImageReso } if (!is_storage) { - mapping.r = ConvertComponentSwizzle(image.dst_sel_x); - mapping.g = ConvertComponentSwizzle(image.dst_sel_y); - mapping.b = ConvertComponentSwizzle(image.dst_sel_z); - mapping.a = ConvertComponentSwizzle(image.dst_sel_w); + mapping = Vulkan::LiverpoolToVK::ComponentMapping(image.DstSelect()); } } ImageViewInfo::ImageViewInfo(const AmdGpu::Liverpool::ColorBuffer& col_buffer) noexcept { - const auto base_format = - Vulkan::LiverpoolToVK::SurfaceFormat(col_buffer.info.format, col_buffer.NumFormat()); range.base.layer = col_buffer.view.slice_start; range.extent.layers = col_buffer.NumSlices() - range.base.layer; type = range.extent.layers > 1 ? vk::ImageViewType::e2DArray : vk::ImageViewType::e2D; - format = Vulkan::LiverpoolToVK::AdjustColorBufferFormat(base_format, - col_buffer.info.comp_swap.Value()); + format = Vulkan::LiverpoolToVK::SurfaceFormat(col_buffer.DataFormat(), col_buffer.NumFormat()); } ImageViewInfo::ImageViewInfo(const AmdGpu::Liverpool::DepthBuffer& depth_buffer, From e1cf1f500da9237eccd4c4a4352332e7ca8326cf Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Tue, 31 Dec 2024 00:04:26 -0800 Subject: [PATCH 313/549] native_clock: Remove unused process code. (#1989) --- src/common/native_clock.cpp | 11 ----------- src/common/native_clock.h | 1 - 2 files changed, 12 deletions(-) diff --git a/src/common/native_clock.cpp b/src/common/native_clock.cpp index c3fa637aa..0c05dbe84 100644 --- a/src/common/native_clock.cpp +++ b/src/common/native_clock.cpp @@ -4,11 +4,6 @@ #include "common/native_clock.h" #include "common/rdtsc.h" #include "common/uint128.h" -#ifdef _WIN64 -#include -#else -#include -#endif namespace Common { @@ -34,10 +29,4 @@ u64 NativeClock::GetUptime() const { return FencedRDTSC(); } -u64 NativeClock::GetProcessTimeUS() const { - timespec ret; - clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ret); - return ret.tv_nsec / 1000 + ret.tv_sec * 1000000; -} - } // namespace Common diff --git a/src/common/native_clock.h b/src/common/native_clock.h index b5e389452..1542c2f3a 100644 --- a/src/common/native_clock.h +++ b/src/common/native_clock.h @@ -20,7 +20,6 @@ public: u64 GetTimeUS(u64 base_ptc = 0) const; u64 GetTimeMS(u64 base_ptc = 0) const; u64 GetUptime() const; - u64 GetProcessTimeUS() const; private: u64 rdtsc_frequency; From 052473e04882c871b1e9bc70769ef7db6045af7b Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Tue, 31 Dec 2024 12:02:33 +0300 Subject: [PATCH 314/549] infra: emphasize the contact information (#1990) --- .github/ISSUE_TEMPLATE/app-bug-report.yaml | 4 ++-- .github/ISSUE_TEMPLATE/game-bug-report.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/app-bug-report.yaml b/.github/ISSUE_TEMPLATE/app-bug-report.yaml index c38bbb814..cd540e06e 100644 --- a/.github/ISSUE_TEMPLATE/app-bug-report.yaml +++ b/.github/ISSUE_TEMPLATE/app-bug-report.yaml @@ -10,8 +10,8 @@ body: value: | ## Important: Read First - Please do not make support requests on GitHub. Our issue tracker is for tracking bugs and feature requests only - If you need help using the emulator or unsure about your issue please contact us on [discord](https://discord.gg/bFJxfftGW6). + **Please do not make support requests on GitHub. Our issue tracker is for tracking bugs and feature requests only. + If you have a support request or are unsure about the nature of your issue please contact us on [discord](https://discord.gg/bFJxfftGW6).** Please make an effort to make sure your issue isn't already reported. diff --git a/.github/ISSUE_TEMPLATE/game-bug-report.yaml b/.github/ISSUE_TEMPLATE/game-bug-report.yaml index 7eb9441d2..407ee2fe3 100644 --- a/.github/ISSUE_TEMPLATE/game-bug-report.yaml +++ b/.github/ISSUE_TEMPLATE/game-bug-report.yaml @@ -10,8 +10,8 @@ body: value: | ## Important: Read First - Please do not make support requests on GitHub. Our issue tracker is for tracking bugs and feature requests only - If you need help using the emulator or unsure about your issue please contact us on [discord](https://discord.gg/bFJxfftGW6). + **Please do not make support requests on GitHub. Our issue tracker is for tracking bugs and feature requests only. + If you have a support request or are unsure about the nature of your issue please contact us on [discord](https://discord.gg/bFJxfftGW6).** You can also check the [Game Compatibility Repository](https://github.com/shadps4-emu/shadps4-game-compatibility) for the information about the status of the game. From f41829707dfea64bf43e9f93d5737ee45fb2036d Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Tue, 31 Dec 2024 02:16:26 -0800 Subject: [PATCH 315/549] equeue: Fix regression from Filter type. (#1992) --- src/core/libraries/kernel/equeue.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/libraries/kernel/equeue.h b/src/core/libraries/kernel/equeue.h index 17900238f..f8759137c 100644 --- a/src/core/libraries/kernel/equeue.h +++ b/src/core/libraries/kernel/equeue.h @@ -21,7 +21,7 @@ class EqueueInternal; struct EqueueEvent; struct SceKernelEvent { - enum Filter : int { + enum Filter : s16 { None = 0, Read = -1, Write = -2, From 927dc6d95c0b5b8ba0dc069cf655d8c58c230529 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Tue, 31 Dec 2024 02:38:30 -0800 Subject: [PATCH 316/549] vk_platform: Fix incorrect type for MVK debug flag. (#1993) --- src/video_core/renderer_vulkan/vk_platform.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/video_core/renderer_vulkan/vk_platform.cpp b/src/video_core/renderer_vulkan/vk_platform.cpp index ab61af6a4..7f0bcb5d2 100644 --- a/src/video_core/renderer_vulkan/vk_platform.cpp +++ b/src/video_core/renderer_vulkan/vk_platform.cpp @@ -283,6 +283,9 @@ vk::UniqueInstance CreateInstance(Frontend::WindowSystemType window_type, bool e Common::FS::GetUserPathString(Common::FS::PathType::LogDir); const char* log_path = crash_diagnostic_path.c_str(); vk::Bool32 enable_force_barriers = vk::True; +#ifdef __APPLE__ + const vk::Bool32 mvk_debug_mode = enable_crash_diagnostic ? vk::True : vk::False; +#endif const std::array layer_setings = { vk::LayerSettingEXT{ @@ -356,7 +359,7 @@ vk::UniqueInstance CreateInstance(Frontend::WindowSystemType window_type, bool e .pSettingName = "MVK_CONFIG_DEBUG", .type = vk::LayerSettingTypeEXT::eBool32, .valueCount = 1, - .pValues = &enable_crash_diagnostic, + .pValues = &mvk_debug_mode, } #endif }; From 48c51bd9eff0dc94e84d7b75afb98f8f02a28832 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Tue, 31 Dec 2024 02:38:52 -0800 Subject: [PATCH 317/549] audio: Accurate audio output timing. (#1986) * audio: Accurate audio output timing. * audio: Handle SDL audio queue stalls. * audio: Format info cleanup. --- .gitmodules | 4 - CMakeLists.txt | 5 +- LICENSES/ISC.txt | 7 - externals/CMakeLists.txt | 10 - externals/cubeb | 1 - src/common/config.cpp | 17 - src/common/config.h | 2 - src/common/ringbuffer.h | 374 -------------------- src/core/libraries/audio/audioout.cpp | 240 +++++-------- src/core/libraries/audio/audioout.h | 45 ++- src/core/libraries/audio/audioout_backend.h | 21 +- src/core/libraries/audio/cubeb_audio.cpp | 174 --------- src/core/libraries/audio/sdl_audio.cpp | 62 +++- src/qt_gui/settings_dialog.cpp | 6 - src/qt_gui/settings_dialog.ui | 23 -- 15 files changed, 170 insertions(+), 821 deletions(-) delete mode 100644 LICENSES/ISC.txt delete mode 160000 externals/cubeb delete mode 100644 src/common/ringbuffer.h delete mode 100644 src/core/libraries/audio/cubeb_audio.cpp diff --git a/.gitmodules b/.gitmodules index 1c05ba6f3..3d0d21c5b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -119,7 +119,3 @@ path = externals/MoltenVK/cereal url = https://github.com/USCiLab/cereal shallow = true -[submodule "externals/cubeb"] - path = externals/cubeb - url = https://github.com/mozilla/cubeb - shallow = true diff --git a/CMakeLists.txt b/CMakeLists.txt index 833bbe3ce..c0f675266 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -127,7 +127,6 @@ find_package(xxHash 0.8.2 MODULE) find_package(ZLIB 1.3 MODULE) find_package(Zydis 5.0.0 CONFIG) find_package(pugixml 1.14 CONFIG) -find_package(cubeb CONFIG) if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR NOT MSVC) find_package(cryptopp 8.9.0 MODULE) @@ -203,7 +202,6 @@ set(AUDIO_LIB src/core/libraries/audio/audioin.cpp src/core/libraries/audio/audioout.h src/core/libraries/audio/audioout_backend.h src/core/libraries/audio/audioout_error.h - src/core/libraries/audio/cubeb_audio.cpp src/core/libraries/audio/sdl_audio.cpp src/core/libraries/ngs2/ngs2.cpp src/core/libraries/ngs2/ngs2.h @@ -499,7 +497,6 @@ set(COMMON src/common/logging/backend.cpp src/common/polyfill_thread.h src/common/rdtsc.cpp src/common/rdtsc.h - src/common/ringbuffer.h src/common/signal_context.h src/common/signal_context.cpp src/common/singleton.h @@ -892,7 +889,7 @@ endif() create_target_directory_groups(shadps4) target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG) -target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers cubeb::cubeb) +target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers) target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h") target_compile_definitions(Dear_ImGui PRIVATE IMGUI_USER_CONFIG="${PROJECT_SOURCE_DIR}/src/imgui/imgui_config.h") diff --git a/LICENSES/ISC.txt b/LICENSES/ISC.txt deleted file mode 100644 index b9bcfa3a4..000000000 --- a/LICENSES/ISC.txt +++ /dev/null @@ -1,7 +0,0 @@ -ISC License - - - -Permission to use, copy, modify, and /or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 8bdf089f8..4350948b7 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -228,16 +228,6 @@ if (NOT TARGET stb::headers) add_library(stb::headers ALIAS stb) endif() -# cubeb -if (NOT TARGET cubeb::cubeb) - option(BUILD_TESTS "" OFF) - option(BUILD_TOOLS "" OFF) - option(BUNDLE_SPEEX "" ON) - option(USE_SANITIZERS "" OFF) - add_subdirectory(cubeb) - add_library(cubeb::cubeb ALIAS cubeb) -endif() - # Apple-only dependencies if (APPLE) # date diff --git a/externals/cubeb b/externals/cubeb deleted file mode 160000 index 9a9d034c5..000000000 --- a/externals/cubeb +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9a9d034c51859a045a34f201334f612c51e6c19d diff --git a/src/common/config.cpp b/src/common/config.cpp index 93627d8c9..deef0fa88 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -67,7 +67,6 @@ static int cursorHideTimeout = 5; // 5 seconds (default) static bool separateupdatefolder = false; static bool compatibilityData = false; static bool checkCompatibilityOnStartup = false; -static std::string audioBackend = "cubeb"; // Gui std::vector settings_install_dirs = {}; @@ -240,10 +239,6 @@ bool getCheckCompatibilityOnStartup() { return checkCompatibilityOnStartup; } -std::string getAudioBackend() { - return audioBackend; -} - void setGpuId(s32 selectedGpuId) { gpuId = selectedGpuId; } @@ -376,10 +371,6 @@ void setCheckCompatibilityOnStartup(bool use) { checkCompatibilityOnStartup = use; } -void setAudioBackend(std::string backend) { - audioBackend = backend; -} - void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h) { main_window_geometry_x = x; main_window_geometry_y = y; @@ -620,12 +611,6 @@ void load(const std::filesystem::path& path) { vkCrashDiagnostic = toml::find_or(vk, "crashDiagnostic", false); } - if (data.contains("Audio")) { - const toml::value& audio = data.at("Audio"); - - audioBackend = toml::find_or(audio, "backend", "cubeb"); - } - if (data.contains("Debug")) { const toml::value& debug = data.at("Debug"); @@ -724,7 +709,6 @@ void save(const std::filesystem::path& path) { data["Vulkan"]["rdocEnable"] = rdocEnable; data["Vulkan"]["rdocMarkersEnable"] = vkMarkers; data["Vulkan"]["crashDiagnostic"] = vkCrashDiagnostic; - data["Audio"]["backend"] = audioBackend; data["Debug"]["DebugDump"] = isDebugDump; data["Debug"]["CollectShader"] = isShaderDebug; @@ -828,7 +812,6 @@ void setDefaultValues() { separateupdatefolder = false; compatibilityData = false; checkCompatibilityOnStartup = false; - audioBackend = "cubeb"; } } // namespace Config diff --git a/src/common/config.h b/src/common/config.h index 43ef5024b..701aadb12 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -24,7 +24,6 @@ bool getEnableDiscordRPC(); bool getSeparateUpdateEnabled(); bool getCompatibilityEnabled(); bool getCheckCompatibilityOnStartup(); -std::string getAudioBackend(); std::string getLogFilter(); std::string getLogType(); @@ -76,7 +75,6 @@ void setSeparateUpdateEnabled(bool use); void setGameInstallDirs(const std::vector& settings_install_dirs_config); void setCompatibilityEnabled(bool use); void setCheckCompatibilityOnStartup(bool use); -void setAudioBackend(std::string backend); void setCursorState(s16 cursorState); void setCursorHideTimeout(int newcursorHideTimeout); diff --git a/src/common/ringbuffer.h b/src/common/ringbuffer.h deleted file mode 100644 index 6a71c2888..000000000 --- a/src/common/ringbuffer.h +++ /dev/null @@ -1,374 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2016 Mozilla Foundation -// SPDX-License-Identifier: ISC - -#pragma once - -#include -#include -#include -#include -#include -#include "common/assert.h" - -/** - * Single producer single consumer lock-free and wait-free ring buffer. - * - * This data structure allows producing data from one thread, and consuming it - * on another thread, safely and without explicit synchronization. If used on - * two threads, this data structure uses atomics for thread safety. It is - * possible to disable the use of atomics at compile time and only use this data - * structure on one thread. - * - * The role for the producer and the consumer must be constant, i.e., the - * producer should always be on one thread and the consumer should always be on - * another thread. - * - * Some words about the inner workings of this class: - * - Capacity is fixed. Only one allocation is performed, in the constructor. - * When reading and writing, the return value of the method allows checking if - * the ring buffer is empty or full. - * - We always keep the read index at least one element ahead of the write - * index, so we can distinguish between an empty and a full ring buffer: an - * empty ring buffer is when the write index is at the same position as the - * read index. A full buffer is when the write index is exactly one position - * before the read index. - * - We synchronize updates to the read index after having read the data, and - * the write index after having written the data. This means that the each - * thread can only touch a portion of the buffer that is not touched by the - * other thread. - * - Callers are expected to provide buffers. When writing to the queue, - * elements are copied into the internal storage from the buffer passed in. - * When reading from the queue, the user is expected to provide a buffer. - * Because this is a ring buffer, data might not be contiguous in memory, - * providing an external buffer to copy into is an easy way to have linear - * data for further processing. - */ -template -class RingBuffer { -public: - /** - * Constructor for a ring buffer. - * - * This performs an allocation, but is the only allocation that will happen - * for the life time of a `RingBuffer`. - * - * @param capacity The maximum number of element this ring buffer will hold. - */ - RingBuffer(int capacity) - /* One more element to distinguish from empty and full buffer. */ - : capacity_(capacity + 1) { - ASSERT(storage_capacity() < std::numeric_limits::max() / 2 && - "buffer too large for the type of index used."); - ASSERT(capacity_ > 0); - - data_.reset(new T[storage_capacity()]); - /* If this queue is using atomics, initializing those members as the last - * action in the constructor acts as a full barrier, and allow capacity() to - * be thread-safe. */ - write_index_ = 0; - read_index_ = 0; - } - /** - * Push `count` zero or default constructed elements in the array. - * - * Only safely called on the producer thread. - * - * @param count The number of elements to enqueue. - * @return The number of element enqueued. - */ - int enqueue_default(int count) { - return enqueue(nullptr, count); - } - /** - * @brief Put an element in the queue - * - * Only safely called on the producer thread. - * - * @param element The element to put in the queue. - * - * @return 1 if the element was inserted, 0 otherwise. - */ - int enqueue(T& element) { - return enqueue(&element, 1); - } - /** - * Push `count` elements in the ring buffer. - * - * Only safely called on the producer thread. - * - * @param elements a pointer to a buffer containing at least `count` elements. - * If `elements` is nullptr, zero or default constructed elements are - * enqueued. - * @param count The number of elements to read from `elements` - * @return The number of elements successfully coped from `elements` and - * inserted into the ring buffer. - */ - int enqueue(T* elements, int count) { -#ifndef NDEBUG - assert_correct_thread(producer_id); -#endif - - int wr_idx = write_index_.load(std::memory_order_relaxed); - int rd_idx = read_index_.load(std::memory_order_acquire); - - if (full_internal(rd_idx, wr_idx)) { - return 0; - } - - int to_write = std::min(available_write_internal(rd_idx, wr_idx), count); - - /* First part, from the write index to the end of the array. */ - int first_part = std::min(storage_capacity() - wr_idx, to_write); - /* Second part, from the beginning of the array */ - int second_part = to_write - first_part; - - if (elements) { - Copy(data_.get() + wr_idx, elements, first_part); - Copy(data_.get(), elements + first_part, second_part); - } else { - ConstructDefault(data_.get() + wr_idx, first_part); - ConstructDefault(data_.get(), second_part); - } - - write_index_.store(increment_index(wr_idx, to_write), std::memory_order_release); - - return to_write; - } - /** - * Retrieve at most `count` elements from the ring buffer, and copy them to - * `elements`, if non-null. - * - * Only safely called on the consumer side. - * - * @param elements A pointer to a buffer with space for at least `count` - * elements. If `elements` is `nullptr`, `count` element will be discarded. - * @param count The maximum number of elements to dequeue. - * @return The number of elements written to `elements`. - */ - int dequeue(T* elements, int count) { -#ifndef NDEBUG - assert_correct_thread(consumer_id); -#endif - - int rd_idx = read_index_.load(std::memory_order_relaxed); - int wr_idx = write_index_.load(std::memory_order_acquire); - - if (empty_internal(rd_idx, wr_idx)) { - return 0; - } - - int to_read = std::min(available_read_internal(rd_idx, wr_idx), count); - - int first_part = std::min(storage_capacity() - rd_idx, to_read); - int second_part = to_read - first_part; - - if (elements) { - Copy(elements, data_.get() + rd_idx, first_part); - Copy(elements + first_part, data_.get(), second_part); - } - - read_index_.store(increment_index(rd_idx, to_read), std::memory_order_release); - - return to_read; - } - /** - * Get the number of available element for consuming. - * - * Only safely called on the consumer thread. - * - * @return The number of available elements for reading. - */ - int available_read() const { -#ifndef NDEBUG - assert_correct_thread(consumer_id); -#endif - return available_read_internal(read_index_.load(std::memory_order_relaxed), - write_index_.load(std::memory_order_acquire)); - } - /** - * Get the number of available elements for consuming. - * - * Only safely called on the producer thread. - * - * @return The number of empty slots in the buffer, available for writing. - */ - int available_write() const { -#ifndef NDEBUG - assert_correct_thread(producer_id); -#endif - return available_write_internal(read_index_.load(std::memory_order_acquire), - write_index_.load(std::memory_order_relaxed)); - } - /** - * Get the total capacity, for this ring buffer. - * - * Can be called safely on any thread. - * - * @return The maximum capacity of this ring buffer. - */ - int capacity() const { - return storage_capacity() - 1; - } - /** - * Reset the consumer and producer thread identifier, in case the thread are - * being changed. This has to be externally synchronized. This is no-op when - * asserts are disabled. - */ - void reset_thread_ids() { -#ifndef NDEBUG - consumer_id = producer_id = std::thread::id(); -#endif - } - -private: - /** Return true if the ring buffer is empty. - * - * @param read_index the read index to consider - * @param write_index the write index to consider - * @return true if the ring buffer is empty, false otherwise. - **/ - bool empty_internal(int read_index, int write_index) const { - return write_index == read_index; - } - /** Return true if the ring buffer is full. - * - * This happens if the write index is exactly one element behind the read - * index. - * - * @param read_index the read index to consider - * @param write_index the write index to consider - * @return true if the ring buffer is full, false otherwise. - **/ - bool full_internal(int read_index, int write_index) const { - return (write_index + 1) % storage_capacity() == read_index; - } - /** - * Return the size of the storage. It is one more than the number of elements - * that can be stored in the buffer. - * - * @return the number of elements that can be stored in the buffer. - */ - int storage_capacity() const { - return capacity_; - } - /** - * Returns the number of elements available for reading. - * - * @return the number of available elements for reading. - */ - int available_read_internal(int read_index, int write_index) const { - if (write_index >= read_index) { - return write_index - read_index; - } else { - return write_index + storage_capacity() - read_index; - } - } - /** - * Returns the number of empty elements, available for writing. - * - * @return the number of elements that can be written into the array. - */ - int available_write_internal(int read_index, int write_index) const { - /* We substract one element here to always keep at least one sample - * free in the buffer, to distinguish between full and empty array. */ - int rv = read_index - write_index - 1; - if (write_index >= read_index) { - rv += storage_capacity(); - } - return rv; - } - /** - * Increments an index, wrapping it around the storage. - * - * @param index a reference to the index to increment. - * @param increment the number by which `index` is incremented. - * @return the new index. - */ - int increment_index(int index, int increment) const { - ASSERT(increment >= 0); - return (index + increment) % storage_capacity(); - } - /** - * @brief This allows checking that enqueue (resp. dequeue) are always called - * by the right thread. - * - * @param id the id of the thread that has called the calling method first. - */ -#ifndef NDEBUG - static void assert_correct_thread(std::thread::id& id) { - if (id == std::thread::id()) { - id = std::this_thread::get_id(); - return; - } - ASSERT(id == std::this_thread::get_id()); - } -#endif - /** Similar to memcpy, but accounts for the size of an element. */ - template - void PodCopy(CopyT* destination, const CopyT* source, size_t count) { - static_assert(std::is_trivial::value, "Requires trivial type"); - ASSERT(destination && source); - memcpy(destination, source, count * sizeof(CopyT)); - } - /** Similar to a memset to zero, but accounts for the size of an element. */ - template - void PodZero(ZeroT* destination, size_t count) { - static_assert(std::is_trivial::value, "Requires trivial type"); - ASSERT(destination); - memset(destination, 0, count * sizeof(ZeroT)); - } - template - void Copy(CopyT* destination, const CopyT* source, size_t count, Trait) { - for (size_t i = 0; i < count; i++) { - destination[i] = source[i]; - } - } - template - void Copy(CopyT* destination, const CopyT* source, size_t count, std::true_type) { - PodCopy(destination, source, count); - } - /** - * This allows copying a number of elements from a `source` pointer to a - * `destination` pointer, using `memcpy` if it is safe to do so, or a loop that - * calls the constructors and destructors otherwise. - */ - template - void Copy(CopyT* destination, const T* source, size_t count) { - ASSERT(destination && source); - Copy(destination, source, count, typename std::is_trivial::type()); - } - template - void ConstructDefault(ConstructT* destination, size_t count, Trait) { - for (size_t i = 0; i < count; i++) { - destination[i] = ConstructT(); - } - } - template - void ConstructDefault(ConstructT* destination, size_t count, std::true_type) { - PodZero(destination, count); - } - /** - * This allows zeroing (using memset) or default-constructing a number of - * elements calling the constructors and destructors if necessary. - */ - template - void ConstructDefault(ConstructT* destination, size_t count) { - ASSERT(destination); - ConstructDefault(destination, count, typename std::is_arithmetic::type()); - } - /** Index at which the oldest element is at, in samples. */ - std::atomic read_index_; - /** Index at which to write new elements. `write_index` is always at - * least one element ahead of `read_index_`. */ - std::atomic write_index_; - /** Maximum number of elements that can be stored in the ring buffer. */ - const int capacity_; - /** Data storage */ - std::unique_ptr data_; -#ifndef NDEBUG - /** The id of the only thread that is allowed to read from the queue. */ - mutable std::thread::id consumer_id; - /** The id of the only thread that is allowed to write from the queue. */ - mutable std::thread::id producer_id; -#endif -}; diff --git a/src/core/libraries/audio/audioout.cpp b/src/core/libraries/audio/audioout.cpp index 89ea1d3f5..d69454c39 100644 --- a/src/core/libraries/audio/audioout.cpp +++ b/src/core/libraries/audio/audioout.cpp @@ -9,6 +9,8 @@ #include "common/assert.h" #include "common/config.h" #include "common/logging/log.h" +#include "common/polyfill_thread.h" +#include "common/thread.h" #include "core/libraries/audio/audioout.h" #include "core/libraries/audio/audioout_backend.h" #include "core/libraries/audio/audioout_error.h" @@ -21,111 +23,28 @@ std::array ports_out{}; static std::unique_ptr audio; -static std::string_view GetAudioOutPort(OrbisAudioOutPort port) { - switch (port) { - case OrbisAudioOutPort::Main: - return "MAIN"; - case OrbisAudioOutPort::Bgm: - return "BGM"; - case OrbisAudioOutPort::Voice: - return "VOICE"; - case OrbisAudioOutPort::Personal: - return "PERSONAL"; - case OrbisAudioOutPort::Padspk: - return "PADSPK"; - case OrbisAudioOutPort::Aux: - return "AUX"; - default: - return "INVALID"; - } -} - -static std::string_view GetAudioOutParamFormat(OrbisAudioOutParamFormat param) { - switch (param) { - case OrbisAudioOutParamFormat::S16Mono: - return "S16_MONO"; - case OrbisAudioOutParamFormat::S16Stereo: - return "S16_STEREO"; - case OrbisAudioOutParamFormat::S16_8CH: - return "S16_8CH"; - case OrbisAudioOutParamFormat::FloatMono: - return "FLOAT_MONO"; - case OrbisAudioOutParamFormat::FloatStereo: - return "FLOAT_STEREO"; - case OrbisAudioOutParamFormat::Float_8CH: - return "FLOAT_8CH"; - case OrbisAudioOutParamFormat::S16_8CH_Std: - return "S16_8CH_STD"; - case OrbisAudioOutParamFormat::Float_8CH_Std: - return "FLOAT_8CH_STD"; - default: - return "INVALID"; - } -} - -static std::string_view GetAudioOutParamAttr(OrbisAudioOutParamAttr attr) { - switch (attr) { - case OrbisAudioOutParamAttr::None: - return "NONE"; - case OrbisAudioOutParamAttr::Restricted: - return "RESTRICTED"; - case OrbisAudioOutParamAttr::MixToMain: - return "MIX_TO_MAIN"; - default: - return "INVALID"; - } -} - -static bool IsFormatFloat(const OrbisAudioOutParamFormat format) { - switch (format) { - case OrbisAudioOutParamFormat::S16Mono: - case OrbisAudioOutParamFormat::S16Stereo: - case OrbisAudioOutParamFormat::S16_8CH: - case OrbisAudioOutParamFormat::S16_8CH_Std: - return false; - case OrbisAudioOutParamFormat::FloatMono: - case OrbisAudioOutParamFormat::FloatStereo: - case OrbisAudioOutParamFormat::Float_8CH: - case OrbisAudioOutParamFormat::Float_8CH_Std: - return true; - default: - UNREACHABLE_MSG("Unknown format"); - } -} - -static u8 GetFormatNumChannels(const OrbisAudioOutParamFormat format) { - switch (format) { - case OrbisAudioOutParamFormat::S16Mono: - case OrbisAudioOutParamFormat::FloatMono: - return 1; - case OrbisAudioOutParamFormat::S16Stereo: - case OrbisAudioOutParamFormat::FloatStereo: - return 2; - case OrbisAudioOutParamFormat::S16_8CH: - case OrbisAudioOutParamFormat::Float_8CH: - case OrbisAudioOutParamFormat::S16_8CH_Std: - case OrbisAudioOutParamFormat::Float_8CH_Std: - return 8; - default: - UNREACHABLE_MSG("Unknown format"); - } -} - -static u8 GetFormatSampleSize(const OrbisAudioOutParamFormat format) { - switch (format) { - case OrbisAudioOutParamFormat::S16Mono: - case OrbisAudioOutParamFormat::S16Stereo: - case OrbisAudioOutParamFormat::S16_8CH: - case OrbisAudioOutParamFormat::S16_8CH_Std: - return 2; - case OrbisAudioOutParamFormat::FloatMono: - case OrbisAudioOutParamFormat::FloatStereo: - case OrbisAudioOutParamFormat::Float_8CH: - case OrbisAudioOutParamFormat::Float_8CH_Std: - return 4; - default: - UNREACHABLE_MSG("Unknown format"); - } +static AudioFormatInfo GetFormatInfo(const OrbisAudioOutParamFormat format) { + static constexpr std::array format_infos = {{ + // S16Mono + {false, 2, 1, {0}}, + // S16Stereo + {false, 2, 2, {0, 1}}, + // S16_8CH + {false, 2, 8, {0, 1, 2, 3, 4, 5, 6, 7}}, + // FloatMono + {true, 4, 1, {0}}, + // FloatStereo + {true, 4, 2, {0, 1}}, + // Float_8CH + {true, 4, 8, {0, 1, 2, 3, 4, 5, 6, 7}}, + // S16_8CH_Std + {false, 2, 8, {0, 1, 2, 3, 6, 7, 4, 5}}, + // Float_8CH_Std + {true, 4, 8, {0, 1, 2, 3, 6, 7, 4, 5}}, + }}; + const auto index = static_cast(format); + ASSERT_MSG(index < format_infos.size(), "Unknown audio format {}", index); + return format_infos[index]; } int PS4_SYSV_ABI sceAudioOutDeviceIdOpen() { @@ -180,6 +99,10 @@ int PS4_SYSV_ABI sceAudioOutClose(s32 handle) { return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; } + port.output_thread.Stop(); + std::free(port.output_buffer); + port.output_buffer = nullptr; + port.output_ready = false; port.impl = nullptr; return ORBIS_OK; } @@ -263,7 +186,7 @@ int PS4_SYSV_ABI sceAudioOutGetPortState(s32 handle, OrbisAudioOutPortState* sta case OrbisAudioOutPort::Bgm: case OrbisAudioOutPort::Voice: state->output = 1; - state->channel = port.channels_num > 2 ? 2 : port.channels_num; + state->channel = port.format_info.num_channels > 2 ? 2 : port.format_info.num_channels; break; case OrbisAudioOutPort::Personal: case OrbisAudioOutPort::Padspk: @@ -311,16 +234,7 @@ int PS4_SYSV_ABI sceAudioOutInit() { if (audio != nullptr) { return ORBIS_AUDIO_OUT_ERROR_ALREADY_INIT; } - const auto backend = Config::getAudioBackend(); - if (backend == "cubeb") { - audio = std::make_unique(); - } else if (backend == "sdl") { - audio = std::make_unique(); - } else { - // Cubeb as a default fallback. - LOG_ERROR(Lib_AudioOut, "Invalid audio backend '{}', defaulting to cubeb.", backend); - audio = std::make_unique(); - } + audio = std::make_unique(); return ORBIS_OK; } @@ -354,6 +268,30 @@ int PS4_SYSV_ABI sceAudioOutMbusInit() { return ORBIS_OK; } +static void AudioOutputThread(PortOut* port, const std::stop_token& stop) { + { + const auto thread_name = fmt::format("shadPS4:AudioOutputThread:{}", fmt::ptr(port)); + Common::SetCurrentThreadName(thread_name.c_str()); + } + + Common::AccurateTimer timer( + std::chrono::nanoseconds(1000000000ULL * port->buffer_frames / port->sample_rate)); + while (true) { + timer.Start(); + { + std::unique_lock lock{port->output_mutex}; + Common::CondvarWait(port->output_cv, lock, stop, [&] { return port->output_ready; }); + if (stop.stop_requested()) { + break; + } + port->impl->Output(port->output_buffer); + port->output_ready = false; + } + port->output_cv.notify_one(); + timer.End(); + } +} + s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id, OrbisAudioOutPort port_type, s32 index, u32 length, u32 sample_rate, @@ -361,9 +299,9 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id, LOG_INFO(Lib_AudioOut, "id = {} port_type = {} index = {} length = {} sample_rate = {} " "param_type = {} attr = {}", - user_id, GetAudioOutPort(port_type), index, length, sample_rate, - GetAudioOutParamFormat(param_type.data_format), - GetAudioOutParamAttr(param_type.attributes)); + user_id, magic_enum::enum_name(port_type), index, length, sample_rate, + magic_enum::enum_name(param_type.data_format.Value()), + magic_enum::enum_name(param_type.attributes.Value())); if ((port_type < OrbisAudioOutPort::Main || port_type > OrbisAudioOutPort::Padspk) && (port_type != OrbisAudioOutPort::Aux)) { LOG_ERROR(Lib_AudioOut, "Invalid port type"); @@ -403,17 +341,18 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id, } port->type = port_type; - port->format = format; - port->is_float = IsFormatFloat(format); - port->sample_size = GetFormatSampleSize(format); - port->channels_num = GetFormatNumChannels(format); - port->samples_num = length; - port->frame_size = port->sample_size * port->channels_num; - port->buffer_size = port->frame_size * port->samples_num; - port->freq = sample_rate; + port->format_info = GetFormatInfo(format); + port->sample_rate = sample_rate; + port->buffer_frames = length; port->volume.fill(SCE_AUDIO_OUT_VOLUME_0DB); + port->impl = audio->Open(*port); + port->output_buffer = std::malloc(port->BufferSize()); + port->output_ready = false; + port->output_thread.Run( + [port](const std::stop_token& stop) { AudioOutputThread(&*port, stop); }); + return std::distance(ports_out.begin(), port) + 1; } @@ -426,24 +365,30 @@ s32 PS4_SYSV_ABI sceAudioOutOutput(s32 handle, void* ptr) { if (handle < 1 || handle > SCE_AUDIO_OUT_NUM_PORTS) { return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; } - if (ptr == nullptr) { - // Nothing to output - return ORBIS_OK; - } auto& port = ports_out.at(handle - 1); if (!port.impl) { return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; } - port.impl->Output(ptr, port.buffer_size); + { + std::unique_lock lock{port.output_mutex}; + port.output_cv.wait(lock, [&] { return !port.output_ready; }); + if (ptr != nullptr) { + std::memcpy(port.output_buffer, ptr, port.BufferSize()); + port.output_ready = true; + } + } + port.output_cv.notify_one(); return ORBIS_OK; } int PS4_SYSV_ABI sceAudioOutOutputs(OrbisAudioOutOutputParam* param, u32 num) { for (u32 i = 0; i < num; i++) { - if (const auto err = sceAudioOutOutput(param[i].handle, param[i].ptr); err != 0) - return err; + const auto [handle, ptr] = param[i]; + if (const auto ret = sceAudioOutOutput(handle, ptr); ret != ORBIS_OK) { + return ret; + } } return ORBIS_OK; } @@ -549,30 +494,9 @@ s32 PS4_SYSV_ABI sceAudioOutSetVolume(s32 handle, s32 flag, s32* vol) { return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; } - for (int i = 0; i < port.channels_num; i++, flag >>= 1u) { - auto bit = flag & 0x1u; - if (bit == 1) { - int src_index = i; - if (port.format == OrbisAudioOutParamFormat::Float_8CH_Std || - port.format == OrbisAudioOutParamFormat::S16_8CH_Std) { - switch (i) { - case 4: - src_index = 6; - break; - case 5: - src_index = 7; - break; - case 6: - src_index = 4; - break; - case 7: - src_index = 5; - break; - default: - break; - } - } - port.volume[i] = vol[src_index]; + for (int i = 0; i < port.format_info.num_channels; i++, flag >>= 1u) { + if (flag & 0x1u) { + port.volume[i] = vol[i]; } } diff --git a/src/core/libraries/audio/audioout.h b/src/core/libraries/audio/audioout.h index 58c77db99..4f7378dcd 100644 --- a/src/core/libraries/audio/audioout.h +++ b/src/core/libraries/audio/audioout.h @@ -6,6 +6,7 @@ #include #include "common/bit_field.h" +#include "core/libraries/kernel/threads.h" #include "core/libraries/system/userservice.h" namespace Libraries::AudioOut { @@ -14,12 +15,12 @@ class PortBackend; // Main up to 8 ports, BGM 1 port, voice up to 4 ports, // personal up to 4 ports, padspk up to 5 ports, aux 1 port -constexpr int SCE_AUDIO_OUT_NUM_PORTS = 22; -constexpr int SCE_AUDIO_OUT_VOLUME_0DB = 32768; // max volume value +constexpr s32 SCE_AUDIO_OUT_NUM_PORTS = 22; +constexpr s32 SCE_AUDIO_OUT_VOLUME_0DB = 32768; // max volume value enum class OrbisAudioOutPort { Main = 0, Bgm = 1, Voice = 2, Personal = 3, Padspk = 4, Aux = 127 }; -enum class OrbisAudioOutParamFormat { +enum class OrbisAudioOutParamFormat : u32 { S16Mono = 0, S16Stereo = 1, S16_8CH = 2, @@ -30,7 +31,7 @@ enum class OrbisAudioOutParamFormat { Float_8CH_Std = 7 }; -enum class OrbisAudioOutParamAttr { +enum class OrbisAudioOutParamAttr : u32 { None = 0, Restricted = 1, MixToMain = 2, @@ -59,19 +60,37 @@ struct OrbisAudioOutPortState { u64 reserved64[2]; }; +struct AudioFormatInfo { + bool is_float; + u8 sample_size; + u8 num_channels; + /// Layout array remapping channel indices, specified in this order: + /// FL, FR, FC, LFE, BL, BR, SL, SR + std::array channel_layout; + + [[nodiscard]] u16 FrameSize() const { + return sample_size * num_channels; + } +}; + struct PortOut { std::unique_ptr impl{}; + void* output_buffer; + std::mutex output_mutex; + std::condition_variable_any output_cv; + bool output_ready; + Kernel::Thread output_thread{}; + OrbisAudioOutPort type; - OrbisAudioOutParamFormat format; - bool is_float; - u8 sample_size; - u8 channels_num; - u32 samples_num; - u32 frame_size; - u32 buffer_size; - u32 freq; - std::array volume; + AudioFormatInfo format_info; + u32 sample_rate; + u32 buffer_frames; + std::array volume; + + [[nodiscard]] u32 BufferSize() const { + return buffer_frames * format_info.FrameSize(); + } }; int PS4_SYSV_ABI sceAudioOutDeviceIdOpen(); diff --git a/src/core/libraries/audio/audioout_backend.h b/src/core/libraries/audio/audioout_backend.h index f423d4963..0f36f19c8 100644 --- a/src/core/libraries/audio/audioout_backend.h +++ b/src/core/libraries/audio/audioout_backend.h @@ -3,8 +3,6 @@ #pragma once -typedef struct cubeb cubeb; - namespace Libraries::AudioOut { struct PortOut; @@ -13,7 +11,10 @@ class PortBackend { public: virtual ~PortBackend() = default; - virtual void Output(void* ptr, size_t size) = 0; + /// Guaranteed to be called in intervals of at least port buffer time, + /// with size equal to port buffer size. + virtual void Output(void* ptr) = 0; + virtual void SetVolume(const std::array& ch_volumes) = 0; }; @@ -25,20 +26,6 @@ public: virtual std::unique_ptr Open(PortOut& port) = 0; }; -class CubebAudioOut final : public AudioOutBackend { -public: - CubebAudioOut(); - ~CubebAudioOut() override; - - std::unique_ptr Open(PortOut& port) override; - -private: - cubeb* ctx = nullptr; -#ifdef _WIN32 - bool owns_com = false; -#endif -}; - class SDLAudioOut final : public AudioOutBackend { public: std::unique_ptr Open(PortOut& port) override; diff --git a/src/core/libraries/audio/cubeb_audio.cpp b/src/core/libraries/audio/cubeb_audio.cpp deleted file mode 100644 index 4127931b7..000000000 --- a/src/core/libraries/audio/cubeb_audio.cpp +++ /dev/null @@ -1,174 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include -#include - -#include "common/logging/log.h" -#include "common/ringbuffer.h" -#include "core/libraries/audio/audioout.h" -#include "core/libraries/audio/audioout_backend.h" - -#ifdef _WIN32 -#include -#endif - -namespace Libraries::AudioOut { - -class CubebPortBackend : public PortBackend { -public: - CubebPortBackend(cubeb* ctx, const PortOut& port) - : frame_size(port.frame_size), buffer(static_cast(port.buffer_size) * 4) { - if (!ctx) { - return; - } - const auto get_channel_layout = [&port] -> cubeb_channel_layout { - switch (port.channels_num) { - case 1: - return CUBEB_LAYOUT_MONO; - case 2: - return CUBEB_LAYOUT_STEREO; - case 8: - return CUBEB_LAYOUT_3F4_LFE; - default: - UNREACHABLE(); - } - }; - cubeb_stream_params stream_params = { - .format = port.is_float ? CUBEB_SAMPLE_FLOAT32LE : CUBEB_SAMPLE_S16LE, - .rate = port.freq, - .channels = port.channels_num, - .layout = get_channel_layout(), - .prefs = CUBEB_STREAM_PREF_NONE, - }; - u32 latency_frames = 512; - if (const auto ret = cubeb_get_min_latency(ctx, &stream_params, &latency_frames); - ret != CUBEB_OK) { - LOG_WARNING(Lib_AudioOut, - "Could not get minimum cubeb audio latency, falling back to default: {}", - ret); - } - char stream_name[64]; - snprintf(stream_name, sizeof(stream_name), "shadPS4 stream %p", this); - if (const auto ret = cubeb_stream_init(ctx, &stream, stream_name, nullptr, nullptr, nullptr, - &stream_params, latency_frames, &DataCallback, - &StateCallback, this); - ret != CUBEB_OK) { - LOG_ERROR(Lib_AudioOut, "Failed to create cubeb stream: {}", ret); - return; - } - if (const auto ret = cubeb_stream_start(stream); ret != CUBEB_OK) { - LOG_ERROR(Lib_AudioOut, "Failed to start cubeb stream: {}", ret); - cubeb_stream_destroy(stream); - stream = nullptr; - return; - } - } - - ~CubebPortBackend() override { - if (!stream) { - return; - } - if (const auto ret = cubeb_stream_stop(stream); ret != CUBEB_OK) { - LOG_WARNING(Lib_AudioOut, "Failed to stop cubeb stream: {}", ret); - } - cubeb_stream_destroy(stream); - stream = nullptr; - } - - void Output(void* ptr, size_t size) override { - if (!stream) { - return; - } - auto* data = static_cast(ptr); - - std::unique_lock lock{buffer_mutex}; - buffer_cv.wait(lock, [&] { return buffer.available_write() >= size; }); - buffer.enqueue(data, static_cast(size)); - } - - void SetVolume(const std::array& ch_volumes) override { - if (!stream) { - return; - } - // Cubeb does not have per-channel volumes, for now just take the maximum of the channels. - const auto vol = *std::ranges::max_element(ch_volumes); - if (const auto ret = - cubeb_stream_set_volume(stream, static_cast(vol) / SCE_AUDIO_OUT_VOLUME_0DB); - ret != CUBEB_OK) { - LOG_WARNING(Lib_AudioOut, "Failed to change cubeb stream volume: {}", ret); - } - } - -private: - static long DataCallback(cubeb_stream* stream, void* user_data, const void* in, void* out, - long num_frames) { - auto* stream_data = static_cast(user_data); - const auto out_data = static_cast(out); - const auto requested_size = static_cast(num_frames * stream_data->frame_size); - - std::unique_lock lock{stream_data->buffer_mutex}; - const auto dequeued_size = stream_data->buffer.dequeue(out_data, requested_size); - lock.unlock(); - stream_data->buffer_cv.notify_one(); - - if (dequeued_size < requested_size) { - // Need to fill remaining space with silence. - std::memset(out_data + dequeued_size, 0, requested_size - dequeued_size); - } - return num_frames; - } - - static void StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state) { - switch (state) { - case CUBEB_STATE_STARTED: - LOG_INFO(Lib_AudioOut, "Cubeb stream started"); - break; - case CUBEB_STATE_STOPPED: - LOG_INFO(Lib_AudioOut, "Cubeb stream stopped"); - break; - case CUBEB_STATE_DRAINED: - LOG_INFO(Lib_AudioOut, "Cubeb stream drained"); - break; - case CUBEB_STATE_ERROR: - LOG_ERROR(Lib_AudioOut, "Cubeb stream encountered an error"); - break; - } - } - - size_t frame_size; - RingBuffer buffer; - std::mutex buffer_mutex; - std::condition_variable buffer_cv; - cubeb_stream* stream{}; -}; - -CubebAudioOut::CubebAudioOut() { -#ifdef _WIN32 - // Need to initialize COM for this thread on Windows, in case WASAPI backend is used. - owns_com = CoInitializeEx(nullptr, COINIT_MULTITHREADED) == S_OK; -#endif - if (const auto ret = cubeb_init(&ctx, "shadPS4", nullptr); ret != CUBEB_OK) { - LOG_CRITICAL(Lib_AudioOut, "Failed to create cubeb context: {}", ret); - } -} - -CubebAudioOut::~CubebAudioOut() { - if (ctx) { - cubeb_destroy(ctx); - ctx = nullptr; - } -#ifdef _WIN32 - if (owns_com) { - CoUninitialize(); - owns_com = false; - } -#endif -} - -std::unique_ptr CubebAudioOut::Open(PortOut& port) { - return std::make_unique(ctx, port); -} - -} // namespace Libraries::AudioOut diff --git a/src/core/libraries/audio/sdl_audio.cpp b/src/core/libraries/audio/sdl_audio.cpp index 598941ba7..59d2d5cfb 100644 --- a/src/core/libraries/audio/sdl_audio.cpp +++ b/src/core/libraries/audio/sdl_audio.cpp @@ -3,6 +3,7 @@ #include #include +#include #include "common/logging/log.h" #include "core/libraries/audio/audioout.h" @@ -10,15 +11,21 @@ namespace Libraries::AudioOut { -constexpr int AUDIO_STREAM_BUFFER_THRESHOLD = 65536; // Define constant for buffer threshold - class SDLPortBackend : public PortBackend { public: - explicit SDLPortBackend(const PortOut& port) { + explicit SDLPortBackend(const PortOut& port) + : frame_size(port.format_info.FrameSize()), buffer_size(port.BufferSize()) { + // We want the latency for delivering frames out to be as small as possible, + // so set the sample frames hint to the number of frames per buffer. + const auto samples_num_str = std::to_string(port.buffer_frames); + if (!SDL_SetHint(SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES, samples_num_str.c_str())) { + LOG_WARNING(Lib_AudioOut, "Failed to set SDL audio sample frames hint to {}: {}", + samples_num_str, SDL_GetError()); + } const SDL_AudioSpec fmt = { - .format = port.is_float ? SDL_AUDIO_F32 : SDL_AUDIO_S16, - .channels = port.channels_num, - .freq = static_cast(port.freq), + .format = port.format_info.is_float ? SDL_AUDIO_F32LE : SDL_AUDIO_S16LE, + .channels = port.format_info.num_channels, + .freq = static_cast(port.sample_rate), }; stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &fmt, nullptr, nullptr); @@ -26,6 +33,15 @@ public: LOG_ERROR(Lib_AudioOut, "Failed to create SDL audio stream: {}", SDL_GetError()); return; } + queue_threshold = CalculateQueueThreshold(); + if (!SDL_SetAudioStreamInputChannelMap(stream, port.format_info.channel_layout.data(), + port.format_info.num_channels)) { + LOG_ERROR(Lib_AudioOut, "Failed to configure SDL audio stream channel map: {}", + SDL_GetError()); + SDL_DestroyAudioStream(stream); + stream = nullptr; + return; + } if (!SDL_ResumeAudioStreamDevice(stream)) { LOG_ERROR(Lib_AudioOut, "Failed to resume SDL audio stream: {}", SDL_GetError()); SDL_DestroyAudioStream(stream); @@ -42,14 +58,23 @@ public: stream = nullptr; } - void Output(void* ptr, size_t size) override { + void Output(void* ptr) override { if (!stream) { return; } - SDL_PutAudioStreamData(stream, ptr, static_cast(size)); - while (SDL_GetAudioStreamAvailable(stream) > AUDIO_STREAM_BUFFER_THRESHOLD) { - // Yield to allow the stream to drain. - std::this_thread::yield(); + // AudioOut library manages timing, but we still need to guard against the SDL + // audio queue stalling, which may happen during device changes, for example. + // Otherwise, latency may grow over time unbounded. + if (const auto queued = SDL_GetAudioStreamQueued(stream); queued >= queue_threshold) { + LOG_WARNING(Lib_AudioOut, + "SDL audio queue backed up ({} queued, {} threshold), clearing.", queued, + queue_threshold); + SDL_ClearAudioStream(stream); + // Recalculate the threshold in case this happened because of a device change. + queue_threshold = CalculateQueueThreshold(); + } + if (!SDL_PutAudioStreamData(stream, ptr, static_cast(buffer_size))) { + LOG_ERROR(Lib_AudioOut, "Failed to output to SDL audio stream: {}", SDL_GetError()); } } @@ -66,6 +91,21 @@ public: } private: + [[nodiscard]] u32 CalculateQueueThreshold() const { + SDL_AudioSpec discard; + int sdl_buffer_frames; + if (!SDL_GetAudioDeviceFormat(SDL_GetAudioStreamDevice(stream), &discard, + &sdl_buffer_frames)) { + LOG_WARNING(Lib_AudioOut, "Failed to get SDL audio stream buffer size: {}", + SDL_GetError()); + sdl_buffer_frames = 0; + } + return std::max(buffer_size, sdl_buffer_frames * frame_size) * 4; + } + + u32 frame_size; + u32 buffer_size; + u32 queue_threshold; SDL_AudioStream* stream; }; diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index 6d4de6603..0c1375c7f 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -211,7 +211,6 @@ SettingsDialog::SettingsDialog(std::span physical_devices, ui->enableCompatibilityCheckBox->installEventFilter(this); ui->checkCompatibilityOnStartupCheckBox->installEventFilter(this); ui->updateCompatibilityButton->installEventFilter(this); - ui->audioBackendComboBox->installEventFilter(this); // Input ui->hideCursorGroupBox->installEventFilter(this); @@ -305,8 +304,6 @@ void SettingsDialog::LoadValuesFromConfig() { toml::find_or(data, "General", "compatibilityEnabled", false)); ui->checkCompatibilityOnStartupCheckBox->setChecked( toml::find_or(data, "General", "checkCompatibilityOnStartup", false)); - ui->audioBackendComboBox->setCurrentText( - QString::fromStdString(toml::find_or(data, "Audio", "backend", "cubeb"))); #ifdef ENABLE_UPDATER ui->updateCheckBox->setChecked(toml::find_or(data, "General", "autoUpdate", false)); @@ -428,8 +425,6 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) { text = tr("checkCompatibilityOnStartupCheckBox"); } else if (elementName == "updateCompatibilityButton") { text = tr("updateCompatibilityButton"); - } else if (elementName == "audioBackendGroupBox") { - text = tr("audioBackendGroupBox"); } // Input @@ -543,7 +538,6 @@ void SettingsDialog::UpdateSettings() { Config::setUpdateChannel(ui->updateComboBox->currentText().toStdString()); Config::setCompatibilityEnabled(ui->enableCompatibilityCheckBox->isChecked()); Config::setCheckCompatibilityOnStartup(ui->checkCompatibilityOnStartupCheckBox->isChecked()); - Config::setAudioBackend(ui->audioBackendComboBox->currentText().toStdString()); #ifdef ENABLE_DISCORD_RPC auto* rpc = Common::Singleton::Instance(); diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index f2d6b77d2..af1edb0dd 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -263,29 +263,6 @@ - - - - Audio Backend - - - - - - - cubeb - - - - - sdl - - - - - - - From 174b5c0f954c1348be29c0b9177a6b89145dd6c9 Mon Sep 17 00:00:00 2001 From: psucien Date: Tue, 31 Dec 2024 17:24:56 +0100 Subject: [PATCH 318/549] kernel: equeue: added missing `sceKernelDeleteHRTimerEvent` --- src/core/libraries/kernel/equeue.cpp | 14 ++++++++++++++ src/core/libraries/kernel/equeue.h | 7 +++++++ 2 files changed, 21 insertions(+) diff --git a/src/core/libraries/kernel/equeue.cpp b/src/core/libraries/kernel/equeue.cpp index 03259cd22..64d4966c0 100644 --- a/src/core/libraries/kernel/equeue.cpp +++ b/src/core/libraries/kernel/equeue.cpp @@ -283,6 +283,19 @@ s32 PS4_SYSV_ABI sceKernelAddHRTimerEvent(SceKernelEqueue eq, int id, timespec* return ORBIS_OK; } +int PS4_SYSV_ABI sceKernelDeleteHRTimerEvent(SceKernelEqueue eq, int id) { + if (eq == nullptr) { + return ORBIS_KERNEL_ERROR_EBADF; + } + + if (eq->HasSmallTimer()) { + return eq->RemoveSmallTimer(id) ? ORBIS_OK : ORBIS_KERNEL_ERROR_ENOENT; + } else { + return eq->RemoveEvent(id, SceKernelEvent::Filter::HrTimer) ? ORBIS_OK + : ORBIS_KERNEL_ERROR_ENOENT; + } +} + int PS4_SYSV_ABI sceKernelAddUserEvent(SceKernelEqueue eq, int id) { if (eq == nullptr) { return ORBIS_KERNEL_ERROR_EBADF; @@ -362,6 +375,7 @@ void RegisterEventQueue(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("4R6-OvI2cEA", "libkernel", 1, "libkernel", 1, 1, sceKernelAddUserEvent); LIB_FUNCTION("WDszmSbWuDk", "libkernel", 1, "libkernel", 1, 1, sceKernelAddUserEventEdge); LIB_FUNCTION("R74tt43xP6k", "libkernel", 1, "libkernel", 1, 1, sceKernelAddHRTimerEvent); + LIB_FUNCTION("J+LF6LwObXU", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteHRTimerEvent); LIB_FUNCTION("F6e0kwo4cnk", "libkernel", 1, "libkernel", 1, 1, sceKernelTriggerUserEvent); LIB_FUNCTION("LJDwdSNTnDg", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteUserEvent); LIB_FUNCTION("mJ7aghmgvfc", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventId); diff --git a/src/core/libraries/kernel/equeue.h b/src/core/libraries/kernel/equeue.h index f8759137c..2db5e6ca7 100644 --- a/src/core/libraries/kernel/equeue.h +++ b/src/core/libraries/kernel/equeue.h @@ -111,6 +111,13 @@ public: bool HasSmallTimer() const { return small_timer_event.event.data != 0; } + bool RemoveSmallTimer(u64 id) { + if (HasSmallTimer() && small_timer_event.event.ident == id) { + small_timer_event = {}; + return true; + } + return false; + } int WaitForSmallTimer(SceKernelEvent* ev, int num, u32 micros); From 65cd3be4cae48c9479a83092b2d7b4a28ffaf0e4 Mon Sep 17 00:00:00 2001 From: oltolm Date: Tue, 31 Dec 2024 19:08:47 +0100 Subject: [PATCH 319/549] Qt: fix deprecation warnings (#1672) --- src/qt_gui/check_update.cpp | 7 +++++-- src/qt_gui/settings_dialog.cpp | 22 +++++++++++++++++----- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/qt_gui/check_update.cpp b/src/qt_gui/check_update.cpp index edd55b804..e3e019144 100644 --- a/src/qt_gui/check_update.cpp +++ b/src/qt_gui/check_update.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -24,11 +25,9 @@ #include #include #include -#include #include "check_update.h" using namespace Common::FS; -namespace fs = std::filesystem; CheckUpdate::CheckUpdate(const bool showMessage, QWidget* parent) : QDialog(parent), networkManager(new QNetworkAccessManager(this)) { @@ -254,7 +253,11 @@ void CheckUpdate::setupUI(const QString& downloadUrl, const QString& latestDate, connect(noButton, &QPushButton::clicked, this, [this]() { close(); }); autoUpdateCheckBox->setChecked(Config::autoUpdate()); +#if (QT_VERSION < QT_VERSION_CHECK(6, 7, 0)) connect(autoUpdateCheckBox, &QCheckBox::stateChanged, this, [](int state) { +#else + connect(autoUpdateCheckBox, &QCheckBox::checkStateChanged, this, [](Qt::CheckState state) { +#endif const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); Config::setAutoUpdate(state == Qt::Checked); Config::save(user_dir / "config.toml"); diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index 0c1375c7f..5cd0a4d65 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -3,22 +3,24 @@ #include #include +#include #include +#include -#include #include "common/config.h" +#include "common/version.h" #include "qt_gui/compatibility_info.h" #ifdef ENABLE_DISCORD_RPC #include "common/discord_rpc_handler.h" +#include "common/singleton.h" #endif #ifdef ENABLE_UPDATER #include "check_update.h" #endif #include +#include "background_music_player.h" #include "common/logging/backend.h" #include "common/logging/filter.h" -#include "common/logging/formatter.h" -#include "main_window.h" #include "settings_dialog.h" #include "ui_settings_dialog.h" QStringList languageNames = {"Arabic", @@ -130,8 +132,13 @@ SettingsDialog::SettingsDialog(std::span physical_devices, // GENERAL TAB { #ifdef ENABLE_UPDATER +#if (QT_VERSION < QT_VERSION_CHECK(6, 7, 0)) connect(ui->updateCheckBox, &QCheckBox::stateChanged, this, [](int state) { Config::setAutoUpdate(state == Qt::Checked); }); +#else + connect(ui->updateCheckBox, &QCheckBox::checkStateChanged, this, + [](Qt::CheckState state) { Config::setAutoUpdate(state == Qt::Checked); }); +#endif connect(ui->updateComboBox, &QComboBox::currentTextChanged, this, [](const QString& channel) { Config::setUpdateChannel(channel.toStdString()); }); @@ -150,7 +157,12 @@ SettingsDialog::SettingsDialog(std::span physical_devices, emit CompatibilityChanged(); }); +#if (QT_VERSION < QT_VERSION_CHECK(6, 7, 0)) connect(ui->enableCompatibilityCheckBox, &QCheckBox::stateChanged, this, [this](int state) { +#else + connect(ui->enableCompatibilityCheckBox, &QCheckBox::checkStateChanged, this, + [this](Qt::CheckState state) { +#endif Config::setCompatibilityEnabled(state); emit CompatibilityChanged(); }); @@ -358,7 +370,7 @@ void SettingsDialog::InitializeEmulatorLanguages() { idx++; } - connect(ui->emulatorLanguageComboBox, qOverload(&QComboBox::currentIndexChanged), this, + connect(ui->emulatorLanguageComboBox, &QComboBox::currentIndexChanged, this, &SettingsDialog::OnLanguageChanged); } @@ -578,4 +590,4 @@ void SettingsDialog::ResetInstallFolders() { } Config::setGameInstallDirs(settings_install_dirs_config); } -} \ No newline at end of file +} From d69341fd31a4209cf2b29b62cd3101301212dac4 Mon Sep 17 00:00:00 2001 From: psucien Date: Wed, 1 Jan 2025 03:40:28 +0100 Subject: [PATCH 320/549] hot-fix: detiler: forgotten lut optimizations --- src/video_core/amdgpu/liverpool.cpp | 2 +- src/video_core/host_shaders/detile_m32x1.comp | 2 +- src/video_core/host_shaders/detile_m32x2.comp | 2 +- src/video_core/host_shaders/detile_m32x4.comp | 2 +- src/video_core/host_shaders/detile_m8x1.comp | 2 +- src/video_core/host_shaders/detile_m8x2.comp | 2 +- .../host_shaders/detile_macro32x1.comp | 70 ++++++++++--------- .../host_shaders/detile_macro32x2.comp | 70 ++++++++++--------- 8 files changed, 78 insertions(+), 74 deletions(-) diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index 2926bcc69..bdf4cc92a 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -815,7 +815,7 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, u32 vqid) { } if (rasterizer && (cs_program.dispatch_initiator & 1)) { const auto cmd_address = reinterpret_cast(header); - rasterizer->ScopeMarkerBegin(fmt::format("acb[{}]:{}:Dispatch", vqid, cmd_address)); + rasterizer->ScopeMarkerBegin(fmt::format("acb[{}]:{}:DispatchIndirect", vqid, cmd_address)); rasterizer->DispatchDirect(); rasterizer->ScopeMarkerEnd(); } diff --git a/src/video_core/host_shaders/detile_m32x1.comp b/src/video_core/host_shaders/detile_m32x1.comp index 802f5f531..cdc8d0018 100644 --- a/src/video_core/host_shaders/detile_m32x1.comp +++ b/src/video_core/host_shaders/detile_m32x1.comp @@ -20,7 +20,7 @@ layout(push_constant) uniform image_info { } info; // Inverse morton LUT, small enough to fit into K$ -uint rmort[16] = { +const uint rmort[16] = { 0x11011000, 0x31213020, 0x13031202, 0x33233222, 0x51415040, 0x71617060, diff --git a/src/video_core/host_shaders/detile_m32x2.comp b/src/video_core/host_shaders/detile_m32x2.comp index 90063a185..c128ba5a1 100644 --- a/src/video_core/host_shaders/detile_m32x2.comp +++ b/src/video_core/host_shaders/detile_m32x2.comp @@ -20,7 +20,7 @@ layout(push_constant) uniform image_info { } info; // Inverse morton LUT, small enough to fit into K$ -uint rmort[16] = { +const uint rmort[16] = { 0x11011000, 0x31213020, 0x13031202, 0x33233222, 0x51415040, 0x71617060, diff --git a/src/video_core/host_shaders/detile_m32x4.comp b/src/video_core/host_shaders/detile_m32x4.comp index e1b988172..a09a0b4c4 100644 --- a/src/video_core/host_shaders/detile_m32x4.comp +++ b/src/video_core/host_shaders/detile_m32x4.comp @@ -20,7 +20,7 @@ layout(push_constant) uniform image_info { } info; // Inverse morton LUT, small enough to fit into K$ -uint rmort[16] = { +const uint rmort[16] = { 0x11011000, 0x31213020, 0x13031202, 0x33233222, 0x51415040, 0x71617060, diff --git a/src/video_core/host_shaders/detile_m8x1.comp b/src/video_core/host_shaders/detile_m8x1.comp index 39d0aaeb1..ecf706450 100644 --- a/src/video_core/host_shaders/detile_m8x1.comp +++ b/src/video_core/host_shaders/detile_m8x1.comp @@ -48,4 +48,4 @@ void main() { uint dw_ofs_x = target_tile_x * 2 + col; // 2 = uints uint dw_ofs_y = (target_tile_y * MICRO_TILE_DIM + row) * tiles_per_pitch * 2; // 2 = uints out_data[dw_ofs_x + dw_ofs_y] = dst_tx; -} \ No newline at end of file +} diff --git a/src/video_core/host_shaders/detile_m8x2.comp b/src/video_core/host_shaders/detile_m8x2.comp index 3f8e5ab33..909a14acc 100644 --- a/src/video_core/host_shaders/detile_m8x2.comp +++ b/src/video_core/host_shaders/detile_m8x2.comp @@ -25,7 +25,7 @@ layout(push_constant) uniform image_info { #define TEXELS_PER_ELEMENT 2 // Inverse morton LUT, small enough to fit into K$ -uint rmort[16] = { +const uint rmort[16] = { 0x11011000, 0x31213020, 0x13031202, 0x33233222, 0x51415040, 0x71617060, diff --git a/src/video_core/host_shaders/detile_macro32x1.comp b/src/video_core/host_shaders/detile_macro32x1.comp index 086fbcfb5..ecac47d1c 100644 --- a/src/video_core/host_shaders/detile_macro32x1.comp +++ b/src/video_core/host_shaders/detile_macro32x1.comp @@ -21,46 +21,46 @@ layout(push_constant) uniform image_info { } info; // Each LUT is 64 bytes, so should fit into K$ given tiled slices locality -const uint lut_32bpp[][64] = { +const uint lut_32bpp[][16] = { { - 0x00, 0x01, 0x04, 0x05, 0x40, 0x41, 0x44, 0x45, - 0x02, 0x03, 0x06, 0x07, 0x42, 0x43, 0x46, 0x47, - 0x10, 0x11, 0x14, 0x15, 0x50, 0x51, 0x54, 0x55, - 0x12, 0x13, 0x16, 0x17, 0x52, 0x53, 0x56, 0x57, - 0x80, 0x81, 0x84, 0x85, 0xc0, 0xc1, 0xc4, 0xc5, - 0x82, 0x83, 0x86, 0x87, 0xc2, 0xc3, 0xc6, 0xc7, - 0x90, 0x91, 0x94, 0x95, 0xd0, 0xd1, 0xd4, 0xd5, - 0x92, 0x93, 0x96, 0x97, 0xd2, 0xd3, 0xd6, 0xd7, + 0x05040100, 0x45444140, + 0x07060302, 0x47464342, + 0x15141110, 0x55545150, + 0x17161312, 0x57565352, + 0x85848180, 0xc5c4c1c0, + 0x87868382, 0xc7c6c3c2, + 0x95949190, 0xd5d4d1d0, + 0x97969392, 0xd7d6d3d2, }, { - 0x08, 0x09, 0x0c, 0x0d, 0x48, 0x49, 0x4c, 0x4d, - 0x0a, 0x0b, 0x0e, 0x0f, 0x4a, 0x4b, 0x4e, 0x4f, - 0x18, 0x19, 0x1c, 0x1d, 0x58, 0x59, 0x5c, 0x5d, - 0x1a, 0x1b, 0x1e, 0x1f, 0x5a, 0x5b, 0x5e, 0x5f, - 0x88, 0x89, 0x8c, 0x8d, 0xc8, 0xc9, 0xcc, 0xcd, - 0x8a, 0x8b, 0x8e, 0x8f, 0xca, 0xcb, 0xce, 0xcf, - 0x98, 0x99, 0x9c, 0x9d, 0xd8, 0xd9, 0xdc, 0xdd, - 0x9a, 0x9b, 0x9e, 0x9f, 0xda, 0xdb, 0xde, 0xdf, + 0x0d0c0908, 0x4d4c4948, + 0x0f0e0b0a, 0x4f4e4b4a, + 0x1d1c1918, 0x5d5c5958, + 0x1f1e1b1a, 0x5f5e5b5a, + 0x8d8c8988, 0xcdccc9c8, + 0x8f8e8b8a, 0xcfcecbca, + 0x9d9c9998, 0xdddcd9d8, + 0x9f9e9b9a, 0xdfdedbda, }, { - 0x20, 0x21, 0x24, 0x25, 0x60, 0x61, 0x64, 0x65, - 0x22, 0x23, 0x26, 0x27, 0x62, 0x63, 0x66, 0x67, - 0x30, 0x31, 0x34, 0x35, 0x70, 0x71, 0x74, 0x75, - 0x32, 0x33, 0x36, 0x37, 0x72, 0x73, 0x76, 0x77, - 0xa0, 0xa1, 0xa4, 0xa5, 0xe0, 0xe1, 0xe4, 0xe5, - 0xa2, 0xa3, 0xa6, 0xa7, 0xe2, 0xe3, 0xe6, 0xe7, - 0xb0, 0xb1, 0xb4, 0xb5, 0xf0, 0xf1, 0xf4, 0xf5, - 0xb2, 0xb3, 0xb6, 0xb7, 0xf2, 0xf3, 0xf6, 0xf7, + 0x25242120, 0x65646160, + 0x27262322, 0x67666362, + 0x35343130, 0x75747170, + 0x37363332, 0x77767372, + 0xa5a4a1a0, 0xe5e4e1e0, + 0xa7a6a3a2, 0xe7e6e3e2, + 0xb5b4b1b0, 0xf5f4f1f0, + 0xb7b6b3b2, 0xf7f6f3f2, }, { - 0x28, 0x29, 0x2c, 0x2d, 0x68, 0x69, 0x6c, 0x6d, - 0x2a, 0x2b, 0x2e, 0x2f, 0x6a, 0x6b, 0x6e, 0x6f, - 0x38, 0x39, 0x3c, 0x3d, 0x78, 0x79, 0x7c, 0x7d, - 0x3a, 0x3b, 0x3e, 0x3f, 0x7a, 0x7b, 0x7e, 0x7f, - 0xa8, 0xa9, 0xac, 0xad, 0xe8, 0xe9, 0xec, 0xed, - 0xaa, 0xab, 0xae, 0xaf, 0xea, 0xeb, 0xee, 0xef, - 0xb8, 0xb9, 0xbc, 0xbd, 0xf8, 0xf9, 0xfc, 0xfd, - 0xba, 0xbb, 0xbe, 0xbf, 0xfa, 0xfb, 0xfe, 0xff, + 0x2d2c2928, 0x6d6c6968, + 0x2f2e2b2a, 0x6f6e6b6a, + 0x3d3c3938, 0x7d7c7978, + 0x3f3e3b3a, 0x7f7e7b7a, + 0xadaca9a8, 0xedece9e8, + 0xafaeabaa, 0xefeeebea, + 0xbdbcb9b8, 0xfdfcf9f8, + 0xbfbebbba, 0xfffefbfa, } }; @@ -77,7 +77,9 @@ void main() { uint col = bitfieldExtract(x, 0, 3); uint row = bitfieldExtract(y, 0, 3); uint lut = bitfieldExtract(z, 0, 2); - uint idx = lut_32bpp[lut][col + row * MICRO_TILE_DIM]; + uint idx_dw = lut_32bpp[lut][(col + row * MICRO_TILE_DIM) >> 2u]; + uint byte_ofs = gl_LocalInvocationID.x & 3u; + uint idx = bitfieldExtract(idx_dw >> (8 * byte_ofs), 0, 8); uint slice_offs = (z >> 2u) * info.c1 * MICRO_TILE_SZ; uint tile_row = y / MICRO_TILE_DIM; diff --git a/src/video_core/host_shaders/detile_macro32x2.comp b/src/video_core/host_shaders/detile_macro32x2.comp index 296311c7a..d161484c1 100644 --- a/src/video_core/host_shaders/detile_macro32x2.comp +++ b/src/video_core/host_shaders/detile_macro32x2.comp @@ -20,46 +20,46 @@ layout(push_constant) uniform image_info { uint c1; } info; -const uint lut_64bpp[][64] = { +const uint lut_64bpp[][16] = { { - 0x00, 0x01, 0x08, 0x09, 0x40, 0x41, 0x48, 0x49, - 0x02, 0x03, 0x0a, 0x0b, 0x42, 0x43, 0x4a, 0x4b, - 0x10, 0x11, 0x18, 0x19, 0x50, 0x51, 0x58, 0x59, - 0x12, 0x13, 0x1a, 0x1b, 0x52, 0x53, 0x5a, 0x5b, - 0x80, 0x81, 0x88, 0x89, 0xc0, 0xc1, 0xc8, 0xc9, - 0x82, 0x83, 0x8a, 0x8b, 0xc2, 0xc3, 0xca, 0xcb, - 0x90, 0x91, 0x98, 0x99, 0xd0, 0xd1, 0xd8, 0xd9, - 0x92, 0x93, 0x9a, 0x9b, 0xd2, 0xd3, 0xda, 0xdb, + 0x09080100, 0x49484140, + 0x0b0a0302, 0x4a4b4342, + 0x19181110, 0x59585150, + 0x1b1a1312, 0x5a5b5352, + 0x89888180, 0xc9c8c1c0, + 0x8b8a8382, 0xcacbc3c2, + 0x99989190, 0xd9d8d1d0, + 0x9b9a9392, 0xdbdad3d2, }, { - 0x04, 0x05, 0x0c, 0x0d, 0x44, 0x45, 0x4c, 0x4d, - 0x06, 0x07, 0x0e, 0x0f, 0x46, 0x47, 0x4e, 0x4f, - 0x14, 0x15, 0x1c, 0x1d, 0x54, 0x55, 0x5c, 0x5d, - 0x16, 0x17, 0x1e, 0x1f, 0x56, 0x57, 0x5e, 0x5f, - 0x84, 0x85, 0x8c, 0x8d, 0xc4, 0xc5, 0xcc, 0xcd, - 0x86, 0x87, 0x8e, 0x8f, 0xc6, 0xc7, 0xce, 0xcf, - 0x94, 0x95, 0x9c, 0x9d, 0xd4, 0xd5, 0xdc, 0xdd, - 0x96, 0x97, 0x9e, 0x9f, 0xd6, 0xd7, 0xde, 0xdf, + 0x0d0c0504, 0x4d4c4544, + 0x0f0e0706, 0x4f4e4746, + 0x1d1c1514, 0x5d5c5554, + 0x1f1e1716, 0x5f5e5756, + 0x8d8c8584, 0xcdccc5c4, + 0x8f8e8786, 0xcfcec7c6, + 0x9d9c9594, 0xdddcd5d4, + 0x9f9e9796, 0xdfded7d6, }, { - 0x20, 0x21, 0x28, 0x29, 0x60, 0x61, 0x68, 0x69, - 0x22, 0x23, 0x2a, 0x2b, 0x62, 0x63, 0x6a, 0x6b, - 0x30, 0x31, 0x38, 0x39, 0x70, 0x71, 0x78, 0x79, - 0x32, 0x33, 0x3a, 0x3b, 0x72, 0x73, 0x7a, 0x7b, - 0xa0, 0xa1, 0xa8, 0xa9, 0xe0, 0xe1, 0xe8, 0xe9, - 0xa2, 0xa3, 0xaa, 0xab, 0xe2, 0xe3, 0xea, 0xeb, - 0xb0, 0xb1, 0xb8, 0xb9, 0xf0, 0xf1, 0xf8, 0xf9, - 0xb2, 0xb3, 0xba, 0xbb, 0xf2, 0xf3, 0xfa, 0xfb, + 0x29282120, 0x69686160, + 0x2b2a2322, 0x6b6a6362, + 0x39383130, 0x79787170, + 0x3b3a3332, 0x7b7a7372, + 0xa9a8a1a0, 0xe9e8e1e0, + 0xabaaa3a2, 0xebeae3e2, + 0xb9b8b1b0, 0xf9f8f1f0, + 0xbbbab3b2, 0xfbfaf3f2, }, { - 0x24, 0x25, 0x2c, 0x2d, 0x64, 0x65, 0x6c, 0x6d, - 0x26, 0x27, 0x2e, 0x2f, 0x66, 0x67, 0x6e, 0x6f, - 0x34, 0x35, 0x3c, 0x3d, 0x74, 0x75, 0x7c, 0x7d, - 0x36, 0x37, 0x3e, 0x3f, 0x76, 0x77, 0x7e, 0x7f, - 0xa4, 0xa5, 0xac, 0xad, 0xe4, 0xe5, 0xec, 0xed, - 0xa6, 0xa7, 0xae, 0xaf, 0xe6, 0xe7, 0xee, 0xef, - 0xb4, 0xb5, 0xbc, 0xbd, 0xf4, 0xf5, 0xfc, 0xfd, - 0xb6, 0xb7, 0xbe, 0xbf, 0xf6, 0xf7, 0xfe, 0xff, + 0x2d2c2524, 0x6d6c6564, + 0x2f2e2726, 0x6f6e6766, + 0x3d3c3534, 0x7d7c7574, + 0x3f3e3736, 0x7f7e7776, + 0xadaca5a4, 0xedece5e4, + 0xafaea7a6, 0xefeee7e6, + 0xbdbcb5b4, 0xfdfcf5f4, + 0xbfbeb7b6, 0xfffef7f6, }, }; @@ -76,7 +76,9 @@ void main() { uint col = bitfieldExtract(x, 0, 3); uint row = bitfieldExtract(y, 0, 3); uint lut = bitfieldExtract(z, 0, 2); - uint idx = lut_64bpp[lut][col + row * MICRO_TILE_DIM]; + uint idx_dw = lut_64bpp[lut][(col + row * MICRO_TILE_DIM) >> 2u]; + uint byte_ofs = gl_LocalInvocationID.x & 3u; + uint idx = bitfieldExtract(idx_dw >> (8 * byte_ofs), 0, 8); uint slice_offs = (z >> 2u) * info.c1 * MICRO_TILE_SZ; uint tile_row = y / MICRO_TILE_DIM; From 283442b42f8593408d305be3b0cf2f83788b7f1e Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Wed, 1 Jan 2025 12:04:51 +0200 Subject: [PATCH 321/549] Storing encryption trophy key in config.toml (#1930) * get trophy key from toml file * clang format fix * get trophy key from toml file * clang format fix * merge fixes * Update config.cpp --- src/common/config.cpp | 16 ++++++++++++++++ src/common/config.h | 3 +++ src/core/crypto/crypto.cpp | 14 ++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/src/common/config.cpp b/src/common/config.cpp index deef0fa88..4fce7d97f 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -67,6 +67,7 @@ static int cursorHideTimeout = 5; // 5 seconds (default) static bool separateupdatefolder = false; static bool compatibilityData = false; static bool checkCompatibilityOnStartup = false; +static std::string trophyKey = ""; // Gui std::vector settings_install_dirs = {}; @@ -91,6 +92,14 @@ std::string emulator_language = "en"; // Language u32 m_language = 1; // english +std::string getTrophyKey() { + return trophyKey; +} + +void setTrophyKey(std::string key) { + trophyKey = key; +} + bool isNeoMode() { return isNeo; } @@ -652,6 +661,11 @@ void load(const std::filesystem::path& path) { m_language = toml::find_or(settings, "consoleLanguage", 1); } + + if (data.contains("Keys")) { + const toml::value& keys = data.at("Keys"); + trophyKey = toml::find_or(keys, "TrophyKey", ""); + } } void save(const std::filesystem::path& path) { @@ -712,6 +726,8 @@ void save(const std::filesystem::path& path) { data["Debug"]["DebugDump"] = isDebugDump; data["Debug"]["CollectShader"] = isShaderDebug; + data["Keys"]["TrophyKey"] = trophyKey; + std::vector install_dirs; for (const auto& dirString : settings_install_dirs) { install_dirs.emplace_back(std::string{fmt::UTF(dirString.u8string()).data}); diff --git a/src/common/config.h b/src/common/config.h index 701aadb12..9d943008b 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -15,6 +15,9 @@ void load(const std::filesystem::path& path); void save(const std::filesystem::path& path); void saveMainWindow(const std::filesystem::path& path); +std::string getTrophyKey(); +void setTrophyKey(std::string key); + bool isNeoMode(); bool isFullscreenMode(); bool getPlayBGM(); diff --git a/src/core/crypto/crypto.cpp b/src/core/crypto/crypto.cpp index 00f1dea46..472d284fc 100644 --- a/src/core/crypto/crypto.cpp +++ b/src/core/crypto/crypto.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include "crypto.h" CryptoPP::RSA::PrivateKey Crypto::key_pkg_derived_key3_keyset_init() { @@ -137,6 +138,13 @@ void Crypto::aesCbcCfb128DecryptEntry(std::span ivkey, } } +static void hexToBytes(const char* hex, unsigned char* dst) { + for (size_t i = 0; hex[i] != 0; i++) { + const unsigned char value = (hex[i] < 0x3A) ? (hex[i] - 0x30) : (hex[i] - 0x37); + dst[i / 2] |= ((i % 2) == 0) ? (value << 4) : (value); + } +} + void Crypto::decryptEFSM(std::span NPcommID, std::span efsmIv, std::span ciphertext, std::span decrypted) { @@ -145,9 +153,15 @@ void Crypto::decryptEFSM(std::span NPcommID, // step 1: Encrypt NPcommID CryptoPP::CBC_Mode::Encryption encrypt; + const char* TrophyKeyget = Config::getTrophyKey().c_str(); + std::vector TrophyKey; + hexToBytes(TrophyKeyget, TrophyKey.data()); + std::vector trpKey(16); encrypt.ProcessData(trpKey.data(), NPcommID.data(), 16); + encrypt.SetKeyWithIV(TrophyKey.data(), TrophyKey.size(), TrophyIV.data()); + // step 2: decrypt efsm. CryptoPP::CBC_Mode::Decryption decrypt; decrypt.SetKeyWithIV(trpKey.data(), trpKey.size(), efsmIv.data()); From a76e8f0211d540c1bd91ee4d457eb2294daeeaa9 Mon Sep 17 00:00:00 2001 From: polybiusproxy <47796739+polybiusproxy@users.noreply.github.com> Date: Wed, 1 Jan 2025 13:21:00 +0100 Subject: [PATCH 322/549] clang-format --- src/video_core/amdgpu/liverpool.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index bdf4cc92a..985f3c652 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -815,7 +815,8 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, u32 vqid) { } if (rasterizer && (cs_program.dispatch_initiator & 1)) { const auto cmd_address = reinterpret_cast(header); - rasterizer->ScopeMarkerBegin(fmt::format("acb[{}]:{}:DispatchIndirect", vqid, cmd_address)); + rasterizer->ScopeMarkerBegin( + fmt::format("acb[{}]:{}:DispatchIndirect", vqid, cmd_address)); rasterizer->DispatchDirect(); rasterizer->ScopeMarkerEnd(); } From 5631a31640c80311e1196b81f4743c92ee4c277c Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Wed, 1 Jan 2025 11:34:40 -0600 Subject: [PATCH 323/549] Fix flags (#1999) --- src/core/libraries/videoout/video_out.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/core/libraries/videoout/video_out.cpp b/src/core/libraries/videoout/video_out.cpp index f36de6ade..78a2b11a4 100644 --- a/src/core/libraries/videoout/video_out.cpp +++ b/src/core/libraries/videoout/video_out.cpp @@ -52,8 +52,7 @@ s32 PS4_SYSV_ABI sceVideoOutAddFlipEvent(Kernel::SceKernelEqueue eq, s32 handle, Kernel::EqueueEvent event{}; event.event.ident = u64(OrbisVideoOutEventId::Flip); event.event.filter = Kernel::SceKernelEvent::Filter::VideoOut; - // The library only sets EV_ADD but kernel driver forces EV_CLEAR - event.event.flags = Kernel::SceKernelEvent::Flags::Clear; + event.event.flags = Kernel::SceKernelEvent::Flags::Add; event.event.udata = udata; event.event.fflags = 0; event.event.data = 0; @@ -79,8 +78,7 @@ s32 PS4_SYSV_ABI sceVideoOutAddVblankEvent(Kernel::SceKernelEqueue eq, s32 handl Kernel::EqueueEvent event{}; event.event.ident = u64(OrbisVideoOutEventId::Vblank); event.event.filter = Kernel::SceKernelEvent::Filter::VideoOut; - // The library only sets EV_ADD but kernel driver forces EV_CLEAR - event.event.flags = Kernel::SceKernelEvent::Flags::Clear; + event.event.flags = Kernel::SceKernelEvent::Flags::Add; event.event.udata = udata; event.event.fflags = 0; event.event.data = 0; From 15c9bb0e83dd809790b366dc125855c050ca8197 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Wed, 1 Jan 2025 19:05:22 +0100 Subject: [PATCH 324/549] Motion controls (#1984) * Initial motion controls * Store sensor polling rate, and add more logging * Revert commented out logging for testing purposes * Code cleanup & clang * New orientation handling * clang --- src/core/libraries/pad/pad.cpp | 46 +++++++------- src/input/controller.cpp | 110 +++++++++++++++++++++++++++++++++ src/input/controller.h | 12 ++++ src/sdl_window.cpp | 14 +++++ 4 files changed, 160 insertions(+), 22 deletions(-) diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index 98f086dd9..27564294e 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -104,8 +104,8 @@ int PS4_SYSV_ABI scePadGetControllerInformation(s32 handle, OrbisPadControllerIn pInfo->touchPadInfo.pixelDensity = 1; pInfo->touchPadInfo.resolution.x = 1920; pInfo->touchPadInfo.resolution.y = 950; - pInfo->stickInfo.deadZoneLeft = 2; - pInfo->stickInfo.deadZoneRight = 2; + pInfo->stickInfo.deadZoneLeft = 20; + pInfo->stickInfo.deadZoneRight = 20; pInfo->connectionType = ORBIS_PAD_PORT_TYPE_STANDARD; pInfo->connectedCount = 1; pInfo->connected = true; @@ -286,6 +286,7 @@ int PS4_SYSV_ABI scePadOutputReport() { } int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) { + LOG_TRACE(Lib_Pad, "called"); int connected_count = 0; bool connected = false; Input::State states[64]; @@ -304,16 +305,15 @@ int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) { pData[i].rightStick.y = states[i].axes[static_cast(Input::Axis::RightY)]; pData[i].analogButtons.l2 = states[i].axes[static_cast(Input::Axis::TriggerLeft)]; pData[i].analogButtons.r2 = states[i].axes[static_cast(Input::Axis::TriggerRight)]; - pData[i].orientation.x = 0.0f; - pData[i].orientation.y = 0.0f; - pData[i].orientation.z = 0.0f; - pData[i].orientation.w = 1.0f; - pData[i].acceleration.x = 0.0f; - pData[i].acceleration.y = 0.0f; - pData[i].acceleration.z = 0.0f; - pData[i].angularVelocity.x = 0.0f; - pData[i].angularVelocity.y = 0.0f; - pData[i].angularVelocity.z = 0.0f; + pData[i].acceleration.x = states[i].acceleration.x; + pData[i].acceleration.y = states[i].acceleration.y; + pData[i].acceleration.z = states[i].acceleration.z; + pData[i].angularVelocity.x = states[i].angularVelocity.x; + pData[i].angularVelocity.y = states[i].angularVelocity.y; + pData[i].angularVelocity.z = states[i].angularVelocity.z; + Input::GameController::CalculateOrientation(pData[i].acceleration, pData[i].angularVelocity, + 1.0f / controller->accel_poll_rate, + pData[i].orientation); pData[i].touchData.touchNum = (states[i].touchpad[0].state ? 1 : 0) + (states[i].touchpad[1].state ? 1 : 0); pData[i].touchData.touch[0].x = states[i].touchpad[0].x; @@ -352,6 +352,7 @@ int PS4_SYSV_ABI scePadReadHistory() { } int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) { + LOG_TRACE(Lib_Pad, "called"); if (handle == ORBIS_PAD_ERROR_DEVICE_NO_HANDLE) { return ORBIS_PAD_ERROR_INVALID_HANDLE; } @@ -367,16 +368,15 @@ int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) { pData->rightStick.y = state.axes[static_cast(Input::Axis::RightY)]; pData->analogButtons.l2 = state.axes[static_cast(Input::Axis::TriggerLeft)]; pData->analogButtons.r2 = state.axes[static_cast(Input::Axis::TriggerRight)]; - pData->orientation.x = 0; - pData->orientation.y = 0; - pData->orientation.z = 0; - pData->orientation.w = 1; - pData->acceleration.x = 0.0f; - pData->acceleration.y = 0.0f; - pData->acceleration.z = 0.0f; - pData->angularVelocity.x = 0.0f; - pData->angularVelocity.y = 0.0f; - pData->angularVelocity.z = 0.0f; + pData->acceleration.x = state.acceleration.x; + pData->acceleration.y = state.acceleration.y; + pData->acceleration.z = state.acceleration.z; + pData->angularVelocity.x = state.angularVelocity.x; + pData->angularVelocity.y = state.angularVelocity.y; + pData->angularVelocity.z = state.angularVelocity.z; + Input::GameController::CalculateOrientation(pData->acceleration, pData->angularVelocity, + 1.0f / controller->accel_poll_rate, + pData->orientation); pData->touchData.touchNum = (state.touchpad[0].state ? 1 : 0) + (state.touchpad[1].state ? 1 : 0); pData->touchData.touch[0].x = state.touchpad[0].x; @@ -498,6 +498,8 @@ int PS4_SYSV_ABI scePadSetLoginUserNumber() { int PS4_SYSV_ABI scePadSetMotionSensorState(s32 handle, bool bEnable) { LOG_ERROR(Lib_Pad, "(STUBBED) called"); return ORBIS_OK; + // it's already handled by the SDL backend and will be on no matter what + // (assuming the controller supports it) } int PS4_SYSV_ABI scePadSetProcessFocus() { diff --git a/src/input/controller.cpp b/src/input/controller.cpp index 3927b096f..daef9c940 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include "common/logging/log.h" #include "core/libraries/kernel/time.h" #include "core/libraries/pad/pad.h" #include "input/controller.h" @@ -116,6 +117,103 @@ void GameController::Axis(int id, Input::Axis axis, int value) { AddState(state); } +void GameController::Gyro(int id, const float gyro[3]) { + std::scoped_lock lock{m_mutex}; + auto state = GetLastState(); + state.time = Libraries::Kernel::sceKernelGetProcessTime(); + + // Update the angular velocity (gyro data) + state.angularVelocity.x = gyro[0]; // X-axis + state.angularVelocity.y = gyro[1]; // Y-axis + state.angularVelocity.z = gyro[2]; // Z-axis + + AddState(state); +} +void GameController::Acceleration(int id, const float acceleration[3]) { + std::scoped_lock lock{m_mutex}; + auto state = GetLastState(); + state.time = Libraries::Kernel::sceKernelGetProcessTime(); + + // Update the acceleration values + state.acceleration.x = acceleration[0]; // X-axis + state.acceleration.y = acceleration[1]; // Y-axis + state.acceleration.z = acceleration[2]; // Z-axis + + AddState(state); +} + +// Stolen from +// https://github.com/xioTechnologies/Open-Source-AHRS-With-x-IMU/blob/master/x-IMU%20IMU%20and%20AHRS%20Algorithms/x-IMU%20IMU%20and%20AHRS%20Algorithms/AHRS/MahonyAHRS.cs +float eInt[3] = {0.0f, 0.0f, 0.0f}; // Integral error terms +const float Kp = 50.0f; // Proportional gain +const float Ki = 1.0f; // Integral gain +Libraries::Pad::OrbisFQuaternion o = {1, 0, 0, 0}; +void GameController::CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration, + Libraries::Pad::OrbisFVector3& angularVelocity, + float deltaTime, + Libraries::Pad::OrbisFQuaternion& orientation) { + float ax = acceleration.x, ay = acceleration.y, az = acceleration.z; + float gx = angularVelocity.x, gy = angularVelocity.y, gz = angularVelocity.z; + + float q1 = o.w, q2 = o.x, q3 = o.y, q4 = o.z; + + // Normalize accelerometer measurement + float norm = std::sqrt(ax * ax + ay * ay + az * az); + if (norm == 0.0f) + return; // Handle NaN + norm = 1.0f / norm; + ax *= norm; + ay *= norm; + az *= norm; + + // Estimated direction of gravity + float vx = 2.0f * (q2 * q4 - q1 * q3); + float vy = 2.0f * (q1 * q2 + q3 * q4); + float vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4; + + // Error is cross product between estimated direction and measured direction of gravity + float ex = (ay * vz - az * vy); + float ey = (az * vx - ax * vz); + float ez = (ax * vy - ay * vx); + if (Ki > 0.0f) { + eInt[0] += ex * deltaTime; // Accumulate integral error + eInt[1] += ey * deltaTime; + eInt[2] += ez * deltaTime; + } else { + eInt[0] = eInt[1] = eInt[2] = 0.0f; // Prevent integral wind-up + } + + // Apply feedback terms + gx += Kp * ex + Ki * eInt[0]; + gy += Kp * ey + Ki * eInt[1]; + gz += Kp * ez + Ki * eInt[2]; + + //// Integrate rate of change of quaternion + // float pa = q2, pb = q3, pc = q4; + // q1 += (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * deltaTime); + // q2 += (pa * gx + pb * gz - pc * gy) * (0.5f * deltaTime); + // q3 += (pb * gy - pa * gz + pc * gx) * (0.5f * deltaTime); + // q4 += (pc * gz + pa * gy - pb * gx) * (0.5f * deltaTime); + q1 += (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * deltaTime); + q2 += (q1 * gx + q3 * gz - q4 * gy) * (0.5f * deltaTime); + q3 += (q1 * gy - q2 * gz + q4 * gx) * (0.5f * deltaTime); + q4 += (q1 * gz + q2 * gy - q3 * gx) * (0.5f * deltaTime); + + // Normalize quaternion + norm = std::sqrt(q1 * q1 + q2 * q2 + q3 * q3 + q4 * q4); + norm = 1.0f / norm; + orientation.w = q1 * norm; + orientation.x = q2 * norm; + orientation.y = q3 * norm; + orientation.z = q4 * norm; + o.w = q1 * norm; + o.x = q2 * norm; + o.y = q3 * norm; + o.z = q4 * norm; + LOG_DEBUG(Lib_Pad, "Calculated orientation: {:.2f} {:.2f} {:.2f} {:.2f}", orientation.x, + orientation.y, orientation.z, orientation.w); +} + void GameController::SetLightBarRGB(u8 r, u8 g, u8 b) { if (m_sdl_gamepad != nullptr) { SDL_SetGamepadLED(m_sdl_gamepad, r, g, b); @@ -149,6 +247,18 @@ void GameController::TryOpenSDLController() { int gamepad_count; SDL_JoystickID* gamepads = SDL_GetGamepads(&gamepad_count); m_sdl_gamepad = gamepad_count > 0 ? SDL_OpenGamepad(gamepads[0]) : nullptr; + if (SDL_SetGamepadSensorEnabled(m_sdl_gamepad, SDL_SENSOR_GYRO, true)) { + gyro_poll_rate = SDL_GetGamepadSensorDataRate(m_sdl_gamepad, SDL_SENSOR_GYRO); + LOG_INFO(Input, "Gyro initialized, poll rate: {}", gyro_poll_rate); + } else { + LOG_ERROR(Input, "Failed to initialize gyro controls for gamepad"); + } + if (SDL_SetGamepadSensorEnabled(m_sdl_gamepad, SDL_SENSOR_ACCEL, true)) { + accel_poll_rate = SDL_GetGamepadSensorDataRate(m_sdl_gamepad, SDL_SENSOR_ACCEL); + LOG_INFO(Input, "Accel initialized, poll rate: {}", accel_poll_rate); + } else { + LOG_ERROR(Input, "Failed to initialize accel controls for gamepad"); + } SDL_free(gamepads); SetLightBarRGB(0, 0, 255); diff --git a/src/input/controller.h b/src/input/controller.h index d425fb46c..c6fc02c24 100644 --- a/src/input/controller.h +++ b/src/input/controller.h @@ -33,6 +33,9 @@ struct State { u64 time = 0; int axes[static_cast(Axis::AxisMax)] = {128, 128, 128, 128, 0, 0}; TouchpadEntry touchpad[2] = {{false, 0, 0}, {false, 0, 0}}; + Libraries::Pad::OrbisFVector3 acceleration = {0.0f, 0.0f, 0.0f}; + Libraries::Pad::OrbisFVector3 angularVelocity = {0.0f, 0.0f, 0.0f}; + Libraries::Pad::OrbisFQuaternion orientation = {0.0f, 0.0f, 0.0f, 1.0f}; }; inline int GetAxis(int min, int max, int value) { @@ -53,12 +56,21 @@ public: void CheckButton(int id, Libraries::Pad::OrbisPadButtonDataOffset button, bool isPressed); void AddState(const State& state); void Axis(int id, Input::Axis axis, int value); + void Gyro(int id, const float gyro[3]); + void Acceleration(int id, const float acceleration[3]); void SetLightBarRGB(u8 r, u8 g, u8 b); bool SetVibration(u8 smallMotor, u8 largeMotor); void SetTouchpadState(int touchIndex, bool touchDown, float x, float y); void TryOpenSDLController(); u32 Poll(); + float gyro_poll_rate; + float accel_poll_rate; + static void CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration, + Libraries::Pad::OrbisFVector3& angularVelocity, + float deltaTime, + Libraries::Pad::OrbisFQuaternion& orientation); + private: struct StateInternal { bool obtained = false; diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index 50c3e93ee..d694b0939 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -161,6 +161,20 @@ void WindowSDL::WaitEvent() { case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION: OnGamepadEvent(&event); break; + // i really would have appreciated ANY KIND OF DOCUMENTATION ON THIS + // AND IT DOESN'T EVEN USE PROPER ENUMS + case SDL_EVENT_GAMEPAD_SENSOR_UPDATE: + switch ((SDL_SensorType)event.gsensor.sensor) { + case SDL_SENSOR_GYRO: + controller->Gyro(0, event.gsensor.data); + break; + case SDL_SENSOR_ACCEL: + controller->Acceleration(0, event.gsensor.data); + break; + default: + break; + } + break; case SDL_EVENT_QUIT: is_open = false; break; From 444016df7e9127a530ff969b674ee8be4aea238d Mon Sep 17 00:00:00 2001 From: psucien <168137814+psucien@users.noreply.github.com> Date: Wed, 1 Jan 2025 19:11:30 +0100 Subject: [PATCH 325/549] Fix for trophy decryption (#2005) * fix for trophy decryption * more sanity checks * switch back to strings for simplicity * get rid of unnecessary span --- src/common/config.cpp | 2 +- src/core/crypto/crypto.cpp | 20 +++++--------------- src/core/crypto/crypto.h | 3 ++- src/core/file_format/trp.cpp | 20 +++++++++++++++++++- 4 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index 4fce7d97f..246644e2d 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -67,7 +67,7 @@ static int cursorHideTimeout = 5; // 5 seconds (default) static bool separateupdatefolder = false; static bool compatibilityData = false; static bool checkCompatibilityOnStartup = false; -static std::string trophyKey = ""; +static std::string trophyKey; // Gui std::vector settings_install_dirs = {}; diff --git a/src/core/crypto/crypto.cpp b/src/core/crypto/crypto.cpp index 472d284fc..4020edfd8 100644 --- a/src/core/crypto/crypto.cpp +++ b/src/core/crypto/crypto.cpp @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include -#include + #include "crypto.h" CryptoPP::RSA::PrivateKey Crypto::key_pkg_derived_key3_keyset_init() { @@ -138,29 +138,19 @@ void Crypto::aesCbcCfb128DecryptEntry(std::span ivkey, } } -static void hexToBytes(const char* hex, unsigned char* dst) { - for (size_t i = 0; hex[i] != 0; i++) { - const unsigned char value = (hex[i] < 0x3A) ? (hex[i] - 0x30) : (hex[i] - 0x37); - dst[i / 2] |= ((i % 2) == 0) ? (value << 4) : (value); - } -} - -void Crypto::decryptEFSM(std::span NPcommID, +void Crypto::decryptEFSM(std::span trophyKey, + std::span NPcommID, std::span efsmIv, std::span ciphertext, std::span decrypted) { - std::vector TrophyIV = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // step 1: Encrypt NPcommID CryptoPP::CBC_Mode::Encryption encrypt; - const char* TrophyKeyget = Config::getTrophyKey().c_str(); - std::vector TrophyKey; - hexToBytes(TrophyKeyget, TrophyKey.data()); - + std::vector trophyIv(16, 0); std::vector trpKey(16); + encrypt.SetKeyWithIV(trophyKey.data(), trophyKey.size(), trophyIv.data()); encrypt.ProcessData(trpKey.data(), NPcommID.data(), 16); - encrypt.SetKeyWithIV(TrophyKey.data(), TrophyKey.size(), TrophyIV.data()); // step 2: decrypt efsm. CryptoPP::CBC_Mode::Decryption decrypt; diff --git a/src/core/crypto/crypto.h b/src/core/crypto/crypto.h index 83249bd7d..b5d8104b5 100644 --- a/src/core/crypto/crypto.h +++ b/src/core/crypto/crypto.h @@ -32,7 +32,8 @@ public: void aesCbcCfb128DecryptEntry(std::span ivkey, std::span ciphertext, std::span decrypted); - void decryptEFSM(std::span, std::span efsmIv, + void decryptEFSM(std::span trophyKey, + std::span NPcommID, std::span efsmIv, std::span ciphertext, std::span decrypted); void PfsGenCryptoKey(std::span ekpfs, std::span seed, diff --git a/src/core/file_format/trp.cpp b/src/core/file_format/trp.cpp index 2ca88c778..d25c93c3f 100644 --- a/src/core/file_format/trp.cpp +++ b/src/core/file_format/trp.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "common/config.h" #include "common/logging/log.h" #include "common/path_util.h" #include "trp.h" @@ -33,12 +34,29 @@ static void removePadding(std::vector& vec) { } } +static void hexToBytes(const char* hex, unsigned char* dst) { + for (size_t i = 0; hex[i] != 0; i++) { + const unsigned char value = (hex[i] < 0x3A) ? (hex[i] - 0x30) : (hex[i] - 0x37); + dst[i / 2] |= ((i % 2) == 0) ? (value << 4) : (value); + } +} + bool TRP::Extract(const std::filesystem::path& trophyPath, const std::string titleId) { std::filesystem::path gameSysDir = trophyPath / "sce_sys/trophy/"; if (!std::filesystem::exists(gameSysDir)) { LOG_CRITICAL(Common_Filesystem, "Game sce_sys directory doesn't exist"); return false; } + + const auto user_key_str = Config::getTrophyKey(); + if (user_key_str.size() != 32) { + LOG_CRITICAL(Common_Filesystem, "Trophy decryption key is not specified"); + return false; + } + + std::array user_key{}; + hexToBytes(user_key_str.c_str(), user_key.data()); + for (int index = 0; const auto& it : std::filesystem::directory_iterator(gameSysDir)) { if (it.is_regular_file()) { GetNPcommID(trophyPath, index); @@ -97,7 +115,7 @@ bool TRP::Extract(const std::filesystem::path& trophyPath, const std::string tit return false; } file.Read(ESFM); - crypto.decryptEFSM(np_comm_id, esfmIv, ESFM, XML); // decrypt + crypto.decryptEFSM(user_key, np_comm_id, esfmIv, ESFM, XML); // decrypt removePadding(XML); std::string xml_name = entry.entry_name; size_t pos = xml_name.find("ESFM"); From 40211642caf32922a13b57703b8a6488a107f716 Mon Sep 17 00:00:00 2001 From: psucien Date: Wed, 1 Jan 2025 21:04:59 +0100 Subject: [PATCH 326/549] kernel: memory: PRT mapped area setter/getter --- src/core/libraries/kernel/memory.cpp | 39 ++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/core/libraries/kernel/memory.cpp b/src/core/libraries/kernel/memory.cpp index 7d326cbbf..b18d5f570 100644 --- a/src/core/libraries/kernel/memory.cpp +++ b/src/core/libraries/kernel/memory.cpp @@ -505,6 +505,41 @@ int PS4_SYSV_ABI posix_munmap(void* addr, size_t len) { return result; } +static constexpr int MAX_PTR_APERTURES = 3; +static constexpr VAddr PRT_AREA_START_ADDR = 0x1000000000; +static constexpr size_t PRT_AREA_SIZE = 0xec00000000; +static std::array, MAX_PTR_APERTURES> PrtApertures{}; + +int PS4_SYSV_ABI sceKernelSetPrtAperture(int id, VAddr address, size_t size) { + if (id < 0 || id >= MAX_PTR_APERTURES) { + return ORBIS_KERNEL_ERROR_EINVAL; + } + + if (address < PRT_AREA_START_ADDR || address + size > PRT_AREA_START_ADDR + PRT_AREA_SIZE) { + return ORBIS_KERNEL_ERROR_EINVAL; + } + + if (address % 4096 != 0) { + return ORBIS_KERNEL_ERROR_EINVAL; + } + + LOG_WARNING(Kernel_Vmm, + "PRT aperture id = {}, address = {:#x}, size = {:#x} is set but not used", id, + address, size); + + PrtApertures[id] = {address, size}; + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceKernelGetPrtAperture(int id, VAddr* addres, size_t* size) { + if (id < 0 || id >= MAX_PTR_APERTURES) { + return ORBIS_KERNEL_ERROR_EINVAL; + } + + std::tie(*addres, *size) = PrtApertures[id]; + return ORBIS_OK; +} + void RegisterMemory(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("rTXw65xmLIA", "libkernel", 1, "libkernel", 1, 1, sceKernelAllocateDirectMemory); LIB_FUNCTION("B+vc2AO2Zrc", "libkernel", 1, "libkernel", 1, 1, @@ -551,6 +586,10 @@ void RegisterMemory(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("BPE9s9vQQXo", "libScePosix", 1, "libkernel", 1, 1, posix_mmap); LIB_FUNCTION("UqDGjXA5yUM", "libkernel", 1, "libkernel", 1, 1, posix_munmap); LIB_FUNCTION("UqDGjXA5yUM", "libScePosix", 1, "libkernel", 1, 1, posix_munmap); + + // PRT memory management + LIB_FUNCTION("BohYr-F7-is", "libkernel", 1, "libkernel", 1, 1, sceKernelSetPrtAperture); + LIB_FUNCTION("L0v2Go5jOuM", "libkernel", 1, "libkernel", 1, 1, sceKernelGetPrtAperture); } } // namespace Libraries::Kernel From 46720e756b82ccb9e2ae86a7100dd2e91587c108 Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Wed, 1 Jan 2025 23:08:04 +0300 Subject: [PATCH 327/549] infra: more clarifications in issue templates (#2010) --- .github/ISSUE_TEMPLATE/game-bug-report.yaml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/game-bug-report.yaml b/.github/ISSUE_TEMPLATE/game-bug-report.yaml index 407ee2fe3..2d984b697 100644 --- a/.github/ISSUE_TEMPLATE/game-bug-report.yaml +++ b/.github/ISSUE_TEMPLATE/game-bug-report.yaml @@ -13,7 +13,11 @@ body: **Please do not make support requests on GitHub. Our issue tracker is for tracking bugs and feature requests only. If you have a support request or are unsure about the nature of your issue please contact us on [discord](https://discord.gg/bFJxfftGW6).** - You can also check the [Game Compatibility Repository](https://github.com/shadps4-emu/shadps4-game-compatibility) for the information about the status of the game. + This repository does not provide support for modded games. You should perform and test a clean game installation before submitting an issue. + + This repository does not provide support for game patches. If you are having issues with patches please refer to [Cheats and Patches Repository](https://github.com/shadps4-emu/ps4_cheats). + + Before submitting an issue please check [Game Compatibility Repository](https://github.com/shadps4-emu/shadps4-game-compatibility) for the information about the status of the game. Please make an effort to make sure your issue isn't already reported. @@ -21,15 +25,15 @@ body: - type: checkboxes id: checklist attributes: - label: Checklist + label: Checklist (we expect you to perform these steps before opening the issue) options: - label: I have searched for a similar issue in this repository and did not find one. required: true - label: I am using an official build obtained from [releases](https://github.com/shadps4-emu/shadPS4/releases) or updated one of those builds using its in-app updater. required: true - - label: I have re-dumped the game and performed a clean install without mods. + - label: I have re-dumped the game and performed a clean install without mods and the issue is still present. required: true - - label: I have disabled all patches and cheats. + - label: I have disabled all patches and cheats and the issue is still present. required: true - label: I have all the required [system modules](https://github.com/shadps4-emu/shadps4-game-compatibility?tab=readme-ov-file#informations) installed. required: true From 4e0757ed550d5dda41c8389e1d9f6eb27a2b7035 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Wed, 1 Jan 2025 23:08:56 +0200 Subject: [PATCH 328/549] Removed LLE libs (#2012) * Removed LLE fiber, JpegEnc modules . HLE replacements are good enough * fixup --- src/core/libraries/libs.cpp | 4 ++++ src/emulator.cpp | 4 +--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index c30c2d7c3..49cd54a5b 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -47,6 +47,8 @@ #include "core/libraries/videodec/videodec.h" #include "core/libraries/videodec/videodec2.h" #include "core/libraries/videoout/video_out.h" +#include "fiber/fiber.h" +#include "jpeg/jpegenc.h" namespace Libraries { @@ -93,6 +95,8 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::Videodec::RegisterlibSceVideodec(sym); Libraries::RazorCpu::RegisterlibSceRazorCpu(sym); Libraries::Move::RegisterlibSceMove(sym); + Libraries::Fiber::RegisterlibSceFiber(sym); + Libraries::JpegEnc::RegisterlibSceJpegEnc(sym); } } // namespace Libraries diff --git a/src/emulator.cpp b/src/emulator.cpp index dbe21a141..4f0c61236 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -282,16 +282,14 @@ void Emulator::Run(const std::filesystem::path& file) { } void Emulator::LoadSystemModules(const std::filesystem::path& file, std::string game_serial) { - constexpr std::array ModulesToLoad{ + constexpr std::array ModulesToLoad{ {{"libSceNgs2.sprx", &Libraries::Ngs2::RegisterlibSceNgs2}, - {"libSceFiber.sprx", &Libraries::Fiber::RegisterlibSceFiber}, {"libSceUlt.sprx", nullptr}, {"libSceJson.sprx", nullptr}, {"libSceJson2.sprx", nullptr}, {"libSceLibcInternal.sprx", &Libraries::LibcInternal::RegisterlibSceLibcInternal}, {"libSceDiscMap.sprx", &Libraries::DiscMap::RegisterlibSceDiscMap}, {"libSceRtc.sprx", &Libraries::Rtc::RegisterlibSceRtc}, - {"libSceJpegEnc.sprx", &Libraries::JpegEnc::RegisterlibSceJpegEnc}, {"libSceCesCs.sprx", nullptr}, {"libSceFont.sprx", nullptr}, {"libSceFontFt.sprx", nullptr}, From 8b66e9f78d9c416b6bd487dc4aeda86c2ec2f37f Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Thu, 2 Jan 2025 04:15:08 -0300 Subject: [PATCH 329/549] GUI: Settings - Trophy Key (#2013) * GUI: Settings-Trophy Key * - * QLineEdit::Password * clang * size --- src/qt_gui/settings_dialog.cpp | 10 + src/qt_gui/settings_dialog.ui | 617 ++++++++++++++++--------------- src/qt_gui/translations/ar.ts | 15 + src/qt_gui/translations/da_DK.ts | 15 + src/qt_gui/translations/de.ts | 15 + src/qt_gui/translations/el.ts | 15 + src/qt_gui/translations/en.ts | 15 + src/qt_gui/translations/es_ES.ts | 15 + src/qt_gui/translations/fa_IR.ts | 15 + src/qt_gui/translations/fi.ts | 15 + src/qt_gui/translations/fr.ts | 15 + src/qt_gui/translations/hu_HU.ts | 15 + src/qt_gui/translations/id.ts | 15 + src/qt_gui/translations/it.ts | 15 + src/qt_gui/translations/ja_JP.ts | 15 + src/qt_gui/translations/ko_KR.ts | 15 + src/qt_gui/translations/lt_LT.ts | 15 + src/qt_gui/translations/nb.ts | 15 + src/qt_gui/translations/nl.ts | 15 + src/qt_gui/translations/pl_PL.ts | 15 + src/qt_gui/translations/pt_BR.ts | 15 + src/qt_gui/translations/ro_RO.ts | 15 + src/qt_gui/translations/ru_RU.ts | 15 + src/qt_gui/translations/sq.ts | 15 + src/qt_gui/translations/tr_TR.ts | 15 + src/qt_gui/translations/uk_UA.ts | 15 + src/qt_gui/translations/vi_VN.ts | 15 + src/qt_gui/translations/zh_CN.ts | 15 + src/qt_gui/translations/zh_TW.ts | 15 + 29 files changed, 736 insertions(+), 296 deletions(-) diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index 5cd0a4d65..6d76a5318 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -213,6 +213,8 @@ SettingsDialog::SettingsDialog(std::span physical_devices, ui->showSplashCheckBox->installEventFilter(this); ui->discordRPCCheckbox->installEventFilter(this); ui->userName->installEventFilter(this); + ui->label_Trophy->installEventFilter(this); + ui->trophyKeyLineEdit->installEventFilter(this); ui->logTypeGroupBox->installEventFilter(this); ui->logFilter->installEventFilter(this); #ifdef ENABLE_UPDATER @@ -307,6 +309,9 @@ void SettingsDialog::LoadValuesFromConfig() { QString::fromStdString(toml::find_or(data, "General", "logFilter", ""))); ui->userNameLineEdit->setText( QString::fromStdString(toml::find_or(data, "General", "userName", "shadPS4"))); + ui->trophyKeyLineEdit->setText( + QString::fromStdString(toml::find_or(data, "Keys", "TrophyKey", ""))); + ui->trophyKeyLineEdit->setEchoMode(QLineEdit::Password); ui->debugDump->setChecked(toml::find_or(data, "Debug", "DebugDump", false)); ui->vkValidationCheckBox->setChecked(toml::find_or(data, "Vulkan", "validation", false)); ui->vkSyncValidationCheckBox->setChecked( @@ -419,6 +424,10 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) { text = tr("discordRPCCheckbox"); } else if (elementName == "userName") { text = tr("userName"); + } else if (elementName == "label_Trophy") { + text = tr("TrophyKey"); + } else if (elementName == "trophyKeyLineEdit") { + text = tr("TrophyKey"); } else if (elementName == "logTypeGroupBox") { text = tr("logTypeGroupBox"); } else if (elementName == "logFilter") { @@ -529,6 +538,7 @@ void SettingsDialog::UpdateSettings() { Config::setLogType(ui->logTypeComboBox->currentText().toStdString()); Config::setLogFilter(ui->logFilterLineEdit->text().toStdString()); Config::setUserName(ui->userNameLineEdit->text().toStdString()); + Config::setTrophyKey(ui->trophyKeyLineEdit->text().toStdString()); Config::setCursorState(ui->hideCursorComboBox->currentIndex()); Config::setCursorHideTimeout(ui->idleTimeoutSpinBox->value()); Config::setGpuId(ui->graphicsAdapterBox->currentIndex() - 1); diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index af1edb0dd..2e7e3db37 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -11,8 +11,8 @@ 0 0 - 950 - 780 + 970 + 670 @@ -67,8 +67,8 @@ 0 0 - 822 - 487 + 946 + 536 @@ -77,87 +77,7 @@ 0 - - - - - - Logger - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Log Type - - - - - - - async - - - - - sync - - - - - - - - - - - - - - 6 - - - 0 - - - - - - - Log Filter - - - - - - - - - - - - - - - - - - + @@ -194,7 +114,7 @@ - + @@ -268,10 +188,10 @@ - + - -1 + 6 QLayout::SizeConstraint::SetDefaultConstraint @@ -436,138 +356,8 @@ - - - - - - 0 - 0 - - - - - 0 - 0 - - - - GUI Settings - - - - 1 - - - 11 - - - - - Disable Trophy Pop-ups - - - - - - - - 0 - 0 - - - - Play title music - - - - - - - 1 - - - 0 - - - - - Qt::Orientation::Vertical - - - QSizePolicy::Policy::Fixed - - - - 20 - 13 - - - - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - Volume - - - - - - - Set the volume of the background music. - - - 100 - - - 10 - - - 20 - - - 50 - - - Qt::Orientation::Horizontal - - - false - - - false - - - QSlider::TickPosition::NoTicks - - - 10 - - - - - - - - - - - - + @@ -638,6 +428,160 @@ + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + GUI Settings + + + + 1 + + + 11 + + + + + + 0 + 0 + + + + Play title music + + + + + + + 1 + + + 0 + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Volume + + + + + + + Set the volume of the background music. + + + 100 + + + 10 + + + 20 + + + 50 + + + Qt::Orientation::Horizontal + + + false + + + false + + + QSlider::TickPosition::NoTicks + + + 10 + + + + + + + 6 + + + 0 + + + + + + + Trophy + + + + + + Disable Trophy Pop-ups + + + + + + + Trophy Key + + + + + + + + 0 + 0 + + + + + + + + + + + + + + + + + + @@ -655,8 +599,8 @@ 0 0 - 396 - 222 + 926 + 536 @@ -946,8 +890,8 @@ 0 0 - 536 - 192 + 926 + 536 @@ -1197,8 +1141,8 @@ 0 0 - 146 - 215 + 926 + 536 @@ -1211,18 +1155,25 @@ - - - Remove + + + 0 - - - - - - Add... - - + + + + Add... + + + + + + + Remove + + + + @@ -1263,71 +1214,145 @@ 0 0 - 288 - 163 + 926 + 536 - + + + 0 + + + 0 + - - - true - - - General - - - Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop - - - - - - Enable Debug Dumping - - - - - - - Qt::Orientation::Vertical - - - QSizePolicy::Policy::MinimumExpanding - - - - 0 - 0 - - - - - - - - Enable Vulkan Validation Layers - - - - - - - Enable Vulkan Synchronization Validation - - - - - - - Enable RenderDoc Debugging - - - - - + + + + + true + + + General + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop + + + + + + Enable Debug Dumping + + + + + + + Enable Vulkan Validation Layers + + + + + + + Enable Vulkan Synchronization Validation + + + + + + + Enable RenderDoc Debugging + + + + + + + + + + + + + + Logger + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Log Type + + + + + + + async + + + + + sync + + + + + + + + + + + + + + 6 + + + 0 + + + + + + + Log Filter + + + + + + + + + + + + + + + + diff --git a/src/qt_gui/translations/ar.ts b/src/qt_gui/translations/ar.ts index 1f65db04a..9296ef9a4 100644 --- a/src/qt_gui/translations/ar.ts +++ b/src/qt_gui/translations/ar.ts @@ -537,6 +537,16 @@ Username اسم المستخدم + + + Trophy Key + Trophy Key + + + + Trophy + Trophy + Logger @@ -1236,6 +1246,11 @@ userName اسم المستخدم:\nيضبط اسم حساب PS4، الذي قد يتم عرضه في بعض الألعاب. + + + TrophyKey + Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + logTypeGroupBox diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts index 943e2d092..ef14d1496 100644 --- a/src/qt_gui/translations/da_DK.ts +++ b/src/qt_gui/translations/da_DK.ts @@ -537,6 +537,16 @@ Username Username + + + Trophy Key + Trophy Key + + + + Trophy + Trophy + Logger @@ -1236,6 +1246,11 @@ userName Brugernavn:\nIndstiller PS4-kontoens navn, som kan blive vist i nogle spil. + + + TrophyKey + Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + logTypeGroupBox diff --git a/src/qt_gui/translations/de.ts b/src/qt_gui/translations/de.ts index cbbef8215..d4587fc87 100644 --- a/src/qt_gui/translations/de.ts +++ b/src/qt_gui/translations/de.ts @@ -537,6 +537,16 @@ Username Benutzername + + + Trophy Key + Trophy Key + + + + Trophy + Trophy + Logger @@ -1236,6 +1246,11 @@ userName Benutzername:\nLegt den Namen des PS4-Kontos fest, der in einigen Spielen angezeigt werden kann. + + + TrophyKey + Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + logTypeGroupBox diff --git a/src/qt_gui/translations/el.ts b/src/qt_gui/translations/el.ts index 8737f5216..671676d62 100644 --- a/src/qt_gui/translations/el.ts +++ b/src/qt_gui/translations/el.ts @@ -537,6 +537,16 @@ Username Username + + + Trophy Key + Trophy Key + + + + Trophy + Trophy + Logger @@ -1236,6 +1246,11 @@ userName Όνομα Χρήστη:\nΟρίζει το όνομα του λογαριασμού PS4, το οποίο μπορεί να εμφανιστεί σε ορισμένα παιχνίδια. + + + TrophyKey + Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + logTypeGroupBox diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index 692aa527e..326cf27b3 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -537,6 +537,16 @@ Username Username + + + Trophy Key + Trophy Key + + + + Trophy + Trophy + Logger @@ -1236,6 +1246,11 @@ userName Username:\nSets the PS4's account username, which may be displayed by some games. + + + TrophyKey + Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + logTypeGroupBox diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index 70be2253d..775f78958 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -537,6 +537,16 @@ Username Nombre de usuario + + + Trophy Key + Trophy Key + + + + Trophy + Trophy + Logger @@ -1236,6 +1246,11 @@ userName Nombre de Usuario:\nEstablece el nombre de usuario de la cuenta de PS4, que puede ser mostrado por algunos juegos. + + + TrophyKey + Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + logTypeGroupBox diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index 54187cf9b..eb60613d2 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -537,6 +537,16 @@ Username نام کاربری + + + Trophy Key + Trophy Key + + + + Trophy + Trophy + Logger @@ -1236,6 +1246,11 @@ userName نام کاربری:\nنام کاربری حساب PS4 را تنظیم می‌کند که ممکن است توسط برخی بازی‌ها نمایش داده شود. + + + TrophyKey + Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + logTypeGroupBox diff --git a/src/qt_gui/translations/fi.ts b/src/qt_gui/translations/fi.ts index bdc1eb703..e7af0f986 100644 --- a/src/qt_gui/translations/fi.ts +++ b/src/qt_gui/translations/fi.ts @@ -537,6 +537,16 @@ Username Username + + + Trophy Key + Trophy Key + + + + Trophy + Trophy + Logger @@ -1236,6 +1246,11 @@ userName Käyttäjänimi:\nAsettaa PS4-tilin käyttäjänimen, joka voi näkyä joissakin peleissä. + + + TrophyKey + Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + logTypeGroupBox diff --git a/src/qt_gui/translations/fr.ts b/src/qt_gui/translations/fr.ts index 19b0f9358..aa2abed78 100644 --- a/src/qt_gui/translations/fr.ts +++ b/src/qt_gui/translations/fr.ts @@ -537,6 +537,16 @@ Username Nom d'utilisateur + + + Trophy Key + Trophy Key + + + + Trophy + Trophy + Logger @@ -1236,6 +1246,11 @@ userName Nom d'utilisateur:\nDéfinit le nom d'utilisateur du compte PS4, qui peut être affiché par certains jeux. + + + TrophyKey + Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + logTypeGroupBox diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index bc337f2cd..51f149422 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -537,6 +537,16 @@ Username Felhasználónév + + + Trophy Key + Trophy Key + + + + Trophy + Trophy + Logger @@ -1236,6 +1246,11 @@ userName Felhasználónév:\nBeállítja a PS4 fiók felhasználónevét, amelyet egyes játékok megjeleníthetnek. + + + TrophyKey + Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + logTypeGroupBox diff --git a/src/qt_gui/translations/id.ts b/src/qt_gui/translations/id.ts index 7a0bf5d05..836bcf2a2 100644 --- a/src/qt_gui/translations/id.ts +++ b/src/qt_gui/translations/id.ts @@ -537,6 +537,16 @@ Username Username + + + Trophy Key + Trophy Key + + + + Trophy + Trophy + Logger @@ -1236,6 +1246,11 @@ userName Nama Pengguna:\nMenetapkan nama pengguna akun PS4, yang mungkin ditampilkan oleh beberapa permainan. + + + TrophyKey + Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + logTypeGroupBox diff --git a/src/qt_gui/translations/it.ts b/src/qt_gui/translations/it.ts index 1391fbc55..6e6022c17 100644 --- a/src/qt_gui/translations/it.ts +++ b/src/qt_gui/translations/it.ts @@ -537,6 +537,16 @@ Username Nome Utente + + + Trophy Key + Trophy Key + + + + Trophy + Trophy + Logger @@ -1236,6 +1246,11 @@ userName Nome Utente:\nImposta il nome utente dell'account PS4, che potrebbe essere visualizzato da alcuni giochi. + + + TrophyKey + Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + logTypeGroupBox diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index 58f213e03..573a05e45 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -537,6 +537,16 @@ Username ユーザー名 + + + Trophy Key + Trophy Key + + + + Trophy + Trophy + Logger @@ -1236,6 +1246,11 @@ userName ユーザー名:\nPS4のアカウントユーザー名を設定します。これは、一部のゲームで表示される場合があります。 + + + TrophyKey + Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + logTypeGroupBox diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts index 75a1b53cf..8b4ac76f3 100644 --- a/src/qt_gui/translations/ko_KR.ts +++ b/src/qt_gui/translations/ko_KR.ts @@ -537,6 +537,16 @@ Username Username + + + Trophy Key + Trophy Key + + + + Trophy + Trophy + Logger @@ -1236,6 +1246,11 @@ userName Username:\nSets the PS4's account username, which may be displayed by some games. + + + TrophyKey + Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + logTypeGroupBox diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts index 092521fdf..bbb563908 100644 --- a/src/qt_gui/translations/lt_LT.ts +++ b/src/qt_gui/translations/lt_LT.ts @@ -537,6 +537,16 @@ Username Username + + + Trophy Key + Trophy Key + + + + Trophy + Trophy + Logger @@ -1236,6 +1246,11 @@ userName Vartotojo vardas:\nNustato PS4 paskyros vartotojo vardą, kuris gali būti rodomas kai kuriuose žaidimuose. + + + TrophyKey + Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + logTypeGroupBox diff --git a/src/qt_gui/translations/nb.ts b/src/qt_gui/translations/nb.ts index cc41573db..6c8e4c38a 100644 --- a/src/qt_gui/translations/nb.ts +++ b/src/qt_gui/translations/nb.ts @@ -537,6 +537,16 @@ Username Brukernavn + + + Trophy Key + Trophy Key + + + + Trophy + Trophy + Logger @@ -1236,6 +1246,11 @@ userName Brukernavn:\nAngir brukernavnet for PS4-kontoen, som kan vises av enkelte spill. + + + TrophyKey + Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + logTypeGroupBox diff --git a/src/qt_gui/translations/nl.ts b/src/qt_gui/translations/nl.ts index 5cd4a4224..d9da2253e 100644 --- a/src/qt_gui/translations/nl.ts +++ b/src/qt_gui/translations/nl.ts @@ -537,6 +537,16 @@ Username Username + + + Trophy Key + Trophy Key + + + + Trophy + Trophy + Logger @@ -1236,6 +1246,11 @@ userName Gebruikersnaam:\nStelt de gebruikersnaam van het PS4-account in, die door sommige games kan worden weergegeven. + + + TrophyKey + Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + logTypeGroupBox diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index b85393bb0..8de1c903c 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -537,6 +537,16 @@ Username Nazwa użytkownika + + + Trophy Key + Trophy Key + + + + Trophy + Trophy + Logger @@ -1236,6 +1246,11 @@ userName Nazwa użytkownika:\nUstala nazwę użytkownika konta PS4, która może być wyświetlana w niektórych grach. + + + TrophyKey + Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + logTypeGroupBox diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index 8ab8db093..bf806fe97 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -537,6 +537,16 @@ Username Nome de usuário + + + Trophy Key + Trophy Key + + + + Trophy + Troféus + Logger @@ -1236,6 +1246,11 @@ userName Nome de usuário:\nDefine o nome de usuário da conta PS4 que pode ser exibido por alguns jogos. + + + TrophyKey + Trophy Key:\nChave usada para descriptografar troféus.\nDeve conter apenas os caracteres hexadecimais de 'Trophy Key, type Release (CEX)', sem vírgulas ou 0x + logTypeGroupBox diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index 00547d6ba..5cbced635 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -537,6 +537,16 @@ Username Username + + + Trophy Key + Trophy Key + + + + Trophy + Trophy + Logger @@ -1236,6 +1246,11 @@ userName Nume utilizator:\nSetează numele de utilizator al contului PS4, care poate fi afișat de unele jocuri. + + + TrophyKey + Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + logTypeGroupBox diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index 505a05a3e..f534acc0d 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -537,6 +537,16 @@ Username Имя пользователя + + + Trophy Key + Trophy Key + + + + Trophy + Trophy + Logger @@ -1236,6 +1246,11 @@ userName Имя пользователя:\nУстановите имя пользователя аккаунта PS4. Это может отображаться в некоторых играх. + + + TrophyKey + Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + logTypeGroupBox diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index 0c318f4f7..0031ab305 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -537,6 +537,16 @@ Username Përdoruesi + + + Trophy Key + Trophy Key + + + + Trophy + Trophy + Logger @@ -1236,6 +1246,11 @@ userName Përdoruesi:\nPërcakton emrin e përdoruesit të llogarisë PS4, i cili mund të shfaqet nga disa lojra. + + + TrophyKey + Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + logTypeGroupBox diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index 2845af462..f671dab2a 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -537,6 +537,16 @@ Username Kullanıcı Adı + + + Trophy Key + Trophy Key + + + + Trophy + Trophy + Logger @@ -1236,6 +1246,11 @@ userName Kullanıcı Adı:\nBazı oyunlar tarafından gösterilebilen PS4 hesabının kullanıcı adını ayarlar. + + + TrophyKey + Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + logTypeGroupBox diff --git a/src/qt_gui/translations/uk_UA.ts b/src/qt_gui/translations/uk_UA.ts index 8abfca435..f6834e818 100644 --- a/src/qt_gui/translations/uk_UA.ts +++ b/src/qt_gui/translations/uk_UA.ts @@ -537,6 +537,16 @@ Username Ім'я користувача + + + Trophy Key + Trophy Key + + + + Trophy + Trophy + Logger @@ -1236,6 +1246,11 @@ userName Ім'я користувача:\nВстановіть ім'я користувача акаунта PS4. Це може відображатися в деяких іграх. + + + TrophyKey + Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + logTypeGroupBox diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index 7d0e9a2cd..315fcac7e 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -537,6 +537,16 @@ Username Username + + + Trophy Key + Trophy Key + + + + Trophy + Trophy + Logger @@ -1236,6 +1246,11 @@ userName Tên người dùng:\nChọn tên người dùng của tài khoản PS4, có thể được một số trò chơi hiển thị. + + + TrophyKey + Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + logTypeGroupBox diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index 32b838fac..5ccd680b5 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -537,6 +537,16 @@ Username 用户名 + + + Trophy Key + Trophy Key + + + + Trophy + Trophy + Logger @@ -1236,6 +1246,11 @@ userName 用户名:\n设置 PS4 帐户的用户名,某些游戏中可能会显示此名称。 + + + TrophyKey + Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + logTypeGroupBox diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index 3d27267b6..fc1bad1a3 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -537,6 +537,16 @@ Username Username + + + Trophy Key + Trophy Key + + + + Trophy + Trophy + Logger @@ -1236,6 +1246,11 @@ userName 用戶名:\n設定PS4帳號的用戶名,某些遊戲中可能會顯示。 + + + TrophyKey + Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + logTypeGroupBox From 3109bd245ff74b512ba96d70221eb9b501ebb780 Mon Sep 17 00:00:00 2001 From: Vinicius Rangel Date: Thu, 2 Jan 2025 04:42:53 -0300 Subject: [PATCH 330/549] savedata: Avoid Save memory concurrency --- src/core/libraries/save_data/savedata.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/core/libraries/save_data/savedata.cpp b/src/core/libraries/save_data/savedata.cpp index 66899fb34..b573ded1e 100644 --- a/src/core/libraries/save_data/savedata.cpp +++ b/src/core/libraries/save_data/savedata.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include #include @@ -1139,10 +1140,6 @@ Error PS4_SYSV_ABI sceSaveDataGetSaveDataMemory2(OrbisSaveDataMemoryGet2* getPar LOG_INFO(Lib_SaveData, "called without save memory initialized"); return Error::MEMORY_NOT_READY; } - if (SaveMemory::IsSaving()) { - LOG_TRACE(Lib_SaveData, "called while saving"); - return Error::BUSY_FOR_SAVING; - } LOG_DEBUG(Lib_SaveData, "called"); auto data = getParam->data; if (data != nullptr) { @@ -1502,8 +1499,14 @@ Error PS4_SYSV_ABI sceSaveDataSetSaveDataMemory2(const OrbisSaveDataMemorySet2* return Error::MEMORY_NOT_READY; } if (SaveMemory::IsSaving()) { - LOG_TRACE(Lib_SaveData, "called while saving"); - return Error::BUSY_FOR_SAVING; + int count = 0; + while (++count < 100 && SaveMemory::IsSaving()) { // try for more 10 seconds + std::this_thread::sleep_for(chrono::milliseconds(100)); + } + if (SaveMemory::IsSaving()) { + LOG_TRACE(Lib_SaveData, "called while saving"); + return Error::BUSY_FOR_SAVING; + } } LOG_DEBUG(Lib_SaveData, "called"); auto data = setParam->data; @@ -1584,8 +1587,8 @@ Error PS4_SYSV_ABI sceSaveDataSetupSaveDataMemory2(const OrbisSaveDataMemorySetu } else { SaveMemory::SetIcon(nullptr, 0); } + SaveMemory::TriggerSaveWithoutEvent(); } - SaveMemory::TriggerSaveWithoutEvent(); if (g_fw_ver >= ElfInfo::FW_45 && result != nullptr) { result->existedMemorySize = existed_size; } From 33afc00c3a39266aa0151bb6bb5da2acf52a7349 Mon Sep 17 00:00:00 2001 From: polyproxy <47796739+polybiusproxy@users.noreply.github.com> Date: Thu, 2 Jan 2025 10:07:48 +0100 Subject: [PATCH 331/549] Update improper wording in translations --- src/qt_gui/translations/ar.ts | 2 +- src/qt_gui/translations/da_DK.ts | 2 +- src/qt_gui/translations/de.ts | 2 +- src/qt_gui/translations/el.ts | 2 +- src/qt_gui/translations/en.ts | 2 +- src/qt_gui/translations/es_ES.ts | 2 +- src/qt_gui/translations/fa_IR.ts | 2 +- src/qt_gui/translations/fi.ts | 2 +- src/qt_gui/translations/fr.ts | 2 +- src/qt_gui/translations/hu_HU.ts | 2 +- src/qt_gui/translations/id.ts | 2 +- src/qt_gui/translations/it.ts | 2 +- src/qt_gui/translations/ja_JP.ts | 2 +- src/qt_gui/translations/ko_KR.ts | 2 +- src/qt_gui/translations/lt_LT.ts | 2 +- src/qt_gui/translations/nb.ts | 2 +- src/qt_gui/translations/nl.ts | 2 +- src/qt_gui/translations/pl_PL.ts | 2 +- src/qt_gui/translations/pt_BR.ts | 2 +- src/qt_gui/translations/ro_RO.ts | 2 +- src/qt_gui/translations/ru_RU.ts | 2 +- src/qt_gui/translations/sq.ts | 2 +- src/qt_gui/translations/tr_TR.ts | 2 +- src/qt_gui/translations/uk_UA.ts | 2 +- src/qt_gui/translations/vi_VN.ts | 2 +- src/qt_gui/translations/zh_CN.ts | 2 +- src/qt_gui/translations/zh_TW.ts | 2 +- 27 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/qt_gui/translations/ar.ts b/src/qt_gui/translations/ar.ts index 9296ef9a4..e851f59a7 100644 --- a/src/qt_gui/translations/ar.ts +++ b/src/qt_gui/translations/ar.ts @@ -1249,7 +1249,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts index ef14d1496..41319c7ff 100644 --- a/src/qt_gui/translations/da_DK.ts +++ b/src/qt_gui/translations/da_DK.ts @@ -1249,7 +1249,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. diff --git a/src/qt_gui/translations/de.ts b/src/qt_gui/translations/de.ts index d4587fc87..62897fe24 100644 --- a/src/qt_gui/translations/de.ts +++ b/src/qt_gui/translations/de.ts @@ -1249,7 +1249,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. diff --git a/src/qt_gui/translations/el.ts b/src/qt_gui/translations/el.ts index 671676d62..43ed81c33 100644 --- a/src/qt_gui/translations/el.ts +++ b/src/qt_gui/translations/el.ts @@ -1249,7 +1249,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index 326cf27b3..293b5fae7 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -1249,7 +1249,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index 775f78958..096e104e3 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -1249,7 +1249,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index eb60613d2..7b93c6769 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -1249,7 +1249,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. diff --git a/src/qt_gui/translations/fi.ts b/src/qt_gui/translations/fi.ts index e7af0f986..cdf331796 100644 --- a/src/qt_gui/translations/fi.ts +++ b/src/qt_gui/translations/fi.ts @@ -1249,7 +1249,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. diff --git a/src/qt_gui/translations/fr.ts b/src/qt_gui/translations/fr.ts index aa2abed78..441eaddb1 100644 --- a/src/qt_gui/translations/fr.ts +++ b/src/qt_gui/translations/fr.ts @@ -1249,7 +1249,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index 51f149422..f6b853e4b 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -1249,7 +1249,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. diff --git a/src/qt_gui/translations/id.ts b/src/qt_gui/translations/id.ts index 836bcf2a2..bee61083c 100644 --- a/src/qt_gui/translations/id.ts +++ b/src/qt_gui/translations/id.ts @@ -1249,7 +1249,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. diff --git a/src/qt_gui/translations/it.ts b/src/qt_gui/translations/it.ts index 6e6022c17..9e375a45e 100644 --- a/src/qt_gui/translations/it.ts +++ b/src/qt_gui/translations/it.ts @@ -1249,7 +1249,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index 573a05e45..409900ade 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -1249,7 +1249,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts index 8b4ac76f3..ab6404a7e 100644 --- a/src/qt_gui/translations/ko_KR.ts +++ b/src/qt_gui/translations/ko_KR.ts @@ -1249,7 +1249,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts index bbb563908..0b9c5b542 100644 --- a/src/qt_gui/translations/lt_LT.ts +++ b/src/qt_gui/translations/lt_LT.ts @@ -1249,7 +1249,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. diff --git a/src/qt_gui/translations/nb.ts b/src/qt_gui/translations/nb.ts index 6c8e4c38a..4d3c4f5af 100644 --- a/src/qt_gui/translations/nb.ts +++ b/src/qt_gui/translations/nb.ts @@ -1249,7 +1249,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. diff --git a/src/qt_gui/translations/nl.ts b/src/qt_gui/translations/nl.ts index d9da2253e..0cb890186 100644 --- a/src/qt_gui/translations/nl.ts +++ b/src/qt_gui/translations/nl.ts @@ -1249,7 +1249,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index 8de1c903c..1aed08394 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -1249,7 +1249,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index bf806fe97..cce66c105 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -1249,7 +1249,7 @@ TrophyKey - Trophy Key:\nChave usada para descriptografar troféus.\nDeve conter apenas os caracteres hexadecimais de 'Trophy Key, type Release (CEX)', sem vírgulas ou 0x + Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index 5cbced635..63df2ff80 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -1249,7 +1249,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index f534acc0d..88eff1aeb 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -1249,7 +1249,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index 0031ab305..1df2a40e2 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -1249,7 +1249,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index f671dab2a..a03a48660 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -1249,7 +1249,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. diff --git a/src/qt_gui/translations/uk_UA.ts b/src/qt_gui/translations/uk_UA.ts index f6834e818..7e0a58ffb 100644 --- a/src/qt_gui/translations/uk_UA.ts +++ b/src/qt_gui/translations/uk_UA.ts @@ -1249,7 +1249,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index 315fcac7e..997c3d3f9 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -1249,7 +1249,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index 5ccd680b5..fecb8857f 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -1249,7 +1249,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index fc1bad1a3..293ed81a6 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -1249,7 +1249,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies.\nMust contain only the hex characters of 'Trophy Key, type Release (CEX)', without commas or 0x + Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. From 19269009451665b5126399b8fefa67dbf2706e75 Mon Sep 17 00:00:00 2001 From: polyproxy <47796739+polybiusproxy@users.noreply.github.com> Date: Thu, 2 Jan 2025 12:30:05 +0100 Subject: [PATCH 332/549] hotfix: reset stop source on thread stop --- src/core/libraries/kernel/threads.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/libraries/kernel/threads.h b/src/core/libraries/kernel/threads.h index ad1393599..409136968 100644 --- a/src/core/libraries/kernel/threads.h +++ b/src/core/libraries/kernel/threads.h @@ -55,6 +55,9 @@ public: stop.request_stop(); Join(); } + thread = nullptr; + func = nullptr; + stop = std::stop_source{}; } static void* PS4_SYSV_ABI RunWrapper(void* arg) { From 099e685bfff6bed3ede25af96a83fb02b996e917 Mon Sep 17 00:00:00 2001 From: Mahmoud Adel <94652220+AboMedoz@users.noreply.github.com> Date: Thu, 2 Jan 2025 14:29:57 +0200 Subject: [PATCH 333/549] add R16Uint to Format Detiler (#1995) helps with Matterfall --- src/video_core/texture_cache/tile_manager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/video_core/texture_cache/tile_manager.cpp b/src/video_core/texture_cache/tile_manager.cpp index de108843b..fda7e511a 100644 --- a/src/video_core/texture_cache/tile_manager.cpp +++ b/src/video_core/texture_cache/tile_manager.cpp @@ -32,6 +32,7 @@ static vk::Format DemoteImageFormatForDetiling(vk::Format format) { case vk::Format::eR5G5B5A1UnormPack16: case vk::Format::eR8G8Unorm: case vk::Format::eR16Sfloat: + case vk::Format::eR16Uint: case vk::Format::eR16Unorm: case vk::Format::eD16Unorm: return vk::Format::eR8G8Uint; From 6862c9aad77c2df28486d1ec86204a044a96f481 Mon Sep 17 00:00:00 2001 From: hspir404 Date: Thu, 2 Jan 2025 13:38:51 +0000 Subject: [PATCH 334/549] Speed up LiverpoolToVK::SurfaceFormat (#1982) * Speed up LiverpoolToVK::SurfaceFormat In Bloodborne this shows up as the function with the very highest cumulative "exclusive time". This is true both in scenes that perform poorly, and scenes that perform well. I took (approximately) 10s samples using an 8khz sampling profiler. In the Nightmare Grand Cathedral (looking towards the stairs, at the rest of the level): - Reduced total time from 757.34ms to 82.61ms (out of ~10000ms). - Reduced average frame times by 2ms (though according to the graph, the gap may be as big as 9ms every N frames). In the Hunter's Dream (in the spawn position): - Reduced the total time from 486.50ms to 53.83ms (out of ~10000ms). - Average frame times appear to be roughly the same. These are profiles of the change vs the version currently in the main branch. These improvements also improve things in the `threading` branch. They might improve them even more in that branch, but I didn't bother keeping track of my measurements as well in that branch. I believe this change will still be useful even when that branch is stabilized and merged. It could be there are other bottlenecks in rendering on this branch that are preventing this code from being the critical path in places like the Hunter's Dream, where performance isn't currently as constrained. That might explain why the reduction in call times isn't resulting in a higher frame rate. * Implement SurfaceFormat with derived lookup table instead of switch * Clang format fixes --- .../renderer_vulkan/liverpool_to_vk.cpp | 42 +++++++++++++++---- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp index c41b760ba..eba2050e0 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp @@ -691,16 +691,40 @@ std::span SurfaceFormats() { return formats; } +// Table 8.13 Data and Image Formats [Sea Islands Series Instruction Set Architecture] +static const size_t amd_gpu_data_format_bit_size = 6; // All values are under 64 +static const size_t amd_gpu_number_format_bit_size = 4; // All values are under 16 + +static size_t GetSurfaceFormatTableIndex(AmdGpu::DataFormat data_format, + AmdGpu::NumberFormat num_format) { + DEBUG_ASSERT(data_format < 1 << amd_gpu_data_format_bit_size); + DEBUG_ASSERT(num_format < 1 << amd_gpu_number_format_bit_size); + size_t result = static_cast(num_format) | + (static_cast(data_format) << amd_gpu_number_format_bit_size); + return result; +} + +static auto surface_format_table = []() constexpr { + std::array + result; + for (auto& entry : result) { + entry = vk::Format::eUndefined; + } + for (const auto& supported_format : SurfaceFormats()) { + result[GetSurfaceFormatTableIndex(supported_format.data_format, + supported_format.number_format)] = + supported_format.vk_format; + } + return result; +}(); + vk::Format SurfaceFormat(AmdGpu::DataFormat data_format, AmdGpu::NumberFormat num_format) { - const auto& formats = SurfaceFormats(); - const auto format = - std::find_if(formats.begin(), formats.end(), [&](const SurfaceFormatInfo& format_info) { - return format_info.data_format == data_format && - format_info.number_format == num_format; - }); - ASSERT_MSG(format != formats.end(), "Unknown data_format={} and num_format={}", - static_cast(data_format), static_cast(num_format)); - return format->vk_format; + vk::Format result = surface_format_table[GetSurfaceFormatTableIndex(data_format, num_format)]; + bool found = + result != vk::Format::eUndefined || data_format == AmdGpu::DataFormat::FormatInvalid; + ASSERT_MSG(found, "Unknown data_format={} and num_format={}", static_cast(data_format), + static_cast(num_format)); + return result; } static constexpr DepthFormatInfo CreateDepthFormatInfo( From c25447097e51684ff4152e19226f572849c5a9cf Mon Sep 17 00:00:00 2001 From: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> Date: Thu, 2 Jan 2025 15:39:02 +0200 Subject: [PATCH 335/549] buffer_cache: Improve buffer cache locking contention (#1973) * Improve buffer cache locking contention * buffer_cache: Revert some changes * clang fmt 1 * clang fmt 2 * clang fmt 3 * buffer_cache: Fix build --- src/video_core/buffer_cache/buffer_cache.cpp | 23 +- src/video_core/buffer_cache/buffer_cache.h | 4 +- .../buffer_cache/memory_tracker_base.h | 61 ++--- src/video_core/buffer_cache/word_manager.h | 241 +++++------------- src/video_core/multi_level_page_table.h | 9 + src/video_core/page_manager.cpp | 2 +- src/video_core/page_manager.h | 4 +- 7 files changed, 104 insertions(+), 240 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index 0088ea4fa..3ac6a3598 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -54,18 +54,10 @@ BufferCache::BufferCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& s BufferCache::~BufferCache() = default; void BufferCache::InvalidateMemory(VAddr device_addr, u64 size) { - std::scoped_lock lk{mutex}; const bool is_tracked = IsRegionRegistered(device_addr, size); - if (!is_tracked) { - return; - } - // Mark the page as CPU modified to stop tracking writes. - SCOPE_EXIT { + if (is_tracked) { + // Mark the page as CPU modified to stop tracking writes. memory_tracker.MarkRegionAsCpuModified(device_addr, size); - }; - if (!memory_tracker.IsRegionGpuModified(device_addr, size)) { - // Page has not been modified by the GPU, nothing to do. - return; } } @@ -346,6 +338,7 @@ bool BufferCache::IsRegionRegistered(VAddr addr, size_t size) { ++page; continue; } + std::shared_lock lk{mutex}; Buffer& buffer = slot_buffers[buffer_id]; const VAddr buf_start_addr = buffer.CpuAddr(); const VAddr buf_end_addr = buf_start_addr + buffer.SizeBytes(); @@ -496,8 +489,11 @@ BufferId BufferCache::CreateBuffer(VAddr device_addr, u32 wanted_size) { wanted_size = static_cast(device_addr_end - device_addr); const OverlapResult overlap = ResolveOverlaps(device_addr, wanted_size); const u32 size = static_cast(overlap.end - overlap.begin); - const BufferId new_buffer_id = slot_buffers.insert( - instance, scheduler, MemoryUsage::DeviceLocal, overlap.begin, AllFlags, size); + const BufferId new_buffer_id = [&] { + std::scoped_lock lk{mutex}; + return slot_buffers.insert(instance, scheduler, MemoryUsage::DeviceLocal, overlap.begin, + AllFlags, size); + }(); auto& new_buffer = slot_buffers[new_buffer_id]; const size_t size_bytes = new_buffer.SizeBytes(); const auto cmdbuf = scheduler.CommandBuffer(); @@ -537,10 +533,8 @@ void BufferCache::ChangeRegister(BufferId buffer_id) { void BufferCache::SynchronizeBuffer(Buffer& buffer, VAddr device_addr, u32 size, bool is_texel_buffer) { - std::scoped_lock lk{mutex}; boost::container::small_vector copies; u64 total_size_bytes = 0; - u64 largest_copy = 0; VAddr buffer_start = buffer.CpuAddr(); memory_tracker.ForEachUploadRange(device_addr, size, [&](u64 device_addr_out, u64 range_size) { copies.push_back(vk::BufferCopy{ @@ -549,7 +543,6 @@ void BufferCache::SynchronizeBuffer(Buffer& buffer, VAddr device_addr, u32 size, .size = range_size, }); total_size_bytes += range_size; - largest_copy = std::max(largest_copy, range_size); }); SCOPE_EXIT { if (is_texel_buffer) { diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 0c70fa10b..c367795f1 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -3,7 +3,7 @@ #pragma once -#include +#include #include #include #include @@ -157,7 +157,7 @@ private: StreamBuffer staging_buffer; StreamBuffer stream_buffer; Buffer gds_buffer; - std::mutex mutex; + std::shared_mutex mutex; Common::SlotVector slot_buffers; RangeSet gpu_modified_ranges; vk::BufferView null_buffer_view; diff --git a/src/video_core/buffer_cache/memory_tracker_base.h b/src/video_core/buffer_cache/memory_tracker_base.h index ae61b55f2..d9166b11c 100644 --- a/src/video_core/buffer_cache/memory_tracker_base.h +++ b/src/video_core/buffer_cache/memory_tracker_base.h @@ -15,13 +15,8 @@ namespace VideoCore { class MemoryTracker { public: static constexpr size_t MAX_CPU_PAGE_BITS = 40; - static constexpr size_t HIGHER_PAGE_BITS = 22; - static constexpr size_t HIGHER_PAGE_SIZE = 1ULL << HIGHER_PAGE_BITS; - static constexpr size_t HIGHER_PAGE_MASK = HIGHER_PAGE_SIZE - 1ULL; static constexpr size_t NUM_HIGH_PAGES = 1ULL << (MAX_CPU_PAGE_BITS - HIGHER_PAGE_BITS); static constexpr size_t MANAGER_POOL_SIZE = 32; - static constexpr size_t WORDS_STACK_NEEDED = HIGHER_PAGE_SIZE / BYTES_PER_WORD; - using Manager = WordManager; public: explicit MemoryTracker(PageManager* tracker_) : tracker{tracker_} {} @@ -30,7 +25,7 @@ public: /// Returns true if a region has been modified from the CPU [[nodiscard]] bool IsRegionCpuModified(VAddr query_cpu_addr, u64 query_size) noexcept { return IteratePages( - query_cpu_addr, query_size, [](Manager* manager, u64 offset, size_t size) { + query_cpu_addr, query_size, [](RegionManager* manager, u64 offset, size_t size) { return manager->template IsRegionModified(offset, size); }); } @@ -38,52 +33,34 @@ public: /// Returns true if a region has been modified from the GPU [[nodiscard]] bool IsRegionGpuModified(VAddr query_cpu_addr, u64 query_size) noexcept { return IteratePages( - query_cpu_addr, query_size, [](Manager* manager, u64 offset, size_t size) { + query_cpu_addr, query_size, [](RegionManager* manager, u64 offset, size_t size) { return manager->template IsRegionModified(offset, size); }); } /// Mark region as CPU modified, notifying the device_tracker about this change void MarkRegionAsCpuModified(VAddr dirty_cpu_addr, u64 query_size) { - IteratePages(dirty_cpu_addr, query_size, - [](Manager* manager, u64 offset, size_t size) { - manager->template ChangeRegionState( - manager->GetCpuAddr() + offset, size); - }); - } - - /// Unmark region as CPU modified, notifying the device_tracker about this change - void UnmarkRegionAsCpuModified(VAddr dirty_cpu_addr, u64 query_size) { - IteratePages(dirty_cpu_addr, query_size, - [](Manager* manager, u64 offset, size_t size) { - manager->template ChangeRegionState( - manager->GetCpuAddr() + offset, size); - }); + IteratePages(dirty_cpu_addr, query_size, + [](RegionManager* manager, u64 offset, size_t size) { + manager->template ChangeRegionState( + manager->GetCpuAddr() + offset, size); + }); } /// Mark region as modified from the host GPU void MarkRegionAsGpuModified(VAddr dirty_cpu_addr, u64 query_size) noexcept { - IteratePages(dirty_cpu_addr, query_size, - [](Manager* manager, u64 offset, size_t size) { - manager->template ChangeRegionState( - manager->GetCpuAddr() + offset, size); - }); - } - - /// Unmark region as modified from the host GPU - void UnmarkRegionAsGpuModified(VAddr dirty_cpu_addr, u64 query_size) noexcept { - IteratePages(dirty_cpu_addr, query_size, - [](Manager* manager, u64 offset, size_t size) { - manager->template ChangeRegionState( - manager->GetCpuAddr() + offset, size); - }); + IteratePages(dirty_cpu_addr, query_size, + [](RegionManager* manager, u64 offset, size_t size) { + manager->template ChangeRegionState( + manager->GetCpuAddr() + offset, size); + }); } /// Call 'func' for each CPU modified range and unmark those pages as CPU modified template void ForEachUploadRange(VAddr query_cpu_range, u64 query_size, Func&& func) { IteratePages(query_cpu_range, query_size, - [&func](Manager* manager, u64 offset, size_t size) { + [&func](RegionManager* manager, u64 offset, size_t size) { manager->template ForEachModifiedRange( manager->GetCpuAddr() + offset, size, func); }); @@ -93,7 +70,7 @@ public: template void ForEachDownloadRange(VAddr query_cpu_range, u64 query_size, Func&& func) { IteratePages(query_cpu_range, query_size, - [&func](Manager* manager, u64 offset, size_t size) { + [&func](RegionManager* manager, u64 offset, size_t size) { if constexpr (clear) { manager->template ForEachModifiedRange( manager->GetCpuAddr() + offset, size, func); @@ -114,7 +91,7 @@ private: */ template bool IteratePages(VAddr cpu_address, size_t size, Func&& func) { - using FuncReturn = typename std::invoke_result::type; + using FuncReturn = typename std::invoke_result::type; static constexpr bool BOOL_BREAK = std::is_same_v; std::size_t remaining_size{size}; std::size_t page_index{cpu_address >> HIGHER_PAGE_BITS}; @@ -155,7 +132,7 @@ private: manager_pool.emplace_back(); auto& last_pool = manager_pool.back(); for (size_t i = 0; i < MANAGER_POOL_SIZE; i++) { - std::construct_at(&last_pool[i], tracker, 0, HIGHER_PAGE_SIZE); + std::construct_at(&last_pool[i], tracker, 0); free_managers.push_back(&last_pool[i]); } } @@ -167,9 +144,9 @@ private: } PageManager* tracker; - std::deque> manager_pool; - std::vector free_managers; - std::array top_tier{}; + std::deque> manager_pool; + std::vector free_managers; + std::array top_tier{}; }; } // namespace VideoCore diff --git a/src/video_core/buffer_cache/word_manager.h b/src/video_core/buffer_cache/word_manager.h index ae85d1eb1..7ad33d7a6 100644 --- a/src/video_core/buffer_cache/word_manager.h +++ b/src/video_core/buffer_cache/word_manager.h @@ -3,10 +3,12 @@ #pragma once -#include +#include +#include #include #include -#include "common/div_ceil.h" + +#include "common/spin_lock.h" #include "common/types.h" #include "video_core/page_manager.h" @@ -16,135 +18,32 @@ constexpr u64 PAGES_PER_WORD = 64; constexpr u64 BYTES_PER_PAGE = 4_KB; constexpr u64 BYTES_PER_WORD = PAGES_PER_WORD * BYTES_PER_PAGE; +constexpr u64 HIGHER_PAGE_BITS = 22; +constexpr u64 HIGHER_PAGE_SIZE = 1ULL << HIGHER_PAGE_BITS; +constexpr u64 HIGHER_PAGE_MASK = HIGHER_PAGE_SIZE - 1ULL; +constexpr u64 NUM_REGION_WORDS = HIGHER_PAGE_SIZE / BYTES_PER_WORD; + enum class Type { CPU, GPU, Untracked, }; -/// Vector tracking modified pages tightly packed with small vector optimization -template -struct WordsArray { - /// Returns the pointer to the words state - [[nodiscard]] const u64* Pointer(bool is_short) const noexcept { - return is_short ? stack.data() : heap; - } +using WordsArray = std::array; - /// Returns the pointer to the words state - [[nodiscard]] u64* Pointer(bool is_short) noexcept { - return is_short ? stack.data() : heap; - } - - std::array stack{}; ///< Small buffers storage - u64* heap; ///< Not-small buffers pointer to the storage -}; - -template -struct Words { - explicit Words() = default; - explicit Words(u64 size_bytes_) : size_bytes{size_bytes_} { - num_words = Common::DivCeil(size_bytes, BYTES_PER_WORD); - if (IsShort()) { - cpu.stack.fill(~u64{0}); - gpu.stack.fill(0); - untracked.stack.fill(~u64{0}); - } else { - // Share allocation between CPU and GPU pages and set their default values - u64* const alloc = new u64[num_words * 3]; - cpu.heap = alloc; - gpu.heap = alloc + num_words; - untracked.heap = alloc + num_words * 2; - std::fill_n(cpu.heap, num_words, ~u64{0}); - std::fill_n(gpu.heap, num_words, 0); - std::fill_n(untracked.heap, num_words, ~u64{0}); - } - // Clean up tailing bits - const u64 last_word_size = size_bytes % BYTES_PER_WORD; - const u64 last_local_page = Common::DivCeil(last_word_size, BYTES_PER_PAGE); - const u64 shift = (PAGES_PER_WORD - last_local_page) % PAGES_PER_WORD; - const u64 last_word = (~u64{0} << shift) >> shift; - cpu.Pointer(IsShort())[NumWords() - 1] = last_word; - untracked.Pointer(IsShort())[NumWords() - 1] = last_word; - } - - ~Words() { - Release(); - } - - Words& operator=(Words&& rhs) noexcept { - Release(); - size_bytes = rhs.size_bytes; - num_words = rhs.num_words; - cpu = rhs.cpu; - gpu = rhs.gpu; - untracked = rhs.untracked; - rhs.cpu.heap = nullptr; - return *this; - } - - Words(Words&& rhs) noexcept - : size_bytes{rhs.size_bytes}, num_words{rhs.num_words}, cpu{rhs.cpu}, gpu{rhs.gpu}, - untracked{rhs.untracked} { - rhs.cpu.heap = nullptr; - } - - Words& operator=(const Words&) = delete; - Words(const Words&) = delete; - - /// Returns true when the buffer fits in the small vector optimization - [[nodiscard]] bool IsShort() const noexcept { - return num_words <= stack_words; - } - - /// Returns the number of words of the buffer - [[nodiscard]] size_t NumWords() const noexcept { - return num_words; - } - - /// Release buffer resources - void Release() { - if (!IsShort()) { - // CPU written words is the base for the heap allocation - delete[] cpu.heap; - } - } - - template - std::span Span() noexcept { - if constexpr (type == Type::CPU) { - return std::span(cpu.Pointer(IsShort()), num_words); - } else if constexpr (type == Type::GPU) { - return std::span(gpu.Pointer(IsShort()), num_words); - } else if constexpr (type == Type::Untracked) { - return std::span(untracked.Pointer(IsShort()), num_words); - } - } - - template - std::span Span() const noexcept { - if constexpr (type == Type::CPU) { - return std::span(cpu.Pointer(IsShort()), num_words); - } else if constexpr (type == Type::GPU) { - return std::span(gpu.Pointer(IsShort()), num_words); - } else if constexpr (type == Type::Untracked) { - return std::span(untracked.Pointer(IsShort()), num_words); - } - } - - u64 size_bytes = 0; - size_t num_words = 0; - WordsArray cpu; - WordsArray gpu; - WordsArray untracked; -}; - -template -class WordManager { +/** + * Allows tracking CPU and GPU modification of pages in a contigious 4MB virtual address region. + * Information is stored in bitsets for spacial locality and fast update of single pages. + */ +class RegionManager { public: - explicit WordManager(PageManager* tracker_, VAddr cpu_addr_, u64 size_bytes) - : tracker{tracker_}, cpu_addr{cpu_addr_}, words{size_bytes} {} - - explicit WordManager() = default; + explicit RegionManager(PageManager* tracker_, VAddr cpu_addr_) + : tracker{tracker_}, cpu_addr{cpu_addr_} { + cpu.fill(~u64{0}); + gpu.fill(0); + untracked.fill(~u64{0}); + } + explicit RegionManager() = default; void SetCpuAddress(VAddr new_cpu_addr) { cpu_addr = new_cpu_addr; @@ -175,12 +74,12 @@ public: static constexpr bool BOOL_BREAK = std::is_same_v; const size_t start = static_cast(std::max(static_cast(offset), 0LL)); const size_t end = static_cast(std::max(static_cast(offset + size), 0LL)); - if (start >= SizeBytes() || end <= start) { + if (start >= HIGHER_PAGE_SIZE || end <= start) { return; } auto [start_word, start_page] = GetWordPage(start); auto [end_word, end_page] = GetWordPage(end + BYTES_PER_PAGE - 1ULL); - const size_t num_words = NumWords(); + constexpr size_t num_words = NUM_REGION_WORDS; start_word = std::min(start_word, num_words); end_word = std::min(end_word, num_words); const size_t diff = end_word - start_word; @@ -225,21 +124,21 @@ public: */ template void ChangeRegionState(u64 dirty_addr, u64 size) noexcept(type == Type::GPU) { - std::span state_words = words.template Span(); - [[maybe_unused]] std::span untracked_words = words.template Span(); + std::scoped_lock lk{lock}; + std::span state_words = Span(); IterateWords(dirty_addr - cpu_addr, size, [&](size_t index, u64 mask) { if constexpr (type == Type::CPU) { - NotifyPageTracker(index, untracked_words[index], mask); + UpdateProtection(index, untracked[index], mask); } if constexpr (enable) { state_words[index] |= mask; if constexpr (type == Type::CPU) { - untracked_words[index] |= mask; + untracked[index] |= mask; } } else { state_words[index] &= ~mask; if constexpr (type == Type::CPU) { - untracked_words[index] &= ~mask; + untracked[index] &= ~mask; } } }); @@ -255,10 +154,10 @@ public: */ template void ForEachModifiedRange(VAddr query_cpu_range, s64 size, Func&& func) { + std::scoped_lock lk{lock}; static_assert(type != Type::Untracked); - std::span state_words = words.template Span(); - [[maybe_unused]] std::span untracked_words = words.template Span(); + std::span state_words = Span(); const size_t offset = query_cpu_range - cpu_addr; bool pending = false; size_t pending_offset{}; @@ -269,16 +168,16 @@ public: }; IterateWords(offset, size, [&](size_t index, u64 mask) { if constexpr (type == Type::GPU) { - mask &= ~untracked_words[index]; + mask &= ~untracked[index]; } const u64 word = state_words[index] & mask; if constexpr (clear) { if constexpr (type == Type::CPU) { - NotifyPageTracker(index, untracked_words[index], mask); + UpdateProtection(index, untracked[index], mask); } state_words[index] &= ~mask; if constexpr (type == Type::CPU) { - untracked_words[index] &= ~mask; + untracked[index] &= ~mask; } } const size_t base_offset = index * PAGES_PER_WORD; @@ -315,13 +214,11 @@ public: [[nodiscard]] bool IsRegionModified(u64 offset, u64 size) const noexcept { static_assert(type != Type::Untracked); - const std::span state_words = words.template Span(); - [[maybe_unused]] const std::span untracked_words = - words.template Span(); + const std::span state_words = Span(); bool result = false; IterateWords(offset, size, [&](size_t index, u64 mask) { if constexpr (type == Type::GPU) { - mask &= ~untracked_words[index]; + mask &= ~untracked[index]; } const u64 word = state_words[index] & mask; if (word != 0) { @@ -333,44 +230,7 @@ public: return result; } - /// Returns the number of words of the manager - [[nodiscard]] size_t NumWords() const noexcept { - return words.NumWords(); - } - - /// Returns the size in bytes of the manager - [[nodiscard]] u64 SizeBytes() const noexcept { - return words.size_bytes; - } - - /// Returns true when the buffer fits in the small vector optimization - [[nodiscard]] bool IsShort() const noexcept { - return words.IsShort(); - } - private: - template - u64* Array() noexcept { - if constexpr (type == Type::CPU) { - return words.cpu.Pointer(IsShort()); - } else if constexpr (type == Type::GPU) { - return words.gpu.Pointer(IsShort()); - } else if constexpr (type == Type::Untracked) { - return words.untracked.Pointer(IsShort()); - } - } - - template - const u64* Array() const noexcept { - if constexpr (type == Type::CPU) { - return words.cpu.Pointer(IsShort()); - } else if constexpr (type == Type::GPU) { - return words.gpu.Pointer(IsShort()); - } else if constexpr (type == Type::Untracked) { - return words.untracked.Pointer(IsShort()); - } - } - /** * Notify tracker about changes in the CPU tracking state of a word in the buffer * @@ -381,7 +241,7 @@ private: * @tparam add_to_tracker True when the tracker should start tracking the new pages */ template - void NotifyPageTracker(u64 word_index, u64 current_bits, u64 new_bits) const { + void UpdateProtection(u64 word_index, u64 current_bits, u64 new_bits) const { u64 changed_bits = (add_to_tracker ? current_bits : ~current_bits) & new_bits; VAddr addr = cpu_addr + word_index * BYTES_PER_WORD; IteratePages(changed_bits, [&](size_t offset, size_t size) { @@ -390,9 +250,34 @@ private: }); } + template + std::span Span() noexcept { + if constexpr (type == Type::CPU) { + return cpu; + } else if constexpr (type == Type::GPU) { + return gpu; + } else if constexpr (type == Type::Untracked) { + return untracked; + } + } + + template + std::span Span() const noexcept { + if constexpr (type == Type::CPU) { + return cpu; + } else if constexpr (type == Type::GPU) { + return gpu; + } else if constexpr (type == Type::Untracked) { + return untracked; + } + } + + Common::SpinLock lock; PageManager* tracker; VAddr cpu_addr = 0; - Words words; + WordsArray cpu; + WordsArray gpu; + WordsArray untracked; }; } // namespace VideoCore diff --git a/src/video_core/multi_level_page_table.h b/src/video_core/multi_level_page_table.h index 527476f3b..7f3205e1a 100644 --- a/src/video_core/multi_level_page_table.h +++ b/src/video_core/multi_level_page_table.h @@ -39,6 +39,15 @@ public: return &(*first_level_map[l1_page])[l2_page]; } + [[nodiscard]] const Entry* find(size_t page) const { + const size_t l1_page = page >> SecondLevelBits; + const size_t l2_page = page & (NumEntriesPerL1Page - 1); + if (!first_level_map[l1_page]) { + return nullptr; + } + return &(*first_level_map[l1_page])[l2_page]; + } + [[nodiscard]] const Entry& operator[](size_t page) const { const size_t l1_page = page >> SecondLevelBits; const size_t l2_page = page & (NumEntriesPerL1Page - 1); diff --git a/src/video_core/page_manager.cpp b/src/video_core/page_manager.cpp index 556555c25..47ed9e543 100644 --- a/src/video_core/page_manager.cpp +++ b/src/video_core/page_manager.cpp @@ -185,7 +185,7 @@ void PageManager::OnGpuUnmap(VAddr address, size_t size) { void PageManager::UpdatePagesCachedCount(VAddr addr, u64 size, s32 delta) { static constexpr u64 PageShift = 12; - std::scoped_lock lk{mutex}; + std::scoped_lock lk{lock}; const u64 num_pages = ((addr + size - 1) >> PageShift) - (addr >> PageShift) + 1; const u64 page_start = addr >> PageShift; const u64 page_end = page_start + num_pages; diff --git a/src/video_core/page_manager.h b/src/video_core/page_manager.h index 29a946a8f..f44307f92 100644 --- a/src/video_core/page_manager.h +++ b/src/video_core/page_manager.h @@ -4,8 +4,8 @@ #pragma once #include -#include #include +#include "common/spin_lock.h" #include "common/types.h" namespace Vulkan { @@ -35,8 +35,8 @@ private: struct Impl; std::unique_ptr impl; Vulkan::Rasterizer* rasterizer; - std::mutex mutex; boost::icl::interval_map cached_pages; + Common::SpinLock lock; }; } // namespace VideoCore From 596f4cdf0e66a97c9d2d4272091d8c0167a5b8e1 Mon Sep 17 00:00:00 2001 From: liberodark Date: Thu, 2 Jan 2025 14:39:39 +0100 Subject: [PATCH 336/549] Fix amdgpu & other issues (#2000) --- src/core/devtools/widget/reg_popup.cpp | 2 +- src/video_core/amdgpu/liverpool.h | 4 +-- src/video_core/amdgpu/resource.h | 30 ++++++++++--------- .../renderer_vulkan/liverpool_to_vk.cpp | 4 +-- .../renderer_vulkan/vk_pipeline_cache.cpp | 4 +-- src/video_core/texture_cache/image_info.cpp | 4 +-- src/video_core/texture_cache/image_view.cpp | 3 +- 7 files changed, 27 insertions(+), 24 deletions(-) diff --git a/src/core/devtools/widget/reg_popup.cpp b/src/core/devtools/widget/reg_popup.cpp index 2727e1745..fae620901 100644 --- a/src/core/devtools/widget/reg_popup.cpp +++ b/src/core/devtools/widget/reg_popup.cpp @@ -66,7 +66,7 @@ void RegPopup::DrawColorBuffer(const AmdGpu::Liverpool::ColorBuffer& buffer) { "GetColorSliceSize()", buffer.GetColorSliceSize(), "GetTilingMode()", buffer.GetTilingMode(), "IsTiled()", buffer.IsTiled(), - "NumFormat()", buffer.NumFormat() + "NumFormat()", buffer.GetNumberFmt() ); // clang-format on diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index f1607f03e..d2d1aab3c 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -889,11 +889,11 @@ struct Liverpool { return !info.linear_general; } - [[nodiscard]] DataFormat DataFormat() const { + [[nodiscard]] DataFormat GetDataFmt() const { return RemapDataFormat(info.format); } - [[nodiscard]] NumberFormat NumFormat() const { + [[nodiscard]] NumberFormat GetNumberFmt() const { // There is a small difference between T# and CB number types, account for it. return RemapNumberFormat(info.number_type == NumberFormat::SnormNz ? NumberFormat::Srgb diff --git a/src/video_core/amdgpu/resource.h b/src/video_core/amdgpu/resource.h index 4de25adbf..208f7f380 100644 --- a/src/video_core/amdgpu/resource.h +++ b/src/video_core/amdgpu/resource.h @@ -79,21 +79,23 @@ inline NumberFormat RemapNumberFormat(const NumberFormat format) { inline CompMapping RemapComponents(const DataFormat format, const CompMapping components) { switch (format) { - case DataFormat::Format11_11_10: - return { - .r = components.b, - .g = components.g, - .b = components.r, - .a = components.a, - }; + case DataFormat::Format11_11_10: { + CompMapping result; + result.r = components.b; + result.g = components.g; + result.b = components.r; + result.a = components.a; + return result; + } case DataFormat::Format10_10_10_2: - case DataFormat::Format5_5_5_1: - return { - .r = components.a, - .g = components.b, - .b = components.g, - .a = components.r, - }; + case DataFormat::Format5_5_5_1: { + CompMapping result; + result.r = components.a; + result.g = components.b; + result.b = components.g; + result.a = components.r; + return result; + } default: return components; } diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp index eba2050e0..97825b1e1 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp @@ -770,8 +770,8 @@ vk::Format DepthFormat(DepthBuffer::ZFormat z_format, DepthBuffer::StencilFormat vk::ClearValue ColorBufferClearValue(const AmdGpu::Liverpool::ColorBuffer& color_buffer) { const auto comp_swizzle = color_buffer.Swizzle(); - const auto format = color_buffer.DataFormat(); - const auto number_type = color_buffer.NumFormat(); + const auto format = color_buffer.GetDataFmt(); + const auto number_type = color_buffer.GetNumberFmt(); const auto& c0 = color_buffer.clear_word0; const auto& c1 = color_buffer.clear_word1; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index cd1b42b05..ba069dae1 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -328,8 +328,8 @@ bool PipelineCache::RefreshGraphicsKey() { } key.color_formats[remapped_cb] = - LiverpoolToVK::SurfaceFormat(col_buf.DataFormat(), col_buf.NumFormat()); - key.color_num_formats[remapped_cb] = col_buf.NumFormat(); + LiverpoolToVK::SurfaceFormat(col_buf.GetDataFmt(), col_buf.GetNumberFmt()); + key.color_num_formats[remapped_cb] = col_buf.GetNumberFmt(); key.color_swizzles[remapped_cb] = col_buf.Swizzle(); } diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index 0559f1be3..adc72c21f 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -265,9 +265,9 @@ ImageInfo::ImageInfo(const AmdGpu::Liverpool::ColorBuffer& buffer, const AmdGpu::Liverpool::CbDbExtent& hint /*= {}*/) noexcept { props.is_tiled = buffer.IsTiled(); tiling_mode = buffer.GetTilingMode(); - pixel_format = LiverpoolToVK::SurfaceFormat(buffer.DataFormat(), buffer.NumFormat()); + pixel_format = LiverpoolToVK::SurfaceFormat(buffer.GetDataFmt(), buffer.GetNumberFmt()); num_samples = buffer.NumSamples(); - num_bits = NumBits(buffer.DataFormat()); + num_bits = NumBits(buffer.GetDataFmt()); type = vk::ImageType::e2D; size.width = hint.Valid() ? hint.width : buffer.Pitch(); size.height = hint.Valid() ? hint.height : buffer.Height(); diff --git a/src/video_core/texture_cache/image_view.cpp b/src/video_core/texture_cache/image_view.cpp index a9ae41dd1..68b116558 100644 --- a/src/video_core/texture_cache/image_view.cpp +++ b/src/video_core/texture_cache/image_view.cpp @@ -76,7 +76,8 @@ ImageViewInfo::ImageViewInfo(const AmdGpu::Liverpool::ColorBuffer& col_buffer) n range.base.layer = col_buffer.view.slice_start; range.extent.layers = col_buffer.NumSlices() - range.base.layer; type = range.extent.layers > 1 ? vk::ImageViewType::e2DArray : vk::ImageViewType::e2D; - format = Vulkan::LiverpoolToVK::SurfaceFormat(col_buffer.DataFormat(), col_buffer.NumFormat()); + format = + Vulkan::LiverpoolToVK::SurfaceFormat(col_buffer.GetDataFmt(), col_buffer.GetNumberFmt()); } ImageViewInfo::ImageViewInfo(const AmdGpu::Liverpool::DepthBuffer& depth_buffer, From 55b50171f83ca247c19d5142d57c0583a4b07c1d Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Thu, 2 Jan 2025 07:33:53 -0800 Subject: [PATCH 337/549] audio: Improve port state guards. (#1998) --- src/core/libraries/audio/audioout.cpp | 151 +++++++++++++------------ src/core/libraries/audio/audioout.h | 8 +- src/core/libraries/audio/sdl_audio.cpp | 27 +++-- 3 files changed, 103 insertions(+), 83 deletions(-) diff --git a/src/core/libraries/audio/audioout.cpp b/src/core/libraries/audio/audioout.cpp index d69454c39..f0ad59c3b 100644 --- a/src/core/libraries/audio/audioout.cpp +++ b/src/core/libraries/audio/audioout.cpp @@ -3,13 +3,13 @@ #include #include -#include +#include +#include #include #include "common/assert.h" #include "common/config.h" #include "common/logging/log.h" -#include "common/polyfill_thread.h" #include "common/thread.h" #include "core/libraries/audio/audioout.h" #include "core/libraries/audio/audioout_backend.h" @@ -18,7 +18,7 @@ namespace Libraries::AudioOut { -std::shared_mutex ports_mutex; +std::mutex port_open_mutex{}; std::array ports_out{}; static std::unique_ptr audio; @@ -93,17 +93,20 @@ int PS4_SYSV_ABI sceAudioOutClose(s32 handle) { return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; } - std::scoped_lock lock(ports_mutex); + std::unique_lock open_lock{port_open_mutex}; auto& port = ports_out.at(handle - 1); - if (!port.impl) { - return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; + { + std::unique_lock lock{port.mutex}; + if (!port.IsOpen()) { + return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; + } + std::free(port.output_buffer); + port.output_buffer = nullptr; + port.output_ready = false; + port.impl = nullptr; } - + // Stop outside of port lock scope to prevent deadlocks. port.output_thread.Stop(); - std::free(port.output_buffer); - port.output_buffer = nullptr; - port.output_ready = false; - port.impl = nullptr; return ORBIS_OK; } @@ -172,35 +175,34 @@ int PS4_SYSV_ABI sceAudioOutGetPortState(s32 handle, OrbisAudioOutPortState* sta return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; } - std::scoped_lock lock(ports_mutex); - const auto& port = ports_out.at(handle - 1); - if (!port.impl) { - return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; + auto& port = ports_out.at(handle - 1); + { + std::unique_lock lock{port.mutex}; + if (!port.IsOpen()) { + return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; + } + switch (port.type) { + case OrbisAudioOutPort::Main: + case OrbisAudioOutPort::Bgm: + case OrbisAudioOutPort::Voice: + state->output = 1; + state->channel = port.format_info.num_channels > 2 ? 2 : port.format_info.num_channels; + break; + case OrbisAudioOutPort::Personal: + case OrbisAudioOutPort::Padspk: + state->output = 4; + state->channel = 1; + break; + case OrbisAudioOutPort::Aux: + state->output = 0; + state->channel = 0; + break; + default: + UNREACHABLE(); + } + state->rerouteCounter = 0; + state->volume = 127; } - - state->rerouteCounter = 0; - state->volume = 127; - - switch (port.type) { - case OrbisAudioOutPort::Main: - case OrbisAudioOutPort::Bgm: - case OrbisAudioOutPort::Voice: - state->output = 1; - state->channel = port.format_info.num_channels > 2 ? 2 : port.format_info.num_channels; - break; - case OrbisAudioOutPort::Personal: - case OrbisAudioOutPort::Padspk: - state->output = 4; - state->channel = 1; - break; - case OrbisAudioOutPort::Aux: - state->output = 0; - state->channel = 0; - break; - default: - UNREACHABLE(); - } - return ORBIS_OK; } @@ -279,15 +281,16 @@ static void AudioOutputThread(PortOut* port, const std::stop_token& stop) { while (true) { timer.Start(); { - std::unique_lock lock{port->output_mutex}; - Common::CondvarWait(port->output_cv, lock, stop, [&] { return port->output_ready; }); - if (stop.stop_requested()) { - break; + std::unique_lock lock{port->mutex}; + if (port->output_cv.wait(lock, stop, [&] { return port->output_ready; })) { + port->impl->Output(port->output_buffer); + port->output_ready = false; } - port->impl->Output(port->output_buffer); - port->output_ready = false; } port->output_cv.notify_one(); + if (stop.stop_requested()) { + break; + } timer.End(); } } @@ -332,27 +335,30 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id, return ORBIS_AUDIO_OUT_ERROR_INVALID_FORMAT; } - std::scoped_lock lock{ports_mutex}; + std::unique_lock open_lock{port_open_mutex}; const auto port = - std::ranges::find_if(ports_out, [&](const PortOut& p) { return p.impl == nullptr; }); + std::ranges::find_if(ports_out, [&](const PortOut& p) { return !p.IsOpen(); }); if (port == ports_out.end()) { LOG_ERROR(Lib_AudioOut, "Audio ports are full"); return ORBIS_AUDIO_OUT_ERROR_PORT_FULL; } - port->type = port_type; - port->format_info = GetFormatInfo(format); - port->sample_rate = sample_rate; - port->buffer_frames = length; - port->volume.fill(SCE_AUDIO_OUT_VOLUME_0DB); + { + std::unique_lock port_lock(port->mutex); - port->impl = audio->Open(*port); + port->type = port_type; + port->format_info = GetFormatInfo(format); + port->sample_rate = sample_rate; + port->buffer_frames = length; + port->volume.fill(SCE_AUDIO_OUT_VOLUME_0DB); - port->output_buffer = std::malloc(port->BufferSize()); - port->output_ready = false; - port->output_thread.Run( - [port](const std::stop_token& stop) { AudioOutputThread(&*port, stop); }); + port->impl = audio->Open(*port); + port->output_buffer = std::malloc(port->BufferSize()); + port->output_ready = false; + port->output_thread.Run( + [port](const std::stop_token& stop) { AudioOutputThread(&*port, stop); }); + } return std::distance(ports_out.begin(), port) + 1; } @@ -367,14 +373,13 @@ s32 PS4_SYSV_ABI sceAudioOutOutput(s32 handle, void* ptr) { } auto& port = ports_out.at(handle - 1); - if (!port.impl) { - return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; - } - { - std::unique_lock lock{port.output_mutex}; + std::unique_lock lock{port.mutex}; + if (!port.IsOpen()) { + return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; + } port.output_cv.wait(lock, [&] { return !port.output_ready; }); - if (ptr != nullptr) { + if (ptr != nullptr && port.IsOpen()) { std::memcpy(port.output_buffer, ptr, port.BufferSize()); port.output_ready = true; } @@ -488,19 +493,19 @@ s32 PS4_SYSV_ABI sceAudioOutSetVolume(s32 handle, s32 flag, s32* vol) { return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; } - std::scoped_lock lock(ports_mutex); auto& port = ports_out.at(handle - 1); - if (!port.impl) { - return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; - } - - for (int i = 0; i < port.format_info.num_channels; i++, flag >>= 1u) { - if (flag & 0x1u) { - port.volume[i] = vol[i]; + { + std::unique_lock lock{port.mutex}; + if (!port.IsOpen()) { + return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; } + for (int i = 0; i < port.format_info.num_channels; i++, flag >>= 1u) { + if (flag & 0x1u) { + port.volume[i] = vol[i]; + } + } + port.impl->SetVolume(port.volume); } - - port.impl->SetVolume(port.volume); return ORBIS_OK; } diff --git a/src/core/libraries/audio/audioout.h b/src/core/libraries/audio/audioout.h index 4f7378dcd..5eafb43a1 100644 --- a/src/core/libraries/audio/audioout.h +++ b/src/core/libraries/audio/audioout.h @@ -3,7 +3,9 @@ #pragma once +#include #include +#include #include "common/bit_field.h" #include "core/libraries/kernel/threads.h" @@ -74,10 +76,10 @@ struct AudioFormatInfo { }; struct PortOut { + std::mutex mutex; std::unique_ptr impl{}; void* output_buffer; - std::mutex output_mutex; std::condition_variable_any output_cv; bool output_ready; Kernel::Thread output_thread{}; @@ -88,6 +90,10 @@ struct PortOut { u32 buffer_frames; std::array volume; + [[nodiscard]] bool IsOpen() const { + return impl != nullptr; + } + [[nodiscard]] u32 BufferSize() const { return buffer_frames * format_info.FrameSize(); } diff --git a/src/core/libraries/audio/sdl_audio.cpp b/src/core/libraries/audio/sdl_audio.cpp index 59d2d5cfb..762a9f682 100644 --- a/src/core/libraries/audio/sdl_audio.cpp +++ b/src/core/libraries/audio/sdl_audio.cpp @@ -14,7 +14,7 @@ namespace Libraries::AudioOut { class SDLPortBackend : public PortBackend { public: explicit SDLPortBackend(const PortOut& port) - : frame_size(port.format_info.FrameSize()), buffer_size(port.BufferSize()) { + : frame_size(port.format_info.FrameSize()), guest_buffer_size(port.BufferSize()) { // We want the latency for delivering frames out to be as small as possible, // so set the sample frames hint to the number of frames per buffer. const auto samples_num_str = std::to_string(port.buffer_frames); @@ -33,7 +33,7 @@ public: LOG_ERROR(Lib_AudioOut, "Failed to create SDL audio stream: {}", SDL_GetError()); return; } - queue_threshold = CalculateQueueThreshold(); + CalculateQueueThreshold(); if (!SDL_SetAudioStreamInputChannelMap(stream, port.format_info.channel_layout.data(), port.format_info.num_channels)) { LOG_ERROR(Lib_AudioOut, "Failed to configure SDL audio stream channel map: {}", @@ -71,9 +71,9 @@ public: queue_threshold); SDL_ClearAudioStream(stream); // Recalculate the threshold in case this happened because of a device change. - queue_threshold = CalculateQueueThreshold(); + CalculateQueueThreshold(); } - if (!SDL_PutAudioStreamData(stream, ptr, static_cast(buffer_size))) { + if (!SDL_PutAudioStreamData(stream, ptr, static_cast(guest_buffer_size))) { LOG_ERROR(Lib_AudioOut, "Failed to output to SDL audio stream: {}", SDL_GetError()); } } @@ -91,7 +91,7 @@ public: } private: - [[nodiscard]] u32 CalculateQueueThreshold() const { + void CalculateQueueThreshold() { SDL_AudioSpec discard; int sdl_buffer_frames; if (!SDL_GetAudioDeviceFormat(SDL_GetAudioStreamDevice(stream), &discard, @@ -100,13 +100,22 @@ private: SDL_GetError()); sdl_buffer_frames = 0; } - return std::max(buffer_size, sdl_buffer_frames * frame_size) * 4; + const auto sdl_buffer_size = sdl_buffer_frames * frame_size; + const auto new_threshold = std::max(guest_buffer_size, sdl_buffer_size) * 4; + if (host_buffer_size != sdl_buffer_size || queue_threshold != new_threshold) { + host_buffer_size = sdl_buffer_size; + queue_threshold = new_threshold; + LOG_INFO(Lib_AudioOut, + "SDL audio buffers: guest = {} bytes, host = {} bytes, threshold = {} bytes", + guest_buffer_size, host_buffer_size, queue_threshold); + } } u32 frame_size; - u32 buffer_size; - u32 queue_threshold; - SDL_AudioStream* stream; + u32 guest_buffer_size; + u32 host_buffer_size{}; + u32 queue_threshold{}; + SDL_AudioStream* stream{}; }; std::unique_ptr SDLAudioOut::Open(PortOut& port) { From b87bca2e46584b5fc1025569b5caf28f81eafd56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A5IGA?= <164882787+Xphalnos@users.noreply.github.com> Date: Thu, 2 Jan 2025 16:37:19 +0100 Subject: [PATCH 338/549] Reduce USBD Log Spamming (#2019) --- src/core/libraries/usbd/usbd.cpp | 130 +++++++++++++++---------------- 1 file changed, 65 insertions(+), 65 deletions(-) diff --git a/src/core/libraries/usbd/usbd.cpp b/src/core/libraries/usbd/usbd.cpp index c0e1b7ea8..fdfa50b23 100644 --- a/src/core/libraries/usbd/usbd.cpp +++ b/src/core/libraries/usbd/usbd.cpp @@ -10,327 +10,327 @@ namespace Libraries::Usbd { int PS4_SYSV_ABI sceUsbdAllocTransfer() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdAttachKernelDriver() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdBulkTransfer() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdCancelTransfer() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdCheckConnected() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdClaimInterface() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdClearHalt() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdClose() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdControlTransfer() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdControlTransferGetData() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdControlTransferGetSetup() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdDetachKernelDriver() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdEventHandlerActive() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdEventHandlingOk() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdExit() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdFillBulkTransfer() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdFillControlSetup() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdFillControlTransfer() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdFillInterruptTransfer() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdFillIsoTransfer() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdFreeConfigDescriptor() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdFreeDeviceList() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdFreeTransfer() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdGetActiveConfigDescriptor() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdGetBusNumber() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdGetConfigDescriptor() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdGetConfigDescriptorByValue() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdGetConfiguration() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdGetDescriptor() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdGetDevice() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdGetDeviceAddress() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdGetDeviceDescriptor() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdGetDeviceList() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdGetDeviceSpeed() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdGetIsoPacketBuffer() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdGetMaxIsoPacketSize() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdGetMaxPacketSize() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdGetStringDescriptor() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdGetStringDescriptorAscii() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdHandleEvents() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdHandleEventsLocked() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdHandleEventsTimeout() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_DEBUG(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdInit() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return 0x80240005; // Skip } int PS4_SYSV_ABI sceUsbdInterruptTransfer() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdKernelDriverActive() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdLockEvents() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdLockEventWaiters() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdOpen() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdOpenDeviceWithVidPid() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdRefDevice() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdReleaseInterface() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdResetDevice() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdSetConfiguration() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdSetInterfaceAltSetting() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdSetIsoPacketLengths() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdSubmitTransfer() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdTryLockEvents() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdUnlockEvents() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdUnlockEventWaiters() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdUnrefDevice() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdWaitForEvent() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI Func_65F6EF33E38FFF50() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI Func_97F056BAD90AADE7() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI Func_C55104A33B35B264() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } int PS4_SYSV_ABI Func_D56B43060720B1E0() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + LOG_ERROR(Lib_Usbd, "(STUBBED) called"); return ORBIS_OK; } From 55ccec4a387dcf1bf3dcf11f55f2ce546e88a173 Mon Sep 17 00:00:00 2001 From: polyproxy <47796739+polybiusproxy@users.noreply.github.com> Date: Thu, 2 Jan 2025 18:40:10 +0100 Subject: [PATCH 339/549] fix typos --- src/core/libraries/kernel/memory.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/core/libraries/kernel/memory.cpp b/src/core/libraries/kernel/memory.cpp index b18d5f570..8deefb496 100644 --- a/src/core/libraries/kernel/memory.cpp +++ b/src/core/libraries/kernel/memory.cpp @@ -505,13 +505,13 @@ int PS4_SYSV_ABI posix_munmap(void* addr, size_t len) { return result; } -static constexpr int MAX_PTR_APERTURES = 3; +static constexpr int MAX_PRT_APERTURES = 3; static constexpr VAddr PRT_AREA_START_ADDR = 0x1000000000; static constexpr size_t PRT_AREA_SIZE = 0xec00000000; -static std::array, MAX_PTR_APERTURES> PrtApertures{}; +static std::array, MAX_PRT_APERTURES> PrtApertures{}; int PS4_SYSV_ABI sceKernelSetPrtAperture(int id, VAddr address, size_t size) { - if (id < 0 || id >= MAX_PTR_APERTURES) { + if (id < 0 || id >= MAX_PRT_APERTURES) { return ORBIS_KERNEL_ERROR_EINVAL; } @@ -531,12 +531,12 @@ int PS4_SYSV_ABI sceKernelSetPrtAperture(int id, VAddr address, size_t size) { return ORBIS_OK; } -int PS4_SYSV_ABI sceKernelGetPrtAperture(int id, VAddr* addres, size_t* size) { - if (id < 0 || id >= MAX_PTR_APERTURES) { +int PS4_SYSV_ABI sceKernelGetPrtAperture(int id, VAddr* address, size_t* size) { + if (id < 0 || id >= MAX_PRT_APERTURES) { return ORBIS_KERNEL_ERROR_EINVAL; } - std::tie(*addres, *size) = PrtApertures[id]; + std::tie(*address, *size) = PrtApertures[id]; return ORBIS_OK; } From f7a8e2409c38a5f49fcf3cd0db1bdbf26ff69d7f Mon Sep 17 00:00:00 2001 From: psucien Date: Thu, 2 Jan 2025 19:41:15 +0100 Subject: [PATCH 340/549] hot-fix: debug build --- src/video_core/renderer_vulkan/liverpool_to_vk.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp index 97825b1e1..690d26cfc 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp @@ -697,8 +697,8 @@ static const size_t amd_gpu_number_format_bit_size = 4; // All values are under static size_t GetSurfaceFormatTableIndex(AmdGpu::DataFormat data_format, AmdGpu::NumberFormat num_format) { - DEBUG_ASSERT(data_format < 1 << amd_gpu_data_format_bit_size); - DEBUG_ASSERT(num_format < 1 << amd_gpu_number_format_bit_size); + DEBUG_ASSERT(u32(data_format) < 1 << amd_gpu_data_format_bit_size); + DEBUG_ASSERT(u32(num_format) < 1 << amd_gpu_number_format_bit_size); size_t result = static_cast(num_format) | (static_cast(data_format) << amd_gpu_number_format_bit_size); return result; From 77d217244142c11e01a960da4558ae122e20b3a8 Mon Sep 17 00:00:00 2001 From: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> Date: Thu, 2 Jan 2025 20:43:56 +0200 Subject: [PATCH 341/549] renderer_vulkan: Cleanup and improve barriers in caches (#1865) * texture_cache: Stricter barriers on image upload * buffer_cache: Stricter barrier for vkCmdUpdateBuffer * vk_rasterizer: Barrier also normal buffers and make it apply to all stages * texture_cache: Minor barrier cleanup * Batch image and buffer barriers in a single command * clang format --- src/video_core/buffer_cache/buffer_cache.cpp | 147 ++++++++++++++---- .../renderer_vulkan/vk_rasterizer.cpp | 8 +- .../texture_cache/texture_cache.cpp | 45 +++++- src/video_core/texture_cache/tile_manager.cpp | 59 +++---- src/video_core/texture_cache/tile_manager.h | 7 +- 5 files changed, 190 insertions(+), 76 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index 3ac6a3598..3e43b4fbc 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -259,7 +259,16 @@ void BufferCache::InlineData(VAddr address, const void* value, u32 num_bytes, bo const BufferId buffer_id = FindBuffer(address, num_bytes); return &slot_buffers[buffer_id]; }(); - const vk::BufferMemoryBarrier2 buf_barrier = { + const vk::BufferMemoryBarrier2 pre_barrier = { + .srcStageMask = vk::PipelineStageFlagBits2::eAllCommands, + .srcAccessMask = vk::AccessFlagBits2::eMemoryRead, + .dstStageMask = vk::PipelineStageFlagBits2::eTransfer, + .dstAccessMask = vk::AccessFlagBits2::eTransferWrite, + .buffer = buffer->Handle(), + .offset = buffer->Offset(address), + .size = num_bytes, + }; + const vk::BufferMemoryBarrier2 post_barrier = { .srcStageMask = vk::PipelineStageFlagBits2::eTransfer, .srcAccessMask = vk::AccessFlagBits2::eTransferWrite, .dstStageMask = vk::PipelineStageFlagBits2::eAllCommands, @@ -271,9 +280,14 @@ void BufferCache::InlineData(VAddr address, const void* value, u32 num_bytes, bo cmdbuf.pipelineBarrier2(vk::DependencyInfo{ .dependencyFlags = vk::DependencyFlagBits::eByRegion, .bufferMemoryBarrierCount = 1, - .pBufferMemoryBarriers = &buf_barrier, + .pBufferMemoryBarriers = &pre_barrier, + }); + cmdbuf.updateBuffer(buffer->Handle(), buffer->Offset(address), num_bytes, value); + cmdbuf.pipelineBarrier2(vk::DependencyInfo{ + .dependencyFlags = vk::DependencyFlagBits::eByRegion, + .bufferMemoryBarrierCount = 1, + .pBufferMemoryBarriers = &post_barrier, }); - cmdbuf.updateBuffer(buffer->Handle(), buf_barrier.offset, num_bytes, value); } std::pair BufferCache::ObtainHostUBO(std::span data) { @@ -465,21 +479,48 @@ void BufferCache::JoinOverlap(BufferId new_buffer_id, BufferId overlap_id, }; scheduler.EndRendering(); const auto cmdbuf = scheduler.CommandBuffer(); - static constexpr vk::MemoryBarrier READ_BARRIER{ - .srcAccessMask = vk::AccessFlagBits::eMemoryWrite, - .dstAccessMask = vk::AccessFlagBits::eTransferRead | vk::AccessFlagBits::eTransferWrite, + const std::array pre_barriers = { + vk::BufferMemoryBarrier2{ + .srcStageMask = vk::PipelineStageFlagBits2::eAllCommands, + .srcAccessMask = vk::AccessFlagBits2::eMemoryRead | vk::AccessFlagBits2::eMemoryWrite, + .dstStageMask = vk::PipelineStageFlagBits2::eTransfer, + .dstAccessMask = vk::AccessFlagBits2::eTransferRead, + .buffer = overlap.Handle(), + .offset = 0, + .size = overlap.SizeBytes(), + }, }; - static constexpr vk::MemoryBarrier WRITE_BARRIER{ - .srcAccessMask = vk::AccessFlagBits::eTransferWrite, - .dstAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite, + const std::array post_barriers = { + vk::BufferMemoryBarrier2{ + .srcStageMask = vk::PipelineStageFlagBits2::eTransfer, + .srcAccessMask = vk::AccessFlagBits2::eTransferRead, + .dstStageMask = vk::PipelineStageFlagBits2::eAllCommands, + .dstAccessMask = vk::AccessFlagBits2::eMemoryWrite, + .buffer = overlap.Handle(), + .offset = 0, + .size = overlap.SizeBytes(), + }, + vk::BufferMemoryBarrier2{ + .srcStageMask = vk::PipelineStageFlagBits2::eTransfer, + .srcAccessMask = vk::AccessFlagBits2::eTransferWrite, + .dstStageMask = vk::PipelineStageFlagBits2::eAllCommands, + .dstAccessMask = vk::AccessFlagBits2::eMemoryRead | vk::AccessFlagBits2::eMemoryWrite, + .buffer = new_buffer.Handle(), + .offset = dst_base_offset, + .size = overlap.SizeBytes(), + }, }; - cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands, - vk::PipelineStageFlagBits::eTransfer, vk::DependencyFlagBits::eByRegion, - READ_BARRIER, {}, {}); - cmdbuf.copyBuffer(overlap.buffer, new_buffer.buffer, copy); - cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, - vk::PipelineStageFlagBits::eAllCommands, - vk::DependencyFlagBits::eByRegion, WRITE_BARRIER, {}, {}); + cmdbuf.pipelineBarrier2(vk::DependencyInfo{ + .dependencyFlags = vk::DependencyFlagBits::eByRegion, + .bufferMemoryBarrierCount = 1, + .pBufferMemoryBarriers = pre_barriers.data(), + }); + cmdbuf.copyBuffer(overlap.Handle(), new_buffer.Handle(), copy); + cmdbuf.pipelineBarrier2(vk::DependencyInfo{ + .dependencyFlags = vk::DependencyFlagBits::eByRegion, + .bufferMemoryBarrierCount = static_cast(post_barriers.size()), + .pBufferMemoryBarriers = post_barriers.data(), + }); DeleteBuffer(overlap_id); } @@ -583,21 +624,35 @@ void BufferCache::SynchronizeBuffer(Buffer& buffer, VAddr device_addr, u32 size, } scheduler.EndRendering(); const auto cmdbuf = scheduler.CommandBuffer(); - static constexpr vk::MemoryBarrier READ_BARRIER{ - .srcAccessMask = vk::AccessFlagBits::eMemoryWrite, - .dstAccessMask = vk::AccessFlagBits::eTransferRead | vk::AccessFlagBits::eTransferWrite, + const vk::BufferMemoryBarrier2 pre_barrier = { + .srcStageMask = vk::PipelineStageFlagBits2::eAllCommands, + .srcAccessMask = vk::AccessFlagBits2::eMemoryRead, + .dstStageMask = vk::PipelineStageFlagBits2::eTransfer, + .dstAccessMask = vk::AccessFlagBits2::eTransferWrite, + .buffer = buffer.Handle(), + .offset = 0, + .size = buffer.SizeBytes(), }; - static constexpr vk::MemoryBarrier WRITE_BARRIER{ - .srcAccessMask = vk::AccessFlagBits::eTransferWrite, - .dstAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite, + const vk::BufferMemoryBarrier2 post_barrier = { + .srcStageMask = vk::PipelineStageFlagBits2::eTransfer, + .srcAccessMask = vk::AccessFlagBits2::eTransferWrite, + .dstStageMask = vk::PipelineStageFlagBits2::eAllCommands, + .dstAccessMask = vk::AccessFlagBits2::eMemoryRead | vk::AccessFlagBits2::eMemoryWrite, + .buffer = buffer.Handle(), + .offset = 0, + .size = buffer.SizeBytes(), }; - cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands, - vk::PipelineStageFlagBits::eTransfer, vk::DependencyFlagBits::eByRegion, - READ_BARRIER, {}, {}); + cmdbuf.pipelineBarrier2(vk::DependencyInfo{ + .dependencyFlags = vk::DependencyFlagBits::eByRegion, + .bufferMemoryBarrierCount = 1, + .pBufferMemoryBarriers = &pre_barrier, + }); cmdbuf.copyBuffer(src_buffer, buffer.buffer, copies); - cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, - vk::PipelineStageFlagBits::eAllCommands, - vk::DependencyFlagBits::eByRegion, WRITE_BARRIER, {}, {}); + cmdbuf.pipelineBarrier2(vk::DependencyInfo{ + .dependencyFlags = vk::DependencyFlagBits::eByRegion, + .bufferMemoryBarrierCount = 1, + .pBufferMemoryBarriers = &post_barrier, + }); } bool BufferCache::SynchronizeBufferFromImage(Buffer& buffer, VAddr device_addr, u32 size) { @@ -647,10 +702,42 @@ bool BufferCache::SynchronizeBufferFromImage(Buffer& buffer, VAddr device_addr, } if (!copies.empty()) { scheduler.EndRendering(); - image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, {}); + const vk::BufferMemoryBarrier2 pre_barrier = { + .srcStageMask = vk::PipelineStageFlagBits2::eAllCommands, + .srcAccessMask = vk::AccessFlagBits2::eMemoryRead, + .dstStageMask = vk::PipelineStageFlagBits2::eTransfer, + .dstAccessMask = vk::AccessFlagBits2::eTransferWrite, + .buffer = buffer.Handle(), + .offset = max_offset - size, + .size = size, + }; + const vk::BufferMemoryBarrier2 post_barrier = { + .srcStageMask = vk::PipelineStageFlagBits2::eTransfer, + .srcAccessMask = vk::AccessFlagBits2::eTransferWrite, + .dstStageMask = vk::PipelineStageFlagBits2::eAllCommands, + .dstAccessMask = vk::AccessFlagBits2::eMemoryRead, + .buffer = buffer.Handle(), + .offset = max_offset - size, + .size = size, + }; + auto barriers = image.GetBarriers(vk::ImageLayout::eTransferSrcOptimal, + vk::AccessFlagBits2::eTransferRead, + vk::PipelineStageFlagBits2::eTransfer, {}); const auto cmdbuf = scheduler.CommandBuffer(); - cmdbuf.copyImageToBuffer(image.image, vk::ImageLayout::eTransferSrcOptimal, buffer.buffer, + cmdbuf.pipelineBarrier2(vk::DependencyInfo{ + .dependencyFlags = vk::DependencyFlagBits::eByRegion, + .bufferMemoryBarrierCount = 1, + .pBufferMemoryBarriers = &pre_barrier, + .imageMemoryBarrierCount = static_cast(barriers.size()), + .pImageMemoryBarriers = barriers.data(), + }); + cmdbuf.copyImageToBuffer(image.image, vk::ImageLayout::eTransferSrcOptimal, buffer.Handle(), copies); + cmdbuf.pipelineBarrier2(vk::DependencyInfo{ + .dependencyFlags = vk::DependencyFlagBits::eByRegion, + .bufferMemoryBarrierCount = 1, + .pBufferMemoryBarriers = &post_barrier, + }); } return true; } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 4384cdbea..6e628239b 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -562,6 +562,12 @@ void Rasterizer::BindBuffers(const Shader::Info& stage, Shader::Backend::Binding push_data.AddOffset(binding.buffer, adjust); buffer_infos.emplace_back(vk_buffer->Handle(), offset_aligned, vsharp.GetSize() + adjust); + if (auto barrier = + vk_buffer->GetBarrier(desc.is_written ? vk::AccessFlagBits2::eShaderWrite + : vk::AccessFlagBits2::eShaderRead, + vk::PipelineStageFlagBits2::eAllCommands)) { + buffer_barriers.emplace_back(*barrier); + } } set_writes.push_back({ @@ -600,7 +606,7 @@ void Rasterizer::BindBuffers(const Shader::Info& stage, Shader::Backend::Binding if (auto barrier = vk_buffer->GetBarrier(desc.is_written ? vk::AccessFlagBits2::eShaderWrite : vk::AccessFlagBits2::eShaderRead, - vk::PipelineStageFlagBits2::eComputeShader)) { + vk::PipelineStageFlagBits2::eAllCommands)) { buffer_barriers.emplace_back(*barrier); } if (desc.is_written) { diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index 897d6f67e..291e1da7c 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -542,31 +542,62 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule sched_ptr->EndRendering(); const auto cmdbuf = sched_ptr->CommandBuffer(); - image.Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite, {}, - cmdbuf); - const VAddr image_addr = image.info.guest_address; const size_t image_size = image.info.guest_size_bytes; const auto [vk_buffer, buf_offset] = buffer_cache.ObtainViewBuffer(image_addr, image_size, is_gpu_dirty); + // The obtained buffer may be written by a shader so we need to emit a barrier to prevent RAW // hazard if (auto barrier = vk_buffer->GetBarrier(vk::AccessFlagBits2::eTransferRead, vk::PipelineStageFlagBits2::eTransfer)) { - const auto dependencies = vk::DependencyInfo{ + cmdbuf.pipelineBarrier2(vk::DependencyInfo{ .dependencyFlags = vk::DependencyFlagBits::eByRegion, .bufferMemoryBarrierCount = 1, .pBufferMemoryBarriers = &barrier.value(), - }; - cmdbuf.pipelineBarrier2(dependencies); + }); } - const auto [buffer, offset] = tile_manager.TryDetile(vk_buffer->Handle(), buf_offset, image); + const auto [buffer, offset] = + tile_manager.TryDetile(vk_buffer->Handle(), buf_offset, image.info); for (auto& copy : image_copy) { copy.bufferOffset += offset; } + const vk::BufferMemoryBarrier2 pre_barrier{ + .srcStageMask = vk::PipelineStageFlagBits2::eAllCommands, + .srcAccessMask = vk::AccessFlagBits2::eMemoryWrite, + .dstStageMask = vk::PipelineStageFlagBits2::eTransfer, + .dstAccessMask = vk::AccessFlagBits2::eTransferRead, + .buffer = buffer, + .offset = offset, + .size = image_size, + }; + const vk::BufferMemoryBarrier2 post_barrier{ + .srcStageMask = vk::PipelineStageFlagBits2::eTransfer, + .srcAccessMask = vk::AccessFlagBits2::eTransferWrite, + .dstStageMask = vk::PipelineStageFlagBits2::eAllCommands, + .dstAccessMask = vk::AccessFlagBits2::eMemoryRead | vk::AccessFlagBits2::eMemoryWrite, + .buffer = buffer, + .offset = offset, + .size = image_size, + }; + const auto image_barriers = + image.GetBarriers(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite, + vk::PipelineStageFlagBits2::eTransfer, {}); + cmdbuf.pipelineBarrier2(vk::DependencyInfo{ + .dependencyFlags = vk::DependencyFlagBits::eByRegion, + .bufferMemoryBarrierCount = 1, + .pBufferMemoryBarriers = &pre_barrier, + .imageMemoryBarrierCount = static_cast(image_barriers.size()), + .pImageMemoryBarriers = image_barriers.data(), + }); cmdbuf.copyBufferToImage(buffer, image.image, vk::ImageLayout::eTransferDstOptimal, image_copy); + cmdbuf.pipelineBarrier2(vk::DependencyInfo{ + .dependencyFlags = vk::DependencyFlagBits::eByRegion, + .bufferMemoryBarrierCount = 1, + .pBufferMemoryBarriers = &post_barrier, + }); image.flags &= ~ImageFlagBits::Dirty; } diff --git a/src/video_core/texture_cache/tile_manager.cpp b/src/video_core/texture_cache/tile_manager.cpp index fda7e511a..c1243dafb 100644 --- a/src/video_core/texture_cache/tile_manager.cpp +++ b/src/video_core/texture_cache/tile_manager.cpp @@ -4,6 +4,7 @@ #include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_shader_util.h" +#include "video_core/texture_cache/image_info.h" #include "video_core/texture_cache/image_view.h" #include "video_core/texture_cache/tile_manager.h" @@ -86,10 +87,10 @@ static vk::Format DemoteImageFormatForDetiling(vk::Format format) { return format; } -const DetilerContext* TileManager::GetDetiler(const Image& image) const { - const auto format = DemoteImageFormatForDetiling(image.info.pixel_format); +const DetilerContext* TileManager::GetDetiler(const ImageInfo& info) const { + const auto format = DemoteImageFormatForDetiling(info.pixel_format); - switch (image.info.tiling_mode) { + switch (info.tiling_mode) { case AmdGpu::TilingMode::Texture_MicroTiled: switch (format) { case vk::Format::eR8Uint: @@ -258,23 +259,23 @@ void TileManager::FreeBuffer(ScratchBuffer buffer) { } std::pair TileManager::TryDetile(vk::Buffer in_buffer, u32 in_offset, - Image& image) { - if (!image.info.props.is_tiled) { + const ImageInfo& info) { + if (!info.props.is_tiled) { return {in_buffer, in_offset}; } - const auto* detiler = GetDetiler(image); + const auto* detiler = GetDetiler(info); if (!detiler) { - if (image.info.tiling_mode != AmdGpu::TilingMode::Texture_MacroTiled && - image.info.tiling_mode != AmdGpu::TilingMode::Display_MacroTiled && - image.info.tiling_mode != AmdGpu::TilingMode::Depth_MacroTiled) { + if (info.tiling_mode != AmdGpu::TilingMode::Texture_MacroTiled && + info.tiling_mode != AmdGpu::TilingMode::Display_MacroTiled && + info.tiling_mode != AmdGpu::TilingMode::Depth_MacroTiled) { LOG_ERROR(Render_Vulkan, "Unsupported tiled image: {} ({})", - vk::to_string(image.info.pixel_format), NameOf(image.info.tiling_mode)); + vk::to_string(info.pixel_format), NameOf(info.tiling_mode)); } return {in_buffer, in_offset}; } - const u32 image_size = image.info.guest_size_bytes; + const u32 image_size = info.guest_size_bytes; // Prepare output buffer auto out_buffer = AllocBuffer(image_size, true); @@ -317,22 +318,21 @@ std::pair TileManager::TryDetile(vk::Buffer in_buffer, u32 in_o set_writes); DetilerParams params; - params.num_levels = image.info.resources.levels; - params.pitch0 = image.info.pitch >> (image.info.props.is_block ? 2u : 0u); - params.height = image.info.size.height; - if (image.info.tiling_mode == AmdGpu::TilingMode::Texture_Volume) { - ASSERT(image.info.resources.levels == 1); - ASSERT(image.info.num_bits >= 32); - const auto tiles_per_row = image.info.pitch / 8u; - const auto tiles_per_slice = tiles_per_row * ((image.info.size.height + 7u) / 8u); + params.num_levels = info.resources.levels; + params.pitch0 = info.pitch >> (info.props.is_block ? 2u : 0u); + params.height = info.size.height; + if (info.tiling_mode == AmdGpu::TilingMode::Texture_Volume) { + ASSERT(info.resources.levels == 1); + ASSERT(info.num_bits >= 32); + const auto tiles_per_row = info.pitch / 8u; + const auto tiles_per_slice = tiles_per_row * ((info.size.height + 7u) / 8u); params.sizes[0] = tiles_per_row; params.sizes[1] = tiles_per_slice; } else { - - ASSERT(image.info.resources.levels <= 14); + ASSERT(info.resources.levels <= 14); std::memset(¶ms.sizes, 0, sizeof(params.sizes)); - for (int m = 0; m < image.info.resources.levels; ++m) { - params.sizes[m] = image.info.mips_layout[m].size * image.info.resources.layers + + for (int m = 0; m < info.resources.levels; ++m) { + params.sizes[m] = info.mips_layout[m].size * info.resources.layers + (m > 0 ? params.sizes[m - 1] : 0); } } @@ -341,20 +341,9 @@ std::pair TileManager::TryDetile(vk::Buffer in_buffer, u32 in_o ¶ms); ASSERT((image_size % 64) == 0); - const auto bpp = image.info.num_bits * (image.info.props.is_block ? 16u : 1u); + const auto bpp = info.num_bits * (info.props.is_block ? 16u : 1u); const auto num_tiles = image_size / (64 * (bpp / 8)); cmdbuf.dispatch(num_tiles, 1, 1); - - const vk::BufferMemoryBarrier post_barrier{ - .srcAccessMask = vk::AccessFlagBits::eShaderWrite, - .dstAccessMask = vk::AccessFlagBits::eTransferRead, - .buffer = out_buffer.first, - .size = image_size, - }; - cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eComputeShader, - vk::PipelineStageFlagBits::eTransfer, vk::DependencyFlagBits::eByRegion, - {}, post_barrier, {}); - return {out_buffer.first, 0}; } diff --git a/src/video_core/texture_cache/tile_manager.h b/src/video_core/texture_cache/tile_manager.h index 72860bca0..1d731d2f2 100644 --- a/src/video_core/texture_cache/tile_manager.h +++ b/src/video_core/texture_cache/tile_manager.h @@ -5,11 +5,11 @@ #include "common/types.h" #include "video_core/buffer_cache/buffer.h" -#include "video_core/texture_cache/image.h" namespace VideoCore { class TextureCache; +struct ImageInfo; enum DetilerType : u32 { Micro8x1, @@ -36,14 +36,15 @@ public: TileManager(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler); ~TileManager(); - std::pair TryDetile(vk::Buffer in_buffer, u32 in_offset, Image& image); + std::pair TryDetile(vk::Buffer in_buffer, u32 in_offset, + const ImageInfo& info); ScratchBuffer AllocBuffer(u32 size, bool is_storage = false); void Upload(ScratchBuffer buffer, const void* data, size_t size); void FreeBuffer(ScratchBuffer buffer); private: - const DetilerContext* GetDetiler(const Image& image) const; + const DetilerContext* GetDetiler(const ImageInfo& info) const; private: const Vulkan::Instance& instance; From 67c531298a6c6c56a2ba82251fd4f04487601bc8 Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Thu, 2 Jan 2025 14:51:47 -0600 Subject: [PATCH 342/549] Fixup returns (#2023) On a signed out console, these two functions return ERROR_SIGNED_OUT. --- src/core/libraries/np_manager/np_manager.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/core/libraries/np_manager/np_manager.cpp b/src/core/libraries/np_manager/np_manager.cpp index ec9cc6bf5..87d752c69 100644 --- a/src/core/libraries/np_manager/np_manager.cpp +++ b/src/core/libraries/np_manager/np_manager.cpp @@ -972,11 +972,8 @@ int PS4_SYSV_ABI sceNpGetGamePresenceStatusA() { } int PS4_SYSV_ABI sceNpGetNpId(OrbisUserServiceUserId user_id, OrbisNpId* np_id) { - LOG_INFO(Lib_NpManager, "user_id {}", user_id); - const auto name = Config::getUserName(); - std::memset(np_id, 0, sizeof(OrbisNpId)); - name.copy(np_id->handle.data, sizeof(np_id->handle.data)); - return ORBIS_OK; + LOG_DEBUG(Lib_NpManager, "user_id {}", user_id); + return ORBIS_NP_ERROR_SIGNED_OUT; } int PS4_SYSV_ABI sceNpGetNpReachabilityState() { @@ -986,10 +983,7 @@ int PS4_SYSV_ABI sceNpGetNpReachabilityState() { int PS4_SYSV_ABI sceNpGetOnlineId(s32 user_id, OrbisNpOnlineId* online_id) { LOG_DEBUG(Lib_NpManager, "user_id {}", user_id); - const auto name = Config::getUserName(); - std::memset(online_id, 0, sizeof(OrbisNpOnlineId)); - name.copy(online_id->data, sizeof(online_id->data)); - return ORBIS_OK; + return ORBIS_NP_ERROR_SIGNED_OUT; } int PS4_SYSV_ABI sceNpGetParentalControlInfo() { From dcc662ff1afa4a7aad8f50260b64c2e03a0dd2e6 Mon Sep 17 00:00:00 2001 From: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> Date: Thu, 2 Jan 2025 22:52:10 +0200 Subject: [PATCH 343/549] ir_passes: Integrate DS barriers in block (#2020) --- .../frontend/translate/data_share.cpp | 6 -- .../frontend/translate/translate.h | 1 - .../ir/passes/shared_memory_barrier_pass.cpp | 71 ++++++++++++++----- 3 files changed, 52 insertions(+), 26 deletions(-) diff --git a/src/shader_recompiler/frontend/translate/data_share.cpp b/src/shader_recompiler/frontend/translate/data_share.cpp index 4408cae28..62c0423dd 100644 --- a/src/shader_recompiler/frontend/translate/data_share.cpp +++ b/src/shader_recompiler/frontend/translate/data_share.cpp @@ -205,7 +205,6 @@ void Translator::DS_WRITE(int bit_size, bool is_signed, bool is_pair, bool strid addr, ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0))); ir.WriteShared(bit_size, ir.GetVectorReg(data0), addr0); } - emit_ds_read_barrier = true; } void Translator::DS_SWIZZLE_B32(const GcnInst& inst) { @@ -222,11 +221,6 @@ void Translator::DS_SWIZZLE_B32(const GcnInst& inst) { void Translator::DS_READ(int bit_size, bool is_signed, bool is_pair, bool stride64, const GcnInst& inst) { - if (emit_ds_read_barrier && profile.needs_lds_barriers) { - ir.Barrier(); - emit_ds_read_barrier = false; - } - const IR::U32 addr{ir.GetVectorReg(IR::VectorReg(inst.src[0].code))}; IR::VectorReg dst_reg{inst.dst[0].code}; if (is_pair) { diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index e8584ec2f..9da0844e4 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -308,7 +308,6 @@ private: const RuntimeInfo& runtime_info; const Profile& profile; bool opcode_missing = false; - bool emit_ds_read_barrier = false; }; void Translate(IR::Block* block, u32 block_base, std::span inst_list, Info& info, diff --git a/src/shader_recompiler/ir/passes/shared_memory_barrier_pass.cpp b/src/shader_recompiler/ir/passes/shared_memory_barrier_pass.cpp index aad8fb148..ec7d7e986 100644 --- a/src/shader_recompiler/ir/passes/shared_memory_barrier_pass.cpp +++ b/src/shader_recompiler/ir/passes/shared_memory_barrier_pass.cpp @@ -8,6 +8,54 @@ namespace Shader::Optimization { +static void EmitBarrierInBlock(IR::Block* block) { + // This is inteded to insert a barrier when shared memory write and read + // occur in the same basic block. Also checks if branch depth is zero as + // we don't want to insert barrier in potentially divergent code. + bool emit_barrier_on_write = false; + bool emit_barrier_on_read = false; + const auto emit_barrier = [block](bool& emit_cond, IR::Inst& inst) { + if (emit_cond) { + IR::IREmitter ir{*block, IR::Block::InstructionList::s_iterator_to(inst)}; + ir.Barrier(); + emit_cond = false; + } + }; + for (IR::Inst& inst : block->Instructions()) { + if (inst.GetOpcode() == IR::Opcode::LoadSharedU32 || + inst.GetOpcode() == IR::Opcode::LoadSharedU64) { + emit_barrier(emit_barrier_on_read, inst); + emit_barrier_on_write = true; + } + if (inst.GetOpcode() == IR::Opcode::WriteSharedU32 || + inst.GetOpcode() == IR::Opcode::WriteSharedU64) { + emit_barrier(emit_barrier_on_write, inst); + emit_barrier_on_read = true; + } + } +} + +static void EmitBarrierInMergeBlock(const IR::AbstractSyntaxNode::Data& data) { + // Insert a barrier after divergent conditional blocks. + // This avoids potential softlocks and crashes when some threads + // initialize shared memory and others read from it. + const IR::U1 cond = data.if_node.cond; + const auto insert_barrier = + IR::BreadthFirstSearch(cond, [](IR::Inst* inst) -> std::optional { + if (inst->GetOpcode() == IR::Opcode::GetAttributeU32 && + inst->Arg(0).Attribute() == IR::Attribute::LocalInvocationId) { + return true; + } + return std::nullopt; + }); + if (insert_barrier) { + IR::Block* const merge = data.if_node.merge; + auto insert_point = std::ranges::find_if_not(merge->Instructions(), IR::IsPhi); + IR::IREmitter ir{*merge, insert_point}; + ir.Barrier(); + } +} + void SharedMemoryBarrierPass(IR::Program& program, const Profile& profile) { if (!program.info.uses_shared || !profile.needs_lds_barriers) { return; @@ -19,27 +67,12 @@ void SharedMemoryBarrierPass(IR::Program& program, const Profile& profile) { --branch_depth; continue; } - if (node.type != Type::If) { + if (node.type == Type::If && branch_depth++ == 0) { + EmitBarrierInMergeBlock(node.data); continue; } - u32 curr_depth = branch_depth++; - if (curr_depth != 0) { - continue; - } - const IR::U1 cond = node.data.if_node.cond; - const auto insert_barrier = - IR::BreadthFirstSearch(cond, [](IR::Inst* inst) -> std::optional { - if (inst->GetOpcode() == IR::Opcode::GetAttributeU32 && - inst->Arg(0).Attribute() == IR::Attribute::LocalInvocationId) { - return true; - } - return std::nullopt; - }); - if (insert_barrier) { - IR::Block* const merge = node.data.if_node.merge; - auto insert_point = std::ranges::find_if_not(merge->Instructions(), IR::IsPhi); - IR::IREmitter ir{*merge, insert_point}; - ir.Barrier(); + if (node.type == Type::Block && branch_depth == 0) { + EmitBarrierInBlock(node.data.block); } } } From 4e1733222f4b42d4292d6c7417f215c109cd51d3 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Thu, 2 Jan 2025 23:21:43 +0200 Subject: [PATCH 344/549] fixed deadzones (#2025) --- src/core/libraries/pad/pad.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index 27564294e..7eb628a90 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -104,8 +104,8 @@ int PS4_SYSV_ABI scePadGetControllerInformation(s32 handle, OrbisPadControllerIn pInfo->touchPadInfo.pixelDensity = 1; pInfo->touchPadInfo.resolution.x = 1920; pInfo->touchPadInfo.resolution.y = 950; - pInfo->stickInfo.deadZoneLeft = 20; - pInfo->stickInfo.deadZoneRight = 20; + pInfo->stickInfo.deadZoneLeft = 2; + pInfo->stickInfo.deadZoneRight = 2; pInfo->connectionType = ORBIS_PAD_PORT_TYPE_STANDARD; pInfo->connectedCount = 1; pInfo->connected = true; From 345d55669eddd5c9c8e5a017a184e98725d3c20c Mon Sep 17 00:00:00 2001 From: psucien Date: Thu, 2 Jan 2025 23:27:18 +0100 Subject: [PATCH 345/549] texture_cache: 8bpp macro detiler --- src/video_core/host_shaders/CMakeLists.txt | 1 + .../host_shaders/detile_macro32x2.comp | 4 +- .../host_shaders/detile_macro8x1.comp | 101 ++++++++++++++++++ src/video_core/texture_cache/tile_manager.cpp | 8 +- src/video_core/texture_cache/tile_manager.h | 1 + 5 files changed, 110 insertions(+), 5 deletions(-) create mode 100644 src/video_core/host_shaders/detile_macro8x1.comp diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index c2a3b53fd..44761545d 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -7,6 +7,7 @@ set(SHADER_FILES detile_m32x1.comp detile_m32x2.comp detile_m32x4.comp + detile_macro8x1.comp detile_macro32x1.comp detile_macro32x2.comp fs_tri.vert diff --git a/src/video_core/host_shaders/detile_macro32x2.comp b/src/video_core/host_shaders/detile_macro32x2.comp index d161484c1..986acc963 100644 --- a/src/video_core/host_shaders/detile_macro32x2.comp +++ b/src/video_core/host_shaders/detile_macro32x2.comp @@ -87,7 +87,7 @@ void main() { uint offs = slice_offs + tile_offs + (idx * BPP / 8); uint p0 = in_data[(offs >> 2) + 0]; - uint p1 = in_data[(offs >> 2) + 1]; + uint p1 = in_data[(offs >> 2) + 1]; out_data[2 * gl_GlobalInvocationID.x + 0] = p0; - out_data[2 * gl_GlobalInvocationID.x + 1] = p1; + out_data[2 * gl_GlobalInvocationID.x + 1] = p1; } diff --git a/src/video_core/host_shaders/detile_macro8x1.comp b/src/video_core/host_shaders/detile_macro8x1.comp new file mode 100644 index 000000000..cddc8af5b --- /dev/null +++ b/src/video_core/host_shaders/detile_macro8x1.comp @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#version 450 + +layout (local_size_x = 64, local_size_y = 1, local_size_z = 1) in; + +layout(std430, binding = 0) buffer input_buf { + uint in_data[]; +}; +layout(std430, binding = 1) buffer output_buf { + uint out_data[]; +}; + +layout(push_constant) uniform image_info { + uint num_levels; + uint pitch; + uint height; + uint c0; + uint c1; +} info; + +const uint lut_8bpp[][16] = { + { + 0x05040100, 0x45444140, + 0x07060302, 0x47464342, + 0x0d0c0908, 0x4d4c4948, + 0x0f0e0b0a, 0x4f4e4b4a, + 0x85848180, 0xc5c4c1c0, + 0x87868382, 0xc7c6c3c2, + 0x8d8c8988, 0xcdccc9c8, + 0x8f8e8b8a, 0xcfcecbca, + }, + { + 0x15141110, 0x55545150, + 0x17161312, 0x57565352, + 0x1d1c1918, 0x5d5c5958, + 0x1f1e1b1a, 0x5f5e5b5a, + 0x95949190, 0xd5d4d1d0, + 0x97969392, 0xd7d6d3d2, + 0x9d9c9998, 0xdddcd9d8, + 0x9f9e9b9a, 0xdfdedbda, + }, + { + 0x25242120, 0x65646160, + 0x27262322, 0x67666362, + 0x2d2c2928, 0x6d6c6968, + 0x2f2e2b2a, 0x6f6e6b6a, + 0xa5a4a1a0, 0xe5e4e1e0, + 0xa7a6a3a2, 0xe7e6e3e2, + 0xadaca9a8, 0xedece9e8, + 0xafaeabaa, 0xefeeebea, + }, + { + 0x35343130, 0x75747170, + 0x37363332, 0x77767372, + 0x3d3c3938, 0x7d7c7978, + 0x3f3e3b3a, 0x7f7e7b7a, + 0xb5b4b1b0, 0xf5f4f1f0, + 0xb7b6b3b2, 0xf7f6f3f2, + 0xbdbcb9b8, 0xfdfcf9f8, + 0xbfbebbba, 0xfffefbfa, + }, +}; + +#define MICRO_TILE_DIM (8) +#define MICRO_TILE_SZ (256) +#define TEXELS_PER_ELEMENT (1) +#define BPP (8) + +shared uint scratch[16]; + +void main() { + uint slot = gl_LocalInvocationID.x >> 2u; + atomicAnd(scratch[slot], 0); + + uint x = gl_GlobalInvocationID.x % info.pitch; + uint y = (gl_GlobalInvocationID.x / info.pitch) % info.height; + uint z = gl_GlobalInvocationID.x / (info.pitch * info.height); + + uint col = bitfieldExtract(x, 0, 3); + uint row = bitfieldExtract(y, 0, 3); + uint lut = bitfieldExtract(z, 0, 2); + uint idx_dw = lut_8bpp[lut][(col + row * MICRO_TILE_DIM) >> 2u]; + uint byte_ofs = (gl_LocalInvocationID.x & 3u) * 8; + uint idx = bitfieldExtract(idx_dw >> byte_ofs, 0, 8); + + uint slice_offs = (z >> 2u) * info.c1 * MICRO_TILE_SZ; + uint tile_row = y / MICRO_TILE_DIM; + uint tile_column = x / MICRO_TILE_DIM; + uint tile_offs = ((tile_row * info.c0) + tile_column) * MICRO_TILE_SZ; + uint offs = (slice_offs + tile_offs) + (idx * BPP / 8); + + uint p0 = in_data[offs >> 2u]; + uint byte = bitfieldExtract(p0 >> (offs * 8), 0, 8); + atomicOr(scratch[slot], byte << byte_ofs); + + if (byte_ofs == 0) { + out_data[gl_GlobalInvocationID.x >> 2u] = scratch[slot]; + } +} diff --git a/src/video_core/texture_cache/tile_manager.cpp b/src/video_core/texture_cache/tile_manager.cpp index c1243dafb..0e550c7dc 100644 --- a/src/video_core/texture_cache/tile_manager.cpp +++ b/src/video_core/texture_cache/tile_manager.cpp @@ -15,6 +15,7 @@ #include "video_core/host_shaders/detile_m8x2_comp.h" #include "video_core/host_shaders/detile_macro32x1_comp.h" #include "video_core/host_shaders/detile_macro32x2_comp.h" +#include "video_core/host_shaders/detile_macro8x1_comp.h" #include #include @@ -108,6 +109,8 @@ const DetilerContext* TileManager::GetDetiler(const ImageInfo& info) const { } case AmdGpu::TilingMode::Texture_Volume: switch (format) { + case vk::Format::eR8Uint: + return &detilers[DetilerType::Macro8x1]; case vk::Format::eR32Uint: return &detilers[DetilerType::Macro32x1]; case vk::Format::eR32G32Uint: @@ -133,8 +136,8 @@ TileManager::TileManager(const Vulkan::Instance& instance, Vulkan::Scheduler& sc static const std::array detiler_shaders{ HostShaders::DETILE_M8X1_COMP, HostShaders::DETILE_M8X2_COMP, HostShaders::DETILE_M32X1_COMP, HostShaders::DETILE_M32X2_COMP, - HostShaders::DETILE_M32X4_COMP, HostShaders::DETILE_MACRO32X1_COMP, - HostShaders::DETILE_MACRO32X2_COMP, + HostShaders::DETILE_M32X4_COMP, HostShaders::DETILE_MACRO8X1_COMP, + HostShaders::DETILE_MACRO32X1_COMP, HostShaders::DETILE_MACRO32X2_COMP, }; boost::container::static_vector bindings{ @@ -323,7 +326,6 @@ std::pair TileManager::TryDetile(vk::Buffer in_buffer, u32 in_o params.height = info.size.height; if (info.tiling_mode == AmdGpu::TilingMode::Texture_Volume) { ASSERT(info.resources.levels == 1); - ASSERT(info.num_bits >= 32); const auto tiles_per_row = info.pitch / 8u; const auto tiles_per_slice = tiles_per_row * ((info.size.height + 7u) / 8u); params.sizes[0] = tiles_per_row; diff --git a/src/video_core/texture_cache/tile_manager.h b/src/video_core/texture_cache/tile_manager.h index 1d731d2f2..bcf5accd3 100644 --- a/src/video_core/texture_cache/tile_manager.h +++ b/src/video_core/texture_cache/tile_manager.h @@ -18,6 +18,7 @@ enum DetilerType : u32 { Micro32x2, Micro32x4, + Macro8x1, Macro32x1, Macro32x2, From 2951788afc1bb4b53c6d427ca56c598247fd6f6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A5IGA?= <164882787+Xphalnos@users.noreply.github.com> Date: Fri, 3 Jan 2025 20:11:24 +0100 Subject: [PATCH 346/549] texture_cache: Adding some missing textures (#2031) --- src/video_core/texture_cache/tile_manager.cpp | 71 +++++++++++-------- 1 file changed, 43 insertions(+), 28 deletions(-) diff --git a/src/video_core/texture_cache/tile_manager.cpp b/src/video_core/texture_cache/tile_manager.cpp index 0e550c7dc..6b14b4602 100644 --- a/src/video_core/texture_cache/tile_manager.cpp +++ b/src/video_core/texture_cache/tile_manager.cpp @@ -25,54 +25,69 @@ namespace VideoCore { static vk::Format DemoteImageFormatForDetiling(vk::Format format) { switch (format) { - case vk::Format::eR8Uint: case vk::Format::eR8Unorm: case vk::Format::eR8Snorm: + case vk::Format::eR8Uint: + case vk::Format::eR8Srgb: return vk::Format::eR8Uint; - case vk::Format::eR4G4B4A4UnormPack16: - case vk::Format::eB5G6R5UnormPack16: - case vk::Format::eR5G5B5A1UnormPack16: case vk::Format::eR8G8Unorm: - case vk::Format::eR16Sfloat: - case vk::Format::eR16Uint: + case vk::Format::eR8G8Snorm: + case vk::Format::eR8G8Uint: + case vk::Format::eR8G8Srgb: case vk::Format::eR16Unorm: + case vk::Format::eR16Snorm: + case vk::Format::eR16Uint: + case vk::Format::eR16Sfloat: case vk::Format::eD16Unorm: + case vk::Format::eR4G4B4A4UnormPack16: + case vk::Format::eR5G5B5A1UnormPack16: + case vk::Format::eB5G5R5A1UnormPack16: + case vk::Format::eB5G6R5UnormPack16: return vk::Format::eR8G8Uint; - case vk::Format::eR8G8B8A8Srgb: - case vk::Format::eB8G8R8A8Srgb: - case vk::Format::eB8G8R8A8Unorm: case vk::Format::eR8G8B8A8Unorm: case vk::Format::eR8G8B8A8Snorm: case vk::Format::eR8G8B8A8Uint: - case vk::Format::eR32Sfloat: - case vk::Format::eD32Sfloat: - case vk::Format::eR32Uint: - case vk::Format::eR16G16Sfloat: + case vk::Format::eR8G8B8A8Srgb: + case vk::Format::eB8G8R8A8Unorm: + case vk::Format::eB8G8R8A8Snorm: + case vk::Format::eB8G8R8A8Uint: + case vk::Format::eB8G8R8A8Srgb: case vk::Format::eR16G16Unorm: case vk::Format::eR16G16Snorm: - case vk::Format::eB10G11R11UfloatPack32: + case vk::Format::eR16G16Uint: + case vk::Format::eR16G16Sfloat: + case vk::Format::eR32Uint: + case vk::Format::eR32Sfloat: + case vk::Format::eD32Sfloat: case vk::Format::eA2B10G10R10UnormPack32: + case vk::Format::eA2B10G10R10SnormPack32: + case vk::Format::eA2B10G10R10UintPack32: + case vk::Format::eB10G11R11UfloatPack32: + case vk::Format::eE5B9G9R9UfloatPack32: return vk::Format::eR32Uint; - case vk::Format::eBc1RgbaSrgbBlock: - case vk::Format::eBc1RgbaUnormBlock: - case vk::Format::eBc4UnormBlock: - case vk::Format::eR32G32Sfloat: - case vk::Format::eR32G32Uint: case vk::Format::eR16G16B16A16Unorm: + case vk::Format::eR16G16B16A16Snorm: case vk::Format::eR16G16B16A16Uint: case vk::Format::eR16G16B16A16Sfloat: + case vk::Format::eR32G32Uint: + case vk::Format::eR32G32Sfloat: + case vk::Format::eBc1RgbaUnormBlock: + case vk::Format::eBc1RgbaSrgbBlock: + case vk::Format::eBc4UnormBlock: + case vk::Format::eBc4SnormBlock: return vk::Format::eR32G32Uint; - case vk::Format::eBc2SrgbBlock: - case vk::Format::eBc2UnormBlock: - case vk::Format::eBc3SrgbBlock: - case vk::Format::eBc3UnormBlock: - case vk::Format::eBc5UnormBlock: - case vk::Format::eBc5SnormBlock: - case vk::Format::eBc7SrgbBlock: - case vk::Format::eBc7UnormBlock: - case vk::Format::eBc6HUfloatBlock: case vk::Format::eR32G32B32A32Uint: case vk::Format::eR32G32B32A32Sfloat: + case vk::Format::eBc2UnormBlock: + case vk::Format::eBc2SrgbBlock: + case vk::Format::eBc3UnormBlock: + case vk::Format::eBc3SrgbBlock: + case vk::Format::eBc5UnormBlock: + case vk::Format::eBc5SnormBlock: + case vk::Format::eBc6HUfloatBlock: + case vk::Format::eBc6HSfloatBlock: + case vk::Format::eBc7UnormBlock: + case vk::Format::eBc7SrgbBlock: return vk::Format::eR32G32B32A32Uint; default: break; From 9434cae458ac3a8d815f753c7c522e40a9765acb Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 3 Jan 2025 12:22:27 -0800 Subject: [PATCH 347/549] gnmdriver: Implement neo mode differences. (#2011) * gnmdriver: Implement neo mode differences. * gnmdriver: Move init sequences to separate file. --- CMakeLists.txt | 1 + src/core/libraries/gnmdriver/gnmdriver.cpp | 430 +++++--------- src/core/libraries/gnmdriver/gnmdriver_init.h | 542 ++++++++++++++++++ src/video_core/amdgpu/pm4_cmds.h | 5 + 4 files changed, 696 insertions(+), 282 deletions(-) create mode 100644 src/core/libraries/gnmdriver/gnmdriver_init.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c0f675266..36ebbf583 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -209,6 +209,7 @@ set(AUDIO_LIB src/core/libraries/audio/audioin.cpp set(GNM_LIB src/core/libraries/gnmdriver/gnmdriver.cpp src/core/libraries/gnmdriver/gnmdriver.h + src/core/libraries/gnmdriver/gnmdriver_init.h src/core/libraries/gnmdriver/gnm_error.h ) diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index 805c9124e..f93b3dbf0 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -12,6 +12,7 @@ #include "core/address_space.h" #include "core/debug_state.h" #include "core/libraries/gnmdriver/gnm_error.h" +#include "core/libraries/gnmdriver/gnmdriver_init.h" #include "core/libraries/kernel/orbis_error.h" #include "core/libraries/kernel/process.h" #include "core/libraries/libs.h" @@ -54,244 +55,11 @@ enum ShaderStages : u32 { static constexpr std::array indirect_sgpr_offsets{0u, 0u, 0x4cu, 0u, 0xccu, 0u, 0x14cu}; -static constexpr auto HwInitPacketSize = 0x100u; - -// clang-format off -static constexpr std::array InitSequence{ - // A fake preamble to mimic context reset sent by FW - 0xc0001200u, 0u, // IT_CLEAR_STATE - - // Actual init state sequence - 0xc0017600u, 0x216u, 0xffffffffu, - 0xc0017600u, 0x217u, 0xffffffffu, - 0xc0017600u, 0x215u, 0u, - 0xc0016900u, 0x2f9u, 0x2du, - 0xc0016900u, 0x282u, 8u, - 0xc0016900u, 0x280u, 0x80008u, - 0xc0016900u, 0x281u, 0xffff0000u, - 0xc0016900u, 0x204u, 0u, - 0xc0016900u, 0x206u, 0x43fu, - 0xc0016900u, 0x83u, 0xffffu, - 0xc0016900u, 0x317u, 0x10u, - 0xc0016900u, 0x2fau, 0x3f800000u, - 0xc0016900u, 0x2fcu, 0x3f800000u, - 0xc0016900u, 0x2fbu, 0x3f800000u, - 0xc0016900u, 0x2fdu, 0x3f800000u, - 0xc0016900u, 0x202u, 0xcc0010u, - 0xc0016900u, 0x30eu, 0xffffffffu, - 0xc0016900u, 0x30fu, 0xffffffffu, - 0xc0002f00u, 1u, - 0xc0017600u, 7u, 0x1ffu, - 0xc0017600u, 0x46u, 0x1ffu, - 0xc0017600u, 0x87u, 0x1ffu, - 0xc0017600u, 0xc7u, 0x1ffu, - 0xc0017600u, 0x107u, 0u, - 0xc0017600u, 0x147u, 0x1ffu, - 0xc0016900u, 0x1b1u, 2u, - 0xc0016900u, 0x101u, 0u, - 0xc0016900u, 0x100u, 0xffffffffu, - 0xc0016900u, 0x103u, 0u, - 0xc0016900u, 0x284u, 0u, - 0xc0016900u, 0x290u, 0u, - 0xc0016900u, 0x2aeu, 0u, - 0xc0016900u, 0x292u, 0u, - 0xc0016900u, 0x293u, 0x6000000u, - 0xc0016900u, 0x2f8u, 0u, - 0xc0016900u, 0x2deu, 0x1e9u, - 0xc0036900u, 0x295u, 0x100u, 0x100u, 4u, - 0xc0017900u, 0x200u, 0xe0000000u, -}; -static_assert(InitSequence.size() == 0x73 + 2); - -static constexpr std::array InitSequence175{ - // A fake preamble to mimic context reset sent by FW - 0xc0001200u, 0u, // IT_CLEAR_STATE - - // Actual init state sequence - 0xc0017600u, 0x216u, 0xffffffffu, - 0xc0017600u, 0x217u, 0xffffffffu, - 0xc0017600u, 0x215u, 0u, - 0xc0016900u, 0x2f9u, 0x2du, - 0xc0016900u, 0x282u, 8u, - 0xc0016900u, 0x280u, 0x80008u, - 0xc0016900u, 0x281u, 0xffff0000u, - 0xc0016900u, 0x204u, 0u, - 0xc0016900u, 0x206u, 0x43fu, - 0xc0016900u, 0x83u, 0xffffu, - 0xc0016900u, 0x317u, 0x10u, - 0xc0016900u, 0x2fau, 0x3f800000u, - 0xc0016900u, 0x2fcu, 0x3f800000u, - 0xc0016900u, 0x2fbu, 0x3f800000u, - 0xc0016900u, 0x2fdu, 0x3f800000u, - 0xc0016900u, 0x202u, 0xcc0010u, - 0xc0016900u, 0x30eu, 0xffffffffu, - 0xc0016900u, 0x30fu, 0xffffffffu, - 0xc0002f00u, 1u, - 0xc0017600u, 7u, 0x1ffu, - 0xc0017600u, 0x46u, 0x1ffu, - 0xc0017600u, 0x87u, 0x1ffu, - 0xc0017600u, 0xc7u, 0x1ffu, - 0xc0017600u, 0x107u, 0u, - 0xc0017600u, 0x147u, 0x1ffu, - 0xc0016900u, 0x1b1u, 2u, - 0xc0016900u, 0x101u, 0u, - 0xc0016900u, 0x100u, 0xffffffffu, - 0xc0016900u, 0x103u, 0u, - 0xc0016900u, 0x284u, 0u, - 0xc0016900u, 0x290u, 0u, - 0xc0016900u, 0x2aeu, 0u, - 0xc0016900u, 0x292u, 0u, - 0xc0016900u, 0x293u, 0x6020000u, - 0xc0016900u, 0x2f8u, 0u, - 0xc0016900u, 0x2deu, 0x1e9u, - 0xc0036900u, 0x295u, 0x100u, 0x100u, 4u, - 0xc0017900u, 0x200u, 0xe0000000u, -}; -static_assert(InitSequence175.size() == 0x73 + 2); - -static constexpr std::array InitSequence200{ - // A fake preamble to mimic context reset sent by FW - 0xc0001200u, 0u, // IT_CLEAR_STATE - - // Actual init state sequence - 0xc0017600u, 0x216u, 0xffffffffu, - 0xc0017600u, 0x217u, 0xffffffffu, - 0xc0017600u, 0x215u, 0u, - 0xc0016900u, 0x2f9u, 0x2du, - 0xc0016900u, 0x282u, 8u, - 0xc0016900u, 0x280u, 0x80008u, - 0xc0016900u, 0x281u, 0xffff0000u, - 0xc0016900u, 0x204u, 0u, - 0xc0016900u, 0x206u, 0x43fu, - 0xc0016900u, 0x83u, 0xffffu, - 0xc0016900u, 0x317u, 0x10u, - 0xc0016900u, 0x2fau, 0x3f800000u, - 0xc0016900u, 0x2fcu, 0x3f800000u, - 0xc0016900u, 0x2fbu, 0x3f800000u, - 0xc0016900u, 0x2fdu, 0x3f800000u, - 0xc0016900u, 0x202u, 0xcc0010u, - 0xc0016900u, 0x30eu, 0xffffffffu, - 0xc0016900u, 0x30fu, 0xffffffffu, - 0xc0002f00u, 1u, - 0xc0017600u, 7u, 0x1701ffu, - 0xc0017600u, 0x46u, 0x1701fdu, - 0xc0017600u, 0x87u, 0x1701ffu, - 0xc0017600u, 0xc7u, 0x1701fdu, - 0xc0017600u, 0x107u, 0x17u, - 0xc0017600u, 0x147u, 0x1701fdu, - 0xc0017600u, 0x47u, 0x1cu, - 0xc0016900u, 0x1b1u, 2u, - 0xc0016900u, 0x101u, 0u, - 0xc0016900u, 0x100u, 0xffffffffu, - 0xc0016900u, 0x103u, 0u, - 0xc0016900u, 0x284u, 0u, - 0xc0016900u, 0x290u, 0u, - 0xc0016900u, 0x2aeu, 0u, - 0xc0016900u, 0x292u, 0u, - 0xc0016900u, 0x293u, 0x6020000u, - 0xc0016900u, 0x2f8u, 0u, - 0xc0016900u, 0x2deu, 0x1e9u, - 0xc0036900u, 0x295u, 0x100u, 0x100u, 4u, - 0xc0017900u, 0x200u, 0xe0000000u, -}; -static_assert(InitSequence200.size() == 0x76 + 2); - -static constexpr std::array InitSequence350{ - // A fake preamble to mimic context reset sent by FW - 0xc0001200u, 0u, // IT_CLEAR_STATE - - // Actual init state sequence - 0xc0017600u, 0x216u, 0xffffffffu, - 0xc0017600u, 0x217u, 0xffffffffu, - 0xc0017600u, 0x215u, 0u, - 0xc0016900u, 0x2f9u, 0x2du, - 0xc0016900u, 0x282u, 8u, - 0xc0016900u, 0x280u, 0x80008u, - 0xc0016900u, 0x281u, 0xffff0000u, - 0xc0016900u, 0x204u, 0u, - 0xc0016900u, 0x206u, 0x43fu, - 0xc0016900u, 0x83u, 0xffffu, - 0xc0016900u, 0x317u, 0x10u, - 0xc0016900u, 0x2fau, 0x3f800000u, - 0xc0016900u, 0x2fcu, 0x3f800000u, - 0xc0016900u, 0x2fbu, 0x3f800000u, - 0xc0016900u, 0x2fdu, 0x3f800000u, - 0xc0016900u, 0x202u, 0xcc0010u, - 0xc0016900u, 0x30eu, 0xffffffffu, - 0xc0016900u, 0x30fu, 0xffffffffu, - 0xc0002f00u, 1u, - 0xc0017600u, 7u, 0x1701ffu, - 0xc0017600u, 0x46u, 0x1701fdu, - 0xc0017600u, 0x87u, 0x1701ffu, - 0xc0017600u, 0xc7u, 0x1701fdu, - 0xc0017600u, 0x107u, 0x17u, - 0xc0017600u, 0x147u, 0x1701fdu, - 0xc0017600u, 0x47u, 0x1cu, - 0xc0016900u, 0x1b1u, 2u, - 0xc0016900u, 0x101u, 0u, - 0xc0016900u, 0x100u, 0xffffffffu, - 0xc0016900u, 0x103u, 0u, - 0xc0016900u, 0x284u, 0u, - 0xc0016900u, 0x290u, 0u, - 0xc0016900u, 0x2aeu, 0u, - 0xc0016900u, 0x102u, 0u, - 0xc0016900u, 0x292u, 0u, - 0xc0016900u, 0x293u, 0x6020000u, - 0xc0016900u, 0x2f8u, 0u, - 0xc0016900u, 0x2deu, 0x1e9u, - 0xc0036900u, 0x295u, 0x100u, 0x100u, 4u, - 0xc0017900u, 0x200u, 0xe0000000u, - 0xc0016900u, 0x2aau, 0xffu, -}; -static_assert(InitSequence350.size() == 0x7c + 2); - -static constexpr std::array CtxInitSequence{ - 0xc0012800u, 0x80000000u, 0x80000000u, - 0xc0001200u, 0u, - 0xc0002f00u, 1u, - 0xc0016900u, 0x102u, 0u, - 0xc0016900u, 0x202u, 0xcc0010u, - 0xc0111000u, 0u -}; -static_assert(CtxInitSequence.size() == 0x0f); - -static constexpr std::array CtxInitSequence400{ - 0xc0012800u, 0x80000000u, 0x80000000u, - 0xc0001200u, 0u, - 0xc0016900u, 0x2f9u, 0x2du, - 0xc0016900u, 0x282u, 8u, - 0xc0016900u, 0x280u, 0x80008u, - 0xc0016900u, 0x281u, 0xffff0000u, - 0xc0016900u, 0x204u, 0u, - 0xc0016900u, 0x206u, 0x43fu, - 0xc0016900u, 0x83u, 0xffffu, - 0xc0016900u, 0x317u, 0x10u, - 0xc0016900u, 0x2fau, 0x3f800000u, - 0xc0016900u, 0x2fcu, 0x3f800000u, - 0xc0016900u, 0x2fbu, 0x3f800000u, - 0xc0016900u, 0x2fdu, 0x3f800000u, - 0xc0016900u, 0x202u, 0xcc0010u, - 0xc0016900u, 0x30eu, 0xffffffffu, - 0xc0016900u, 0x30fu, 0xffffffffu, - 0xc0002f00u, 1u, - 0xc0016900u, 0x1b1u, 2u, - 0xc0016900u, 0x101u, 0u, - 0xc0016900u, 0x100u, 0xffffffffu, - 0xc0016900u, 0x103u, 0u, - 0xc0016900u, 0x284u, 0u, - 0xc0016900u, 0x290u, 0u, - 0xc0016900u, 0x2aeu, 0u, - 0xc0016900u, 0x102u, 0u, - 0xc0016900u, 0x292u, 0u, - 0xc0016900u, 0x293u, 0x6020000u, - 0xc0016900u, 0x2f8u, 0u, - 0xc0016900u, 0x2deu, 0x1e9u, - 0xc0036900u, 0x295u, 0x100u, 0x100u, 4u, - 0xc0016900u, 0x2aau, 0xffu, - 0xc09e1000u, -}; -static_assert(CtxInitSequence400.size() == 0x61); -// clang-format on +// Gates use of what appear to be the neo-mode init sequences but with the older +// IA_MULTI_VGT_PARAM register address. No idea what this is for as the ioctl +// that controls it is still a mystery, but leaving the sequences in gated behind +// this flag in case we need it in the future. +static constexpr bool UseNeoCompatSequences = false; // In case if `submitDone` is issued we need to block submissions until GPU idle static u32 submission_lock{}; @@ -317,6 +85,14 @@ static void WaitGpuIdle() { cv_lock.wait(lock, [] { return submission_lock == 0; }); } +// Write a special ending NOP packet with N DWs data block +static inline u32* WriteTrailingNop(u32* cmdbuf, u32 data_block_size) { + auto* nop = reinterpret_cast(cmdbuf); + nop->header = PM4Type3Header{PM4ItOpcode::Nop, data_block_size - 1}; + nop->data_block[0] = 0u; // only one out of `data_block_size` is initialized + return cmdbuf + data_block_size + 1 /* header */; +} + // Write a special ending NOP packet with N DWs data block template static inline u32* WriteTrailingNop(u32* cmdbuf) { @@ -619,17 +395,30 @@ u32 PS4_SYSV_ABI sceGnmDispatchInitDefaultHardwareState(u32* cmdbuf, u32 size) { return 0; } - cmdbuf = PM4CmdSetData::SetShReg(cmdbuf, 0x216u, - 0xffffffffu); // COMPUTE_STATIC_THREAD_MGMT_SE0 - cmdbuf = PM4CmdSetData::SetShReg(cmdbuf, 0x217u, - 0xffffffffu); // COMPUTE_STATIC_THREAD_MGMT_SE1 - cmdbuf = PM4CmdSetData::SetShReg(cmdbuf, 0x215u, 0x170u); // COMPUTE_RESOURCE_LIMITS + cmdbuf = PM4CmdSetData::SetShReg( + cmdbuf, 0x216u, + 0xffffffffu); // COMPUTE_STATIC_THREAD_MGMT_SE0 + cmdbuf = PM4CmdSetData::SetShReg( + cmdbuf, 0x217u, + 0xffffffffu); // COMPUTE_STATIC_THREAD_MGMT_SE1 + + if (sceKernelIsNeoMode()) { + cmdbuf = PM4CmdSetData::SetShReg( + cmdbuf, 0x219u, + 0xffffffffu); // COMPUTE_STATIC_THREAD_MGMT_SE2 + cmdbuf = PM4CmdSetData::SetShReg( + cmdbuf, 0x21au, + 0xffffffffu); // COMPUTE_STATIC_THREAD_MGMT_SE3 + } + + cmdbuf = PM4CmdSetData::SetShReg( + cmdbuf, 0x215u, 0x170u); // COMPUTE_RESOURCE_LIMITS cmdbuf = WriteHeader(cmdbuf, 6); - cmdbuf = WriteBody(cmdbuf, 0x28000000u, 0u, 0u, 0u, 0u, 0u); + cmdbuf = WriteBody(cmdbuf, 0x28000000u, 0u, 0u, 0u, 0u, 0xau); - cmdbuf = WriteHeader(cmdbuf, 0xef); - cmdbuf = WriteBody(cmdbuf, 0xau, 0u); + cmdbuf = WriteHeader(cmdbuf, sceKernelIsNeoMode() ? 0xe9 : 0xef); + cmdbuf = WriteBody(cmdbuf, 0u); return HwInitPacketSize; } @@ -646,7 +435,7 @@ s32 PS4_SYSV_ABI sceGnmDrawIndex(u32* cmdbuf, u32 size, u32 index_count, uintptr draw_index->index_base_lo = u32(index_addr); draw_index->index_base_hi = u32(index_addr >> 32); draw_index->index_count = index_count; - draw_index->draw_initiator = 0; + draw_index->draw_initiator = sceKernelIsNeoMode() ? flags & 0xe0000000u : 0; WriteTrailingNop<3>(cmdbuf + 6); return ORBIS_OK; @@ -659,8 +448,9 @@ s32 PS4_SYSV_ABI sceGnmDrawIndexAuto(u32* cmdbuf, u32 size, u32 index_count, u32 if (cmdbuf && (size == 7) && (flags & 0x1ffffffe) == 0) { // no predication will be set in the packet - cmdbuf = WritePacket(cmdbuf, PM4ShaderType::ShaderGraphics, - index_count, 2u); + cmdbuf = WritePacket( + cmdbuf, PM4ShaderType::ShaderGraphics, index_count, + sceKernelIsNeoMode() ? flags & 0xe0000000u | 2u : 2u); WriteTrailingNop<3>(cmdbuf); return ORBIS_OK; } @@ -684,7 +474,7 @@ s32 PS4_SYSV_ABI sceGnmDrawIndexIndirect(u32* cmdbuf, u32 size, u32 data_offset, cmdbuf[0] = data_offset; cmdbuf[1] = vertex_sgpr_offset == 0 ? 0 : (vertex_sgpr_offset & 0xffffu) + sgpr_offset; cmdbuf[2] = instance_sgpr_offset == 0 ? 0 : (instance_sgpr_offset & 0xffffu) + sgpr_offset; - cmdbuf[3] = 0; + cmdbuf[3] = sceKernelIsNeoMode() ? flags & 0xe0000000u : 0u; cmdbuf += 4; WriteTrailingNop<3>(cmdbuf); @@ -699,8 +489,9 @@ s32 PS4_SYSV_ABI sceGnmDrawIndexIndirectCountMulti(u32* cmdbuf, u32 size, u32 da u32 flags) { LOG_TRACE(Lib_GnmDriver, "called"); - if (cmdbuf && (size == 16) && (shader_stage < ShaderStages::Max) && - (vertex_sgpr_offset < 0x10u) && (instance_sgpr_offset < 0x10u)) { + if ((!sceKernelIsNeoMode() || !UseNeoCompatSequences) && !cmdbuf && (size == 16) && + (shader_stage < ShaderStages::Max) && (vertex_sgpr_offset < 0x10u) && + (instance_sgpr_offset < 0x10u)) { cmdbuf = WriteHeader(cmdbuf, 2); cmdbuf = WriteBody(cmdbuf, 0u); @@ -719,7 +510,7 @@ s32 PS4_SYSV_ABI sceGnmDrawIndexIndirectCountMulti(u32* cmdbuf, u32 size, u32 da cmdbuf[4] = max_count; *(u64*)(&cmdbuf[5]) = count_addr; cmdbuf[7] = sizeof(DrawIndexedIndirectArgs); - cmdbuf[8] = 0; + cmdbuf[8] = sceKernelIsNeoMode() ? flags & 0xe0000000u : 0; cmdbuf += 9; WriteTrailingNop<2>(cmdbuf); @@ -748,7 +539,8 @@ s32 PS4_SYSV_ABI sceGnmDrawIndexOffset(u32* cmdbuf, u32 size, u32 index_offset, const auto predicate = flags & 1 ? PM4Predicate::PredEnable : PM4Predicate::PredDisable; cmdbuf = WriteHeader( cmdbuf, 4, PM4ShaderType::ShaderGraphics, predicate); - cmdbuf = WriteBody(cmdbuf, index_count, index_offset, index_count, 0u); + cmdbuf = WriteBody(cmdbuf, index_count, index_offset, index_count, + sceKernelIsNeoMode() ? flags & 0xe0000000u : 0u); WriteTrailingNop<3>(cmdbuf); return ORBIS_OK; @@ -772,7 +564,7 @@ s32 PS4_SYSV_ABI sceGnmDrawIndirect(u32* cmdbuf, u32 size, u32 data_offset, u32 cmdbuf[0] = data_offset; cmdbuf[1] = vertex_sgpr_offset == 0 ? 0 : (vertex_sgpr_offset & 0xffffu) + sgpr_offset; cmdbuf[2] = instance_sgpr_offset == 0 ? 0 : (instance_sgpr_offset & 0xffffu) + sgpr_offset; - cmdbuf[3] = 2; // auto index + cmdbuf[3] = sceKernelIsNeoMode() ? flags & 0xe0000000u | 2u : 2u; // auto index cmdbuf += 4; WriteTrailingNop<3>(cmdbuf); @@ -801,6 +593,7 @@ u32 PS4_SYSV_ABI sceGnmDrawInitDefaultHardwareState(u32* cmdbuf, u32 size) { } const auto& SetupContext = [](u32* cmdbuf, u32 size, bool clear_state) { + const auto* cmdbuf_end = cmdbuf + HwInitPacketSize; if (clear_state) { cmdbuf = ClearContextState(cmdbuf); } @@ -808,10 +601,8 @@ u32 PS4_SYSV_ABI sceGnmDrawInitDefaultHardwareState(u32* cmdbuf, u32 size) { std::memcpy(cmdbuf, &InitSequence[2], (InitSequence.size() - 2) * 4); cmdbuf += InitSequence.size() - 2; - const auto cmdbuf_left = - HwInitPacketSize - (InitSequence.size() - 2) - (clear_state ? 0xc : 0) - 1; - cmdbuf = WriteHeader(cmdbuf, cmdbuf_left); - cmdbuf = WriteBody(cmdbuf, 0u); + const auto cmdbuf_left = cmdbuf_end - cmdbuf - 1; + WriteTrailingNop(cmdbuf, cmdbuf_left); return HwInitPacketSize; }; @@ -826,12 +617,13 @@ u32 PS4_SYSV_ABI sceGnmDrawInitDefaultHardwareState175(u32* cmdbuf, u32 size) { return 0; } + const auto* cmdbuf_end = cmdbuf + HwInitPacketSize; cmdbuf = ClearContextState(cmdbuf); std::memcpy(cmdbuf, &InitSequence175[2], (InitSequence175.size() - 2) * 4); cmdbuf += InitSequence175.size() - 2; - constexpr auto cmdbuf_left = HwInitPacketSize - (InitSequence175.size() - 2) - 0xc - 1; - WriteTrailingNop(cmdbuf); + const auto cmdbuf_left = cmdbuf_end - cmdbuf - 1; + WriteTrailingNop(cmdbuf, cmdbuf_left); return HwInitPacketSize; } @@ -844,17 +636,27 @@ u32 PS4_SYSV_ABI sceGnmDrawInitDefaultHardwareState200(u32* cmdbuf, u32 size) { } const auto& SetupContext200 = [](u32* cmdbuf, u32 size, bool clear_state) { + const auto* cmdbuf_end = cmdbuf + HwInitPacketSize; if (clear_state) { cmdbuf = ClearContextState(cmdbuf); } - std::memcpy(cmdbuf, &InitSequence200[2], (InitSequence200.size() - 2) * 4); - cmdbuf += InitSequence200.size() - 2; + if (sceKernelIsNeoMode()) { + if (!UseNeoCompatSequences) { + std::memcpy(cmdbuf, &InitSequence200Neo[2], (InitSequence200Neo.size() - 2) * 4); + cmdbuf += InitSequence200Neo.size() - 2; + } else { + std::memcpy(cmdbuf, &InitSequence200NeoCompat[2], + (InitSequence200NeoCompat.size() - 2) * 4); + cmdbuf += InitSequence200NeoCompat.size() - 2; + } + } else { + std::memcpy(cmdbuf, &InitSequence200[2], (InitSequence200.size() - 2) * 4); + cmdbuf += InitSequence200.size() - 2; + } - const auto cmdbuf_left = - HwInitPacketSize - (InitSequence200.size() - 2) - (clear_state ? 0xc : 0) - 1; - cmdbuf = WriteHeader(cmdbuf, cmdbuf_left); - cmdbuf = WriteBody(cmdbuf, 0u); + const auto cmdbuf_left = cmdbuf_end - cmdbuf - 1; + WriteTrailingNop(cmdbuf, cmdbuf_left); return HwInitPacketSize; }; @@ -870,17 +672,27 @@ u32 PS4_SYSV_ABI sceGnmDrawInitDefaultHardwareState350(u32* cmdbuf, u32 size) { } const auto& SetupContext350 = [](u32* cmdbuf, u32 size, bool clear_state) { + const auto* cmdbuf_end = cmdbuf + HwInitPacketSize; if (clear_state) { cmdbuf = ClearContextState(cmdbuf); } - std::memcpy(cmdbuf, &InitSequence350[2], (InitSequence350.size() - 2) * 4); - cmdbuf += InitSequence350.size() - 2; + if (sceKernelIsNeoMode()) { + if (!UseNeoCompatSequences) { + std::memcpy(cmdbuf, &InitSequence350Neo[2], (InitSequence350Neo.size() - 2) * 4); + cmdbuf += InitSequence350Neo.size() - 2; + } else { + std::memcpy(cmdbuf, &InitSequence350NeoCompat[2], + (InitSequence350NeoCompat.size() - 2) * 4); + cmdbuf += InitSequence350NeoCompat.size() - 2; + } + } else { + std::memcpy(cmdbuf, &InitSequence350[2], (InitSequence350.size() - 2) * 4); + cmdbuf += InitSequence350.size() - 2; + } - const auto cmdbuf_left = - HwInitPacketSize - (InitSequence350.size() - 2) - (clear_state ? 0xc : 0) - 1; - cmdbuf = WriteHeader(cmdbuf, cmdbuf_left); - cmdbuf = WriteBody(cmdbuf, 0u); + const auto cmdbuf_left = cmdbuf_end - cmdbuf - 1; + WriteTrailingNop(cmdbuf, cmdbuf_left); return HwInitPacketSize; }; @@ -896,7 +708,11 @@ u32 PS4_SYSV_ABI sceGnmDrawInitToDefaultContextState(u32* cmdbuf, u32 size) { return 0; } - std::memcpy(cmdbuf, CtxInitSequence.data(), CtxInitSequence.size() * 4); + if (sceKernelIsNeoMode()) { + std::memcpy(cmdbuf, CtxInitSequenceNeo.data(), CtxInitSequenceNeo.size() * 4); + } else { + std::memcpy(cmdbuf, CtxInitSequence.data(), CtxInitSequence.size() * 4); + } return CtxInitPacketSize; } @@ -908,7 +724,16 @@ u32 PS4_SYSV_ABI sceGnmDrawInitToDefaultContextState400(u32* cmdbuf, u32 size) { return 0; } - std::memcpy(cmdbuf, CtxInitSequence400.data(), CtxInitSequence400.size() * 4); + if (sceKernelIsNeoMode()) { + if (!UseNeoCompatSequences) { + std::memcpy(cmdbuf, CtxInitSequence400Neo.data(), CtxInitSequence400Neo.size() * 4); + } else { + std::memcpy(cmdbuf, CtxInitSequence400NeoCompat.data(), + CtxInitSequence400NeoCompat.size() * 4); + } + } else { + std::memcpy(cmdbuf, CtxInitSequence400.data(), CtxInitSequence400.size() * 4); + } return CtxInitPacketSize; } @@ -1030,7 +855,8 @@ int PS4_SYSV_ABI sceGnmGetGpuBlockStatus() { u32 PS4_SYSV_ABI sceGnmGetGpuCoreClockFrequency() { LOG_TRACE(Lib_GnmDriver, "called"); - return Config::isNeoMode() ? 911'000'000 : 800'000'000; + // On console this uses an ioctl check, but we assume it is equal to just checking for neo mode. + return sceKernelIsNeoMode() ? 911'000'000 : 800'000'000; } int PS4_SYSV_ABI sceGnmGetGpuInfoStatus() { @@ -1369,7 +1195,15 @@ s32 PS4_SYSV_ABI sceGnmResetVgtControl(u32* cmdbuf, u32 size) { if (cmdbuf == nullptr || size != 3) { return -1; } - PM4CmdSetData::SetContextReg(cmdbuf, 0x2aau, 0xffu); // IA_MULTI_VGT_PARAM + if (sceKernelIsNeoMode()) { + if (!UseNeoCompatSequences) { + PM4CmdSetData::SetUconfigReg(cmdbuf, 0x40000258u, 0x6d007fu); // IA_MULTI_VGT_PARAM + } else { + PM4CmdSetData::SetContextReg(cmdbuf, 0x100002aau, 0xd00ffu); // IA_MULTI_VGT_PARAM + } + } else { + PM4CmdSetData::SetContextReg(cmdbuf, 0x2aau, 0xffu); // IA_MULTI_VGT_PARAM + } return ORBIS_OK; } @@ -1830,9 +1664,25 @@ s32 PS4_SYSV_ABI sceGnmSetVgtControl(u32* cmdbuf, u32 size, u32 prim_group_sz_mi return -1; } - const u32 reg_value = - ((partial_vs_wave_mode & 1) << 0x10) | (prim_group_sz_minus_one & 0xffffu); - PM4CmdSetData::SetContextReg(cmdbuf, 0x2aau, reg_value); // IA_MULTI_VGT_PARAM + if (sceKernelIsNeoMode()) { + const u32 wd_switch_on_eop = u32(wd_switch_only_on_eop_mode != 0) << 0x14; + const u32 switch_on_eoi = u32(wd_switch_only_on_eop_mode == 0) << 0x13; + const u32 reg_value = + wd_switch_only_on_eop_mode != 0 + ? (partial_vs_wave_mode & 1) << 0x10 | prim_group_sz_minus_one | wd_switch_on_eop | + switch_on_eoi | 0x40000u + : prim_group_sz_minus_one & 0x1cffffu | wd_switch_on_eop | switch_on_eoi | 0x50000u; + if (!UseNeoCompatSequences) { + PM4CmdSetData::SetUconfigReg(cmdbuf, 0x40000258u, + reg_value | 0x600000u); // IA_MULTI_VGT_PARAM + } else { + PM4CmdSetData::SetContextReg(cmdbuf, 0x100002aau, reg_value); // IA_MULTI_VGT_PARAM + } + } else { + const u32 reg_value = + ((partial_vs_wave_mode & 1) << 0x10) | (prim_group_sz_minus_one & 0xffffu); + PM4CmdSetData::SetContextReg(cmdbuf, 0x2aau, reg_value); // IA_MULTI_VGT_PARAM + } return ORBIS_OK; } @@ -2215,9 +2065,25 @@ int PS4_SYSV_ABI sceGnmSubmitCommandBuffersForWorkload(u32 workload, u32 count, if (sdk_version <= 0x1ffffffu) { liverpool->SubmitGfx(InitSequence, {}); } else if (sdk_version <= 0x3ffffffu) { - liverpool->SubmitGfx(InitSequence200, {}); + if (sceKernelIsNeoMode()) { + if (!UseNeoCompatSequences) { + liverpool->SubmitGfx(InitSequence200Neo, {}); + } else { + liverpool->SubmitGfx(InitSequence200NeoCompat, {}); + } + } else { + liverpool->SubmitGfx(InitSequence200, {}); + } } else { - liverpool->SubmitGfx(InitSequence350, {}); + if (sceKernelIsNeoMode()) { + if (!UseNeoCompatSequences) { + liverpool->SubmitGfx(InitSequence350Neo, {}); + } else { + liverpool->SubmitGfx(InitSequence350NeoCompat, {}); + } + } else { + liverpool->SubmitGfx(InitSequence350, {}); + } } send_init_packet = false; } diff --git a/src/core/libraries/gnmdriver/gnmdriver_init.h b/src/core/libraries/gnmdriver/gnmdriver_init.h new file mode 100644 index 000000000..da6d65f32 --- /dev/null +++ b/src/core/libraries/gnmdriver/gnmdriver_init.h @@ -0,0 +1,542 @@ +// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +namespace Libraries::GnmDriver { + +constexpr auto HwInitPacketSize = 0x100u; + +// clang-format off +constexpr std::array InitSequence{ + // A fake preamble to mimic context reset sent by FW + 0xc0001200u, 0u, // IT_CLEAR_STATE + + // Actual init state sequence + 0xc0017600u, 0x216u, 0xffffffffu, + 0xc0017600u, 0x217u, 0xffffffffu, + 0xc0017600u, 0x215u, 0u, + 0xc0016900u, 0x2f9u, 0x2du, + 0xc0016900u, 0x282u, 8u, + 0xc0016900u, 0x280u, 0x80008u, + 0xc0016900u, 0x281u, 0xffff0000u, + 0xc0016900u, 0x204u, 0u, + 0xc0016900u, 0x206u, 0x43fu, + 0xc0016900u, 0x83u, 0xffffu, + 0xc0016900u, 0x317u, 0x10u, + 0xc0016900u, 0x2fau, 0x3f800000u, + 0xc0016900u, 0x2fcu, 0x3f800000u, + 0xc0016900u, 0x2fbu, 0x3f800000u, + 0xc0016900u, 0x2fdu, 0x3f800000u, + 0xc0016900u, 0x202u, 0xcc0010u, + 0xc0016900u, 0x30eu, 0xffffffffu, + 0xc0016900u, 0x30fu, 0xffffffffu, + 0xc0002f00u, 1u, + 0xc0017600u, 7u, 0x1ffu, + 0xc0017600u, 0x46u, 0x1ffu, + 0xc0017600u, 0x87u, 0x1ffu, + 0xc0017600u, 0xc7u, 0x1ffu, + 0xc0017600u, 0x107u, 0u, + 0xc0017600u, 0x147u, 0x1ffu, + 0xc0016900u, 0x1b1u, 2u, + 0xc0016900u, 0x101u, 0u, + 0xc0016900u, 0x100u, 0xffffffffu, + 0xc0016900u, 0x103u, 0u, + 0xc0016900u, 0x284u, 0u, + 0xc0016900u, 0x290u, 0u, + 0xc0016900u, 0x2aeu, 0u, + 0xc0016900u, 0x292u, 0u, + 0xc0016900u, 0x293u, 0x6000000u, + 0xc0016900u, 0x2f8u, 0u, + 0xc0016900u, 0x2deu, 0x1e9u, + 0xc0036900u, 0x295u, 0x100u, 0x100u, 4u, + 0xc0017900u, 0x200u, 0xe0000000u, +}; +static_assert(InitSequence.size() == 0x73 + 2); + +constexpr std::array InitSequence175{ + // A fake preamble to mimic context reset sent by FW + 0xc0001200u, 0u, // IT_CLEAR_STATE + + // Actual init state sequence + 0xc0017600u, 0x216u, 0xffffffffu, + 0xc0017600u, 0x217u, 0xffffffffu, + 0xc0017600u, 0x215u, 0u, + 0xc0016900u, 0x2f9u, 0x2du, + 0xc0016900u, 0x282u, 8u, + 0xc0016900u, 0x280u, 0x80008u, + 0xc0016900u, 0x281u, 0xffff0000u, + 0xc0016900u, 0x204u, 0u, + 0xc0016900u, 0x206u, 0x43fu, + 0xc0016900u, 0x83u, 0xffffu, + 0xc0016900u, 0x317u, 0x10u, + 0xc0016900u, 0x2fau, 0x3f800000u, + 0xc0016900u, 0x2fcu, 0x3f800000u, + 0xc0016900u, 0x2fbu, 0x3f800000u, + 0xc0016900u, 0x2fdu, 0x3f800000u, + 0xc0016900u, 0x202u, 0xcc0010u, + 0xc0016900u, 0x30eu, 0xffffffffu, + 0xc0016900u, 0x30fu, 0xffffffffu, + 0xc0002f00u, 1u, + 0xc0017600u, 7u, 0x1ffu, + 0xc0017600u, 0x46u, 0x1ffu, + 0xc0017600u, 0x87u, 0x1ffu, + 0xc0017600u, 0xc7u, 0x1ffu, + 0xc0017600u, 0x107u, 0u, + 0xc0017600u, 0x147u, 0x1ffu, + 0xc0016900u, 0x1b1u, 2u, + 0xc0016900u, 0x101u, 0u, + 0xc0016900u, 0x100u, 0xffffffffu, + 0xc0016900u, 0x103u, 0u, + 0xc0016900u, 0x284u, 0u, + 0xc0016900u, 0x290u, 0u, + 0xc0016900u, 0x2aeu, 0u, + 0xc0016900u, 0x292u, 0u, + 0xc0016900u, 0x293u, 0x6020000u, + 0xc0016900u, 0x2f8u, 0u, + 0xc0016900u, 0x2deu, 0x1e9u, + 0xc0036900u, 0x295u, 0x100u, 0x100u, 4u, + 0xc0017900u, 0x200u, 0xe0000000u, +}; +static_assert(InitSequence175.size() == 0x73 + 2); + +constexpr std::array InitSequence200{ + // A fake preamble to mimic context reset sent by FW + 0xc0001200u, 0u, // IT_CLEAR_STATE + + // Actual init state sequence + 0xc0017600u, 0x216u, 0xffffffffu, + 0xc0017600u, 0x217u, 0xffffffffu, + 0xc0017600u, 0x215u, 0u, + 0xc0016900u, 0x2f9u, 0x2du, + 0xc0016900u, 0x282u, 8u, + 0xc0016900u, 0x280u, 0x80008u, + 0xc0016900u, 0x281u, 0xffff0000u, + 0xc0016900u, 0x204u, 0u, + 0xc0016900u, 0x206u, 0x43fu, + 0xc0016900u, 0x83u, 0xffffu, + 0xc0016900u, 0x317u, 0x10u, + 0xc0016900u, 0x2fau, 0x3f800000u, + 0xc0016900u, 0x2fcu, 0x3f800000u, + 0xc0016900u, 0x2fbu, 0x3f800000u, + 0xc0016900u, 0x2fdu, 0x3f800000u, + 0xc0016900u, 0x202u, 0xcc0010u, + 0xc0016900u, 0x30eu, 0xffffffffu, + 0xc0016900u, 0x30fu, 0xffffffffu, + 0xc0002f00u, 1u, + 0xc0017600u, 7u, 0x1701ffu, + 0xc0017600u, 0x46u, 0x1701fdu, + 0xc0017600u, 0x87u, 0x1701ffu, + 0xc0017600u, 0xc7u, 0x1701fdu, + 0xc0017600u, 0x107u, 0x17u, + 0xc0017600u, 0x147u, 0x1701fdu, + 0xc0017600u, 0x47u, 0x1cu, + 0xc0016900u, 0x1b1u, 2u, + 0xc0016900u, 0x101u, 0u, + 0xc0016900u, 0x100u, 0xffffffffu, + 0xc0016900u, 0x103u, 0u, + 0xc0016900u, 0x284u, 0u, + 0xc0016900u, 0x290u, 0u, + 0xc0016900u, 0x2aeu, 0u, + 0xc0016900u, 0x292u, 0u, + 0xc0016900u, 0x293u, 0x6020000u, + 0xc0016900u, 0x2f8u, 0u, + 0xc0016900u, 0x2deu, 0x1e9u, + 0xc0036900u, 0x295u, 0x100u, 0x100u, 4u, + 0xc0017900u, 0x200u, 0xe0000000u, +}; +static_assert(InitSequence200.size() == 0x76 + 2); + +constexpr std::array InitSequence200Neo{ + // A fake preamble to mimic context reset sent by FW + 0xc0001200u, 0u, // IT_CLEAR_STATE + + // Actual init state sequence + 0xc0017600u, 0x216u, 0xffffffffu, + 0xc0017600u, 0x217u, 0xffffffffu, + 0xc0017600u, 0x219u, 0xffffffffu, + 0xc0017600u, 0x21au, 0xffffffffu, + 0xc0017600u, 0x215u, 0u, + 0xc0016900u, 0x2f9u, 0x2du, + 0xc0016900u, 0x282u, 8u, + 0xc0016900u, 0x280u, 0x80008u, + 0xc0016900u, 0x281u, 0xffff0000u, + 0xc0016900u, 0x204u, 0u, + 0xc0016900u, 0x206u, 0x43fu, + 0xc0016900u, 0x83u, 0xffffu, + 0xc0016900u, 0x317u, 0x10u, + 0xc0016900u, 0x2fau, 0x3f800000u, + 0xc0016900u, 0x2fcu, 0x3f800000u, + 0xc0016900u, 0x2fbu, 0x3f800000u, + 0xc0016900u, 0x2fdu, 0x3f800000u, + 0xc0016900u, 0x202u, 0xcc0010u, + 0xc0016900u, 0x30eu, 0xffffffffu, + 0xc0016900u, 0x30fu, 0xffffffffu, + 0xc0002f00u, 1u, + 0xc0017600u, 7u, 0x1701ffu, + 0xc0017600u, 0x46u, 0x1701fdu, + 0xc0017600u, 0x87u, 0x1701ffu, + 0xc0017600u, 0xc7u, 0x1701fdu, + 0xc0017600u, 0x107u, 0x17u, + 0xc0017600u, 0x147u, 0x1701fdu, + 0xc0017600u, 0x47u, 0x1cu, + 0xc0016900u, 0x1b1u, 2u, + 0xc0016900u, 0x101u, 0u, + 0xc0016900u, 0x100u, 0xffffffffu, + 0xc0016900u, 0x103u, 0u, + 0xc0016900u, 0x284u, 0u, + 0xc0016900u, 0x290u, 0u, + 0xc0016900u, 0x2aeu, 0u, + 0xc0016900u, 0x292u, 0u, + 0xc0016900u, 0x293u, 0x6020000u, + 0xc0016900u, 0x2f8u, 0u, + 0xc0016900u, 0x2deu, 0x1e9u, + 0xc0026900u, 0xebu, 0xff00ff00u, 0xff00u, + 0xc0036900u, 0x295u, 0x100u, 0x100u, 4u, + 0xc0017900u, 0x200u, 0xe0000000u, + 0xc0017900u, 0x40000258u, 0x6d007fu, +}; +static_assert(InitSequence200Neo.size() == 0x83 + 2); + +constexpr std::array InitSequence200NeoCompat{ + // A fake preamble to mimic context reset sent by FW + 0xc0001200u, 0u, // IT_CLEAR_STATE + + // Actual init state sequence + 0xc0017600u, 0x216u, 0xffffffffu, + 0xc0017600u, 0x217u, 0xffffffffu, + 0xc0017600u, 0x219u, 0xffffffffu, + 0xc0017600u, 0x21au, 0xffffffffu, + 0xc0017600u, 0x215u, 0u, + 0xc0016900u, 0x2f9u, 0x2du, + 0xc0016900u, 0x282u, 8u, + 0xc0016900u, 0x280u, 0x80008u, + 0xc0016900u, 0x281u, 0xffff0000u, + 0xc0016900u, 0x204u, 0u, + 0xc0016900u, 0x206u, 0x43fu, + 0xc0016900u, 0x83u, 0xffffu, + 0xc0016900u, 0x317u, 0x10u, + 0xc0016900u, 0x2fau, 0x3f800000u, + 0xc0016900u, 0x2fcu, 0x3f800000u, + 0xc0016900u, 0x2fbu, 0x3f800000u, + 0xc0016900u, 0x2fdu, 0x3f800000u, + 0xc0016900u, 0x202u, 0xcc0010u, + 0xc0016900u, 0x30eu, 0xffffffffu, + 0xc0016900u, 0x30fu, 0xffffffffu, + 0xc0002f00u, 1u, + 0xc0017600u, 7u, 0x1701ffu, + 0xc0017600u, 0x46u, 0x1701fdu, + 0xc0017600u, 0x87u, 0x1701ffu, + 0xc0017600u, 0xc7u, 0x1701fdu, + 0xc0017600u, 0x107u, 0x17u, + 0xc0017600u, 0x147u, 0x1701fdu, + 0xc0017600u, 0x47u, 0x1cu, + 0xc0016900u, 0x1b1u, 2u, + 0xc0016900u, 0x101u, 0u, + 0xc0016900u, 0x100u, 0xffffffffu, + 0xc0016900u, 0x103u, 0u, + 0xc0016900u, 0x284u, 0u, + 0xc0016900u, 0x290u, 0u, + 0xc0016900u, 0x2aeu, 0u, + 0xc0016900u, 0x292u, 0u, + 0xc0016900u, 0x293u, 0x6020000u, + 0xc0016900u, 0x2f8u, 0u, + 0xc0016900u, 0x2deu, 0x1e9u, + 0xc0026900u, 0xebu, 0xff00ff00u, 0xff00u, + 0xc0036900u, 0x295u, 0x100u, 0x100u, 4u, + 0xc0017900u, 0x200u, 0xe0000000u, + 0xc0016900u, 0x100002aau, 0xd00ffu, +}; +static_assert(InitSequence200NeoCompat.size() == 0x83 + 2); + +constexpr std::array InitSequence350{ + // A fake preamble to mimic context reset sent by FW + 0xc0001200u, 0u, // IT_CLEAR_STATE + + // Actual init state sequence + 0xc0017600u, 0x216u, 0xffffffffu, + 0xc0017600u, 0x217u, 0xffffffffu, + 0xc0017600u, 0x215u, 0u, + 0xc0016900u, 0x2f9u, 0x2du, + 0xc0016900u, 0x282u, 8u, + 0xc0016900u, 0x280u, 0x80008u, + 0xc0016900u, 0x281u, 0xffff0000u, + 0xc0016900u, 0x204u, 0u, + 0xc0016900u, 0x206u, 0x43fu, + 0xc0016900u, 0x83u, 0xffffu, + 0xc0016900u, 0x317u, 0x10u, + 0xc0016900u, 0x2fau, 0x3f800000u, + 0xc0016900u, 0x2fcu, 0x3f800000u, + 0xc0016900u, 0x2fbu, 0x3f800000u, + 0xc0016900u, 0x2fdu, 0x3f800000u, + 0xc0016900u, 0x202u, 0xcc0010u, + 0xc0016900u, 0x30eu, 0xffffffffu, + 0xc0016900u, 0x30fu, 0xffffffffu, + 0xc0002f00u, 1u, + 0xc0017600u, 7u, 0x1701ffu, + 0xc0017600u, 0x46u, 0x1701fdu, + 0xc0017600u, 0x87u, 0x1701ffu, + 0xc0017600u, 0xc7u, 0x1701fdu, + 0xc0017600u, 0x107u, 0x17u, + 0xc0017600u, 0x147u, 0x1701fdu, + 0xc0017600u, 0x47u, 0x1cu, + 0xc0016900u, 0x1b1u, 2u, + 0xc0016900u, 0x101u, 0u, + 0xc0016900u, 0x100u, 0xffffffffu, + 0xc0016900u, 0x103u, 0u, + 0xc0016900u, 0x284u, 0u, + 0xc0016900u, 0x290u, 0u, + 0xc0016900u, 0x2aeu, 0u, + 0xc0016900u, 0x102u, 0u, + 0xc0016900u, 0x292u, 0u, + 0xc0016900u, 0x293u, 0x6020000u, + 0xc0016900u, 0x2f8u, 0u, + 0xc0016900u, 0x2deu, 0x1e9u, + 0xc0036900u, 0x295u, 0x100u, 0x100u, 4u, + 0xc0017900u, 0x200u, 0xe0000000u, + 0xc0016900u, 0x2aau, 0xffu, +}; +static_assert(InitSequence350.size() == 0x7c + 2); + +constexpr std::array InitSequence350Neo{ + // A fake preamble to mimic context reset sent by FW + 0xc0001200u, 0u, // IT_CLEAR_STATE + + // Actual init state sequence + 0xc0017600u, 0x216u, 0xffffffffu, + 0xc0017600u, 0x217u, 0xffffffffu, + 0xc0017600u, 0x219u, 0xffffffffu, + 0xc0017600u, 0x21au, 0xffffffffu, + 0xc0017600u, 0x215u, 0u, + 0xc0016900u, 0x2f9u, 0x2du, + 0xc0016900u, 0x282u, 8u, + 0xc0016900u, 0x280u, 0x80008u, + 0xc0016900u, 0x281u, 0xffff0000u, + 0xc0016900u, 0x204u, 0u, + 0xc0016900u, 0x206u, 0x43fu, + 0xc0016900u, 0x83u, 0xffffu, + 0xc0016900u, 0x317u, 0x10u, + 0xc0016900u, 0x2fau, 0x3f800000u, + 0xc0016900u, 0x2fcu, 0x3f800000u, + 0xc0016900u, 0x2fbu, 0x3f800000u, + 0xc0016900u, 0x2fdu, 0x3f800000u, + 0xc0016900u, 0x202u, 0xcc0010u, + 0xc0016900u, 0x30eu, 0xffffffffu, + 0xc0016900u, 0x30fu, 0xffffffffu, + 0xc0002f00u, 1u, + 0xc0017600u, 7u, 0x1701ffu, + 0xc0017600u, 0x46u, 0x1701fdu, + 0xc0017600u, 0x87u, 0x1701ffu, + 0xc0017600u, 0xc7u, 0x1701fdu, + 0xc0017600u, 0x107u, 0x17u, + 0xc0017600u, 0x147u, 0x1701fdu, + 0xc0017600u, 0x47u, 0x1cu, + 0xc0016900u, 0x1b1u, 2u, + 0xc0016900u, 0x101u, 0u, + 0xc0016900u, 0x100u, 0xffffffffu, + 0xc0016900u, 0x103u, 0u, + 0xc0016900u, 0x284u, 0u, + 0xc0016900u, 0x290u, 0u, + 0xc0016900u, 0x2aeu, 0u, + 0xc0016900u, 0x102u, 0u, + 0xc0016900u, 0x292u, 0u, + 0xc0016900u, 0x293u, 0x6020000u, + 0xc0016900u, 0x2f8u, 0u, + 0xc0016900u, 0x2deu, 0x1e9u, + 0xc0026900u, 0xebu, 0xff00ff00u, 0xff00u, + 0xc0036900u, 0x295u, 0x100u, 0x100u, 4u, + 0xc0017900u, 0x200u, 0xe0000000u, + 0xc0017900u, 0x40000258u, 0x6d007fu, +}; +static_assert(InitSequence350Neo.size() == 0x86 + 2); + +constexpr std::array InitSequence350NeoCompat{ + // A fake preamble to mimic context reset sent by FW + 0xc0001200u, 0u, // IT_CLEAR_STATE + + // Actual init state sequence + 0xc0017600u, 0x216u, 0xffffffffu, + 0xc0017600u, 0x217u, 0xffffffffu, + 0xc0017600u, 0x219u, 0xffffffffu, + 0xc0017600u, 0x21au, 0xffffffffu, + 0xc0017600u, 0x215u, 0u, + 0xc0016900u, 0x2f9u, 0x2du, + 0xc0016900u, 0x282u, 8u, + 0xc0016900u, 0x280u, 0x80008u, + 0xc0016900u, 0x281u, 0xffff0000u, + 0xc0016900u, 0x204u, 0u, + 0xc0016900u, 0x206u, 0x43fu, + 0xc0016900u, 0x83u, 0xffffu, + 0xc0016900u, 0x317u, 0x10u, + 0xc0016900u, 0x2fau, 0x3f800000u, + 0xc0016900u, 0x2fcu, 0x3f800000u, + 0xc0016900u, 0x2fbu, 0x3f800000u, + 0xc0016900u, 0x2fdu, 0x3f800000u, + 0xc0016900u, 0x202u, 0xcc0010u, + 0xc0016900u, 0x30eu, 0xffffffffu, + 0xc0016900u, 0x30fu, 0xffffffffu, + 0xc0002f00u, 1u, + 0xc0017600u, 7u, 0x1701ffu, + 0xc0017600u, 0x46u, 0x1701fdu, + 0xc0017600u, 0x87u, 0x1701ffu, + 0xc0017600u, 0xc7u, 0x1701fdu, + 0xc0017600u, 0x107u, 0x17u, + 0xc0017600u, 0x147u, 0x1701fdu, + 0xc0017600u, 0x47u, 0x1cu, + 0xc0016900u, 0x1b1u, 2u, + 0xc0016900u, 0x101u, 0u, + 0xc0016900u, 0x100u, 0xffffffffu, + 0xc0016900u, 0x103u, 0u, + 0xc0016900u, 0x284u, 0u, + 0xc0016900u, 0x290u, 0u, + 0xc0016900u, 0x2aeu, 0u, + 0xc0016900u, 0x102u, 0u, + 0xc0016900u, 0x292u, 0u, + 0xc0016900u, 0x293u, 0x6020000u, + 0xc0016900u, 0x2f8u, 0u, + 0xc0016900u, 0x2deu, 0x1e9u, + 0xc0026900u, 0xebu, 0xff00ff00u, 0xff00u, + 0xc0036900u, 0x295u, 0x100u, 0x100u, 4u, + 0xc0017900u, 0x200u, 0xe0000000u, + 0xc0016900u, 0x100002aau, 0xd00ffu, +}; +static_assert(InitSequence350NeoCompat.size() == 0x86 + 2); + +constexpr std::array CtxInitSequence{ + 0xc0012800u, 0x80000000u, 0x80000000u, + 0xc0001200u, 0u, + 0xc0002f00u, 1u, + 0xc0016900u, 0x102u, 0u, + 0xc0016900u, 0x202u, 0xcc0010u, + 0xc0111000u, 0u +}; +static_assert(CtxInitSequence.size() == 0x0f); + +constexpr std::array CtxInitSequenceNeo{ + 0xc0012800u, 0x80000000u, 0x80000000u, + 0xc0001200u, 0u, + 0xc0002f00u, 1u, + 0xc0016900u, 0x102u, 0u, + 0xc0016900u, 0x202u, 0xcc0010u, + 0xc0026900u, 0xebu, 0xff00ff00u, 0xff00u, + 0xc00d1000, 0u +}; +static_assert(CtxInitSequenceNeo.size() == 0x13); + +constexpr std::array CtxInitSequence400{ + 0xc0012800u, 0x80000000u, 0x80000000u, + 0xc0001200u, 0u, + 0xc0016900u, 0x2f9u, 0x2du, + 0xc0016900u, 0x282u, 8u, + 0xc0016900u, 0x280u, 0x80008u, + 0xc0016900u, 0x281u, 0xffff0000u, + 0xc0016900u, 0x204u, 0u, + 0xc0016900u, 0x206u, 0x43fu, + 0xc0016900u, 0x83u, 0xffffu, + 0xc0016900u, 0x317u, 0x10u, + 0xc0016900u, 0x2fau, 0x3f800000u, + 0xc0016900u, 0x2fcu, 0x3f800000u, + 0xc0016900u, 0x2fbu, 0x3f800000u, + 0xc0016900u, 0x2fdu, 0x3f800000u, + 0xc0016900u, 0x202u, 0xcc0010u, + 0xc0016900u, 0x30eu, 0xffffffffu, + 0xc0016900u, 0x30fu, 0xffffffffu, + 0xc0002f00u, 1u, + 0xc0016900u, 0x1b1u, 2u, + 0xc0016900u, 0x101u, 0u, + 0xc0016900u, 0x100u, 0xffffffffu, + 0xc0016900u, 0x103u, 0u, + 0xc0016900u, 0x284u, 0u, + 0xc0016900u, 0x290u, 0u, + 0xc0016900u, 0x2aeu, 0u, + 0xc0016900u, 0x102u, 0u, + 0xc0016900u, 0x292u, 0u, + 0xc0016900u, 0x293u, 0x6020000u, + 0xc0016900u, 0x2f8u, 0u, + 0xc0016900u, 0x2deu, 0x1e9u, + 0xc0036900u, 0x295u, 0x100u, 0x100u, 4u, + 0xc0016900u, 0x2aau, 0xffu, + 0xc09e1000u, +}; +static_assert(CtxInitSequence400.size() == 0x61); + +constexpr std::array CtxInitSequence400Neo{ + 0xc0012800u, 0x80000000u, 0x80000000u, + 0xc0001200u, 0u, + 0xc0016900u, 0x2f9u, 0x2du, + 0xc0016900u, 0x282u, 8u, + 0xc0016900u, 0x280u, 0x80008u, + 0xc0016900u, 0x281u, 0xffff0000u, + 0xc0016900u, 0x204u, 0u, + 0xc0016900u, 0x206u, 0x43fu, + 0xc0016900u, 0x83u, 0xffffu, + 0xc0016900u, 0x317u, 0x10u, + 0xc0016900u, 0x2fau, 0x3f800000u, + 0xc0016900u, 0x2fcu, 0x3f800000u, + 0xc0016900u, 0x2fbu, 0x3f800000u, + 0xc0016900u, 0x2fdu, 0x3f800000u, + 0xc0016900u, 0x202u, 0xcc0010u, + 0xc0016900u, 0x30eu, 0xffffffffu, + 0xc0016900u, 0x30fu, 0xffffffffu, + 0xc0002f00u, 1u, + 0xc0016900u, 0x1b1u, 2u, + 0xc0016900u, 0x101u, 0u, + 0xc0016900u, 0x100u, 0xffffffffu, + 0xc0016900u, 0x103u, 0u, + 0xc0016900u, 0x284u, 0u, + 0xc0016900u, 0x290u, 0u, + 0xc0016900u, 0x2aeu, 0u, + 0xc0016900u, 0x102u, 0u, + 0xc0016900u, 0x292u, 0u, + 0xc0016900u, 0x293u, 0x6020000u, + 0xc0016900u, 0x2f8u, 0u, + 0xc0016900u, 0x2deu, 0x1e9u, + 0xc0026900u, 0xebu, 0xff00ff00u, 0xff00u, + 0xc0036900u, 0x295u, 0x100u, 0x100u, 4u, + 0xc0017900u, 0x40000258u, 0x6d007fu, + 0xc09a1000u, +}; +static_assert(CtxInitSequence400Neo.size() == 0x65); + +constexpr std::array CtxInitSequence400NeoCompat{ + 0xc0012800u, 0x80000000u, 0x80000000u, + 0xc0001200u, 0u, + 0xc0016900u, 0x2f9u, 0x2du, + 0xc0016900u, 0x282u, 8u, + 0xc0016900u, 0x280u, 0x80008u, + 0xc0016900u, 0x281u, 0xffff0000u, + 0xc0016900u, 0x204u, 0u, + 0xc0016900u, 0x206u, 0x43fu, + 0xc0016900u, 0x83u, 0xffffu, + 0xc0016900u, 0x317u, 0x10u, + 0xc0016900u, 0x2fau, 0x3f800000u, + 0xc0016900u, 0x2fcu, 0x3f800000u, + 0xc0016900u, 0x2fbu, 0x3f800000u, + 0xc0016900u, 0x2fdu, 0x3f800000u, + 0xc0016900u, 0x202u, 0xcc0010u, + 0xc0016900u, 0x30eu, 0xffffffffu, + 0xc0016900u, 0x30fu, 0xffffffffu, + 0xc0002f00u, 1u, + 0xc0016900u, 0x1b1u, 2u, + 0xc0016900u, 0x101u, 0u, + 0xc0016900u, 0x100u, 0xffffffffu, + 0xc0016900u, 0x103u, 0u, + 0xc0016900u, 0x284u, 0u, + 0xc0016900u, 0x290u, 0u, + 0xc0016900u, 0x2aeu, 0u, + 0xc0016900u, 0x102u, 0u, + 0xc0016900u, 0x292u, 0u, + 0xc0016900u, 0x293u, 0x6020000u, + 0xc0016900u, 0x2f8u, 0u, + 0xc0016900u, 0x2deu, 0x1e9u, + 0xc0026900u, 0xebu, 0xff00ff00u, 0xff00u, + 0xc0036900u, 0x295u, 0x100u, 0x100u, 4u, + 0xc0016900u, 0x100002aau, 0xd00ffu, + 0xc09a1000u, +}; +static_assert(CtxInitSequence400Neo.size() == 0x65); +// clang-format on + +} // namespace Libraries::GnmDriver diff --git a/src/video_core/amdgpu/pm4_cmds.h b/src/video_core/amdgpu/pm4_cmds.h index 238e09fad..e7d6b29e5 100644 --- a/src/video_core/amdgpu/pm4_cmds.h +++ b/src/video_core/amdgpu/pm4_cmds.h @@ -204,6 +204,11 @@ struct PM4CmdSetData { static constexpr u32* SetShReg(u32* cmdbuf, Args... data) { return WritePacket(cmdbuf, type, data...); } + + template + static constexpr u32* SetUconfigReg(u32* cmdbuf, Args... data) { + return WritePacket(cmdbuf, type, data...); + } }; struct PM4CmdNop { From c2be12f009ca3970c713b22fb328a5ae62e9239f Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 3 Jan 2025 12:25:20 -0800 Subject: [PATCH 348/549] amdgpu: Add some resource bits for Neo mode. (#2035) --- src/video_core/amdgpu/liverpool.h | 4 +++- src/video_core/amdgpu/resource.h | 10 +++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index d2d1aab3c..85ea4a75c 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -814,7 +814,9 @@ struct Liverpool { BitField<26, 1, u32> fmask_compression_disable_ci; BitField<27, 1, u32> fmask_compress_1frag_only; BitField<28, 1, u32> dcc_enable; - BitField<29, 1, u32> cmask_addr_type; + BitField<29, 2, u32> cmask_addr_type; + /// Neo-mode only + BitField<31, 1, u32> alt_tile_mode; u32 u32all; } info; diff --git a/src/video_core/amdgpu/resource.h b/src/video_core/amdgpu/resource.h index 208f7f380..1d9673850 100644 --- a/src/video_core/amdgpu/resource.h +++ b/src/video_core/amdgpu/resource.h @@ -263,7 +263,15 @@ struct Image { u64 min_lod_warn : 12; u64 counter_bank_id : 8; u64 lod_hw_cnt_en : 1; - u64 : 43; + /// Neo-mode only + u64 compression_en : 1; + /// Neo-mode only + u64 alpha_is_on_msb : 1; + /// Neo-mode only + u64 color_transform : 1; + /// Neo-mode only + u64 alt_tile_mode : 1; + u64 : 39; static constexpr Image Null() { Image image{}; From 8e8671323accda5df5d3ee3c1908751283c1da5e Mon Sep 17 00:00:00 2001 From: psucien <168137814+psucien@users.noreply.github.com> Date: Fri, 3 Jan 2025 21:42:23 +0100 Subject: [PATCH 349/549] texture_cache: slight detilers refactoring (#2036) --- src/video_core/buffer_cache/buffer_cache.cpp | 2 +- src/video_core/host_shaders/CMakeLists.txt | 16 +- .../macro_32bpp.comp} | 0 .../macro_64bpp.comp} | 0 .../macro_8bpp.comp} | 0 .../micro_128bpp.comp} | 0 .../micro_16bpp.comp} | 0 .../micro_32bpp.comp} | 0 .../micro_64bpp.comp} | 0 .../micro_8bpp.comp} | 0 .../renderer_vulkan/vk_presenter.cpp | 2 +- src/video_core/texture_cache/image.cpp | 2 +- src/video_core/texture_cache/image.h | 2 +- src/video_core/texture_cache/image_info.cpp | 28 ++-- src/video_core/texture_cache/image_info.h | 2 +- .../texture_cache/texture_cache.cpp | 34 ++-- src/video_core/texture_cache/tile_manager.cpp | 147 ++++-------------- src/video_core/texture_cache/tile_manager.h | 16 +- 18 files changed, 87 insertions(+), 164 deletions(-) rename src/video_core/host_shaders/{detile_macro32x1.comp => detilers/macro_32bpp.comp} (100%) rename src/video_core/host_shaders/{detile_macro32x2.comp => detilers/macro_64bpp.comp} (100%) rename src/video_core/host_shaders/{detile_macro8x1.comp => detilers/macro_8bpp.comp} (100%) rename src/video_core/host_shaders/{detile_m32x4.comp => detilers/micro_128bpp.comp} (100%) rename src/video_core/host_shaders/{detile_m8x2.comp => detilers/micro_16bpp.comp} (100%) rename src/video_core/host_shaders/{detile_m32x1.comp => detilers/micro_32bpp.comp} (100%) rename src/video_core/host_shaders/{detile_m32x2.comp => detilers/micro_64bpp.comp} (100%) rename src/video_core/host_shaders/{detile_m8x1.comp => detilers/micro_8bpp.comp} (100%) diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index 3e43b4fbc..322a9dd4e 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -660,7 +660,7 @@ bool BufferCache::SynchronizeBufferFromImage(Buffer& buffer, VAddr device_addr, FindFlags::NoCreate | FindFlags::RelaxDim | FindFlags::RelaxFmt | FindFlags::RelaxSize; TextureCache::BaseDesc desc{}; desc.info.guest_address = device_addr; - desc.info.guest_size_bytes = size; + desc.info.guest_size = size; const ImageId image_id = texture_cache.FindImage(desc, find_flags); if (!image_id) { return false; diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index 44761545d..a9c2964ad 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -2,14 +2,14 @@ # SPDX-License-Identifier: GPL-2.0-or-later set(SHADER_FILES - detile_m8x1.comp - detile_m8x2.comp - detile_m32x1.comp - detile_m32x2.comp - detile_m32x4.comp - detile_macro8x1.comp - detile_macro32x1.comp - detile_macro32x2.comp + detilers/macro_32bpp.comp + detilers/macro_64bpp.comp + detilers/macro_8bpp.comp + detilers/micro_128bpp.comp + detilers/micro_16bpp.comp + detilers/micro_32bpp.comp + detilers/micro_64bpp.comp + detilers/micro_8bpp.comp fs_tri.vert post_process.frag ) diff --git a/src/video_core/host_shaders/detile_macro32x1.comp b/src/video_core/host_shaders/detilers/macro_32bpp.comp similarity index 100% rename from src/video_core/host_shaders/detile_macro32x1.comp rename to src/video_core/host_shaders/detilers/macro_32bpp.comp diff --git a/src/video_core/host_shaders/detile_macro32x2.comp b/src/video_core/host_shaders/detilers/macro_64bpp.comp similarity index 100% rename from src/video_core/host_shaders/detile_macro32x2.comp rename to src/video_core/host_shaders/detilers/macro_64bpp.comp diff --git a/src/video_core/host_shaders/detile_macro8x1.comp b/src/video_core/host_shaders/detilers/macro_8bpp.comp similarity index 100% rename from src/video_core/host_shaders/detile_macro8x1.comp rename to src/video_core/host_shaders/detilers/macro_8bpp.comp diff --git a/src/video_core/host_shaders/detile_m32x4.comp b/src/video_core/host_shaders/detilers/micro_128bpp.comp similarity index 100% rename from src/video_core/host_shaders/detile_m32x4.comp rename to src/video_core/host_shaders/detilers/micro_128bpp.comp diff --git a/src/video_core/host_shaders/detile_m8x2.comp b/src/video_core/host_shaders/detilers/micro_16bpp.comp similarity index 100% rename from src/video_core/host_shaders/detile_m8x2.comp rename to src/video_core/host_shaders/detilers/micro_16bpp.comp diff --git a/src/video_core/host_shaders/detile_m32x1.comp b/src/video_core/host_shaders/detilers/micro_32bpp.comp similarity index 100% rename from src/video_core/host_shaders/detile_m32x1.comp rename to src/video_core/host_shaders/detilers/micro_32bpp.comp diff --git a/src/video_core/host_shaders/detile_m32x2.comp b/src/video_core/host_shaders/detilers/micro_64bpp.comp similarity index 100% rename from src/video_core/host_shaders/detile_m32x2.comp rename to src/video_core/host_shaders/detilers/micro_64bpp.comp diff --git a/src/video_core/host_shaders/detile_m8x1.comp b/src/video_core/host_shaders/detilers/micro_8bpp.comp similarity index 100% rename from src/video_core/host_shaders/detile_m8x1.comp rename to src/video_core/host_shaders/detilers/micro_8bpp.comp diff --git a/src/video_core/renderer_vulkan/vk_presenter.cpp b/src/video_core/renderer_vulkan/vk_presenter.cpp index 93129842f..1679aa691 100644 --- a/src/video_core/renderer_vulkan/vk_presenter.cpp +++ b/src/video_core/renderer_vulkan/vk_presenter.cpp @@ -427,7 +427,7 @@ bool Presenter::ShowSplash(Frame* frame /*= nullptr*/) { VideoCore::Extent3D{splash->GetImageInfo().width, splash->GetImageInfo().height, 1}; info.pitch = splash->GetImageInfo().width; info.guest_address = VAddr(splash->GetImageData().data()); - info.guest_size_bytes = splash->GetImageData().size(); + info.guest_size = splash->GetImageData().size(); info.mips_layout.emplace_back(splash->GetImageData().size(), splash->GetImageInfo().width, splash->GetImageInfo().height, 0); diff --git a/src/video_core/texture_cache/image.cpp b/src/video_core/texture_cache/image.cpp index 03339d280..23249bf21 100644 --- a/src/video_core/texture_cache/image.cpp +++ b/src/video_core/texture_cache/image.cpp @@ -210,7 +210,7 @@ Image::Image(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_, Vulkan::SetObjectName(instance->GetDevice(), (vk::Image)image, "Image {}x{}x{} {:#x}:{:#x}", info.size.width, info.size.height, info.size.depth, info.guest_address, - info.guest_size_bytes); + info.guest_size); } boost::container::small_vector Image::GetBarriers( diff --git a/src/video_core/texture_cache/image.h b/src/video_core/texture_cache/image.h index 473dd731e..b04fd188c 100644 --- a/src/video_core/texture_cache/image.h +++ b/src/video_core/texture_cache/image.h @@ -80,7 +80,7 @@ struct Image { [[nodiscard]] bool Overlaps(VAddr overlap_cpu_addr, size_t overlap_size) const noexcept { const VAddr overlap_end = overlap_cpu_addr + overlap_size; const auto image_addr = info.guest_address; - const auto image_end = info.guest_address + info.guest_size_bytes; + const auto image_end = info.guest_address + info.guest_size; return image_addr < overlap_end && overlap_cpu_addr < image_end; } diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index adc72c21f..8ea9267fd 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -250,15 +250,15 @@ ImageInfo::ImageInfo(const Libraries::VideoOut::BufferAttributeGroup& group, guest_address = cpu_address; if (!props.is_tiled) { - guest_size_bytes = pitch * size.height * 4; + guest_size = pitch * size.height * 4; } else { if (Config::isNeoMode()) { - guest_size_bytes = pitch * ((size.height + 127) & (~127)) * 4; + guest_size = pitch * ((size.height + 127) & (~127)) * 4; } else { - guest_size_bytes = pitch * ((size.height + 63) & (~63)) * 4; + guest_size = pitch * ((size.height + 63) & (~63)) * 4; } } - mips_layout.emplace_back(guest_size_bytes, pitch, 0); + mips_layout.emplace_back(guest_size, pitch, 0); } ImageInfo::ImageInfo(const AmdGpu::Liverpool::ColorBuffer& buffer, @@ -279,7 +279,7 @@ ImageInfo::ImageInfo(const AmdGpu::Liverpool::ColorBuffer& buffer, guest_address = buffer.Address(); const auto color_slice_sz = buffer.GetColorSliceSize(); - guest_size_bytes = color_slice_sz * buffer.NumSlices(); + guest_size = color_slice_sz * buffer.NumSlices(); mips_layout.emplace_back(color_slice_sz, pitch, 0); tiling_idx = static_cast(buffer.attrib.tile_mode_index.Value()); } @@ -303,7 +303,7 @@ ImageInfo::ImageInfo(const AmdGpu::Liverpool::DepthBuffer& buffer, u32 num_slice guest_address = buffer.Address(); const auto depth_slice_sz = buffer.GetDepthSliceSize(); - guest_size_bytes = depth_slice_sz * num_slices; + guest_size = depth_slice_sz * num_slices; mips_layout.emplace_back(depth_slice_sz, pitch, 0); } @@ -339,7 +339,7 @@ ImageInfo::ImageInfo(const AmdGpu::Image& image, const Shader::ImageResource& de void ImageInfo::UpdateSize() { mips_layout.clear(); MipInfo mip_info{}; - guest_size_bytes = 0; + guest_size = 0; for (auto mip = 0u; mip < resources.levels; ++mip) { auto bpp = num_bits; auto mip_w = pitch >> mip; @@ -392,11 +392,11 @@ void ImageInfo::UpdateSize() { } } mip_info.size *= mip_d; - mip_info.offset = guest_size_bytes; + mip_info.offset = guest_size; mips_layout.emplace_back(mip_info); - guest_size_bytes += mip_info.size; + guest_size += mip_info.size; } - guest_size_bytes *= resources.layers; + guest_size *= resources.layers; } int ImageInfo::IsMipOf(const ImageInfo& info) const { @@ -468,18 +468,18 @@ int ImageInfo::IsSliceOf(const ImageInfo& info) const { } // Check for size alignment. - const bool slice_size = info.guest_size_bytes / info.resources.layers; - if (guest_size_bytes % slice_size != 0) { + const bool slice_size = info.guest_size / info.resources.layers; + if (guest_size % slice_size != 0) { return -1; } // Ensure that address is aligned too. const auto addr_diff = guest_address - info.guest_address; - if ((addr_diff % guest_size_bytes) != 0) { + if ((addr_diff % guest_size) != 0) { return -1; } - return addr_diff / guest_size_bytes; + return addr_diff / guest_size; } } // namespace VideoCore diff --git a/src/video_core/texture_cache/image_info.h b/src/video_core/texture_cache/image_info.h index a657310a8..74f6c5674 100644 --- a/src/video_core/texture_cache/image_info.h +++ b/src/video_core/texture_cache/image_info.h @@ -84,7 +84,7 @@ struct ImageInfo { }; boost::container::small_vector mips_layout; VAddr guest_address{0}; - u32 guest_size_bytes{0}; + u32 guest_size{0}; u32 tiling_idx{0}; // TODO: merge with existing! VAddr stencil_addr{0}; diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index 291e1da7c..bef083d1a 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -3,7 +3,9 @@ #include #include + #include "common/assert.h" +#include "common/debug.h" #include "video_core/buffer_cache/buffer_cache.h" #include "video_core/page_manager.h" #include "video_core/renderer_vulkan/vk_instance.h" @@ -34,7 +36,7 @@ TextureCache::TextureCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& Vulkan::SetObjectName(instance.GetDevice(), null_image, "Null Image"); img.flags = ImageFlagBits::Empty; img.track_addr = img.info.guest_address; - img.track_addr_end = img.info.guest_address + img.info.guest_size_bytes; + img.track_addr_end = img.info.guest_address + img.info.guest_size; ImageViewInfo view_info; const auto null_view_id = @@ -50,7 +52,7 @@ void TextureCache::MarkAsMaybeDirty(ImageId image_id, Image& image) { if (image.hash == 0) { // Initialize hash const u8* addr = std::bit_cast(image.info.guest_address); - image.hash = XXH3_64bits(addr, image.info.guest_size_bytes); + image.hash = XXH3_64bits(addr, image.info.guest_size); } image.flags |= ImageFlagBits::MaybeCpuDirty; UntrackImage(image_id); @@ -63,7 +65,7 @@ void TextureCache::InvalidateMemory(VAddr addr, size_t size) { const auto pages_end = PageManager::GetNextPageAddr(addr + size - 1); ForEachImageInRegion(pages_start, pages_end - pages_start, [&](ImageId image_id, Image& image) { const auto image_begin = image.info.guest_address; - const auto image_end = image.info.guest_address + image.info.guest_size_bytes; + const auto image_end = image.info.guest_address + image.info.guest_size; if (image_begin < end && addr < image_end) { // Start or end of the modified region is in the image, or the image is entirely within // the modified region, so the image was definitely accessed by this page fault. @@ -201,7 +203,7 @@ std::tuple TextureCache::ResolveOverlap(const ImageInfo& imag } if (image_info.pixel_format != tex_cache_image.info.pixel_format || - image_info.guest_size_bytes <= tex_cache_image.info.guest_size_bytes) { + image_info.guest_size <= tex_cache_image.info.guest_size) { auto result_id = merged_image_id ? merged_image_id : cache_image_id; const auto& result_image = slot_images[result_id]; return { @@ -302,7 +304,7 @@ ImageId TextureCache::FindImage(BaseDesc& desc, FindFlags flags) { std::scoped_lock lock{mutex}; boost::container::small_vector image_ids; - ForEachImageInRegion(info.guest_address, info.guest_size_bytes, + ForEachImageInRegion(info.guest_address, info.guest_size, [&](ImageId image_id, Image& image) { image_ids.push_back(image_id); }); ImageId image_id{}; @@ -313,8 +315,7 @@ ImageId TextureCache::FindImage(BaseDesc& desc, FindFlags flags) { if (cache_image.info.guest_address != info.guest_address) { continue; } - if (False(flags & FindFlags::RelaxSize) && - cache_image.info.guest_size_bytes != info.guest_size_bytes) { + if (False(flags & FindFlags::RelaxSize) && cache_image.info.guest_size != info.guest_size) { continue; } if (False(flags & FindFlags::RelaxDim) && cache_image.info.size != info.size) { @@ -455,7 +456,7 @@ ImageView& TextureCache::FindDepthTarget(BaseDesc& desc) { if (!stencil_id) { ImageInfo info{}; info.guest_address = desc.info.stencil_addr; - info.guest_size_bytes = desc.info.stencil_size; + info.guest_size = desc.info.stencil_size; info.size = desc.info.size; stencil_id = slot_images.insert(instance, scheduler, info); RegisterImage(stencil_id); @@ -468,6 +469,9 @@ ImageView& TextureCache::FindDepthTarget(BaseDesc& desc) { } void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_scheduler /*= nullptr*/) { + RENDERER_TRACE; + TRACE_HINT(fmt::format("{:x}:{:x}", image.info.guest_address, image.info.guest_size)); + if (False(image.flags & ImageFlagBits::Dirty)) { return; } @@ -543,7 +547,7 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule const auto cmdbuf = sched_ptr->CommandBuffer(); const VAddr image_addr = image.info.guest_address; - const size_t image_size = image.info.guest_size_bytes; + const size_t image_size = image.info.guest_size; const auto [vk_buffer, buf_offset] = buffer_cache.ObtainViewBuffer(image_addr, image_size, is_gpu_dirty); @@ -612,7 +616,7 @@ void TextureCache::RegisterImage(ImageId image_id) { ASSERT_MSG(False(image.flags & ImageFlagBits::Registered), "Trying to register an already registered image"); image.flags |= ImageFlagBits::Registered; - ForEachPage(image.info.guest_address, image.info.guest_size_bytes, + ForEachPage(image.info.guest_address, image.info.guest_size, [this, image_id](u64 page) { page_table[page].push_back(image_id); }); } @@ -621,7 +625,7 @@ void TextureCache::UnregisterImage(ImageId image_id) { ASSERT_MSG(True(image.flags & ImageFlagBits::Registered), "Trying to unregister an already unregistered image"); image.flags &= ~ImageFlagBits::Registered; - ForEachPage(image.info.guest_address, image.info.guest_size_bytes, [this, image_id](u64 page) { + ForEachPage(image.info.guest_address, image.info.guest_size, [this, image_id](u64 page) { const auto page_it = page_table.find(page); if (page_it == nullptr) { UNREACHABLE_MSG("Unregistering unregistered page=0x{:x}", page << PageShift); @@ -640,7 +644,7 @@ void TextureCache::UnregisterImage(ImageId image_id) { void TextureCache::TrackImage(ImageId image_id) { auto& image = slot_images[image_id]; const auto image_begin = image.info.guest_address; - const auto image_end = image.info.guest_address + image.info.guest_size_bytes; + const auto image_end = image.info.guest_address + image.info.guest_size; if (image_begin == image.track_addr && image_end == image.track_addr_end) { return; } @@ -649,7 +653,7 @@ void TextureCache::TrackImage(ImageId image_id) { // Re-track the whole image image.track_addr = image_begin; image.track_addr_end = image_end; - tracker.UpdatePagesCachedCount(image_begin, image.info.guest_size_bytes, 1); + tracker.UpdatePagesCachedCount(image_begin, image.info.guest_size, 1); } else { if (image_begin < image.track_addr) { TrackImageHead(image_id); @@ -674,7 +678,7 @@ void TextureCache::TrackImageHead(ImageId image_id) { void TextureCache::TrackImageTail(ImageId image_id) { auto& image = slot_images[image_id]; - const auto image_end = image.info.guest_address + image.info.guest_size_bytes; + const auto image_end = image.info.guest_address + image.info.guest_size; if (image_end == image.track_addr_end) { return; } @@ -719,7 +723,7 @@ void TextureCache::UntrackImageHead(ImageId image_id) { void TextureCache::UntrackImageTail(ImageId image_id) { auto& image = slot_images[image_id]; - const auto image_end = image.info.guest_address + image.info.guest_size_bytes; + const auto image_end = image.info.guest_address + image.info.guest_size; if (!image.IsTracked() || image.track_addr_end < image_end) { return; } diff --git a/src/video_core/texture_cache/tile_manager.cpp b/src/video_core/texture_cache/tile_manager.cpp index 6b14b4602..aba255ce5 100644 --- a/src/video_core/texture_cache/tile_manager.cpp +++ b/src/video_core/texture_cache/tile_manager.cpp @@ -8,128 +8,47 @@ #include "video_core/texture_cache/image_view.h" #include "video_core/texture_cache/tile_manager.h" -#include "video_core/host_shaders/detile_m32x1_comp.h" -#include "video_core/host_shaders/detile_m32x2_comp.h" -#include "video_core/host_shaders/detile_m32x4_comp.h" -#include "video_core/host_shaders/detile_m8x1_comp.h" -#include "video_core/host_shaders/detile_m8x2_comp.h" -#include "video_core/host_shaders/detile_macro32x1_comp.h" -#include "video_core/host_shaders/detile_macro32x2_comp.h" -#include "video_core/host_shaders/detile_macro8x1_comp.h" +#include "video_core/host_shaders/detilers/macro_32bpp_comp.h" +#include "video_core/host_shaders/detilers/macro_64bpp_comp.h" +#include "video_core/host_shaders/detilers/macro_8bpp_comp.h" +#include "video_core/host_shaders/detilers/micro_128bpp_comp.h" +#include "video_core/host_shaders/detilers/micro_16bpp_comp.h" +#include "video_core/host_shaders/detilers/micro_32bpp_comp.h" +#include "video_core/host_shaders/detilers/micro_64bpp_comp.h" +#include "video_core/host_shaders/detilers/micro_8bpp_comp.h" -#include +// #include #include #include namespace VideoCore { -static vk::Format DemoteImageFormatForDetiling(vk::Format format) { - switch (format) { - case vk::Format::eR8Unorm: - case vk::Format::eR8Snorm: - case vk::Format::eR8Uint: - case vk::Format::eR8Srgb: - return vk::Format::eR8Uint; - case vk::Format::eR8G8Unorm: - case vk::Format::eR8G8Snorm: - case vk::Format::eR8G8Uint: - case vk::Format::eR8G8Srgb: - case vk::Format::eR16Unorm: - case vk::Format::eR16Snorm: - case vk::Format::eR16Uint: - case vk::Format::eR16Sfloat: - case vk::Format::eD16Unorm: - case vk::Format::eR4G4B4A4UnormPack16: - case vk::Format::eR5G5B5A1UnormPack16: - case vk::Format::eB5G5R5A1UnormPack16: - case vk::Format::eB5G6R5UnormPack16: - return vk::Format::eR8G8Uint; - case vk::Format::eR8G8B8A8Unorm: - case vk::Format::eR8G8B8A8Snorm: - case vk::Format::eR8G8B8A8Uint: - case vk::Format::eR8G8B8A8Srgb: - case vk::Format::eB8G8R8A8Unorm: - case vk::Format::eB8G8R8A8Snorm: - case vk::Format::eB8G8R8A8Uint: - case vk::Format::eB8G8R8A8Srgb: - case vk::Format::eR16G16Unorm: - case vk::Format::eR16G16Snorm: - case vk::Format::eR16G16Uint: - case vk::Format::eR16G16Sfloat: - case vk::Format::eR32Uint: - case vk::Format::eR32Sfloat: - case vk::Format::eD32Sfloat: - case vk::Format::eA2B10G10R10UnormPack32: - case vk::Format::eA2B10G10R10SnormPack32: - case vk::Format::eA2B10G10R10UintPack32: - case vk::Format::eB10G11R11UfloatPack32: - case vk::Format::eE5B9G9R9UfloatPack32: - return vk::Format::eR32Uint; - case vk::Format::eR16G16B16A16Unorm: - case vk::Format::eR16G16B16A16Snorm: - case vk::Format::eR16G16B16A16Uint: - case vk::Format::eR16G16B16A16Sfloat: - case vk::Format::eR32G32Uint: - case vk::Format::eR32G32Sfloat: - case vk::Format::eBc1RgbaUnormBlock: - case vk::Format::eBc1RgbaSrgbBlock: - case vk::Format::eBc4UnormBlock: - case vk::Format::eBc4SnormBlock: - return vk::Format::eR32G32Uint; - case vk::Format::eR32G32B32A32Uint: - case vk::Format::eR32G32B32A32Sfloat: - case vk::Format::eBc2UnormBlock: - case vk::Format::eBc2SrgbBlock: - case vk::Format::eBc3UnormBlock: - case vk::Format::eBc3SrgbBlock: - case vk::Format::eBc5UnormBlock: - case vk::Format::eBc5SnormBlock: - case vk::Format::eBc6HUfloatBlock: - case vk::Format::eBc6HSfloatBlock: - case vk::Format::eBc7UnormBlock: - case vk::Format::eBc7SrgbBlock: - return vk::Format::eR32G32B32A32Uint; - default: - break; - } - - // Log missing formats only once to avoid spamming the log. - static constexpr size_t MaxFormatIndex = 256; - static std::array logged_formats{}; - if (const u32 index = u32(format); !logged_formats[index]) { - LOG_ERROR(Render_Vulkan, "Unexpected format for demotion {}", vk::to_string(format)); - logged_formats[index] = true; - } - return format; -} - const DetilerContext* TileManager::GetDetiler(const ImageInfo& info) const { - const auto format = DemoteImageFormatForDetiling(info.pixel_format); - + const auto bpp = info.num_bits * (info.props.is_block ? 16 : 1); switch (info.tiling_mode) { case AmdGpu::TilingMode::Texture_MicroTiled: - switch (format) { - case vk::Format::eR8Uint: - return &detilers[DetilerType::Micro8x1]; - case vk::Format::eR8G8Uint: - return &detilers[DetilerType::Micro8x2]; - case vk::Format::eR32Uint: - return &detilers[DetilerType::Micro32x1]; - case vk::Format::eR32G32Uint: - return &detilers[DetilerType::Micro32x2]; - case vk::Format::eR32G32B32A32Uint: - return &detilers[DetilerType::Micro32x4]; + switch (bpp) { + case 8: + return &detilers[DetilerType::Micro8]; + case 16: + return &detilers[DetilerType::Micro16]; + case 32: + return &detilers[DetilerType::Micro32]; + case 64: + return &detilers[DetilerType::Micro64]; + case 128: + return &detilers[DetilerType::Micro128]; default: return nullptr; } case AmdGpu::TilingMode::Texture_Volume: - switch (format) { - case vk::Format::eR8Uint: - return &detilers[DetilerType::Macro8x1]; - case vk::Format::eR32Uint: - return &detilers[DetilerType::Macro32x1]; - case vk::Format::eR32G32Uint: - return &detilers[DetilerType::Macro32x2]; + switch (bpp) { + case 8: + return &detilers[DetilerType::Macro8]; + case 32: + return &detilers[DetilerType::Macro32]; + case 64: + return &detilers[DetilerType::Macro64]; default: return nullptr; } @@ -149,10 +68,10 @@ struct DetilerParams { TileManager::TileManager(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler) : instance{instance}, scheduler{scheduler} { static const std::array detiler_shaders{ - HostShaders::DETILE_M8X1_COMP, HostShaders::DETILE_M8X2_COMP, - HostShaders::DETILE_M32X1_COMP, HostShaders::DETILE_M32X2_COMP, - HostShaders::DETILE_M32X4_COMP, HostShaders::DETILE_MACRO8X1_COMP, - HostShaders::DETILE_MACRO32X1_COMP, HostShaders::DETILE_MACRO32X2_COMP, + HostShaders::MICRO_8BPP_COMP, HostShaders::MICRO_16BPP_COMP, + HostShaders::MICRO_32BPP_COMP, HostShaders::MICRO_64BPP_COMP, + HostShaders::MICRO_128BPP_COMP, HostShaders::MACRO_8BPP_COMP, + HostShaders::MACRO_32BPP_COMP, HostShaders::MACRO_64BPP_COMP, }; boost::container::static_vector bindings{ @@ -293,7 +212,7 @@ std::pair TileManager::TryDetile(vk::Buffer in_buffer, u32 in_o return {in_buffer, in_offset}; } - const u32 image_size = info.guest_size_bytes; + const u32 image_size = info.guest_size; // Prepare output buffer auto out_buffer = AllocBuffer(image_size, true); diff --git a/src/video_core/texture_cache/tile_manager.h b/src/video_core/texture_cache/tile_manager.h index bcf5accd3..4eae7be9e 100644 --- a/src/video_core/texture_cache/tile_manager.h +++ b/src/video_core/texture_cache/tile_manager.h @@ -12,15 +12,15 @@ class TextureCache; struct ImageInfo; enum DetilerType : u32 { - Micro8x1, - Micro8x2, - Micro32x1, - Micro32x2, - Micro32x4, + Micro8, + Micro16, + Micro32, + Micro64, + Micro128, - Macro8x1, - Macro32x1, - Macro32x2, + Macro8, + Macro32, + Macro64, Max }; From 7153bc8d8fab813d36111423cf8dfa2c9c3b756e Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 3 Jan 2025 15:29:09 -0800 Subject: [PATCH 350/549] kernel: Check PSF for neo mode support. (#2028) --- src/common/config.cpp | 2 +- src/common/config.h | 2 +- src/common/elf_info.h | 47 +++++++++ src/core/libraries/kernel/process.cpp | 2 +- src/core/memory.cpp | 3 +- src/emulator.cpp | 105 ++++++++++---------- src/video_core/texture_cache/image_info.cpp | 3 +- 7 files changed, 105 insertions(+), 59 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index 246644e2d..9e2cc0020 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -100,7 +100,7 @@ void setTrophyKey(std::string key) { trophyKey = key; } -bool isNeoMode() { +bool isNeoModeConsole() { return isNeo; } diff --git a/src/common/config.h b/src/common/config.h index 9d943008b..2b9a35449 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -18,7 +18,7 @@ void saveMainWindow(const std::filesystem::path& path); std::string getTrophyKey(); void setTrophyKey(std::string key); -bool isNeoMode(); +bool isNeoModeConsole(); bool isFullscreenMode(); bool getPlayBGM(); int getBGMvolume(); diff --git a/src/common/elf_info.h b/src/common/elf_info.h index 6eb144e9a..02eefbb7a 100644 --- a/src/common/elf_info.h +++ b/src/common/elf_info.h @@ -7,6 +7,7 @@ #include #include "assert.h" +#include "bit_field.h" #include "singleton.h" #include "types.h" @@ -16,6 +17,46 @@ class Emulator; namespace Common { +union PSFAttributes { + /// Supports initial user's logout + BitField<0, 1, u32> support_initial_user_logout; + /// Enter button for the common dialog is cross. + BitField<1, 1, u32> enter_button_cross; + /// Warning dialog for PS Move is displayed in the options menu. + BitField<2, 1, u32> ps_move_warning; + /// Supports stereoscopic 3D. + BitField<3, 1, u32> support_stereoscopic_3d; + /// Suspends when PS button is pressed. + BitField<4, 1, u32> ps_button_suspend; + /// Enter button for the common dialog is assigned by the system software. + BitField<5, 1, u32> enter_button_system; + /// Overrides share menu behavior. + BitField<6, 1, u32> override_share_menu; + /// Suspends when PS button is pressed and special output resolution is set. + BitField<8, 1, u32> special_res_ps_button_suspend; + /// Enable HDCP. + BitField<9, 1, u32> enable_hdcp; + /// Disable HDCP for non-game. + BitField<10, 1, u32> disable_hdcp_non_game; + /// Supports PS VR. + BitField<14, 1, u32> support_ps_vr; + /// CPU mode (6 CPU) + BitField<15, 1, u32> six_cpu_mode; + /// CPU mode (7 CPU) + BitField<16, 1, u32> seven_cpu_mode; + /// Supports PS4 Pro (Neo) mode. + BitField<23, 1, u32> support_neo_mode; + /// Requires PS VR. + BitField<26, 1, u32> require_ps_vr; + /// Supports HDR. + BitField<29, 1, u32> support_hdr; + /// Display location. + BitField<31, 1, u32> display_location; + + u32 raw{}; +}; +static_assert(sizeof(PSFAttributes) == 4); + class ElfInfo { friend class Core::Emulator; @@ -26,6 +67,7 @@ class ElfInfo { std::string app_ver{}; u32 firmware_ver = 0; u32 raw_firmware_ver = 0; + PSFAttributes psf_attributes{}; public: static constexpr u32 FW_15 = 0x1500000; @@ -68,6 +110,11 @@ public: ASSERT(initialized); return raw_firmware_ver; } + + [[nodiscard]] const PSFAttributes& PSFAttributes() const { + ASSERT(initialized); + return psf_attributes; + } }; } // namespace Common diff --git a/src/core/libraries/kernel/process.cpp b/src/core/libraries/kernel/process.cpp index 97cc01ebc..2fa597d4d 100644 --- a/src/core/libraries/kernel/process.cpp +++ b/src/core/libraries/kernel/process.cpp @@ -14,7 +14,7 @@ namespace Libraries::Kernel { int PS4_SYSV_ABI sceKernelIsNeoMode() { LOG_DEBUG(Kernel_Sce, "called"); - return Config::isNeoMode(); + return Config::isNeoModeConsole() && Common::ElfInfo::Instance().PSFAttributes().support_neo_mode; } int PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(int* ver) { diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 0a69ad773..1327ede5f 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -7,6 +7,7 @@ #include "common/debug.h" #include "core/libraries/kernel/memory.h" #include "core/libraries/kernel/orbis_error.h" +#include "core/libraries/kernel/process.h" #include "core/memory.h" #include "video_core/renderer_vulkan/vk_rasterizer.h" @@ -35,7 +36,7 @@ MemoryManager::~MemoryManager() = default; void MemoryManager::SetupMemoryRegions(u64 flexible_size, bool use_extended_mem1, bool use_extended_mem2) { - const bool is_neo = Config::isNeoMode(); + const bool is_neo = ::Libraries::Kernel::sceKernelIsNeoMode(); auto total_size = is_neo ? SCE_KERNEL_TOTAL_MEM_PRO : SCE_KERNEL_TOTAL_MEM; if (!use_extended_mem1 && is_neo) { total_size -= 256_MB; diff --git a/src/emulator.cpp b/src/emulator.cpp index 4f0c61236..5d037e26c 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -28,8 +28,6 @@ #include "core/file_format/trp.h" #include "core/file_sys/fs.h" #include "core/libraries/disc_map/disc_map.h" -#include "core/libraries/fiber/fiber.h" -#include "core/libraries/jpeg/jpegenc.h" #include "core/libraries/libc_internal/libc_internal.h" #include "core/libraries/libs.h" #include "core/libraries/ngs2/ngs2.h" @@ -59,8 +57,8 @@ Emulator::Emulator() { LOG_INFO(Loader, "Branch {}", Common::g_scm_branch); LOG_INFO(Loader, "Description {}", Common::g_scm_desc); - LOG_INFO(Config, "General Logtype: {}", Config::getLogType()); - LOG_INFO(Config, "General isNeo: {}", Config::isNeoMode()); + LOG_INFO(Config, "General LogType: {}", Config::getLogType()); + LOG_INFO(Config, "General isNeo: {}", Config::isNeoModeConsole()); LOG_INFO(Config, "GPU isNullGpu: {}", Config::nullGpu()); LOG_INFO(Config, "GPU shouldDumpShaders: {}", Config::dumpShaders()); LOG_INFO(Config, "GPU vblankDivider: {}", Config::vblankDiv()); @@ -101,19 +99,12 @@ Emulator::~Emulator() { } void Emulator::Run(const std::filesystem::path& file) { - - // Use the eboot from the separated updates folder if it's there - std::filesystem::path game_patch_folder = file.parent_path(); - game_patch_folder += "-UPDATE"; - std::filesystem::path eboot_path = std::filesystem::exists(game_patch_folder / file.filename()) - ? game_patch_folder / file.filename() - : file; - // Applications expect to be run from /app0 so mount the file's parent path as app0. auto* mnt = Common::Singleton::Instance(); - mnt->Mount(file.parent_path(), "/app0"); + const auto game_folder = file.parent_path(); + mnt->Mount(game_folder, "/app0"); // Certain games may use /hostapp as well such as CUSA001100 - mnt->Mount(file.parent_path(), "/hostapp"); + mnt->Mount(game_folder, "/hostapp"); auto& game_info = Common::ElfInfo::Instance(); @@ -122,50 +113,52 @@ void Emulator::Run(const std::filesystem::path& file) { std::string title; std::string app_version; u32 fw_version; + Common::PSFAttributes psf_attributes{}; - std::filesystem::path sce_sys_folder = eboot_path.parent_path() / "sce_sys"; - if (std::filesystem::is_directory(sce_sys_folder)) { - for (const auto& entry : std::filesystem::directory_iterator(sce_sys_folder)) { - if (entry.path().filename() == "param.sfo") { - auto* param_sfo = Common::Singleton::Instance(); - const bool success = param_sfo->Open(sce_sys_folder / "param.sfo"); - ASSERT_MSG(success, "Failed to open param.sfo"); - const auto content_id = param_sfo->GetString("CONTENT_ID"); - ASSERT_MSG(content_id.has_value(), "Failed to get CONTENT_ID"); - id = std::string(*content_id, 7, 9); - Libraries::NpTrophy::game_serial = id; - const auto trophyDir = - Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / id / "TrophyFiles"; - if (!std::filesystem::exists(trophyDir)) { - TRP trp; - if (!trp.Extract(eboot_path.parent_path(), id)) { - LOG_ERROR(Loader, "Couldn't extract trophies"); - } - } + const auto param_sfo_path = mnt->GetHostPath("/app0/sce_sys/param.sfo"); + if (std::filesystem::exists(param_sfo_path)) { + auto* param_sfo = Common::Singleton::Instance(); + const bool success = param_sfo->Open(param_sfo_path); + ASSERT_MSG(success, "Failed to open param.sfo"); + const auto content_id = param_sfo->GetString("CONTENT_ID"); + ASSERT_MSG(content_id.has_value(), "Failed to get CONTENT_ID"); + id = std::string(*content_id, 7, 9); + Libraries::NpTrophy::game_serial = id; + const auto trophyDir = + Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / id / "TrophyFiles"; + if (!std::filesystem::exists(trophyDir)) { + TRP trp; + if (!trp.Extract(game_folder, id)) { + LOG_ERROR(Loader, "Couldn't extract trophies"); + } + } #ifdef ENABLE_QT_GUI - MemoryPatcher::g_game_serial = id; + MemoryPatcher::g_game_serial = id; - // Timer for 'Play Time' - QTimer* timer = new QTimer(); - QObject::connect(timer, &QTimer::timeout, [this, id]() { - UpdatePlayTime(id); - start_time = std::chrono::steady_clock::now(); - }); - timer->start(60000); // 60000 ms = 1 minute + // Timer for 'Play Time' + QTimer* timer = new QTimer(); + QObject::connect(timer, &QTimer::timeout, [this, id]() { + UpdatePlayTime(id); + start_time = std::chrono::steady_clock::now(); + }); + timer->start(60000); // 60000 ms = 1 minute #endif - title = param_sfo->GetString("TITLE").value_or("Unknown title"); - LOG_INFO(Loader, "Game id: {} Title: {}", id, title); - fw_version = param_sfo->GetInteger("SYSTEM_VER").value_or(0x4700000); - app_version = param_sfo->GetString("APP_VER").value_or("Unknown version"); - LOG_INFO(Loader, "Fw: {:#x} App Version: {}", fw_version, app_version); - } else if (entry.path().filename() == "pic1.png") { - auto* splash = Common::Singleton::Instance(); - if (splash->IsLoaded()) { - continue; - } - if (!splash->Open(entry.path())) { - LOG_ERROR(Loader, "Game splash: unable to open file"); - } + title = param_sfo->GetString("TITLE").value_or("Unknown title"); + LOG_INFO(Loader, "Game id: {} Title: {}", id, title); + fw_version = param_sfo->GetInteger("SYSTEM_VER").value_or(0x4700000); + app_version = param_sfo->GetString("APP_VER").value_or("Unknown version"); + LOG_INFO(Loader, "Fw: {:#x} App Version: {}", fw_version, app_version); + if (const auto raw_attributes = param_sfo->GetInteger("ATTRIBUTE")) { + psf_attributes.raw = *raw_attributes; + } + } + + const auto pic1_path = mnt->GetHostPath("/app0/sce_sys/pic1.png"); + if (std::filesystem::exists(pic1_path)) { + auto* splash = Common::Singleton::Instance(); + if (!splash->IsLoaded()) { + if (!splash->Open(pic1_path)) { + LOG_ERROR(Loader, "Game splash: unable to open file"); } } } @@ -176,6 +169,7 @@ void Emulator::Run(const std::filesystem::path& file) { game_info.app_ver = app_version; game_info.firmware_ver = fw_version & 0xFFF00000; game_info.raw_firmware_ver = fw_version; + game_info.psf_attributes = psf_attributes; std::string game_title = fmt::format("{} - {} <{}>", id, title, app_version); std::string window_title = ""; @@ -219,6 +213,7 @@ void Emulator::Run(const std::filesystem::path& file) { Libraries::InitHLELibs(&linker->GetHLESymbols()); // Load the module with the linker + const auto eboot_path = mnt->GetHostPath("/app0/" + file.filename().string()); linker->LoadModule(eboot_path); // check if we have system modules to load @@ -236,6 +231,8 @@ void Emulator::Run(const std::filesystem::path& file) { } // Load all prx from separate update's sce_module folder + std::filesystem::path game_patch_folder = game_folder; + game_patch_folder += "-UPDATE"; std::filesystem::path update_module_folder = game_patch_folder / "sce_module"; if (std::filesystem::is_directory(update_module_folder)) { for (const auto& entry : std::filesystem::directory_iterator(update_module_folder)) { diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index 8ea9267fd..2f69410cd 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -3,6 +3,7 @@ #include "common/assert.h" #include "common/config.h" +#include "core/libraries/kernel/process.h" #include "video_core/renderer_vulkan/liverpool_to_vk.h" #include "video_core/texture_cache/image_info.h" @@ -252,7 +253,7 @@ ImageInfo::ImageInfo(const Libraries::VideoOut::BufferAttributeGroup& group, if (!props.is_tiled) { guest_size = pitch * size.height * 4; } else { - if (Config::isNeoMode()) { + if (Libraries::Kernel::sceKernelIsNeoMode()) { guest_size = pitch * ((size.height + 127) & (~127)) * 4; } else { guest_size = pitch * ((size.height + 63) & (~63)) * 4; From ddc658f8c85a927488c827bd5d79ac8bad1e9b52 Mon Sep 17 00:00:00 2001 From: psucien Date: Sat, 4 Jan 2025 00:32:17 +0100 Subject: [PATCH 351/549] clang-format --- src/core/libraries/kernel/process.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/libraries/kernel/process.cpp b/src/core/libraries/kernel/process.cpp index 2fa597d4d..791a98a36 100644 --- a/src/core/libraries/kernel/process.cpp +++ b/src/core/libraries/kernel/process.cpp @@ -14,7 +14,8 @@ namespace Libraries::Kernel { int PS4_SYSV_ABI sceKernelIsNeoMode() { LOG_DEBUG(Kernel_Sce, "called"); - return Config::isNeoModeConsole() && Common::ElfInfo::Instance().PSFAttributes().support_neo_mode; + return Config::isNeoModeConsole() && + Common::ElfInfo::Instance().PSFAttributes().support_neo_mode; } int PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(int* ver) { From 78a32a3c0f19cc39478def05e14371763a87c3da Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sat, 4 Jan 2025 02:44:14 -0800 Subject: [PATCH 352/549] image_info: Add Neo mode macro tile extents. (#2045) --- src/video_core/texture_cache/image_info.cpp | 194 +---------- src/video_core/texture_cache/image_info.h | 1 + src/video_core/texture_cache/tile.h | 347 ++++++++++++++++++++ 3 files changed, 352 insertions(+), 190 deletions(-) create mode 100644 src/video_core/texture_cache/tile.h diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index 2f69410cd..bdbaecda6 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -6,6 +6,7 @@ #include "core/libraries/kernel/process.h" #include "video_core/renderer_vulkan/liverpool_to_vk.h" #include "video_core/texture_cache/image_info.h" +#include "video_core/texture_cache/tile.h" namespace VideoCore { @@ -46,195 +47,6 @@ static vk::ImageType ConvertImageType(AmdGpu::ImageType type) noexcept { } } -// clang-format off -// The table of macro tiles parameters for given tiling index (row) and bpp (column) -static constexpr std::array macro_tile_extents_x1{ - std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, // 00 - std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 01 - std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 02 - std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 03 - std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, // 04 - std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 05 - std::pair{256u, 256u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 06 - std::pair{256u, 256u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, // 07 - std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 08 - std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 09 - std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, // 0A - std::pair{256u, 256u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, // 0B - std::pair{256u, 256u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 0C - std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 0D - std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, // 0E - std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, // 0F - std::pair{256u, 256u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, // 10 - std::pair{256u, 256u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, // 11 - std::pair{256u, 256u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, // 12 - std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 13 - std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 14 - std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 15 - std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 16 - std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 17 - std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 18 - std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 19 - std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 1A -}; - -static constexpr std::array macro_tile_extents_x2{ - std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, // 00 - std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 01 - std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 02 - std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 03 - std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 04 - std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 05 - std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 06 - std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 07 - std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 08 - std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 09 - std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0A - std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0B - std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0C - std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 0D - std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0E - std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0F - std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 10 - std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 11 - std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 12 - std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 13 - std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 14 - std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 15 - std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 16 - std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 17 - std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 18 - std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 19 - std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 1A -}; - -static constexpr std::array macro_tile_extents_x4{ - std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, // 00 - std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 01 - std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 02 - std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 03 - std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 04 - std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 05 - std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 06 - std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 07 - std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 08 - std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 09 - std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0A - std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0B - std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0C - std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 0D - std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0E - std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0F - std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 10 - std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 11 - std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 12 - std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 13 - std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 14 - std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 15 - std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 16 - std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 17 - std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 18 - std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 19 - std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 1A -}; - -static constexpr std::array macro_tile_extents_x8{ - std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, // 00 - std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 01 - std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 02 - std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 03 - std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 04 - std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 05 - std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 06 - std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 07 - std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 08 - std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 09 - std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0A - std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0B - std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0C - std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 0D - std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0E - std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0F - std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 10 - std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 11 - std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 12 - std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 13 - std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 14 - std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 15 - std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 16 - std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 17 - std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 18 - std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 19 - std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 1A -}; - -static constexpr std::array macro_tile_extents{ - macro_tile_extents_x1, - macro_tile_extents_x2, - macro_tile_extents_x4, - macro_tile_extents_x8, -}; -// clang-format on - -static constexpr std::pair micro_tile_extent{8u, 8u}; -static constexpr auto hw_pipe_interleave = 256u; - -static constexpr std::pair GetMacroTileExtents(u32 tiling_idx, u32 bpp, u32 num_samples) { - ASSERT(num_samples <= 8); - const auto row = tiling_idx * 5; - const auto column = std::bit_width(bpp) - 4; // bpps are 8, 16, 32, 64, 128 - return (macro_tile_extents[std::log2(num_samples)])[row + column]; -} - -static constexpr std::pair ImageSizeLinearAligned(u32 pitch, u32 height, u32 bpp, - u32 num_samples) { - const auto pitch_align = std::max(8u, 64u / ((bpp + 7) / 8)); - auto pitch_aligned = (pitch + pitch_align - 1) & ~(pitch_align - 1); - const auto height_aligned = height; - size_t log_sz = pitch_aligned * height_aligned * num_samples; - const auto slice_align = std::max(64u, 256u / ((bpp + 7) / 8)); - while (log_sz % slice_align) { - pitch_aligned += pitch_align; - log_sz = pitch_aligned * height_aligned * num_samples; - } - return {pitch_aligned, (log_sz * bpp + 7) / 8}; -} - -static constexpr std::pair ImageSizeMicroTiled(u32 pitch, u32 height, u32 bpp, - u32 num_samples) { - const auto& [pitch_align, height_align] = micro_tile_extent; - auto pitch_aligned = (pitch + pitch_align - 1) & ~(pitch_align - 1); - const auto height_aligned = (height + height_align - 1) & ~(height_align - 1); - size_t log_sz = (pitch_aligned * height_aligned * bpp * num_samples + 7) / 8; - while (log_sz % 256) { - pitch_aligned += 8; - log_sz = (pitch_aligned * height_aligned * bpp * num_samples + 7) / 8; - } - return {pitch_aligned, log_sz}; -} - -static constexpr std::pair ImageSizeMacroTiled(u32 pitch, u32 height, u32 bpp, - u32 num_samples, u32 tiling_idx, - u32 mip_n) { - const auto& [pitch_align, height_align] = GetMacroTileExtents(tiling_idx, bpp, num_samples); - ASSERT(pitch_align != 0 && height_align != 0); - bool downgrade_to_micro = false; - if (mip_n > 0) { - const bool is_less_than_tile = pitch < pitch_align || height < height_align; - // TODO: threshold check - downgrade_to_micro = is_less_than_tile; - } - - if (downgrade_to_micro) { - return ImageSizeMicroTiled(pitch, height, bpp, num_samples); - } - - const auto pitch_aligned = (pitch + pitch_align - 1) & ~(pitch_align - 1); - const auto height_aligned = (height + height_align - 1) & ~(height_align - 1); - const auto log_sz = pitch_aligned * height_aligned * num_samples; - return {pitch_aligned, (log_sz * bpp + 7) / 8}; -} - ImageInfo::ImageInfo(const Libraries::VideoOut::BufferAttributeGroup& group, VAddr cpu_address) noexcept { const auto& attrib = group.attrib; @@ -283,6 +95,7 @@ ImageInfo::ImageInfo(const AmdGpu::Liverpool::ColorBuffer& buffer, guest_size = color_slice_sz * buffer.NumSlices(); mips_layout.emplace_back(color_slice_sz, pitch, 0); tiling_idx = static_cast(buffer.attrib.tile_mode_index.Value()); + alt_tile = Libraries::Kernel::sceKernelIsNeoMode() && buffer.info.alt_tile_mode; } ImageInfo::ImageInfo(const AmdGpu::Liverpool::DepthBuffer& buffer, u32 num_slices, @@ -334,6 +147,7 @@ ImageInfo::ImageInfo(const AmdGpu::Image& image, const Shader::ImageResource& de mips_layout.reserve(resources.levels); tiling_idx = image.tiling_index; + alt_tile = Libraries::Kernel::sceKernelIsNeoMode() && image.alt_tile_mode; UpdateSize(); } @@ -385,7 +199,7 @@ void ImageInfo::UpdateSize() { case AmdGpu::TilingMode::Depth_MacroTiled: { ASSERT(!props.is_block); std::tie(mip_info.pitch, mip_info.size) = - ImageSizeMacroTiled(mip_w, mip_h, bpp, num_samples, tiling_idx, mip); + ImageSizeMacroTiled(mip_w, mip_h, bpp, num_samples, tiling_idx, mip, alt_tile); break; } default: { diff --git a/src/video_core/texture_cache/image_info.h b/src/video_core/texture_cache/image_info.h index 74f6c5674..6faca49c5 100644 --- a/src/video_core/texture_cache/image_info.h +++ b/src/video_core/texture_cache/image_info.h @@ -86,6 +86,7 @@ struct ImageInfo { VAddr guest_address{0}; u32 guest_size{0}; u32 tiling_idx{0}; // TODO: merge with existing! + bool alt_tile{false}; VAddr stencil_addr{0}; u32 stencil_size{0}; diff --git a/src/video_core/texture_cache/tile.h b/src/video_core/texture_cache/tile.h new file mode 100644 index 000000000..532bf3d88 --- /dev/null +++ b/src/video_core/texture_cache/tile.h @@ -0,0 +1,347 @@ +// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/assert.h" +#include "common/types.h" + +namespace VideoCore { + +// clang-format off +// The table of macro tiles parameters for given tiling index (row) and bpp (column) +/* Calculation: + * - Inputs: + * TileMode, BytesPerPixel, NumFragments + * - Constants: + * MicroTileWidth = 8, MicroTileHeight = 8, + * Tile Mode LUTs: IsDepth(), IsPrt(), TileThickness(), TileSplit(), SampleSplit(), NumPipes() + * Macro Tile Mode LUTs: BankWidth(), BankHeight(), NumBanks(), MacroTileAspect() + * - Determine the macro tile mode: + * TileBytes = MicroTileWidth * MicroTileHeight * TileThickness(TileMode) * BytesPerPixel + * TileSplit = min(IsDepth(TileMode) ? TileSplit(TileMode) : max(TileBytes * SampleSplit(TileMode), 256), NumFragments * TileBytes, 1024) + * MacroTileModeIndex = log2(TileSplit / 64) + * MacroTileMode = IsPrt(TileMode) ? MacroTileModeIndex + 8 : MacroTileModeIndex + * - Calculate macro tile width and height: + * Width = NumPipes(TileMode) * BankWidth(MacroTileMode) * MicroTileWidth * MacroTileAspect(MacroTileMode, AltTileMode) + * Height = NumBanks(MacroTileMode, AltTileMode) * BankHeight(MacroTileMode, AltTileMode) * MicroTileHeight / MacroTileAspect(MacroTileMode, AltTileMode) + */ + +constexpr std::array macro_tile_extents_x1{ + std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, // 00 + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 01 + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 02 + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 03 + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, // 04 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 05 + std::pair{256u, 256u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 06 + std::pair{256u, 256u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, // 07 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 08 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 09 + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, // 0A + std::pair{256u, 256u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, // 0B + std::pair{256u, 256u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, // 0C + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 0D + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, // 0E + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, // 0F + std::pair{256u, 256u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, // 10 + std::pair{256u, 256u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, // 11 + std::pair{256u, 256u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, // 12 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 13 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 14 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 15 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 16 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 17 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 18 + std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 19 + std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 1A +}; + +constexpr std::array macro_tile_extents_x2{ + std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, // 00 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 01 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 02 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 03 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 04 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 05 + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 06 + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 07 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 08 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 09 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0A + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0B + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0C + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 0D + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0E + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0F + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 10 + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 11 + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 12 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 13 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 14 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 15 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 16 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 17 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 18 + std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 19 + std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 1A +}; + +constexpr std::array macro_tile_extents_x4{ + std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, // 00 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 01 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 02 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 03 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 04 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 05 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 06 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 07 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 08 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 09 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0A + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0B + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0C + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 0D + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0E + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0F + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 10 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 11 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 12 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 13 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 14 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 15 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 16 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 17 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 18 + std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 19 + std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 1A +}; + +constexpr std::array macro_tile_extents_x8{ + std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, // 00 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 01 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 02 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 03 + std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 04 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 05 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 06 + std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 07 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 08 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 09 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0A + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0B + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0C + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 0D + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0E + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0F + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 10 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 11 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 12 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 13 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 14 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 15 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 16 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 17 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 18 + std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 19 + std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 1A +}; + +constexpr std::array macro_tile_extents_alt_x1{ + std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, // 00 + std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, // 01 + std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 02 + std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 03 + std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, // 04 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 05 + std::pair{256u, 256u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 06 + std::pair{256u, 256u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, // 07 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 08 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 09 + std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, // 0A + std::pair{256u, 256u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, // 0B + std::pair{256u, 256u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, // 0C + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 0D + std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, // 0E + std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, // 0F + std::pair{256u, 256u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, // 10 + std::pair{256u, 256u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, // 11 + std::pair{256u, 256u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, // 12 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 13 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 14 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 15 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, std::pair{128u, 32u}, std::pair{128u, 32u}, // 16 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, std::pair{128u, 32u}, std::pair{128u, 32u}, // 17 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, std::pair{128u, 32u}, std::pair{128u, 32u}, // 18 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 19 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 1A +}; + +constexpr std::array macro_tile_extents_alt_x2{ + std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, // 00 + std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, // 01 + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 02 + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 03 + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 04 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 05 + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 06 + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, std::pair{128u, 32u}, // 07 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 08 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 09 + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 0A + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, std::pair{128u, 32u}, // 0B + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, std::pair{128u, 32u}, // 0C + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 0D + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 0E + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 0F + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, std::pair{128u, 32u}, // 10 + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, std::pair{128u, 32u}, // 11 + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, std::pair{128u, 32u}, // 12 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 13 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 14 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 15 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, std::pair{128u, 32u}, std::pair{128u, 32u}, // 16 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, std::pair{128u, 32u}, std::pair{128u, 32u}, // 17 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, std::pair{128u, 32u}, std::pair{128u, 32u}, // 18 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 19 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 1A +}; + +constexpr std::array macro_tile_extents_alt_x4{ + std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, // 00 + std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, // 01 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 02 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 03 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 04 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 05 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 06 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, std::pair{128u, 32u}, std::pair{128u, 32u}, // 07 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 08 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 09 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 0A + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, std::pair{128u, 32u}, // 0B + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, std::pair{128u, 32u}, // 0C + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 0D + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 0E + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 0F + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, std::pair{128u, 32u}, // 10 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, std::pair{128u, 32u}, // 11 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, std::pair{128u, 32u}, // 12 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 13 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 14 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 15 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, std::pair{128u, 32u}, std::pair{128u, 32u}, // 16 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, std::pair{128u, 32u}, std::pair{128u, 32u}, // 17 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, std::pair{128u, 32u}, std::pair{128u, 32u}, // 18 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 19 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 1A +}; + +constexpr std::array macro_tile_extents_alt_x8{ + std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, // 00 + std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, // 01 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 02 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 03 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 04 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 05 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 06 + std::pair{128u, 64u}, std::pair{128u, 32u}, std::pair{128u, 32u}, std::pair{128u, 32u}, std::pair{128u, 32u}, // 07 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 08 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 09 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 0A + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, std::pair{128u, 32u}, // 0B + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, std::pair{128u, 32u}, // 0C + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 0D + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 0E + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 0F + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, std::pair{128u, 32u}, // 10 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, std::pair{128u, 32u}, // 11 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, std::pair{128u, 32u}, // 12 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 13 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 14 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 15 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, std::pair{128u, 32u}, std::pair{128u, 32u}, // 16 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, std::pair{128u, 32u}, std::pair{128u, 32u}, // 17 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 32u}, std::pair{128u, 32u}, std::pair{128u, 32u}, // 18 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 19 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 1A +}; + +constexpr std::array macro_tile_extents{ + macro_tile_extents_x1, + macro_tile_extents_x2, + macro_tile_extents_x4, + macro_tile_extents_x8, +}; + +constexpr std::array macro_tile_extents_alt{ + macro_tile_extents_alt_x1, + macro_tile_extents_alt_x2, + macro_tile_extents_alt_x4, + macro_tile_extents_alt_x8, +}; +// clang-format on + +constexpr std::pair micro_tile_extent{8u, 8u}; +constexpr auto hw_pipe_interleave = 256u; + +constexpr std::pair GetMacroTileExtents(u32 tiling_idx, u32 bpp, u32 num_samples, + bool alt) { + ASSERT(num_samples <= 8); + const auto samples_log = static_cast(std::log2(num_samples)); + const auto row = tiling_idx * 5; + const auto column = std::bit_width(bpp) - 4; // bpps are 8, 16, 32, 64, 128 + return (alt ? macro_tile_extents_alt : macro_tile_extents)[samples_log][row + column]; +} + +constexpr std::pair ImageSizeLinearAligned(u32 pitch, u32 height, u32 bpp, + u32 num_samples) { + const auto pitch_align = std::max(8u, 64u / ((bpp + 7) / 8)); + auto pitch_aligned = (pitch + pitch_align - 1) & ~(pitch_align - 1); + const auto height_aligned = height; + size_t log_sz = pitch_aligned * height_aligned * num_samples; + const auto slice_align = std::max(64u, 256u / ((bpp + 7) / 8)); + while (log_sz % slice_align) { + pitch_aligned += pitch_align; + log_sz = pitch_aligned * height_aligned * num_samples; + } + return {pitch_aligned, (log_sz * bpp + 7) / 8}; +} + +constexpr std::pair ImageSizeMicroTiled(u32 pitch, u32 height, u32 bpp, + u32 num_samples) { + const auto& [pitch_align, height_align] = micro_tile_extent; + auto pitch_aligned = (pitch + pitch_align - 1) & ~(pitch_align - 1); + const auto height_aligned = (height + height_align - 1) & ~(height_align - 1); + size_t log_sz = (pitch_aligned * height_aligned * bpp * num_samples + 7) / 8; + while (log_sz % 256) { + pitch_aligned += 8; + log_sz = (pitch_aligned * height_aligned * bpp * num_samples + 7) / 8; + } + return {pitch_aligned, log_sz}; +} + +constexpr std::pair ImageSizeMacroTiled(u32 pitch, u32 height, u32 bpp, + u32 num_samples, u32 tiling_idx, u32 mip_n, + bool alt) { + const auto& [pitch_align, height_align] = + GetMacroTileExtents(tiling_idx, bpp, num_samples, alt); + ASSERT(pitch_align != 0 && height_align != 0); + bool downgrade_to_micro = false; + if (mip_n > 0) { + const bool is_less_than_tile = pitch < pitch_align || height < height_align; + // TODO: threshold check + downgrade_to_micro = is_less_than_tile; + } + + if (downgrade_to_micro) { + return ImageSizeMicroTiled(pitch, height, bpp, num_samples); + } + + const auto pitch_aligned = (pitch + pitch_align - 1) & ~(pitch_align - 1); + const auto height_aligned = (height + height_align - 1) & ~(height_align - 1); + const auto log_sz = pitch_aligned * height_aligned * num_samples; + return {pitch_aligned, (log_sz * bpp + 7) / 8}; +} + +} // namespace VideoCore From f42b8acf478f1bfddf212bb42c524b0ba1f84226 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sat, 4 Jan 2025 04:33:07 -0800 Subject: [PATCH 353/549] sdl_audio: Remove buffer samples hint. (#2038) --- src/core/libraries/audio/sdl_audio.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/core/libraries/audio/sdl_audio.cpp b/src/core/libraries/audio/sdl_audio.cpp index 762a9f682..9aee2b447 100644 --- a/src/core/libraries/audio/sdl_audio.cpp +++ b/src/core/libraries/audio/sdl_audio.cpp @@ -15,13 +15,6 @@ class SDLPortBackend : public PortBackend { public: explicit SDLPortBackend(const PortOut& port) : frame_size(port.format_info.FrameSize()), guest_buffer_size(port.BufferSize()) { - // We want the latency for delivering frames out to be as small as possible, - // so set the sample frames hint to the number of frames per buffer. - const auto samples_num_str = std::to_string(port.buffer_frames); - if (!SDL_SetHint(SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES, samples_num_str.c_str())) { - LOG_WARNING(Lib_AudioOut, "Failed to set SDL audio sample frames hint to {}: {}", - samples_num_str, SDL_GetError()); - } const SDL_AudioSpec fmt = { .format = port.format_info.is_float ? SDL_AUDIO_F32LE : SDL_AUDIO_S16LE, .channels = port.format_info.num_channels, From f2f24bb2cd4360eaf05f2245fbc15198b4160741 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quang=20Ng=C3=B4?= Date: Sat, 4 Jan 2025 19:33:23 +0700 Subject: [PATCH 354/549] input: Add missing poll lock (#2044) --- src/input/controller.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/input/controller.cpp b/src/input/controller.cpp index daef9c940..366d80f8f 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -266,6 +266,7 @@ void GameController::TryOpenSDLController() { } u32 GameController::Poll() { + std::scoped_lock lock{m_mutex}; if (m_connected) { auto time = Libraries::Kernel::sceKernelGetProcessTime(); if (m_states_num == 0) { From 7459d9c333a0011003212948b06a3cac396e3682 Mon Sep 17 00:00:00 2001 From: psucien Date: Sat, 4 Jan 2025 22:23:12 +0100 Subject: [PATCH 355/549] hot-fix: amdgpu: use different indirect dispatch packet on ASC --- src/core/libraries/gnmdriver/gnmdriver.cpp | 13 ++++++++++--- src/core/libraries/gnmdriver/gnmdriver.h | 2 +- src/video_core/amdgpu/liverpool.cpp | 8 ++++---- src/video_core/amdgpu/pm4_cmds.h | 12 ++++++++++++ 4 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index f93b3dbf0..fdc3a1acd 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -383,9 +383,16 @@ s32 PS4_SYSV_ABI sceGnmDispatchIndirect(u32* cmdbuf, u32 size, u32 data_offset, return -1; } -int PS4_SYSV_ABI sceGnmDispatchIndirectOnMec() { - LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceGnmDispatchIndirectOnMec(u32* cmdbuf, u32 size, VAddr args, u32 modifier) { + if (cmdbuf != nullptr && size == 8 && args != 0 && ((args & 3u) == 0)) { + cmdbuf[0] = 0xc0021602 | (modifier & 1u); + *(VAddr*)(&cmdbuf[1]) = args; + cmdbuf[3] = (modifier & 0x18) | 1u; + cmdbuf[4] = 0xc0021000; + cmdbuf[5] = 0; + return ORBIS_OK; + } + return ORBIS_FAIL; } u32 PS4_SYSV_ABI sceGnmDispatchInitDefaultHardwareState(u32* cmdbuf, u32 size) { diff --git a/src/core/libraries/gnmdriver/gnmdriver.h b/src/core/libraries/gnmdriver/gnmdriver.h index d15483323..609e26c0d 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.h +++ b/src/core/libraries/gnmdriver/gnmdriver.h @@ -39,7 +39,7 @@ int PS4_SYSV_ABI sceGnmDisableMipStatsReport(); s32 PS4_SYSV_ABI sceGnmDispatchDirect(u32* cmdbuf, u32 size, u32 threads_x, u32 threads_y, u32 threads_z, u32 flags); s32 PS4_SYSV_ABI sceGnmDispatchIndirect(u32* cmdbuf, u32 size, u32 data_offset, u32 flags); -int PS4_SYSV_ABI sceGnmDispatchIndirectOnMec(); +s32 PS4_SYSV_ABI sceGnmDispatchIndirectOnMec(u32* cmdbuf, u32 size, VAddr args, u32 modifier); u32 PS4_SYSV_ABI sceGnmDispatchInitDefaultHardwareState(u32* cmdbuf, u32 size); s32 PS4_SYSV_ABI sceGnmDrawIndex(u32* cmdbuf, u32 size, u32 index_count, uintptr_t index_addr, u32 flags, u32 type); diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index 985f3c652..a59d4a64d 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -823,10 +823,10 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, u32 vqid) { break; } case PM4ItOpcode::DispatchIndirect: { - const auto* dispatch_indirect = reinterpret_cast(header); + const auto* dispatch_indirect = + reinterpret_cast(header); auto& cs_program = GetCsRegs(); - const auto offset = dispatch_indirect->data_offset; - const auto ib_address = mapped_queues[vqid].indirect_args_addr; + const auto ib_address = dispatch_indirect->Address(); const auto size = sizeof(PM4CmdDispatchIndirect::GroupDimensions); if (DebugState.DumpingCurrentReg()) { DebugState.PushRegsDumpCompute(base_addr, reinterpret_cast(header), @@ -835,7 +835,7 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, u32 vqid) { if (rasterizer && (cs_program.dispatch_initiator & 1)) { const auto cmd_address = reinterpret_cast(header); rasterizer->ScopeMarkerBegin(fmt::format("acb[{}]:{}:Dispatch", vqid, cmd_address)); - rasterizer->DispatchIndirect(ib_address, offset, size); + rasterizer->DispatchIndirect(ib_address, 0, size); rasterizer->ScopeMarkerEnd(); } break; diff --git a/src/video_core/amdgpu/pm4_cmds.h b/src/video_core/amdgpu/pm4_cmds.h index e7d6b29e5..135415458 100644 --- a/src/video_core/amdgpu/pm4_cmds.h +++ b/src/video_core/amdgpu/pm4_cmds.h @@ -796,6 +796,18 @@ struct PM4CmdDispatchIndirect { u32 dispatch_initiator; ///< Dispatch Initiator Register }; +struct PM4CmdDispatchIndirectMec { + PM4Type3Header header; + u32 address0; + u32 address1; + u32 dispatch_initiator; ///< Dispatch Initiator Register + + template + T Address() const { + return reinterpret_cast(address0 | (u64(address1 & 0xffff) << 32u)); + } +}; + struct DrawIndirectArgs { u32 vertex_count_per_instance; u32 instance_count; From 9d3143231cf64b06515563172105893e4d5c659a Mon Sep 17 00:00:00 2001 From: psucien Date: Sat, 4 Jan 2025 22:44:46 +0100 Subject: [PATCH 356/549] macOS build fixed; `indirect_args_addr` moved out from queues context --- src/video_core/amdgpu/liverpool.cpp | 18 +++++++----------- src/video_core/amdgpu/liverpool.h | 3 ++- src/video_core/amdgpu/pm4_cmds.h | 2 +- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index a59d4a64d..16ed84f74 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -454,7 +454,6 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); const auto offset = draw_indirect->data_offset; - const auto ib_address = mapped_queues[GfxQueueId].indirect_args_addr; const auto size = sizeof(DrawIndirectArgs); if (DebugState.DumpingCurrentReg()) { DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); @@ -462,7 +461,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:DrawIndirect", cmd_address)); - rasterizer->DrawIndirect(false, ib_address, offset, size, 1, 0); + rasterizer->DrawIndirect(false, indirect_args_addr, offset, size, 1, 0); rasterizer->ScopeMarkerEnd(); } break; @@ -471,7 +470,6 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); const auto offset = draw_index_indirect->data_offset; - const auto ib_address = mapped_queues[GfxQueueId].indirect_args_addr; const auto size = sizeof(DrawIndexedIndirectArgs); if (DebugState.DumpingCurrentReg()) { DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); @@ -480,7 +478,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); rasterizer->ScopeMarkerBegin( fmt::format("dcb:{}:DrawIndexIndirect", cmd_address)); - rasterizer->DrawIndirect(true, ib_address, offset, size, 1, 0); + rasterizer->DrawIndirect(true, indirect_args_addr, offset, size, 1, 0); rasterizer->ScopeMarkerEnd(); } break; @@ -489,7 +487,6 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); const auto offset = draw_index_indirect->data_offset; - const auto ib_address = mapped_queues[GfxQueueId].indirect_args_addr; if (DebugState.DumpingCurrentReg()) { DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); } @@ -497,9 +494,9 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); rasterizer->ScopeMarkerBegin( fmt::format("dcb:{}:DrawIndexIndirectCountMulti", cmd_address)); - rasterizer->DrawIndirect(true, ib_address, offset, draw_index_indirect->stride, - draw_index_indirect->count, - draw_index_indirect->countAddr); + rasterizer->DrawIndirect( + true, indirect_args_addr, offset, draw_index_indirect->stride, + draw_index_indirect->count, draw_index_indirect->countAddr); rasterizer->ScopeMarkerEnd(); } break; @@ -528,7 +525,6 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); auto& cs_program = GetCsRegs(); const auto offset = dispatch_indirect->data_offset; - const auto ib_address = mapped_queues[GfxQueueId].indirect_args_addr; const auto size = sizeof(PM4CmdDispatchIndirect::GroupDimensions); if (DebugState.DumpingCurrentReg()) { DebugState.PushRegsDumpCompute(base_addr, reinterpret_cast(header), @@ -538,7 +534,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); rasterizer->ScopeMarkerBegin( fmt::format("dcb:{}:DispatchIndirect", cmd_address)); - rasterizer->DispatchIndirect(ib_address, offset, size); + rasterizer->DispatchIndirect(indirect_args_addr, offset, size); rasterizer->ScopeMarkerEnd(); } break; @@ -562,7 +558,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); ASSERT(set_base->base_index == PM4CmdSetBase::BaseIndex::DrawIndexIndirPatchTable); - mapped_queues[GfxQueueId].indirect_args_addr = set_base->Address(); + indirect_args_addr = set_base->Address(); break; } case PM4ItOpcode::EventWrite: { diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index 85ea4a75c..0f1783057 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -1479,11 +1479,12 @@ private: std::vector ccb_buffer; std::queue submits{}; ComputeProgram cs_state{}; - VAddr indirect_args_addr{}; }; std::array mapped_queues{}; u32 num_mapped_queues{1u}; // GFX is always available + VAddr indirect_args_addr{}; + struct ConstantEngine { void Reset() { ce_count = 0; diff --git a/src/video_core/amdgpu/pm4_cmds.h b/src/video_core/amdgpu/pm4_cmds.h index 135415458..311f4d4d0 100644 --- a/src/video_core/amdgpu/pm4_cmds.h +++ b/src/video_core/amdgpu/pm4_cmds.h @@ -804,7 +804,7 @@ struct PM4CmdDispatchIndirectMec { template T Address() const { - return reinterpret_cast(address0 | (u64(address1 & 0xffff) << 32u)); + return std::bit_cast(address0 | (u64(address1 & 0xffff) << 32u)); } }; From 79663789bdac6d40f0ecb69981f84eba53411ac4 Mon Sep 17 00:00:00 2001 From: Mahmoud Adel <94652220+AboMedoz@users.noreply.github.com> Date: Sun, 5 Jan 2025 00:02:37 +0200 Subject: [PATCH 357/549] bump up vector size to 64 in image_info and image_binding (#2055) solves ```boost::bad_alloc``` error when compiling shaders --- src/video_core/renderer_vulkan/vk_rasterizer.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 1bbb90b6c..2905c5ddb 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -108,7 +108,7 @@ private: std::pair, 8> cb_descs; std::optional> db_desc; - boost::container::static_vector image_infos; + boost::container::static_vector image_infos; boost::container::static_vector buffer_views; boost::container::static_vector buffer_infos; boost::container::static_vector bound_images; @@ -121,7 +121,7 @@ private: using TexBufferBindingInfo = std::pair; boost::container::static_vector texbuffer_bindings; using ImageBindingInfo = std::pair; - boost::container::static_vector image_bindings; + boost::container::static_vector image_bindings; }; } // namespace Vulkan From 8f33dfe4f17c6f4616bf273f615d5a42a76380cf Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Sun, 5 Jan 2025 14:10:23 +0300 Subject: [PATCH 358/549] infra: require the log to be attched in template --- .github/ISSUE_TEMPLATE/game-bug-report.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/game-bug-report.yaml b/.github/ISSUE_TEMPLATE/game-bug-report.yaml index 2d984b697..a9c669ff9 100644 --- a/.github/ISSUE_TEMPLATE/game-bug-report.yaml +++ b/.github/ISSUE_TEMPLATE/game-bug-report.yaml @@ -89,7 +89,7 @@ body: - type: textarea id: logs attributes: - label: "Logs" - description: Attach any logs here. Log can be found by right clicking on a game name -> Open Folder... -> Open Log Folder. Make sure that the log type is set to `sync`. + label: "Log File" + description: Drag and drop the log file here. It can be found by right clicking on a game name -> Open Folder... -> Open Log Folder. Make sure that the log type is set to `sync`. validations: - required: false + required: true From c0f57df4e67c41de1939fa6d505a12561c0c51bf Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sun, 5 Jan 2025 14:45:54 -0800 Subject: [PATCH 359/549] vk_instance: Enable additional debug tagging if crash diagnostics is enabled. (#2066) --- src/video_core/renderer_vulkan/vk_instance.cpp | 6 ++++-- src/video_core/renderer_vulkan/vk_instance.h | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 790e76400..9bc627830 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -92,13 +92,15 @@ std::string GetReadableVersion(u32 version) { Instance::Instance(bool enable_validation, bool enable_crash_diagnostic) : instance{CreateInstance(Frontend::WindowSystemType::Headless, enable_validation, enable_crash_diagnostic)}, - physical_devices{EnumeratePhysicalDevices(instance)} {} + physical_devices{EnumeratePhysicalDevices(instance)}, + crash_diagnostic{enable_crash_diagnostic} {} Instance::Instance(Frontend::WindowSDL& window, s32 physical_device_index, bool enable_validation /*= false*/, bool enable_crash_diagnostic /*= false*/) : instance{CreateInstance(window.GetWindowInfo().type, enable_validation, enable_crash_diagnostic)}, - physical_devices{EnumeratePhysicalDevices(instance)} { + physical_devices{EnumeratePhysicalDevices(instance)}, + crash_diagnostic{enable_crash_diagnostic} { if (enable_validation) { debug_callback = CreateDebugCallback(*instance); } diff --git a/src/video_core/renderer_vulkan/vk_instance.h b/src/video_core/renderer_vulkan/vk_instance.h index 62838140c..4e091824d 100644 --- a/src/video_core/renderer_vulkan/vk_instance.h +++ b/src/video_core/renderer_vulkan/vk_instance.h @@ -81,7 +81,7 @@ public: /// Returns true when a known debugging tool is attached. bool HasDebuggingToolAttached() const { - return has_renderdoc || has_nsight_graphics; + return crash_diagnostic || has_renderdoc || has_nsight_graphics; } /// Returns true if anisotropic filtering is supported @@ -338,6 +338,7 @@ private: u32 subgroup_size{}; bool tooling_info{}; bool debug_utils_supported{}; + bool crash_diagnostic{}; bool has_nsight_graphics{}; bool has_renderdoc{}; }; From 4dcd7f027130db873f268de7b1ab80787046cc03 Mon Sep 17 00:00:00 2001 From: Bettehem Date: Mon, 6 Jan 2025 00:46:08 +0200 Subject: [PATCH 360/549] translation: Update Finnish translation (#2057) --- src/qt_gui/translations/fi.ts | 444 +++++++++++++++++----------------- 1 file changed, 222 insertions(+), 222 deletions(-) diff --git a/src/qt_gui/translations/fi.ts b/src/qt_gui/translations/fi.ts index cdf331796..99c1de67e 100644 --- a/src/qt_gui/translations/fi.ts +++ b/src/qt_gui/translations/fi.ts @@ -8,7 +8,7 @@ About shadPS4 - About shadPS4 + Tietoa shadPS4:sta @@ -18,12 +18,12 @@ shadPS4 is an experimental open-source emulator for the PlayStation 4. - shadPS4 is an experimental open-source emulator for the PlayStation 4. + shadPS4 on kokeellinen avoimen lähdekoodin PlayStation 4 emulaattori. This software should not be used to play games you have not legally obtained. - This software should not be used to play games you have not legally obtained. + Tätä ohjelmistoa ei saa käyttää pelien pelaamiseen, joita et ole hankkinut laillisesti. @@ -31,7 +31,7 @@ Open Folder - Open Folder + Avaa Hakemisto @@ -39,17 +39,17 @@ Loading game list, please wait :3 - Loading game list, please wait :3 + Ole hyvä ja odota, ladataan pelilistaa :3 Cancel - Cancel + Peruuta Loading... - Loading... + Ladataan... @@ -57,12 +57,12 @@ shadPS4 - Choose directory - shadPS4 - Choose directory + shadPS4 - Valitse hakemisto Select which directory you want to install to. - Select which directory you want to install to. + Valitse, mihin hakemistoon haluat asentaa. @@ -70,27 +70,27 @@ shadPS4 - Choose directory - shadPS4 - Choose directory + shadPS4 - Valitse hakemisto Directory to install games - Directory to install games + Pelien asennushakemisto Browse - Browse + Selaa Error - Error + Virhe The value for location to install games is not valid. - The value for location to install games is not valid. + Peliasennushakemiston sijainti on virheellinen. @@ -98,7 +98,7 @@ Create Shortcut - Create Shortcut + Luo Pikakuvake @@ -108,157 +108,157 @@ SFO Viewer - SFO Viewer + SFO Selain Trophy Viewer - Trophy Viewer + Trophy Selain Open Folder... - Avaa Kansio... + Avaa Hakemisto... Open Game Folder - Avaa Pelikansio + Avaa Pelihakemisto Open Save Data Folder - Avaa Tallennustiedostokansio + Avaa Tallennustiedostohakemisto Open Log Folder - Avaa Lokikansio + Avaa Lokihakemisto Copy info... - Copy info... + Kopioi tietoja... Copy Name - Copy Name + Kopioi Nimi Copy Serial - Copy Serial + Kopioi Sarjanumero Copy All - Copy All + Kopioi kaikki Delete... - Delete... + Poista... Delete Game - Delete Game + Poista Peli Delete Update - Delete Update + Poista Päivitys Delete DLC - Delete DLC + Poista Lisäsisältö Compatibility... - Compatibility... + Yhteensopivuus... Update database - Update database + Päivitä tietokanta View report - View report + Näytä raportti Submit a report - Submit a report + Tee raportti Shortcut creation - Shortcut creation + Pikakuvakkeen luonti Shortcut created successfully! - Shortcut created successfully! + Pikakuvake luotu onnistuneesti! Error - Error + Virhe Error creating shortcut! - Error creating shortcut! + Virhe pikakuvakkeen luonnissa! Install PKG - Install PKG + Asenna PKG Game - Game + Peli requiresEnableSeparateUpdateFolder_MSG - This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + Tämä ominaisuus vaatii, että 'Ota käyttöön erillinen päivityshakemisto' -asetus on päällä. Jos haluat käyttää tätä ominaisuutta, laita se asetus päälle. This game has no update to delete! - This game has no update to delete! + Tällä pelillä ei ole poistettavaa päivitystä! Update - Update + Päivitä This game has no DLC to delete! - This game has no DLC to delete! + Tällä pelillä ei ole poistettavaa lisäsisältöä! DLC - DLC + Lisäsisältö Delete %1 - Delete %1 + Poista %1 Are you sure you want to delete %1's %2 directory? - Are you sure you want to delete %1's %2 directory? + Haluatko varmasti poistaa %1n %2hakemiston? @@ -266,107 +266,107 @@ Open/Add Elf Folder - Open/Add Elf Folder + Avaa/Lisää Elf Hakemisto Install Packages (PKG) - Install Packages (PKG) + Asenna Paketteja (PKG) Boot Game - Boot Game + Käynnistä Peli Check for Updates - Tarkista päivitykset + Tarkista Päivitykset About shadPS4 - About shadPS4 + Tietoa shadPS4:sta Configure... - Configure... + Asetukset... Install application from a .pkg file - Install application from a .pkg file + Asenna sovellus .pkg tiedostosta Recent Games - Recent Games + Viimeisimmät Pelit Exit - Exit + Sulje Exit shadPS4 - Exit shadPS4 + Sulje shadPS4 Exit the application. - Exit the application. + Sulje sovellus. Show Game List - Show Game List + Avaa pelilista Game List Refresh - Game List Refresh + Päivitä pelilista Tiny - Tiny + Hyvin pieni Small - Small + Pieni Medium - Medium + Keskikokoinen Large - Large + Suuri List View - List View + Listanäkymä Grid View - Grid View + Ruudukkonäkymä Elf Viewer - Elf Viewer + Elf Selain Game Install Directory - Game Install Directory + Peliasennushakemisto @@ -376,52 +376,52 @@ Dump Game List - Dump Game List + Kirjoita Pelilista Tiedostoon PKG Viewer - PKG Viewer + PKG Selain Search... - Search... + Hae... File - File + Tiedosto View - View + Näkymä Game List Icons - Game List Icons + Pelilistan Ikonit Game List Mode - Game List Mode + Pelilistamuoto Settings - Settings + Asetukset Utils - Utils + Työkalut Themes - Themes + Teemat @@ -431,32 +431,32 @@ Dark - Dark + Tumma Light - Light + Vaalea Green - Green + Vihreä Blue - Blue + Sininen Violet - Violet + Violetti toolBar - toolBar + Työkalupalkki @@ -464,7 +464,7 @@ Open Folder - Open Folder + Avaa Hakemisto @@ -472,7 +472,7 @@ Trophy Viewer - Trophy Viewer + Trophy Selain @@ -480,52 +480,52 @@ Settings - Settings + Asetukset General - General + Yleinen System - System + Järjestelmä Console Language - Console Language + Konsolin Kieli Emulator Language - Emulator Language + Emulaattorin Kieli Emulator - Emulator + Emulaattori Enable Fullscreen - Enable Fullscreen + Ota Käyttöön Koko Ruudun Tila Enable Separate Update Folder - Enable Separate Update Folder + Ota Käyttöön Erillinen Päivityshakemisto Show Splash - Show Splash + Näytä Aloitusnäyttö Is PS4 Pro - Is PS4 Pro + On PS4 Pro @@ -535,12 +535,12 @@ Username - Username + Käyttäjänimi Trophy Key - Trophy Key + Trophy Avain @@ -550,17 +550,17 @@ Logger - Logger + Lokinkerääjä Log Type - Log Type + Lokin Tyyppi Log Filter - Log Filter + Lokisuodatin @@ -575,12 +575,12 @@ Hide Cursor - Piilota kursor + Piilota Kursori Hide Cursor Idle Timeout - Inaktiivisuuden aikaraja kursorin piilottamiselle + Inaktiivisuuden Aikaraja Kursorin Piilottamiseen @@ -595,47 +595,47 @@ Back Button Behavior - Takaisin-painikkeen käyttäytyminen + Takaisin-painikkeen Käyttäytyminen Graphics - Graphics + Grafiikka Graphics Device - Graphics Device + Näytönohjain Width - Width + Leveys Height - Height + Korkeus Vblank Divider - Vblank Divider + Vblank jakaja Advanced - Advanced + Lisäasetukset Enable Shaders Dumping - Enable Shaders Dumping + Ota Käyttöön Varjostinvedokset Enable NULL GPU - Enable NULL GPU + Ota Käyttöön NULL GPU @@ -660,27 +660,27 @@ Debug - Debug + Virheenkorjaus Enable Debug Dumping - Enable Debug Dumping + Ota Käyttöön Virheenkorjausvedokset Enable Vulkan Validation Layers - Enable Vulkan Validation Layers + Ota Käyttöön Vulkan-validointikerrokset Enable Vulkan Synchronization Validation - Enable Vulkan Synchronization Validation + Ota Käyttöön Vulkan-synkronointivalidointi Enable RenderDoc Debugging - Enable RenderDoc Debugging + Ota Käyttöön RenderDoc Virheenkorjaus @@ -690,7 +690,7 @@ Check for Updates at Startup - Tarkista päivitykset alussa + Tarkista Päivitykset Käynnistäessä @@ -700,42 +700,42 @@ Check for Updates - Tarkista päivitykset + Tarkista Päivitykset GUI Settings - GUI-Asetukset + GUI-asetukset Disable Trophy Pop-ups - Disable Trophy Pop-ups + Poista Trophy Pop-upit Käytöstä Play title music - Soita otsikkomusiikkia + Soita Otsikkomusiikkia Update Compatibility Database On Startup - Update Compatibility Database On Startup + Päivitä Yhteensopivuustietokanta Käynnistäessä Game Compatibility - Game Compatibility + Peliyhteensopivuus Display Compatibility Data - Display Compatibility Data + Näytä Yhteensopivuustiedot Update Compatibility Database - Update Compatibility Database + Päivitä Yhteensopivuustietokanta @@ -745,7 +745,7 @@ Audio Backend - Audio Backend + Äänijärjestelmä @@ -758,22 +758,22 @@ * Unsupported Vulkan Version - * Tuettu Vulkan-versio + * Ei Tuettu Vulkan-versio Download Cheats For All Installed Games - Lataa huijaukset kaikille asennetuille peleille + Lataa Huijaukset Kaikille Asennetuille Peleille Download Patches For All Games - Lataa korjaukset kaikille peleille + Lataa Paikkaukset Kaikille Peleille Download Complete - Lataus valmis + Lataus Valmis @@ -783,12 +783,12 @@ Patches Downloaded Successfully! - Korjaukset ladattu onnistuneesti! + Paikkaukset Ladattu Onnistuneesti! All Patches available for all games have been downloaded. - Kaikki saatavilla olevat korjaukset kaikille peleille on ladattu. + Kaikki saatavilla olevat Paikkaukset kaikille peleille on ladattu. @@ -808,12 +808,12 @@ Game Boot - Pelin käynnistys + Pelin Käynnistys Only one file can be selected! - Vain yksi tiedosto voidaan valita! + Vain yksi tiedosto voi olla valittuna! @@ -848,22 +848,22 @@ Would you like to install Patch: - Haluatko asentaa päivityksen: + Haluatko asentaa Päivityksen: DLC Installation - DLC-asennus + Lisäsisällön asennus Would you like to install DLC: %1? - Haluatko asentaa DLC:n: %1? + Haluatko asentaa lisäsisällön: %1? DLC already installed: - DLC on jo asennettu: + Lisäsisältö on jo asennettu: @@ -873,7 +873,7 @@ PKG is a patch, please install the game first! - PKG on korjaus, asenna peli ensin! + PKG on päivitys, asenna peli ensin! @@ -888,7 +888,7 @@ Extraction Finished - Purku valmis + Purku Valmis @@ -906,12 +906,12 @@ Cheats / Patches for - Cheats / Patches for + Huijaukset / Paikkaukset pelille defaultTextEdit_MSG - Cheats/Patches ovat kokeellisia.\nKäytä varoen.\n\nLataa cheats yksitellen valitsemalla repositorio ja napsauttamalla latauspainiketta.\nPatches-välilehdellä voit ladata kaikki patchit kerralla, valita, mitä haluat käyttää, ja tallentaa valinnan.\n\nKoska emme kehitä Cheats/Patches,\nilmoita ongelmista cheatin tekijälle.\n\nLuo uusi cheat? Käy osoitteessa:\nhttps://github.com/shadps4-emu/ps4_cheats + Huijaukset/Paikkaukset ovat kokeellisia.\nKäytä varoen.\n\nLataa huijaukset yksitellen valitsemalla repositorion ja napsauttamalla latauspainiketta.\nPaikkaukset-välilehdessä voit ladata kaikki paikkaukset kerralla, valita, mitä haluat käyttää ja tallentaa valinnan.\n\nKoska me emme kehitä Huijauksia/Paikkauksia,\nole hyvä ja ilmoita ongelmista huijauksen tekijälle.\n\nLoitko uuden huijauksen? Käy osoitteessa:\nhttps://github.com/shadps4-emu/ps4_cheats @@ -936,27 +936,27 @@ Select Cheat File: - Valitse huijaustiedosto: + Valitse Huijaustiedosto: Repository: - Repo: + Repositorio: Download Cheats - Lataa huijaukset + Lataa Huijaukset Delete File - Poista tiedosto + Poista Tiedosto No files selected. - Ei tiedostoja valittu. + Tiedostoja ei ole valittuna. @@ -971,12 +971,12 @@ Select Patch File: - Valitse korjaustiedosto: + Valitse Paikkaustiedosto: Download Patches - Lataa korjaukset + Lataa Paikkaukset @@ -991,7 +991,7 @@ Patches - Korjaukset + Paikkaukset @@ -1001,7 +1001,7 @@ No patch selected. - Ei korjausta valittu. + Paikkausta ei ole valittuna. @@ -1011,7 +1011,7 @@ No patch file found for the current serial. - Nykyiselle sarjanumerolle ei löytynyt korjaustiedostoa. + Nykyiselle sarjanumerolle ei löytynyt paikkaustiedostoa. @@ -1031,7 +1031,7 @@ Success - Onnistui + Onnistuminen @@ -1041,7 +1041,7 @@ Invalid Source - Virheellinen lähde + Virheellinen Lähde @@ -1051,7 +1051,7 @@ File Exists - Tiedosto on olemassa + Olemassaoleva Tiedosto @@ -1071,22 +1071,22 @@ Cheats Not Found - Huijauksia ei löytynyt + Huijauksia Ei Löytynyt CheatsNotFound_MSG - Huijauksia ei löytynyt tälle pelille tämän version valitusta repositoriosta, yritä toista repositoriota tai pelin eri versiota. + Huijauksia ei löytynyt tälle pelin versiolle valitusta repositoriosta. Kokeile toista repositoriota tai eri versiota pelistä. Cheats Downloaded Successfully - Huijaukset ladattu onnistuneesti + Huijaukset Ladattu Onnistuneesti CheatsDownloadedSuccessfully_MSG - Olet ladannut huijaukset onnistuneesti valitusta repositoriosta tälle pelin versiolle. Voit yrittää ladata toisesta repositoriosta, jos se on saatavilla, voit myös käyttää sitä valitsemalla tiedoston luettelosta. + Olet ladannut huijaukset onnistuneesti valitusta repositoriosta tälle pelin versiolle. Voit yrittää ladata toisesta repositoriosta. Jos se on saatavilla, voit myös käyttää sitä valitsemalla tiedoston listasta. @@ -1096,7 +1096,7 @@ Failed to download: - Lataaminen epäonnistui: + Lataus epäonnistui: @@ -1106,7 +1106,7 @@ DownloadComplete_MSG - Korjaukset ladattu onnistuneesti! Kaikki saatavilla olevat korjaukset kaikille peleille on ladattu, eikä niitä tarvitse ladata yksittäin jokaiselle pelille kuten huijauksissa. Jos päivitystä ei näy, se saattaa olla, että sitä ei ole saatavilla tietylle sarjanumerolle ja peliversiolle. + Paikkaukset ladattu onnistuneesti! Kaikki saatavilla olevat paikkaukset kaikille peleille on ladattu, eikä niitä tarvitse ladata yksittäin jokaiselle pelille, kuten huijausten kohdalla. Jos paikkausta ei näy, saattaa olla, että sitä ei ole saatavilla kyseiselle sarjanumerolle ja peliversiolle. @@ -1126,12 +1126,12 @@ The downloaded patch only works on version: %1 - ladattu päivitys toimii vain versiossa: %1 + Ladattu paikkaus toimii vain versiossa: %1 You may need to update your game. - Sinun on ehkä päivitettävä peliäsi. + Sinun on ehkä päivitettävä pelisi. @@ -1161,7 +1161,7 @@ Directory does not exist: - Kansiota ei ole olemassa: + Hakemistoa ei ole olemassa: @@ -1176,7 +1176,7 @@ Can't apply cheats before the game is started - Ei voi käyttää huijauksia ennen kuin peli on aloitettu. + Huijauksia ei voi käyttää ennen kuin peli on käynnissä. @@ -1189,12 +1189,12 @@ Apply - Ota käyttöön + Ota Käyttöön Restore Defaults - Palauta oletukset + Palauta Oletukset @@ -1204,12 +1204,12 @@ Point your mouse at an option to display its description. - Siirrä hiiri vaihtoehdon päälle näyttämään sen kuvaus. + Siirrä hiiri vaihtoehdon päälle näyttääksesi sen kuvauksen. consoleLanguageGroupBox - Konsoli Kieli:\nAseta PS4 pelin käyttämä kieli.\nOn suositeltavaa asettaa tämä kieleksi, jota peli tukee, mikä vaihtelee alueittain. + Konsolin Kieli:\nAseta PS4-pelin käyttämä kieli.\nOn suositeltavaa asettaa tämä kieleksi, jota peli tukee, mikä vaihtelee alueittain. @@ -1219,22 +1219,22 @@ fullscreenCheckBox - Ota Täysikokoisuus käyttöön:\nSiirtää pelin ikkunan automaattisesti täysikokoiseen tilaan.\nTätä voidaan vaihtaa painamalla F11-näppäintä. + Ota Koko Näytön Tila Käyttöön:\nAvaa pelin ikkunan automaattisesti koko näytön tilassa.\nTilaa voi vaihtaa painamalla F11-näppäintä. separateUpdatesCheckBox - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + Ota Käyttöön Erillinen Päivityskansio:\nOttaa käyttöön päivitysten asennuksen erilliseen kansioon helpottamaan niiden hallintaa.\nTämä on tehtävissä manuaalisesti lisäämällä puretun päivityksen pelikansioon "CUSA00000-UPDATE" nimellä, missä CUSA ID vastaa pelin ID:tä. showSplashCheckBox - Näytä Alkunäyttö:\nNäyttää pelin alkunäytön (erityinen kuva) pelin käynnistyessä. + Näytä Aloitusnäyttö:\nNäyttää pelin aloitusnäytön (erityinen kuva) pelin käynnistyessä. ps4proCheckBox - Onko PS4 Pro:\nAsettaa emulaattorin toimimaan PS4 PRO:na, mikä voi mahdollistaa erityisiä ominaisuuksia peleissä, jotka tukevat sitä. + On PS4 Pro:\nAsettaa emulaattorin toimimaan PS4 PRO:na, mikä voi mahdollistaa erityisiä ominaisuuksia peleissä, jotka tukevat sitä. @@ -1244,12 +1244,12 @@ userName - Käyttäjänimi:\nAsettaa PS4-tilin käyttäjänimen, joka voi näkyä joissakin peleissä. + Käyttäjänimi:\nAsettaa PS4-tilin käyttäjänimen, joka voi näkyä joissain peleissä. TrophyKey - Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. + Trophy Avain:\nThrophyjen dekryptoinnissa käytetty avain. Pitää hankkia jailbreakatusta konsolista.\nSaa sisältää vain hex-merkkejä. @@ -1259,12 +1259,12 @@ logFilter - Lokifiltteri:\nSuodattaa lokia tulostamaan vain erityistä tietoa.\nEsimerkkejä: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Tasot: Jälki, Virheenkorjaus, Tieto, Varoitus, Virhe, Kriittinen - tällä järjestyksellä, tietty taso vaientaa kaikki edeltävät tasot luettelossa ja kirjaa kaikki tasot sen jälkeen. + Lokisuodatin:\nSuodattaa lokia tulostamaan vain määrättyä tietoa.\nEsimerkkejä: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical"\nTasot: Trace, Debug, Info, Warning, Error, Critical - tässä järjestyksessä. Valittu taso vaientaa kaikki edeltävät tasot luettelossa ja kirjaa kaikki tasot sen jälkeen. updaterGroupBox - Päivitys:\nRelease: Viralliset versiot, jotka julkaistaan joka kuukausi ja voivat olla hyvin vanhoja, mutta ovat luotettavampia ja testatumpia.\nNightly: Kehitysversiot, joissa on kaikki uusimmat ominaisuudet ja korjaukset, mutta ne voivat sisältää bugeja ja ovat vähemmän vakaita. + Päivitys:\nRelease: Viralliset versiot, jotka julkaistaan kuukausittain ja saattavat olla hyvin vanhoja, mutta ovat luotettavampia ja testatumpia.\nNightly: Kehitysversiot, joissa on kaikki uusimmat ominaisuudet ja korjaukset, mutta ne saattavat sisältää virheitä ja ovat vähemmän vakaita. @@ -1274,17 +1274,17 @@ disableTrophycheckBox - Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + Poista Trophy Pop-upit Käytöstä:\nPoista trophy ilmoitukset pelin aikana. Trophyjen edistystä voi silti seurata Trophy Selainta käyttämällä (klikkaa peliä hiiren oikealla emulaattorin pääikkunassa). hideCursorGroupBox - Piilota kursori:\nValitse, milloin kursori häviää:\nEi koskaan: Näet hiiren aina.\nAktiivinen: Aseta aika, jolloin se häviää oltuaan aktiivinen.\nAina: et koskaan näe hiirtä. + Piilota kursori:\nValitse, milloin kursori häviää:\nEi koskaan: Näet hiiren aina.\nInaktiivinen: Aseta aika, jolloin se häviää oltuaan aktiivinen.\nAina: et koskaan näe hiirtä. idleTimeoutGroupBox - Aseta aika, jolloin hiiri häviää oltuaan aktiivinen. + Aseta aika, milloin hiiri häviää oltuaan aktiivinen. @@ -1294,17 +1294,17 @@ enableCompatibilityCheckBox - Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + Näytä Yhteensopivuustiedot:\nNäyttää pelien yhteensopivuustiedot listanäkymässä. Ota käyttöön "Päivitä Yhteensopivuustietokanta Käynnistäessä" saadaksesi ajantasaista tietoa. checkCompatibilityOnStartupCheckBox - Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + Päivitä Yhteensopivuustiedot Käynnistäessä:\nPäivitä yhteensopivuustiedot automaattisesti shadPS4:n käynnistyessä. updateCompatibilityButton - Update Compatibility Database:\nImmediately update the compatibility database. + Päivitä Yhteensopivuustietokanta:\nPäivitää yhteensopivuustietokannan heti. @@ -1314,27 +1314,27 @@ Idle - Odotustila + Inaktiivinen Always - aina + Aina Touchpad Left - Kosketuslevy Vasemmalla + Kosketuslevyn Vasen Puoli Touchpad Right - Kosketuslevy Oikealla + Kosketuslevyn Oikea Puoli Touchpad Center - Kosketuslevy Keskellä + Kosketuslevyn Keskikohta @@ -1344,62 +1344,62 @@ graphicsAdapterGroupBox - Kuvakortti:\nValitse GPU, jota emulaattori käyttää monigpu-järjestelmissä pudotusvalikosta,\n tai valitse "Auto Select" automaattiseen määrittämiseen. + Näytönohjain:\nUseamman näytönohjaimen järjestelmissä, valitse pudotusvalikosta, mitä näytönohjainta emulaattori käyttää,\n tai valitse "Auto Select" automaattiseen määritykseen. resolutionLayout - Leveys/Korkeus:\nAsettaa emulaattorin ikkunan koon käynnistyksen aikana, jota voidaan muuttaa pelin aikana.\nTämä on eri kuin pelin sisäinen resoluutio. + Leveys/Korkeus:\nAsettaa käynnistetyn emulaattori-ikkunan koon, jota voidaan muuttaa pelin aikana.\nTämä on eri, kuin pelin sisäinen resoluutio. heightDivider - Vblank Jakaja:\nEmulaattorin virkistystaajuus kerrotaan tällä numerolla. Tämän muuttaminen voi vaikuttaa haitallisesti, kuten pelin nopeuden lisääminen tai kriittisten pelitoimintojen rikkoutuminen, jotka eivät odota tämän muuttuvan! + Vblank Jakaja:\nEmulaattorin virkistystaajuus kerrotaan tällä numerolla. Tämän muuttaminen voi vaikuttaa haitallisesti, kuten lisätä pelin nopeutta tai rikkoa kriittisiä pelitoimintoja, jotka eivät odota tämän muuttuvan! dumpShadersCheckBox - Ota Shadersin dumpaus käyttöön:\nTeknistä vianetsintää varten pelin shadereita tallennetaan kansioon niiden renderöinnin aikana. + Ota Käyttöön Varjostinvedokset:\nTeknistä vianetsintää varten. Pelin varjostimia tallennetaan hakemistoon niiden renderöityessä. nullGpuCheckBox - Ota Null GPU käyttöön:\nTeknistä vianetsintää varten pelin renderöinti estetään niin, että ikään kuin grafiikkakorttia ei olisi. + Ota Null GPU käyttöön:\nTeknistä vianetsintää varten. Pelin renderöinti estetään, ikään kuin näytönohjainta ei olisi. gameFoldersBox - Pelihakemistot:\nLuettelo hakemistoista asennettujen pelien tarkistamiseksi. + Pelihakemistot:\nLista hakemistoista, joista pelejä haetaan. addFolderButton - Lisää:\nLisää hakemisto luetteloon. + Lisää:\nLisää hakemisto listalle. removeFolderButton - Poista:\nPoista hakemisto luettelosta. + Poista:\nPoista hakemisto listalta. debugDump - Ota Debug Dumpaus käyttöön:\nTallentaa käynnissä olevan PS4-ohjelman tuonti- ja vientisymbolit ja tiedosto-otsikkotiedot hakemistoon. + Ota Käyttöön Virheenkorjausvedokset:\nTallentaa käynnissä olevan PS4-ohjelman tuonti- ja vientisymbolit ja tiedosto-otsikkotiedot hakemistoon. vkValidationCheckBox - Ota Vulkanin Validointikerrokset käyttöön:\nAktivoi järjestelmä, joka validoi Vulkan-renderöijän tilan ja kirjaa tietoa sen sisäisestä tilasta. Tämä heikentää suorituskykyä ja todennäköisesti muuttaa emulaation käyttäytymistä. + Ota Käyttöön Vulkan-validointikerrokset:\nAktivoi järjestelmä, joka validoi Vulkan-renderöijän tilan ja kirjaa tietoa sen sisäisestä tilasta. Tämä heikentää suorituskykyä ja todennäköisesti muuttaa emulaation käyttäytymistä. vkSyncValidationCheckBox - Ota Vulkanin Synkronointivalaistus käyttöön:\nAktivoi järjestelmä, joka validoi Vulkan-renderöinnin tehtävien aikataulutuksen. Tämä heikentää suorituskykyä ja todennäköisesti muuttaa emulaation käyttäytymistä. + Ota Käyttöön Vulkan-synkronointivalidointi:\nAktivoi järjestelmä, joka validoi Vulkan-renderöinnin tehtävien aikataulutuksen. Tämä heikentää suorituskykyä ja todennäköisesti muuttaa emulaation käyttäytymistä. rdocCheckBox - Ota RenderDoc Debugging käyttöön:\nJos se on käytössä, emulaattori tarjoaa yhteensopivuuden Renderdocin kanssa, mikä mahdollistaa nykyisen renderöidyn kehyksen tallennuksen ja analysoinnin. + Ota Käyttöön RenderDoc Virheenkorjaus:\nJos käytössä, emulaattori tarjoaa Renderdoc-yhteensopivuuden, mikä mahdollistaa renderöidyn kehyksen tallennuksen ja analysoinnin. @@ -1457,7 +1457,7 @@ Never Played - Never Played + Pelaamaton @@ -1477,32 +1477,32 @@ Compatibility is untested - Compatibility is untested + Yhteensopivuutta ei ole testattu Game does not initialize properly / crashes the emulator - Game does not initialize properly / crashes the emulator + Peli ei alustaudu kunnolla / kaataa emulaattorin Game boots, but only displays a blank screen - Game boots, but only displays a blank screen + Peli käynnistyy, mutta näyttää vain tyhjän ruudun Game displays an image but does not go past the menu - Game displays an image but does not go past the menu + Peli näyttää kuvan mutta ei mene valikosta eteenpäin Game has game-breaking glitches or unplayable performance - Game has game-breaking glitches or unplayable performance + Pelissä on pelikokemusta rikkovia häiriöitä tai kelvoton suorituskyky Game can be completed with playable performance and no major glitches - Game can be completed with playable performance and no major glitches + Pelillä on hyväksyttävä suorituskyky, eikä mitään suuria häiriöitä @@ -1510,7 +1510,7 @@ Auto Updater - Automaattinen päivitys + Automaattinen Päivitys @@ -1525,7 +1525,7 @@ Failed to parse update information. - Päivitysinformaation jäsentäminen epäonnistui. + Päivitystietojen jäsentäminen epäonnistui. @@ -1540,7 +1540,7 @@ No download URL found for the specified asset. - Ei lataus-URL:ia löytynyt määritetylle omaisuudelle. + Lataus-URL:ia ei löytynyt määritetylle omaisuudelle. @@ -1550,7 +1550,7 @@ Update Available - Päivitys saatavilla + Päivitys Saatavilla @@ -1560,12 +1560,12 @@ Current Version - Nykyinen versio + Nykyinen Versio Latest Version - Uusin versio + Uusin Versio @@ -1575,12 +1575,12 @@ Show Changelog - Näytä muutospäiväkirja + Näytä Muutoshistoria Check for Updates at Startup - Tarkista päivitykset alussa + Tarkista Päivitykset Käynnistettäessä @@ -1595,22 +1595,22 @@ Hide Changelog - Piilota muutospäiväkirja + Piilota Muutoshistoria Changes - Muutos + Muutokset Network error occurred while trying to access the URL - Verkkovirhe tapahtui yrittäessäsi päästä URL-osoitteeseen + URL-osoitteeseen yhdistettäessä tapahtui verkkovirhe Download Complete - Download valmis + Lataus Valmis @@ -1620,12 +1620,12 @@ Failed to save the update file at - Päivitystiedoston tallentaminen epäonnistui osoitteeseen + Päivitystiedoston tallentaminen epäonnistui sijaintiin Starting Update... - Aloitetaan päivitys... + Aloitetaan päivitystä... @@ -1661,4 +1661,4 @@ TB - \ No newline at end of file + From e5f638b378393bc87ed444c1047a8b833a5b6288 Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Sun, 5 Jan 2025 16:46:26 -0600 Subject: [PATCH 361/549] fix scePlayGoGetLocus (#2067) Due to an issue with the if statement, scePlayGoGetLocus outputs an extra locus compared to real hardware. --- src/core/libraries/playgo/playgo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/libraries/playgo/playgo.cpp b/src/core/libraries/playgo/playgo.cpp index 848533ff7..ade2ee496 100644 --- a/src/core/libraries/playgo/playgo.cpp +++ b/src/core/libraries/playgo/playgo.cpp @@ -157,7 +157,7 @@ s32 PS4_SYSV_ABI scePlayGoGetLocus(OrbisPlayGoHandle handle, const OrbisPlayGoCh } for (int i = 0; i < numberOfEntries; i++) { - if (chunkIds[i] <= playgo->chunks.size()) { + if (chunkIds[i] < playgo->chunks.size()) { outLoci[i] = OrbisPlayGoLocus::LocalFast; } else { outLoci[i] = OrbisPlayGoLocus::NotDownloaded; From 887938042702821bfd858480517d1c8ff89c157b Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sun, 5 Jan 2025 15:08:27 -0800 Subject: [PATCH 362/549] shader_recompiler: Implement S_BITSET(0/1)_B32 (#2039) --- .../frontend/translate/scalar_alu.cpp | 11 +++++++++++ src/shader_recompiler/frontend/translate/translate.h | 1 + 2 files changed, 12 insertions(+) diff --git a/src/shader_recompiler/frontend/translate/scalar_alu.cpp b/src/shader_recompiler/frontend/translate/scalar_alu.cpp index e18cda012..7f34126f5 100644 --- a/src/shader_recompiler/frontend/translate/scalar_alu.cpp +++ b/src/shader_recompiler/frontend/translate/scalar_alu.cpp @@ -106,6 +106,10 @@ void Translator::EmitScalarAlu(const GcnInst& inst) { return S_FF1_I32_B32(inst); case Opcode::S_FF1_I32_B64: return S_FF1_I32_B64(inst); + case Opcode::S_BITSET0_B32: + return S_BITSET_B32(inst, 0); + case Opcode::S_BITSET1_B32: + return S_BITSET_B32(inst, 1); case Opcode::S_AND_SAVEEXEC_B64: return S_SAVEEXEC_B64(NegateMode::None, false, inst); case Opcode::S_ORN2_SAVEEXEC_B64: @@ -607,6 +611,13 @@ void Translator::S_FF1_I32_B64(const GcnInst& inst) { SetDst(inst.dst[0], result); } +void Translator::S_BITSET_B32(const GcnInst& inst, u32 bit_value) { + const IR::U32 old_value{GetSrc(inst.dst[0])}; + const IR::U32 offset{ir.BitFieldExtract(GetSrc(inst.src[0]), ir.Imm32(0U), ir.Imm32(5U))}; + const IR::U32 result{ir.BitFieldInsert(old_value, ir.Imm32(bit_value), offset, ir.Imm32(1U))}; + SetDst(inst.dst[0], result); +} + void Translator::S_SAVEEXEC_B64(NegateMode negate, bool is_or, const GcnInst& inst) { // This instruction normally operates on 64-bit data (EXEC, VCC, SGPRs) // However here we flatten it to 1-bit EXEC and 1-bit VCC. For the destination diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index 9da0844e4..7a0b736d4 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -114,6 +114,7 @@ public: void S_BCNT1_I32_B64(const GcnInst& inst); void S_FF1_I32_B32(const GcnInst& inst); void S_FF1_I32_B64(const GcnInst& inst); + void S_BITSET_B32(const GcnInst& inst, u32 bit_value); void S_GETPC_B64(u32 pc, const GcnInst& inst); void S_SAVEEXEC_B64(NegateMode negate, bool is_or, const GcnInst& inst); void S_ABS_I32(const GcnInst& inst); From 7cdeb516706a2659232305808bd3fbdbcad2bf6c Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Mon, 6 Jan 2025 05:31:25 -0800 Subject: [PATCH 363/549] renderer_vulkan: Add debug names to pipelines. (#2069) --- .../renderer_vulkan/vk_compute_pipeline.cpp | 6 +++++- .../renderer_vulkan/vk_graphics_pipeline.cpp | 5 ++++- .../renderer_vulkan/vk_pipeline_common.cpp | 16 ++++++++++++++++ .../renderer_vulkan/vk_pipeline_common.h | 2 ++ 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp index 71659c06c..b24767e8a 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp @@ -18,6 +18,7 @@ ComputePipeline::ComputePipeline(const Instance& instance_, Scheduler& scheduler : Pipeline{instance_, scheduler_, desc_heap_, pipeline_cache, true}, compute_key{compute_key_} { auto& info = stages[int(Shader::LogicalStage::Compute)]; info = &info_; + const auto debug_str = GetDebugString(); const vk::PipelineShaderStageCreateInfo shader_ci = { .stage = vk::ShaderStageFlagBits::eCompute, @@ -89,8 +90,9 @@ ComputePipeline::ComputePipeline(const Instance& instance_, Scheduler& scheduler .bindingCount = static_cast(bindings.size()), .pBindings = bindings.data(), }; + const auto device = instance.GetDevice(); auto [descriptor_set_result, descriptor_set] = - instance.GetDevice().createDescriptorSetLayoutUnique(desc_layout_ci); + device.createDescriptorSetLayoutUnique(desc_layout_ci); ASSERT_MSG(descriptor_set_result == vk::Result::eSuccess, "Failed to create compute descriptor set layout: {}", vk::to_string(descriptor_set_result)); @@ -107,6 +109,7 @@ ComputePipeline::ComputePipeline(const Instance& instance_, Scheduler& scheduler ASSERT_MSG(layout_result == vk::Result::eSuccess, "Failed to create compute pipeline layout: {}", vk::to_string(layout_result)); pipeline_layout = std::move(layout); + SetObjectName(device, *pipeline_layout, "Compute PipelineLayout {}", debug_str); const vk::ComputePipelineCreateInfo compute_pipeline_ci = { .stage = shader_ci, @@ -117,6 +120,7 @@ ComputePipeline::ComputePipeline(const Instance& instance_, Scheduler& scheduler ASSERT_MSG(pipeline_result == vk::Result::eSuccess, "Failed to create compute pipeline: {}", vk::to_string(pipeline_result)); pipeline = std::move(pipe); + SetObjectName(device, *pipeline, "Compute Pipeline {}", debug_str); } ComputePipeline::~ComputePipeline() = default; diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index ffa474a1c..0ca1bed8b 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -8,7 +8,6 @@ #include "common/assert.h" #include "common/io_file.h" -#include "common/scope_exit.h" #include "shader_recompiler/backend/spirv/emit_spirv_quad_rect.h" #include "shader_recompiler/frontend/fetch_shader.h" #include "shader_recompiler/runtime_info.h" @@ -16,6 +15,7 @@ #include "video_core/buffer_cache/buffer_cache.h" #include "video_core/renderer_vulkan/vk_graphics_pipeline.h" #include "video_core/renderer_vulkan/vk_instance.h" +#include "video_core/renderer_vulkan/vk_pipeline_cache.h" #include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_shader_util.h" #include "video_core/texture_cache/texture_cache.h" @@ -36,6 +36,7 @@ GraphicsPipeline::GraphicsPipeline( const vk::Device device = instance.GetDevice(); std::ranges::copy(infos, stages.begin()); BuildDescSetLayout(); + const auto debug_str = GetDebugString(); const vk::PushConstantRange push_constants = { .stageFlags = gp_stage_flags, @@ -54,6 +55,7 @@ GraphicsPipeline::GraphicsPipeline( ASSERT_MSG(layout_result == vk::Result::eSuccess, "Failed to create graphics pipeline layout: {}", vk::to_string(layout_result)); pipeline_layout = std::move(layout); + SetObjectName(device, *pipeline_layout, "Graphics PipelineLayout {}", debug_str); boost::container::static_vector vertex_bindings; boost::container::static_vector vertex_attributes; @@ -322,6 +324,7 @@ GraphicsPipeline::GraphicsPipeline( ASSERT_MSG(pipeline_result == vk::Result::eSuccess, "Failed to create graphics pipeline: {}", vk::to_string(pipeline_result)); pipeline = std::move(pipe); + SetObjectName(device, *pipeline, "Graphics Pipeline {}", debug_str); } GraphicsPipeline::~GraphicsPipeline() = default; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_common.cpp b/src/video_core/renderer_vulkan/vk_pipeline_common.cpp index 6b48a40a0..91f53109e 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_common.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_common.cpp @@ -6,6 +6,7 @@ #include "shader_recompiler/info.h" #include "video_core/buffer_cache/buffer_cache.h" #include "video_core/renderer_vulkan/vk_instance.h" +#include "video_core/renderer_vulkan/vk_pipeline_cache.h" #include "video_core/renderer_vulkan/vk_pipeline_common.h" #include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/texture_cache/texture_cache.h" @@ -55,4 +56,19 @@ void Pipeline::BindResources(DescriptorWrites& set_writes, const BufferBarriers& cmdbuf.bindDescriptorSets(bind_point, *pipeline_layout, 0, desc_set, {}); } +std::string Pipeline::GetDebugString() const { + std::string stage_desc; + for (const auto& stage : stages) { + if (stage) { + const auto shader_name = PipelineCache::GetShaderName(stage->stage, stage->pgm_hash); + if (stage_desc.empty()) { + stage_desc = shader_name; + } else { + stage_desc = fmt::format("{},{}", stage_desc, shader_name); + } + } + } + return stage_desc; +} + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_pipeline_common.h b/src/video_core/renderer_vulkan/vk_pipeline_common.h index 1b13a1797..f71631da0 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_common.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_common.h @@ -61,6 +61,8 @@ public: const Shader::PushData& push_data) const; protected: + [[nodiscard]] std::string GetDebugString() const; + const Instance& instance; Scheduler& scheduler; DescriptorHeap& desc_heap; From fb67d948b63bc0298f8fd4597a18e2994df5d426 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Mon, 6 Jan 2025 05:31:45 -0800 Subject: [PATCH 364/549] vk_resource_pool: Handle eErrorFragmentedPool. (#2071) --- src/video_core/renderer_vulkan/vk_resource_pool.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/video_core/renderer_vulkan/vk_resource_pool.cpp b/src/video_core/renderer_vulkan/vk_resource_pool.cpp index 25a134528..dba603e71 100644 --- a/src/video_core/renderer_vulkan/vk_resource_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_resource_pool.cpp @@ -153,7 +153,8 @@ vk::DescriptorSet DescriptorHeap::Commit(vk::DescriptorSetLayout set_layout) { } // The pool has run out. Record current tick and place it in pending list. - ASSERT_MSG(result == vk::Result::eErrorOutOfPoolMemory, + ASSERT_MSG(result == vk::Result::eErrorOutOfPoolMemory || + result == vk::Result::eErrorFragmentedPool, "Unexpected error during descriptor set allocation {}", vk::to_string(result)); pending_pools.emplace_back(curr_pool, master_semaphore->CurrentTick()); if (const auto [pool, tick] = pending_pools.front(); master_semaphore->IsFree(tick)) { From 121328ecedc6bcbe1eb319308f621df8a8b28f09 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Mon, 6 Jan 2025 18:45:53 +0200 Subject: [PATCH 365/549] dummy sceMouse module and change sceMouseRead to debug to reduce spam (#2074) --- CMakeLists.txt | 2 + src/common/logging/filter.cpp | 1 + src/common/logging/types.h | 1 + src/core/libraries/libs.cpp | 2 + src/core/libraries/mouse/mouse.cpp | 99 ++++++++++++++++++++++++++++++ src/core/libraries/mouse/mouse.h | 29 +++++++++ 6 files changed, 134 insertions(+) create mode 100644 src/core/libraries/mouse/mouse.cpp create mode 100644 src/core/libraries/mouse/mouse.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 36ebbf583..886824934 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -336,6 +336,8 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp src/core/libraries/share_play/shareplay.h src/core/libraries/razor_cpu/razor_cpu.cpp src/core/libraries/razor_cpu/razor_cpu.h + src/core/libraries/mouse/mouse.cpp + src/core/libraries/mouse/mouse.h ) set(VIDEOOUT_LIB src/core/libraries/videoout/buffer.h diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index a2fd2c0a4..5ee2dce73 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -126,6 +126,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Lib, Vdec2) \ SUB(Lib, Videodec) \ SUB(Lib, RazorCpu) \ + SUB(Lib, Mouse) \ CLS(Frontend) \ CLS(Render) \ SUB(Render, Vulkan) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index 5b496d175..6829e2d1b 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -93,6 +93,7 @@ enum class Class : u8 { Lib_Vdec2, ///< The LibSceVideodec2 implementation. Lib_Videodec, ///< The LibSceVideodec implementation. Lib_RazorCpu, ///< The LibRazorCpu implementation. + Lib_Mouse, ///< The LibSceMouse implementation Frontend, ///< Emulator UI Render, ///< Video Core Render_Vulkan, ///< Vulkan backend diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index 49cd54a5b..69728e523 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -18,6 +18,7 @@ #include "core/libraries/libc_internal/libc_internal.h" #include "core/libraries/libpng/pngdec.h" #include "core/libraries/libs.h" +#include "core/libraries/mouse/mouse.h" #include "core/libraries/move/move.h" #include "core/libraries/network/http.h" #include "core/libraries/network/net.h" @@ -97,6 +98,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::Move::RegisterlibSceMove(sym); Libraries::Fiber::RegisterlibSceFiber(sym); Libraries::JpegEnc::RegisterlibSceJpegEnc(sym); + Libraries::Mouse::RegisterlibSceMouse(sym); } } // namespace Libraries diff --git a/src/core/libraries/mouse/mouse.cpp b/src/core/libraries/mouse/mouse.cpp new file mode 100644 index 000000000..dffd2346c --- /dev/null +++ b/src/core/libraries/mouse/mouse.cpp @@ -0,0 +1,99 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +// Generated By moduleGenerator +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" +#include "mouse.h" + +namespace Libraries::Mouse { + +int PS4_SYSV_ABI sceMouseClose() { + LOG_ERROR(Lib_Mouse, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceMouseConnectPort() { + LOG_ERROR(Lib_Mouse, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceMouseDebugGetDeviceId() { + LOG_ERROR(Lib_Mouse, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceMouseDeviceOpen() { + LOG_ERROR(Lib_Mouse, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceMouseDisconnectDevice() { + LOG_ERROR(Lib_Mouse, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceMouseDisconnectPort() { + LOG_ERROR(Lib_Mouse, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceMouseGetDeviceInfo() { + LOG_ERROR(Lib_Mouse, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceMouseInit() { + LOG_ERROR(Lib_Mouse, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceMouseMbusInit() { + LOG_ERROR(Lib_Mouse, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceMouseOpen() { + LOG_ERROR(Lib_Mouse, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceMouseRead() { + LOG_DEBUG(Lib_Mouse, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceMouseSetHandType() { + LOG_ERROR(Lib_Mouse, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceMouseSetPointerSpeed() { + LOG_ERROR(Lib_Mouse, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceMouseSetProcessPrivilege() { + LOG_ERROR(Lib_Mouse, "(STUBBED) called"); + return ORBIS_OK; +} + +void RegisterlibSceMouse(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("cAnT0Rw-IwU", "libSceMouse", 1, "libSceMouse", 1, 1, sceMouseClose); + LIB_FUNCTION("Ymyy1HSSJLQ", "libSceMouse", 1, "libSceMouse", 1, 1, sceMouseConnectPort); + LIB_FUNCTION("BRXOoXQtb+k", "libSceMouse", 1, "libSceMouse", 1, 1, sceMouseDebugGetDeviceId); + LIB_FUNCTION("WiGKINCZWkc", "libSceMouse", 1, "libSceMouse", 1, 1, sceMouseDeviceOpen); + LIB_FUNCTION("eDQTFHbgeTU", "libSceMouse", 1, "libSceMouse", 1, 1, sceMouseDisconnectDevice); + LIB_FUNCTION("jJP1vYMEPd4", "libSceMouse", 1, "libSceMouse", 1, 1, sceMouseDisconnectPort); + LIB_FUNCTION("QA9Qupz3Zjw", "libSceMouse", 1, "libSceMouse", 1, 1, sceMouseGetDeviceInfo); + LIB_FUNCTION("Qs0wWulgl7U", "libSceMouse", 1, "libSceMouse", 1, 1, sceMouseInit); + LIB_FUNCTION("1FeceR5YhAo", "libSceMouse", 1, "libSceMouse", 1, 1, sceMouseMbusInit); + LIB_FUNCTION("RaqxZIf6DvE", "libSceMouse", 1, "libSceMouse", 1, 1, sceMouseOpen); + LIB_FUNCTION("x8qnXqh-tiM", "libSceMouse", 1, "libSceMouse", 1, 1, sceMouseRead); + LIB_FUNCTION("crkFfp-cmFo", "libSceMouse", 1, "libSceMouse", 1, 1, sceMouseSetHandType); + LIB_FUNCTION("ghLUU2Z5Lcg", "libSceMouse", 1, "libSceMouse", 1, 1, sceMouseSetPointerSpeed); + LIB_FUNCTION("6aANndpS0Wo", "libSceMouse", 1, "libSceMouse", 1, 1, sceMouseSetProcessPrivilege); +}; + +} // namespace Libraries::Mouse \ No newline at end of file diff --git a/src/core/libraries/mouse/mouse.h b/src/core/libraries/mouse/mouse.h new file mode 100644 index 000000000..8264f62e0 --- /dev/null +++ b/src/core/libraries/mouse/mouse.h @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::Mouse { + +int PS4_SYSV_ABI sceMouseClose(); +int PS4_SYSV_ABI sceMouseConnectPort(); +int PS4_SYSV_ABI sceMouseDebugGetDeviceId(); +int PS4_SYSV_ABI sceMouseDeviceOpen(); +int PS4_SYSV_ABI sceMouseDisconnectDevice(); +int PS4_SYSV_ABI sceMouseDisconnectPort(); +int PS4_SYSV_ABI sceMouseGetDeviceInfo(); +int PS4_SYSV_ABI sceMouseInit(); +int PS4_SYSV_ABI sceMouseMbusInit(); +int PS4_SYSV_ABI sceMouseOpen(); +int PS4_SYSV_ABI sceMouseRead(); +int PS4_SYSV_ABI sceMouseSetHandType(); +int PS4_SYSV_ABI sceMouseSetPointerSpeed(); +int PS4_SYSV_ABI sceMouseSetProcessPrivilege(); + +void RegisterlibSceMouse(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::Mouse \ No newline at end of file From 6f3c767b99d815dc99417366f3d26118822a5706 Mon Sep 17 00:00:00 2001 From: Daniel Nylander Date: Mon, 6 Jan 2025 22:10:15 +0100 Subject: [PATCH 366/549] Adding Swedish translation (#2075) --- src/qt_gui/translations/sv.ts | 1336 +++++++++++++++++++++++++++++++++ 1 file changed, 1336 insertions(+) create mode 100644 src/qt_gui/translations/sv.ts diff --git a/src/qt_gui/translations/sv.ts b/src/qt_gui/translations/sv.ts new file mode 100644 index 000000000..756119ba4 --- /dev/null +++ b/src/qt_gui/translations/sv.ts @@ -0,0 +1,1336 @@ + + + + + + AboutDialog + + About shadPS4 + Om shadPS4 + + + shadPS4 + shadPS4 + + + shadPS4 is an experimental open-source emulator for the PlayStation 4. + shadPS4 är en experimentell emulator för PlayStation 4 baserad på öppen källkod. + + + This software should not be used to play games you have not legally obtained. + Denna programvara bör inte användas för att spela spel som du inte legalt äger. + + + + ElfViewer + + Open Folder + Öppna mapp + + + + GameInfoClass + + Loading game list, please wait :3 + Läser in spellistan, vänta :3 + + + Cancel + Avbryt + + + Loading... + Läser in... + + + + InstallDirSelect + + shadPS4 - Choose directory + shadPS4 - Välj katalog + + + Select which directory you want to install to. + Välj vilken katalog som du vill installera till. + + + + GameInstallDialog + + shadPS4 - Choose directory + shadPS4 - Välj katalog + + + Directory to install games + Katalog att installera spel till + + + Browse + Bläddra + + + Error + Fel + + + The value for location to install games is not valid. + Värdet för platsen att installera spel till är inte giltig. + + + + GuiContextMenus + + Create Shortcut + Skapa genväg + + + Cheats / Patches + Fusk / Patchar + + + SFO Viewer + SFO-visare + + + Trophy Viewer + Trofé-visare + + + Open Folder... + Öppna mapp... + + + Open Game Folder + Öppna spelmapp + + + Open Save Data Folder + Öppna mapp för sparat data + + + Open Log Folder + Öppna loggmapp + + + Copy info... + Kopiera till... + + + Copy Name + Kopiera namn + + + Copy Serial + Kopiera serienummer + + + Copy All + Kopiera alla + + + Delete... + Ta bort... + + + Delete Game + Ta bort spel + + + Delete Update + Ta bort uppdatering + + + Delete DLC + Ta bort DLC + + + Compatibility... + Kompatibilitet... + + + Update database + Uppdatera databasen + + + View report + Visa rapport + + + Submit a report + Skicka en rapport + + + Shortcut creation + Skapa genväg + + + Shortcut created successfully! + Genvägen skapades! + + + Error + Fel + + + Error creating shortcut! + Fel vid skapandet av genväg! + + + Install PKG + Installera PKG + + + Game + Spel + + + requiresEnableSeparateUpdateFolder_MSG + Denna funktion kräver konfigurationsalternativet 'Aktivera separat uppdateringsmapp' för att fungera. Om du vill använda denna funktion, aktivera den + + + This game has no update to delete! + Detta spel har ingen uppdatering att ta bort! + + + Update + Uppdatera + + + This game has no DLC to delete! + Detta spel har inga DLC att ta bort! + + + DLC + DLC + + + Delete %1 + Ta bort %1 + + + Are you sure you want to delete %1's %2 directory? + Är du säker på att du vill ta bort %1s %2-katalog? + + + + MainWindow + + Open/Add Elf Folder + Öppna/Lägg till Elf-mapp + + + Install Packages (PKG) + Installera paket (PKG) + + + Boot Game + Starta spel + + + Check for Updates + Leta efter uppdateringar + + + About shadPS4 + Om shadPS4 + + + Configure... + Konfigurera... + + + Install application from a .pkg file + Installera program från en .pkg-fil + + + Recent Games + Senaste spel + + + Exit + Avsluta + + + Exit shadPS4 + Avsluta shadPS4 + + + Exit the application. + Avsluta programmet. + + + Show Game List + Visa spellista + + + Game List Refresh + Uppdatera spellista + + + Tiny + Mycket små + + + Small + Små + + + Medium + Medel + + + Large + Stora + + + List View + Listvy + + + Grid View + Rutnätsvy + + + Elf Viewer + Elf-visare + + + Game Install Directory + Installationskatalog för spel + + + Download Cheats/Patches + Hämta fusk/patchar + + + Dump Game List + Dumpa spellista + + + PKG Viewer + PKG-visare + + + Search... + Sök... + + + File + Arkiv + + + View + Visa + + + Game List Icons + Ikoner för spellista + + + Game List Mode + Läge för spellista + + + Settings + Inställningar + + + Utils + Verktyg + + + Themes + Teman + + + Help + Hjälp + + + Dark + Mörk + + + Light + Ljus + + + Green + Grön + + + Blue + Blå + + + Violet + Lila + + + toolBar + toolBar + + + Game List + Spellista + + + * Unsupported Vulkan Version + * Vulkan-versionen stöds inte + + + Download Cheats For All Installed Games + Hämta fusk för alla installerade spel + + + Download Patches For All Games + Hämta patchar för alla spel + + + Download Complete + Hämtning färdig + + + You have downloaded cheats for all the games you have installed. + Du har hämtat fusk till alla spelen som du har installerade. + + + Patches Downloaded Successfully! + Patchar hämtades ner! + + + All Patches available for all games have been downloaded. + Alla patchar tillgängliga för alla spel har hämtats ner. + + + Games: + Spel: + + + PKG File (*.PKG) + PKG-fil (*.PKG) + + + ELF files (*.bin *.elf *.oelf) + ELF-filer (*.bin *.elf *.oelf) + + + Game Boot + Starta spel + + + Only one file can be selected! + Endast en fil kan väljas! + + + PKG Extraction + PKG-extrahering + + + Patch detected! + Patch upptäcktes! + + + PKG and Game versions match: + PKG och spelversioner matchar: + + + Would you like to overwrite? + Vill du skriva över? + + + PKG Version %1 is older than installed version: + PKG-versionen %1 är äldre än installerad version: + + + Game is installed: + Spelet är installerat: + + + Would you like to install Patch: + Vill du installera patch: + + + DLC Installation + DLC-installation + + + Would you like to install DLC: %1? + Vill du installera DLC: %1? + + + DLC already installed: + DLC redan installerat: + + + Game already installed + Spelet redan installerat + + + PKG is a patch, please install the game first! + PKH är en patch. Installera spelet först! + + + PKG ERROR + PKG-FEL + + + Extracting PKG %1/%2 + Extraherar PKG %1/%2 + + + Extraction Finished + Extrahering färdig + + + Game successfully installed at %1 + Spelet installerades i %1 + + + File doesn't appear to be a valid PKG file + Filen verkar inte vara en giltig PKG-fil + + + + PKGViewer + + Open Folder + Öppna mapp + + + + TrophyViewer + + Trophy Viewer + Trofé-visare + + + + SettingsDialog + + Settings + Inställningar + + + General + Allmänt + + + System + System + + + Console Language + Konsollspråk + + + Emulator Language + Emulatorspråk + + + Emulator + Emulator + + + Enable Fullscreen + Aktivera helskärm + + + Enable Separate Update Folder + Aktivera separat uppdateringsmapp + + + Show Splash + Visa startskärm + + + Is PS4 Pro + Är PS4 Pro + + + Enable Discord Rich Presence + Aktivera Discord Rich Presence + + + Username + Användarnamn + + + Trophy Key + Trofényckel + + + Trophy + Trofé + + + Logger + Loggning + + + Log Type + Loggtyp + + + Log Filter + Loggfilter + + + Input + Inmatning + + + Cursor + Pekare + + + Hide Cursor + Dölj pekare + + + Hide Cursor Idle Timeout + Dölj pekare vid overksam + + + s + s + + + Controller + Handkontroller + + + Back Button Behavior + Beteende för bakåtknapp + + + Graphics + Grafik + + + Graphics Device + Grafikenhet + + + Width + Bredd + + + Height + Höjd + + + Vblank Divider + Vblank Divider + + + Advanced + Avancerat + + + Enable Shaders Dumping + Aktivera Shaders Dumping + + + Enable NULL GPU + Aktivera NULL GPU + + + Paths + Sökvägar + + + Game Folders + Spelmappar + + + Add... + Lägg till... + + + Remove + Ta bort + + + Debug + Felsök + + + Enable Debug Dumping + Aktivera felsökningsdumpning + + + Enable Vulkan Validation Layers + Aktivera Vulkan Validation Layers + + + Enable Vulkan Synchronization Validation + Aktivera Vulkan Synchronization Validation + + + Enable RenderDoc Debugging + Aktivera RenderDoc-felsökning + + + Update + Uppdatera + + + Check for Updates at Startup + Leta efter uppdateringar vid uppstart + + + Update Channel + Uppdateringskanal + + + Check for Updates + Leta efter uppdateringar + + + GUI Settings + Gränssnittsinställningar + + + Disable Trophy Pop-ups + Inaktivera popup för troféer + + + Play title music + Spela titelmusik + + + Update Compatibility Database On Startup + Uppdatera databas vid uppstart + + + Game Compatibility + Spelkompatibilitet + + + Display Compatibility Data + Visa kompatibilitetsdata + + + Update Compatibility Database + Uppdatera kompatibilitetsdatabasen + + + Volume + Volym + + + Audio Backend + Ljudbakände + + + Save + Spara + + + Apply + Verkställ + + + Restore Defaults + Återställ till standard + + + Close + Stäng + + + Point your mouse at an option to display its description. + Peka din mus på ett alternativ för att visa dess beskrivning. + + + consoleLanguageGroupBox + Konsollspråk:\nStäller in språket som PS4-spelet använder.\nDet rekommenderas att ställa in detta till ett språk som spelet har stöd för, vilket kan skilja sig mellan regioner + + + emulatorLanguageGroupBox + Emulatorspråk:\nStäller in språket för emulatorns användargränssnitt + + + fullscreenCheckBox + Aktivera helskärm:\nStäller automatiskt in spelfönstret till helskämsläget.\nDetta kan växlas genom att trycka på F11-tangenten + + + separateUpdatesCheckBox + Aktivera separat uppdateringsmapp:\nAktiverar installation av speluppdateringar i en separat mapp för enkel hantering.\nDetta kan skapas manuellt genom att lägga till uppackad uppdatering till spelmappen med namnet "CUSA00000-UPDATE" där CUSA ID matchar spelets id + + + showSplashCheckBox + Visa startskärm:\nVisar spelets startskärm (en speciell bild) när spelet startas + + + ps4proCheckBox + Är PS4 Pro:\nGör att emulatorn fungerar som en PS4 PRO, som kan aktivera speciella funktioner i spel som har stöds för det + + + discordRPCCheckbox + Aktivera Discord Rich Presence:\nVisar emulatorikonen och relevant information på din Discord-profil + + + userName + Användarnamn:\nStäller in PS4ans användarkonto, som kan visas av vissa spel + + + TrophyKey + Trofényckel:\nNyckel som används för att avkryptera troféer. Måste hämtas från din konsoll (jailbroken).\nMåste innehålla endast hex-tecken + + + logTypeGroupBox + Loggtyp:\nStäller in huruvida synkronisering av utdata för loggfönstret för prestanda. Kan ha inverkan på emulationen + + + logFilter + Loggfilter:\nFiltrera loggen till att endast skriva ut specifik information.\nExempel: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical"\nNivåer: Trace, Debug, Info, Warning, Error, Critical - i den ordningen, en specifik nivå som tystar alla nivåer före den i listan och loggar allting efter den + + + updaterGroupBox + updaterGroupBox + + + GUIgroupBox + Spela upp titelmusik:\nOm ett spel har stöd för det kan speciell musik spelas upp från spelet i gränssnittet + + + disableTrophycheckBox + Inaktivera popup för troféer:\nInaktivera troféeaviseringar i spel. Troféförlopp kan fortfarande följas med Troféevisaren (högerklicka på spelet i huvudfönstret) + + + hideCursorGroupBox + Dölj pekare:\nVälj när muspekaren ska försvinna:\nAldrig: Du kommer alltid se muspekaren.\nOverksam: Ställ in en tid för när den ska försvinna efter den inte använts.\nAlltid: du kommer aldrig se muspekaren + + + idleTimeoutGroupBox + Dölj pekare vid overksam:\nLängden (sekunder) efter vilken som muspekaren som har varit overksam döljer sig själv + + + backButtonBehaviorGroupBox + Beteende för bakåtknapp:\nStäller in handkontrollerns bakåtknapp för att emulera ett tryck på angivna positionen på PS4ns touchpad + + + enableCompatibilityCheckBox + Visa kompatibilitetsdata:\nVisar information om spelkompatibilitet i tabellvyn. Aktivera "Uppdatera kompatibilitet vid uppstart" för att få uppdaterad information + + + checkCompatibilityOnStartupCheckBox + Uppdatera kompatibilitet vid uppstart:\nUppdatera automatiskt kompatibilitetsdatabasen när shadPS4 startar + + + updateCompatibilityButton + Uppdatera kompatibilitetsdatabasen:\nUppdaterar kompatibilitetsdatabasen direkt + + + Never + Aldrig + + + Idle + Overksam + + + Always + Alltid + + + Touchpad Left + Touchpad vänster + + + Touchpad Right + Touchpad höger + + + Touchpad Center + Touchpad mitten + + + None + Ingen + + + graphicsAdapterGroupBox + Grafikenhet:\nFör system med flera GPUer kan du välja den GPU som emulatorn ska använda från rullgardinsmenyn,\neller välja "Auto Select" för att automatiskt bestämma det + + + resolutionLayout + Bredd/Höjd:\nStäller in storleken för emulatorfönstret vid uppstart, som kan storleksändras under spelning.\nDetta är inte det samma som spelupplösningen + + + heightDivider + Vblank Divider:\nBildfrekvensen som emulatorn uppdaterar vid multipliceras med detta tal. Ändra detta kan ha inverkan på saker, såsom ökad spelhastighet eller göra sönder kritisk spelfunktionalitet, som inte förväntar sig denna ändring + + + dumpShadersCheckBox + Aktivera Shaders Dumping:\nFör teknisk felsökning, sparar spelets shaders till en mapp när de renderas + + + nullGpuCheckBox + Aktivera Null GPU:\nFör teknisk felsökning, inaktiverar spelrenderingen som om det inte fanns något grafikkort + + + gameFoldersBox + Spelmappar:\nListan över mappar att leta i efter installerade spel + + + addFolderButton + Aktivera separat uppdateringsmapp:\nAktiverar installation av speluppdateringar till en separat mapp för enkel hantering.\nDetta kan manuellt skapas genom att lägga till den uppackade uppdateringen till spelmappen med namnet "CUSA00000-UPDATE" där CUSA ID matchar spelets id + + + removeFolderButton + Ta bort:\nTa bort en mapp från listan + + + debugDump + Aktivera felsökningsdumpning:\nSparar import och export av symboler och fil-header-information för aktuellt körande PS4-program till en katalog + + + vkValidationCheckBox + Aktivera Vulkan Validation Layers:\nAktiverar ett system som validerar tillståndet för Vulkan renderer och loggar information om dess interna tillstånd.\nDetta kommer minska prestandan och antagligen ändra beteendet för emuleringen + + + vkSyncValidationCheckBox + Aktivera Vulkan Synchronization Validation:\nAktiverar ett system som validerar timing för Vulkan rendering tasks.\nDetta kommer minska prestandan och antagligen ändra beteendet för emuleringen + + + rdocCheckBox + Aktivera RenderDoc-felsökning:\nOm aktiverad kommer emulatorn att tillhandahålla kompatibilitet med Renderdoc för att tillåta fångst och analys för aktuell renderad bildruta + + + + CheatsPatches + + Cheats / Patches for + Fusk / Patchar för + + + defaultTextEdit_MSG + Fusk/Patchar är experimentella.\nAnvänd med försiktighet.\n\nHämta fusk individuellt genom att välja förrådet och klicka på hämtningsknappen.\nUnder Patchar-fliken kan du hämta alla patchar på en gång, välj vilken du vill använda och spara ditt val.\n\nEftersom vi inte utvecklar fusk eller patchar,\nrapportera gärna problem till fuskets upphovsperson.\n\nSkapat ett nytt fusk? Besök:\nhttps://github.com/shadps4-emu/ps4_cheats + + + No Image Available + Ingen bild tillgänglig + + + Serial: + Serienummer: + + + Version: + Version: + + + Size: + Storlek: + + + Select Cheat File: + Välj fuskfil: + + + Repository: + Förråd: + + + Download Cheats + Hämta fusk + + + Delete File + Ta bort fil + + + No files selected. + Inga filer valda. + + + You can delete the cheats you don't want after downloading them. + Du kan ta bort fusk som du inte vill ha efter de hämtats ner. + + + Do you want to delete the selected file?\n%1 + Vill du ta bort markerade filen?\n%1 + + + Select Patch File: + Välj patchfil: + + + Download Patches + Hämta patchar + + + Save + Spara + + + Cheats + Fusk + + + Patches + Patchar + + + Error + Fel + + + No patch selected. + Ingen patch vald. + + + Unable to open files.json for reading. + Kunde inte öppna files.json för läsning. + + + No patch file found for the current serial. + Ingen patchfil hittades för aktuella serienumret. + + + Unable to open the file for reading. + Kunde inte öppna filen för läsning. + + + Unable to open the file for writing. + Kunde inte öppna filen för skrivning. + + + Failed to parse XML: + Misslyckades med att tolka XML: + + + Success + Lyckades + + + Options saved successfully. + Inställningarna sparades. + + + Invalid Source + Ogiltig källa + + + The selected source is invalid. + Vald källa är ogiltig. + + + File Exists + Filen finns + + + File already exists. Do you want to replace it? + Filen finns redan. Vill du ersätta den? + + + Failed to save file: + Misslyckades med att spara fil: + + + Failed to download file: + Misslyckades med att hämta filen: + + + Cheats Not Found + Fusk hittades inte + + + CheatsNotFound_MSG + Inga fusk hittades för detta spel i denna version av det valda förrådet. Prova ett annat förråd eller en annan version av spelet + + + Cheats Downloaded Successfully + Fusk hämtades ner + + + CheatsDownloadedSuccessfully_MSG + Du har hämtat ner fusken för denna version av spelet från valt förråd. Du kan försöka att hämta från andra förråd, om de är tillgängliga så kan det vara möjligt att använda det genom att välja det genom att välja filen från listan + + + Failed to save: + Misslyckades med att spara: + + + Failed to download: + Misslyckades med att hämta: + + + Download Complete + Hämtning färdig + + + DownloadComplete_MSG + Patchhämtningen är färdig! Alla patchar tillgängliga för alla spel har hämtats och de behövs inte hämtas individuellt för varje spel som med fusk. Om patchen inte dyker upp kan det bero på att den inte finns för det specifika serienumret och versionen av spelet + + + Failed to parse JSON data from HTML. + Misslyckades med att tolka JSON-data från HTML. + + + Failed to retrieve HTML page. + Misslyckades med att hämta HTML-sida. + + + The game is in version: %1 + Spelet är i version: %1 + + + The downloaded patch only works on version: %1 + Hämtad patch fungerar endast på version: %1 + + + You may need to update your game. + Du kan behöva uppdatera ditt spel. + + + Incompatibility Notice + Inkompatibilitetsmeddelande + + + Failed to open file: + Misslyckades med att öppna filen: + + + XML ERROR: + XML-FEL: + + + Failed to open files.json for writing + Misslyckades med att öppna files.json för skrivning + + + Author: + Upphovsperson: + + + Directory does not exist: + Katalogen finns inte: + + + Failed to open files.json for reading. + Misslyckades med att öppna files.json för läsning. + + + Name: + Namn: + + + Can't apply cheats before the game is started + Kan inte tillämpa fusk innan spelet är startat + + + + GameListFrame + + Icon + Ikon + + + Name + Namn + + + Serial + Serienummer + + + Compatibility + Kompatibilitet + + + Region + Region + + + Firmware + Firmware + + + Size + Storlek + + + Version + Version + + + Path + Sökväg + + + Play Time + Speltid + + + Never Played + Aldrig spelat + + + h + h + + + m + m + + + s + s + + + Compatibility is untested + Kompatibilitet är otestat + + + Game does not initialize properly / crashes the emulator + Spelet initierar inte korrekt / kraschar emulatorn + + + Game boots, but only displays a blank screen + Spelet startar men visar endast en blank skärm + + + Game displays an image but does not go past the menu + Spelet visar en bild men kommer inte förbi menyn + + + Game has game-breaking glitches or unplayable performance + Spelet har allvarliga problem eller ospelbar prestanda + + + Game can be completed with playable performance and no major glitches + Spelet kan spelas klart med spelbar prestanda och utan större problem + + + + CheckUpdate + + Auto Updater + Automatisk uppdatering + + + Error + Fel + + + Network error: + Nätverksfel: + + + Failed to parse update information. + Misslyckades med att tolka uppdateringsinformationen. + + + No pre-releases found. + Inga förutgåva hittades. + + + Invalid release data. + Ogiltig release-data. + + + No download URL found for the specified asset. + Ingen hämtnings-URL hittades för angiven tillgång. + + + Your version is already up to date! + Din version är redan den senaste! + + + Update Available + Uppdatering tillgänglig + + + Update Channel + Uppdateringskanal + + + Current Version + Aktuell version + + + Latest Version + Senaste version + + + Do you want to update? + Vill du uppdatera? + + + Show Changelog + Visa ändringslogg + + + Check for Updates at Startup + Leta efter uppdateringar vid uppstart + + + Update + Uppdatera + + + No + Nej + + + Hide Changelog + Dölj ändringslogg + + + Changes + Ändringar + + + Network error occurred while trying to access the URL + Nätverksfel inträffade vid försök att komma åt URL:en + + + Download Complete + Hämtning färdig + + + The update has been downloaded, press OK to install. + Uppdateringen har hämtats. Tryck på Ok för att installera. + + + Failed to save the update file at + Misslyckades med att spara uppdateringsfilen i + + + Starting Update... + Startar uppdatering... + + + Failed to create the update script file + Misslyckades med att skapa uppdateringsskriptfil + + + + GameListUtils + + B + B + + + KB + KB + + + MB + MB + + + GB + GB + + + TB + TB + + + \ No newline at end of file From 5559f3590599a1b0534a830c2a4f855722b79142 Mon Sep 17 00:00:00 2001 From: psucien Date: Mon, 6 Jan 2025 22:50:09 +0100 Subject: [PATCH 367/549] hot-fix: buffers resolve barriers fixed --- src/video_core/buffer_cache/buffer.h | 16 ++++-- src/video_core/buffer_cache/buffer_cache.cpp | 60 +++++++++----------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/video_core/buffer_cache/buffer.h b/src/video_core/buffer_cache/buffer.h index feeafd9bd..63391a180 100644 --- a/src/video_core/buffer_cache/buffer.h +++ b/src/video_core/buffer_cache/buffer.h @@ -119,19 +119,23 @@ public: return buffer; } - std::optional GetBarrier(vk::AccessFlagBits2 dst_acess_mask, - vk::PipelineStageFlagBits2 dst_stage) { + std::optional GetBarrier( + vk::Flags dst_acess_mask, vk::PipelineStageFlagBits2 dst_stage, + u32 offset = 0) { if (dst_acess_mask == access_mask && stage == dst_stage) { return {}; } + DEBUG_ASSERT(offset < size_bytes); + auto barrier = vk::BufferMemoryBarrier2{ .srcStageMask = stage, .srcAccessMask = access_mask, .dstStageMask = dst_stage, .dstAccessMask = dst_acess_mask, .buffer = buffer.buffer, - .size = size_bytes, + .offset = offset, + .size = size_bytes - offset, }; access_mask = dst_acess_mask; stage = dst_stage; @@ -150,8 +154,10 @@ public: Vulkan::Scheduler* scheduler; MemoryUsage usage; UniqueBuffer buffer; - vk::AccessFlagBits2 access_mask{vk::AccessFlagBits2::eNone}; - vk::PipelineStageFlagBits2 stage{vk::PipelineStageFlagBits2::eNone}; + vk::Flags access_mask{ + vk::AccessFlagBits2::eMemoryRead | vk::AccessFlagBits2::eMemoryWrite | + vk::AccessFlagBits2::eTransferRead | vk::AccessFlagBits2::eTransferWrite}; + vk::PipelineStageFlagBits2 stage{vk::PipelineStageFlagBits2::eAllCommands}; }; class StreamBuffer : public Buffer { diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index 322a9dd4e..d5ebd85fc 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -479,43 +479,36 @@ void BufferCache::JoinOverlap(BufferId new_buffer_id, BufferId overlap_id, }; scheduler.EndRendering(); const auto cmdbuf = scheduler.CommandBuffer(); - const std::array pre_barriers = { - vk::BufferMemoryBarrier2{ - .srcStageMask = vk::PipelineStageFlagBits2::eAllCommands, - .srcAccessMask = vk::AccessFlagBits2::eMemoryRead | vk::AccessFlagBits2::eMemoryWrite, - .dstStageMask = vk::PipelineStageFlagBits2::eTransfer, - .dstAccessMask = vk::AccessFlagBits2::eTransferRead, - .buffer = overlap.Handle(), - .offset = 0, - .size = overlap.SizeBytes(), - }, - }; - const std::array post_barriers = { - vk::BufferMemoryBarrier2{ - .srcStageMask = vk::PipelineStageFlagBits2::eTransfer, - .srcAccessMask = vk::AccessFlagBits2::eTransferRead, - .dstStageMask = vk::PipelineStageFlagBits2::eAllCommands, - .dstAccessMask = vk::AccessFlagBits2::eMemoryWrite, - .buffer = overlap.Handle(), - .offset = 0, - .size = overlap.SizeBytes(), - }, - vk::BufferMemoryBarrier2{ - .srcStageMask = vk::PipelineStageFlagBits2::eTransfer, - .srcAccessMask = vk::AccessFlagBits2::eTransferWrite, - .dstStageMask = vk::PipelineStageFlagBits2::eAllCommands, - .dstAccessMask = vk::AccessFlagBits2::eMemoryRead | vk::AccessFlagBits2::eMemoryWrite, - .buffer = new_buffer.Handle(), - .offset = dst_base_offset, - .size = overlap.SizeBytes(), - }, - }; + + boost::container::static_vector pre_barriers{}; + if (auto src_barrier = overlap.GetBarrier(vk::AccessFlagBits2::eTransferRead, + vk::PipelineStageFlagBits2::eTransfer)) { + pre_barriers.push_back(*src_barrier); + } + if (auto dst_barrier = + new_buffer.GetBarrier(vk::AccessFlagBits2::eTransferWrite, + vk::PipelineStageFlagBits2::eTransfer, dst_base_offset)) { + pre_barriers.push_back(*dst_barrier); + } cmdbuf.pipelineBarrier2(vk::DependencyInfo{ .dependencyFlags = vk::DependencyFlagBits::eByRegion, - .bufferMemoryBarrierCount = 1, + .bufferMemoryBarrierCount = static_cast(pre_barriers.size()), .pBufferMemoryBarriers = pre_barriers.data(), }); + cmdbuf.copyBuffer(overlap.Handle(), new_buffer.Handle(), copy); + + boost::container::static_vector post_barriers{}; + if (auto src_barrier = + overlap.GetBarrier(vk::AccessFlagBits2::eMemoryRead | vk::AccessFlagBits2::eMemoryWrite, + vk::PipelineStageFlagBits2::eAllCommands)) { + post_barriers.push_back(*src_barrier); + } + if (auto dst_barrier = new_buffer.GetBarrier( + vk::AccessFlagBits2::eMemoryRead | vk::AccessFlagBits2::eMemoryWrite, + vk::PipelineStageFlagBits2::eAllCommands, dst_base_offset)) { + post_barriers.push_back(*dst_barrier); + } cmdbuf.pipelineBarrier2(vk::DependencyInfo{ .dependencyFlags = vk::DependencyFlagBits::eByRegion, .bufferMemoryBarrierCount = static_cast(post_barriers.size()), @@ -626,7 +619,8 @@ void BufferCache::SynchronizeBuffer(Buffer& buffer, VAddr device_addr, u32 size, const auto cmdbuf = scheduler.CommandBuffer(); const vk::BufferMemoryBarrier2 pre_barrier = { .srcStageMask = vk::PipelineStageFlagBits2::eAllCommands, - .srcAccessMask = vk::AccessFlagBits2::eMemoryRead, + .srcAccessMask = vk::AccessFlagBits2::eMemoryRead | vk::AccessFlagBits2::eMemoryWrite | + vk::AccessFlagBits2::eTransferRead | vk::AccessFlagBits2::eTransferWrite, .dstStageMask = vk::PipelineStageFlagBits2::eTransfer, .dstAccessMask = vk::AccessFlagBits2::eTransferWrite, .buffer = buffer.Handle(), From 39b511070ac781447b7015d115c80bc394e51e06 Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Tue, 7 Jan 2025 01:58:49 -0300 Subject: [PATCH 368/549] TR: remove 'location' (#2078) --- src/qt_gui/translations/ar.ts | 1018 ++++++++++------------------- src/qt_gui/translations/da_DK.ts | 1018 ++++++++++------------------- src/qt_gui/translations/de.ts | 1018 ++++++++++------------------- src/qt_gui/translations/el.ts | 1018 ++++++++++------------------- src/qt_gui/translations/en.ts | 1018 ++++++++++------------------- src/qt_gui/translations/es_ES.ts | 1018 ++++++++++------------------- src/qt_gui/translations/fa_IR.ts | 1020 ++++++++++------------------- src/qt_gui/translations/fi.ts | 1028 ++++++++++-------------------- src/qt_gui/translations/fr.ts | 1018 ++++++++++------------------- src/qt_gui/translations/hu_HU.ts | 1018 ++++++++++------------------- src/qt_gui/translations/id.ts | 1018 ++++++++++------------------- src/qt_gui/translations/it.ts | 1022 ++++++++++------------------- src/qt_gui/translations/ja_JP.ts | 1018 ++++++++++------------------- src/qt_gui/translations/ko_KR.ts | 1018 ++++++++++------------------- src/qt_gui/translations/lt_LT.ts | 1018 ++++++++++------------------- src/qt_gui/translations/nb.ts | 1018 ++++++++++------------------- src/qt_gui/translations/nl.ts | 1018 ++++++++++------------------- src/qt_gui/translations/pl_PL.ts | 1018 ++++++++++------------------- src/qt_gui/translations/pt_BR.ts | 1020 ++++++++++------------------- src/qt_gui/translations/ro_RO.ts | 1018 ++++++++++------------------- src/qt_gui/translations/ru_RU.ts | 1018 ++++++++++------------------- src/qt_gui/translations/sq.ts | 1020 ++++++++++------------------- src/qt_gui/translations/tr_TR.ts | 1018 ++++++++++------------------- src/qt_gui/translations/uk_UA.ts | 1018 ++++++++++------------------- src/qt_gui/translations/vi_VN.ts | 1018 ++++++++++------------------- src/qt_gui/translations/zh_CN.ts | 1020 ++++++++++------------------- src/qt_gui/translations/zh_TW.ts | 1018 ++++++++++------------------- 27 files changed, 9326 insertions(+), 18182 deletions(-) diff --git a/src/qt_gui/translations/ar.ts b/src/qt_gui/translations/ar.ts index e851f59a7..c1964356a 100644 --- a/src/qt_gui/translations/ar.ts +++ b/src/qt_gui/translations/ar.ts @@ -6,22 +6,18 @@ AboutDialog - About shadPS4 حول shadPS4 - shadPS4 shadPS4 - shadPS4 is an experimental open-source emulator for the PlayStation 4. shadPS4 هو محاكي تجريبي مفتوح المصدر لجهاز PlayStation 4. - This software should not be used to play games you have not legally obtained. يجب عدم استخدام هذا البرنامج لتشغيل الألعاب التي لم تحصل عليها بشكل قانوني. @@ -29,7 +25,6 @@ ElfViewer - Open Folder فتح المجلد @@ -37,17 +32,14 @@ GameInfoClass - Loading game list, please wait :3 جارٍ تحميل قائمة الألعاب، يرجى الانتظار :3 - Cancel إلغاء - Loading... ...جارٍ التحميل @@ -55,12 +47,10 @@ InstallDirSelect - shadPS4 - Choose directory shadPS4 - اختر المجلد - Select which directory you want to install to. Select which directory you want to install to. @@ -68,27 +58,22 @@ GameInstallDialog - shadPS4 - Choose directory shadPS4 - اختر المجلد - Directory to install games مجلد تثبيت الألعاب - Browse تصفح - Error خطأ - The value for location to install games is not valid. قيمة موقع تثبيت الألعاب غير صالحة. @@ -96,167 +81,134 @@ GuiContextMenus - Create Shortcut إنشاء اختصار - Cheats / Patches الغش / التصحيحات - SFO Viewer عارض SFO - Trophy Viewer عارض الجوائز - Open Folder... فتح المجلد... - Open Game Folder فتح مجلد اللعبة - Open Save Data Folder فتح مجلد بيانات الحفظ - Open Log Folder فتح مجلد السجل - Copy info... ...نسخ المعلومات - Copy Name نسخ الاسم - Copy Serial نسخ الرقم التسلسلي - Copy All نسخ الكل - Delete... Delete... - Delete Game Delete Game - Delete Update Delete Update - Delete DLC Delete DLC - Compatibility... Compatibility... - Update database Update database - View report View report - Submit a report Submit a report - Shortcut creation إنشاء اختصار - Shortcut created successfully! تم إنشاء الاختصار بنجاح! - Error خطأ - Error creating shortcut! خطأ في إنشاء الاختصار - Install PKG PKG تثبيت - Game Game - requiresEnableSeparateUpdateFolder_MSG This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. - This game has no update to delete! This game has no update to delete! - - + Update Update - This game has no DLC to delete! This game has no DLC to delete! - DLC DLC - Delete %1 Delete %1 - Are you sure you want to delete %1's %2 directory? Are you sure you want to delete %1's %2 directory? @@ -264,205 +216,285 @@ MainWindow - Open/Add Elf Folder Elf فتح/إضافة مجلد - Install Packages (PKG) (PKG) تثبيت الحزم - Boot Game تشغيل اللعبة - Check for Updates تحقق من التحديثات - About shadPS4 shadPS4 حول - Configure... ...تكوين - Install application from a .pkg file .pkg تثبيت التطبيق من ملف - Recent Games الألعاب الأخيرة - Exit خروج - Exit shadPS4 الخروج من shadPS4 - Exit the application. الخروج من التطبيق. - Show Game List إظهار قائمة الألعاب - Game List Refresh تحديث قائمة الألعاب - Tiny صغير جدًا - Small صغير - Medium متوسط - Large كبير - List View عرض القائمة - Grid View عرض الشبكة - Elf Viewer عارض Elf - Game Install Directory دليل تثبيت اللعبة - Download Cheats/Patches تنزيل الغش/التصحيحات - Dump Game List تفريغ قائمة الألعاب - PKG Viewer عارض PKG - Search... ...بحث - File ملف - View عرض - Game List Icons أيقونات قائمة الألعاب - Game List Mode وضع قائمة الألعاب - Settings الإعدادات - Utils الأدوات - Themes السمات - Help مساعدة - Dark داكن - Light فاتح - Green أخضر - Blue أزرق - Violet بنفسجي - toolBar شريط الأدوات + + Game List + ققائمة الألعاب + + + * Unsupported Vulkan Version + * إصدار Vulkan غير مدعوم + + + Download Cheats For All Installed Games + تنزيل الغش لجميع الألعاب المثبتة + + + Download Patches For All Games + تنزيل التصحيحات لجميع الألعاب + + + Download Complete + اكتمل التنزيل + + + You have downloaded cheats for all the games you have installed. + لقد قمت بتنزيل الغش لجميع الألعاب التي قمت بتثبيتها. + + + Patches Downloaded Successfully! + !تم تنزيل التصحيحات بنجاح + + + All Patches available for all games have been downloaded. + .تم تنزيل جميع التصحيحات المتاحة لجميع الألعاب + + + Games: + :الألعاب + + + PKG File (*.PKG) + PKG (*.PKG) ملف + + + ELF files (*.bin *.elf *.oelf) + ELF (*.bin *.elf *.oelf) ملفات + + + Game Boot + تشغيل اللعبة + + + Only one file can be selected! + !يمكن تحديد ملف واحد فقط + + + PKG Extraction + PKG استخراج + + + Patch detected! + تم اكتشاف تصحيح! + + + PKG and Game versions match: + :واللعبة تتطابق إصدارات PKG + + + Would you like to overwrite? + هل ترغب في الكتابة فوق الملف الموجود؟ + + + PKG Version %1 is older than installed version: + :أقدم من الإصدار المثبت PKG Version %1 + + + Game is installed: + :اللعبة مثبتة + + + Would you like to install Patch: + :هل ترغب في تثبيت التصحيح + + + DLC Installation + تثبيت المحتوى القابل للتنزيل + + + Would you like to install DLC: %1? + هل ترغب في تثبيت المحتوى القابل للتنزيل: 1%؟ + + + DLC already installed: + :المحتوى القابل للتنزيل مثبت بالفعل + + + Game already installed + اللعبة مثبتة بالفعل + + + PKG is a patch, please install the game first! + !PKG هو تصحيح، يرجى تثبيت اللعبة أولاً + + + PKG ERROR + PKG خطأ في + + + Extracting PKG %1/%2 + PKG %1/%2 جاري استخراج + + + Extraction Finished + اكتمل الاستخراج + + + Game successfully installed at %1 + تم تثبيت اللعبة بنجاح في %1 + + + File doesn't appear to be a valid PKG file + يبدو أن الملف ليس ملف PKG صالحًا + PKGViewer - Open Folder فتح المجلد @@ -470,7 +502,6 @@ TrophyViewer - Trophy Viewer عارض الجوائز @@ -478,1029 +509,700 @@ SettingsDialog - Settings الإعدادات - General عام - System النظام - Console Language لغة وحدة التحكم - Emulator Language لغة المحاكي - Emulator المحاكي - Enable Fullscreen تمكين ملء الشاشة - Enable Separate Update Folder Enable Separate Update Folder - Show Splash إظهار شاشة البداية - Is PS4 Pro PS4 Pro هل هو - Enable Discord Rich Presence تفعيل حالة الثراء في ديسكورد - Username اسم المستخدم - Trophy Key Trophy Key - Trophy Trophy - Logger المسجل - Log Type نوع السجل - Log Filter مرشح السجل - Input إدخال - Cursor مؤشر - Hide Cursor إخفاء المؤشر - Hide Cursor Idle Timeout مهلة إخفاء المؤشر عند الخمول - s s - Controller التحكم - Back Button Behavior سلوك زر العودة - Graphics الرسومات - Graphics Device جهاز الرسومات - Width العرض - Height الارتفاع - Vblank Divider Vblank مقسم - Advanced متقدم - Enable Shaders Dumping تمكين تفريغ الشيدرات - Enable NULL GPU تمكين وحدة معالجة الرسومات الفارغة - Paths المسارات - Game Folders مجلدات اللعبة - Add... إضافة... - Remove إزالة - Debug تصحيح الأخطاء - Enable Debug Dumping تمكين تفريغ التصحيح - Enable Vulkan Validation Layers Vulkan تمكين طبقات التحقق من - Enable Vulkan Synchronization Validation Vulkan تمكين التحقق من تزامن - Enable RenderDoc Debugging RenderDoc تمكين تصحيح أخطاء - Update تحديث - Check for Updates at Startup تحقق من التحديثات عند بدء التشغيل - Update Channel قناة التحديث - Check for Updates التحقق من التحديثات - GUI Settings إعدادات الواجهة - Disable Trophy Pop-ups Disable Trophy Pop-ups - Play title music تشغيل موسيقى العنوان - Update Compatibility Database On Startup Update Compatibility Database On Startup - Game Compatibility Game Compatibility - Display Compatibility Data Display Compatibility Data - Update Compatibility Database Update Compatibility Database - Volume الصوت - Audio Backend Audio Backend - - - MainWindow - - Game List - ققائمة الألعاب - - - - * Unsupported Vulkan Version - * إصدار Vulkan غير مدعوم - - - - Download Cheats For All Installed Games - تنزيل الغش لجميع الألعاب المثبتة - - - - Download Patches For All Games - تنزيل التصحيحات لجميع الألعاب - - - - Download Complete - اكتمل التنزيل - - - - You have downloaded cheats for all the games you have installed. - لقد قمت بتنزيل الغش لجميع الألعاب التي قمت بتثبيتها. - - - - Patches Downloaded Successfully! - !تم تنزيل التصحيحات بنجاح - - - - All Patches available for all games have been downloaded. - .تم تنزيل جميع التصحيحات المتاحة لجميع الألعاب - - - - Games: - :الألعاب - - - - PKG File (*.PKG) - PKG (*.PKG) ملف - - - - ELF files (*.bin *.elf *.oelf) - ELF (*.bin *.elf *.oelf) ملفات - - - - Game Boot - تشغيل اللعبة - - - - Only one file can be selected! - !يمكن تحديد ملف واحد فقط - - - - PKG Extraction - PKG استخراج - - - - Patch detected! - تم اكتشاف تصحيح! - - - - PKG and Game versions match: - :واللعبة تتطابق إصدارات PKG - - - - Would you like to overwrite? - هل ترغب في الكتابة فوق الملف الموجود؟ - - - - PKG Version %1 is older than installed version: - :أقدم من الإصدار المثبت PKG Version %1 - - - - Game is installed: - :اللعبة مثبتة - - - - Would you like to install Patch: - :هل ترغب في تثبيت التصحيح - - - - DLC Installation - تثبيت المحتوى القابل للتنزيل - - - - Would you like to install DLC: %1? - هل ترغب في تثبيت المحتوى القابل للتنزيل: 1%؟ - - - - DLC already installed: - :المحتوى القابل للتنزيل مثبت بالفعل - - - - Game already installed - اللعبة مثبتة بالفعل - - - - PKG is a patch, please install the game first! - !PKG هو تصحيح، يرجى تثبيت اللعبة أولاً - - - - PKG ERROR - PKG خطأ في - - - - Extracting PKG %1/%2 - PKG %1/%2 جاري استخراج - - - - Extraction Finished - اكتمل الاستخراج - - - - Game successfully installed at %1 - تم تثبيت اللعبة بنجاح في %1 - - - - File doesn't appear to be a valid PKG file - يبدو أن الملف ليس ملف PKG صالحًا - - - - CheatsPatches - - - Cheats / Patches for - Cheats / Patches for - - - - defaultTextEdit_MSG - الغش والتصحيحات هي ميزات تجريبية.\nاستخدمها بحذر.\n\nقم بتنزيل الغش بشكل فردي عن طريق اختيار المستودع والنقر على زر التنزيل.\nفي علامة تبويب التصحيحات، يمكنك تنزيل جميع التصحيحات دفعة واحدة، واختيار ما تريد استخدامه، وحفظ اختياراتك.\n\nنظرًا لأننا لا نقوم بتطوير الغش/التصحيحات،\nيرجى الإبلاغ عن أي مشاكل إلى مؤلف الغش.\n\nهل قمت بإنشاء غش جديد؟ قم بزيارة:\nhttps://github.com/shadps4-emu/ps4_cheats - - - - No Image Available - لا تتوفر صورة - - - - Serial: - الرقم التسلسلي: - - - - Version: - الإصدار: - - - - Size: - الحجم: - - - - Select Cheat File: - اختر ملف الغش: - - - - Repository: - المستودع: - - - - Download Cheats - تنزيل الغش - - - - Delete File - حذف الملف - - - - No files selected. - لم يتم اختيار أي ملفات. - - - - You can delete the cheats you don't want after downloading them. - يمكنك حذف الغش الذي لا تريده بعد تنزيله. - - - - Do you want to delete the selected file?\n%1 - هل تريد حذف الملف المحدد؟\n%1 - - - - Select Patch File: - اختر ملف التصحيح: - - - - Download Patches - تنزيل التصحيحات - - - Save حفظ - - Cheats - الغش - - - - Patches - التصحيحات - - - - Error - خطأ - - - - No patch selected. - لم يتم اختيار أي تصحيح. - - - - Unable to open files.json for reading. - تعذر فتح files.json للقراءة. - - - - No patch file found for the current serial. - لم يتم العثور على ملف تصحيح للرقم التسلسلي الحالي. - - - - Unable to open the file for reading. - تعذر فتح الملف للقراءة. - - - - Unable to open the file for writing. - تعذر فتح الملف للكتابة. - - - - Failed to parse XML: - :فشل في تحليل XML - - - - Success - نجاح - - - - Options saved successfully. - تم حفظ الخيارات بنجاح. - - - - Invalid Source - مصدر غير صالح - - - - The selected source is invalid. - المصدر المحدد غير صالح. - - - - File Exists - الملف موجود - - - - File already exists. Do you want to replace it? - الملف موجود بالفعل. هل تريد استبداله؟ - - - - Failed to save file: - :فشل في حفظ الملف - - - - Failed to download file: - :فشل في تنزيل الملف - - - - Cheats Not Found - لم يتم العثور على الغش - - - - CheatsNotFound_MSG - لم يتم العثور على غش لهذه اللعبة في هذا الإصدار من المستودع المحدد. حاول استخدام مستودع آخر أو إصدار آخر من اللعبة. - - - - Cheats Downloaded Successfully - تم تنزيل الغش بنجاح - - - - CheatsDownloadedSuccessfully_MSG - لقد نجحت في تنزيل الغش لهذا الإصدار من اللعبة من المستودع المحدد. يمكنك محاولة التنزيل من مستودع آخر. إذا كان متاحًا، يمكنك اختياره عن طريق تحديد الملف من القائمة. - - - - Failed to save: - :فشل في الحفظ - - - - Failed to download: - :فشل في التنزيل - - - - Download Complete - اكتمل التنزيل - - - - DownloadComplete_MSG - تم تنزيل التصحيحات بنجاح! تم تنزيل جميع التصحيحات لجميع الألعاب، ولا داعي لتنزيلها بشكل فردي لكل لعبة كما هو الحال مع الغش. إذا لم يظهر التحديث، قد يكون السبب أنه غير متوفر للإصدار وسيريال اللعبة المحدد. - - - - Failed to parse JSON data from HTML. - فشل في تحليل بيانات JSON من HTML. - - - - Failed to retrieve HTML page. - .HTML فشل في استرجاع صفحة - - - - The game is in version: %1 - اللعبة في الإصدار: %1 - - - - The downloaded patch only works on version: %1 - الباتش الذي تم تنزيله يعمل فقط على الإصدار: %1 - - - - You may need to update your game. - قد تحتاج إلى تحديث لعبتك. - - - - Incompatibility Notice - إشعار عدم التوافق - - - - Failed to open file: - :فشل في فتح الملف - - - - XML ERROR: - :خطأ في XML - - - - Failed to open files.json for writing - فشل في فتح files.json للكتابة - - - - Author: - :المؤلف - - - - Directory does not exist: - :المجلد غير موجود - - - - Failed to open files.json for reading. - فشل في فتح files.json للقراءة. - - - - Name: - :الاسم - - - - Can't apply cheats before the game is started - لا يمكن تطبيق الغش قبل بدء اللعبة. - - - - SettingsDialog - - - Save - حفظ - - - Apply تطبيق - Restore Defaults استعادة الإعدادات الافتراضية - Close إغلاق - Point your mouse at an option to display its description. وجّه الماوس نحو خيار لعرض وصفه. - consoleLanguageGroupBox لغة الجهاز:\nتحدد لغة اللعبة التي يستخدمها جهاز PS4.\nيوصى بضبطها على لغة يدعمها الجهاز، والتي قد تختلف حسب المنطقة. - emulatorLanguageGroupBox لغة المحاكي:\nتحدد لغة واجهة المستخدم الخاصة بالمحاكي. - fullscreenCheckBox تمكين وضع ملء الشاشة:\nيجعل نافذة اللعبة تنتقل تلقائيًا إلى وضع ملء الشاشة.\nيمكن التبديل بالضغط على المفتاح F11. - separateUpdatesCheckBox Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. - showSplashCheckBox إظهار شاشة البداية:\nيعرض شاشة البداية الخاصة باللعبة (صورة خاصة) أثناء بدء التشغيل. - ps4proCheckBox هل هو PS4 Pro:\nيجعل المحاكي يعمل كـ PS4 PRO، مما قد يتيح ميزات خاصة في الألعاب التي تدعمه. - discordRPCCheckbox تفعيل حالة الثراء في ديسكورد:\nيعرض أيقونة المحاكي ومعلومات ذات صلة على ملفك الشخصي في ديسكورد. - userName اسم المستخدم:\nيضبط اسم حساب PS4، الذي قد يتم عرضه في بعض الألعاب. - TrophyKey Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. - logTypeGroupBox نوع السجل:\nيضبط ما إذا كان سيتم مزامنة مخرجات نافذة السجل للأداء. قد يؤثر سلبًا على المحاكاة. - logFilter فلتر السجل:\nيقوم بتصفية السجل لطباعة معلومات محددة فقط.\nأمثلة: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" المستويات: Trace, Debug, Info, Warning, Error, Critical - بالترتيب، مستوى محدد يخفي جميع المستويات التي تسبقه ويعرض جميع المستويات بعده. - updaterGroupBox تحديث: Release: إصدارات رسمية تصدر شهريًا، قد تكون قديمة بعض الشيء، لكنها أكثر استقرارًا واختبارًا. Nightly: إصدارات تطوير تحتوي على أحدث الميزات والإصلاحات، لكنها قد تحتوي على أخطاء وأقل استقرارًا. - GUIgroupBox تشغيل موسيقى العنوان:\nإذا كانت اللعبة تدعم ذلك، قم بتمكين تشغيل موسيقى خاصة عند اختيار اللعبة في واجهة المستخدم. - disableTrophycheckBox Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). - hideCursorGroupBox إخفاء المؤشر:\nاختر متى سيختفي المؤشر:\nأبداً: سترى الفأرة دائماً.\nعاطل: حدد وقتاً لاختفائه بعد أن يكون غير مستخدم.\nدائماً: لن ترى الفأرة أبداً. - idleTimeoutGroupBox حدد وقتاً لاختفاء الفأرة بعد أن تكون غير مستخدم. - backButtonBehaviorGroupBox سلوك زر العودة:\nيضبط زر العودة في وحدة التحكم ليحاكي الضغط على الموضع المحدد على لوحة اللمس في PS4. - enableCompatibilityCheckBox Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. - checkCompatibilityOnStartupCheckBox Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. - updateCompatibilityButton Update Compatibility Database:\nImmediately update the compatibility database. - Never أبداً - Idle خامل - Always دائماً - Touchpad Left لوحة اللمس اليسرى - Touchpad Right لوحة اللمس اليمنى - Touchpad Center وسط لوحة اللمس - None لا شيء - graphicsAdapterGroupBox جهاز الرسومات:\nعلى الأنظمة متعددة وحدات معالجة الرسومات، اختر وحدة معالجة الرسومات التي سيستخدمها المحاكي من قائمة منسدلة،\nأو اختر "Auto Select" لتحديدها تلقائيًا. - resolutionLayout العرض / الارتفاع:\nيضبط حجم نافذة المحاكي عند التشغيل، والذي يمكن تغيير حجمه أثناء اللعب.\nهذا يختلف عن دقة اللعبة نفسها. - heightDivider مقسم معدل التحديث:\nيتم مضاعفة معدل الإطارات الذي يتم تحديث المحاكي به بواسطة هذا الرقم. قد يؤدي تغيير هذا إلى آثار سلبية، مثل زيادة سرعة اللعبة أو كسر الوظائف الأساسية التي لا تتوقع هذا التغيير! - dumpShadersCheckBox تمكين تفريغ الـ Shaders:\nلأغراض تصحيح الأخطاء التقنية، يحفظ الـ Shaders الخاصة باللعبة في مجلد أثناء التشغيل. - nullGpuCheckBox تمكين GPU الافتراضية:\nلأغراض تصحيح الأخطاء التقنية، يقوم بتعطيل عرض اللعبة كما لو لم يكن هناك بطاقة رسومات. - gameFoldersBox مجلدات اللعبة:\nقائمة بالمجلدات للتحقق من الألعاب المثبتة. - addFolderButton إضافة:\nأضف مجلداً إلى القائمة. - removeFolderButton إزالة:\nأزل مجلداً من القائمة. - debugDump تمكين تفريغ التصحيح:\nيحفظ رموز الاستيراد والتصدير ومعلومات رأس الملف للبرنامج الحالي لجهاز PS4 إلى دليل. - vkValidationCheckBox تمكين طبقات التحقق من Vulkan:\nيتيح نظام يتحقق من حالة مشغل Vulkan ويسجل معلومات حول حالته الداخلية. سيؤدي هذا إلى تقليل الأداء ومن المحتمل تغيير سلوك المحاكاة. - vkSyncValidationCheckBox تمكين التحقق من تزامن Vulkan:\nيتيح نظام يتحقق من توقيت مهام عرض Vulkan. سيؤدي ذلك إلى تقليل الأداء ومن المحتمل تغيير سلوك المحاكاة. - rdocCheckBox تمكين تصحيح RenderDoc:\nإذا تم التمكين، سيوفر المحاكي توافقًا مع Renderdoc لالتقاط وتحليل الإطار الذي يتم عرضه حاليًا. + + CheatsPatches + + Cheats / Patches for + Cheats / Patches for + + + defaultTextEdit_MSG + الغش والتصحيحات هي ميزات تجريبية.\nاستخدمها بحذر.\n\nقم بتنزيل الغش بشكل فردي عن طريق اختيار المستودع والنقر على زر التنزيل.\nفي علامة تبويب التصحيحات، يمكنك تنزيل جميع التصحيحات دفعة واحدة، واختيار ما تريد استخدامه، وحفظ اختياراتك.\n\nنظرًا لأننا لا نقوم بتطوير الغش/التصحيحات،\nيرجى الإبلاغ عن أي مشاكل إلى مؤلف الغش.\n\nهل قمت بإنشاء غش جديد؟ قم بزيارة:\nhttps://github.com/shadps4-emu/ps4_cheats + + + No Image Available + لا تتوفر صورة + + + Serial: + الرقم التسلسلي: + + + Version: + الإصدار: + + + Size: + الحجم: + + + Select Cheat File: + اختر ملف الغش: + + + Repository: + المستودع: + + + Download Cheats + تنزيل الغش + + + Delete File + حذف الملف + + + No files selected. + لم يتم اختيار أي ملفات. + + + You can delete the cheats you don't want after downloading them. + يمكنك حذف الغش الذي لا تريده بعد تنزيله. + + + Do you want to delete the selected file?\n%1 + هل تريد حذف الملف المحدد؟\n%1 + + + Select Patch File: + اختر ملف التصحيح: + + + Download Patches + تنزيل التصحيحات + + + Save + حفظ + + + Cheats + الغش + + + Patches + التصحيحات + + + Error + خطأ + + + No patch selected. + لم يتم اختيار أي تصحيح. + + + Unable to open files.json for reading. + تعذر فتح files.json للقراءة. + + + No patch file found for the current serial. + لم يتم العثور على ملف تصحيح للرقم التسلسلي الحالي. + + + Unable to open the file for reading. + تعذر فتح الملف للقراءة. + + + Unable to open the file for writing. + تعذر فتح الملف للكتابة. + + + Failed to parse XML: + :فشل في تحليل XML + + + Success + نجاح + + + Options saved successfully. + تم حفظ الخيارات بنجاح. + + + Invalid Source + مصدر غير صالح + + + The selected source is invalid. + المصدر المحدد غير صالح. + + + File Exists + الملف موجود + + + File already exists. Do you want to replace it? + الملف موجود بالفعل. هل تريد استبداله؟ + + + Failed to save file: + :فشل في حفظ الملف + + + Failed to download file: + :فشل في تنزيل الملف + + + Cheats Not Found + لم يتم العثور على الغش + + + CheatsNotFound_MSG + لم يتم العثور على غش لهذه اللعبة في هذا الإصدار من المستودع المحدد. حاول استخدام مستودع آخر أو إصدار آخر من اللعبة. + + + Cheats Downloaded Successfully + تم تنزيل الغش بنجاح + + + CheatsDownloadedSuccessfully_MSG + لقد نجحت في تنزيل الغش لهذا الإصدار من اللعبة من المستودع المحدد. يمكنك محاولة التنزيل من مستودع آخر. إذا كان متاحًا، يمكنك اختياره عن طريق تحديد الملف من القائمة. + + + Failed to save: + :فشل في الحفظ + + + Failed to download: + :فشل في التنزيل + + + Download Complete + اكتمل التنزيل + + + DownloadComplete_MSG + تم تنزيل التصحيحات بنجاح! تم تنزيل جميع التصحيحات لجميع الألعاب، ولا داعي لتنزيلها بشكل فردي لكل لعبة كما هو الحال مع الغش. إذا لم يظهر التحديث، قد يكون السبب أنه غير متوفر للإصدار وسيريال اللعبة المحدد. + + + Failed to parse JSON data from HTML. + فشل في تحليل بيانات JSON من HTML. + + + Failed to retrieve HTML page. + .HTML فشل في استرجاع صفحة + + + The game is in version: %1 + اللعبة في الإصدار: %1 + + + The downloaded patch only works on version: %1 + الباتش الذي تم تنزيله يعمل فقط على الإصدار: %1 + + + You may need to update your game. + قد تحتاج إلى تحديث لعبتك. + + + Incompatibility Notice + إشعار عدم التوافق + + + Failed to open file: + :فشل في فتح الملف + + + XML ERROR: + :خطأ في XML + + + Failed to open files.json for writing + فشل في فتح files.json للكتابة + + + Author: + :المؤلف + + + Directory does not exist: + :المجلد غير موجود + + + Failed to open files.json for reading. + فشل في فتح files.json للقراءة. + + + Name: + :الاسم + + + Can't apply cheats before the game is started + لا يمكن تطبيق الغش قبل بدء اللعبة. + + GameListFrame - Icon أيقونة - Name اسم - Serial سيريال - Compatibility Compatibility - Region منطقة - Firmware البرمجيات الثابتة - Size حجم - Version إصدار - Path مسار - Play Time وقت اللعب - Never Played Never Played - h h - m m - s s - Compatibility is untested Compatibility is untested - Game does not initialize properly / crashes the emulator Game does not initialize properly / crashes the emulator - Game boots, but only displays a blank screen Game boots, but only displays a blank screen - Game displays an image but does not go past the menu Game displays an image but does not go past the menu - Game has game-breaking glitches or unplayable performance Game has game-breaking glitches or unplayable performance - Game can be completed with playable performance and no major glitches Game can be completed with playable performance and no major glitches @@ -1508,127 +1210,102 @@ CheckUpdate - Auto Updater محدث تلقائي - Error خطأ - Network error: خطأ في الشبكة: - Failed to parse update information. فشل في تحليل معلومات التحديث. - No pre-releases found. لم يتم العثور على أي إصدارات مسبقة. - Invalid release data. بيانات الإصدار غير صالحة. - No download URL found for the specified asset. لم يتم العثور على عنوان URL للتنزيل للأصل المحدد. - Your version is already up to date! نسختك محدثة بالفعل! - Update Available تحديث متاح - Update Channel قناة التحديث - Current Version الإصدار الحالي - Latest Version آخر إصدار - Do you want to update? هل تريد التحديث؟ - Show Changelog عرض سجل التغييرات - Check for Updates at Startup تحقق من التحديثات عند بدء التشغيل - Update تحديث - No لا - Hide Changelog إخفاء سجل التغييرات - Changes تغييرات - Network error occurred while trying to access the URL حدث خطأ في الشبكة أثناء محاولة الوصول إلى عنوان URL - Download Complete اكتمل التنزيل - The update has been downloaded, press OK to install. تم تنزيل التحديث، اضغط على OK للتثبيت. - Failed to save the update file at فشل في حفظ ملف التحديث في - Starting Update... بدء التحديث... - Failed to create the update script file فشل في إنشاء ملف سكريبت التحديث @@ -1636,29 +1313,24 @@ GameListUtils - B B - KB KB - MB MB - GB GB - TB TB - + \ No newline at end of file diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts index 41319c7ff..51fa2ca5f 100644 --- a/src/qt_gui/translations/da_DK.ts +++ b/src/qt_gui/translations/da_DK.ts @@ -6,22 +6,18 @@ AboutDialog - About shadPS4 About shadPS4 - shadPS4 shadPS4 - shadPS4 is an experimental open-source emulator for the PlayStation 4. shadPS4 is an experimental open-source emulator for the PlayStation 4. - This software should not be used to play games you have not legally obtained. This software should not be used to play games you have not legally obtained. @@ -29,7 +25,6 @@ ElfViewer - Open Folder Open Folder @@ -37,17 +32,14 @@ GameInfoClass - Loading game list, please wait :3 Loading game list, please wait :3 - Cancel Cancel - Loading... Loading... @@ -55,12 +47,10 @@ InstallDirSelect - shadPS4 - Choose directory shadPS4 - Choose directory - Select which directory you want to install to. Select which directory you want to install to. @@ -68,27 +58,22 @@ GameInstallDialog - shadPS4 - Choose directory shadPS4 - Choose directory - Directory to install games Directory to install games - Browse Browse - Error Error - The value for location to install games is not valid. The value for location to install games is not valid. @@ -96,167 +81,134 @@ GuiContextMenus - Create Shortcut Create Shortcut - Cheats / Patches Trick / Patches - SFO Viewer SFO Viewer - Trophy Viewer Trophy Viewer - Open Folder... Åbn Mappe... - Open Game Folder Åbn Spilmappe - Open Save Data Folder Åbn Gem Data Mappe - Open Log Folder Åbn Log Mappe - Copy info... Copy info... - Copy Name Copy Name - Copy Serial Copy Serial - Copy All Copy All - Delete... Delete... - Delete Game Delete Game - Delete Update Delete Update - Delete DLC Delete DLC - Compatibility... Compatibility... - Update database Update database - View report View report - Submit a report Submit a report - Shortcut creation Shortcut creation - Shortcut created successfully! Shortcut created successfully! - Error Error - Error creating shortcut! Error creating shortcut! - Install PKG Install PKG - Game Game - requiresEnableSeparateUpdateFolder_MSG This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. - This game has no update to delete! This game has no update to delete! - - + Update Update - This game has no DLC to delete! This game has no DLC to delete! - DLC DLC - Delete %1 Delete %1 - Are you sure you want to delete %1's %2 directory? Are you sure you want to delete %1's %2 directory? @@ -264,205 +216,285 @@ MainWindow - Open/Add Elf Folder Open/Add Elf Folder - Install Packages (PKG) Install Packages (PKG) - Boot Game Boot Game - Check for Updates Tjek for opdateringer - About shadPS4 About shadPS4 - Configure... Configure... - Install application from a .pkg file Install application from a .pkg file - Recent Games Recent Games - Exit Exit - Exit shadPS4 Exit shadPS4 - Exit the application. Exit the application. - Show Game List Show Game List - Game List Refresh Game List Refresh - Tiny Tiny - Small Small - Medium Medium - Large Large - List View List View - Grid View Grid View - Elf Viewer Elf Viewer - Game Install Directory Game Install Directory - Download Cheats/Patches Download Tricks / Patches - Dump Game List Dump Game List - PKG Viewer PKG Viewer - Search... Search... - File File - View View - Game List Icons Game List Icons - Game List Mode Game List Mode - Settings Settings - Utils Utils - Themes Themes - Help Hjælp - Dark Dark - Light Light - Green Green - Blue Blue - Violet Violet - toolBar toolBar + + Game List + Spiloversigt + + + * Unsupported Vulkan Version + * Ikke understøttet Vulkan-version + + + Download Cheats For All Installed Games + Hent snyd til alle installerede spil + + + Download Patches For All Games + Hent patches til alle spil + + + Download Complete + Download fuldført + + + You have downloaded cheats for all the games you have installed. + Du har hentet snyd til alle de spil, du har installeret. + + + Patches Downloaded Successfully! + Patcher hentet med succes! + + + All Patches available for all games have been downloaded. + Alle patches til alle spil er blevet hentet. + + + Games: + Spil: + + + PKG File (*.PKG) + PKG-fil (*.PKG) + + + ELF files (*.bin *.elf *.oelf) + ELF-filer (*.bin *.elf *.oelf) + + + Game Boot + Spil-boot + + + Only one file can be selected! + Kun én fil kan vælges! + + + PKG Extraction + PKG-udtrækning + + + Patch detected! + Opdatering detekteret! + + + PKG and Game versions match: + PKG og spilversioner matcher: + + + Would you like to overwrite? + Vil du overskrive? + + + PKG Version %1 is older than installed version: + PKG Version %1 er ældre end den installerede version: + + + Game is installed: + Spillet er installeret: + + + Would you like to install Patch: + Vil du installere opdateringen: + + + DLC Installation + DLC Installation + + + Would you like to install DLC: %1? + Vil du installere DLC: %1? + + + DLC already installed: + DLC allerede installeret: + + + Game already installed + Spillet er allerede installeret + + + PKG is a patch, please install the game first! + PKG er en patch, venligst installer spillet først! + + + PKG ERROR + PKG FEJL + + + Extracting PKG %1/%2 + Udvinding af PKG %1/%2 + + + Extraction Finished + Udvinding afsluttet + + + Game successfully installed at %1 + Spillet blev installeret succesfuldt på %1 + + + File doesn't appear to be a valid PKG file + Filen ser ikke ud til at være en gyldig PKG-fil + PKGViewer - Open Folder Open Folder @@ -470,7 +502,6 @@ TrophyViewer - Trophy Viewer Trophy Viewer @@ -478,1029 +509,700 @@ SettingsDialog - Settings Settings - General General - System System - Console Language Console Language - Emulator Language Emulator Language - Emulator Emulator - Enable Fullscreen Enable Fullscreen - Enable Separate Update Folder Enable Separate Update Folder - Show Splash Show Splash - Is PS4 Pro Is PS4 Pro - Enable Discord Rich Presence Aktiver Discord Rich Presence - Username Username - Trophy Key Trophy Key - Trophy Trophy - Logger Logger - Log Type Log Type - Log Filter Log Filter - Input Indtastning - Cursor Markør - Hide Cursor Skjul markør - Hide Cursor Idle Timeout Timeout for skjul markør ved inaktivitet - s s - Controller Controller - Back Button Behavior Tilbageknap adfærd - Graphics Graphics - Graphics Device Graphics Device - Width Width - Height Height - Vblank Divider Vblank Divider - Advanced Advanced - Enable Shaders Dumping Enable Shaders Dumping - Enable NULL GPU Enable NULL GPU - Paths Stier - Game Folders Spilmapper - Add... Tilføj... - Remove Fjern - Debug Debug - Enable Debug Dumping Enable Debug Dumping - Enable Vulkan Validation Layers Enable Vulkan Validation Layers - Enable Vulkan Synchronization Validation Enable Vulkan Synchronization Validation - Enable RenderDoc Debugging Enable RenderDoc Debugging - Update Opdatering - Check for Updates at Startup Tjek for opdateringer ved start - Update Channel Opdateringskanal - Check for Updates Tjek for opdateringer - GUI Settings GUI-Indstillinger - Disable Trophy Pop-ups Disable Trophy Pop-ups - Play title music Afspil titelsang - Update Compatibility Database On Startup Update Compatibility Database On Startup - Game Compatibility Game Compatibility - Display Compatibility Data Display Compatibility Data - Update Compatibility Database Update Compatibility Database - Volume Lydstyrke - Audio Backend Audio Backend - - - MainWindow - - Game List - Spiloversigt - - - - * Unsupported Vulkan Version - * Ikke understøttet Vulkan-version - - - - Download Cheats For All Installed Games - Hent snyd til alle installerede spil - - - - Download Patches For All Games - Hent patches til alle spil - - - - Download Complete - Download fuldført - - - - You have downloaded cheats for all the games you have installed. - Du har hentet snyd til alle de spil, du har installeret. - - - - Patches Downloaded Successfully! - Patcher hentet med succes! - - - - All Patches available for all games have been downloaded. - Alle patches til alle spil er blevet hentet. - - - - Games: - Spil: - - - - PKG File (*.PKG) - PKG-fil (*.PKG) - - - - ELF files (*.bin *.elf *.oelf) - ELF-filer (*.bin *.elf *.oelf) - - - - Game Boot - Spil-boot - - - - Only one file can be selected! - Kun én fil kan vælges! - - - - PKG Extraction - PKG-udtrækning - - - - Patch detected! - Opdatering detekteret! - - - - PKG and Game versions match: - PKG og spilversioner matcher: - - - - Would you like to overwrite? - Vil du overskrive? - - - - PKG Version %1 is older than installed version: - PKG Version %1 er ældre end den installerede version: - - - - Game is installed: - Spillet er installeret: - - - - Would you like to install Patch: - Vil du installere opdateringen: - - - - DLC Installation - DLC Installation - - - - Would you like to install DLC: %1? - Vil du installere DLC: %1? - - - - DLC already installed: - DLC allerede installeret: - - - - Game already installed - Spillet er allerede installeret - - - - PKG is a patch, please install the game first! - PKG er en patch, venligst installer spillet først! - - - - PKG ERROR - PKG FEJL - - - - Extracting PKG %1/%2 - Udvinding af PKG %1/%2 - - - - Extraction Finished - Udvinding afsluttet - - - - Game successfully installed at %1 - Spillet blev installeret succesfuldt på %1 - - - - File doesn't appear to be a valid PKG file - Filen ser ikke ud til at være en gyldig PKG-fil - - - - CheatsPatches - - - Cheats / Patches for - Cheats / Patches for - - - - defaultTextEdit_MSG - Cheats/Patches er eksperimentelle.\nBrug med forsigtighed.\n\nDownload cheats individuelt ved at vælge lageret og klikke på download-knappen.\nUnder fanen Patches kan du downloade alle patches på én gang, vælge hvilke du vil bruge og gemme valget.\n\nDa vi ikke udvikler cheats/patches,\nvenligst rapporter problemer til cheat-udvikleren.\n\nHar du lavet en ny cheat? Besøg:\nhttps://github.com/shadps4-emu/ps4_cheats - - - - No Image Available - Ingen billede tilgængelig - - - - Serial: - Serienummer: - - - - Version: - Version: - - - - Size: - Størrelse: - - - - Select Cheat File: - Vælg snyd-fil: - - - - Repository: - Repository: - - - - Download Cheats - Hent snyd - - - - Delete File - Slet fil - - - - No files selected. - Ingen filer valgt. - - - - You can delete the cheats you don't want after downloading them. - Du kan slette de snyd, du ikke ønsker, efter at have hentet dem. - - - - Do you want to delete the selected file?\n%1 - Ønsker du at slette den valgte fil?\n%1 - - - - Select Patch File: - Vælg patch-fil: - - - - Download Patches - Hent patches - - - Save Gem - - Cheats - Snyd - - - - Patches - Patches - - - - Error - Fejl - - - - No patch selected. - Ingen patch valgt. - - - - Unable to open files.json for reading. - Kan ikke åbne files.json til læsning. - - - - No patch file found for the current serial. - Ingen patch-fil fundet for det nuværende serienummer. - - - - Unable to open the file for reading. - Kan ikke åbne filen til læsning. - - - - Unable to open the file for writing. - Kan ikke åbne filen til skrivning. - - - - Failed to parse XML: - Kunne ikke analysere XML: - - - - Success - Succes - - - - Options saved successfully. - Indstillinger gemt med succes. - - - - Invalid Source - Ugyldig kilde - - - - The selected source is invalid. - Den valgte kilde er ugyldig. - - - - File Exists - Fil findes - - - - File already exists. Do you want to replace it? - Filen findes allerede. Vil du erstatte den? - - - - Failed to save file: - Kunne ikke gemme fil: - - - - Failed to download file: - Kunne ikke hente fil: - - - - Cheats Not Found - Snyd ikke fundet - - - - CheatsNotFound_MSG - Ingen snyd fundet til dette spil i denne version af det valgte repository, prøv et andet repository eller en anden version af spillet. - - - - Cheats Downloaded Successfully - Snyd hentet med succes - - - - CheatsDownloadedSuccessfully_MSG - Du har succesfuldt hentet snyd for denne version af spillet fra det valgte repository. Du kan prøve at hente fra et andet repository, hvis det er tilgængeligt, vil det også være muligt at bruge det ved at vælge filen fra listen. - - - - Failed to save: - Kunne ikke gemme: - - - - Failed to download: - Kunne ikke hente: - - - - Download Complete - Download fuldført - - - - DownloadComplete_MSG - Patcher hentet med succes! Alle patches til alle spil er blevet hentet, der er ikke behov for at hente dem individuelt for hvert spil, som det sker med snyd. Hvis opdateringen ikke vises, kan det være, at den ikke findes for den specifikke serie og version af spillet. - - - - Failed to parse JSON data from HTML. - Kunne ikke analysere JSON-data fra HTML. - - - - Failed to retrieve HTML page. - Kunne ikke hente HTML-side. - - - - The game is in version: %1 - Spillet er i version: %1 - - - - The downloaded patch only works on version: %1 - Den downloadede patch fungerer kun på version: %1 - - - - You may need to update your game. - Du skal muligvis opdatere dit spil. - - - - Incompatibility Notice - Uforenelighedsmeddelelse - - - - Failed to open file: - Kunne ikke åbne fil: - - - - XML ERROR: - XML FEJL: - - - - Failed to open files.json for writing - Kunne ikke åbne files.json til skrivning - - - - Author: - Forfatter: - - - - Directory does not exist: - Mappe findes ikke: - - - - Failed to open files.json for reading. - Kunne ikke åbne files.json til læsning. - - - - Name: - Navn: - - - - Can't apply cheats before the game is started - Kan ikke anvende snyd før spillet er startet. - - - - SettingsDialog - - - Save - Gem - - - Apply Anvend - Restore Defaults Gendan standardindstillinger - Close Luk - Point your mouse at an option to display its description. Peg musen over et valg for at vise dets beskrivelse. - consoleLanguageGroupBox Konsolsprog:\nIndstiller sproget, som PS4-spillet bruger.\nDet anbefales at indstille dette til et sprog, som spillet understøtter, hvilket kan variere efter region. - emulatorLanguageGroupBox Emulatorsprog:\nIndstiller sproget i emulatorens brugergrænseflade. - fullscreenCheckBox Aktiver fuld skærm:\nSætter automatisk spilvinduet i fuld skærm.\nDette kan skiftes ved at trykke på F11-tasten. - separateUpdatesCheckBox Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. - showSplashCheckBox Vis startskærm:\nViser en startskærm (speciel grafik) under opstarten. - ps4proCheckBox Er det en PS4 Pro:\nGør det muligt for emulatoren at fungere som en PS4 PRO, hvilket kan aktivere visse funktioner i spil, der understøtter det. - discordRPCCheckbox Aktiver Discord Rich Presence:\nViser emulatorikonet og relevante oplysninger på din Discord-profil. - userName Brugernavn:\nIndstiller PS4-kontoens navn, som kan blive vist i nogle spil. - TrophyKey Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. - logTypeGroupBox Logtype:\nIndstiller, om logvinduets output vil blive synkroniseret for at øge ydeevnen. Dette kan påvirke emulatorens ydeevne negativt. - logFilter Logfilter:\nFiltrerer loggen for kun at udskrive bestemte oplysninger.\nEksempler: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Niveaus: Trace, Debug, Info, Warning, Error, Critical - i rækkefølge, et valgt niveau skjuler alle forudgående niveauer og viser alle efterfølgende niveauer. - updaterGroupBox Opdatering:\nRelease: Officielle builds, der frigives månedligt, som kan være meget ældre, men mere stabile og testet.\nNightly: Udviklerbuilds med de nyeste funktioner og rettelser, men som kan indeholde fejl og være mindre stabile. - GUIgroupBox Titelsmusikafspilning:\nHvis spillet understøtter det, aktiver speciel musik, når spillet vælges i brugergrænsefladen. - disableTrophycheckBox Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). - hideCursorGroupBox Skjul Cursor:\nVælg hvornår cursoren skal forsvinde:\nAldrig: Du vil altid se musen.\nInaktiv: Indstil en tid for, hvornår den skal forsvinde efter at være inaktiv.\nAltid: du vil aldrig se musen. - idleTimeoutGroupBox Indstil en tid for, at musen skal forsvinde efter at være inaktiv. - backButtonBehaviorGroupBox Tilbageknap Adfærd:\nIndstiller controllerens tilbageknap til at efterligne tryk på den angivne position på PS4 berøringsflade. - enableCompatibilityCheckBox Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. - checkCompatibilityOnStartupCheckBox Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. - updateCompatibilityButton Update Compatibility Database:\nImmediately update the compatibility database. - Never Aldrig - Idle Inaktiv - Always Altid - Touchpad Left Berøringsplade Venstre - Touchpad Right Berøringsplade Højre - Touchpad Center Berøringsplade Center - None Ingen - graphicsAdapterGroupBox Grafikadapter:\nPå systemer med flere GPU'er skal du vælge den GPU, emulatoren vil bruge fra en rullemenu,\neller vælge "Auto Select" for at vælge den automatisk. - resolutionLayout Skærmopløsning:\nIndstiller emulatorvinduets størrelse under afspilning, som kan ændres under afspilning.\nDette er forskelligt fra selve spillets opløsning. - heightDivider Opdateringshastighedsdeler:\nMultiplicerer den frekvens, som emulatoren opdaterer billedet med, med dette tal. Ændring af dette kan have negative effekter, såsom hurtigere spil eller ødelagte funktioner! - dumpShadersCheckBox Aktiver dumping af Shaders:\nTil teknisk fejlfinding gemmer det spillets shaders i en mappe under afspilning. - nullGpuCheckBox Aktiver virtuel GPU:\nTil teknisk fejlfinding deaktiverer det spilvisning, som om der ikke var et grafikkort. - gameFoldersBox Spilmappen:\nListen over mapper til at tjekke for installerede spil. - addFolderButton Tilføj:\nTilføj en mappe til listen. - removeFolderButton Fjern:\nFjern en mappe fra listen. - debugDump Aktiver debugging-dump:\nGemmer import/export-symboler og headeroplysninger for det aktuelle PS4-program til en mappe. - vkValidationCheckBox Aktiver Vulkan-valideringslag:\nAktiverer et system, der validerer Vulkan-driverens tilstand og logger oplysninger om dens interne tilstand. Dette vil reducere ydeevnen og kan muligvis ændre emulatorens adfærd. - vkSyncValidationCheckBox Aktiver Vulkan-synkroniseringsvalidering:\nAktiverer et system, der validerer tidspunktet for Vulkan's renderingsopgaver. Dette vil reducere ydeevnen og kan muligvis ændre emulatorens adfærd. - rdocCheckBox Aktiver RenderDoc-fejlfinding:\nHvis aktiveret, giver det emulatoren mulighed for kompatibilitet med Renderdoc til at fange og analysere det aktuelle gengivne billede. + + CheatsPatches + + Cheats / Patches for + Cheats / Patches for + + + defaultTextEdit_MSG + Cheats/Patches er eksperimentelle.\nBrug med forsigtighed.\n\nDownload cheats individuelt ved at vælge lageret og klikke på download-knappen.\nUnder fanen Patches kan du downloade alle patches på én gang, vælge hvilke du vil bruge og gemme valget.\n\nDa vi ikke udvikler cheats/patches,\nvenligst rapporter problemer til cheat-udvikleren.\n\nHar du lavet en ny cheat? Besøg:\nhttps://github.com/shadps4-emu/ps4_cheats + + + No Image Available + Ingen billede tilgængelig + + + Serial: + Serienummer: + + + Version: + Version: + + + Size: + Størrelse: + + + Select Cheat File: + Vælg snyd-fil: + + + Repository: + Repository: + + + Download Cheats + Hent snyd + + + Delete File + Slet fil + + + No files selected. + Ingen filer valgt. + + + You can delete the cheats you don't want after downloading them. + Du kan slette de snyd, du ikke ønsker, efter at have hentet dem. + + + Do you want to delete the selected file?\n%1 + Ønsker du at slette den valgte fil?\n%1 + + + Select Patch File: + Vælg patch-fil: + + + Download Patches + Hent patches + + + Save + Gem + + + Cheats + Snyd + + + Patches + Patches + + + Error + Fejl + + + No patch selected. + Ingen patch valgt. + + + Unable to open files.json for reading. + Kan ikke åbne files.json til læsning. + + + No patch file found for the current serial. + Ingen patch-fil fundet for det nuværende serienummer. + + + Unable to open the file for reading. + Kan ikke åbne filen til læsning. + + + Unable to open the file for writing. + Kan ikke åbne filen til skrivning. + + + Failed to parse XML: + Kunne ikke analysere XML: + + + Success + Succes + + + Options saved successfully. + Indstillinger gemt med succes. + + + Invalid Source + Ugyldig kilde + + + The selected source is invalid. + Den valgte kilde er ugyldig. + + + File Exists + Fil findes + + + File already exists. Do you want to replace it? + Filen findes allerede. Vil du erstatte den? + + + Failed to save file: + Kunne ikke gemme fil: + + + Failed to download file: + Kunne ikke hente fil: + + + Cheats Not Found + Snyd ikke fundet + + + CheatsNotFound_MSG + Ingen snyd fundet til dette spil i denne version af det valgte repository, prøv et andet repository eller en anden version af spillet. + + + Cheats Downloaded Successfully + Snyd hentet med succes + + + CheatsDownloadedSuccessfully_MSG + Du har succesfuldt hentet snyd for denne version af spillet fra det valgte repository. Du kan prøve at hente fra et andet repository, hvis det er tilgængeligt, vil det også være muligt at bruge det ved at vælge filen fra listen. + + + Failed to save: + Kunne ikke gemme: + + + Failed to download: + Kunne ikke hente: + + + Download Complete + Download fuldført + + + DownloadComplete_MSG + Patcher hentet med succes! Alle patches til alle spil er blevet hentet, der er ikke behov for at hente dem individuelt for hvert spil, som det sker med snyd. Hvis opdateringen ikke vises, kan det være, at den ikke findes for den specifikke serie og version af spillet. + + + Failed to parse JSON data from HTML. + Kunne ikke analysere JSON-data fra HTML. + + + Failed to retrieve HTML page. + Kunne ikke hente HTML-side. + + + The game is in version: %1 + Spillet er i version: %1 + + + The downloaded patch only works on version: %1 + Den downloadede patch fungerer kun på version: %1 + + + You may need to update your game. + Du skal muligvis opdatere dit spil. + + + Incompatibility Notice + Uforenelighedsmeddelelse + + + Failed to open file: + Kunne ikke åbne fil: + + + XML ERROR: + XML FEJL: + + + Failed to open files.json for writing + Kunne ikke åbne files.json til skrivning + + + Author: + Forfatter: + + + Directory does not exist: + Mappe findes ikke: + + + Failed to open files.json for reading. + Kunne ikke åbne files.json til læsning. + + + Name: + Navn: + + + Can't apply cheats before the game is started + Kan ikke anvende snyd før spillet er startet. + + GameListFrame - Icon Ikon - Name Navn - Serial Seriel - Compatibility Compatibility - Region Region - Firmware Firmware - Size Størrelse - Version Version - Path Sti - Play Time Spilletid - Never Played Never Played - h h - m m - s s - Compatibility is untested Compatibility is untested - Game does not initialize properly / crashes the emulator Game does not initialize properly / crashes the emulator - Game boots, but only displays a blank screen Game boots, but only displays a blank screen - Game displays an image but does not go past the menu Game displays an image but does not go past the menu - Game has game-breaking glitches or unplayable performance Game has game-breaking glitches or unplayable performance - Game can be completed with playable performance and no major glitches Game can be completed with playable performance and no major glitches @@ -1508,127 +1210,102 @@ CheckUpdate - Auto Updater Automatisk opdatering - Error Fejl - Network error: Netsværksfejl: - Failed to parse update information. Kunne ikke analysere opdateringsoplysninger. - No pre-releases found. Ingen forhåndsudgivelser fundet. - Invalid release data. Ugyldige udgivelsesdata. - No download URL found for the specified asset. Ingen download-URL fundet for den specificerede aktiver. - Your version is already up to date! Din version er allerede opdateret! - Update Available Opdatering tilgængelig - Update Channel Opdateringskanal - Current Version Nuværende version - Latest Version Nyeste version - Do you want to update? Vil du opdatere? - Show Changelog Vis ændringslog - Check for Updates at Startup Tjek for opdateringer ved start - Update Opdater - No Nej - Hide Changelog Skjul ændringslog - Changes Ændringer - Network error occurred while trying to access the URL Netsværksfejl opstod, mens der blev forsøgt at få adgang til URL'en - Download Complete Download fuldført - The update has been downloaded, press OK to install. Opdateringen er blevet downloadet, tryk på OK for at installere. - Failed to save the update file at Kunne ikke gemme opdateringsfilen på - Starting Update... Starter opdatering... - Failed to create the update script file Kunne ikke oprette opdateringsscriptfilen @@ -1636,29 +1313,24 @@ GameListUtils - B B - KB KB - MB MB - GB GB - TB TB - + \ No newline at end of file diff --git a/src/qt_gui/translations/de.ts b/src/qt_gui/translations/de.ts index 62897fe24..1653298cf 100644 --- a/src/qt_gui/translations/de.ts +++ b/src/qt_gui/translations/de.ts @@ -6,22 +6,18 @@ AboutDialog - About shadPS4 Über shadPS4 - shadPS4 shadPS4 - shadPS4 is an experimental open-source emulator for the PlayStation 4. shadPS4 ist ein experimenteller Open-Source-Emulator für die Playstation 4. - This software should not be used to play games you have not legally obtained. Diese Software soll nicht dazu benutzt werden illegal kopierte Spiele zu spielen. @@ -29,7 +25,6 @@ ElfViewer - Open Folder Ordner öffnen @@ -37,17 +32,14 @@ GameInfoClass - Loading game list, please wait :3 Lade Spielliste, bitte warten :3 - Cancel Abbrechen - Loading... Lade... @@ -55,12 +47,10 @@ InstallDirSelect - shadPS4 - Choose directory shadPS4 - Wähle Ordner - Select which directory you want to install to. Select which directory you want to install to. @@ -68,27 +58,22 @@ GameInstallDialog - shadPS4 - Choose directory shadPS4 - Wähle Ordner - Directory to install games Installationsverzeichnis für Spiele - Browse Durchsuchen - Error Fehler - The value for location to install games is not valid. Der ausgewählte Ordner ist nicht gültig. @@ -96,167 +81,134 @@ GuiContextMenus - Create Shortcut Verknüpfung erstellen - Cheats / Patches Cheats / Patches - SFO Viewer SFO anzeigen - Trophy Viewer Trophäen anzeigen - Open Folder... Ordner öffnen... - Open Game Folder Spielordner öffnen - Open Save Data Folder Speicherordner öffnen - Open Log Folder Protokollordner öffnen - Copy info... Infos kopieren... - Copy Name Namen kopieren - Copy Serial Seriennummer kopieren - Copy All Alles kopieren - Delete... Delete... - Delete Game Delete Game - Delete Update Delete Update - Delete DLC Delete DLC - Compatibility... Compatibility... - Update database Update database - View report View report - Submit a report Submit a report - Shortcut creation Verknüpfungserstellung - Shortcut created successfully! Verknüpfung erfolgreich erstellt! - Error Fehler - Error creating shortcut! Fehler beim Erstellen der Verknüpfung! - Install PKG PKG installieren - Game Game - requiresEnableSeparateUpdateFolder_MSG This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. - This game has no update to delete! This game has no update to delete! - - + Update Update - This game has no DLC to delete! This game has no DLC to delete! - DLC DLC - Delete %1 Delete %1 - Are you sure you want to delete %1's %2 directory? Are you sure you want to delete %1's %2 directory? @@ -264,205 +216,285 @@ MainWindow - Open/Add Elf Folder Elf-Ordner öffnen/hinzufügen - Install Packages (PKG) Pakete installieren (PKG) - Boot Game Spiel starten - Check for Updates Nach Updates suchen - About shadPS4 Über shadPS4 - Configure... Konfigurieren... - Install application from a .pkg file Installiere Anwendung aus .pkg-Datei - Recent Games Zuletzt gespielt - Exit Beenden - Exit shadPS4 shadPS4 beenden - Exit the application. Die Anwendung beenden. - Show Game List Spielliste anzeigen - Game List Refresh Spielliste aktualisieren - Tiny Winzig - Small Klein - Medium Mittel - Large Groß - List View Listenansicht - Grid View Gitteransicht - Elf Viewer Elf-Ansicht - Game Install Directory Installationsverzeichnis für Spiele - Download Cheats/Patches Cheats / Patches herunterladen - Dump Game List Spielliste ausgeben - PKG Viewer PKG-Ansicht - Search... Suchen... - File Datei - View Ansicht - Game List Icons Game List Icons - Game List Mode Spiellisten-Symoble - Settings Einstellungen - Utils Werkzeuge - Themes Stile - Help Hilfe - Dark Dunkel - Light Hell - Green Grün - Blue Blau - Violet Violett - toolBar toolBar + + Game List + Spieleliste + + + * Unsupported Vulkan Version + * Nicht unterstützte Vulkan-Version + + + Download Cheats For All Installed Games + Cheats für alle installierten Spiele herunterladen + + + Download Patches For All Games + Patches für alle Spiele herunterladen + + + Download Complete + Download abgeschlossen + + + You have downloaded cheats for all the games you have installed. + Sie haben Cheats für alle installierten Spiele heruntergeladen. + + + Patches Downloaded Successfully! + Patches erfolgreich heruntergeladen! + + + All Patches available for all games have been downloaded. + Alle Patches für alle Spiele wurden heruntergeladen. + + + Games: + Spiele: + + + PKG File (*.PKG) + PKG-Datei (*.PKG) + + + ELF files (*.bin *.elf *.oelf) + ELF-Dateien (*.bin *.elf *.oelf) + + + Game Boot + Spiel-Start + + + Only one file can be selected! + Es kann nur eine Datei ausgewählt werden! + + + PKG Extraction + PKG-Extraktion + + + Patch detected! + Patch erkannt! + + + PKG and Game versions match: + PKG- und Spielversionen stimmen überein: + + + Would you like to overwrite? + Willst du überschreiben? + + + PKG Version %1 is older than installed version: + PKG-Version %1 ist älter als die installierte Version: + + + Game is installed: + Spiel ist installiert: + + + Would you like to install Patch: + Willst du den Patch installieren: + + + DLC Installation + DLC-Installation + + + Would you like to install DLC: %1? + Willst du den DLC installieren: %1? + + + DLC already installed: + DLC bereits installiert: + + + Game already installed + Spiel bereits installiert + + + PKG is a patch, please install the game first! + PKG ist ein Patch, bitte installieren Sie zuerst das Spiel! + + + PKG ERROR + PKG-FEHLER + + + Extracting PKG %1/%2 + Extrahiere PKG %1/%2 + + + Extraction Finished + Extraktion abgeschlossen + + + Game successfully installed at %1 + Spiel erfolgreich installiert auf %1 + + + File doesn't appear to be a valid PKG file + Die Datei scheint keine gültige PKG-Datei zu sein + PKGViewer - Open Folder Ordner öffnen @@ -470,7 +502,6 @@ TrophyViewer - Trophy Viewer Trophäenansicht @@ -478,1029 +509,700 @@ SettingsDialog - Settings Einstellungen - General Allgemein - System System - Console Language Konsolensprache - Emulator Language Emulatorsprache - Emulator Emulator - Enable Fullscreen Vollbild aktivieren - Enable Separate Update Folder Enable Separate Update Folder - Show Splash Startbildschirm anzeigen - Is PS4 Pro Ist PS4 Pro - Enable Discord Rich Presence Discord Rich Presence aktivieren - Username Benutzername - Trophy Key Trophy Key - Trophy Trophy - Logger Logger - Log Type Logtyp - Log Filter Log-Filter - Input Eingabe - Cursor Cursor - Hide Cursor Cursor ausblenden - Hide Cursor Idle Timeout Inaktivitätszeitüberschreitung zum Ausblenden des Cursors - s s - Controller Controller - Back Button Behavior Verhalten der Zurück-Taste - Graphics Grafik - Graphics Device Grafikgerät - Width Breite - Height Höhe - Vblank Divider Vblank-Teiler - Advanced Erweitert - Enable Shaders Dumping Shader-Dumping aktivieren - Enable NULL GPU NULL GPU aktivieren - Paths Pfad - Game Folders Spieleordner - Add... Hinzufügen... - Remove Entfernen - Debug Debug - Enable Debug Dumping Debug-Dumping aktivieren - Enable Vulkan Validation Layers Vulkan Validations-Ebenen aktivieren - Enable Vulkan Synchronization Validation Vulkan Synchronisations-Validierung aktivieren - Enable RenderDoc Debugging RenderDoc-Debugging aktivieren - Update Aktualisieren - Check for Updates at Startup Beim Start nach Updates suchen - Update Channel Update-Kanal - Check for Updates Nach Updates suchen - GUI Settings GUI-Einstellungen - Disable Trophy Pop-ups Disable Trophy Pop-ups - Play title music Titelmusik abspielen - Update Compatibility Database On Startup Update Compatibility Database On Startup - Game Compatibility Game Compatibility - Display Compatibility Data Display Compatibility Data - Update Compatibility Database Update Compatibility Database - Volume Lautstärke - Audio Backend Audio Backend - - - MainWindow - - Game List - Spieleliste - - - - * Unsupported Vulkan Version - * Nicht unterstützte Vulkan-Version - - - - Download Cheats For All Installed Games - Cheats für alle installierten Spiele herunterladen - - - - Download Patches For All Games - Patches für alle Spiele herunterladen - - - - Download Complete - Download abgeschlossen - - - - You have downloaded cheats for all the games you have installed. - Sie haben Cheats für alle installierten Spiele heruntergeladen. - - - - Patches Downloaded Successfully! - Patches erfolgreich heruntergeladen! - - - - All Patches available for all games have been downloaded. - Alle Patches für alle Spiele wurden heruntergeladen. - - - - Games: - Spiele: - - - - PKG File (*.PKG) - PKG-Datei (*.PKG) - - - - ELF files (*.bin *.elf *.oelf) - ELF-Dateien (*.bin *.elf *.oelf) - - - - Game Boot - Spiel-Start - - - - Only one file can be selected! - Es kann nur eine Datei ausgewählt werden! - - - - PKG Extraction - PKG-Extraktion - - - - Patch detected! - Patch erkannt! - - - - PKG and Game versions match: - PKG- und Spielversionen stimmen überein: - - - - Would you like to overwrite? - Willst du überschreiben? - - - - PKG Version %1 is older than installed version: - PKG-Version %1 ist älter als die installierte Version: - - - - Game is installed: - Spiel ist installiert: - - - - Would you like to install Patch: - Willst du den Patch installieren: - - - - DLC Installation - DLC-Installation - - - - Would you like to install DLC: %1? - Willst du den DLC installieren: %1? - - - - DLC already installed: - DLC bereits installiert: - - - - Game already installed - Spiel bereits installiert - - - - PKG is a patch, please install the game first! - PKG ist ein Patch, bitte installieren Sie zuerst das Spiel! - - - - PKG ERROR - PKG-FEHLER - - - - Extracting PKG %1/%2 - Extrahiere PKG %1/%2 - - - - Extraction Finished - Extraktion abgeschlossen - - - - Game successfully installed at %1 - Spiel erfolgreich installiert auf %1 - - - - File doesn't appear to be a valid PKG file - Die Datei scheint keine gültige PKG-Datei zu sein - - - - CheatsPatches - - - Cheats / Patches for - Cheats / Patches for - - - - defaultTextEdit_MSG - Cheats/Patches sind experimentell.\nVerwende sie mit Vorsicht.\n\nLade Cheats einzeln herunter, indem du das Repository auswählst und auf die Download-Schaltfläche klickst.\nAuf der Registerkarte Patches kannst du alle Patches auf einmal herunterladen, auswählen, welche du verwenden möchtest, und die Auswahl speichern.\n\nDa wir die Cheats/Patches nicht entwickeln,\nbitte melde Probleme an den Cheat-Autor.\n\nHast du einen neuen Cheat erstellt? Besuche:\nhttps://github.com/shadps4-emu/ps4_cheats - - - - No Image Available - Kein Bild verfügbar - - - - Serial: - Seriennummer: - - - - Version: - Version: - - - - Size: - Größe: - - - - Select Cheat File: - Cheat-Datei auswählen: - - - - Repository: - Repository: - - - - Download Cheats - Cheats herunterladen - - - - Delete File - Datei löschen - - - - No files selected. - Keine Dateien ausgewählt. - - - - You can delete the cheats you don't want after downloading them. - Du kannst die Cheats, die du nicht möchtest, nach dem Herunterladen löschen. - - - - Do you want to delete the selected file?\n%1 - Willst du die ausgewählte Datei löschen?\n%1 - - - - Select Patch File: - Patch-Datei auswählen: - - - - Download Patches - Patches herunterladen - - - Save Speichern - - Cheats - Cheats - - - - Patches - Patches - - - - Error - Fehler - - - - No patch selected. - Kein Patch ausgewählt. - - - - Unable to open files.json for reading. - Kann files.json nicht zum Lesen öffnen. - - - - No patch file found for the current serial. - Keine Patch-Datei für die aktuelle Seriennummer gefunden. - - - - Unable to open the file for reading. - Kann die Datei nicht zum Lesen öffnen. - - - - Unable to open the file for writing. - Kann die Datei nicht zum Schreiben öffnen. - - - - Failed to parse XML: - Fehler beim Parsen von XML: - - - - Success - Erfolg - - - - Options saved successfully. - Optionen erfolgreich gespeichert. - - - - Invalid Source - Ungültige Quelle - - - - The selected source is invalid. - Die ausgewählte Quelle ist ungültig. - - - - File Exists - Datei existiert - - - - File already exists. Do you want to replace it? - Datei existiert bereits. Möchtest du sie ersetzen? - - - - Failed to save file: - Fehler beim Speichern der Datei: - - - - Failed to download file: - Fehler beim Herunterladen der Datei: - - - - Cheats Not Found - Cheats nicht gefunden - - - - CheatsNotFound_MSG - Keine Cheats für dieses Spiel in dieser Version des gewählten Repositories gefunden. Versuche es mit einem anderen Repository oder einer anderen Version des Spiels. - - - - Cheats Downloaded Successfully - Cheats erfolgreich heruntergeladen - - - - CheatsDownloadedSuccessfully_MSG - Du hast erfolgreich Cheats für diese Version des Spiels aus dem gewählten Repository heruntergeladen. Du kannst auch versuchen, Cheats von einem anderen Repository herunterzuladen. Wenn verfügbar, kannst du sie auswählen, indem du die Datei aus der Liste auswählst. - - - - Failed to save: - Speichern fehlgeschlagen: - - - - Failed to download: - Herunterladen fehlgeschlagen: - - - - Download Complete - Download abgeschlossen - - - - DownloadComplete_MSG - Patches erfolgreich heruntergeladen! Alle Patches für alle Spiele wurden heruntergeladen, es ist nicht notwendig, sie einzeln für jedes Spiel herunterzuladen, wie es bei Cheats der Fall ist. Wenn der Patch nicht angezeigt wird, könnte es sein, dass er für die spezifische Seriennummer und Version des Spiels nicht existiert. - - - - Failed to parse JSON data from HTML. - Fehler beim Parsen der JSON-Daten aus HTML. - - - - Failed to retrieve HTML page. - Fehler beim Abrufen der HTML-Seite. - - - - The game is in version: %1 - Das Spiel ist in der Version: %1 - - - - The downloaded patch only works on version: %1 - Der heruntergeladene Patch funktioniert nur in der Version: %1 - - - - You may need to update your game. - Sie müssen möglicherweise Ihr Spiel aktualisieren. - - - - Incompatibility Notice - Inkompatibilitätsbenachrichtigung - - - - Failed to open file: - Fehler beim Öffnen der Datei: - - - - XML ERROR: - XML-Fehler: - - - - Failed to open files.json for writing - Kann files.json nicht zum Schreiben öffnen - - - - Author: - Autor: - - - - Directory does not exist: - Verzeichnis existiert nicht: - - - - Failed to open files.json for reading. - Kann files.json nicht zum Lesen öffnen. - - - - Name: - Name: - - - - Can't apply cheats before the game is started - Kann keine Cheats anwenden, bevor das Spiel gestartet ist. - - - - SettingsDialog - - - Save - Speichern - - - Apply Übernehmen - Restore Defaults Werkseinstellungen wiederherstellen - Close Schließen - Point your mouse at an option to display its description. Bewege die Maus über eine Option, um deren Beschreibung anzuzeigen. - consoleLanguageGroupBox Konsolensprache:\nLegt die Sprache fest, die das PS4-Spiel verwendet.\nEs wird empfohlen, diese auf eine vom Spiel unterstützte Sprache einzustellen, die je nach Region unterschiedlich sein kann. - emulatorLanguageGroupBox Emulatorsprache:\nLegt die Sprache der Emulator-Benutzeroberfläche fest. - fullscreenCheckBox Vollbildmodus aktivieren:\nSchaltet das Spielfenster automatisch in den Vollbildmodus.\nKann durch Drücken der F11-Taste umgeschaltet werden. - separateUpdatesCheckBox Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. - showSplashCheckBox Startbildschirm anzeigen:\nZeigt beim Start einen speziellen Bildschirm (Splash) des Spiels an. - ps4proCheckBox Ist es eine PS4 Pro:\nErmöglicht es dem Emulator, als PS4 PRO zu arbeiten, was in Spielen, die dies unterstützen, spezielle Funktionen aktivieren kann. - discordRPCCheckbox Discord Rich Presence aktivieren:\nZeigt das Emulator-Icon und relevante Informationen in deinem Discord-Profil an. - userName Benutzername:\nLegt den Namen des PS4-Kontos fest, der in einigen Spielen angezeigt werden kann. - TrophyKey Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. - logTypeGroupBox Protokolltyp:\nLegt fest, ob die Ausgabe des Protokollfensters synchronisiert wird, um die Leistung zu verbessern. Dies kann sich negativ auf die Emulation auswirken. - logFilter Protokollfilter:\nFiltert das Protokoll so, dass nur bestimmte Informationen ausgegeben werden.\nBeispiele: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Ebenen: Trace, Debug, Info, Warning, Error, Critical - in dieser Reihenfolge, ein ausgewähltes Level blendet alle vorherigen Ebenen aus und zeigt alle nachfolgenden an. - updaterGroupBox Update:\nRelease: Offizielle Builds, die monatlich veröffentlicht werden, können viel älter sein, aber stabiler und getestet.\nNightly: Entwickler-Builds, die die neuesten Funktionen und Fehlerbehebungen enthalten, aber Fehler enthalten und weniger stabil sein können. - GUIgroupBox Wiedergabe der Titelmusik:\nWenn das Spiel dies unterstützt, wird beim Auswählen des Spiels in der Benutzeroberfläche spezielle Musik abgespielt. - disableTrophycheckBox Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). - hideCursorGroupBox Maus ausblenden:\nWählen Sie, wann der Cursor verschwinden soll:\nNie: Sie sehen die Maus immer.\nInaktiv: Legen Sie eine Zeit fest, nach der sie nach Inaktivität verschwindet.\nImmer: Sie sehen die Maus niemals. - idleTimeoutGroupBox Stellen Sie eine Zeit ein, nach der die Maus nach Inaktivität verschwinden soll. - backButtonBehaviorGroupBox Zurück-Button Verhalten:\nStellt die Zurück-Taste des Controllers so ein, dass sie das Antippen der angegebenen Position auf dem PS4-Touchpad emuliert. - enableCompatibilityCheckBox Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. - checkCompatibilityOnStartupCheckBox Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. - updateCompatibilityButton Update Compatibility Database:\nImmediately update the compatibility database. - Never Niemals - Idle Im Leerlauf - Always Immer - Touchpad Left Touchpad Links - Touchpad Right Touchpad Rechts - Touchpad Center Touchpad Mitte - None Keine - graphicsAdapterGroupBox Grafikkarte:\nAuf Systemen mit mehreren GPUs wählen Sie aus einem Dropdown-Menü die GPU aus, die der Emulator verwenden wird,\noder wählen Sie "Auto Select", um sie automatisch auszuwählen. - resolutionLayout Auflösung:\nLegt die Größe des Emulator-Fensters während der Wiedergabe fest, die während der Wiedergabe geändert werden kann.\nDies unterscheidet sich von der tatsächlichen Spielauflösung. - heightDivider Framerate-Teiler:\nMultipliziert die Bildrate, mit der der Emulator aktualisiert wird, mit diesem Wert. Dies kann sich negativ auswirken, wie z.B. beschleunigtes Gameplay oder Funktionsstörungen! - dumpShadersCheckBox Shader-Dumping aktivieren:\nZum technischen Debuggen speichert es die Shaders des Spiels in einem Ordner während der Wiedergabe. - nullGpuCheckBox Virtuelle GPU aktivieren:\nFür das technische Debugging deaktiviert es die Spielanzeige, als ob keine Grafikkarte vorhanden wäre. - gameFoldersBox Spieleordner:\nDie Liste der Ordner, in denen nach installierten Spielen gesucht wird. - addFolderButton Hinzufügen:\nFügen Sie einen Ordner zur Liste hinzu. - removeFolderButton Entfernen:\nEntfernen Sie einen Ordner aus der Liste. - debugDump Debug-Dump aktivieren:\nSpeichert Import-/Exportsymbole und Headerinformationen des aktuellen PS4-Programms in einem Verzeichnis. - vkValidationCheckBox Vulkan-Validierungsebenen aktivieren:\nAktiviert ein System, das den Zustand des Vulkan-Treibers validiert und Informationen über dessen internen Zustand protokolliert. Dies verringert die Leistung und kann möglicherweise das Verhalten der Emulation ändern. - vkSyncValidationCheckBox Vulkan-Synchronisationsvalidierung aktivieren:\nAktiviert ein System, das die Zeitplanung der Rendering-Aufgaben von Vulkan validiert. Dies wird die Leistung verringern und kann möglicherweise das Verhalten der Emulation ändern. - rdocCheckBox RenderDoc-Debugging aktivieren:\nWenn aktiviert, bietet der Emulator Kompatibilität mit Renderdoc zur Erfassung und Analyse des aktuell gerenderten Frames. + + CheatsPatches + + Cheats / Patches for + Cheats / Patches for + + + defaultTextEdit_MSG + Cheats/Patches sind experimentell.\nVerwende sie mit Vorsicht.\n\nLade Cheats einzeln herunter, indem du das Repository auswählst und auf die Download-Schaltfläche klickst.\nAuf der Registerkarte Patches kannst du alle Patches auf einmal herunterladen, auswählen, welche du verwenden möchtest, und die Auswahl speichern.\n\nDa wir die Cheats/Patches nicht entwickeln,\nbitte melde Probleme an den Cheat-Autor.\n\nHast du einen neuen Cheat erstellt? Besuche:\nhttps://github.com/shadps4-emu/ps4_cheats + + + No Image Available + Kein Bild verfügbar + + + Serial: + Seriennummer: + + + Version: + Version: + + + Size: + Größe: + + + Select Cheat File: + Cheat-Datei auswählen: + + + Repository: + Repository: + + + Download Cheats + Cheats herunterladen + + + Delete File + Datei löschen + + + No files selected. + Keine Dateien ausgewählt. + + + You can delete the cheats you don't want after downloading them. + Du kannst die Cheats, die du nicht möchtest, nach dem Herunterladen löschen. + + + Do you want to delete the selected file?\n%1 + Willst du die ausgewählte Datei löschen?\n%1 + + + Select Patch File: + Patch-Datei auswählen: + + + Download Patches + Patches herunterladen + + + Save + Speichern + + + Cheats + Cheats + + + Patches + Patches + + + Error + Fehler + + + No patch selected. + Kein Patch ausgewählt. + + + Unable to open files.json for reading. + Kann files.json nicht zum Lesen öffnen. + + + No patch file found for the current serial. + Keine Patch-Datei für die aktuelle Seriennummer gefunden. + + + Unable to open the file for reading. + Kann die Datei nicht zum Lesen öffnen. + + + Unable to open the file for writing. + Kann die Datei nicht zum Schreiben öffnen. + + + Failed to parse XML: + Fehler beim Parsen von XML: + + + Success + Erfolg + + + Options saved successfully. + Optionen erfolgreich gespeichert. + + + Invalid Source + Ungültige Quelle + + + The selected source is invalid. + Die ausgewählte Quelle ist ungültig. + + + File Exists + Datei existiert + + + File already exists. Do you want to replace it? + Datei existiert bereits. Möchtest du sie ersetzen? + + + Failed to save file: + Fehler beim Speichern der Datei: + + + Failed to download file: + Fehler beim Herunterladen der Datei: + + + Cheats Not Found + Cheats nicht gefunden + + + CheatsNotFound_MSG + Keine Cheats für dieses Spiel in dieser Version des gewählten Repositories gefunden. Versuche es mit einem anderen Repository oder einer anderen Version des Spiels. + + + Cheats Downloaded Successfully + Cheats erfolgreich heruntergeladen + + + CheatsDownloadedSuccessfully_MSG + Du hast erfolgreich Cheats für diese Version des Spiels aus dem gewählten Repository heruntergeladen. Du kannst auch versuchen, Cheats von einem anderen Repository herunterzuladen. Wenn verfügbar, kannst du sie auswählen, indem du die Datei aus der Liste auswählst. + + + Failed to save: + Speichern fehlgeschlagen: + + + Failed to download: + Herunterladen fehlgeschlagen: + + + Download Complete + Download abgeschlossen + + + DownloadComplete_MSG + Patches erfolgreich heruntergeladen! Alle Patches für alle Spiele wurden heruntergeladen, es ist nicht notwendig, sie einzeln für jedes Spiel herunterzuladen, wie es bei Cheats der Fall ist. Wenn der Patch nicht angezeigt wird, könnte es sein, dass er für die spezifische Seriennummer und Version des Spiels nicht existiert. + + + Failed to parse JSON data from HTML. + Fehler beim Parsen der JSON-Daten aus HTML. + + + Failed to retrieve HTML page. + Fehler beim Abrufen der HTML-Seite. + + + The game is in version: %1 + Das Spiel ist in der Version: %1 + + + The downloaded patch only works on version: %1 + Der heruntergeladene Patch funktioniert nur in der Version: %1 + + + You may need to update your game. + Sie müssen möglicherweise Ihr Spiel aktualisieren. + + + Incompatibility Notice + Inkompatibilitätsbenachrichtigung + + + Failed to open file: + Fehler beim Öffnen der Datei: + + + XML ERROR: + XML-Fehler: + + + Failed to open files.json for writing + Kann files.json nicht zum Schreiben öffnen + + + Author: + Autor: + + + Directory does not exist: + Verzeichnis existiert nicht: + + + Failed to open files.json for reading. + Kann files.json nicht zum Lesen öffnen. + + + Name: + Name: + + + Can't apply cheats before the game is started + Kann keine Cheats anwenden, bevor das Spiel gestartet ist. + + GameListFrame - Icon Symbol - Name Name - Serial Seriennummer - Compatibility Compatibility - Region Region - Firmware Firmware - Size Größe - Version Version - Path Pfad - Play Time Spielzeit - Never Played Never Played - h h - m m - s s - Compatibility is untested Compatibility is untested - Game does not initialize properly / crashes the emulator Game does not initialize properly / crashes the emulator - Game boots, but only displays a blank screen Game boots, but only displays a blank screen - Game displays an image but does not go past the menu Game displays an image but does not go past the menu - Game has game-breaking glitches or unplayable performance Game has game-breaking glitches or unplayable performance - Game can be completed with playable performance and no major glitches Game can be completed with playable performance and no major glitches @@ -1508,127 +1210,102 @@ CheckUpdate - Auto Updater Automatischer Aktualisierer - Error Fehler - Network error: Netzwerkfehler: - Failed to parse update information. Fehler beim Parsen der Aktualisierungsinformationen. - No pre-releases found. Keine Vorabveröffentlichungen gefunden. - Invalid release data. Ungültige Versionsdaten. - No download URL found for the specified asset. Keine Download-URL für das angegebene Asset gefunden. - Your version is already up to date! Ihre Version ist bereits aktuell! - Update Available Aktualisierung verfügbar - Update Channel Update-Kanal - Current Version Aktuelle Version - Latest Version Neueste Version - Do you want to update? Möchten Sie aktualisieren? - Show Changelog Änderungsprotokoll anzeigen - Check for Updates at Startup Beim Start nach Updates suchen - Update Aktualisieren - No Nein - Hide Changelog Änderungsprotokoll ausblenden - Changes Änderungen - Network error occurred while trying to access the URL Beim Zugriff auf die URL ist ein Netzwerkfehler aufgetreten - Download Complete Download abgeschlossen - The update has been downloaded, press OK to install. Die Aktualisierung wurde heruntergeladen, drücken Sie OK, um zu installieren. - Failed to save the update file at Fehler beim Speichern der Aktualisierungsdatei in - Starting Update... Aktualisierung wird gestartet... - Failed to create the update script file Fehler beim Erstellen der Aktualisierungs-Skriptdatei @@ -1636,29 +1313,24 @@ GameListUtils - B B - KB KB - MB MB - GB GB - TB TB - + \ No newline at end of file diff --git a/src/qt_gui/translations/el.ts b/src/qt_gui/translations/el.ts index 43ed81c33..6226e1292 100644 --- a/src/qt_gui/translations/el.ts +++ b/src/qt_gui/translations/el.ts @@ -6,22 +6,18 @@ AboutDialog - About shadPS4 About shadPS4 - shadPS4 shadPS4 - shadPS4 is an experimental open-source emulator for the PlayStation 4. shadPS4 is an experimental open-source emulator for the PlayStation 4. - This software should not be used to play games you have not legally obtained. This software should not be used to play games you have not legally obtained. @@ -29,7 +25,6 @@ ElfViewer - Open Folder Open Folder @@ -37,17 +32,14 @@ GameInfoClass - Loading game list, please wait :3 Loading game list, please wait :3 - Cancel Cancel - Loading... Loading... @@ -55,12 +47,10 @@ InstallDirSelect - shadPS4 - Choose directory shadPS4 - Choose directory - Select which directory you want to install to. Select which directory you want to install to. @@ -68,27 +58,22 @@ GameInstallDialog - shadPS4 - Choose directory shadPS4 - Choose directory - Directory to install games Directory to install games - Browse Browse - Error Error - The value for location to install games is not valid. The value for location to install games is not valid. @@ -96,167 +81,134 @@ GuiContextMenus - Create Shortcut Create Shortcut - Cheats / Patches Kodikí / Enimeróseis - SFO Viewer SFO Viewer - Trophy Viewer Trophy Viewer - Open Folder... Άνοιγμα Φακέλου... - Open Game Folder Άνοιγμα Φακέλου Παιχνιδιού - Open Save Data Folder Άνοιγμα Φακέλου Αποθηκευμένων Δεδομένων - Open Log Folder Άνοιγμα Φακέλου Καταγραφής - Copy info... Copy info... - Copy Name Copy Name - Copy Serial Copy Serial - Copy All Copy All - Delete... Delete... - Delete Game Delete Game - Delete Update Delete Update - Delete DLC Delete DLC - Compatibility... Compatibility... - Update database Update database - View report View report - Submit a report Submit a report - Shortcut creation Shortcut creation - Shortcut created successfully! Shortcut created successfully! - Error Error - Error creating shortcut! Error creating shortcut! - Install PKG Install PKG - Game Game - requiresEnableSeparateUpdateFolder_MSG This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. - This game has no update to delete! This game has no update to delete! - - + Update Update - This game has no DLC to delete! This game has no DLC to delete! - DLC DLC - Delete %1 Delete %1 - Are you sure you want to delete %1's %2 directory? Are you sure you want to delete %1's %2 directory? @@ -264,205 +216,285 @@ MainWindow - Open/Add Elf Folder Open/Add Elf Folder - Install Packages (PKG) Install Packages (PKG) - Boot Game Boot Game - Check for Updates Έλεγχος για ενημερώσεις - About shadPS4 About shadPS4 - Configure... Configure... - Install application from a .pkg file Install application from a .pkg file - Recent Games Recent Games - Exit Exit - Exit shadPS4 Exit shadPS4 - Exit the application. Exit the application. - Show Game List Show Game List - Game List Refresh Game List Refresh - Tiny Tiny - Small Small - Medium Medium - Large Large - List View List View - Grid View Grid View - Elf Viewer Elf Viewer - Game Install Directory Game Install Directory - Download Cheats/Patches Κατεβάστε Κωδικούς / Ενημερώσεις - Dump Game List Dump Game List - PKG Viewer PKG Viewer - Search... Search... - File File - View View - Game List Icons Game List Icons - Game List Mode Game List Mode - Settings Settings - Utils Utils - Themes Themes - Help Βοήθεια - Dark Dark - Light Light - Green Green - Blue Blue - Violet Violet - toolBar toolBar + + Game List + Λίστα παιχνιδιών + + + * Unsupported Vulkan Version + * Μη υποστηριζόμενη έκδοση Vulkan + + + Download Cheats For All Installed Games + Λήψη Cheats για όλα τα εγκατεστημένα παιχνίδια + + + Download Patches For All Games + Λήψη Patches για όλα τα παιχνίδια + + + Download Complete + Η λήψη ολοκληρώθηκε + + + You have downloaded cheats for all the games you have installed. + Έχετε κατεβάσει cheats για όλα τα εγκατεστημένα παιχνίδια. + + + Patches Downloaded Successfully! + Τα Patches κατέβηκαν επιτυχώς! + + + All Patches available for all games have been downloaded. + Όλα τα διαθέσιμα Patches για όλα τα παιχνίδια έχουν κατέβει. + + + Games: + Παιχνίδια: + + + PKG File (*.PKG) + Αρχείο PKG (*.PKG) + + + ELF files (*.bin *.elf *.oelf) + Αρχεία ELF (*.bin *.elf *.oelf) + + + Game Boot + Εκκίνηση παιχνιδιού + + + Only one file can be selected! + Μπορεί να επιλεγεί μόνο ένα αρχείο! + + + PKG Extraction + Εξαγωγή PKG + + + Patch detected! + Αναγνώριση ενημέρωσης! + + + PKG and Game versions match: + Οι εκδόσεις PKG και παιχνιδιού ταιριάζουν: + + + Would you like to overwrite? + Θέλετε να αντικαταστήσετε; + + + PKG Version %1 is older than installed version: + Η έκδοση PKG %1 είναι παλαιότερη από την εγκατεστημένη έκδοση: + + + Game is installed: + Το παιχνίδι είναι εγκατεστημένο: + + + Would you like to install Patch: + Θέλετε να εγκαταστήσετε την ενημέρωση: + + + DLC Installation + Εγκατάσταση DLC + + + Would you like to install DLC: %1? + Θέλετε να εγκαταστήσετε το DLC: %1; + + + DLC already installed: + DLC ήδη εγκατεστημένο: + + + Game already installed + Παιχνίδι ήδη εγκατεστημένο + + + PKG is a patch, please install the game first! + Το PKG είναι patch, παρακαλώ εγκαταστήστε πρώτα το παιχνίδι! + + + PKG ERROR + ΣΦΑΛΜΑ PKG + + + Extracting PKG %1/%2 + Εξαγωγή PKG %1/%2 + + + Extraction Finished + Η εξαγωγή ολοκληρώθηκε + + + Game successfully installed at %1 + Το παιχνίδι εγκαταστάθηκε επιτυχώς στο %1 + + + File doesn't appear to be a valid PKG file + Η αρχείο δεν φαίνεται να είναι έγκυρο αρχείο PKG + PKGViewer - Open Folder Open Folder @@ -470,7 +502,6 @@ TrophyViewer - Trophy Viewer Trophy Viewer @@ -478,1029 +509,700 @@ SettingsDialog - Settings Settings - General General - System System - Console Language Console Language - Emulator Language Emulator Language - Emulator Emulator - Enable Fullscreen Enable Fullscreen - Enable Separate Update Folder Enable Separate Update Folder - Show Splash Show Splash - Is PS4 Pro Is PS4 Pro - Enable Discord Rich Presence Ενεργοποίηση Discord Rich Presence - Username Username - Trophy Key Trophy Key - Trophy Trophy - Logger Logger - Log Type Log Type - Log Filter Log Filter - Input Είσοδος - Cursor Δείκτης - Hide Cursor Απόκρυψη δείκτη - Hide Cursor Idle Timeout Χρόνος αδράνειας απόκρυψης δείκτη - s s - Controller Controller - Back Button Behavior Συμπεριφορά κουμπιού επιστροφής - Graphics Graphics - Graphics Device Graphics Device - Width Width - Height Height - Vblank Divider Vblank Divider - Advanced Advanced - Enable Shaders Dumping Enable Shaders Dumping - Enable NULL GPU Enable NULL GPU - Paths Διαδρομές - Game Folders Φάκελοι παιχνιδιών - Add... Προσθήκη... - Remove Αφαίρεση - Debug Debug - Enable Debug Dumping Enable Debug Dumping - Enable Vulkan Validation Layers Enable Vulkan Validation Layers - Enable Vulkan Synchronization Validation Enable Vulkan Synchronization Validation - Enable RenderDoc Debugging Enable RenderDoc Debugging - Update Ενημέρωση - Check for Updates at Startup Έλεγχος για ενημερώσεις κατά την εκκίνηση - Update Channel Κανάλι Ενημέρωσης - Check for Updates Έλεγχος για ενημερώσεις - GUI Settings Ρυθμίσεις GUI - Disable Trophy Pop-ups Disable Trophy Pop-ups - Play title music Αναπαραγωγή μουσικής τίτλου - Update Compatibility Database On Startup Update Compatibility Database On Startup - Game Compatibility Game Compatibility - Display Compatibility Data Display Compatibility Data - Update Compatibility Database Update Compatibility Database - Volume ένταση - Audio Backend Audio Backend - - - MainWindow - - Game List - Λίστα παιχνιδιών - - - - * Unsupported Vulkan Version - * Μη υποστηριζόμενη έκδοση Vulkan - - - - Download Cheats For All Installed Games - Λήψη Cheats για όλα τα εγκατεστημένα παιχνίδια - - - - Download Patches For All Games - Λήψη Patches για όλα τα παιχνίδια - - - - Download Complete - Η λήψη ολοκληρώθηκε - - - - You have downloaded cheats for all the games you have installed. - Έχετε κατεβάσει cheats για όλα τα εγκατεστημένα παιχνίδια. - - - - Patches Downloaded Successfully! - Τα Patches κατέβηκαν επιτυχώς! - - - - All Patches available for all games have been downloaded. - Όλα τα διαθέσιμα Patches για όλα τα παιχνίδια έχουν κατέβει. - - - - Games: - Παιχνίδια: - - - - PKG File (*.PKG) - Αρχείο PKG (*.PKG) - - - - ELF files (*.bin *.elf *.oelf) - Αρχεία ELF (*.bin *.elf *.oelf) - - - - Game Boot - Εκκίνηση παιχνιδιού - - - - Only one file can be selected! - Μπορεί να επιλεγεί μόνο ένα αρχείο! - - - - PKG Extraction - Εξαγωγή PKG - - - - Patch detected! - Αναγνώριση ενημέρωσης! - - - - PKG and Game versions match: - Οι εκδόσεις PKG και παιχνιδιού ταιριάζουν: - - - - Would you like to overwrite? - Θέλετε να αντικαταστήσετε; - - - - PKG Version %1 is older than installed version: - Η έκδοση PKG %1 είναι παλαιότερη από την εγκατεστημένη έκδοση: - - - - Game is installed: - Το παιχνίδι είναι εγκατεστημένο: - - - - Would you like to install Patch: - Θέλετε να εγκαταστήσετε την ενημέρωση: - - - - DLC Installation - Εγκατάσταση DLC - - - - Would you like to install DLC: %1? - Θέλετε να εγκαταστήσετε το DLC: %1; - - - - DLC already installed: - DLC ήδη εγκατεστημένο: - - - - Game already installed - Παιχνίδι ήδη εγκατεστημένο - - - - PKG is a patch, please install the game first! - Το PKG είναι patch, παρακαλώ εγκαταστήστε πρώτα το παιχνίδι! - - - - PKG ERROR - ΣΦΑΛΜΑ PKG - - - - Extracting PKG %1/%2 - Εξαγωγή PKG %1/%2 - - - - Extraction Finished - Η εξαγωγή ολοκληρώθηκε - - - - Game successfully installed at %1 - Το παιχνίδι εγκαταστάθηκε επιτυχώς στο %1 - - - - File doesn't appear to be a valid PKG file - Η αρχείο δεν φαίνεται να είναι έγκυρο αρχείο PKG - - - - CheatsPatches - - - Cheats / Patches for - Cheats / Patches for - - - - defaultTextEdit_MSG - Οι cheats/patches είναι πειραματικά.\nΧρησιμοποιήστε τα με προσοχή.\n\nΚατεβάστε τους cheats μεμονωμένα επιλέγοντας το αποθετήριο και κάνοντας κλικ στο κουμπί λήψης.\nΣτην καρτέλα Patches, μπορείτε να κατεβάσετε όλα τα patches ταυτόχρονα, να επιλέξετε ποια θέλετε να χρησιμοποιήσετε και να αποθηκεύσετε την επιλογή.\n\nΔεδομένου ότι δεν αναπτύσσουμε τους cheats/patches,\nπαρακαλώ αναφέρετε προβλήματα στον δημιουργό του cheat.\n\nΔημιουργήσατε ένα νέο cheat; Επισκεφθείτε:\nhttps://github.com/shadps4-emu/ps4_cheats - - - - No Image Available - Δεν διατίθεται εικόνα - - - - Serial: - Σειριακός αριθμός: - - - - Version: - Έκδοση: - - - - Size: - Μέγεθος: - - - - Select Cheat File: - Επιλέξτε αρχείο Cheat: - - - - Repository: - Αποθετήριο: - - - - Download Cheats - Λήψη Cheats - - - - Delete File - Διαγραφή αρχείου - - - - No files selected. - Δεν έχουν επιλεγεί αρχεία. - - - - You can delete the cheats you don't want after downloading them. - Μπορείτε να διαγράψετε τα cheats που δεν θέλετε μετά τη λήψη τους. - - - - Do you want to delete the selected file?\n%1 - Θέλετε να διαγράψετε το επιλεγμένο αρχείο;\n%1 - - - - Select Patch File: - Επιλέξτε αρχείο Patch: - - - - Download Patches - Λήψη Patches - - - Save Αποθήκευση - - Cheats - Cheats - - - - Patches - Patches - - - - Error - Σφάλμα - - - - No patch selected. - Δεν έχει επιλεγεί κανένα patch. - - - - Unable to open files.json for reading. - Αδυναμία ανοίγματος του files.json για ανάγνωση. - - - - No patch file found for the current serial. - Δεν βρέθηκε αρχείο patch για τον τρέχοντα σειριακό αριθμό. - - - - Unable to open the file for reading. - Αδυναμία ανοίγματος του αρχείου για ανάγνωση. - - - - Unable to open the file for writing. - Αδυναμία ανοίγματος του αρχείου για εγγραφή. - - - - Failed to parse XML: - Αποτυχία ανάλυσης XML: - - - - Success - Επιτυχία - - - - Options saved successfully. - Οι ρυθμίσεις αποθηκεύτηκαν επιτυχώς. - - - - Invalid Source - Μη έγκυρη Πηγή - - - - The selected source is invalid. - Η επιλεγμένη πηγή είναι μη έγκυρη. - - - - File Exists - Η αρχείο υπάρχει - - - - File already exists. Do you want to replace it? - Η αρχείο υπάρχει ήδη. Θέλετε να την αντικαταστήσετε; - - - - Failed to save file: - Αποτυχία αποθήκευσης αρχείου: - - - - Failed to download file: - Αποτυχία λήψης αρχείου: - - - - Cheats Not Found - Δεν βρέθηκαν Cheats - - - - CheatsNotFound_MSG - Δεν βρέθηκαν cheats για αυτό το παιχνίδι στην τρέχουσα έκδοση του επιλεγμένου αποθετηρίου. Δοκιμάστε να κατεβάσετε από άλλο αποθετήριο ή άλλη έκδοση του παιχνιδιού. - - - - Cheats Downloaded Successfully - Cheats κατεβάστηκαν επιτυχώς - - - - CheatsDownloadedSuccessfully_MSG - Κατεβάσατε επιτυχώς cheats για αυτή την έκδοση του παιχνιδιού από το επιλεγμένο αποθετήριο. Μπορείτε να δοκιμάσετε να κατεβάσετε από άλλο αποθετήριο. Αν είναι διαθέσιμο, μπορείτε να το επιλέξετε επιλέγοντας το αρχείο από τη λίστα. - - - - Failed to save: - Αποτυχία αποθήκευσης: - - - - Failed to download: - Αποτυχία λήψης: - - - - Download Complete - Η λήψη ολοκληρώθηκε - - - - DownloadComplete_MSG - Τα Patches κατεβάστηκαν επιτυχώς! Όλα τα Patches για όλα τα παιχνίδια έχουν κατέβει, δεν είναι απαραίτητο να τα κατεβάσετε ένα-ένα για κάθε παιχνίδι, όπως με τα Cheats. Εάν η ενημέρωση δεν εμφανίζεται, μπορεί να μην υπάρχει για τον συγκεκριμένο σειριακό αριθμό και έκδοση του παιχνιδιού. - - - - Failed to parse JSON data from HTML. - Αποτυχία ανάλυσης δεδομένων JSON από HTML. - - - - Failed to retrieve HTML page. - Αποτυχία ανάκτησης σελίδας HTML. - - - - The game is in version: %1 - Το παιχνίδι είναι στην έκδοση: %1 - - - - The downloaded patch only works on version: %1 - Η ληφθείσα ενημέρωση λειτουργεί μόνο στην έκδοση: %1 - - - - You may need to update your game. - Μπορεί να χρειαστεί να ενημερώσετε το παιχνίδι σας. - - - - Incompatibility Notice - Ειδοποίηση ασυμβατότητας - - - - Failed to open file: - Αποτυχία ανοίγματος αρχείου: - - - - XML ERROR: - ΣΦΑΛΜΑ XML: - - - - Failed to open files.json for writing - Αποτυχία ανοίγματος του files.json για εγγραφή - - - - Author: - Συγγραφέας: - - - - Directory does not exist: - Ο φάκελος δεν υπάρχει: - - - - Failed to open files.json for reading. - Αποτυχία ανοίγματος του files.json για ανάγνωση. - - - - Name: - Όνομα: - - - - Can't apply cheats before the game is started - Δεν μπορείτε να εφαρμόσετε cheats πριν ξεκινήσει το παιχνίδι. - - - - SettingsDialog - - - Save - Αποθήκευση - - - Apply Εφαρμογή - Restore Defaults Επαναφορά Προεπιλογών - Close Κλείσιμο - Point your mouse at an option to display its description. Τοποθετήστε το ποντίκι σας πάνω σε μια επιλογή για να εμφανίσετε την περιγραφή της. - consoleLanguageGroupBox Γλώσσα Κονσόλας:\nΡυθμίζει τη γλώσσα που θα χρησιμοποιήσει το παιχνίδι PS4.\nΣυνιστάται να επιλέξετε μία από τις γλώσσες που υποστηρίζονται από το παιχνίδι, η οποία ενδέχεται να διαφέρει ανάλογα με την περιοχή. - emulatorLanguageGroupBox Γλώσσα Εξομοιωτή:\nΡυθμίζει τη γλώσσα του γραφικού περιβάλλοντος του εξομοιωτή. - fullscreenCheckBox Ενεργοποίηση Πλήρους Οθόνης:\nΑυτόματα μετατρέπει το παράθυρο του παιχνιδιού σε λειτουργία πλήρους οθόνης.\nΜπορεί να ενεργοποιηθεί/απενεργοποιηθεί πατώντας το πλήκτρο F11. - separateUpdatesCheckBox Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. - showSplashCheckBox Εμφάνιση Splash Screen:\nΕμφανίζει ειδική γραφική οθόνη κατά την εκκίνηση. - ps4proCheckBox Είναι PS4 Pro:\nΕπιτρέπει στον εξομοιωτή να λειτουργεί σαν PS4 PRO, κάτι που μπορεί να ενεργοποιήσει συγκεκριμένες λειτουργίες σε παιχνίδια που το υποστηρίζουν. - discordRPCCheckbox Ενεργοποίηση Discord Rich Presence:\nΕμφανίζει το εικονίδιο του emulator και σχετικές πληροφορίες στο προφίλ σας στο Discord. - userName Όνομα Χρήστη:\nΟρίζει το όνομα του λογαριασμού PS4, το οποίο μπορεί να εμφανιστεί σε ορισμένα παιχνίδια. - TrophyKey Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. - logTypeGroupBox Τύπος Καταγραφής:\nΚαθορίζει αν η έξοδος του παραθύρου καταγραφής θα συγχρονιστεί για αύξηση της απόδοσης. Αυτό μπορεί να επηρεάσει αρνητικά τις επιδόσεις του εξομοιωτή. - logFilter Φίλτρο Καταγραφής:\nΦιλτράρει τις καταγραφές ώστε να εκτυπώνονται μόνο συγκεκριμένες πληροφορίες.\nΠαραδείγματα: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Επίπεδα: Trace, Debug, Info, Warning, Error, Critical - με τη σειρά αυτή, κάθε επίπεδο που επιλέγεται αποκλείει τα προηγούμενα και εμφανίζει τα επόμενα επίπεδα. - updaterGroupBox Ενημερώσεις:\nRelease: Επίσημες εκδόσεις που κυκλοφορούν μηνιαίως, είναι παλαιότερες αλλά πιο σταθερές και δοκιμασμένες.\nNightly: Εκδόσεις προγραμματιστών με νέες δυνατότητες και διορθώσεις, αλλά μπορεί να περιέχουν σφάλματα και να είναι λιγότερο σταθερές. - GUIgroupBox Αναπαραγωγή Μουσικής Τίτλων:\nΕάν το παιχνίδι το υποστηρίζει, ενεργοποιεί ειδική μουσική κατά την επιλογή του παιχνιδιού από τη διεπαφή χρήστη. - disableTrophycheckBox Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). - hideCursorGroupBox Απόκρυψη Κέρσορα:\nΕπιλέξτε πότε θα εξαφανιστεί ο κέρσορας:\nΠοτέ: θα βλέπετε πάντα το ποντίκι.\nΑδρανές: ορίστε έναν χρόνο για να εξαφανιστεί μετά από αδράνεια.\nΠάντα: δεν θα δείτε ποτέ το ποντίκι. - idleTimeoutGroupBox Ορίστε έναν χρόνο για να εξαφανιστεί το ποντίκι μετά από αδράνεια. - backButtonBehaviorGroupBox Συμπεριφορά Κουμπιού Επιστροφής:\nΟρίζει το κουμπί επιστροφής του ελεγκτή να προσομοιώνει το πάτημα της καθορισμένης θέσης στην οθόνη αφής PS4. - enableCompatibilityCheckBox Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. - checkCompatibilityOnStartupCheckBox Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. - updateCompatibilityButton Update Compatibility Database:\nImmediately update the compatibility database. - Never Ποτέ - Idle Αδρανής - Always Πάντα - Touchpad Left Touchpad Αριστερά - Touchpad Right Touchpad Δεξιά - Touchpad Center Κέντρο Touchpad - None Κανένα - graphicsAdapterGroupBox Προσαρμογέας Γραφικών:\nΣε συστήματα με πολλές GPU, επιλέξτε από το μενού την GPU που θα χρησιμοποιήσει ο εξομοιωτής,\nή επιλέξτε "Auto Select" για αυτόματη επιλογή. - resolutionLayout Ανάλυση Οθόνης:\nΚαθορίζει το μέγεθος του παραθύρου του εξομοιωτή κατά την αναπαραγωγή, το οποίο μπορεί να αλλάξει κατά τη διάρκεια του παιχνιδιού.\nΑυτό είναι διαφορετικό από την ανάλυση του ίδιου του παιχνιδιού. - heightDivider Διαιρέτης Συχνότητας Ανανέωσης:\nΠολλαπλασιάζει τον ρυθμό με τον οποίο ο εξομοιωτής ενημερώνει την εικόνα με αυτόν τον αριθμό. Η αλλαγή αυτής της ρύθμισης μπορεί να έχει αρνητικές επιπτώσεις, όπως ταχύτερο παιχνίδι ή σπασμένες λειτουργίες! - dumpShadersCheckBox Ενεργοποίηση Καταγραφής Σκιάσεων (Shaders):\nΓια τεχνικό εντοπισμό σφαλμάτων, αποθηκεύει τις σκιάσεις του παιχνιδιού σε φάκελο κατά τη διάρκεια της αναπαραγωγής. - nullGpuCheckBox Ενεργοποίηση Εικονικής GPU:\nΓια τεχνικό εντοπισμό σφαλμάτων, απενεργοποιεί την εμφάνιση του παιχνιδιού σαν να μην υπάρχει κάρτα γραφικών. - gameFoldersBox Φάκελοι Παιχνιδιών:\nΗ λίστα των φακέλων για έλεγχο των εγκατεστημένων παιχνιδιών. - addFolderButton Προσθήκη:\nΠροσθέστε έναν φάκελο στη λίστα. - removeFolderButton Αφαίρεση:\nΑφαιρέστε έναν φάκελο από τη λίστα. - debugDump Ενεργοποίηση Καταγραφής Αποσφαλμάτωσης:\nΑποθηκεύει τα σύμβολα εισαγωγής/εξαγωγής και τις κεφαλίδες πληροφοριών του τρέχοντος προγράμματος PS4 σε έναν φάκελο. - vkValidationCheckBox Ενεργοποίηση Επικύρωσης Vulkan:\nΕνεργοποιεί ένα σύστημα που επικυρώνει την κατάσταση του προγράμματος οδήγησης Vulkan και καταγράφει πληροφορίες για την εσωτερική του κατάσταση. Αυτό θα μειώσει την απόδοση και ενδέχεται να αλλάξει τη συμπεριφορά του εξομοιωτή. - vkSyncValidationCheckBox Ενεργοποίηση Επικύρωσης Συγχρονισμού Vulkan:\nΕνεργοποιεί ένα σύστημα που επικυρώνει τον συγχρονισμό των εργασιών απόδοσης του Vulkan. Αυτό θα μειώσει την απόδοση και ενδέχεται να αλλάξει τη συμπεριφορά του εξομοιωτή. - rdocCheckBox Ενεργοποίηση Καταγραφής RenderDoc:\nΌταν είναι ενεργοποιημένο, ο εξομοιωτής είναι συμβατός με το RenderDoc για τη λήψη και ανάλυση του τρέχοντος καρέ. + + CheatsPatches + + Cheats / Patches for + Cheats / Patches for + + + defaultTextEdit_MSG + Οι cheats/patches είναι πειραματικά.\nΧρησιμοποιήστε τα με προσοχή.\n\nΚατεβάστε τους cheats μεμονωμένα επιλέγοντας το αποθετήριο και κάνοντας κλικ στο κουμπί λήψης.\nΣτην καρτέλα Patches, μπορείτε να κατεβάσετε όλα τα patches ταυτόχρονα, να επιλέξετε ποια θέλετε να χρησιμοποιήσετε και να αποθηκεύσετε την επιλογή.\n\nΔεδομένου ότι δεν αναπτύσσουμε τους cheats/patches,\nπαρακαλώ αναφέρετε προβλήματα στον δημιουργό του cheat.\n\nΔημιουργήσατε ένα νέο cheat; Επισκεφθείτε:\nhttps://github.com/shadps4-emu/ps4_cheats + + + No Image Available + Δεν διατίθεται εικόνα + + + Serial: + Σειριακός αριθμός: + + + Version: + Έκδοση: + + + Size: + Μέγεθος: + + + Select Cheat File: + Επιλέξτε αρχείο Cheat: + + + Repository: + Αποθετήριο: + + + Download Cheats + Λήψη Cheats + + + Delete File + Διαγραφή αρχείου + + + No files selected. + Δεν έχουν επιλεγεί αρχεία. + + + You can delete the cheats you don't want after downloading them. + Μπορείτε να διαγράψετε τα cheats που δεν θέλετε μετά τη λήψη τους. + + + Do you want to delete the selected file?\n%1 + Θέλετε να διαγράψετε το επιλεγμένο αρχείο;\n%1 + + + Select Patch File: + Επιλέξτε αρχείο Patch: + + + Download Patches + Λήψη Patches + + + Save + Αποθήκευση + + + Cheats + Cheats + + + Patches + Patches + + + Error + Σφάλμα + + + No patch selected. + Δεν έχει επιλεγεί κανένα patch. + + + Unable to open files.json for reading. + Αδυναμία ανοίγματος του files.json για ανάγνωση. + + + No patch file found for the current serial. + Δεν βρέθηκε αρχείο patch για τον τρέχοντα σειριακό αριθμό. + + + Unable to open the file for reading. + Αδυναμία ανοίγματος του αρχείου για ανάγνωση. + + + Unable to open the file for writing. + Αδυναμία ανοίγματος του αρχείου για εγγραφή. + + + Failed to parse XML: + Αποτυχία ανάλυσης XML: + + + Success + Επιτυχία + + + Options saved successfully. + Οι ρυθμίσεις αποθηκεύτηκαν επιτυχώς. + + + Invalid Source + Μη έγκυρη Πηγή + + + The selected source is invalid. + Η επιλεγμένη πηγή είναι μη έγκυρη. + + + File Exists + Η αρχείο υπάρχει + + + File already exists. Do you want to replace it? + Η αρχείο υπάρχει ήδη. Θέλετε να την αντικαταστήσετε; + + + Failed to save file: + Αποτυχία αποθήκευσης αρχείου: + + + Failed to download file: + Αποτυχία λήψης αρχείου: + + + Cheats Not Found + Δεν βρέθηκαν Cheats + + + CheatsNotFound_MSG + Δεν βρέθηκαν cheats για αυτό το παιχνίδι στην τρέχουσα έκδοση του επιλεγμένου αποθετηρίου. Δοκιμάστε να κατεβάσετε από άλλο αποθετήριο ή άλλη έκδοση του παιχνιδιού. + + + Cheats Downloaded Successfully + Cheats κατεβάστηκαν επιτυχώς + + + CheatsDownloadedSuccessfully_MSG + Κατεβάσατε επιτυχώς cheats για αυτή την έκδοση του παιχνιδιού από το επιλεγμένο αποθετήριο. Μπορείτε να δοκιμάσετε να κατεβάσετε από άλλο αποθετήριο. Αν είναι διαθέσιμο, μπορείτε να το επιλέξετε επιλέγοντας το αρχείο από τη λίστα. + + + Failed to save: + Αποτυχία αποθήκευσης: + + + Failed to download: + Αποτυχία λήψης: + + + Download Complete + Η λήψη ολοκληρώθηκε + + + DownloadComplete_MSG + Τα Patches κατεβάστηκαν επιτυχώς! Όλα τα Patches για όλα τα παιχνίδια έχουν κατέβει, δεν είναι απαραίτητο να τα κατεβάσετε ένα-ένα για κάθε παιχνίδι, όπως με τα Cheats. Εάν η ενημέρωση δεν εμφανίζεται, μπορεί να μην υπάρχει για τον συγκεκριμένο σειριακό αριθμό και έκδοση του παιχνιδιού. + + + Failed to parse JSON data from HTML. + Αποτυχία ανάλυσης δεδομένων JSON από HTML. + + + Failed to retrieve HTML page. + Αποτυχία ανάκτησης σελίδας HTML. + + + The game is in version: %1 + Το παιχνίδι είναι στην έκδοση: %1 + + + The downloaded patch only works on version: %1 + Η ληφθείσα ενημέρωση λειτουργεί μόνο στην έκδοση: %1 + + + You may need to update your game. + Μπορεί να χρειαστεί να ενημερώσετε το παιχνίδι σας. + + + Incompatibility Notice + Ειδοποίηση ασυμβατότητας + + + Failed to open file: + Αποτυχία ανοίγματος αρχείου: + + + XML ERROR: + ΣΦΑΛΜΑ XML: + + + Failed to open files.json for writing + Αποτυχία ανοίγματος του files.json για εγγραφή + + + Author: + Συγγραφέας: + + + Directory does not exist: + Ο φάκελος δεν υπάρχει: + + + Failed to open files.json for reading. + Αποτυχία ανοίγματος του files.json για ανάγνωση. + + + Name: + Όνομα: + + + Can't apply cheats before the game is started + Δεν μπορείτε να εφαρμόσετε cheats πριν ξεκινήσει το παιχνίδι. + + GameListFrame - Icon Εικονίδιο - Name Όνομα - Serial Σειριακός αριθμός - Compatibility Compatibility - Region Περιοχή - Firmware Λογισμικό - Size Μέγεθος - Version Έκδοση - Path Διαδρομή - Play Time Χρόνος παιχνιδιού - Never Played Never Played - h h - m m - s s - Compatibility is untested Compatibility is untested - Game does not initialize properly / crashes the emulator Game does not initialize properly / crashes the emulator - Game boots, but only displays a blank screen Game boots, but only displays a blank screen - Game displays an image but does not go past the menu Game displays an image but does not go past the menu - Game has game-breaking glitches or unplayable performance Game has game-breaking glitches or unplayable performance - Game can be completed with playable performance and no major glitches Game can be completed with playable performance and no major glitches @@ -1508,127 +1210,102 @@ CheckUpdate - Auto Updater Αυτόματος Ενημερωτής - Error Σφάλμα - Network error: Σφάλμα δικτύου: - Failed to parse update information. Αποτυχία ανάλυσης πληροφοριών ενημέρωσης. - No pre-releases found. Δεν βρέθηκαν προ-κυκλοφορίες. - Invalid release data. Μη έγκυρα δεδομένα έκδοσης. - No download URL found for the specified asset. Δεν βρέθηκε URL λήψης για το συγκεκριμένο στοιχείο. - Your version is already up to date! Η έκδοσή σας είναι ήδη ενημερωμένη! - Update Available Διαθέσιμη Ενημέρωση - Update Channel Κανάλι Ενημέρωσης - Current Version Τρέχουσα Έκδοση - Latest Version Τελευταία Έκδοση - Do you want to update? Θέλετε να ενημερώσετε; - Show Changelog Εμφάνιση Ιστορικού Αλλαγών - Check for Updates at Startup Έλεγχος για ενημερώσεις κατά την εκκίνηση - Update Ενημέρωση - No Όχι - Hide Changelog Απόκρυψη Ιστορικού Αλλαγών - Changes Αλλαγές - Network error occurred while trying to access the URL Σφάλμα δικτύου κατά την προσπάθεια πρόσβασης στη διεύθυνση URL - Download Complete Λήψη ολοκληρώθηκε - The update has been downloaded, press OK to install. Η ενημέρωση έχει ληφθεί, πατήστε OK για να εγκαταστήσετε. - Failed to save the update file at Αποτυχία αποθήκευσης του αρχείου ενημέρωσης στο - Starting Update... Εκκίνηση Ενημέρωσης... - Failed to create the update script file Αποτυχία δημιουργίας του αρχείου σεναρίου ενημέρωσης @@ -1636,29 +1313,24 @@ GameListUtils - B B - KB KB - MB MB - GB GB - TB TB - + \ No newline at end of file diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index 293b5fae7..1f932ea97 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -6,22 +6,18 @@ AboutDialog - About shadPS4 About shadPS4 - shadPS4 shadPS4 - shadPS4 is an experimental open-source emulator for the PlayStation 4. shadPS4 is an experimental open-source emulator for the PlayStation 4. - This software should not be used to play games you have not legally obtained. This software should not be used to play games you have not legally obtained. @@ -29,7 +25,6 @@ ElfViewer - Open Folder Open Folder @@ -37,17 +32,14 @@ GameInfoClass - Loading game list, please wait :3 Loading game list, please wait :3 - Cancel Cancel - Loading... Loading... @@ -55,12 +47,10 @@ InstallDirSelect - shadPS4 - Choose directory shadPS4 - Choose directory - Select which directory you want to install to. Select which directory you want to install to. @@ -68,27 +58,22 @@ GameInstallDialog - shadPS4 - Choose directory shadPS4 - Choose directory - Directory to install games Directory to install games - Browse Browse - Error Error - The value for location to install games is not valid. The value for location to install games is not valid. @@ -96,167 +81,134 @@ GuiContextMenus - Create Shortcut Create Shortcut - Cheats / Patches Cheats / Patches - SFO Viewer SFO Viewer - Trophy Viewer Trophy Viewer - Open Folder... Open Folder... - Open Game Folder Open Game Folder - Open Save Data Folder Open Save Data Folder - Open Log Folder Open Log Folder - Copy info... Copy info... - Copy Name Copy Name - Copy Serial Copy Serial - Copy All Copy All - Delete... Delete... - Delete Game Delete Game - Delete Update Delete Update - Delete DLC Delete DLC - Compatibility... Compatibility... - Update database Update database - View report View report - Submit a report Submit a report - Shortcut creation Shortcut creation - Shortcut created successfully! Shortcut created successfully! - Error Error - Error creating shortcut! Error creating shortcut! - Install PKG Install PKG - Game Game - requiresEnableSeparateUpdateFolder_MSG This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. - This game has no update to delete! This game has no update to delete! - - + Update Update - This game has no DLC to delete! This game has no DLC to delete! - DLC DLC - Delete %1 Delete %1 - Are you sure you want to delete %1's %2 directory? Are you sure you want to delete %1's %2 directory? @@ -264,205 +216,285 @@ MainWindow - Open/Add Elf Folder Open/Add Elf Folder - Install Packages (PKG) Install Packages (PKG) - Boot Game Boot Game - Check for Updates Check for Updates - About shadPS4 About shadPS4 - Configure... Configure... - Install application from a .pkg file Install application from a .pkg file - Recent Games Recent Games - Exit Exit - Exit shadPS4 Exit shadPS4 - Exit the application. Exit the application. - Show Game List Show Game List - Game List Refresh Game List Refresh - Tiny Tiny - Small Small - Medium Medium - Large Large - List View List View - Grid View Grid View - Elf Viewer Elf Viewer - Game Install Directory Game Install Directory - Download Cheats/Patches Download Cheats / Patches - Dump Game List Dump Game List - PKG Viewer PKG Viewer - Search... Search... - File File - View View - Game List Icons Game List Icons - Game List Mode Game List Mode - Settings Settings - Utils Utils - Themes Themes - Help Help - Dark Dark - Light Light - Green Green - Blue Blue - Violet Violet - toolBar toolBar + + Game List + Game List + + + * Unsupported Vulkan Version + * Unsupported Vulkan Version + + + Download Cheats For All Installed Games + Download Cheats For All Installed Games + + + Download Patches For All Games + Download Patches For All Games + + + Download Complete + Download Complete + + + You have downloaded cheats for all the games you have installed. + You have downloaded cheats for all the games you have installed. + + + Patches Downloaded Successfully! + Patches Downloaded Successfully! + + + All Patches available for all games have been downloaded. + All Patches available for all games have been downloaded. + + + Games: + Games: + + + PKG File (*.PKG) + PKG File (*.PKG) + + + ELF files (*.bin *.elf *.oelf) + ELF files (*.bin *.elf *.oelf) + + + Game Boot + Game Boot + + + Only one file can be selected! + Only one file can be selected! + + + PKG Extraction + PKG Extraction + + + Patch detected! + Patch detected! + + + PKG and Game versions match: + PKG and Game versions match: + + + Would you like to overwrite? + Would you like to overwrite? + + + PKG Version %1 is older than installed version: + PKG Version %1 is older than installed version: + + + Game is installed: + Game is installed: + + + Would you like to install Patch: + Would you like to install Patch: + + + DLC Installation + DLC Installation + + + Would you like to install DLC: %1? + Would you like to install DLC: %1? + + + DLC already installed: + DLC already installed: + + + Game already installed + Game already installed + + + PKG is a patch, please install the game first! + PKG is a patch, please install the game first! + + + PKG ERROR + PKG ERROR + + + Extracting PKG %1/%2 + Extracting PKG %1/%2 + + + Extraction Finished + Extraction Finished + + + Game successfully installed at %1 + Game successfully installed at %1 + + + File doesn't appear to be a valid PKG file + File doesn't appear to be a valid PKG file + PKGViewer - Open Folder Open Folder @@ -470,7 +502,6 @@ TrophyViewer - Trophy Viewer Trophy Viewer @@ -478,1029 +509,700 @@ SettingsDialog - Settings Settings - General General - System System - Console Language Console Language - Emulator Language Emulator Language - Emulator Emulator - Enable Fullscreen Enable Fullscreen - Enable Separate Update Folder Enable Separate Update Folder - Show Splash Show Splash - Is PS4 Pro Is PS4 Pro - Enable Discord Rich Presence Enable Discord Rich Presence - Username Username - Trophy Key Trophy Key - Trophy Trophy - Logger Logger - Log Type Log Type - Log Filter Log Filter - Input Input - Cursor Cursor - Hide Cursor Hide Cursor - Hide Cursor Idle Timeout Hide Cursor Idle Timeout - s s - Controller Controller - Back Button Behavior Back Button Behavior - Graphics Graphics - Graphics Device Graphics Device - Width Width - Height Height - Vblank Divider Vblank Divider - Advanced Advanced - Enable Shaders Dumping Enable Shaders Dumping - Enable NULL GPU Enable NULL GPU - Paths Paths - Game Folders Game Folders - Add... Add... - Remove Remove - Debug Debug - Enable Debug Dumping Enable Debug Dumping - Enable Vulkan Validation Layers Enable Vulkan Validation Layers - Enable Vulkan Synchronization Validation Enable Vulkan Synchronization Validation - Enable RenderDoc Debugging Enable RenderDoc Debugging - Update Update - Check for Updates at Startup Check for Updates at Startup - Update Channel Update Channel - Check for Updates Check for Updates - GUI Settings GUI Settings - Disable Trophy Pop-ups Disable Trophy Pop-ups - Play title music Play title music - Update Compatibility Database On Startup Update Compatibility Database On Startup - Game Compatibility Game Compatibility - Display Compatibility Data Display Compatibility Data - Update Compatibility Database Update Compatibility Database - Volume Volume - Audio Backend Audio Backend - - - MainWindow - - Game List - Game List - - - - * Unsupported Vulkan Version - * Unsupported Vulkan Version - - - - Download Cheats For All Installed Games - Download Cheats For All Installed Games - - - - Download Patches For All Games - Download Patches For All Games - - - - Download Complete - Download Complete - - - - You have downloaded cheats for all the games you have installed. - You have downloaded cheats for all the games you have installed. - - - - Patches Downloaded Successfully! - Patches Downloaded Successfully! - - - - All Patches available for all games have been downloaded. - All Patches available for all games have been downloaded. - - - - Games: - Games: - - - - PKG File (*.PKG) - PKG File (*.PKG) - - - - ELF files (*.bin *.elf *.oelf) - ELF files (*.bin *.elf *.oelf) - - - - Game Boot - Game Boot - - - - Only one file can be selected! - Only one file can be selected! - - - - PKG Extraction - PKG Extraction - - - - Patch detected! - Patch detected! - - - - PKG and Game versions match: - PKG and Game versions match: - - - - Would you like to overwrite? - Would you like to overwrite? - - - - PKG Version %1 is older than installed version: - PKG Version %1 is older than installed version: - - - - Game is installed: - Game is installed: - - - - Would you like to install Patch: - Would you like to install Patch: - - - - DLC Installation - DLC Installation - - - - Would you like to install DLC: %1? - Would you like to install DLC: %1? - - - - DLC already installed: - DLC already installed: - - - - Game already installed - Game already installed - - - - PKG is a patch, please install the game first! - PKG is a patch, please install the game first! - - - - PKG ERROR - PKG ERROR - - - - Extracting PKG %1/%2 - Extracting PKG %1/%2 - - - - Extraction Finished - Extraction Finished - - - - Game successfully installed at %1 - Game successfully installed at %1 - - - - File doesn't appear to be a valid PKG file - File doesn't appear to be a valid PKG file - - - - CheatsPatches - - - Cheats / Patches for - Cheats / Patches for - - - - defaultTextEdit_MSG - Cheats/Patches are experimental.\nUse with caution.\n\nDownload cheats individually by selecting the repository and clicking the download button.\nIn the Patches tab, you can download all patches at once, choose which ones you want to use, and save your selection.\n\nSince we do not develop the Cheats/Patches,\nplease report issues to the cheat author.\n\nCreated a new cheat? Visit:\nhttps://github.com/shadps4-emu/ps4_cheats - - - - No Image Available - No Image Available - - - - Serial: - Serial: - - - - Version: - Version: - - - - Size: - Size: - - - - Select Cheat File: - Select Cheat File: - - - - Repository: - Repository: - - - - Download Cheats - Download Cheats - - - - Delete File - Delete File - - - - No files selected. - No files selected. - - - - You can delete the cheats you don't want after downloading them. - You can delete the cheats you don't want after downloading them. - - - - Do you want to delete the selected file?\n%1 - Do you want to delete the selected file?\n%1 - - - - Select Patch File: - Select Patch File: - - - - Download Patches - Download Patches - - - Save Save - - Cheats - Cheats - - - - Patches - Patches - - - - Error - Error - - - - No patch selected. - No patch selected. - - - - Unable to open files.json for reading. - Unable to open files.json for reading. - - - - No patch file found for the current serial. - No patch file found for the current serial. - - - - Unable to open the file for reading. - Unable to open the file for reading. - - - - Unable to open the file for writing. - Unable to open the file for writing. - - - - Failed to parse XML: - Failed to parse XML: - - - - Success - Success - - - - Options saved successfully. - Options saved successfully. - - - - Invalid Source - Invalid Source - - - - The selected source is invalid. - The selected source is invalid. - - - - File Exists - File Exists - - - - File already exists. Do you want to replace it? - File already exists. Do you want to replace it? - - - - Failed to save file: - Failed to save file: - - - - Failed to download file: - Failed to download file: - - - - Cheats Not Found - Cheats Not Found - - - - CheatsNotFound_MSG - No Cheats found for this game in this version of the selected repository,try another repository or a different version of the game. - - - - Cheats Downloaded Successfully - Cheats Downloaded Successfully - - - - CheatsDownloadedSuccessfully_MSG - You have successfully downloaded the cheats for this version of the game from the selected repository. You can try downloading from another repository, if it is available it will also be possible to use it by selecting the file from the list. - - - - Failed to save: - Failed to save: - - - - Failed to download: - Failed to download: - - - - Download Complete - Download Complete - - - - DownloadComplete_MSG - Patches Downloaded Successfully! All Patches available for all games have been downloaded, there is no need to download them individually for each game as happens in Cheats. If the patch does not appear, it may be that it does not exist for the specific serial and version of the game. - - - - Failed to parse JSON data from HTML. - Failed to parse JSON data from HTML. - - - - Failed to retrieve HTML page. - Failed to retrieve HTML page. - - - - The game is in version: %1 - The game is in version: %1 - - - - The downloaded patch only works on version: %1 - The downloaded patch only works on version: %1 - - - - You may need to update your game. - You may need to update your game. - - - - Incompatibility Notice - Incompatibility Notice - - - - Failed to open file: - Failed to open file: - - - - XML ERROR: - XML ERROR: - - - - Failed to open files.json for writing - Failed to open files.json for writing - - - - Author: - Author: - - - - Directory does not exist: - Directory does not exist: - - - - Failed to open files.json for reading. - Failed to open files.json for reading. - - - - Name: - Name: - - - - Can't apply cheats before the game is started - Can't apply cheats before the game is started. - - - - SettingsDialog - - - Save - Save - - - Apply Apply - Restore Defaults Restore Defaults - Close Close - Point your mouse at an option to display its description. Point your mouse at an option to display its description. - consoleLanguageGroupBox Console Language:\nSets the language that the PS4 game uses.\nIt's recommended to set this to a language the game supports, which will vary by region. - emulatorLanguageGroupBox Emulator Language:\nSets the language of the emulator's user interface. - fullscreenCheckBox Enable Full Screen:\nAutomatically puts the game window into full-screen mode.\nThis can be toggled by pressing the F11 key. - separateUpdatesCheckBox Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - showSplashCheckBox Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. - ps4proCheckBox Is PS4 Pro:\nMakes the emulator act as a PS4 PRO, which may enable special features in games that support it. - discordRPCCheckbox Enable Discord Rich Presence:\nDisplays the emulator icon and relevant information on your Discord profile. - userName Username:\nSets the PS4's account username, which may be displayed by some games. - TrophyKey Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. - logTypeGroupBox Log Type:\nSets whether to synchronize the output of the log window for performance. May have adverse effects on emulation. - logFilter Log Filter:\nFilters the log to only print specific information.\nExamples: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical"\nLevels: Trace, Debug, Info, Warning, Error, Critical - in this order, a specific level silences all levels preceding it in the list and logs every level after it. - updaterGroupBox Update:\nRelease: Official versions released every month that may be very outdated, but are more reliable and tested.\nNightly: Development versions that have all the latest features and fixes, but may contain bugs and are less stable. - GUIgroupBox Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI. - disableTrophycheckBox Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). - hideCursorGroupBox Hide Cursor:\nChoose when the cursor will disappear:\nNever: You will always see the mouse.\nidle: Set a time for it to disappear after being idle.\nAlways: you will never see the mouse. - idleTimeoutGroupBox Hide Idle Cursor Timeout:\nThe duration (seconds) after which the cursor that has been idle hides itself. - backButtonBehaviorGroupBox Back Button Behavior:\nSets the controller's back button to emulate tapping the specified position on the PS4 touchpad. - enableCompatibilityCheckBox Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. - checkCompatibilityOnStartupCheckBox Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. - updateCompatibilityButton Update Compatibility Database:\nImmediately update the compatibility database. - Never Never - Idle Idle - Always Always - Touchpad Left Touchpad Left - Touchpad Right Touchpad Right - Touchpad Center Touchpad Center - None None - graphicsAdapterGroupBox Graphics Device:\nOn multiple GPU systems, select the GPU the emulator will use from the drop down list,\nor select "Auto Select" to automatically determine it. - resolutionLayout Width/Height:\nSets the size of the emulator window at launch, which can be resized during gameplay.\nThis is different from the in-game resolution. - heightDivider Vblank Divider:\nThe frame rate at which the emulator refreshes at is multiplied by this number. Changing this may have adverse effects, such as increasing the game speed, or breaking critical game functionality that does not expect this to change! - dumpShadersCheckBox Enable Shaders Dumping:\nFor the sake of technical debugging, saves the games shaders to a folder as they render. - nullGpuCheckBox Enable Null GPU:\nFor the sake of technical debugging, disables game rendering as if there were no graphics card. - gameFoldersBox Game Folders:\nThe list of folders to check for installed games. - addFolderButton Add:\nAdd a folder to the list. - removeFolderButton Remove:\nRemove a folder from the list. - debugDump Enable Debug Dumping:\nSaves the import and export symbols and file header information of the currently running PS4 program to a directory. - vkValidationCheckBox Enable Vulkan Validation Layers:\nEnables a system that validates the state of the Vulkan renderer and logs information about its internal state.\nThis will reduce performance and likely change the behavior of emulation. - vkSyncValidationCheckBox Enable Vulkan Synchronization Validation:\nEnables a system that validates the timing of Vulkan rendering tasks.\nThis will reduce performance and likely change the behavior of emulation. - rdocCheckBox Enable RenderDoc Debugging:\nIf enabled, the emulator will provide compatibility with Renderdoc to allow capture and analysis of the currently rendered frame. + + CheatsPatches + + Cheats / Patches for + Cheats / Patches for + + + defaultTextEdit_MSG + Cheats/Patches are experimental.\nUse with caution.\n\nDownload cheats individually by selecting the repository and clicking the download button.\nIn the Patches tab, you can download all patches at once, choose which ones you want to use, and save your selection.\n\nSince we do not develop the Cheats/Patches,\nplease report issues to the cheat author.\n\nCreated a new cheat? Visit:\nhttps://github.com/shadps4-emu/ps4_cheats + + + No Image Available + No Image Available + + + Serial: + Serial: + + + Version: + Version: + + + Size: + Size: + + + Select Cheat File: + Select Cheat File: + + + Repository: + Repository: + + + Download Cheats + Download Cheats + + + Delete File + Delete File + + + No files selected. + No files selected. + + + You can delete the cheats you don't want after downloading them. + You can delete the cheats you don't want after downloading them. + + + Do you want to delete the selected file?\n%1 + Do you want to delete the selected file?\n%1 + + + Select Patch File: + Select Patch File: + + + Download Patches + Download Patches + + + Save + Save + + + Cheats + Cheats + + + Patches + Patches + + + Error + Error + + + No patch selected. + No patch selected. + + + Unable to open files.json for reading. + Unable to open files.json for reading. + + + No patch file found for the current serial. + No patch file found for the current serial. + + + Unable to open the file for reading. + Unable to open the file for reading. + + + Unable to open the file for writing. + Unable to open the file for writing. + + + Failed to parse XML: + Failed to parse XML: + + + Success + Success + + + Options saved successfully. + Options saved successfully. + + + Invalid Source + Invalid Source + + + The selected source is invalid. + The selected source is invalid. + + + File Exists + File Exists + + + File already exists. Do you want to replace it? + File already exists. Do you want to replace it? + + + Failed to save file: + Failed to save file: + + + Failed to download file: + Failed to download file: + + + Cheats Not Found + Cheats Not Found + + + CheatsNotFound_MSG + No Cheats found for this game in this version of the selected repository,try another repository or a different version of the game. + + + Cheats Downloaded Successfully + Cheats Downloaded Successfully + + + CheatsDownloadedSuccessfully_MSG + You have successfully downloaded the cheats for this version of the game from the selected repository. You can try downloading from another repository, if it is available it will also be possible to use it by selecting the file from the list. + + + Failed to save: + Failed to save: + + + Failed to download: + Failed to download: + + + Download Complete + Download Complete + + + DownloadComplete_MSG + Patches Downloaded Successfully! All Patches available for all games have been downloaded, there is no need to download them individually for each game as happens in Cheats. If the patch does not appear, it may be that it does not exist for the specific serial and version of the game. + + + Failed to parse JSON data from HTML. + Failed to parse JSON data from HTML. + + + Failed to retrieve HTML page. + Failed to retrieve HTML page. + + + The game is in version: %1 + The game is in version: %1 + + + The downloaded patch only works on version: %1 + The downloaded patch only works on version: %1 + + + You may need to update your game. + You may need to update your game. + + + Incompatibility Notice + Incompatibility Notice + + + Failed to open file: + Failed to open file: + + + XML ERROR: + XML ERROR: + + + Failed to open files.json for writing + Failed to open files.json for writing + + + Author: + Author: + + + Directory does not exist: + Directory does not exist: + + + Failed to open files.json for reading. + Failed to open files.json for reading. + + + Name: + Name: + + + Can't apply cheats before the game is started + Can't apply cheats before the game is started. + + GameListFrame - Icon Icon - Name Name - Serial Serial - Compatibility Compatibility - Region Region - Firmware Firmware - Size Size - Version Version - Path Path - Play Time Play Time - Never Played Never Played - h h - m m - s s - Compatibility is untested Compatibility is untested - Game does not initialize properly / crashes the emulator Game does not initialize properly / crashes the emulator - Game boots, but only displays a blank screen Game boots, but only displays a blank screen - Game displays an image but does not go past the menu Game displays an image but does not go past the menu - Game has game-breaking glitches or unplayable performance Game has game-breaking glitches or unplayable performance - Game can be completed with playable performance and no major glitches Game can be completed with playable performance and no major glitches @@ -1508,127 +1210,102 @@ CheckUpdate - Auto Updater Auto Updater - Error Error - Network error: Network error: - Failed to parse update information. Failed to parse update information. - No pre-releases found. No pre-releases found. - Invalid release data. Invalid release data. - No download URL found for the specified asset. No download URL found for the specified asset. - Your version is already up to date! Your version is already up to date! - Update Available Update Available - Update Channel Update Channel - Current Version Current Version - Latest Version Latest Version - Do you want to update? Do you want to update? - Show Changelog Show Changelog - Check for Updates at Startup Check for Updates at Startup - Update Update - No No - Hide Changelog Hide Changelog - Changes Changes - Network error occurred while trying to access the URL Network error occurred while trying to access the URL - Download Complete Download Complete - The update has been downloaded, press OK to install. The update has been downloaded, press OK to install. - Failed to save the update file at Failed to save the update file at - Starting Update... Starting Update... - Failed to create the update script file Failed to create the update script file @@ -1636,29 +1313,24 @@ GameListUtils - B B - KB KB - MB MB - GB GB - TB TB - + \ No newline at end of file diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index 096e104e3..021b39ed8 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -6,22 +6,18 @@ AboutDialog - About shadPS4 Acerca de shadPS4 - shadPS4 shadPS4 - shadPS4 is an experimental open-source emulator for the PlayStation 4. shadPS4 es un emulador experimental de código abierto para la PlayStation 4. - This software should not be used to play games you have not legally obtained. Este software no debe utilizarse para jugar juegos que hayas obtenido ilegalmente. @@ -29,7 +25,6 @@ ElfViewer - Open Folder Abrir carpeta @@ -37,17 +32,14 @@ GameInfoClass - Loading game list, please wait :3 Cargando lista de juegos, por favor espera :3 - Cancel Cancelar - Loading... Cargando... @@ -55,12 +47,10 @@ InstallDirSelect - shadPS4 - Choose directory shadPS4 - Elegir carpeta - Select which directory you want to install to. Select which directory you want to install to. @@ -68,27 +58,22 @@ GameInstallDialog - shadPS4 - Choose directory shadPS4 - Elegir carpeta - Directory to install games Carpeta para instalar juegos - Browse Buscar - Error Error - The value for location to install games is not valid. El valor para la ubicación de instalación de los juegos no es válido. @@ -96,167 +81,134 @@ GuiContextMenus - Create Shortcut Crear acceso directo - Cheats / Patches Trucos / Parches - SFO Viewer Vista SFO - Trophy Viewer Ver trofeos - Open Folder... Abrir Carpeta... - Open Game Folder Abrir Carpeta del Juego - Open Save Data Folder Abrir Carpeta de Datos Guardados - Open Log Folder Abrir Carpeta de Registros - Copy info... Copiar información... - Copy Name Copiar nombre - Copy Serial Copiar número de serie - Copy All Copiar todo - Delete... Delete... - Delete Game Delete Game - Delete Update Delete Update - Delete DLC Delete DLC - Compatibility... Compatibility... - Update database Update database - View report View report - Submit a report Submit a report - Shortcut creation Acceso directo creado - Shortcut created successfully! ¡Acceso directo creado con éxito! - Error Error - Error creating shortcut! ¡Error al crear el acceso directo! - Install PKG Instalar PKG - Game Game - requiresEnableSeparateUpdateFolder_MSG This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. - This game has no update to delete! This game has no update to delete! - - + Update Update - This game has no DLC to delete! This game has no DLC to delete! - DLC DLC - Delete %1 Delete %1 - Are you sure you want to delete %1's %2 directory? Are you sure you want to delete %1's %2 directory? @@ -264,205 +216,285 @@ MainWindow - Open/Add Elf Folder Abrir/Agregar carpeta Elf - Install Packages (PKG) Instalar paquetes (PKG) - Boot Game Iniciar juego - Check for Updates Buscar actualizaciones - About shadPS4 Acerca de shadPS4 - Configure... Configurar... - Install application from a .pkg file Instalar aplicación desde un archivo .pkg - Recent Games Juegos recientes - Exit Salir - Exit shadPS4 Salir de shadPS4 - Exit the application. Salir de la aplicación. - Show Game List Mostrar lista de juegos - Game List Refresh Actualizar lista de juegos - Tiny Muy pequeño - Small Pequeño - Medium Mediano - Large Grande - List View Vista de lista - Grid View Vista de cuadrícula - Elf Viewer Vista Elf - Game Install Directory Carpeta de instalación de los juegos - Download Cheats/Patches Descargar Trucos / Parches - Dump Game List Volcar lista de juegos - PKG Viewer Vista PKG - Search... Buscar... - File Archivo - View Vista - Game List Icons Iconos de los juegos - Game List Mode Tipo de lista - Settings Configuración - Utils Utilidades - Themes Temas - Help Ayuda - Dark Oscuro - Light Claro - Green Verde - Blue Azul - Violet Violeta - toolBar Barra de herramientas + + Game List + Lista de juegos + + + * Unsupported Vulkan Version + * Versión de Vulkan no soportada + + + Download Cheats For All Installed Games + Descargar trucos para todos los juegos instalados + + + Download Patches For All Games + Descargar parches para todos los juegos + + + Download Complete + Descarga completa + + + You have downloaded cheats for all the games you have installed. + Has descargado trucos para todos los juegos que tienes instalados. + + + Patches Downloaded Successfully! + ¡Parches descargados exitosamente! + + + All Patches available for all games have been downloaded. + Todos los parches disponibles han sido descargados para todos los juegos. + + + Games: + Juegos: + + + PKG File (*.PKG) + Archivo PKG (*.PKG) + + + ELF files (*.bin *.elf *.oelf) + Archivos ELF (*.bin *.elf *.oelf) + + + Game Boot + Inicio del juego + + + Only one file can be selected! + ¡Solo se puede seleccionar un archivo! + + + PKG Extraction + Extracción de PKG + + + Patch detected! + ¡Actualización detectada! + + + PKG and Game versions match: + Las versiones de PKG y del juego coinciden: + + + Would you like to overwrite? + ¿Desea sobrescribir? + + + PKG Version %1 is older than installed version: + La versión de PKG %1 es más antigua que la versión instalada: + + + Game is installed: + El juego está instalado: + + + Would you like to install Patch: + ¿Desea instalar la actualización: + + + DLC Installation + Instalación de DLC + + + Would you like to install DLC: %1? + ¿Desea instalar el DLC: %1? + + + DLC already installed: + DLC ya instalado: + + + Game already installed + Juego ya instalado + + + PKG is a patch, please install the game first! + PKG es un parche, ¡por favor instala el juego primero! + + + PKG ERROR + ERROR PKG + + + Extracting PKG %1/%2 + Extrayendo PKG %1/%2 + + + Extraction Finished + Extracción terminada + + + Game successfully installed at %1 + Juego instalado exitosamente en %1 + + + File doesn't appear to be a valid PKG file + El archivo parece no ser un archivo PKG válido + PKGViewer - Open Folder Abrir carpeta @@ -470,7 +502,6 @@ TrophyViewer - Trophy Viewer Vista de trofeos @@ -478,1029 +509,700 @@ SettingsDialog - Settings Configuración - General General - System Sistema - Console Language Idioma de la consola - Emulator Language Idioma del emulador - Emulator Emulador - Enable Fullscreen Habilitar pantalla completa - Enable Separate Update Folder Enable Separate Update Folder - Show Splash Mostrar splash - Is PS4 Pro Modo PS4 Pro - Enable Discord Rich Presence Habilitar Discord Rich Presence - Username Nombre de usuario - Trophy Key Trophy Key - Trophy Trophy - Logger Registro - Log Type Tipo de registro - Log Filter Filtro de registro - Input Entrada - Cursor Cursor - Hide Cursor Ocultar cursor - Hide Cursor Idle Timeout Tiempo de espera para ocultar cursor inactivo - s s - Controller Controlador - Back Button Behavior Comportamiento del botón de retroceso - Graphics Gráficos - Graphics Device Dispositivo gráfico - Width Ancho - Height Alto - Vblank Divider Divisor de Vblank - Advanced Avanzado - Enable Shaders Dumping Habilitar volcado de shaders - Enable NULL GPU Habilitar GPU NULL - Paths Rutas - Game Folders Carpetas de juego - Add... Añadir... - Remove Eliminar - Debug Depuración - Enable Debug Dumping Habilitar volcado de depuración - Enable Vulkan Validation Layers Habilitar capas de validación de Vulkan - Enable Vulkan Synchronization Validation Habilitar validación de sincronización de Vulkan - Enable RenderDoc Debugging Habilitar depuración de RenderDoc - Update Actualización - Check for Updates at Startup Buscar actualizaciones al iniciar - Update Channel Canal de Actualización - Check for Updates Verificar actualizaciones - GUI Settings Configuraciones de la Interfaz - Disable Trophy Pop-ups Disable Trophy Pop-ups - Play title music Reproducir la música de apertura - Update Compatibility Database On Startup Update Compatibility Database On Startup - Game Compatibility Game Compatibility - Display Compatibility Data Display Compatibility Data - Update Compatibility Database Update Compatibility Database - Volume Volumen - Audio Backend Audio Backend - - - MainWindow - - Game List - Lista de juegos - - - - * Unsupported Vulkan Version - * Versión de Vulkan no soportada - - - - Download Cheats For All Installed Games - Descargar trucos para todos los juegos instalados - - - - Download Patches For All Games - Descargar parches para todos los juegos - - - - Download Complete - Descarga completa - - - - You have downloaded cheats for all the games you have installed. - Has descargado trucos para todos los juegos que tienes instalados. - - - - Patches Downloaded Successfully! - ¡Parches descargados exitosamente! - - - - All Patches available for all games have been downloaded. - Todos los parches disponibles han sido descargados para todos los juegos. - - - - Games: - Juegos: - - - - PKG File (*.PKG) - Archivo PKG (*.PKG) - - - - ELF files (*.bin *.elf *.oelf) - Archivos ELF (*.bin *.elf *.oelf) - - - - Game Boot - Inicio del juego - - - - Only one file can be selected! - ¡Solo se puede seleccionar un archivo! - - - - PKG Extraction - Extracción de PKG - - - - Patch detected! - ¡Actualización detectada! - - - - PKG and Game versions match: - Las versiones de PKG y del juego coinciden: - - - - Would you like to overwrite? - ¿Desea sobrescribir? - - - - PKG Version %1 is older than installed version: - La versión de PKG %1 es más antigua que la versión instalada: - - - - Game is installed: - El juego está instalado: - - - - Would you like to install Patch: - ¿Desea instalar la actualización: - - - - DLC Installation - Instalación de DLC - - - - Would you like to install DLC: %1? - ¿Desea instalar el DLC: %1? - - - - DLC already installed: - DLC ya instalado: - - - - Game already installed - Juego ya instalado - - - - PKG is a patch, please install the game first! - PKG es un parche, ¡por favor instala el juego primero! - - - - PKG ERROR - ERROR PKG - - - - Extracting PKG %1/%2 - Extrayendo PKG %1/%2 - - - - Extraction Finished - Extracción terminada - - - - Game successfully installed at %1 - Juego instalado exitosamente en %1 - - - - File doesn't appear to be a valid PKG file - El archivo parece no ser un archivo PKG válido - - - - CheatsPatches - - - Cheats / Patches for - Cheats / Patches for - - - - defaultTextEdit_MSG - Los cheats/patches son experimentales.\nÚselos con precaución.\n\nDescargue los cheats individualmente seleccionando el repositorio y haciendo clic en el botón de descarga.\nEn la pestaña Patches, puede descargar todos los patches a la vez, elegir cuáles desea usar y guardar la selección.\n\nComo no desarrollamos los Cheats/Patches,\npor favor informe los problemas al autor del cheat.\n\n¿Creaste un nuevo cheat? Visita:\nhttps://github.com/shadps4-emu/ps4_cheats - - - - No Image Available - No hay imagen disponible - - - - Serial: - Número de serie: - - - - Version: - Versión: - - - - Size: - Tamaño: - - - - Select Cheat File: - Seleccionar archivo de trucos: - - - - Repository: - Repositorio: - - - - Download Cheats - Descargar trucos - - - - Delete File - Eliminar archivo - - - - No files selected. - No se han seleccionado archivos. - - - - You can delete the cheats you don't want after downloading them. - Puedes eliminar los trucos que no quieras una vez descargados. - - - - Do you want to delete the selected file?\n%1 - ¿Deseas eliminar el archivo seleccionado?\n%1 - - - - Select Patch File: - Seleccionar archivo de parche: - - - - Download Patches - Descargar parches - - - Save Guardar - - Cheats - Trucos - - - - Patches - Parches - - - - Error - Error - - - - No patch selected. - No se ha seleccionado ningún parche. - - - - Unable to open files.json for reading. - No se puede abrir files.json para lectura. - - - - No patch file found for the current serial. - No se encontró ningún archivo de parche para el número de serie actual. - - - - Unable to open the file for reading. - No se puede abrir el archivo para lectura. - - - - Unable to open the file for writing. - No se puede abrir el archivo para escritura. - - - - Failed to parse XML: - Error al analizar XML: - - - - Success - Éxito - - - - Options saved successfully. - Opciones guardadas exitosamente. - - - - Invalid Source - Fuente inválida - - - - The selected source is invalid. - La fuente seleccionada es inválida. - - - - File Exists - El archivo ya existe - - - - File already exists. Do you want to replace it? - El archivo ya existe. ¿Deseas reemplazarlo? - - - - Failed to save file: - Error al guardar el archivo: - - - - Failed to download file: - Error al descargar el archivo: - - - - Cheats Not Found - Trucos no encontrados - - - - CheatsNotFound_MSG - No se encontraron trucos para este juego en esta versión del repositorio seleccionado,intenta con otro repositorio o con una versión diferente del juego. - - - - Cheats Downloaded Successfully - Trucos descargados exitosamente - - - - CheatsDownloadedSuccessfully_MSG - Has descargado exitosamente los trucos para esta versión del juego desde el repositorio seleccionado. Puedes intentar descargar desde otro repositorio; si está disponible, también será posible usarlo seleccionando el archivo de la lista. - - - - Failed to save: - Error al guardar: - - - - Failed to download: - Error al descargar: - - - - Download Complete - Descarga completa - - - - DownloadComplete_MSG - ¡Parches descargados exitosamente! Todos los parches disponibles para todos los juegos han sido descargados, no es necesario descargarlos individualmente para cada juego como ocurre con los trucos. Si el parche no aparece, puede ser que no exista para el número de serie y versión específicos del juego. - - - - Failed to parse JSON data from HTML. - Error al analizar los datos JSON del HTML. - - - - Failed to retrieve HTML page. - Error al recuperar la página HTML. - - - - The game is in version: %1 - El juego está en la versión: %1 - - - - The downloaded patch only works on version: %1 - El parche descargado solo funciona en la versión: %1 - - - - You may need to update your game. - Puede que necesites actualizar tu juego. - - - - Incompatibility Notice - Aviso de incompatibilidad - - - - Failed to open file: - Error al abrir el archivo: - - - - XML ERROR: - ERROR XML: - - - - Failed to open files.json for writing - Error al abrir files.json para escritura - - - - Author: - Autor: - - - - Directory does not exist: - El directorio no existe: - - - - Failed to open files.json for reading. - Error al abrir files.json para lectura. - - - - Name: - Nombre: - - - - Can't apply cheats before the game is started - No se pueden aplicar trucos antes de que se inicie el juego. - - - - SettingsDialog - - - Save - Guardar - - - Apply Aplicar - Restore Defaults Restaurar Valores Predeterminados - Close Cerrar - Point your mouse at an option to display its description. Coloque el mouse sobre una opción para mostrar su descripción. - consoleLanguageGroupBox Idioma de la Consola:\nEstablece el idioma que utiliza el juego de PS4.\nSe recomienda configurarlo a un idioma que el juego soporte, lo cual varía por región. - emulatorLanguageGroupBox Idioma del Emulador:\nConfigura el idioma de la interfaz de usuario del emulador. - fullscreenCheckBox Habilitar Pantalla Completa:\nColoca automáticamente la ventana del juego en modo de pantalla completa.\nEsto se puede alternar presionando la tecla F11. - separateUpdatesCheckBox Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. - showSplashCheckBox Mostrar Pantalla de Inicio:\nMuestra la pantalla de inicio del juego (una imagen especial) mientras el juego se está iniciando. - ps4proCheckBox Es PS4 Pro:\nHace que el emulador actúe como una PS4 PRO, lo que puede habilitar funciones especiales en los juegos que lo admitan. - discordRPCCheckbox Habilitar Discord Rich Presence:\nMuestra el ícono del emulador y la información relevante en tu perfil de Discord. - userName Nombre de Usuario:\nEstablece el nombre de usuario de la cuenta de PS4, que puede ser mostrado por algunos juegos. - TrophyKey Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. - logTypeGroupBox Tipo de Registro:\nEstablece si sincronizar la salida de la ventana de registro para mejorar el rendimiento. Puede tener efectos adversos en la emulación. - logFilter Filtro de Registro:\nFiltra el registro para imprimir solo información específica.\nEjemplos: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Niveles: Trace, Debug, Info, Warning, Error, Critical - en este orden, un nivel específico silencia todos los niveles anteriores en la lista y registra cada nivel posterior. - updaterGroupBox Actualización:\nRelease: Versiones oficiales lanzadas cada mes que pueden estar muy desactualizadas, pero son más confiables y están probadas.\nNightly: Versiones de desarrollo que tienen todas las últimas funciones y correcciones, pero pueden contener errores y son menos estables. - GUIgroupBox Reproducir Música del Título:\nSi un juego lo admite, habilita la reproducción de música especial al seleccionar el juego en la interfaz gráfica. - disableTrophycheckBox Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). - hideCursorGroupBox Ocultar Cursor:\nElija cuándo desaparecerá el cursor:\nNunca: Siempre verá el mouse.\nInactivo: Establezca un tiempo para que desaparezca después de estar inactivo.\nSiempre: nunca verá el mouse. - idleTimeoutGroupBox Establezca un tiempo para que el mouse desaparezca después de estar inactivo. - backButtonBehaviorGroupBox Comportamiento del Botón Atrás:\nEstablece el botón atrás del controlador para emular el toque en la posición especificada en el touchpad del PS4. - enableCompatibilityCheckBox Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. - checkCompatibilityOnStartupCheckBox Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. - updateCompatibilityButton Update Compatibility Database:\nImmediately update the compatibility database. - Never Nunca - Idle Inactivo - Always Siempre - Touchpad Left Touchpad Izquierda - Touchpad Right Touchpad Derecha - Touchpad Center Centro del Touchpad - None Ninguno - graphicsAdapterGroupBox Dispositivo Gráfico:\nEn sistemas con múltiples GPU, selecciona la GPU que el emulador utilizará de la lista desplegable,\o selecciona "Auto Select" para determinarla automáticamente. - resolutionLayout Anchura/Altura:\nEstablece el tamaño de la ventana del emulador al iniciar, que se puede redimensionar durante el juego.\nEsto es diferente de la resolución en el juego. - heightDivider Divisor de Vblank:\nLa tasa de cuadros a la que se refresca el emulador se multiplica por este número. Cambiar esto puede tener efectos adversos, como aumentar la velocidad del juego, o romper la funcionalidad crítica del juego que no espera que esto cambie. - dumpShadersCheckBox Habilitar la Volcadura de Sombras:\nPor el bien de la depuración técnica, guarda las sombras del juego en una carpeta mientras se renderizan. - nullGpuCheckBox Habilitar GPU Nula:\nPor el bien de la depuración técnica, desactiva el renderizado del juego como si no hubiera tarjeta gráfica. - gameFoldersBox Carpetas de Juegos:\nLa lista de carpetas para verificar los juegos instalados. - addFolderButton Añadir:\nAgregar una carpeta a la lista. - removeFolderButton Eliminar:\nEliminar una carpeta de la lista. - debugDump Habilitar la Volcadura de Depuración:\nGuarda los símbolos de importación y exportación y la información del encabezado del archivo del programa de PS4 que se está ejecutando actualmente en un directorio. - vkValidationCheckBox Habilitar Capas de Validación de Vulkan:\nHabilita un sistema que valida el estado del renderizador de Vulkan y registra información sobre su estado interno. Esto reducirá el rendimiento y probablemente cambiará el comportamiento de la emulación. - vkSyncValidationCheckBox Habilitar Validación de Sincronización de Vulkan:\nHabilita un sistema que valida el tiempo de las tareas de renderizado de Vulkan. Esto reducirá el rendimiento y probablemente cambiará el comportamiento de la emulación. - rdocCheckBox Habilitar Depuración de RenderDoc:\nSi se habilita, el emulador proporcionará compatibilidad con Renderdoc para permitir la captura y análisis del fotograma actualmente renderizado. + + CheatsPatches + + Cheats / Patches for + Cheats / Patches for + + + defaultTextEdit_MSG + Los cheats/patches son experimentales.\nÚselos con precaución.\n\nDescargue los cheats individualmente seleccionando el repositorio y haciendo clic en el botón de descarga.\nEn la pestaña Patches, puede descargar todos los patches a la vez, elegir cuáles desea usar y guardar la selección.\n\nComo no desarrollamos los Cheats/Patches,\npor favor informe los problemas al autor del cheat.\n\n¿Creaste un nuevo cheat? Visita:\nhttps://github.com/shadps4-emu/ps4_cheats + + + No Image Available + No hay imagen disponible + + + Serial: + Número de serie: + + + Version: + Versión: + + + Size: + Tamaño: + + + Select Cheat File: + Seleccionar archivo de trucos: + + + Repository: + Repositorio: + + + Download Cheats + Descargar trucos + + + Delete File + Eliminar archivo + + + No files selected. + No se han seleccionado archivos. + + + You can delete the cheats you don't want after downloading them. + Puedes eliminar los trucos que no quieras una vez descargados. + + + Do you want to delete the selected file?\n%1 + ¿Deseas eliminar el archivo seleccionado?\n%1 + + + Select Patch File: + Seleccionar archivo de parche: + + + Download Patches + Descargar parches + + + Save + Guardar + + + Cheats + Trucos + + + Patches + Parches + + + Error + Error + + + No patch selected. + No se ha seleccionado ningún parche. + + + Unable to open files.json for reading. + No se puede abrir files.json para lectura. + + + No patch file found for the current serial. + No se encontró ningún archivo de parche para el número de serie actual. + + + Unable to open the file for reading. + No se puede abrir el archivo para lectura. + + + Unable to open the file for writing. + No se puede abrir el archivo para escritura. + + + Failed to parse XML: + Error al analizar XML: + + + Success + Éxito + + + Options saved successfully. + Opciones guardadas exitosamente. + + + Invalid Source + Fuente inválida + + + The selected source is invalid. + La fuente seleccionada es inválida. + + + File Exists + El archivo ya existe + + + File already exists. Do you want to replace it? + El archivo ya existe. ¿Deseas reemplazarlo? + + + Failed to save file: + Error al guardar el archivo: + + + Failed to download file: + Error al descargar el archivo: + + + Cheats Not Found + Trucos no encontrados + + + CheatsNotFound_MSG + No se encontraron trucos para este juego en esta versión del repositorio seleccionado,intenta con otro repositorio o con una versión diferente del juego. + + + Cheats Downloaded Successfully + Trucos descargados exitosamente + + + CheatsDownloadedSuccessfully_MSG + Has descargado exitosamente los trucos para esta versión del juego desde el repositorio seleccionado. Puedes intentar descargar desde otro repositorio; si está disponible, también será posible usarlo seleccionando el archivo de la lista. + + + Failed to save: + Error al guardar: + + + Failed to download: + Error al descargar: + + + Download Complete + Descarga completa + + + DownloadComplete_MSG + ¡Parches descargados exitosamente! Todos los parches disponibles para todos los juegos han sido descargados, no es necesario descargarlos individualmente para cada juego como ocurre con los trucos. Si el parche no aparece, puede ser que no exista para el número de serie y versión específicos del juego. + + + Failed to parse JSON data from HTML. + Error al analizar los datos JSON del HTML. + + + Failed to retrieve HTML page. + Error al recuperar la página HTML. + + + The game is in version: %1 + El juego está en la versión: %1 + + + The downloaded patch only works on version: %1 + El parche descargado solo funciona en la versión: %1 + + + You may need to update your game. + Puede que necesites actualizar tu juego. + + + Incompatibility Notice + Aviso de incompatibilidad + + + Failed to open file: + Error al abrir el archivo: + + + XML ERROR: + ERROR XML: + + + Failed to open files.json for writing + Error al abrir files.json para escritura + + + Author: + Autor: + + + Directory does not exist: + El directorio no existe: + + + Failed to open files.json for reading. + Error al abrir files.json para lectura. + + + Name: + Nombre: + + + Can't apply cheats before the game is started + No se pueden aplicar trucos antes de que se inicie el juego. + + GameListFrame - Icon Icono - Name Nombre - Serial Numero de serie - Compatibility Compatibility - Region Región - Firmware Firmware - Size Tamaño - Version Versión - Path Ruta - Play Time Tiempo de Juego - Never Played Never Played - h h - m m - s s - Compatibility is untested Compatibility is untested - Game does not initialize properly / crashes the emulator Game does not initialize properly / crashes the emulator - Game boots, but only displays a blank screen Game boots, but only displays a blank screen - Game displays an image but does not go past the menu Game displays an image but does not go past the menu - Game has game-breaking glitches or unplayable performance Game has game-breaking glitches or unplayable performance - Game can be completed with playable performance and no major glitches Game can be completed with playable performance and no major glitches @@ -1508,127 +1210,102 @@ CheckUpdate - Auto Updater Actualizador Automático - Error Error - Network error: Error de red: - Failed to parse update information. Error al analizar la información de actualización. - No pre-releases found. No se encontraron prelanzamientos. - Invalid release data. Datos de versión no válidos. - No download URL found for the specified asset. No se encontró URL de descarga para el activo especificado. - Your version is already up to date! ¡Su versión ya está actualizada! - Update Available Actualización disponible - Update Channel Canal de Actualización - Current Version Versión actual - Latest Version Última versión - Do you want to update? ¿Quieres actualizar? - Show Changelog Mostrar registro de cambios - Check for Updates at Startup Buscar actualizaciones al iniciar - Update Actualizar - No No - Hide Changelog Ocultar registro de cambios - Changes Cambios - Network error occurred while trying to access the URL Se produjo un error de red al intentar acceder a la URL - Download Complete Descarga completa - The update has been downloaded, press OK to install. La actualización se ha descargado, presione Aceptar para instalar. - Failed to save the update file at No se pudo guardar el archivo de actualización en - Starting Update... Iniciando actualización... - Failed to create the update script file No se pudo crear el archivo del script de actualización @@ -1636,29 +1313,24 @@ GameListUtils - B B - KB KB - MB MB - GB GB - TB TB - + \ No newline at end of file diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index 7b93c6769..ee37cd22a 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -6,22 +6,18 @@ AboutDialog - About shadPS4 درباره ShadPS4 - shadPS4 ShadPS4 - shadPS4 is an experimental open-source emulator for the PlayStation 4. یک شبیه ساز متن باز برای پلی استیشن 4 است. - This software should not be used to play games you have not legally obtained. این برنامه نباید برای بازی هایی که شما به صورت غیرقانونی به دست آوردید استفاده شود. @@ -29,7 +25,6 @@ ElfViewer - Open Folder فولدر را بازکن @@ -37,17 +32,14 @@ GameInfoClass - Loading game list, please wait :3 درحال بارگیری لیست بازی ها,لطفا کمی صبرکنید :3 - Cancel لغو - Loading... ...درحال بارگیری @@ -55,12 +47,10 @@ InstallDirSelect - shadPS4 - Choose directory ShadPS4 - انتخاب محل نصب بازی - Select which directory you want to install to. محلی را که می‌خواهید در آن نصب شود، انتخاب کنید. @@ -68,27 +58,22 @@ GameInstallDialog - shadPS4 - Choose directory ShadPS4 - انتخاب محل نصب بازی - Directory to install games محل نصب بازی ها - Browse انتخاب دستی - Error ارور - The value for location to install games is not valid. .مکان داده شده برای نصب بازی درست نمی باشد @@ -96,167 +81,134 @@ GuiContextMenus - Create Shortcut ایجاد میانبر - Cheats / Patches چیت/پچ ها - SFO Viewer SFO مشاهده - Trophy Viewer مشاهده جوایز - Open Folder... باز کردن پوشه... - Open Game Folder باز کردن پوشه بازی - Open Save Data Folder پوشه ذخیره داده را باز کنید - Open Log Folder باز کردن پوشه لاگ - Copy info... ...کپی کردن اطلاعات - Copy Name کپی کردن نام - Copy Serial کپی کردن سریال - Copy All کپی کردن تمامی مقادیر - Delete... حذف... - Delete Game حذف بازی - Delete Update حذف به‌روزرسانی - Delete DLC حذف محتوای اضافی (DLC) - Compatibility... Compatibility... - Update database Update database - View report View report - Submit a report Submit a report - Shortcut creation ایجاد میانبر - Shortcut created successfully! میانبر با موفقیت ساخته شد! - Error ارور - Error creating shortcut! مشکلی در هنگام ساخت میانبر بوجود آمد! - Install PKG نصب PKG - Game بازی - requiresEnableSeparateUpdateFolder_MSG این قابلیت نیازمند فعال‌سازی گزینه تنظیمات «ایجاد پوشه جداگانه برای به‌روزرسانی» است. در صورت تمایل به استفاده از این قابلیت، لطفاً آن را فعال کنید. - This game has no update to delete! این بازی به‌روزرسانی‌ای برای حذف ندارد! - - + Update به‌روزرسانی - This game has no DLC to delete! این بازی محتوای اضافی (DLC) برای حذف ندارد! - DLC DLC - Delete %1 حذف %1 - Are you sure you want to delete %1's %2 directory? Are you sure you want to delete %1's %2 directory? @@ -264,205 +216,285 @@ MainWindow - Open/Add Elf Folder ELF بازکردن/ساختن پوشه - Install Packages (PKG) نصب بسته (PKG) - Boot Game اجرای بازی - Check for Updates به روز رسانی را بررسی کنید - About shadPS4 ShadPS4 درباره - Configure... ...تنظیمات - Install application from a .pkg file .PKG نصب بازی از فایل - Recent Games بازی های اخیر - Exit خروج - Exit shadPS4 ShadPS4 بستن - Exit the application. بستن برنامه - Show Game List نشان دادن بازی ها - Game List Refresh رفرش لیست بازی ها - Tiny کوچک ترین - Small کوچک - Medium متوسط - Large بزرگ - List View نمایش لیست - Grid View شبکه ای (چهارخونه) - Elf Viewer مشاهده گر Elf - Game Install Directory محل نصب بازی - Download Cheats/Patches دانلود چیت/پچ - Dump Game List استخراج لیست بازی ها - PKG Viewer PKG مشاهده گر - Search... جست و جو... - File فایل - View شخصی سازی - Game List Icons آیکون ها - Game List Mode حالت نمایش لیست بازی ها - Settings تنظیمات - Utils ابزارها - Themes تم ها - Help کمک - Dark تیره - Light روشن - Green سبز - Blue آبی - Violet بنفش - toolBar نوار ابزار + + Game List + لیست بازی + + + * Unsupported Vulkan Version + شما پشتیبانی نمیشود Vulkan ورژن * + + + Download Cheats For All Installed Games + دانلود چیت برای همه بازی ها + + + Download Patches For All Games + دانلود پچ برای همه بازی ها + + + Download Complete + دانلود کامل شد✅ + + + You have downloaded cheats for all the games you have installed. + چیت برای همه بازی های شما دانلودشد✅ + + + Patches Downloaded Successfully! + پچ ها با موفقیت دانلود شد✅ + + + All Patches available for all games have been downloaded. + ✅تمام پچ های موجود برای همه بازی های شما دانلود شد + + + Games: + بازی ها: + + + PKG File (*.PKG) + PKG فایل (*.PKG) + + + ELF files (*.bin *.elf *.oelf) + ELF فایل های (*.bin *.elf *.oelf) + + + Game Boot + اجرای بازی + + + Only one file can be selected! + فقط یک فایل انتخاب کنید! + + + PKG Extraction + PKG استخراج فایل + + + Patch detected! + پچ شناسایی شد! + + + PKG and Game versions match: + و نسخه بازی همخوانی دارد PKG فایل: + + + Would you like to overwrite? + آیا مایل به جایگزینی فایل هستید؟ + + + PKG Version %1 is older than installed version: + نسخه فایل PKG %1 قدیمی تر از نسخه نصب شده است: + + + Game is installed: + بازی نصب شد: + + + Would you like to install Patch: + آیا مایل به نصب پچ هستید: + + + DLC Installation + نصب DLC + + + Would you like to install DLC: %1? + آیا مایل به نصب DLC هستید: %1 + + + DLC already installed: + قبلا نصب شده DLC این: + + + Game already installed + این بازی قبلا نصب شده + + + PKG is a patch, please install the game first! + فایل انتخاب شده یک پچ است, لطفا اول بازی را نصب کنید + + + PKG ERROR + PKG ارور فایل + + + Extracting PKG %1/%2 + درحال استخراج PKG %1/%2 + + + Extraction Finished + استخراج به پایان رسید + + + Game successfully installed at %1 + بازی با موفقیت در %1 نصب شد + + + File doesn't appear to be a valid PKG file + این فایل یک PKG درست به نظر نمی آید + PKGViewer - Open Folder بازکردن پوشه @@ -470,7 +502,6 @@ TrophyViewer - Trophy Viewer مشاهده جوایز @@ -478,1029 +509,700 @@ SettingsDialog - Settings تنظیمات - General عمومی - System سیستم - Console Language زبان کنسول - Emulator Language زبان شبیه ساز - Emulator شبیه ساز - Enable Fullscreen تمام صفحه - Enable Separate Update Folder فعال‌سازی پوشه جداگانه برای به‌روزرسانی - Show Splash Splash نمایش - Is PS4 Pro PS4 Pro حالت - Enable Discord Rich Presence Discord Rich Presence را فعال کنید - Username نام کاربری - Trophy Key Trophy Key - Trophy Trophy - Logger Logger - Log Type Log نوع - Log Filter Log فیلتر - Input ورودی - Cursor نشانگر - Hide Cursor پنهان کردن نشانگر - Hide Cursor Idle Timeout مخفی کردن زمان توقف مکان نما - s s - Controller دسته بازی - Back Button Behavior رفتار دکمه بازگشت - Graphics گرافیک - Graphics Device کارت گرافیک مورداستفاده - Width عرض - Height طول - Vblank Divider تقسیم‌کننده Vblank - Advanced ...بیشتر - Enable Shaders Dumping فعال‌سازی ذخیره‌سازی شیدرها - Enable NULL GPU NULL GPU فعال کردن - Paths مسیرها - Game Folders پوشه های بازی - Add... افزودن... - Remove حذف - Debug دیباگ - Enable Debug Dumping Debug Dumping - Enable Vulkan Validation Layers Vulkan Validation Layers - Enable Vulkan Synchronization Validation Vulkan Synchronization Validation - Enable RenderDoc Debugging RenderDoc Debugging - Update به‌روزرسانی - Check for Updates at Startup بررسی به‌روزرسانی‌ها در زمان راه‌اندازی - Update Channel کانال به‌روزرسانی - Check for Updates بررسی به‌روزرسانی‌ها - GUI Settings تنظیمات رابط کاربری - Disable Trophy Pop-ups غیرفعال کردن نمایش جوایز - Play title music پخش موسیقی عنوان - Update Compatibility Database On Startup به‌روزرسانی پایگاه داده سازگاری هنگام راه‌اندازی - Game Compatibility سازگاری بازی با سیستم - Display Compatibility Data نمایش داده‌های سازگاری - Update Compatibility Database به‌روزرسانی پایگاه داده سازگاری - Volume صدا - Audio Backend Audio Backend - - - MainWindow - - Game List - لیست بازی - - - - * Unsupported Vulkan Version - شما پشتیبانی نمیشود Vulkan ورژن * - - - - Download Cheats For All Installed Games - دانلود چیت برای همه بازی ها - - - - Download Patches For All Games - دانلود پچ برای همه بازی ها - - - - Download Complete - دانلود کامل شد✅ - - - - You have downloaded cheats for all the games you have installed. - چیت برای همه بازی های شما دانلودشد✅ - - - - Patches Downloaded Successfully! - پچ ها با موفقیت دانلود شد✅ - - - - All Patches available for all games have been downloaded. - ✅تمام پچ های موجود برای همه بازی های شما دانلود شد - - - - Games: - بازی ها: - - - - PKG File (*.PKG) - PKG فایل (*.PKG) - - - - ELF files (*.bin *.elf *.oelf) - ELF فایل های (*.bin *.elf *.oelf) - - - - Game Boot - اجرای بازی - - - - Only one file can be selected! - فقط یک فایل انتخاب کنید! - - - - PKG Extraction - PKG استخراج فایل - - - - Patch detected! - پچ شناسایی شد! - - - - PKG and Game versions match: - و نسخه بازی همخوانی دارد PKG فایل: - - - - Would you like to overwrite? - آیا مایل به جایگزینی فایل هستید؟ - - - - PKG Version %1 is older than installed version: - نسخه فایل PKG %1 قدیمی تر از نسخه نصب شده است: - - - - Game is installed: - بازی نصب شد: - - - - Would you like to install Patch: - آیا مایل به نصب پچ هستید: - - - - DLC Installation - نصب DLC - - - - Would you like to install DLC: %1? - آیا مایل به نصب DLC هستید: %1 - - - - DLC already installed: - قبلا نصب شده DLC این: - - - - Game already installed - این بازی قبلا نصب شده - - - - PKG is a patch, please install the game first! - فایل انتخاب شده یک پچ است, لطفا اول بازی را نصب کنید - - - - PKG ERROR - PKG ارور فایل - - - - Extracting PKG %1/%2 - درحال استخراج PKG %1/%2 - - - - Extraction Finished - استخراج به پایان رسید - - - - Game successfully installed at %1 - بازی با موفقیت در %1 نصب شد - - - - File doesn't appear to be a valid PKG file - این فایل یک PKG درست به نظر نمی آید - - - - CheatsPatches - - - Cheats / Patches for - چیت / پچ برای - - - - defaultTextEdit_MSG - defaultTextEdit_MSG - - - - No Image Available - تصویری موجود نمی باشد - - - - Serial: - سریال: - - - - Version: - نسخه: - - - - Size: - حجم: - - - - Select Cheat File: - فایل چیت را انتخاب کنید: - - - - Repository: - :منبع - - - - Download Cheats - دانلود چیت ها - - - - Delete File - حذف فایل - - - - No files selected. - فایلی انتخاب نشده. - - - - You can delete the cheats you don't want after downloading them. - شما میتوانید بعد از دانلود چیت هایی که نمیخواهید را پاک کنید - - - - Do you want to delete the selected file?\n%1 - آیا میخواهید فایل های انتخاب شده را پاک کنید؟ \n%1 - - - - Select Patch File: - فایل پچ را انتخاب کنید - - - - Download Patches - دانلود کردن پچ ها - - - Save ذخیره - - Cheats - چیت ها - - - - Patches - پچ ها - - - - Error - ارور - - - - No patch selected. - هیچ پچ انتخاب نشده - - - - Unable to open files.json for reading. - .json مشکل در خواندن فایل - - - - No patch file found for the current serial. - هیچ فایل پچ برای سریال بازی شما پیدا نشد. - - - - Unable to open the file for reading. - خطا در خواندن فایل - - - - Unable to open the file for writing. - خطا در نوشتن فایل - - - - Failed to parse XML: - انجام نشد XML تجزیه فایل: - - - - Success - عملیات موفق بود - - - - Options saved successfully. - تغییرات با موفقیت ذخیره شد✅ - - - - Invalid Source - منبع نامعتبر❌ - - - - The selected source is invalid. - منبع انتخاب شده نامعتبر است - - - - File Exists - فایل وجود دارد - - - - File already exists. Do you want to replace it? - فایل از قبل وجود دارد. آیا می خواهید آن را جایگزین کنید؟ - - - - Failed to save file: - ذخیره فایل موفقیت آمیز نبود: - - - - Failed to download file: - خطا در دانلود فایل: - - - - Cheats Not Found - چیت یافت نشد - - - - CheatsNotFound_MSG - متاسفانه هیچ چیتی از منبع انتخاب شده پیدا نشد! شما میتوانید منابع دیگری را برای دانلود انتخاب و یا چیت های خود را به صورت دستی واردکنید. - - - - Cheats Downloaded Successfully - دانلود چیت ها موفقیت آمیز بود✅ - - - - CheatsDownloadedSuccessfully_MSG - تمامی چیت های موجود برای این بازی از منبع انتخاب شده دانلود شد! شما همچنان میتوانید چیت های دیگری را ازمنابع مختلف دانلود کنید و درصورت موجود بودن از آنها استفاده کنید. - - - - Failed to save: - خطا در ذخیره اطلاعات: - - - - Failed to download: - خطا در دانلود❌ - - - - Download Complete - دانلود کامل شد - - - - DownloadComplete_MSG - پچ ها با موفقیت بارگیری شدند! تمام وصله های موجود برای همه بازی ها دانلود شده اند، نیازی به دانلود جداگانه آنها برای هر بازی نیست، همانطور که در Cheats اتفاق می افتد. اگر پچ ظاهر نشد، ممکن است برای سریال و نسخه خاصی از بازی وجود نداشته باشد. - - - - Failed to parse JSON data from HTML. - HTML از JSON خطا در تجزیه اطلاعات. - - - - Failed to retrieve HTML page. - HTML خطا دربازیابی صفحه - - - - The game is in version: %1 - بازی در نسخه: %1 است - - - - The downloaded patch only works on version: %1 - وصله دانلود شده فقط در نسخه: %1 کار می کند - - - - You may need to update your game. - شاید لازم باشد بازی خود را به روز کنید. - - - - Incompatibility Notice - اطلاعیه عدم سازگاری - - - - Failed to open file: - خطا در اجرای فایل: - - - - XML ERROR: - XML ERROR: - - - - Failed to open files.json for writing - .json خطا در نوشتن فایل - - - - Author: - تولید کننده: - - - - Directory does not exist: - پوشه وجود ندارد: - - - - Failed to open files.json for reading. - .json خطا در خواندن فایل - - - - Name: - نام: - - - - Can't apply cheats before the game is started - قبل از شروع بازی نمی توانید تقلب ها را اعمال کنید. - - - - SettingsDialog - - - Save - ذخیره - - - Apply اعمال - Restore Defaults بازیابی پیش فرض ها - Close بستن - Point your mouse at an option to display its description. ماوس خود را بر روی یک گزینه قرار دهید تا توضیحات آن نمایش داده شود. - consoleLanguageGroupBox Console Language:\nSets the language that the PS4 game uses.\nIt's recommended to set this to a language the game supports, which will vary by region. - emulatorLanguageGroupBox زبان شبیه‌ساز:\nزبان رابط کاربری شبیه‌ساز را انتخاب می‌کند. - fullscreenCheckBox فعال‌سازی تمام صفحه:\nپنجره بازی را به‌طور خودکار به حالت تمام صفحه در می‌آورد.\nبرای تغییر این حالت می‌توانید کلید F11 را فشار دهید. - separateUpdatesCheckBox فعال‌سازی پوشه جداگانه برای به‌روزرسانی:\nامکان نصب به‌روزرسانی‌های بازی در یک پوشه جداگانه برای مدیریت راحت‌تر را فراهم می‌کند. - showSplashCheckBox نمایش صفحه شروع:\nصفحه شروع بازی (تصویری ویژه) را هنگام بارگذاری بازی نمایش می‌دهد. - ps4proCheckBox حالت PS4 Pro:\nشبیه‌ساز را به‌عنوان PS4 Pro شبیه‌سازی می‌کند که ممکن است ویژگی‌های ویژه‌ای را در بازی‌های پشتیبانی‌شده فعال کند. - discordRPCCheckbox فعال کردن Discord Rich Presence:\nآیکون شبیه ساز و اطلاعات مربوطه را در نمایه Discord شما نمایش می دهد. - userName نام کاربری:\nنام کاربری حساب PS4 را تنظیم می‌کند که ممکن است توسط برخی بازی‌ها نمایش داده شود. - TrophyKey Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. - logTypeGroupBox نوع لاگ:\nتنظیم می‌کند که آیا خروجی پنجره لاگ برای بهبود عملکرد همگام‌سازی شود یا خیر. این ممکن است تأثیر منفی بر شبیه‌سازی داشته باشد. - logFilter Log Filter:\nFilters the log to only print specific information.\nExamples: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Levels: Trace, Debug, Info, Warning, Error, Critical - in this order, a specific level silences all levels preceding it in the list and logs every level after it. - updaterGroupBox به‌روزرسانی:\nانتشار: نسخه‌های رسمی که هر ماه منتشر می‌شوند و ممکن است بسیار قدیمی باشند، اما پایدارتر و تست‌ شده‌تر هستند.\nشبانه: نسخه‌های توسعه‌ای که شامل جدیدترین ویژگی‌ها و اصلاحات هستند، اما ممکن است دارای اشکال باشند و کمتر پایدار باشند. - GUIgroupBox پخش موسیقی عنوان:\nIدر صورتی که بازی از آن پشتیبانی کند، پخش موسیقی ویژه هنگام انتخاب بازی در رابط کاربری را فعال می‌کند. - disableTrophycheckBox غیرفعال کردن نمایش جوایز:\nنمایش اعلان‌های جوایز درون بازی را غیرفعال می‌کند. پیشرفت جوایز همچنان از طریق نمایشگر جوایز (کلیک راست روی بازی در پنجره اصلی) قابل پیگیری است.. - hideCursorGroupBox پنهان کردن نشانگر:\nانتخاب کنید که نشانگر چه زمانی ناپدید شود:\nهرگز: شما همیشه ماوس را خواهید دید.\nغیرفعال: زمانی را برای ناپدید شدن بعد از غیرفعالی تعیین کنید.\nهمیشه: شما هرگز ماوس را نخواهید دید. - idleTimeoutGroupBox زمانی را برای ناپدید شدن ماوس بعد از غیرفعالی تعیین کنید. - backButtonBehaviorGroupBox رفتار دکمه برگشت:\nدکمه برگشت کنترلر را طوری تنظیم می کند که ضربه زدن روی موقعیت مشخص شده روی صفحه لمسی PS4 را شبیه سازی کند. - enableCompatibilityCheckBox نمایش داده‌های سازگاری:\nاطلاعات سازگاری بازی را به صورت جدول نمایش می‌دهد. برای دریافت اطلاعات به‌روز، گزینه "به‌روزرسانی سازگاری هنگام راه‌اندازی" را فعال کنید. - checkCompatibilityOnStartupCheckBox به‌روزرسانی سازگاری هنگام راه‌اندازی:\nبه‌طور خودکار پایگاه داده سازگاری را هنگام راه‌اندازی ShadPS4 به‌روزرسانی می‌کند. - updateCompatibilityButton به‌روزرسانی پایگاه داده سازگاری:\nپایگاه داده سازگاری را بلافاصله به‌روزرسانی می‌کند. - Never هرگز - Idle بیکار - Always همیشه - Touchpad Left صفحه لمسی سمت چپ - Touchpad Right صفحه لمسی سمت راست - Touchpad Center مرکز صفحه لمسی - None هیچ کدام - graphicsAdapterGroupBox دستگاه گرافیکی:\nدر سیستم‌های با چندین پردازنده گرافیکی، از فهرست کشویی، پردازنده گرافیکی که شبیه‌ساز از آن استفاده می‌کند را انتخاب کنید، یا گزینه "انتخاب خودکار" را انتخاب کنید تا به طور خودکار تعیین شود. - resolutionLayout عرض/ارتفاع:\nاندازه پنجره شبیه‌ساز را در هنگام راه‌اندازی تنظیم می‌کند، که در حین بازی قابل تغییر اندازه است.\nاین با وضوح داخل بازی متفاوت است. - heightDivider تقسیم‌کننده Vblank:\nمیزان فریم ریت که شبیه‌ساز با آن به‌روزرسانی می‌شود، در این عدد ضرب می‌شود. تغییر این مقدار ممکن است تأثیرات منفی داشته باشد، مانند افزایش سرعت بازی یا خراب شدن عملکردهای حیاتی بازی که انتظار تغییر آن را ندارند! - dumpShadersCheckBox فعال‌سازی ذخیره‌سازی شیدرها:\nبه‌منظور اشکال‌زدایی فنی، شیدرهای بازی را هنگام رندر شدن در یک پوشه ذخیره می‌کند. - nullGpuCheckBox Enable Null GPU:\nFor the sake of technical debugging, disables game rendering as if there were no graphics card. - gameFoldersBox پوشه های بازی:\nلیست پوشه هایی که باید بازی های نصب شده را بررسی کنید. - addFolderButton اضافه کردن:\nیک پوشه به لیست اضافه کنید. - removeFolderButton حذف:\nیک پوشه را از لیست حذف کنید. - debugDump فعال‌سازی ذخیره‌سازی دیباگ:\nنمادهای import و export و اطلاعات هدر فایل برنامه در حال اجرای PS4 را در یک پوشه ذخیره می‌کند. - vkValidationCheckBox Enable Vulkan Validation Layers:\nEnables a system that validates the state of the Vulkan renderer and logs information about its internal state. This will reduce performance and likely change the behavior of emulation. - vkSyncValidationCheckBox Enable Vulkan Synchronization Validation:\nEnables a system that validates the timing of Vulkan rendering tasks. This will reduce performance and likely change the behavior of emulation. - rdocCheckBox Enable RenderDoc Debugging:\nIf enabled, the emulator will provide compatibility with Renderdoc to allow capture and analysis of the currently rendered frame. + + CheatsPatches + + Cheats / Patches for + چیت / پچ برای + + + defaultTextEdit_MSG + defaultTextEdit_MSG + + + No Image Available + تصویری موجود نمی باشد + + + Serial: + سریال: + + + Version: + نسخه: + + + Size: + حجم: + + + Select Cheat File: + فایل چیت را انتخاب کنید: + + + Repository: + :منبع + + + Download Cheats + دانلود چیت ها + + + Delete File + حذف فایل + + + No files selected. + فایلی انتخاب نشده. + + + You can delete the cheats you don't want after downloading them. + شما میتوانید بعد از دانلود چیت هایی که نمیخواهید را پاک کنید + + + Do you want to delete the selected file?\n%1 + آیا میخواهید فایل های انتخاب شده را پاک کنید؟ \n%1 + + + Select Patch File: + فایل پچ را انتخاب کنید + + + Download Patches + دانلود کردن پچ ها + + + Save + ذخیره + + + Cheats + چیت ها + + + Patches + پچ ها + + + Error + ارور + + + No patch selected. + هیچ پچ انتخاب نشده + + + Unable to open files.json for reading. + .json مشکل در خواندن فایل + + + No patch file found for the current serial. + هیچ فایل پچ برای سریال بازی شما پیدا نشد. + + + Unable to open the file for reading. + خطا در خواندن فایل + + + Unable to open the file for writing. + خطا در نوشتن فایل + + + Failed to parse XML: + انجام نشد XML تجزیه فایل: + + + Success + عملیات موفق بود + + + Options saved successfully. + تغییرات با موفقیت ذخیره شد✅ + + + Invalid Source + منبع نامعتبر❌ + + + The selected source is invalid. + منبع انتخاب شده نامعتبر است + + + File Exists + فایل وجود دارد + + + File already exists. Do you want to replace it? + فایل از قبل وجود دارد. آیا می خواهید آن را جایگزین کنید؟ + + + Failed to save file: + ذخیره فایل موفقیت آمیز نبود: + + + Failed to download file: + خطا در دانلود فایل: + + + Cheats Not Found + چیت یافت نشد + + + CheatsNotFound_MSG + متاسفانه هیچ چیتی از منبع انتخاب شده پیدا نشد! شما میتوانید منابع دیگری را برای دانلود انتخاب و یا چیت های خود را به صورت دستی واردکنید. + + + Cheats Downloaded Successfully + دانلود چیت ها موفقیت آمیز بود✅ + + + CheatsDownloadedSuccessfully_MSG + تمامی چیت های موجود برای این بازی از منبع انتخاب شده دانلود شد! شما همچنان میتوانید چیت های دیگری را ازمنابع مختلف دانلود کنید و درصورت موجود بودن از آنها استفاده کنید. + + + Failed to save: + خطا در ذخیره اطلاعات: + + + Failed to download: + خطا در دانلود❌ + + + Download Complete + دانلود کامل شد + + + DownloadComplete_MSG + پچ ها با موفقیت بارگیری شدند! تمام وصله های موجود برای همه بازی ها دانلود شده اند، نیازی به دانلود جداگانه آنها برای هر بازی نیست، همانطور که در Cheats اتفاق می افتد. اگر پچ ظاهر نشد، ممکن است برای سریال و نسخه خاصی از بازی وجود نداشته باشد. + + + Failed to parse JSON data from HTML. + HTML از JSON خطا در تجزیه اطلاعات. + + + Failed to retrieve HTML page. + HTML خطا دربازیابی صفحه + + + The game is in version: %1 + بازی در نسخه: %1 است + + + The downloaded patch only works on version: %1 + وصله دانلود شده فقط در نسخه: %1 کار می کند + + + You may need to update your game. + شاید لازم باشد بازی خود را به روز کنید. + + + Incompatibility Notice + اطلاعیه عدم سازگاری + + + Failed to open file: + خطا در اجرای فایل: + + + XML ERROR: + XML ERROR: + + + Failed to open files.json for writing + .json خطا در نوشتن فایل + + + Author: + تولید کننده: + + + Directory does not exist: + پوشه وجود ندارد: + + + Failed to open files.json for reading. + .json خطا در خواندن فایل + + + Name: + نام: + + + Can't apply cheats before the game is started + قبل از شروع بازی نمی توانید تقلب ها را اعمال کنید. + + GameListFrame - Icon آیکون - Name نام - Serial سریال - Compatibility سازگاری - Region منطقه - Firmware فریم‌ور - Size اندازه - Version نسخه - Path مسیر - Play Time زمان بازی - Never Played هرگز بازی نشده - h h - m m - s s - Compatibility is untested سازگاری تست نشده است - Game does not initialize properly / crashes the emulator بازی به درستی راه‌اندازی نمی‌شود / شبیه‌ساز کرش می‌کند - Game boots, but only displays a blank screen بازی اجرا می‌شود، اما فقط یک صفحه خالی نمایش داده می‌شود - Game displays an image but does not go past the menu بازی تصویری نمایش می‌دهد، اما از منو فراتر نمی‌رود - Game has game-breaking glitches or unplayable performance بازی دارای اشکالات بحرانی یا عملکرد غیرقابل بازی است - Game can be completed with playable performance and no major glitches بازی با عملکرد قابل قبول و بدون اشکالات عمده قابل بازی است. @@ -1508,127 +1210,102 @@ CheckUpdate - Auto Updater به‌روزرسانی خودکار - Error خطا - Network error: خطای شبکه: - Failed to parse update information. خطا در تجزیه اطلاعات بهروزرسانی. - No pre-releases found. هیچ پیش انتشاری یافت نشد. - Invalid release data. داده های نسخه نامعتبر است. - No download URL found for the specified asset. هیچ URL دانلودی برای دارایی مشخص شده پیدا نشد. - Your version is already up to date! نسخه شما اکنون به روز شده است! - Update Available به روز رسانی موجود است - Update Channel کانال به‌روزرسانی - Current Version نسخه فعلی - Latest Version جدیدترین نسخه - Do you want to update? آیا می خواهید به روز رسانی کنید؟ - Show Changelog نمایش تغییرات - Check for Updates at Startup بررسی به‌روزرسانی هنگام شروع - Update به روز رسانی - No خیر - Hide Changelog مخفی کردن تغییرات - Changes تغییرات - Network error occurred while trying to access the URL در حین تلاش برای دسترسی به URL خطای شبکه رخ داد - Download Complete دانلود کامل شد - The update has been downloaded, press OK to install. به روز رسانی دانلود شده است، برای نصب OK را فشار دهید. - Failed to save the update file at فایل به روز رسانی ذخیره نشد - Starting Update... شروع به روز رسانی... - Failed to create the update script file فایل اسکریپت به روز رسانی ایجاد نشد @@ -1636,29 +1313,24 @@ GameListUtils - B B - KB KB - MB MB - GB GB - TB TB - + - + \ No newline at end of file diff --git a/src/qt_gui/translations/fi.ts b/src/qt_gui/translations/fi.ts index 99c1de67e..47c38fc46 100644 --- a/src/qt_gui/translations/fi.ts +++ b/src/qt_gui/translations/fi.ts @@ -6,22 +6,18 @@ AboutDialog - About shadPS4 Tietoa shadPS4:sta - shadPS4 shadPS4 - shadPS4 is an experimental open-source emulator for the PlayStation 4. shadPS4 on kokeellinen avoimen lähdekoodin PlayStation 4 emulaattori. - This software should not be used to play games you have not legally obtained. Tätä ohjelmistoa ei saa käyttää pelien pelaamiseen, joita et ole hankkinut laillisesti. @@ -29,7 +25,6 @@ ElfViewer - Open Folder Avaa Hakemisto @@ -37,17 +32,14 @@ GameInfoClass - Loading game list, please wait :3 Ole hyvä ja odota, ladataan pelilistaa :3 - Cancel Peruuta - Loading... Ladataan... @@ -55,12 +47,10 @@ InstallDirSelect - shadPS4 - Choose directory shadPS4 - Valitse hakemisto - Select which directory you want to install to. Valitse, mihin hakemistoon haluat asentaa. @@ -68,27 +58,22 @@ GameInstallDialog - shadPS4 - Choose directory shadPS4 - Valitse hakemisto - Directory to install games Pelien asennushakemisto - Browse Selaa - Error Virhe - The value for location to install games is not valid. Peliasennushakemiston sijainti on virheellinen. @@ -96,167 +81,134 @@ GuiContextMenus - Create Shortcut Luo Pikakuvake - Cheats / Patches Huijaukset / Korjaukset - SFO Viewer SFO Selain - Trophy Viewer Trophy Selain - Open Folder... Avaa Hakemisto... - Open Game Folder Avaa Pelihakemisto - Open Save Data Folder Avaa Tallennustiedostohakemisto - Open Log Folder Avaa Lokihakemisto - Copy info... Kopioi tietoja... - Copy Name Kopioi Nimi - Copy Serial Kopioi Sarjanumero - Copy All Kopioi kaikki - Delete... Poista... - Delete Game Poista Peli - Delete Update Poista Päivitys - Delete DLC Poista Lisäsisältö - Compatibility... Yhteensopivuus... - Update database Päivitä tietokanta - View report Näytä raportti - Submit a report Tee raportti - Shortcut creation Pikakuvakkeen luonti - Shortcut created successfully! Pikakuvake luotu onnistuneesti! - Error Virhe - Error creating shortcut! Virhe pikakuvakkeen luonnissa! - Install PKG Asenna PKG - Game Peli - requiresEnableSeparateUpdateFolder_MSG Tämä ominaisuus vaatii, että 'Ota käyttöön erillinen päivityshakemisto' -asetus on päällä. Jos haluat käyttää tätä ominaisuutta, laita se asetus päälle. - This game has no update to delete! Tällä pelillä ei ole poistettavaa päivitystä! - - + Update Päivitä - This game has no DLC to delete! Tällä pelillä ei ole poistettavaa lisäsisältöä! - DLC Lisäsisältö - Delete %1 Poista %1 - Are you sure you want to delete %1's %2 directory? Haluatko varmasti poistaa %1n %2hakemiston? @@ -264,205 +216,285 @@ MainWindow - Open/Add Elf Folder Avaa/Lisää Elf Hakemisto - Install Packages (PKG) Asenna Paketteja (PKG) - Boot Game Käynnistä Peli - Check for Updates Tarkista Päivitykset - About shadPS4 Tietoa shadPS4:sta - Configure... Asetukset... - Install application from a .pkg file Asenna sovellus .pkg tiedostosta - Recent Games Viimeisimmät Pelit - Exit Sulje - Exit shadPS4 Sulje shadPS4 - Exit the application. Sulje sovellus. - Show Game List Avaa pelilista - Game List Refresh Päivitä pelilista - Tiny Hyvin pieni - Small Pieni - Medium Keskikokoinen - Large Suuri - List View Listanäkymä - Grid View Ruudukkonäkymä - Elf Viewer Elf Selain - Game Install Directory Peliasennushakemisto - Download Cheats/Patches Lataa Huijaukset / Korjaukset - Dump Game List Kirjoita Pelilista Tiedostoon - PKG Viewer PKG Selain - Search... Hae... - File Tiedosto - View Näkymä - Game List Icons Pelilistan Ikonit - Game List Mode Pelilistamuoto - Settings Asetukset - Utils Työkalut - Themes Teemat - Help Apua - Dark Tumma - Light Vaalea - Green Vihreä - Blue Sininen - Violet Violetti - toolBar Työkalupalkki + + Game List + Pelilista + + + * Unsupported Vulkan Version + * Ei Tuettu Vulkan-versio + + + Download Cheats For All Installed Games + Lataa Huijaukset Kaikille Asennetuille Peleille + + + Download Patches For All Games + Lataa Paikkaukset Kaikille Peleille + + + Download Complete + Lataus Valmis + + + You have downloaded cheats for all the games you have installed. + Olet ladannut huijaukset kaikkiin asennettuihin peleihin. + + + Patches Downloaded Successfully! + Paikkaukset Ladattu Onnistuneesti! + + + All Patches available for all games have been downloaded. + Kaikki saatavilla olevat Paikkaukset kaikille peleille on ladattu. + + + Games: + Pelit: + + + PKG File (*.PKG) + PKG-tiedosto (*.PKG) + + + ELF files (*.bin *.elf *.oelf) + ELF-tiedostot (*.bin *.elf *.oelf) + + + Game Boot + Pelin Käynnistys + + + Only one file can be selected! + Vain yksi tiedosto voi olla valittuna! + + + PKG Extraction + PKG:n purku + + + Patch detected! + Päivitys havaittu! + + + PKG and Game versions match: + PKG- ja peliversiot vastaavat: + + + Would you like to overwrite? + Haluatko korvata? + + + PKG Version %1 is older than installed version: + PKG-versio %1 on vanhempi kuin asennettu versio: + + + Game is installed: + Peli on asennettu: + + + Would you like to install Patch: + Haluatko asentaa päivityksen: + + + DLC Installation + Lisäsisällön asennus + + + Would you like to install DLC: %1? + Haluatko asentaa lisäsisällön: %1? + + + DLC already installed: + Lisäsisältö on jo asennettu: + + + Game already installed + Peli on jo asennettu + + + PKG is a patch, please install the game first! + PKG on päivitys, asenna peli ensin! + + + PKG ERROR + PKG VIRHE + + + Extracting PKG %1/%2 + Purkaminen PKG %1/%2 + + + Extraction Finished + Purku valmis + + + Game successfully installed at %1 + Peli asennettu onnistuneesti kohtaan %1 + + + File doesn't appear to be a valid PKG file + Tiedosto ei vaikuta olevan kelvollinen PKG-tiedosto + PKGViewer - Open Folder Avaa Hakemisto @@ -470,7 +502,6 @@ TrophyViewer - Trophy Viewer Trophy Selain @@ -478,1029 +509,700 @@ SettingsDialog - Settings Asetukset - General Yleinen - System Järjestelmä - Console Language Konsolin Kieli - Emulator Language Emulaattorin Kieli - Emulator Emulaattori - Enable Fullscreen Ota Käyttöön Koko Ruudun Tila - Enable Separate Update Folder Ota Käyttöön Erillinen Päivityshakemisto - Show Splash Näytä Aloitusnäyttö - Is PS4 Pro On PS4 Pro - Enable Discord Rich Presence Ota käyttöön Discord Rich Presence - Username Käyttäjänimi - Trophy Key Trophy Avain - Trophy Trophy - Logger Lokinkerääjä - Log Type Lokin Tyyppi - Log Filter Lokisuodatin - Input Syöttö - Cursor Kursori - Hide Cursor Piilota Kursori - Hide Cursor Idle Timeout Inaktiivisuuden Aikaraja Kursorin Piilottamiseen - s s - Controller Ohjain - Back Button Behavior Takaisin-painikkeen Käyttäytyminen - Graphics Grafiikka - Graphics Device Näytönohjain - Width Leveys - Height Korkeus - Vblank Divider Vblank jakaja - Advanced Lisäasetukset - Enable Shaders Dumping Ota Käyttöön Varjostinvedokset - Enable NULL GPU Ota Käyttöön NULL GPU - Paths Polut - Game Folders Pelihakemistot - Add... - Lisää... + Lisää... - Remove Poista - Debug Virheenkorjaus - Enable Debug Dumping Ota Käyttöön Virheenkorjausvedokset - Enable Vulkan Validation Layers Ota Käyttöön Vulkan-validointikerrokset - Enable Vulkan Synchronization Validation Ota Käyttöön Vulkan-synkronointivalidointi - Enable RenderDoc Debugging Ota Käyttöön RenderDoc Virheenkorjaus - Update Päivitys - Check for Updates at Startup Tarkista Päivitykset Käynnistäessä - Update Channel Päivityskanava - Check for Updates Tarkista Päivitykset - GUI Settings GUI-asetukset - Disable Trophy Pop-ups Poista Trophy Pop-upit Käytöstä - Play title music Soita Otsikkomusiikkia - Update Compatibility Database On Startup Päivitä Yhteensopivuustietokanta Käynnistäessä - Game Compatibility Peliyhteensopivuus - Display Compatibility Data Näytä Yhteensopivuustiedot - Update Compatibility Database Päivitä Yhteensopivuustietokanta - Volume Äänenvoimakkuus - Audio Backend Äänijärjestelmä - - - MainWindow - - Game List - Pelilista - - - - * Unsupported Vulkan Version - * Ei Tuettu Vulkan-versio - - - - Download Cheats For All Installed Games - Lataa Huijaukset Kaikille Asennetuille Peleille - - - - Download Patches For All Games - Lataa Paikkaukset Kaikille Peleille - - - - Download Complete - Lataus Valmis - - - - You have downloaded cheats for all the games you have installed. - Olet ladannut huijaukset kaikkiin asennettuihin peleihin. - - - - Patches Downloaded Successfully! - Paikkaukset Ladattu Onnistuneesti! - - - - All Patches available for all games have been downloaded. - Kaikki saatavilla olevat Paikkaukset kaikille peleille on ladattu. - - - - Games: - Pelit: - - - - PKG File (*.PKG) - PKG-tiedosto (*.PKG) - - - - ELF files (*.bin *.elf *.oelf) - ELF-tiedostot (*.bin *.elf *.oelf) - - - - Game Boot - Pelin Käynnistys - - - - Only one file can be selected! - Vain yksi tiedosto voi olla valittuna! - - - - PKG Extraction - PKG:n purku - - - - Patch detected! - Päivitys havaittu! - - - - PKG and Game versions match: - PKG- ja peliversiot vastaavat: - - - - Would you like to overwrite? - Haluatko korvata? - - - - PKG Version %1 is older than installed version: - PKG-versio %1 on vanhempi kuin asennettu versio: - - - - Game is installed: - Peli on asennettu: - - - - Would you like to install Patch: - Haluatko asentaa Päivityksen: - - - - DLC Installation - Lisäsisällön asennus - - - - Would you like to install DLC: %1? - Haluatko asentaa lisäsisällön: %1? - - - - DLC already installed: - Lisäsisältö on jo asennettu: - - - - Game already installed - Peli on jo asennettu - - - - PKG is a patch, please install the game first! - PKG on päivitys, asenna peli ensin! - - - - PKG ERROR - PKG VIRHE - - - - Extracting PKG %1/%2 - Purkaminen PKG %1/%2 - - - - Extraction Finished - Purku Valmis - - - - Game successfully installed at %1 - Peli asennettu onnistuneesti kohtaan %1 - - - - File doesn't appear to be a valid PKG file - Tiedosto ei vaikuta olevan kelvollinen PKG-tiedosto - - - - CheatsPatches - - - Cheats / Patches for - Huijaukset / Paikkaukset pelille - - - - defaultTextEdit_MSG - Huijaukset/Paikkaukset ovat kokeellisia.\nKäytä varoen.\n\nLataa huijaukset yksitellen valitsemalla repositorion ja napsauttamalla latauspainiketta.\nPaikkaukset-välilehdessä voit ladata kaikki paikkaukset kerralla, valita, mitä haluat käyttää ja tallentaa valinnan.\n\nKoska me emme kehitä Huijauksia/Paikkauksia,\nole hyvä ja ilmoita ongelmista huijauksen tekijälle.\n\nLoitko uuden huijauksen? Käy osoitteessa:\nhttps://github.com/shadps4-emu/ps4_cheats - - - - No Image Available - Kuvaa ei saatavilla - - - - Serial: - Sarjanumero: - - - - Version: - Versio: - - - - Size: - Koko: - - - - Select Cheat File: - Valitse Huijaustiedosto: - - - - Repository: - Repositorio: - - - - Download Cheats - Lataa Huijaukset - - - - Delete File - Poista Tiedosto - - - - No files selected. - Tiedostoja ei ole valittuna. - - - - You can delete the cheats you don't want after downloading them. - Voit poistaa ei-toivomasi huijaukset lataamisen jälkeen. - - - - Do you want to delete the selected file?\n%1 - Haluatko poistaa valitun tiedoston?\n%1 - - - - Select Patch File: - Valitse Paikkaustiedosto: - - - - Download Patches - Lataa Paikkaukset - - - Save Tallenna - - Cheats - Huijaukset - - - - Patches - Paikkaukset - - - - Error - Virhe - - - - No patch selected. - Paikkausta ei ole valittuna. - - - - Unable to open files.json for reading. - Tiedostoa files.json ei voitu avata lukemista varten. - - - - No patch file found for the current serial. - Nykyiselle sarjanumerolle ei löytynyt paikkaustiedostoa. - - - - Unable to open the file for reading. - Tiedostoa ei voitu avata lukemista varten. - - - - Unable to open the file for writing. - Tiedostoa ei voitu avata kirjoittamista varten. - - - - Failed to parse XML: - XML:n jäsentäminen epäonnistui: - - - - Success - Onnistuminen - - - - Options saved successfully. - Vaihtoehdot tallennettu onnistuneesti. - - - - Invalid Source - Virheellinen Lähde - - - - The selected source is invalid. - Valittu lähde on virheellinen. - - - - File Exists - Olemassaoleva Tiedosto - - - - File already exists. Do you want to replace it? - Tiedosto on jo olemassa. Haluatko korvata sen? - - - - Failed to save file: - Tiedoston tallentaminen epäonnistui: - - - - Failed to download file: - Tiedoston lataaminen epäonnistui: - - - - Cheats Not Found - Huijauksia Ei Löytynyt - - - - CheatsNotFound_MSG - Huijauksia ei löytynyt tälle pelin versiolle valitusta repositoriosta. Kokeile toista repositoriota tai eri versiota pelistä. - - - - Cheats Downloaded Successfully - Huijaukset Ladattu Onnistuneesti - - - - CheatsDownloadedSuccessfully_MSG - Olet ladannut huijaukset onnistuneesti valitusta repositoriosta tälle pelin versiolle. Voit yrittää ladata toisesta repositoriosta. Jos se on saatavilla, voit myös käyttää sitä valitsemalla tiedoston listasta. - - - - Failed to save: - Tallentaminen epäonnistui: - - - - Failed to download: - Lataus epäonnistui: - - - - Download Complete - Lataus valmis - - - - DownloadComplete_MSG - Paikkaukset ladattu onnistuneesti! Kaikki saatavilla olevat paikkaukset kaikille peleille on ladattu, eikä niitä tarvitse ladata yksittäin jokaiselle pelille, kuten huijausten kohdalla. Jos paikkausta ei näy, saattaa olla, että sitä ei ole saatavilla kyseiselle sarjanumerolle ja peliversiolle. - - - - Failed to parse JSON data from HTML. - JSON-tietojen jäsentäminen HTML:stä epäonnistui. - - - - Failed to retrieve HTML page. - HTML-sivun hakeminen epäonnistui. - - - - The game is in version: %1 - Peli on versiossa: %1 - - - - The downloaded patch only works on version: %1 - Ladattu paikkaus toimii vain versiossa: %1 - - - - You may need to update your game. - Sinun on ehkä päivitettävä pelisi. - - - - Incompatibility Notice - Yhteensopivuusilmoitus - - - - Failed to open file: - Tiedoston avaaminen epäonnistui: - - - - XML ERROR: - XML VIRHE: - - - - Failed to open files.json for writing - Tiedostoa files.json ei voitu avata kirjoittamista varten - - - - Author: - Tekijä: - - - - Directory does not exist: - Hakemistoa ei ole olemassa: - - - - Failed to open files.json for reading. - Tiedostoa files.json ei voitu avata lukemista varten. - - - - Name: - Nimi: - - - - Can't apply cheats before the game is started - Huijauksia ei voi käyttää ennen kuin peli on käynnissä. - - - - SettingsDialog - - - Save - Tallenna - - - Apply - Ota Käyttöön + Ota käyttöön - Restore Defaults Palauta Oletukset - Close Sulje - Point your mouse at an option to display its description. Siirrä hiiri vaihtoehdon päälle näyttääksesi sen kuvauksen. - consoleLanguageGroupBox Konsolin Kieli:\nAseta PS4-pelin käyttämä kieli.\nOn suositeltavaa asettaa tämä kieleksi, jota peli tukee, mikä vaihtelee alueittain. - emulatorLanguageGroupBox Emulaattorin Kieli:\nAsettaa emulaattorin käyttöliittymän kielen. - fullscreenCheckBox Ota Koko Näytön Tila Käyttöön:\nAvaa pelin ikkunan automaattisesti koko näytön tilassa.\nTilaa voi vaihtaa painamalla F11-näppäintä. - separateUpdatesCheckBox Ota Käyttöön Erillinen Päivityskansio:\nOttaa käyttöön päivitysten asennuksen erilliseen kansioon helpottamaan niiden hallintaa.\nTämä on tehtävissä manuaalisesti lisäämällä puretun päivityksen pelikansioon "CUSA00000-UPDATE" nimellä, missä CUSA ID vastaa pelin ID:tä. - showSplashCheckBox - Näytä Aloitusnäyttö:\nNäyttää pelin aloitusnäytön (erityinen kuva) pelin käynnistyessä. + Näytä Aloitusnäyttö:\nNäyttää pelin aloitusnäytön (erityinen kuva) pelin käynnistyessä. - ps4proCheckBox On PS4 Pro:\nAsettaa emulaattorin toimimaan PS4 PRO:na, mikä voi mahdollistaa erityisiä ominaisuuksia peleissä, jotka tukevat sitä. - discordRPCCheckbox Ota käyttöön Discord Rich Presence:\nNäyttää emulaattorin kuvakkeen ja asiaankuuluvat tiedot Discord-profiilissasi. - userName Käyttäjänimi:\nAsettaa PS4-tilin käyttäjänimen, joka voi näkyä joissain peleissä. - TrophyKey Trophy Avain:\nThrophyjen dekryptoinnissa käytetty avain. Pitää hankkia jailbreakatusta konsolista.\nSaa sisältää vain hex-merkkejä. - logTypeGroupBox Lokityyppi:\nAsettaa, synkronoidaanko loki-ikkunan ulostulo suorituskyvyn vuoksi. Tämä voi vaikuttaa haitallisesti emulointiin. - logFilter Lokisuodatin:\nSuodattaa lokia tulostamaan vain määrättyä tietoa.\nEsimerkkejä: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical"\nTasot: Trace, Debug, Info, Warning, Error, Critical - tässä järjestyksessä. Valittu taso vaientaa kaikki edeltävät tasot luettelossa ja kirjaa kaikki tasot sen jälkeen. - updaterGroupBox Päivitys:\nRelease: Viralliset versiot, jotka julkaistaan kuukausittain ja saattavat olla hyvin vanhoja, mutta ovat luotettavampia ja testatumpia.\nNightly: Kehitysversiot, joissa on kaikki uusimmat ominaisuudet ja korjaukset, mutta ne saattavat sisältää virheitä ja ovat vähemmän vakaita. - GUIgroupBox Soita Otsikkomusiikkia:\nJos peli tukee sitä, ota käyttöön erityisen musiikin soittaminen pelin valinnan yhteydessä käyttöliittymässä. - disableTrophycheckBox Poista Trophy Pop-upit Käytöstä:\nPoista trophy ilmoitukset pelin aikana. Trophyjen edistystä voi silti seurata Trophy Selainta käyttämällä (klikkaa peliä hiiren oikealla emulaattorin pääikkunassa). - hideCursorGroupBox Piilota kursori:\nValitse, milloin kursori häviää:\nEi koskaan: Näet hiiren aina.\nInaktiivinen: Aseta aika, jolloin se häviää oltuaan aktiivinen.\nAina: et koskaan näe hiirtä. - idleTimeoutGroupBox Aseta aika, milloin hiiri häviää oltuaan aktiivinen. - backButtonBehaviorGroupBox Takaisin-napin käyttäytyminen:\nAsettaa ohjaimen takaisin-napin jäljittelemään kosketusta PS4:n kosketuslevyn määritettyyn kohtaan. - enableCompatibilityCheckBox Näytä Yhteensopivuustiedot:\nNäyttää pelien yhteensopivuustiedot listanäkymässä. Ota käyttöön "Päivitä Yhteensopivuustietokanta Käynnistäessä" saadaksesi ajantasaista tietoa. - checkCompatibilityOnStartupCheckBox Päivitä Yhteensopivuustiedot Käynnistäessä:\nPäivitä yhteensopivuustiedot automaattisesti shadPS4:n käynnistyessä. - updateCompatibilityButton Päivitä Yhteensopivuustietokanta:\nPäivitää yhteensopivuustietokannan heti. - Never Ei koskaan - Idle Inaktiivinen - Always Aina - Touchpad Left Kosketuslevyn Vasen Puoli - Touchpad Right Kosketuslevyn Oikea Puoli - Touchpad Center Kosketuslevyn Keskikohta - None Ei mitään - graphicsAdapterGroupBox Näytönohjain:\nUseamman näytönohjaimen järjestelmissä, valitse pudotusvalikosta, mitä näytönohjainta emulaattori käyttää,\n tai valitse "Auto Select" automaattiseen määritykseen. - resolutionLayout Leveys/Korkeus:\nAsettaa käynnistetyn emulaattori-ikkunan koon, jota voidaan muuttaa pelin aikana.\nTämä on eri, kuin pelin sisäinen resoluutio. - heightDivider Vblank Jakaja:\nEmulaattorin virkistystaajuus kerrotaan tällä numerolla. Tämän muuttaminen voi vaikuttaa haitallisesti, kuten lisätä pelin nopeutta tai rikkoa kriittisiä pelitoimintoja, jotka eivät odota tämän muuttuvan! - dumpShadersCheckBox Ota Käyttöön Varjostinvedokset:\nTeknistä vianetsintää varten. Pelin varjostimia tallennetaan hakemistoon niiden renderöityessä. - nullGpuCheckBox Ota Null GPU käyttöön:\nTeknistä vianetsintää varten. Pelin renderöinti estetään, ikään kuin näytönohjainta ei olisi. - gameFoldersBox Pelihakemistot:\nLista hakemistoista, joista pelejä haetaan. - addFolderButton Lisää:\nLisää hakemisto listalle. - removeFolderButton Poista:\nPoista hakemisto listalta. - debugDump Ota Käyttöön Virheenkorjausvedokset:\nTallentaa käynnissä olevan PS4-ohjelman tuonti- ja vientisymbolit ja tiedosto-otsikkotiedot hakemistoon. - vkValidationCheckBox Ota Käyttöön Vulkan-validointikerrokset:\nAktivoi järjestelmä, joka validoi Vulkan-renderöijän tilan ja kirjaa tietoa sen sisäisestä tilasta. Tämä heikentää suorituskykyä ja todennäköisesti muuttaa emulaation käyttäytymistä. - vkSyncValidationCheckBox Ota Käyttöön Vulkan-synkronointivalidointi:\nAktivoi järjestelmä, joka validoi Vulkan-renderöinnin tehtävien aikataulutuksen. Tämä heikentää suorituskykyä ja todennäköisesti muuttaa emulaation käyttäytymistä. - rdocCheckBox Ota Käyttöön RenderDoc Virheenkorjaus:\nJos käytössä, emulaattori tarjoaa Renderdoc-yhteensopivuuden, mikä mahdollistaa renderöidyn kehyksen tallennuksen ja analysoinnin. + + CheatsPatches + + Cheats / Patches for + Huijaukset / Paikkaukset pelille + + + defaultTextEdit_MSG + Huijaukset/Paikkaukset ovat kokeellisia.\nKäytä varoen.\n\nLataa huijaukset yksitellen valitsemalla repositorion ja napsauttamalla latauspainiketta.\nPaikkaukset-välilehdessä voit ladata kaikki paikkaukset kerralla, valita, mitä haluat käyttää ja tallentaa valinnan.\n\nKoska me emme kehitä Huijauksia/Paikkauksia,\nole hyvä ja ilmoita ongelmista huijauksen tekijälle.\n\nLoitko uuden huijauksen? Käy osoitteessa:\nhttps://github.com/shadps4-emu/ps4_cheats + + + No Image Available + Kuvaa ei saatavilla + + + Serial: + Sarjanumero: + + + Version: + Versio: + + + Size: + Koko: + + + Select Cheat File: + Valitse Huijaustiedosto: + + + Repository: + Repositorio: + + + Download Cheats + Lataa Huijaukset + + + Delete File + Poista Tiedosto + + + No files selected. + Tiedostoja ei ole valittuna. + + + You can delete the cheats you don't want after downloading them. + Voit poistaa ei-toivomasi huijaukset lataamisen jälkeen. + + + Do you want to delete the selected file?\n%1 + Haluatko poistaa valitun tiedoston?\n%1 + + + Select Patch File: + Valitse Paikkaustiedosto: + + + Download Patches + Lataa Paikkaukset + + + Save + Tallenna + + + Cheats + Huijaukset + + + Patches + Paikkaukset + + + Error + Virhe + + + No patch selected. + Paikkausta ei ole valittuna. + + + Unable to open files.json for reading. + Tiedostoa files.json ei voitu avata lukemista varten. + + + No patch file found for the current serial. + Nykyiselle sarjanumerolle ei löytynyt paikkaustiedostoa. + + + Unable to open the file for reading. + Tiedostoa ei voitu avata lukemista varten. + + + Unable to open the file for writing. + Tiedostoa ei voitu avata kirjoittamista varten. + + + Failed to parse XML: + XML:n jäsentäminen epäonnistui: + + + Success + Onnistuminen + + + Options saved successfully. + Vaihtoehdot tallennettu onnistuneesti. + + + Invalid Source + Virheellinen Lähde + + + The selected source is invalid. + Valittu lähde on virheellinen. + + + File Exists + Olemassaoleva Tiedosto + + + File already exists. Do you want to replace it? + Tiedosto on jo olemassa. Haluatko korvata sen? + + + Failed to save file: + Tiedoston tallentaminen epäonnistui: + + + Failed to download file: + Tiedoston lataaminen epäonnistui: + + + Cheats Not Found + Huijauksia Ei Löytynyt + + + CheatsNotFound_MSG + Huijauksia ei löytynyt tälle pelin versiolle valitusta repositoriosta. Kokeile toista repositoriota tai eri versiota pelistä. + + + Cheats Downloaded Successfully + Huijaukset Ladattu Onnistuneesti + + + CheatsDownloadedSuccessfully_MSG + Olet ladannut huijaukset onnistuneesti valitusta repositoriosta tälle pelin versiolle. Voit yrittää ladata toisesta repositoriosta. Jos se on saatavilla, voit myös käyttää sitä valitsemalla tiedoston listasta. + + + Failed to save: + Tallentaminen epäonnistui: + + + Failed to download: + Lataus epäonnistui: + + + Download Complete + Lataus valmis + + + DownloadComplete_MSG + Paikkaukset ladattu onnistuneesti! Kaikki saatavilla olevat paikkaukset kaikille peleille on ladattu, eikä niitä tarvitse ladata yksittäin jokaiselle pelille, kuten huijausten kohdalla. Jos paikkausta ei näy, saattaa olla, että sitä ei ole saatavilla kyseiselle sarjanumerolle ja peliversiolle. + + + Failed to parse JSON data from HTML. + JSON-tietojen jäsentäminen HTML:stä epäonnistui. + + + Failed to retrieve HTML page. + HTML-sivun hakeminen epäonnistui. + + + The game is in version: %1 + Peli on versiossa: %1 + + + The downloaded patch only works on version: %1 + Ladattu paikkaus toimii vain versiossa: %1 + + + You may need to update your game. + Sinun on ehkä päivitettävä pelisi. + + + Incompatibility Notice + Yhteensopivuusilmoitus + + + Failed to open file: + Tiedoston avaaminen epäonnistui: + + + XML ERROR: + XML VIRHE: + + + Failed to open files.json for writing + Tiedostoa files.json ei voitu avata kirjoittamista varten + + + Author: + Tekijä: + + + Directory does not exist: + Hakemistoa ei ole olemassa: + + + Failed to open files.json for reading. + Tiedostoa files.json ei voitu avata lukemista varten. + + + Name: + Nimi: + + + Can't apply cheats before the game is started + Huijauksia ei voi käyttää ennen kuin peli on käynnissä. + + GameListFrame - Icon Ikoni - Name Nimi - Serial Sarjanumero - Compatibility Compatibility - Region Alue - Firmware Ohjelmisto - Size Koko - Version Versio - Path Polku - Play Time Peliaika - Never Played Pelaamaton - h h - m m - s s - Compatibility is untested Yhteensopivuutta ei ole testattu - Game does not initialize properly / crashes the emulator - Peli ei alustaudu kunnolla / kaataa emulaattorin + Peli ei alustaudu kunnolla / kaataa emulaattorin - Game boots, but only displays a blank screen Peli käynnistyy, mutta näyttää vain tyhjän ruudun - Game displays an image but does not go past the menu Peli näyttää kuvan mutta ei mene valikosta eteenpäin - Game has game-breaking glitches or unplayable performance Pelissä on pelikokemusta rikkovia häiriöitä tai kelvoton suorituskyky - Game can be completed with playable performance and no major glitches Pelillä on hyväksyttävä suorituskyky, eikä mitään suuria häiriöitä @@ -1508,127 +1210,102 @@ CheckUpdate - Auto Updater Automaattinen Päivitys - Error Virhe - Network error: Verkkovirhe: - Failed to parse update information. Päivitystietojen jäsentäminen epäonnistui. - No pre-releases found. Ennakkojulkaisuja ei löytynyt. - Invalid release data. Virheelliset julkaisutiedot. - No download URL found for the specified asset. Lataus-URL:ia ei löytynyt määritetylle omaisuudelle. - Your version is already up to date! Versiosi on jo ajan tasalla! - Update Available Päivitys Saatavilla - Update Channel Päivityskanava - Current Version Nykyinen Versio - Latest Version Uusin Versio - Do you want to update? Haluatko päivittää? - Show Changelog Näytä Muutoshistoria - Check for Updates at Startup Tarkista Päivitykset Käynnistettäessä - Update Päivitä - No Ei - Hide Changelog Piilota Muutoshistoria - Changes Muutokset - Network error occurred while trying to access the URL URL-osoitteeseen yhdistettäessä tapahtui verkkovirhe - Download Complete Lataus Valmis - The update has been downloaded, press OK to install. Päivitys on ladattu, paina OK asentaaksesi. - Failed to save the update file at Päivitystiedoston tallentaminen epäonnistui sijaintiin - Starting Update... Aloitetaan päivitystä... - Failed to create the update script file Päivitysskripttitiedoston luominen epäonnistui @@ -1636,29 +1313,24 @@ GameListUtils - B B - KB KB - MB MB - GB GB - TB TB - + - + \ No newline at end of file diff --git a/src/qt_gui/translations/fr.ts b/src/qt_gui/translations/fr.ts index 441eaddb1..c2a2a64d7 100644 --- a/src/qt_gui/translations/fr.ts +++ b/src/qt_gui/translations/fr.ts @@ -6,22 +6,18 @@ AboutDialog - About shadPS4 À propos de shadPS4 - shadPS4 shadPS4 - shadPS4 is an experimental open-source emulator for the PlayStation 4. shadPS4 est un émulateur open-source expérimental de la PlayStation 4. - This software should not be used to play games you have not legally obtained. Ce logiciel ne doit pas être utilisé pour jouer à des jeux que vous n'avez pas obtenus légalement. @@ -29,7 +25,6 @@ ElfViewer - Open Folder Ouvrir un dossier @@ -37,17 +32,14 @@ GameInfoClass - Loading game list, please wait :3 Chargement de la liste de jeu, veuillez patienter... - Cancel Annuler - Loading... Chargement... @@ -55,12 +47,10 @@ InstallDirSelect - shadPS4 - Choose directory shadPS4 - Choisir un répertoire - Select which directory you want to install to. Sélectionnez le répertoire où vous souhaitez effectuer l'installation. @@ -68,27 +58,22 @@ GameInstallDialog - shadPS4 - Choose directory shadPS4 - Choisir un répertoire - Directory to install games Répertoire d'installation des jeux - Browse Parcourir - Error Erreur - The value for location to install games is not valid. Le répertoire d'installation des jeux n'est pas valide. @@ -96,167 +81,134 @@ GuiContextMenus - Create Shortcut Créer un raccourci - Cheats / Patches Cheats/Patchs - SFO Viewer Visionneuse SFO - Trophy Viewer Visionneuse de trophées - Open Folder... Ouvrir le Dossier... - Open Game Folder Ouvrir le Dossier du Jeu - Open Save Data Folder Ouvrir le Dossier des Données de Sauvegarde - Open Log Folder Ouvrir le Dossier des Logs - Copy info... Copier infos... - Copy Name Copier le nom - Copy Serial Copier le N° de série - Copy All Copier tout - Delete... Supprimer... - Delete Game Supprimer jeu - Delete Update Supprimer MÀJ - Delete DLC Supprimer DLC - Compatibility... Compatibility... - Update database Update database - View report View report - Submit a report Submit a report - Shortcut creation Création du raccourci - Shortcut created successfully! Raccourci créé avec succès ! - Error Erreur - Error creating shortcut! Erreur lors de la création du raccourci ! - Install PKG Installer un PKG - Game Jeu - requiresEnableSeparateUpdateFolder_MSG This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. - This game has no update to delete! Ce jeu n'a pas de mise à jour à supprimer! - - + Update Mise à jour - This game has no DLC to delete! Ce jeu n'a pas de DLC à supprimer! - DLC DLC - Delete %1 Supprime %1 - Are you sure you want to delete %1's %2 directory? Êtes vous sûr de vouloir supprimer le répertoire %1 %2 ? @@ -264,205 +216,285 @@ MainWindow - Open/Add Elf Folder Ouvrir/Ajouter un dossier ELF - Install Packages (PKG) Installer des packages (PKG) - Boot Game Démarrer un jeu - Check for Updates Vérifier les mises à jour - About shadPS4 À propos de shadPS4 - Configure... Configurer... - Install application from a .pkg file Installer une application depuis un fichier .pkg - Recent Games Jeux récents - Exit Fermer - Exit shadPS4 Fermer shadPS4 - Exit the application. Fermer l'application. - Show Game List Afficher la liste de jeux - Game List Refresh Rafraîchir la liste de jeux - Tiny Très Petit - Small Petit - Medium Moyen - Large Grand - List View Mode liste - Grid View Mode grille - Elf Viewer Visionneuse ELF - Game Install Directory Répertoire des jeux - Download Cheats/Patches Télécharger Cheats/Patchs - Dump Game List Dumper la liste des jeux - PKG Viewer Visionneuse PKG - Search... Chercher... - File Fichier - View Affichage - Game List Icons Icônes des jeux - Game List Mode Mode d'affichage - Settings Paramètres - Utils Utilitaires - Themes Thèmes - Help Aide - Dark Sombre - Light Clair - Green Vert - Blue Bleu - Violet Violet - toolBar Bare d'outils + + Game List + Liste de jeux + + + * Unsupported Vulkan Version + * Version de Vulkan non prise en charge + + + Download Cheats For All Installed Games + Télécharger les Cheats pour tous les jeux installés + + + Download Patches For All Games + Télécharger les patchs pour tous les jeux + + + Download Complete + Téléchargement terminé + + + You have downloaded cheats for all the games you have installed. + Vous avez téléchargé des Cheats pour tous les jeux installés. + + + Patches Downloaded Successfully! + Patchs téléchargés avec succès ! + + + All Patches available for all games have been downloaded. + Tous les patchs disponibles ont été téléchargés. + + + Games: + Jeux: + + + PKG File (*.PKG) + Fichiers PKG (*.PKG) + + + ELF files (*.bin *.elf *.oelf) + Fichiers ELF (*.bin *.elf *.oelf) + + + Game Boot + Démarrer un jeu + + + Only one file can be selected! + Un seul fichier peut être sélectionné ! + + + PKG Extraction + Extraction du PKG + + + Patch detected! + Patch détecté ! + + + PKG and Game versions match: + Les versions PKG et jeu correspondent: + + + Would you like to overwrite? + Souhaitez-vous remplacer ? + + + PKG Version %1 is older than installed version: + La version PKG %1 est plus ancienne que la version installée: + + + Game is installed: + Jeu installé: + + + Would you like to install Patch: + Souhaitez-vous installer le patch: + + + DLC Installation + Installation du DLC + + + Would you like to install DLC: %1? + Souhaitez-vous installer le DLC: %1 ? + + + DLC already installed: + DLC déjà installé: + + + Game already installed + Jeu déjà installé + + + PKG is a patch, please install the game first! + Le PKG est un patch, veuillez d'abord installer le jeu ! + + + PKG ERROR + Erreur PKG + + + Extracting PKG %1/%2 + Extraction PKG %1/%2 + + + Extraction Finished + Extraction terminée + + + Game successfully installed at %1 + Jeu installé avec succès à %1 + + + File doesn't appear to be a valid PKG file + Le fichier ne semble pas être un PKG valide + PKGViewer - Open Folder Ouvrir un dossier @@ -470,7 +502,6 @@ TrophyViewer - Trophy Viewer Visionneuse de trophées @@ -478,1029 +509,700 @@ SettingsDialog - Settings Paramètres - General Général - System Système - Console Language Langage de la console - Emulator Language Langage de l'émulateur - Emulator Émulateur - Enable Fullscreen Plein écran - Enable Separate Update Folder Dossier séparé pour les mises à jours - Show Splash Afficher l'image du jeu - Is PS4 Pro Mode PS4 Pro - Enable Discord Rich Presence Activer la présence Discord - Username Nom d'utilisateur - Trophy Key Trophy Key - Trophy Trophy - Logger Journalisation - Log Type Type - Log Filter Filtre - Input Entrée - Cursor Curseur - Hide Cursor Masquer le curseur - Hide Cursor Idle Timeout Délai d'inactivité pour masquer le curseur - s s - Controller Manette - Back Button Behavior Comportement du bouton retour - Graphics Graphismes - Graphics Device Carte graphique - Width Largeur - Height Hauteur - Vblank Divider Vblank - Advanced Avancé - Enable Shaders Dumping Dumper les shaders - Enable NULL GPU NULL GPU - Paths Chemins - Game Folders Dossiers de jeu - Add... Ajouter... - Remove Supprimer - Debug Débogage - Enable Debug Dumping Activer le débogage - Enable Vulkan Validation Layers Activer la couche de validation Vulkan - Enable Vulkan Synchronization Validation Activer la synchronisation de la validation Vulkan - Enable RenderDoc Debugging Activer le débogage RenderDoc - Update Mise à jour - Check for Updates at Startup Vérif. maj au démarrage - Update Channel Canal de Mise à Jour - Check for Updates Vérifier les mises à jour - GUI Settings Paramètres de l'interface - Disable Trophy Pop-ups Disable Trophy Pop-ups - Play title music Lire la musique du titre - Update Compatibility Database On Startup Update Compatibility Database On Startup - Game Compatibility Game Compatibility - Display Compatibility Data Display Compatibility Data - Update Compatibility Database Update Compatibility Database - Volume Volume - Audio Backend Audio Backend - - - MainWindow - - Game List - Liste de jeux - - - - * Unsupported Vulkan Version - * Version de Vulkan non prise en charge - - - - Download Cheats For All Installed Games - Télécharger les Cheats pour tous les jeux installés - - - - Download Patches For All Games - Télécharger les patchs pour tous les jeux - - - - Download Complete - Téléchargement terminé - - - - You have downloaded cheats for all the games you have installed. - Vous avez téléchargé des Cheats pour tous les jeux installés. - - - - Patches Downloaded Successfully! - Patchs téléchargés avec succès ! - - - - All Patches available for all games have been downloaded. - Tous les patchs disponibles ont été téléchargés. - - - - Games: - Jeux: - - - - PKG File (*.PKG) - Fichiers PKG (*.PKG) - - - - ELF files (*.bin *.elf *.oelf) - Fichiers ELF (*.bin *.elf *.oelf) - - - - Game Boot - Démarrer un jeu - - - - Only one file can be selected! - Un seul fichier peut être sélectionné ! - - - - PKG Extraction - Extraction du PKG - - - - Patch detected! - Patch détecté ! - - - - PKG and Game versions match: - Les versions PKG et jeu correspondent: - - - - Would you like to overwrite? - Souhaitez-vous remplacer ? - - - - PKG Version %1 is older than installed version: - La version PKG %1 est plus ancienne que la version installée: - - - - Game is installed: - Jeu installé: - - - - Would you like to install Patch: - Souhaitez-vous installer le patch: - - - - DLC Installation - Installation du DLC - - - - Would you like to install DLC: %1? - Souhaitez-vous installer le DLC: %1 ? - - - - DLC already installed: - DLC déjà installé: - - - - Game already installed - Jeu déjà installé - - - - PKG is a patch, please install the game first! - Le PKG est un patch, veuillez d'abord installer le jeu ! - - - - PKG ERROR - Erreur PKG - - - - Extracting PKG %1/%2 - Extraction PKG %1/%2 - - - - Extraction Finished - Extraction terminée - - - - Game successfully installed at %1 - Jeu installé avec succès à %1 - - - - File doesn't appear to be a valid PKG file - Le fichier ne semble pas être un PKG valide - - - - CheatsPatches - - - Cheats / Patches for - Cheats/Patchs pour - - - - defaultTextEdit_MSG - Les Cheats/Patchs sont expérimentaux.\nUtilisez-les avec précaution.\n\nTéléchargez les Cheats individuellement en sélectionnant le dépôt et en cliquant sur le bouton de téléchargement.\nDans l'onglet Patchs, vous pouvez télécharger tous les patchs en une seule fois, choisir lesquels vous souhaitez utiliser et enregistrer votre sélection.\n\nComme nous ne développons pas les Cheats/Patches,\nmerci de signaler les problèmes à l'auteur du Cheat.\n\nVous avez créé un nouveau cheat ? Visitez:\nhttps://github.com/shadps4-emu/ps4_cheats - - - - No Image Available - Aucune image disponible - - - - Serial: - Série: - - - - Version: - Version: - - - - Size: - Taille: - - - - Select Cheat File: - Sélectionner le fichier de Cheat: - - - - Repository: - Dépôt: - - - - Download Cheats - Télécharger les Cheats - - - - Delete File - Supprimer le fichier - - - - No files selected. - Aucun fichier sélectionné. - - - - You can delete the cheats you don't want after downloading them. - Vous pouvez supprimer les Cheats que vous ne souhaitez pas après les avoir téléchargés. - - - - Do you want to delete the selected file?\n%1 - Voulez-vous supprimer le fichier sélectionné ?\n%1 - - - - Select Patch File: - Sélectionner le fichier de patch: - - - - Download Patches - Télécharger les patchs - - - Save Enregistrer - - Cheats - Cheats - - - - Patches - Patchs - - - - Error - Erreur - - - - No patch selected. - Aucun patch sélectionné. - - - - Unable to open files.json for reading. - Impossible d'ouvrir files.json pour la lecture. - - - - No patch file found for the current serial. - Aucun fichier de patch trouvé pour la série actuelle. - - - - Unable to open the file for reading. - Impossible d'ouvrir le fichier pour la lecture. - - - - Unable to open the file for writing. - Impossible d'ouvrir le fichier pour l'écriture. - - - - Failed to parse XML: - Échec de l'analyse XML: - - - - Success - Succès - - - - Options saved successfully. - Options enregistrées avec succès. - - - - Invalid Source - Source invalide - - - - The selected source is invalid. - La source sélectionnée est invalide. - - - - File Exists - Le fichier existe - - - - File already exists. Do you want to replace it? - Le fichier existe déjà. Voulez-vous le remplacer ? - - - - Failed to save file: - Échec de l'enregistrement du fichier: - - - - Failed to download file: - Échec du téléchargement du fichier: - - - - Cheats Not Found - Cheats non trouvés - - - - CheatsNotFound_MSG - Aucun Cheat trouvé pour ce jeu dans cette version du dépôt sélectionné, essayez un autre dépôt ou une version différente du jeu. - - - - Cheats Downloaded Successfully - Cheats téléchargés avec succès - - - - CheatsDownloadedSuccessfully_MSG - Vous avez téléchargé les cheats avec succès pour cette version du jeu depuis le dépôt sélectionné. Vous pouvez essayer de télécharger depuis un autre dépôt, si disponible, il sera également possible de l'utiliser en sélectionnant le fichier dans la liste. - - - - Failed to save: - Échec de l'enregistrement: - - - - Failed to download: - Échec du téléchargement: - - - - Download Complete - Téléchargement terminé - - - - DownloadComplete_MSG - Patchs téléchargés avec succès ! Tous les patches disponibles pour tous les jeux ont été téléchargés, il n'est pas nécessaire de les télécharger individuellement pour chaque jeu comme c'est le cas pour les Cheats. Si le correctif n'apparaît pas, il se peut qu'il n'existe pas pour le numéro de série et la version spécifiques du jeu. - - - - Failed to parse JSON data from HTML. - Échec de l'analyse des données JSON à partir du HTML. - - - - Failed to retrieve HTML page. - Échec de la récupération de la page HTML. - - - - The game is in version: %1 - Le jeu est en version: %1 - - - - The downloaded patch only works on version: %1 - Le patch téléchargé ne fonctionne que sur la version: %1 - - - - You may need to update your game. - Vous devrez peut-être mettre à jour votre jeu. - - - - Incompatibility Notice - Avis d'incompatibilité - - - - Failed to open file: - Échec de l'ouverture du fichier: - - - - XML ERROR: - Erreur XML: - - - - Failed to open files.json for writing - Échec de l'ouverture de files.json pour l'écriture - - - - Author: - Auteur: - - - - Directory does not exist: - Répertoire n'existe pas: - - - - Failed to open files.json for reading. - Échec de l'ouverture de files.json pour la lecture. - - - - Name: - Nom: - - - - Can't apply cheats before the game is started - Impossible d'appliquer les Cheats avant que le jeu ne commence. - - - - SettingsDialog - - - Save - Enregistrer - - - Apply Appliquer - Restore Defaults Restaurer les paramètres par défaut - Close Fermer - Point your mouse at an option to display its description. Pointez votre souris sur une option pour afficher sa description. - consoleLanguageGroupBox Langue de la console:\nDéfinit la langue utilisée par le jeu PS4.\nIl est recommandé de le définir sur une langue que le jeu prend en charge, ce qui variera selon la région. - emulatorLanguageGroupBox Langue de l'émulateur:\nDéfinit la langue de l'interface utilisateur de l'émulateur. - fullscreenCheckBox Activer le mode plein écran:\nMet automatiquement la fenêtre du jeu en mode plein écran.\nCela peut être activé en appuyant sur la touche F11. - separateUpdatesCheckBox Dossier séparé pour les mises à jours:\nInstalle les mises à jours des jeux dans un dossier séparé pour une gestion plus facile. - showSplashCheckBox Afficher l'écran de démarrage:\nAffiche l'écran de démarrage du jeu (une image spéciale) lors du démarrage du jeu. - ps4proCheckBox Mode PS4 Pro:\nFait en sorte que l'émulateur se comporte comme un PS4 PRO, ce qui peut activer des fonctionnalités spéciales dans les jeux qui le prennent en charge. - discordRPCCheckbox Activer Discord Rich Presence:\nAffiche l'icône de l'émulateur et les informations pertinentes sur votre profil Discord. - userName Nom d'utilisateur:\nDéfinit le nom d'utilisateur du compte PS4, qui peut être affiché par certains jeux. - TrophyKey Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. - logTypeGroupBox Type de journal:\nDétermine si la sortie de la fenêtre de journalisation est synchronisée pour des raisons de performance. Cela peut avoir un impact négatif sur l'émulation. - logFilter Filtre de journal:\n n'imprime que des informations spécifiques.\nExemples: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Niveaux: Trace, Debug, Info, Avertissement, Erreur, Critique - dans cet ordre, un niveau particulier désactive tous les niveaux précédents de la liste et enregistre tous les niveaux suivants. - updaterGroupBox Mise à jour:\nRelease: versions officielles publiées chaque mois qui peuvent être très anciennes, mais plus fiables et testées.\nNightly: versions de développement avec toutes les dernières fonctionnalités et correctifs, mais pouvant avoir des bogues et être moins stables. - GUIgroupBox Jouer de la musique de titre:\nSi le jeu le prend en charge, cela active la musique spéciale lorsque vous sélectionnez le jeu dans l'interface utilisateur. - disableTrophycheckBox Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). - hideCursorGroupBox Masquer le curseur:\nChoisissez quand le curseur disparaîtra:\nJamais: Vous verrez toujours la souris.\nInactif: Définissez un temps pour qu'il disparaisse après inactivité.\nToujours: vous ne verrez jamais la souris. - idleTimeoutGroupBox Définissez un temps pour que la souris disparaisse après être inactif. - backButtonBehaviorGroupBox Comportement du bouton retour:\nDéfinit le bouton de retour de la manette pour imiter le toucher de la position spécifiée sur le pavé tactile PS4. - enableCompatibilityCheckBox Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. - checkCompatibilityOnStartupCheckBox Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. - updateCompatibilityButton Update Compatibility Database:\nImmediately update the compatibility database. - Never Jamais - Idle Inactif - Always Toujours - Touchpad Left Pavé Tactile Gauche - Touchpad Right Pavé Tactile Droit - Touchpad Center Centre du Pavé Tactile - None Aucun - graphicsAdapterGroupBox Adaptateur graphique:\nSélectionnez le GPU que l'émulateur utilisera dans les systèmes multi-GPU à partir de la liste déroulante,\nou choisissez "Auto Select" pour le déterminer automatiquement. - resolutionLayout Largeur/Hauteur:\nDéfinit la taille de la fenêtre de l'émulateur au démarrage, qui peut être redimensionnée pendant le jeu.\nCela diffère de la résolution interne du jeu. - heightDivider Diviseur Vblank:\nLe taux de rafraîchissement de l'émulateur est multiplié par ce nombre. Changer cela peut avoir des effets négatifs, tels qu'une augmentation de la vitesse du jeu ou la rupture de fonctionnalités critiques du jeu qui ne s'attendent pas à ce changement ! - dumpShadersCheckBox Activer l'exportation de shaders:\nPour le débogage technique, les shaders du jeu sont enregistrés dans un dossier lors du rendu. - nullGpuCheckBox Activer le GPU nul:\nPour le débogage technique, désactive le rendu du jeu comme s'il n'y avait pas de carte graphique. - gameFoldersBox Dossiers de jeux:\nLa liste des dossiers à vérifier pour les jeux installés. - addFolderButton Ajouter:\nAjouter un dossier à la liste. - removeFolderButton Supprimer:\nSupprimer un dossier de la liste. - debugDump Activer l'exportation de débogage:\nEnregistre les symboles d'importation et d'exportation et les informations d'en-tête du fichier du programme PS4 actuel dans un répertoire. - vkValidationCheckBox Activer les couches de validation Vulkan:\nActive un système qui valide l'état du rendu Vulkan et enregistre des informations sur son état interne. Cela réduit les performances et peut changer le comportement de l'émulation. - vkSyncValidationCheckBox Activer la validation de synchronisation Vulkan:\nActive un système qui valide la planification des tâches de rendu Vulkan. Cela réduit les performances et peut changer le comportement de l'émulation. - rdocCheckBox Activer le débogage RenderDoc:\nS'il est activé, l'émulateur fournit une compatibilité avec Renderdoc, permettant d'enregistrer et d'analyser la trame rendue actuelle. + + CheatsPatches + + Cheats / Patches for + Cheats/Patchs pour + + + defaultTextEdit_MSG + Les Cheats/Patchs sont expérimentaux.\nUtilisez-les avec précaution.\n\nTéléchargez les Cheats individuellement en sélectionnant le dépôt et en cliquant sur le bouton de téléchargement.\nDans l'onglet Patchs, vous pouvez télécharger tous les patchs en une seule fois, choisir lesquels vous souhaitez utiliser et enregistrer votre sélection.\n\nComme nous ne développons pas les Cheats/Patches,\nmerci de signaler les problèmes à l'auteur du Cheat.\n\nVous avez créé un nouveau cheat ? Visitez:\nhttps://github.com/shadps4-emu/ps4_cheats + + + No Image Available + Aucune image disponible + + + Serial: + Série: + + + Version: + Version: + + + Size: + Taille: + + + Select Cheat File: + Sélectionner le fichier de Cheat: + + + Repository: + Dépôt: + + + Download Cheats + Télécharger les Cheats + + + Delete File + Supprimer le fichier + + + No files selected. + Aucun fichier sélectionné. + + + You can delete the cheats you don't want after downloading them. + Vous pouvez supprimer les Cheats que vous ne souhaitez pas après les avoir téléchargés. + + + Do you want to delete the selected file?\n%1 + Voulez-vous supprimer le fichier sélectionné ?\n%1 + + + Select Patch File: + Sélectionner le fichier de patch: + + + Download Patches + Télécharger les patchs + + + Save + Enregistrer + + + Cheats + Cheats + + + Patches + Patchs + + + Error + Erreur + + + No patch selected. + Aucun patch sélectionné. + + + Unable to open files.json for reading. + Impossible d'ouvrir files.json pour la lecture. + + + No patch file found for the current serial. + Aucun fichier de patch trouvé pour la série actuelle. + + + Unable to open the file for reading. + Impossible d'ouvrir le fichier pour la lecture. + + + Unable to open the file for writing. + Impossible d'ouvrir le fichier pour l'écriture. + + + Failed to parse XML: + Échec de l'analyse XML: + + + Success + Succès + + + Options saved successfully. + Options enregistrées avec succès. + + + Invalid Source + Source invalide + + + The selected source is invalid. + La source sélectionnée est invalide. + + + File Exists + Le fichier existe + + + File already exists. Do you want to replace it? + Le fichier existe déjà. Voulez-vous le remplacer ? + + + Failed to save file: + Échec de l'enregistrement du fichier: + + + Failed to download file: + Échec du téléchargement du fichier: + + + Cheats Not Found + Cheats non trouvés + + + CheatsNotFound_MSG + Aucun Cheat trouvé pour ce jeu dans cette version du dépôt sélectionné, essayez un autre dépôt ou une version différente du jeu. + + + Cheats Downloaded Successfully + Cheats téléchargés avec succès + + + CheatsDownloadedSuccessfully_MSG + Vous avez téléchargé les cheats avec succès pour cette version du jeu depuis le dépôt sélectionné. Vous pouvez essayer de télécharger depuis un autre dépôt, si disponible, il sera également possible de l'utiliser en sélectionnant le fichier dans la liste. + + + Failed to save: + Échec de l'enregistrement: + + + Failed to download: + Échec du téléchargement: + + + Download Complete + Téléchargement terminé + + + DownloadComplete_MSG + Patchs téléchargés avec succès ! Tous les patches disponibles pour tous les jeux ont été téléchargés, il n'est pas nécessaire de les télécharger individuellement pour chaque jeu comme c'est le cas pour les Cheats. Si le correctif n'apparaît pas, il se peut qu'il n'existe pas pour le numéro de série et la version spécifiques du jeu. + + + Failed to parse JSON data from HTML. + Échec de l'analyse des données JSON à partir du HTML. + + + Failed to retrieve HTML page. + Échec de la récupération de la page HTML. + + + The game is in version: %1 + Le jeu est en version: %1 + + + The downloaded patch only works on version: %1 + Le patch téléchargé ne fonctionne que sur la version: %1 + + + You may need to update your game. + Vous devrez peut-être mettre à jour votre jeu. + + + Incompatibility Notice + Avis d'incompatibilité + + + Failed to open file: + Échec de l'ouverture du fichier: + + + XML ERROR: + Erreur XML: + + + Failed to open files.json for writing + Échec de l'ouverture de files.json pour l'écriture + + + Author: + Auteur: + + + Directory does not exist: + Répertoire n'existe pas: + + + Failed to open files.json for reading. + Échec de l'ouverture de files.json pour la lecture. + + + Name: + Nom: + + + Can't apply cheats before the game is started + Impossible d'appliquer les Cheats avant que le jeu ne commence. + + GameListFrame - Icon Icône - Name Nom - Serial Série - Compatibility Compatibility - Region Région - Firmware Firmware - Size Taille - Version Version - Path Répertoire - Play Time Temps de jeu - Never Played Jamais joué - h h - m m - s s - Compatibility is untested Compatibility is untested - Game does not initialize properly / crashes the emulator Game does not initialize properly / crashes the emulator - Game boots, but only displays a blank screen Game boots, but only displays a blank screen - Game displays an image but does not go past the menu Game displays an image but does not go past the menu - Game has game-breaking glitches or unplayable performance Game has game-breaking glitches or unplayable performance - Game can be completed with playable performance and no major glitches Game can be completed with playable performance and no major glitches @@ -1508,127 +1210,102 @@ CheckUpdate - Auto Updater Mise à jour automatique - Error Erreur - Network error: Erreur réseau: - Failed to parse update information. Échec de l'analyse des informations de mise à jour. - No pre-releases found. Aucune pré-version trouvée. - Invalid release data. Données de version invalides. - No download URL found for the specified asset. Aucune URL de téléchargement trouvée pour l'élément spécifié. - Your version is already up to date! Votre version est déjà à jour ! - Update Available Mise à jour disponible - Update Channel Canal de Mise à Jour - Current Version Version actuelle - Latest Version Dernière version - Do you want to update? Voulez-vous mettre à jour ? - Show Changelog Afficher le journal des modifications - Check for Updates at Startup Vérif. maj au démarrage - Update Mettre à jour - No Non - Hide Changelog Cacher le journal des modifications - Changes Modifications - Network error occurred while trying to access the URL Une erreur réseau s'est produite en essayant d'accéder à l'URL - Download Complete Téléchargement terminé - The update has been downloaded, press OK to install. La mise à jour a été téléchargée, appuyez sur OK pour l'installer. - Failed to save the update file at Échec de la sauvegarde du fichier de mise à jour à - Starting Update... Démarrage de la mise à jour... - Failed to create the update script file Échec de la création du fichier de script de mise à jour @@ -1636,29 +1313,24 @@ GameListUtils - B B - KB KB - MB MB - GB GB - TB TB - + \ No newline at end of file diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index f6b853e4b..677302e01 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -6,22 +6,18 @@ AboutDialog - About shadPS4 A shadPS4-ről - shadPS4 shadPS4 - shadPS4 is an experimental open-source emulator for the PlayStation 4. A shadPS4 egy kezdetleges, open-source PlayStation 4 emulátor. - This software should not be used to play games you have not legally obtained. Ne használja ezt a szoftvert olyan játékokkal, amelyeket nem legális módon szerzett be. @@ -29,7 +25,6 @@ ElfViewer - Open Folder Mappa megnyitása @@ -37,17 +32,14 @@ GameInfoClass - Loading game list, please wait :3 Játék könyvtár betöltése, kérjük várjon :3 - Cancel Megszakítás - Loading... Betöltés.. @@ -55,12 +47,10 @@ InstallDirSelect - shadPS4 - Choose directory shadPS4 - Mappa kiválasztása - Select which directory you want to install to. Válassza ki a mappát a játékok telepítésére. @@ -68,27 +58,22 @@ GameInstallDialog - shadPS4 - Choose directory shadPS4 - Mappa kiválasztása - Directory to install games Mappa a játékok telepítésére - Browse Böngészés - Error Hiba - The value for location to install games is not valid. A játékok telepítéséhez megadott útvonal nem érvényes. @@ -96,167 +81,134 @@ GuiContextMenus - Create Shortcut Parancsikon Létrehozása - Cheats / Patches Csalások / Javítások - SFO Viewer SFO Nézegető - Trophy Viewer Trófeák Megtekintése - Open Folder... Mappa megnyitása... - Open Game Folder Játék Mappa Megnyitása - Open Save Data Folder Mentési adatok mappa megnyitása - Open Log Folder Napló mappa megnyitása - Copy info... Információ Másolása... - Copy Name Név Másolása - Copy Serial Széria Másolása - Copy All Összes Másolása - Delete... Törlés... - Delete Game Játék törlése - Delete Update Frissítések törlése - Delete DLC DLC-k törlése - Compatibility... Compatibility... - Update database Update database - View report View report - Submit a report Submit a report - Shortcut creation Parancsikon létrehozása - Shortcut created successfully! Parancsikon sikeresen létrehozva! - Error Hiba - Error creating shortcut! Hiba a parancsikon létrehozásával! - Install PKG PKG telepítése - Game Játék - requiresEnableSeparateUpdateFolder_MSG Ehhez a funkcióhoz szükséges a 'Külön Frissítési Mappa Engedélyezése' opció, hogy működjön. Ha használni akarja, először engedélyezze azt. - This game has no update to delete! Ehhez a játékhoz nem tartozik törlendő frissítés! - - + Update Frissítés - This game has no DLC to delete! Ehhez a játékhoz nem tartozik törlendő DLC! - DLC DLC - Delete %1 Delete %1 - Are you sure you want to delete %1's %2 directory? Biztosan törölni akarja a %1's %2 mappát? @@ -264,205 +216,285 @@ MainWindow - Open/Add Elf Folder ELF Mappa Megnyitása/Hozzáadása - Install Packages (PKG) PKG-k Telepítése (PKG) - Boot Game Játék Indítása - Check for Updates Frissítések keresése - About shadPS4 A shadPS4-ről - Configure... Konfigurálás... - Install application from a .pkg file Program telepítése egy .pkg fájlból - Recent Games Legutóbbi Játékok - Exit Kilépés - Exit shadPS4 Kilépés a shadPS4-ből - Exit the application. Lépjen ki a programból. - Show Game List Játék Könyvtár Megjelenítése - Game List Refresh Játék Könyvtár Újratöltése - Tiny Apró - Small Kicsi - Medium Közepes - Large Nagy - List View Lista Nézet - Grid View Rács Nézet - Elf Viewer Elf Nézegető - Game Install Directory Játék Telepítési Mappa - Download Cheats/Patches Csalások / Javítások letöltése - Dump Game List Játéklista Dumpolása - PKG Viewer PKG Nézegető - Search... Keresés... - File Fájl - View Nézet - Game List Icons Játékkönyvtár Ikonok - Game List Mode Játékkönyvtár Nézet - Settings Beállítások - Utils Segédeszközök - Themes Témák - Help Segítség - Dark Sötét - Light Világos - Green Zöld - Blue Kék - Violet Ibolya - toolBar Eszköztár + + Game List + Játéklista + + + * Unsupported Vulkan Version + * Nem támogatott Vulkan verzió + + + Download Cheats For All Installed Games + Csalások letöltése minden telepített játékhoz + + + Download Patches For All Games + Javítások letöltése minden játékhoz + + + Download Complete + Letöltés befejezve + + + You have downloaded cheats for all the games you have installed. + Minden elérhető csalás letöltődött az összes telepített játékhoz. + + + Patches Downloaded Successfully! + Javítások sikeresen letöltve! + + + All Patches available for all games have been downloaded. + Az összes játékhoz elérhető javítás letöltésre került. + + + Games: + Játékok: + + + PKG File (*.PKG) + PKG fájl (*.PKG) + + + ELF files (*.bin *.elf *.oelf) + ELF fájlok (*.bin *.elf *.oelf) + + + Game Boot + Játék indító + + + Only one file can be selected! + Csak egy fájl választható ki! + + + PKG Extraction + PKG kicsomagolás + + + Patch detected! + Frissítés észlelve! + + + PKG and Game versions match: + A PKG és a játék verziói egyeznek: + + + Would you like to overwrite? + Szeretné felülírni? + + + PKG Version %1 is older than installed version: + A(z) %1-es PKG verzió régebbi, mint a telepített verzió: + + + Game is installed: + A játék telepítve van: + + + Would you like to install Patch: + Szeretné telepíteni a frissítést: + + + DLC Installation + DLC Telepítés + + + Would you like to install DLC: %1? + Szeretné telepíteni a %1 DLC-t? + + + DLC already installed: + DLC már telepítve: + + + Game already installed + A játék már telepítve van + + + PKG is a patch, please install the game first! + A PKG egy javítás, először telepítsd a játékot! + + + PKG ERROR + PKG HIBA + + + Extracting PKG %1/%2 + PKG kicsomagolása %1/%2 + + + Extraction Finished + Kicsomagolás befejezve + + + Game successfully installed at %1 + A játék sikeresen telepítve itt: %1 + + + File doesn't appear to be a valid PKG file + A fájl nem tűnik érvényes PKG fájlnak + PKGViewer - Open Folder Mappa Megnyitása @@ -470,7 +502,6 @@ TrophyViewer - Trophy Viewer Trófeák Megtekintése @@ -478,1029 +509,700 @@ SettingsDialog - Settings Beállítások - General Általános - System Rendszer - Console Language A Konzol Nyelvezete - Emulator Language Az Emulátor Nyelvezete - Emulator Emulátor - Enable Fullscreen Teljes Képernyő Engedélyezése - Enable Separate Update Folder Külön Frissítési Mappa Engedélyezése - Show Splash Indítóképernyő Mutatása - Is PS4 Pro PS4 Pro mód - Enable Discord Rich Presence A Discord Rich Presence engedélyezése - Username Felhasználónév - Trophy Key Trophy Key - Trophy Trophy - Logger Naplózó - Log Type Naplózási Típus - Log Filter Naplózási Filter - Input Bemenet - Cursor Kurzor - Hide Cursor Kurzor elrejtése - Hide Cursor Idle Timeout Kurzor inaktivitási időtúllépés - s s - Controller Kontroller - Back Button Behavior Vissza gomb Viselkedése - Graphics Grafika - Graphics Device Grafikai Eszköz - Width Szélesség - Height Magasság - Vblank Divider Vblank Elosztó - Advanced Haladó - Enable Shaders Dumping Shader Dumpolás Engedélyezése - Enable NULL GPU NULL GPU Engedélyezése - Paths Útvonalak - Game Folders Játékmappák - Add... Hozzáadás... - Remove Eltávolítás - Debug Debugolás - Enable Debug Dumping Debug Dumpolás Engedélyezése - Enable Vulkan Validation Layers Vulkan Validációs Rétegek Engedélyezése - Enable Vulkan Synchronization Validation Vulkan Szinkronizálás Validáció - Enable RenderDoc Debugging RenderDoc Debugolás Engedélyezése - Update Frissítés - Check for Updates at Startup Frissítések keresése indításkor - Update Channel Frissítési Csatorna - Check for Updates Frissítések keresése - GUI Settings GUI Beállítások - Disable Trophy Pop-ups Disable Trophy Pop-ups - Play title music Címzene lejátszása - Update Compatibility Database On Startup Update Compatibility Database On Startup - Game Compatibility Game Compatibility - Display Compatibility Data Display Compatibility Data - Update Compatibility Database Update Compatibility Database - Volume Hangerő - Audio Backend Audio Backend - - - MainWindow - - Game List - Játéklista - - - - * Unsupported Vulkan Version - * Nem támogatott Vulkan verzió - - - - Download Cheats For All Installed Games - Csalások letöltése minden telepített játékhoz - - - - Download Patches For All Games - Javítások letöltése minden játékhoz - - - - Download Complete - Letöltés befejezve - - - - You have downloaded cheats for all the games you have installed. - Minden elérhető csalás letöltődött az összes telepített játékhoz. - - - - Patches Downloaded Successfully! - Javítások sikeresen letöltve! - - - - All Patches available for all games have been downloaded. - Az összes játékhoz elérhető javítás letöltésre került. - - - - Games: - Játékok: - - - - PKG File (*.PKG) - PKG fájl (*.PKG) - - - - ELF files (*.bin *.elf *.oelf) - ELF fájlok (*.bin *.elf *.oelf) - - - - Game Boot - Játék indító - - - - Only one file can be selected! - Csak egy fájl választható ki! - - - - PKG Extraction - PKG kicsomagolás - - - - Patch detected! - Frissítés észlelve! - - - - PKG and Game versions match: - A PKG és a játék verziói egyeznek: - - - - Would you like to overwrite? - Szeretné felülírni? - - - - PKG Version %1 is older than installed version: - A(z) %1-es PKG verzió régebbi, mint a telepített verzió: - - - - Game is installed: - A játék telepítve van: - - - - Would you like to install Patch: - Szeretné telepíteni a frissítést: - - - - DLC Installation - DLC Telepítés - - - - Would you like to install DLC: %1? - Szeretné telepíteni a %1 DLC-t? - - - - DLC already installed: - DLC már telepítve: - - - - Game already installed - A játék már telepítve van - - - - PKG is a patch, please install the game first! - A PKG egy javítás, először telepítsd a játékot! - - - - PKG ERROR - PKG HIBA - - - - Extracting PKG %1/%2 - PKG kicsomagolása %1/%2 - - - - Extraction Finished - Kicsomagolás befejezve - - - - Game successfully installed at %1 - A játék sikeresen telepítve itt: %1 - - - - File doesn't appear to be a valid PKG file - A fájl nem tűnik érvényes PKG fájlnak - - - - CheatsPatches - - - Cheats / Patches for - Cheats / Patches for - - - - defaultTextEdit_MSG - A csalások/javítások kísérleti jellegűek.\nHasználja őket óvatosan.\n\nTöltse le a csalásokat egyesével a tároló kiválasztásával és a letöltés gombra kattintással.\nA Javítások fül alatt egyszerre letöltheti az összes javítást, majd választhat, melyeket szeretné használni, és elmentheti a választását.\n\nMivel nem mi fejlesztjük a csalásokat/patch-eket,\nkérjük, jelentse a problémákat a csalás szerzőjének.\n\nKészített egy új csalást? Látogasson el ide:\nhttps://github.com/shadps4-emu/ps4_cheats - - - - No Image Available - Nincs elérhető kép - - - - Serial: - Sorozatszám: - - - - Version: - Verzió: - - - - Size: - Méret: - - - - Select Cheat File: - Válaszd ki a csalás fájlt: - - - - Repository: - Tároló: - - - - Download Cheats - Csalások letöltése - - - - Delete File - Fájl törlése - - - - No files selected. - Nincsenek kiválasztott fájlok. - - - - You can delete the cheats you don't want after downloading them. - Törölheted a nem kívánt csalásokat a letöltés után. - - - - Do you want to delete the selected file?\n%1 - Szeretnéd törölni a kiválasztott fájlt?\n%1 - - - - Select Patch File: - Válaszd ki a javítás fájlt: - - - - Download Patches - Javítások letöltése - - - Save Mentés - - Cheats - Csalások - - - - Patches - Javítások - - - - Error - Hiba - - - - No patch selected. - Nincs kiválasztva javítás. - - - - Unable to open files.json for reading. - Nem sikerült megnyitni a files.json fájlt olvasásra. - - - - No patch file found for the current serial. - Nincs található javítási fájl a jelenlegi sorozatszámhoz. - - - - Unable to open the file for reading. - Nem sikerült megnyitni a fájlt olvasásra. - - - - Unable to open the file for writing. - Nem sikerült megnyitni a fájlt írásra. - - - - Failed to parse XML: - XML elemzési hiba: - - - - Success - Siker - - - - Options saved successfully. - A beállítások sikeresen elmentve. - - - - Invalid Source - Érvénytelen forrás - - - - The selected source is invalid. - A kiválasztott forrás érvénytelen. - - - - File Exists - A fájl létezik - - - - File already exists. Do you want to replace it? - A fájl már létezik. Szeretnéd helyettesíteni? - - - - Failed to save file: - Nem sikerült elmenteni a fájlt: - - - - Failed to download file: - Nem sikerült letölteni a fájlt: - - - - Cheats Not Found - Csalások nem találhatóak - - - - CheatsNotFound_MSG - Nincs található csalás ezen a játékverzión ebben a kiválasztott tárolóban, próbálj meg egy másik tárolót vagy a játék egy másik verzióját. - - - - Cheats Downloaded Successfully - Csalások sikeresen letöltve - - - - CheatsDownloadedSuccessfully_MSG - Sikeresen letöltötted a csalásokat ennek a játéknak a verziójához a kiválasztott tárolóból. Próbálhatsz letölteni egy másik tárolóból is, ha az elérhető, akkor a fájl kiválasztásával az is használható lesz. - - - - Failed to save: - Nem sikerült menteni: - - - - Failed to download: - Nem sikerült letölteni: - - - - Download Complete - Letöltés befejezve - - - - DownloadComplete_MSG - Frissítések sikeresen letöltve! Minden elérhető frissítés letöltésre került, nem szükséges egyesével letölteni őket minden játékhoz, mint a csalások esetében. Ha a javítások nem jelennek meg, lehet, hogy nem léteznek a játék adott sorozatszámához és verziójához. - - - - Failed to parse JSON data from HTML. - Nem sikerült az JSON adatok elemzése HTML-ből. - - - - Failed to retrieve HTML page. - Nem sikerült HTML oldal lekérése. - - - - The game is in version: %1 - A játék verziója: %1 - - - - The downloaded patch only works on version: %1 - A letöltött javításhoz a(z) %1 verzió működik - - - - You may need to update your game. - Lehet, hogy frissítened kell a játékodat. - - - - Incompatibility Notice - Inkompatibilitási értesítés - - - - Failed to open file: - Nem sikerült megnyitni a fájlt: - - - - XML ERROR: - XML HIBA: - - - - Failed to open files.json for writing - Nem sikerült megnyitni a files.json fájlt írásra - - - - Author: - Szerző: - - - - Directory does not exist: - A mappa nem létezik: - - - - Failed to open files.json for reading. - Nem sikerült megnyitni a files.json fájlt olvasásra. - - - - Name: - Név: - - - - Can't apply cheats before the game is started - Nem lehet csalásokat alkalmazni, mielőtt a játék elindul. - - - - SettingsDialog - - - Save - Mentés - - - Apply Alkalmaz - Restore Defaults Alapértelmezett értékek visszaállítása - Close Bezárás - Point your mouse at an option to display its description. Helyezze az egérmutatót egy lehetőség fölé, hogy megjelenítse annak leírását. - consoleLanguageGroupBox Konzol nyelve:\nBeállítja a PS4 játék nyelvét.\nAjánlott a játék által támogatott nyelvre állítani, amely régiónként változhat. - emulatorLanguageGroupBox Emulátor nyelve:\nBeállítja az emulátor felhasználói felületének nyelvét. - fullscreenCheckBox Teljes képernyő engedélyezése:\nAutomatikusan teljes képernyőre állítja a játék ablakát.\nEz a F11 billentyű megnyomásával kapcsolható ki/be. - separateUpdatesCheckBox Külön Frissítéi Mappa Engedélyezése:\nEngedélyezi a frissítések külön mappába helyezését, a könnyű kezelésük érdekében. - showSplashCheckBox Indítóképernyő megjelenítése:\nMegjeleníti a játék indítóképernyőjét (különleges képet) a játék elindításakor. - ps4proCheckBox PS4 Pro:\nAz emulátort PS4 PRO-ként kezeli, ami engedélyezhet speciális funkciókat olyan játékokban, amelyek támogatják azt. - discordRPCCheckbox A Discord Rich Presence engedélyezése:\nMegjeleníti az emulator ikonját és a kapcsolódó információkat a Discord profilján. - userName Felhasználónév:\nBeállítja a PS4 fiók felhasználónevét, amelyet egyes játékok megjeleníthetnek. - TrophyKey Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. - logTypeGroupBox Napló típusa:\nBeállítja, hogy szinkronizálja-e a naplóablak kimenetét a teljesítmény érdekében. Ennek kedvezőtlen hatásai lehetnek az emulációra. - logFilter Napló szűrő:\nCsak bizonyos információk megjelenítésére szűri a naplót.\nPéldák: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Szintek: Trace, Debug, Info, Warning, Error, Critical - ebben a sorrendben, egy konkrét szint elnémítja az előtte lévő összes szintet, és naplózza az utána következő szinteket. - updaterGroupBox Frissítés:\nRelease: Hivatalos verziók, amelyeket havonta adnak ki, és amelyek nagyon elavultak lehetnek, de megbízhatóbbak és teszteltek.\nNightly: Fejlesztési verziók, amelyek az összes legújabb funkciót és javítást tartalmazzák, de hibákat tartalmazhatnak és kevésbé stabilak. - GUIgroupBox Játék címzene lejátszása:\nHa a játék támogatja, engedélyezze egy speciális zene lejátszását, amikor a játékot kiválasztja a GUI-ban. - disableTrophycheckBox Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). - hideCursorGroupBox Kurzor elrejtése:\nVálassza ki, mikor tűnjön el az egérmutató:\nSoha: Az egér mindig látható.\nInaktív: Állítson be egy időt, amennyi idő mozdulatlanság után eltűnik.\nMindig: az egér mindig el lesz rejtve. - idleTimeoutGroupBox Állítson be egy időt, ami után egér inaktív állapotban eltűnik. - backButtonBehaviorGroupBox Vissza gomb viselkedés:\nBeállítja a vezérlő vissza gombját, hogy utánozza a PS4 érintőpadján megadott pozíció megérintését. - enableCompatibilityCheckBox Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. - checkCompatibilityOnStartupCheckBox Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. - updateCompatibilityButton Update Compatibility Database:\nImmediately update the compatibility database. - Never Soha - Idle Inaktív - Always Mindig - Touchpad Left Érintőpad Bal - Touchpad Right Érintőpad Jobb - Touchpad Center Érintőpad Közép - None Semmi - graphicsAdapterGroupBox Grafikus eszköz:\nTöbb GPU-s rendszereken válassza ki, melyik GPU-t használja az emulátor a legördülő listából,\nvagy válassza az "Auto Select" lehetőséget, hogy automatikusan kiválassza azt. - resolutionLayout Szélesség/Magasság:\nBeállítja az emulátor ablakának méretét induláskor, amely a játék során átméretezhető.\nEz különbözik a játékbeli felbontástól. - heightDivider Vblank elosztó:\nAz emulátor frissítési sebessége e számot megszorozva működik. Ennek megváltoztatása kedvezőtlen hatásokat okozhat, például növelheti a játék sebességét, vagy megszakíthat kritikus játékfunkciókat, amelyek nem számítanak arra, hogy ez megváltozik! - dumpShadersCheckBox Shader dumping engedélyezése:\nMűszaki hibaelhárítás céljából a játékok shaderjeit elmenti egy mappába, ahogy renderelődnek. - nullGpuCheckBox Null GPU engedélyezése:\nMűszaki hibaelhárítás céljából letiltja a játék renderelését, mintha nem lenne grafikus kártya. - gameFoldersBox Játék mappák:\nA mappák listája, ahol telepített játékok vannak. - addFolderButton Hozzáadás:\nHozzon létre egy mappát a listában. - removeFolderButton Eltávolítás:\nTávolítson el egy mappát a listából. - debugDump Debug dumpolás engedélyezése:\nElmenti a futó PS4 program import- és exportszimbólumait, valamint a fájl fejlécinformációit egy könyvtárba. - vkValidationCheckBox Vulkan validációs rétegek engedélyezése:\nEngedélyezi a Vulkan renderelő állapotának validálását és információk naplózását annak belső állapotáról. Ez csökkenti a teljesítményt és valószínűleg megváltoztatja az emuláció viselkedését. - vkSyncValidationCheckBox Vulkan szinkronizációs validáció engedélyezése:\nEngedélyezi a Vulkan renderelési feladatok időzítésének validálását. Ez csökkenti a teljesítményt és valószínűleg megváltoztatja az emuláció viselkedését. - rdocCheckBox RenderDoc hibakeresés engedélyezése:\nHa engedélyezve van, az emulátor kompatibilitást biztosít a Renderdoc számára, hogy lehetővé tegye a jelenleg renderelt keret rögzítését és elemzését. + + CheatsPatches + + Cheats / Patches for + Cheats / Patches for + + + defaultTextEdit_MSG + A csalások/javítások kísérleti jellegűek.\nHasználja őket óvatosan.\n\nTöltse le a csalásokat egyesével a tároló kiválasztásával és a letöltés gombra kattintással.\nA Javítások fül alatt egyszerre letöltheti az összes javítást, majd választhat, melyeket szeretné használni, és elmentheti a választását.\n\nMivel nem mi fejlesztjük a csalásokat/patch-eket,\nkérjük, jelentse a problémákat a csalás szerzőjének.\n\nKészített egy új csalást? Látogasson el ide:\nhttps://github.com/shadps4-emu/ps4_cheats + + + No Image Available + Nincs elérhető kép + + + Serial: + Sorozatszám: + + + Version: + Verzió: + + + Size: + Méret: + + + Select Cheat File: + Válaszd ki a csalás fájlt: + + + Repository: + Tároló: + + + Download Cheats + Csalások letöltése + + + Delete File + Fájl törlése + + + No files selected. + Nincsenek kiválasztott fájlok. + + + You can delete the cheats you don't want after downloading them. + Törölheted a nem kívánt csalásokat a letöltés után. + + + Do you want to delete the selected file?\n%1 + Szeretnéd törölni a kiválasztott fájlt?\n%1 + + + Select Patch File: + Válaszd ki a javítás fájlt: + + + Download Patches + Javítások letöltése + + + Save + Mentés + + + Cheats + Csalások + + + Patches + Javítások + + + Error + Hiba + + + No patch selected. + Nincs kiválasztva javítás. + + + Unable to open files.json for reading. + Nem sikerült megnyitni a files.json fájlt olvasásra. + + + No patch file found for the current serial. + Nincs található javítási fájl a jelenlegi sorozatszámhoz. + + + Unable to open the file for reading. + Nem sikerült megnyitni a fájlt olvasásra. + + + Unable to open the file for writing. + Nem sikerült megnyitni a fájlt írásra. + + + Failed to parse XML: + XML elemzési hiba: + + + Success + Siker + + + Options saved successfully. + A beállítások sikeresen elmentve. + + + Invalid Source + Érvénytelen forrás + + + The selected source is invalid. + A kiválasztott forrás érvénytelen. + + + File Exists + A fájl létezik + + + File already exists. Do you want to replace it? + A fájl már létezik. Szeretnéd helyettesíteni? + + + Failed to save file: + Nem sikerült elmenteni a fájlt: + + + Failed to download file: + Nem sikerült letölteni a fájlt: + + + Cheats Not Found + Csalások nem találhatóak + + + CheatsNotFound_MSG + Nincs található csalás ezen a játékverzión ebben a kiválasztott tárolóban, próbálj meg egy másik tárolót vagy a játék egy másik verzióját. + + + Cheats Downloaded Successfully + Csalások sikeresen letöltve + + + CheatsDownloadedSuccessfully_MSG + Sikeresen letöltötted a csalásokat ennek a játéknak a verziójához a kiválasztott tárolóból. Próbálhatsz letölteni egy másik tárolóból is, ha az elérhető, akkor a fájl kiválasztásával az is használható lesz. + + + Failed to save: + Nem sikerült menteni: + + + Failed to download: + Nem sikerült letölteni: + + + Download Complete + Letöltés befejezve + + + DownloadComplete_MSG + Frissítések sikeresen letöltve! Minden elérhető frissítés letöltésre került, nem szükséges egyesével letölteni őket minden játékhoz, mint a csalások esetében. Ha a javítások nem jelennek meg, lehet, hogy nem léteznek a játék adott sorozatszámához és verziójához. + + + Failed to parse JSON data from HTML. + Nem sikerült az JSON adatok elemzése HTML-ből. + + + Failed to retrieve HTML page. + Nem sikerült HTML oldal lekérése. + + + The game is in version: %1 + A játék verziója: %1 + + + The downloaded patch only works on version: %1 + A letöltött javításhoz a(z) %1 verzió működik + + + You may need to update your game. + Lehet, hogy frissítened kell a játékodat. + + + Incompatibility Notice + Inkompatibilitási értesítés + + + Failed to open file: + Nem sikerült megnyitni a fájlt: + + + XML ERROR: + XML HIBA: + + + Failed to open files.json for writing + Nem sikerült megnyitni a files.json fájlt írásra + + + Author: + Szerző: + + + Directory does not exist: + A mappa nem létezik: + + + Failed to open files.json for reading. + Nem sikerült megnyitni a files.json fájlt olvasásra. + + + Name: + Név: + + + Can't apply cheats before the game is started + Nem lehet csalásokat alkalmazni, mielőtt a játék elindul. + + GameListFrame - Icon Ikon - Name Név - Serial Sorozatszám - Compatibility Compatibility - Region Régió - Firmware Firmware - Size Méret - Version Verzió - Path Útvonal - Play Time Játékidő - Never Played Never Played - h h - m m - s s - Compatibility is untested Compatibility is untested - Game does not initialize properly / crashes the emulator Game does not initialize properly / crashes the emulator - Game boots, but only displays a blank screen Game boots, but only displays a blank screen - Game displays an image but does not go past the menu Game displays an image but does not go past the menu - Game has game-breaking glitches or unplayable performance Game has game-breaking glitches or unplayable performance - Game can be completed with playable performance and no major glitches Game can be completed with playable performance and no major glitches @@ -1508,127 +1210,102 @@ CheckUpdate - Auto Updater Automatikus frissítő - Error Hiba - Network error: Hálózati hiba: - Failed to parse update information. A frissítési információk elemzése sikertelen. - No pre-releases found. Nem található előzetes kiadás. - Invalid release data. Érvénytelen kiadási adatok. - No download URL found for the specified asset. Nincs letöltési URL a megadott eszközhöz. - Your version is already up to date! A verziód már naprakész! - Update Available Frissítés elérhető - Update Channel Frissítési Csatorna - Current Version Jelenlegi verzió - Latest Version Új verzió - Do you want to update? Szeretnéd frissíteni? - Show Changelog Változások megjelenítése - Check for Updates at Startup Frissítések keresése indításkor - Update Frissítés - No Mégse - Hide Changelog Változások elrejtése - Changes Változások - Network error occurred while trying to access the URL Hálózati hiba történt az URL elérésekor - Download Complete Letöltés kész - The update has been downloaded, press OK to install. A frissítés letöltődött, nyomja meg az OK gombot az telepítéshez. - Failed to save the update file at A frissítési fájl mentése nem sikerült a következő helyre - Starting Update... Frissítés indítása... - Failed to create the update script file A frissítési szkript fájl létrehozása nem sikerült @@ -1636,29 +1313,24 @@ GameListUtils - B B - KB KB - MB MB - GB GB - TB TB - + \ No newline at end of file diff --git a/src/qt_gui/translations/id.ts b/src/qt_gui/translations/id.ts index bee61083c..7a1391c0e 100644 --- a/src/qt_gui/translations/id.ts +++ b/src/qt_gui/translations/id.ts @@ -6,22 +6,18 @@ AboutDialog - About shadPS4 About shadPS4 - shadPS4 shadPS4 - shadPS4 is an experimental open-source emulator for the PlayStation 4. shadPS4 is an experimental open-source emulator for the PlayStation 4. - This software should not be used to play games you have not legally obtained. This software should not be used to play games you have not legally obtained. @@ -29,7 +25,6 @@ ElfViewer - Open Folder Open Folder @@ -37,17 +32,14 @@ GameInfoClass - Loading game list, please wait :3 Loading game list, please wait :3 - Cancel Cancel - Loading... Loading... @@ -55,12 +47,10 @@ InstallDirSelect - shadPS4 - Choose directory shadPS4 - Choose directory - Select which directory you want to install to. Select which directory you want to install to. @@ -68,27 +58,22 @@ GameInstallDialog - shadPS4 - Choose directory shadPS4 - Choose directory - Directory to install games Directory to install games - Browse Browse - Error Error - The value for location to install games is not valid. The value for location to install games is not valid. @@ -96,167 +81,134 @@ GuiContextMenus - Create Shortcut Create Shortcut - Cheats / Patches Cheat / Patch - SFO Viewer SFO Viewer - Trophy Viewer Trophy Viewer - Open Folder... Buka Folder... - Open Game Folder Buka Folder Game - Open Save Data Folder Buka Folder Data Simpanan - Open Log Folder Buka Folder Log - Copy info... Copy info... - Copy Name Copy Name - Copy Serial Copy Serial - Copy All Copy All - Delete... Delete... - Delete Game Delete Game - Delete Update Delete Update - Delete DLC Delete DLC - Compatibility... Compatibility... - Update database Update database - View report View report - Submit a report Submit a report - Shortcut creation Shortcut creation - Shortcut created successfully! Shortcut created successfully! - Error Error - Error creating shortcut! Error creating shortcut! - Install PKG Install PKG - Game Game - requiresEnableSeparateUpdateFolder_MSG This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. - This game has no update to delete! This game has no update to delete! - - + Update Update - This game has no DLC to delete! This game has no DLC to delete! - DLC DLC - Delete %1 Delete %1 - Are you sure you want to delete %1's %2 directory? Are you sure you want to delete %1's %2 directory? @@ -264,205 +216,285 @@ MainWindow - Open/Add Elf Folder Open/Add Elf Folder - Install Packages (PKG) Install Packages (PKG) - Boot Game Boot Game - Check for Updates Periksa pembaruan - About shadPS4 About shadPS4 - Configure... Configure... - Install application from a .pkg file Install application from a .pkg file - Recent Games Recent Games - Exit Exit - Exit shadPS4 Exit shadPS4 - Exit the application. Exit the application. - Show Game List Show Game List - Game List Refresh Game List Refresh - Tiny Tiny - Small Small - Medium Medium - Large Large - List View List View - Grid View Grid View - Elf Viewer Elf Viewer - Game Install Directory Game Install Directory - Download Cheats/Patches Unduh Cheat / Patch - Dump Game List Dump Game List - PKG Viewer PKG Viewer - Search... Search... - File File - View View - Game List Icons Game List Icons - Game List Mode Game List Mode - Settings Settings - Utils Utils - Themes Themes - Help Bantuan - Dark Dark - Light Light - Green Green - Blue Blue - Violet Violet - toolBar toolBar + + Game List + Daftar game + + + * Unsupported Vulkan Version + * Versi Vulkan Tidak Didukung + + + Download Cheats For All Installed Games + Unduh Cheat Untuk Semua Game Yang Terpasang + + + Download Patches For All Games + Unduh Patch Untuk Semua Game + + + Download Complete + Unduhan Selesai + + + You have downloaded cheats for all the games you have installed. + Anda telah mengunduh cheat untuk semua game yang terpasang. + + + Patches Downloaded Successfully! + Patch Berhasil Diunduh! + + + All Patches available for all games have been downloaded. + Semua Patch yang tersedia untuk semua game telah diunduh. + + + Games: + Game: + + + PKG File (*.PKG) + File PKG (*.PKG) + + + ELF files (*.bin *.elf *.oelf) + File ELF (*.bin *.elf *.oelf) + + + Game Boot + Boot Game + + + Only one file can be selected! + Hanya satu file yang bisa dipilih! + + + PKG Extraction + Ekstraksi PKG + + + Patch detected! + Patch terdeteksi! + + + PKG and Game versions match: + Versi PKG dan Game cocok: + + + Would you like to overwrite? + Apakah Anda ingin menimpa? + + + PKG Version %1 is older than installed version: + Versi PKG %1 lebih lama dari versi yang terpasang: + + + Game is installed: + Game telah terpasang: + + + Would you like to install Patch: + Apakah Anda ingin menginstal patch: + + + DLC Installation + Instalasi DLC + + + Would you like to install DLC: %1? + Apakah Anda ingin menginstal DLC: %1? + + + DLC already installed: + DLC sudah terpasang: + + + Game already installed + Game sudah terpasang + + + PKG is a patch, please install the game first! + PKG adalah patch, harap pasang game terlebih dahulu! + + + PKG ERROR + KESALAHAN PKG + + + Extracting PKG %1/%2 + Mengekstrak PKG %1/%2 + + + Extraction Finished + Ekstraksi Selesai + + + Game successfully installed at %1 + Game berhasil dipasang di %1 + + + File doesn't appear to be a valid PKG file + File tampaknya bukan file PKG yang valid + PKGViewer - Open Folder Open Folder @@ -470,7 +502,6 @@ TrophyViewer - Trophy Viewer Trophy Viewer @@ -478,1029 +509,700 @@ SettingsDialog - Settings Settings - General General - System System - Console Language Console Language - Emulator Language Emulator Language - Emulator Emulator - Enable Fullscreen Enable Fullscreen - Enable Separate Update Folder Enable Separate Update Folder - Show Splash Show Splash - Is PS4 Pro Is PS4 Pro - Enable Discord Rich Presence Aktifkan Discord Rich Presence - Username Username - Trophy Key Trophy Key - Trophy Trophy - Logger Logger - Log Type Log Type - Log Filter Log Filter - Input Masukan - Cursor Kursor - Hide Cursor Sembunyikan kursor - Hide Cursor Idle Timeout Batas waktu sembunyikan kursor tidak aktif - s s - Controller Pengontrol - Back Button Behavior Perilaku tombol kembali - Graphics Graphics - Graphics Device Graphics Device - Width Width - Height Height - Vblank Divider Vblank Divider - Advanced Advanced - Enable Shaders Dumping Enable Shaders Dumping - Enable NULL GPU Enable NULL GPU - Paths Jalur - Game Folders Folder Permainan - Add... Tambah... - Remove Hapus - Debug Debug - Enable Debug Dumping Enable Debug Dumping - Enable Vulkan Validation Layers Enable Vulkan Validation Layers - Enable Vulkan Synchronization Validation Enable Vulkan Synchronization Validation - Enable RenderDoc Debugging Enable RenderDoc Debugging - Update Pembaruan - Check for Updates at Startup Periksa pembaruan saat mulai - Update Channel Saluran Pembaruan - Check for Updates Periksa pembaruan - GUI Settings Pengaturan GUI - Disable Trophy Pop-ups Disable Trophy Pop-ups - Play title music Putar musik judul - Update Compatibility Database On Startup Update Compatibility Database On Startup - Game Compatibility Game Compatibility - Display Compatibility Data Display Compatibility Data - Update Compatibility Database Update Compatibility Database - Volume Volume - Audio Backend Audio Backend - - - MainWindow - - Game List - Daftar game - - - - * Unsupported Vulkan Version - * Versi Vulkan Tidak Didukung - - - - Download Cheats For All Installed Games - Unduh Cheat Untuk Semua Game Yang Terpasang - - - - Download Patches For All Games - Unduh Patch Untuk Semua Game - - - - Download Complete - Unduhan Selesai - - - - You have downloaded cheats for all the games you have installed. - Anda telah mengunduh cheat untuk semua game yang terpasang. - - - - Patches Downloaded Successfully! - Patch Berhasil Diunduh! - - - - All Patches available for all games have been downloaded. - Semua Patch yang tersedia untuk semua game telah diunduh. - - - - Games: - Game: - - - - PKG File (*.PKG) - File PKG (*.PKG) - - - - ELF files (*.bin *.elf *.oelf) - File ELF (*.bin *.elf *.oelf) - - - - Game Boot - Boot Game - - - - Only one file can be selected! - Hanya satu file yang bisa dipilih! - - - - PKG Extraction - Ekstraksi PKG - - - - Patch detected! - Patch terdeteksi! - - - - PKG and Game versions match: - Versi PKG dan Game cocok: - - - - Would you like to overwrite? - Apakah Anda ingin menimpa? - - - - PKG Version %1 is older than installed version: - Versi PKG %1 lebih lama dari versi yang terpasang: - - - - Game is installed: - Game telah terpasang: - - - - Would you like to install Patch: - Apakah Anda ingin menginstal patch: - - - - DLC Installation - Instalasi DLC - - - - Would you like to install DLC: %1? - Apakah Anda ingin menginstal DLC: %1? - - - - DLC already installed: - DLC sudah terpasang: - - - - Game already installed - Game sudah terpasang - - - - PKG is a patch, please install the game first! - PKG adalah patch, harap pasang game terlebih dahulu! - - - - PKG ERROR - KESALAHAN PKG - - - - Extracting PKG %1/%2 - Mengekstrak PKG %1/%2 - - - - Extraction Finished - Ekstraksi Selesai - - - - Game successfully installed at %1 - Game berhasil dipasang di %1 - - - - File doesn't appear to be a valid PKG file - File tampaknya bukan file PKG yang valid - - - - CheatsPatches - - - Cheats / Patches for - Cheats / Patches for - - - - defaultTextEdit_MSG - Cheats/Patches bersifat eksperimental.\nGunakan dengan hati-hati.\n\nUnduh cheats satu per satu dengan memilih repositori dan mengklik tombol unduh.\nDi tab Patches, Anda dapat mengunduh semua patch sekaligus, memilih yang ingin digunakan, dan menyimpan pilihan Anda.\n\nKarena kami tidak mengembangkan Cheats/Patches,\nharap laporkan masalah kepada pembuat cheat.\n\nMembuat cheat baru? Kunjungi:\nhttps://github.com/shadps4-emu/ps4_cheats - - - - No Image Available - Tidak Ada Gambar Tersedia - - - - Serial: - Serial: - - - - Version: - Versi: - - - - Size: - Ukuran: - - - - Select Cheat File: - Pilih File Cheat: - - - - Repository: - Repositori: - - - - Download Cheats - Unduh Cheat - - - - Delete File - Hapus File - - - - No files selected. - Tidak ada file yang dipilih. - - - - You can delete the cheats you don't want after downloading them. - Anda dapat menghapus cheat yang tidak Anda inginkan setelah mengunduhnya. - - - - Do you want to delete the selected file?\n%1 - Apakah Anda ingin menghapus berkas yang dipilih?\n%1 - - - - Select Patch File: - Pilih File Patch: - - - - Download Patches - Unduh Patch - - - Save Simpan - - Cheats - Cheat - - - - Patches - Patch - - - - Error - Kesalahan - - - - No patch selected. - Tidak ada patch yang dipilih. - - - - Unable to open files.json for reading. - Tidak dapat membuka files.json untuk dibaca. - - - - No patch file found for the current serial. - Tidak ada file patch ditemukan untuk serial saat ini. - - - - Unable to open the file for reading. - Tidak dapat membuka file untuk dibaca. - - - - Unable to open the file for writing. - Tidak dapat membuka file untuk menulis. - - - - Failed to parse XML: - Gagal menganalisis XML: - - - - Success - Sukses - - - - Options saved successfully. - Opsi berhasil disimpan. - - - - Invalid Source - Sumber Tidak Valid - - - - The selected source is invalid. - Sumber yang dipilih tidak valid. - - - - File Exists - File Ada - - - - File already exists. Do you want to replace it? - File sudah ada. Apakah Anda ingin menggantinya? - - - - Failed to save file: - Gagal menyimpan file: - - - - Failed to download file: - Gagal mengunduh file: - - - - Cheats Not Found - Cheat Tidak Ditemukan - - - - CheatsNotFound_MSG - Cheat tidak ditemukan untuk game ini dalam versi repositori yang dipilih,cobalah repositori lain atau versi game yang berbeda. - - - - Cheats Downloaded Successfully - Cheat Berhasil Diunduh - - - - CheatsDownloadedSuccessfully_MSG - Anda telah berhasil mengunduh cheat untuk versi game ini dari repositori yang dipilih. Anda bisa mencoba mengunduh dari repositori lain, jika tersedia akan juga memungkinkan untuk menggunakannya dengan memilih file dari daftar. - - - - Failed to save: - Gagal menyimpan: - - - - Failed to download: - Gagal mengunduh: - - - - Download Complete - Unduhan Selesai - - - - DownloadComplete_MSG - Patch Berhasil Diunduh! Semua Patch yang tersedia untuk semua game telah diunduh, tidak perlu mengunduhnya satu per satu seperti yang terjadi pada Cheat. Jika patch tidak muncul, mungkin patch tersebut tidak ada untuk nomor seri dan versi game yang spesifik. - - - - Failed to parse JSON data from HTML. - Gagal menganalisis data JSON dari HTML. - - - - Failed to retrieve HTML page. - Gagal mengambil halaman HTML. - - - - The game is in version: %1 - Permainan berada di versi: %1 - - - - The downloaded patch only works on version: %1 - Patch yang diunduh hanya berfungsi pada versi: %1 - - - - You may need to update your game. - Anda mungkin perlu memperbarui permainan Anda. - - - - Incompatibility Notice - Pemberitahuan Ketidakcocokan - - - - Failed to open file: - Gagal membuka file: - - - - XML ERROR: - KESALAHAN XML: - - - - Failed to open files.json for writing - Gagal membuka files.json untuk menulis - - - - Author: - Penulis: - - - - Directory does not exist: - Direktori tidak ada: - - - - Failed to open files.json for reading. - Gagal membuka files.json untuk dibaca. - - - - Name: - Nama: - - - - Can't apply cheats before the game is started - Tidak bisa menerapkan cheat sebelum permainan dimulai. - - - - SettingsDialog - - - Save - Simpan - - - Apply Terapkan - Restore Defaults Kembalikan Pengaturan Default - Close Tutup - Point your mouse at an option to display its description. Arahkan mouse Anda pada opsi untuk menampilkan deskripsinya. - consoleLanguageGroupBox Bahasa Konsol:\nMenetapkan bahasa yang digunakan oleh permainan PS4.\nDisarankan untuk mengatur ini ke bahasa yang didukung oleh permainan, yang dapat bervariasi berdasarkan wilayah. - emulatorLanguageGroupBox Bahasa Emulator:\nMenetapkan bahasa antarmuka pengguna emulator. - fullscreenCheckBox Aktifkan Mode Layar Penuh:\nSecara otomatis menempatkan jendela permainan dalam mode layar penuh.\nIni dapat dinonaktifkan dengan menekan tombol F11. - separateUpdatesCheckBox Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. - showSplashCheckBox Tampilkan Layar Pembuka:\nMenampilkan layar pembuka permainan (gambar khusus) saat permainan dimulai. - ps4proCheckBox Adalah PS4 Pro:\nMembuat emulator berfungsi sebagai PS4 PRO, yang mungkin mengaktifkan fitur khusus dalam permainan yang mendukungnya. - discordRPCCheckbox Aktifkan Discord Rich Presence:\nMenampilkan ikon emulator dan informasi relevan di profil Discord Anda. - userName Nama Pengguna:\nMenetapkan nama pengguna akun PS4, yang mungkin ditampilkan oleh beberapa permainan. - TrophyKey Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. - logTypeGroupBox Jenis Log:\nMenetapkan apakah untuk menyinkronkan output jendela log untuk kinerja. Dapat memiliki efek buruk pada emulasi. - logFilter Filter Log:\nMenyaring log untuk hanya mencetak informasi tertentu.\nContoh: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Tingkatan: Trace, Debug, Info, Warning, Error, Critical - dalam urutan ini, tingkat tertentu membungkam semua tingkat sebelumnya dalam daftar dan mencatat setiap tingkat setelahnya. - updaterGroupBox Pembaruan:\nRelease: Versi resmi yang dirilis setiap bulan yang mungkin sangat ketinggalan zaman, tetapi lebih dapat diandalkan dan teruji.\nNightly: Versi pengembangan yang memiliki semua fitur dan perbaikan terbaru, tetapi mungkin mengandung bug dan kurang stabil. - GUIgroupBox Putar Musik Judul Permainan:\nJika permainan mendukungnya, aktifkan pemutaran musik khusus saat memilih permainan di GUI. - disableTrophycheckBox Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). - hideCursorGroupBox Sembunyikan Kursor:\nPilih kapan kursor akan menghilang:\nTidak Pernah: Anda akan selalu melihat mouse.\nTidak Aktif: Tetapkan waktu untuk menghilang setelah tidak aktif.\nSelalu: Anda tidak akan pernah melihat mouse. - idleTimeoutGroupBox Tetapkan waktu untuk mouse menghilang setelah tidak aktif. - backButtonBehaviorGroupBox Perilaku Tombol Kembali:\nMengatur tombol kembali pada pengontrol untuk meniru ketukan di posisi yang ditentukan di touchpad PS4. - enableCompatibilityCheckBox Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. - checkCompatibilityOnStartupCheckBox Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. - updateCompatibilityButton Update Compatibility Database:\nImmediately update the compatibility database. - Never Tidak Pernah - Idle Diam - Always Selalu - Touchpad Left Touchpad Kiri - Touchpad Right Touchpad Kanan - Touchpad Center Pusat Touchpad - None Tidak Ada - graphicsAdapterGroupBox Perangkat Grafis:\nPada sistem GPU ganda, pilih GPU yang akan digunakan emulator dari daftar dropdown,\natau pilih "Auto Select" untuk menentukan secara otomatis. - resolutionLayout Lebar/Tinggi:\nMenetapkan ukuran jendela emulator saat diluncurkan, yang dapat diubah ukurannya selama permainan.\nIni berbeda dari resolusi dalam permainan. - heightDivider Pembagi Vblank:\nKecepatan bingkai di mana emulator menyegarkan dikalikan dengan angka ini. Mengubah ini dapat memiliki efek buruk, seperti meningkatkan kecepatan permainan, atau merusak fungsi kritis permainan yang tidak mengharapkan ini berubah! - dumpShadersCheckBox Aktifkan Pembuangan Shader:\nUntuk tujuan debugging teknis, menyimpan shader permainan ke folder saat mereka dirender. - nullGpuCheckBox Aktifkan GPU Null:\nUntuk tujuan debugging teknis, menonaktifkan rendering permainan seolah-olah tidak ada kartu grafis. - gameFoldersBox Folder Permainan:\nDaftar folder untuk memeriksa permainan yang diinstal. - addFolderButton Tambah:\nTambahkan folder ke daftar. - removeFolderButton Hapus:\nHapus folder dari daftar. - debugDump Aktifkan Pembuangan Debug:\nMenyimpan simbol impor dan ekspor serta informasi header file dari program PS4 yang sedang berjalan ke direktori. - vkValidationCheckBox Aktifkan Vulkan Validation Layers:\nMengaktifkan sistem yang memvalidasi status penggambaran Vulkan dan mencatat informasi tentang status internalnya. Ini akan mengurangi kinerja dan kemungkinan mengubah perilaku emulasi. - vkSyncValidationCheckBox Aktifkan Vulkan Synchronization Validation:\nMengaktifkan sistem yang memvalidasi waktu tugas penggambaran Vulkan. Ini akan mengurangi kinerja dan kemungkinan mengubah perilaku emulasi. - rdocCheckBox Aktifkan Debugging RenderDoc:\nJika diaktifkan, emulator akan menyediakan kompatibilitas dengan Renderdoc untuk memungkinkan pengambilan dan analisis bingkai yang sedang dirender. + + CheatsPatches + + Cheats / Patches for + Cheats / Patches for + + + defaultTextEdit_MSG + Cheats/Patches bersifat eksperimental.\nGunakan dengan hati-hati.\n\nUnduh cheats satu per satu dengan memilih repositori dan mengklik tombol unduh.\nDi tab Patches, Anda dapat mengunduh semua patch sekaligus, memilih yang ingin digunakan, dan menyimpan pilihan Anda.\n\nKarena kami tidak mengembangkan Cheats/Patches,\nharap laporkan masalah kepada pembuat cheat.\n\nMembuat cheat baru? Kunjungi:\nhttps://github.com/shadps4-emu/ps4_cheats + + + No Image Available + Tidak Ada Gambar Tersedia + + + Serial: + Serial: + + + Version: + Versi: + + + Size: + Ukuran: + + + Select Cheat File: + Pilih File Cheat: + + + Repository: + Repositori: + + + Download Cheats + Unduh Cheat + + + Delete File + Hapus File + + + No files selected. + Tidak ada file yang dipilih. + + + You can delete the cheats you don't want after downloading them. + Anda dapat menghapus cheat yang tidak Anda inginkan setelah mengunduhnya. + + + Do you want to delete the selected file?\n%1 + Apakah Anda ingin menghapus berkas yang dipilih?\n%1 + + + Select Patch File: + Pilih File Patch: + + + Download Patches + Unduh Patch + + + Save + Simpan + + + Cheats + Cheat + + + Patches + Patch + + + Error + Kesalahan + + + No patch selected. + Tidak ada patch yang dipilih. + + + Unable to open files.json for reading. + Tidak dapat membuka files.json untuk dibaca. + + + No patch file found for the current serial. + Tidak ada file patch ditemukan untuk serial saat ini. + + + Unable to open the file for reading. + Tidak dapat membuka file untuk dibaca. + + + Unable to open the file for writing. + Tidak dapat membuka file untuk menulis. + + + Failed to parse XML: + Gagal menganalisis XML: + + + Success + Sukses + + + Options saved successfully. + Opsi berhasil disimpan. + + + Invalid Source + Sumber Tidak Valid + + + The selected source is invalid. + Sumber yang dipilih tidak valid. + + + File Exists + File Ada + + + File already exists. Do you want to replace it? + File sudah ada. Apakah Anda ingin menggantinya? + + + Failed to save file: + Gagal menyimpan file: + + + Failed to download file: + Gagal mengunduh file: + + + Cheats Not Found + Cheat Tidak Ditemukan + + + CheatsNotFound_MSG + Cheat tidak ditemukan untuk game ini dalam versi repositori yang dipilih,cobalah repositori lain atau versi game yang berbeda. + + + Cheats Downloaded Successfully + Cheat Berhasil Diunduh + + + CheatsDownloadedSuccessfully_MSG + Anda telah berhasil mengunduh cheat untuk versi game ini dari repositori yang dipilih. Anda bisa mencoba mengunduh dari repositori lain, jika tersedia akan juga memungkinkan untuk menggunakannya dengan memilih file dari daftar. + + + Failed to save: + Gagal menyimpan: + + + Failed to download: + Gagal mengunduh: + + + Download Complete + Unduhan Selesai + + + DownloadComplete_MSG + Patch Berhasil Diunduh! Semua Patch yang tersedia untuk semua game telah diunduh, tidak perlu mengunduhnya satu per satu seperti yang terjadi pada Cheat. Jika patch tidak muncul, mungkin patch tersebut tidak ada untuk nomor seri dan versi game yang spesifik. + + + Failed to parse JSON data from HTML. + Gagal menganalisis data JSON dari HTML. + + + Failed to retrieve HTML page. + Gagal mengambil halaman HTML. + + + The game is in version: %1 + Permainan berada di versi: %1 + + + The downloaded patch only works on version: %1 + Patch yang diunduh hanya berfungsi pada versi: %1 + + + You may need to update your game. + Anda mungkin perlu memperbarui permainan Anda. + + + Incompatibility Notice + Pemberitahuan Ketidakcocokan + + + Failed to open file: + Gagal membuka file: + + + XML ERROR: + KESALAHAN XML: + + + Failed to open files.json for writing + Gagal membuka files.json untuk menulis + + + Author: + Penulis: + + + Directory does not exist: + Direktori tidak ada: + + + Failed to open files.json for reading. + Gagal membuka files.json untuk dibaca. + + + Name: + Nama: + + + Can't apply cheats before the game is started + Tidak bisa menerapkan cheat sebelum permainan dimulai. + + GameListFrame - Icon Ikon - Name Nama - Serial Serial - Compatibility Compatibility - Region Wilayah - Firmware Firmware - Size Ukuran - Version Versi - Path Jalur - Play Time Waktu Bermain - Never Played Never Played - h h - m m - s s - Compatibility is untested Compatibility is untested - Game does not initialize properly / crashes the emulator Game does not initialize properly / crashes the emulator - Game boots, but only displays a blank screen Game boots, but only displays a blank screen - Game displays an image but does not go past the menu Game displays an image but does not go past the menu - Game has game-breaking glitches or unplayable performance Game has game-breaking glitches or unplayable performance - Game can be completed with playable performance and no major glitches Game can be completed with playable performance and no major glitches @@ -1508,127 +1210,102 @@ CheckUpdate - Auto Updater Pembaruan Otomatis - Error Kesalahan - Network error: Kesalahan jaringan: - Failed to parse update information. Gagal memparse informasi pembaruan. - No pre-releases found. Tidak ada pra-rilis yang ditemukan. - Invalid release data. Data rilis tidak valid. - No download URL found for the specified asset. Tidak ada URL unduhan ditemukan untuk aset yang ditentukan. - Your version is already up to date! Versi Anda sudah terbaru! - Update Available Pembaruan Tersedia - Update Channel Saluran Pembaruan - Current Version Versi Saat Ini - Latest Version Versi Terbaru - Do you want to update? Apakah Anda ingin memperbarui? - Show Changelog Tampilkan Catatan Perubahan - Check for Updates at Startup Periksa pembaruan saat mulai - Update Perbarui - No Tidak - Hide Changelog Sembunyikan Catatan Perubahan - Changes Perubahan - Network error occurred while trying to access the URL Kesalahan jaringan terjadi saat mencoba mengakses URL - Download Complete Unduhan Selesai - The update has been downloaded, press OK to install. Pembaruan telah diunduh, tekan OK untuk menginstal. - Failed to save the update file at Gagal menyimpan file pembaruan di - Starting Update... Memulai Pembaruan... - Failed to create the update script file Gagal membuat file skrip pembaruan @@ -1636,29 +1313,24 @@ GameListUtils - B B - KB KB - MB MB - GB GB - TB TB - + \ No newline at end of file diff --git a/src/qt_gui/translations/it.ts b/src/qt_gui/translations/it.ts index 9e375a45e..8bdb7a8fd 100644 --- a/src/qt_gui/translations/it.ts +++ b/src/qt_gui/translations/it.ts @@ -6,22 +6,18 @@ AboutDialog - About shadPS4 Riguardo shadPS4 - shadPS4 shadPS4 - shadPS4 is an experimental open-source emulator for the PlayStation 4. shadPS4 è un emulatore sperimentale open-source per PlayStation 4. - This software should not be used to play games you have not legally obtained. Questo programma non dovrebbe essere utilizzato per riprodurre giochi che non vengono ottenuti legalmente. @@ -29,7 +25,6 @@ ElfViewer - Open Folder Apri Cartella @@ -37,17 +32,14 @@ GameInfoClass - Loading game list, please wait :3 Caricamento lista giochi, attendere :3 - Cancel Annulla - Loading... Caricamento... @@ -55,12 +47,10 @@ InstallDirSelect - shadPS4 - Choose directory shadPS4 - Scegli cartella - Select which directory you want to install to. Seleziona in quale cartella vuoi effettuare l'installazione. @@ -68,27 +58,22 @@ GameInstallDialog - shadPS4 - Choose directory shadPS4 - Scegli cartella - Directory to install games Cartella di installazione dei giochi - Browse Sfoglia - Error Errore - The value for location to install games is not valid. Il valore del percorso di installazione dei giochi non è valido. @@ -96,167 +81,134 @@ GuiContextMenus - Create Shortcut Crea scorciatoia - Cheats / Patches Trucchi / Patch - SFO Viewer Visualizzatore SFO - Trophy Viewer Visualizzatore Trofei - Open Folder... Apri Cartella... - Open Game Folder Apri Cartella del Gioco - Open Save Data Folder Apri Cartella dei Dati di Salvataggio - Open Log Folder Apri Cartella dei Log - Copy info... Copia informazioni... - Copy Name Copia Nome - Copy Serial Copia Seriale - Copy All Copia Tutto - Delete... Elimina... - Delete Game Elimina Gioco - Delete Update Elimina Aggiornamento - Delete DLC Elimina DLC - Compatibility... Compatibility... - Update database Update database - View report View report - Submit a report Submit a report - Shortcut creation Creazione scorciatoia - Shortcut created successfully! Scorciatoia creata con successo! - Error Errore - Error creating shortcut! Errore nella creazione della scorciatoia! - Install PKG Installa PKG - Game Gioco - requiresEnableSeparateUpdateFolder_MSG Questa feature richiede che venga attivata l'opzione "Abilita Cartella Aggiornamenti Separata" per poter funzionare, per favore abilitala. - This game has no update to delete! Questo gioco non ha alcun aggiornamento da eliminare! - - + Update Update - This game has no DLC to delete! Questo gioco non ha alcun DLC da eliminare! - DLC DLC - Delete %1 Elimina %1 - Are you sure you want to delete %1's %2 directory? Sei sicuro di eliminale la cartella %2 di %1? @@ -264,205 +216,285 @@ MainWindow - Open/Add Elf Folder Apri/Aggiungi cartella Elf - Install Packages (PKG) Installa Pacchetti (PKG) - Boot Game Avvia Gioco - Check for Updates Controlla aggiornamenti - About shadPS4 Riguardo a shadPS4 - Configure... Configura... - Install application from a .pkg file Installa applicazione da un file .pkg - Recent Games Giochi Recenti - Exit Uscita - Exit shadPS4 Esci da shadPS4 - Exit the application. Esci dall'applicazione. - Show Game List Mostra Lista Giochi - Game List Refresh Aggiorna Lista Giochi - Tiny Minuscolo - Small Piccolo - Medium Medio - Large Grande - List View Visualizzazione Lista - Grid View Visualizzazione Griglia - Elf Viewer Visualizzatore Elf - Game Install Directory Cartella Installazione Giochi - Download Cheats/Patches Scarica Trucchi/Patch - Dump Game List Scarica Lista Giochi - PKG Viewer Visualizzatore PKG - Search... Cerca... - File File - View Visualizza - Game List Icons Icone Lista Giochi - Game List Mode Modalità Lista Giochi - Settings Impostazioni - Utils Utilità - Themes Temi - Help Aiuto - Dark Scuro - Light Chiaro - Green Verde - Blue Blu - Violet Viola - toolBar Barra strumenti + + Game List + Elenco giochi + + + * Unsupported Vulkan Version + * Versione Vulkan non supportata + + + Download Cheats For All Installed Games + Scarica Trucchi per tutti i giochi installati + + + Download Patches For All Games + Scarica Patch per tutti i giochi + + + Download Complete + Download completato + + + You have downloaded cheats for all the games you have installed. + Hai scaricato trucchi per tutti i giochi installati. + + + Patches Downloaded Successfully! + Patch scaricate con successo! + + + All Patches available for all games have been downloaded. + Tutte le patch disponibili per tutti i giochi sono state scaricate. + + + Games: + Giochi: + + + PKG File (*.PKG) + File PKG (*.PKG) + + + ELF files (*.bin *.elf *.oelf) + File ELF (*.bin *.elf *.oelf) + + + Game Boot + Avvia Gioco + + + Only one file can be selected! + Si può selezionare solo un file! + + + PKG Extraction + Estrazione file PKG + + + Patch detected! + Patch rilevata! + + + PKG and Game versions match: + Le versioni di PKG e del Gioco corrispondono: + + + Would you like to overwrite? + Vuoi sovrascrivere? + + + PKG Version %1 is older than installed version: + La versione PKG %1 è più vecchia rispetto alla versione installata: + + + Game is installed: + Gioco installato: + + + Would you like to install Patch: + Vuoi installare la patch: + + + DLC Installation + Installazione DLC + + + Would you like to install DLC: %1? + Vuoi installare il DLC: %1? + + + DLC already installed: + DLC già installato: + + + Game already installed + Gioco già installato + + + PKG is a patch, please install the game first! + Questo file PKG contiene una patch. Per favore, installa prima il gioco! + + + PKG ERROR + ERRORE PKG + + + Extracting PKG %1/%2 + Estrazione file PKG %1/%2 + + + Extraction Finished + Estrazione Completata + + + Game successfully installed at %1 + Gioco installato correttamente in %1 + + + File doesn't appear to be a valid PKG file + Il file sembra non essere un file PKG valido + PKGViewer - Open Folder Apri Cartella @@ -470,7 +502,6 @@ TrophyViewer - Trophy Viewer Visualizzatore Trofei @@ -478,1029 +509,700 @@ SettingsDialog - Settings Impostazioni - General Generale - System Sistema - Console Language Lingua della console - Emulator Language Lingua dell'emulatore - Emulator Emulatore - Enable Fullscreen Abilita Schermo Intero - Enable Separate Update Folder Abilita Cartella Aggiornamenti Separata - Show Splash Mostra Schermata Iniziale - Is PS4 Pro Modalità Ps4 Pro - Enable Discord Rich Presence Abilita Discord Rich Presence - Username Nome Utente - Trophy Key Trophy Key - Trophy Trophy - Logger Logger - Log Type Tipo di Log - Log Filter Filtro Log - Input Input - Cursor Cursore - Hide Cursor Nascondi Cursore - Hide Cursor Idle Timeout Timeout inattività per nascondere il cursore - s s - Controller Controller - Back Button Behavior Comportamento del pulsante Indietro - Graphics Grafica - Graphics Device Scheda Grafica - Width Larghezza - Height Altezza - Vblank Divider Divisore Vblank - Advanced Avanzate - Enable Shaders Dumping Abilita Dump Shader - Enable NULL GPU Abilita NULL GPU - Paths Percorsi - Game Folders Cartelle di gioco - Add... - Aggiungi... + Aggiungi... - Remove Rimuovi - Debug Debug - Enable Abilita Debug Dumping - Enable Vulkan Validation Layers Abilita Vulkan Validation Layers - Enable Vulkan Synchronization Validation Abilita Vulkan Synchronization Validation - Enable RenderDoc Debugging Abilita RenderDoc Debugging - Update Aggiornamento - Check for Updates at Startup Verifica aggiornamenti all’avvio - Update Channel Canale di Aggiornamento - Check for Updates Controlla aggiornamenti - GUI Settings Impostazioni GUI - Disable Trophy Pop-ups Disabilita Notifica Trofei - Play title music Riproduci musica del titolo - Update Compatibility Database On Startup Aggiorna Database Compatibilità all'Avvio - Game Compatibility Compatibilità Gioco - Display Compatibility Data Mostra Dati Compatibilità - Update Compatibility Database Aggiorna Database Compatibilità - Volume Volume - Audio Backend Audio Backend - - - MainWindow - - Game List - Elenco giochi - - - - * Unsupported Vulkan Version - * Versione Vulkan non supportata - - - - Download Cheats For All Installed Games - Scarica Trucchi per tutti i giochi installati - - - - Download Patches For All Games - Scarica Patch per tutti i giochi - - - - Download Complete - Download completato - - - - You have downloaded cheats for all the games you have installed. - Hai scaricato trucchi per tutti i giochi installati. - - - - Patches Downloaded Successfully! - Patch scaricate con successo! - - - - All Patches available for all games have been downloaded. - Tutte le patch disponibili per tutti i giochi sono state scaricate. - - - - Games: - Giochi: - - - - PKG File (*.PKG) - File PKG (*.PKG) - - - - ELF files (*.bin *.elf *.oelf) - File ELF (*.bin *.elf *.oelf) - - - - Game Boot - Avvia Gioco - - - - Only one file can be selected! - Si può selezionare solo un file! - - - - PKG Extraction - Estrazione file PKG - - - - Patch detected! - Patch rilevata! - - - - PKG and Game versions match: - Le versioni di PKG e del Gioco corrispondono: - - - - Would you like to overwrite? - Vuoi sovrascrivere? - - - - PKG Version %1 is older than installed version: - La versione PKG %1 è più vecchia rispetto alla versione installata: - - - - Game is installed: - Gioco installato: - - - - Would you like to install Patch: - Vuoi installare la patch: - - - - DLC Installation - Installazione DLC - - - - Would you like to install DLC: %1? - Vuoi installare il DLC: %1? - - - - DLC already installed: - DLC già installato: - - - - Game already installed - Gioco già installato - - - - PKG is a patch, please install the game first! - Questo file PKG contiene una patch. Per favore, installa prima il gioco! - - - - PKG ERROR - ERRORE PKG - - - - Extracting PKG %1/%2 - Estrazione file PKG %1/%2 - - - - Extraction Finished - Estrazione Completata - - - - Game successfully installed at %1 - Gioco installato correttamente in %1 - - - - File doesn't appear to be a valid PKG file - Il file sembra non essere un file PKG valido - - - - CheatsPatches - - - Cheats / Patches for - Cheats / Patches for - - - - defaultTextEdit_MSG - I trucchi e le patch sono sperimentali.\nUtilizzali con cautela.\n\nScarica i trucchi singolarmente selezionando l'archivio e cliccando sul pulsante di download.\nNella scheda Patch, puoi scaricare tutte le patch in una volta sola, scegliere quali vuoi utilizzare e salvare la tua selezione.\n\nPoiché non sviluppiamo i trucchi e le patch,\nper favore segnala i problemi all'autore dei trucchi.\n\nHai creato un nuovo trucco? Visita:\nhttps://github.com/shadps4-emu/ps4_cheats - - - - No Image Available - Nessuna immagine disponibile - - - - Serial: - Seriale: - - - - Version: - Versione: - - - - Size: - Dimensione: - - - - Select Cheat File: - Seleziona File Trucchi: - - - - Repository: - Archivio: - - - - Download Cheats - Scarica trucchi - - - - Delete File - Cancella File - - - - No files selected. - Nessun file selezionato. - - - - You can delete the cheats you don't want after downloading them. - Puoi cancellare i trucchi che non vuoi utilizzare dopo averli scaricati. - - - - Do you want to delete the selected file?\n%1 - Vuoi cancellare il file selezionato?\n%1 - - - - Select Patch File: - Seleziona File Patch: - - - - Download Patches - Scarica Patch - - - Save Salva - - Cheats - Trucchi - - - - Patches - Patch - - - - Error - Errore - - - - No patch selected. - Nessuna patch selezionata. - - - - Unable to open files.json for reading. - Impossibile aprire il file .json per la lettura. - - - - No patch file found for the current serial. - Nessun file patch trovato per il seriale selezionato. - - - - Unable to open the file for reading. - Impossibile aprire il file per la lettura. - - - - Unable to open the file for writing. - Impossibile aprire il file per la scrittura. - - - - Failed to parse XML: - Analisi XML fallita: - - - - Success - Successo - - - - Options saved successfully. - Opzioni salvate con successo. - - - - Invalid Source - Fonte non valida - - - - The selected source is invalid. - La fonte selezionata non è valida. - - - - File Exists - Il file è presente - - - - File already exists. Do you want to replace it? - Il file è già presente. Vuoi sostituirlo? - - - - Failed to save file: - Salvataggio file fallito: - - - - Failed to download file: - Scaricamento file fallito: - - - - Cheats Not Found - Trucchi non trovati - - - - CheatsNotFound_MSG - Non sono stati trovati trucchi per questa versione del gioco nell'archivio selezionato, prova un altro archivio o una versione diversa del gioco. - - - - Cheats Downloaded Successfully - Trucchi scaricati con successo! - - - - CheatsDownloadedSuccessfully_MSG - Hai scaricato con successo i trucchi per questa versione del gioco dall'archivio selezionato. Puoi provare a scaricare da un altro archivio, se disponibile, puoi anche utilizzarlo selezionando il file dall'elenco. - - - - Failed to save: - Salvataggio fallito: - - - - Failed to download: - Impossibile scaricare: - - - - Download Complete - Scaricamento completo - - - - DownloadComplete_MSG - Patch scaricata con successo! Vengono scaricate tutte le patch disponibili per tutti i giochi, non è necessario scaricarle singolarmente per ogni gioco come nel caso dei trucchi. Se la patch non appare, potrebbe essere che non esista per il numero di serie e la versione specifica del gioco. - - - - Failed to parse JSON data from HTML. - Impossibile analizzare i dati JSON dall'HTML. - - - - Failed to retrieve HTML page. - Impossibile recuperare la pagina HTML. - - - - The game is in version: %1 - Il gioco è nella versione: %1 - - - - The downloaded patch only works on version: %1 - La patch scaricata funziona solo sulla versione: %1 - - - - You may need to update your game. - Potresti aver bisogno di aggiornare il tuo gioco. - - - - Incompatibility Notice - Avviso di incompatibilità - - - - Failed to open file: - Impossibile aprire file: - - - - XML ERROR: - ERRORE XML: - - - - Failed to open files.json for writing - Impossibile aprire i file .json per la scrittura - - - - Author: - Autore: - - - - Directory does not exist: - La cartella non esiste: - - - - Failed to open files.json for reading. - Impossibile aprire i file .json per la lettura. - - - - Name: - Nome: - - - - Can't apply cheats before the game is started - Non è possibile applicare i trucchi prima dell'inizio del gioco. - - - - SettingsDialog - - - Save - Salva - - - Apply Applica - Restore Defaults Ripristina Impostazioni Predefinite - Close Chiudi - Point your mouse at an option to display its description. Sposta il mouse su un'opzione per visualizzarne la descrizione. - consoleLanguageGroupBox Lingua della Console:\nImposta la lingua utilizzata dal gioco PS4.\nÈ consigliabile impostare questa su una lingua supportata dal gioco, che può variare a seconda della regione. - emulatorLanguageGroupBox Lingua dell'Emulatore:\nImposta la lingua dell'interfaccia utente dell'emulatore. - fullscreenCheckBox Abilita Schermo Intero:\nMetti automaticamente la finestra di gioco in modalità schermo intero.\nQuesto può essere disattivato premendo il tasto F11. - separateUpdatesCheckBox Abilita Cartella Aggiornamenti Separata:\nAbilita l'installazione degli aggiornamenti in una cartella separata per una più facile gestione. - showSplashCheckBox Mostra Schermata di Avvio:\nMostra la schermata di avvio del gioco (un'immagine speciale) mentre il gioco si sta avviando. - ps4proCheckBox È PS4 Pro:\nFa sì che l'emulatore si comporti come una PS4 PRO, il che può abilitare funzionalità speciali in giochi che la supportano. - discordRPCCheckbox Abilita Discord Rich Presence:\nMostra l'icona dell'emulatore e informazioni pertinenti sul tuo profilo Discord. - userName Nome Utente:\nImposta il nome utente dell'account PS4, che potrebbe essere visualizzato da alcuni giochi. - TrophyKey Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. - logTypeGroupBox Tipo di Log:\nImposta se sincronizzare l'output della finestra di log per le prestazioni. Potrebbe avere effetti avversi sull'emulazione. - logFilter Filtro Log:\nFiltra il log per stampare solo informazioni specifiche.\nEsempi: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Livelli: Trace, Debug, Info, Warning, Error, Critical - in questo ordine, un livello specifico silenzia tutti i livelli precedenti nell'elenco e registra ogni livello successivo. - updaterGroupBox Aggiornamento:\nRelease: Versioni ufficiali rilasciate ogni mese che potrebbero essere molto datate, ma sono più affidabili e testate.\nNightly: Versioni di sviluppo che hanno tutte le ultime funzionalità e correzioni, ma potrebbero contenere bug e sono meno stabili. - GUIgroupBox Riproduci Musica del Titolo:\nSe un gioco lo supporta, attiva la riproduzione di musica speciale quando selezioni il gioco nell'interfaccia grafica. - disableTrophycheckBox Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). - hideCursorGroupBox Nascondi cursore:\nScegli quando il cursore scomparirà:\nMai: Vedrai sempre il mouse.\nInattivo: Imposta un tempo per farlo scomparire dopo essere stato inattivo.\nSempre: non vedrai mai il mouse. - - idleTimeoutGroupBox + idleTimeoutGroupBox Imposta un tempo affinché il mouse scompaia dopo essere stato inattivo. - backButtonBehaviorGroupBox Comportamento del pulsante Indietro:\nImposta il pulsante Indietro del controller per emulare il tocco sulla posizione specificata sul touchpad PS4. - enableCompatibilityCheckBox Mostra Dati Compatibilità:\nMostra informazioni sulla compatibilità del gioco nella visualizzazione lista. Abilita "Aggiorna Compatiblità all'Avvio" per ottenere informazioni aggiornate. - checkCompatibilityOnStartupCheckBox Aggiorna Compatibilità all'Avvio:\nAggiorna automaticamente il database della compatibilità quando si avvia shadps4. - updateCompatibilityButton Aggiorna Database Compatibilità:\nAggiorna immediatamente il database di compatibilità. - Never Mai - Idle Inattivo - Always Sempre - Touchpad Left Touchpad Sinistra - Touchpad Right Touchpad Destra - Touchpad Center Centro del Touchpad - None Nessuno - graphicsAdapterGroupBox Dispositivo Grafico:\nIn sistemi con più GPU, seleziona la GPU che l'emulatore utilizzerà dall'elenco a discesa,\no seleziona "Auto Select" per determinarlo automaticamente. - resolutionLayout Larghezza/Altezza:\nImposta la dimensione della finestra dell'emulatore all'avvio, che può essere ridimensionata durante il gioco.\nQuesto è diverso dalla risoluzione in gioco. - heightDivider Divisore Vblank:\nIl frame rate con cui l'emulatore si aggiorna viene moltiplicato per questo numero. Cambiare questo potrebbe avere effetti avversi, come aumentare la velocità del gioco o rompere funzionalità critiche del gioco che non si aspettano questa modifica! - dumpShadersCheckBox Abilita Pompaggio Shader:\nPer scopi di debug tecnico, salva gli shader dei giochi in una cartella mentre vengono resi. - nullGpuCheckBox Abilita GPU Null:\nPer scopi di debug tecnico, disabilita il rendering del gioco come se non ci fosse alcuna scheda grafica. - gameFoldersBox Cartelle di Gioco:\nL'elenco delle cartelle da controllare per i giochi installati. - addFolderButton Aggiungi:\nAggiungi una cartella all'elenco. - removeFolderButton Rimuovi:\nRimuovi una cartella dall'elenco. - debugDump Abilita Pompaggio di Debug:\nSalva i simboli di importazione ed esportazione e le informazioni sull'intestazione del file del programma PS4 attualmente in esecuzione in una directory. - vkValidationCheckBox Abilita Strati di Validazione Vulkan:\nAbilita un sistema che convalida lo stato del renderer Vulkan e registra informazioni sul suo stato interno. Ciò ridurrà le prestazioni e probabilmente cambierà il comportamento dell'emulazione. - vkSyncValidationCheckBox Abilita Validazione della Sincronizzazione Vulkan:\nAbilita un sistema che convalida il timing delle attività di rendering Vulkan. Ciò ridurrà le prestazioni e probabilmente cambierà il comportamento dell'emulazione. - rdocCheckBox Abilita Debugging RenderDoc:\nSe abilitato, l'emulatore fornirà compatibilità con Renderdoc per consentire la cattura e l'analisi del frame attualmente reso. + + CheatsPatches + + Cheats / Patches for + Cheats / Patches for + + + defaultTextEdit_MSG + I trucchi e le patch sono sperimentali.\nUtilizzali con cautela.\n\nScarica i trucchi singolarmente selezionando l'archivio e cliccando sul pulsante di download.\nNella scheda Patch, puoi scaricare tutte le patch in una volta sola, scegliere quali vuoi utilizzare e salvare la tua selezione.\n\nPoiché non sviluppiamo i trucchi e le patch,\nper favore segnala i problemi all'autore dei trucchi.\n\nHai creato un nuovo trucco? Visita:\nhttps://github.com/shadps4-emu/ps4_cheats + + + No Image Available + Nessuna immagine disponibile + + + Serial: + Seriale: + + + Version: + Versione: + + + Size: + Dimensione: + + + Select Cheat File: + Seleziona File Trucchi: + + + Repository: + Archivio: + + + Download Cheats + Scarica trucchi + + + Delete File + Cancella File + + + No files selected. + Nessun file selezionato. + + + You can delete the cheats you don't want after downloading them. + Puoi cancellare i trucchi che non vuoi utilizzare dopo averli scaricati. + + + Do you want to delete the selected file?\n%1 + Vuoi cancellare il file selezionato?\n%1 + + + Select Patch File: + Seleziona File Patch: + + + Download Patches + Scarica Patch + + + Save + Salva + + + Cheats + Trucchi + + + Patches + Patch + + + Error + Errore + + + No patch selected. + Nessuna patch selezionata. + + + Unable to open files.json for reading. + Impossibile aprire il file .json per la lettura. + + + No patch file found for the current serial. + Nessun file patch trovato per il seriale selezionato. + + + Unable to open the file for reading. + Impossibile aprire il file per la lettura. + + + Unable to open the file for writing. + Impossibile aprire il file per la scrittura. + + + Failed to parse XML: + Analisi XML fallita: + + + Success + Successo + + + Options saved successfully. + Opzioni salvate con successo. + + + Invalid Source + Fonte non valida + + + The selected source is invalid. + La fonte selezionata non è valida. + + + File Exists + Il file è presente + + + File already exists. Do you want to replace it? + Il file è già presente. Vuoi sostituirlo? + + + Failed to save file: + Salvataggio file fallito: + + + Failed to download file: + Scaricamento file fallito: + + + Cheats Not Found + Trucchi non trovati + + + CheatsNotFound_MSG + Non sono stati trovati trucchi per questa versione del gioco nell'archivio selezionato, prova un altro archivio o una versione diversa del gioco. + + + Cheats Downloaded Successfully + Trucchi scaricati con successo! + + + CheatsDownloadedSuccessfully_MSG + Hai scaricato con successo i trucchi per questa versione del gioco dall'archivio selezionato. Puoi provare a scaricare da un altro archivio, se disponibile, puoi anche utilizzarlo selezionando il file dall'elenco. + + + Failed to save: + Salvataggio fallito: + + + Failed to download: + Impossibile scaricare: + + + Download Complete + Scaricamento completo + + + DownloadComplete_MSG + Patch scaricata con successo! Vengono scaricate tutte le patch disponibili per tutti i giochi, non è necessario scaricarle singolarmente per ogni gioco come nel caso dei trucchi. Se la patch non appare, potrebbe essere che non esista per il numero di serie e la versione specifica del gioco. + + + Failed to parse JSON data from HTML. + Impossibile analizzare i dati JSON dall'HTML. + + + Failed to retrieve HTML page. + Impossibile recuperare la pagina HTML. + + + The game is in version: %1 + Il gioco è nella versione: %1 + + + The downloaded patch only works on version: %1 + La patch scaricata funziona solo sulla versione: %1 + + + You may need to update your game. + Potresti aver bisogno di aggiornare il tuo gioco. + + + Incompatibility Notice + Avviso di incompatibilità + + + Failed to open file: + Impossibile aprire file: + + + XML ERROR: + ERRORE XML: + + + Failed to open files.json for writing + Impossibile aprire i file .json per la scrittura + + + Author: + Autore: + + + Directory does not exist: + La cartella non esiste: + + + Failed to open files.json for reading. + Impossibile aprire i file .json per la lettura. + + + Name: + Nome: + + + Can't apply cheats before the game is started + Non è possibile applicare i trucchi prima dell'inizio del gioco. + + GameListFrame - Icon Icona - Name Nome - Serial Seriale - Compatibility Compatibilità - Region Regione - Firmware Firmware - Size Dimensione - Version Versione - Path Percorso - Play Time Tempo di Gioco - Never Played Mai Giocato - h h - m m - s s - Compatibility is untested Nessuna informazione sulla compatibilità - Game does not initialize properly / crashes the emulator Il gioco non si avvia in modo corretto / forza chiusura dell'emulatore - Game boots, but only displays a blank screen Il gioco si avvia, ma mostra solo una schermata nera - Game displays an image but does not go past the menu Il gioco mostra immagini ma non va oltre il menu - Game has game-breaking glitches or unplayable performance Il gioco ha problemi gravi di emulazione oppure framerate troppo basso - Game can be completed with playable performance and no major glitches Il gioco può essere completato con buone prestazioni e senza problemi gravi @@ -1508,127 +1210,102 @@ CheckUpdate - Auto Updater Aggiornamento automatico - Error Errore - Network error: Errore di rete: - Failed to parse update information. Impossibile analizzare le informazioni di aggiornamento. - No pre-releases found. Nessuna anteprima trovata. - Invalid release data. Dati della release non validi. - No download URL found for the specified asset. Nessun URL di download trovato per l'asset specificato. - Your version is already up to date! La tua versione è già aggiornata! - Update Available Aggiornamento disponibile - Update Channel Canale di Aggiornamento - Current Version Versione attuale - Latest Version Ultima versione - Do you want to update? Vuoi aggiornare? - Show Changelog Mostra il Changelog - Check for Updates at Startup Controlla aggiornamenti all’avvio - Update Aggiorna - No No - Hide Changelog Nascondi il Changelog - Changes Modifiche - Network error occurred while trying to access the URL Si è verificato un errore di rete durante il tentativo di accesso all'URL - Download Complete Download completato - The update has been downloaded, press OK to install. L'aggiornamento è stato scaricato, premi OK per installare. - Failed to save the update file at Impossibile salvare il file di aggiornamento in - Starting Update... Inizio aggiornamento... - Failed to create the update script file Impossibile creare il file di script di aggiornamento @@ -1636,29 +1313,24 @@ GameListUtils - B B - KB KB - MB MB - GB GB - TB TB - + \ No newline at end of file diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index 409900ade..f34747549 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -6,22 +6,18 @@ AboutDialog - About shadPS4 shadPS4について - shadPS4 shadPS4 - shadPS4 is an experimental open-source emulator for the PlayStation 4. shadPS4は、PlayStation 4の実験的なオープンソースエミュレーターです。 - This software should not be used to play games you have not legally obtained. このソフトウェアは、合法的に入手していないゲームをプレイするために使用するものではありません。 @@ -29,7 +25,6 @@ ElfViewer - Open Folder フォルダを開く @@ -37,17 +32,14 @@ GameInfoClass - Loading game list, please wait :3 ゲームリストを読み込み中です。お待ちください :3 - Cancel キャンセル - Loading... 読み込み中... @@ -55,12 +47,10 @@ InstallDirSelect - shadPS4 - Choose directory shadPS4 - ディレクトリを選択 - Select which directory you want to install to. Select which directory you want to install to. @@ -68,27 +58,22 @@ GameInstallDialog - shadPS4 - Choose directory shadPS4 - ディレクトリを選択 - Directory to install games ゲームをインストールするディレクトリ - Browse 参照 - Error エラー - The value for location to install games is not valid. ゲームをインストールする場所が無効です。 @@ -96,167 +81,134 @@ GuiContextMenus - Create Shortcut ショートカットを作成 - Cheats / Patches チート / パッチ - SFO Viewer SFOビューワー - Trophy Viewer トロフィービューワー - Open Folder... フォルダを開く... - Open Game Folder ゲームフォルダを開く - Open Save Data Folder セーブデータフォルダを開く - Open Log Folder ログフォルダを開く - Copy info... 情報をコピー... - Copy Name 名前をコピー - Copy Serial シリアルをコピー - Copy All すべてコピー - Delete... Delete... - Delete Game Delete Game - Delete Update Delete Update - Delete DLC Delete DLC - Compatibility... Compatibility... - Update database Update database - View report View report - Submit a report Submit a report - Shortcut creation ショートカットの作成 - Shortcut created successfully! ショートカットが正常に作成されました! - Error エラー - Error creating shortcut! ショートカットの作成に失敗しました! - Install PKG PKGをインストール - Game Game - requiresEnableSeparateUpdateFolder_MSG This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. - This game has no update to delete! This game has no update to delete! - - + Update Update - This game has no DLC to delete! This game has no DLC to delete! - DLC DLC - Delete %1 Delete %1 - Are you sure you want to delete %1's %2 directory? Are you sure you want to delete %1's %2 directory? @@ -264,205 +216,285 @@ MainWindow - Open/Add Elf Folder Elfフォルダを開く/追加する - Install Packages (PKG) パッケージをインストール (PKG) - Boot Game ゲームを起動 - Check for Updates 更新を確認する - About shadPS4 shadPS4について - Configure... 設定... - Install application from a .pkg file .pkgファイルからアプリケーションをインストールする - Recent Games 最近のゲーム - Exit 終了 - Exit shadPS4 shadPS4を終了 - Exit the application. アプリケーションを終了します。 - Show Game List ゲームリストを表示 - Game List Refresh ゲームリストの更新 - Tiny 極小 - Small - Medium - Large - List View リストビュー - Grid View グリッドビュー - Elf Viewer Elfビュワー - Game Install Directory ゲームインストールディレクトリ - Download Cheats/Patches チート / パッチをダウンロード - Dump Game List ゲームリストをダンプ - PKG Viewer PKGビューアー - Search... 検索... - File ファイル - View 表示 - Game List Icons ゲームリストアイコン - Game List Mode ゲームリストモード - Settings 設定 - Utils ユーティリティ - Themes テーマ - Help ヘルプ - Dark ダーク - Light ライト - Green グリーン - Blue ブルー - Violet バイオレット - toolBar ツールバー + + Game List + ゲームリスト + + + * Unsupported Vulkan Version + * サポートされていないVulkanバージョン + + + Download Cheats For All Installed Games + すべてのインストール済みゲームのチートをダウンロード + + + Download Patches For All Games + すべてのゲームのパッチをダウンロード + + + Download Complete + ダウンロード完了 + + + You have downloaded cheats for all the games you have installed. + インストールしたすべてのゲームのチートをダウンロードしました。 + + + Patches Downloaded Successfully! + パッチが正常にダウンロードされました! + + + All Patches available for all games have been downloaded. + すべてのゲームに利用可能なパッチがダウンロードされました。 + + + Games: + ゲーム: + + + PKG File (*.PKG) + PKGファイル (*.PKG) + + + ELF files (*.bin *.elf *.oelf) + ELFファイル (*.bin *.elf *.oelf) + + + Game Boot + ゲームブート + + + Only one file can be selected! + 1つのファイルしか選択できません! + + + PKG Extraction + PKG抽出 + + + Patch detected! + パッチが検出されました! + + + PKG and Game versions match: + PKGとゲームのバージョンが一致しています: + + + Would you like to overwrite? + 上書きしてもよろしいですか? + + + PKG Version %1 is older than installed version: + PKGバージョン %1 はインストールされているバージョンよりも古いです: + + + Game is installed: + ゲームはインストール済みです: + + + Would you like to install Patch: + パッチをインストールしてもよろしいですか: + + + DLC Installation + DLCのインストール + + + Would you like to install DLC: %1? + DLCをインストールしてもよろしいですか: %1? + + + DLC already installed: + DLCはすでにインストールされています: + + + Game already installed + ゲームはすでにインストールされています + + + PKG is a patch, please install the game first! + PKGはパッチです。ゲームを先にインストールしてください! + + + PKG ERROR + PKGエラー + + + Extracting PKG %1/%2 + PKGを抽出中 %1/%2 + + + Extraction Finished + 抽出完了 + + + Game successfully installed at %1 + ゲームが %1 に正常にインストールされました + + + File doesn't appear to be a valid PKG file + ファイルが有効なPKGファイルでないようです + PKGViewer - Open Folder フォルダーを開く @@ -470,7 +502,6 @@ TrophyViewer - Trophy Viewer トロフィービューアー @@ -478,1029 +509,700 @@ SettingsDialog - Settings 設定 - General 一般 - System システム - Console Language コンソール言語 - Emulator Language エミュレーター言語 - Emulator エミュレーター - Enable Fullscreen フルスクリーンを有効にする - Enable Separate Update Folder Enable Separate Update Folder - Show Splash スプラッシュを表示する - Is PS4 Pro PS4 Proモード - Enable Discord Rich Presence Discord Rich Presenceを有効にする - Username ユーザー名 - Trophy Key Trophy Key - Trophy Trophy - Logger ロガー - Log Type ログタイプ - Log Filter ログフィルター - Input 入力 - Cursor カーソル - Hide Cursor カーソルを隠す - Hide Cursor Idle Timeout カーソル非アクティブタイムアウト - s s - Controller コントローラー - Back Button Behavior 戻るボタンの動作 - Graphics グラフィックス - Graphics Device グラフィックスデバイス - Width - Height 高さ - Vblank Divider Vblankディバイダー - Advanced 高度な設定 - Enable Shaders Dumping シェーダーのダンプを有効にする - Enable NULL GPU NULL GPUを有効にする - Paths パス - Game Folders ゲームフォルダ - Add... 追加... - Remove 削除 - Debug デバッグ - Enable Debug Dumping デバッグダンプを有効にする - Enable Vulkan Validation Layers Vulkan検証レイヤーを有効にする - Enable Vulkan Synchronization Validation Vulkan同期検証を有効にする - Enable RenderDoc Debugging RenderDocデバッグを有効にする - Update 更新 - Check for Updates at Startup 起動時に更新確認 - Update Channel アップデートチャネル - Check for Updates 更新を確認 - GUI Settings GUI設定 - Disable Trophy Pop-ups Disable Trophy Pop-ups - Play title music タイトル音楽を再生する - Update Compatibility Database On Startup Update Compatibility Database On Startup - Game Compatibility Game Compatibility - Display Compatibility Data Display Compatibility Data - Update Compatibility Database Update Compatibility Database - Volume 音量 - Audio Backend Audio Backend - - - MainWindow - - Game List - ゲームリスト - - - - * Unsupported Vulkan Version - * サポートされていないVulkanバージョン - - - - Download Cheats For All Installed Games - すべてのインストール済みゲームのチートをダウンロード - - - - Download Patches For All Games - すべてのゲームのパッチをダウンロード - - - - Download Complete - ダウンロード完了 - - - - You have downloaded cheats for all the games you have installed. - インストールしたすべてのゲームのチートをダウンロードしました。 - - - - Patches Downloaded Successfully! - パッチが正常にダウンロードされました! - - - - All Patches available for all games have been downloaded. - すべてのゲームに利用可能なパッチがダウンロードされました。 - - - - Games: - ゲーム: - - - - PKG File (*.PKG) - PKGファイル (*.PKG) - - - - ELF files (*.bin *.elf *.oelf) - ELFファイル (*.bin *.elf *.oelf) - - - - Game Boot - ゲームブート - - - - Only one file can be selected! - 1つのファイルしか選択できません! - - - - PKG Extraction - PKG抽出 - - - - Patch detected! - パッチが検出されました! - - - - PKG and Game versions match: - PKGとゲームのバージョンが一致しています: - - - - Would you like to overwrite? - 上書きしてもよろしいですか? - - - - PKG Version %1 is older than installed version: - PKGバージョン %1 はインストールされているバージョンよりも古いです: - - - - Game is installed: - ゲームはインストール済みです: - - - - Would you like to install Patch: - パッチをインストールしてもよろしいですか: - - - - DLC Installation - DLCのインストール - - - - Would you like to install DLC: %1? - DLCをインストールしてもよろしいですか: %1? - - - - DLC already installed: - DLCはすでにインストールされています: - - - - Game already installed - ゲームはすでにインストールされています - - - - PKG is a patch, please install the game first! - PKGはパッチです。ゲームを先にインストールしてください! - - - - PKG ERROR - PKGエラー - - - - Extracting PKG %1/%2 - PKGを抽出中 %1/%2 - - - - Extraction Finished - 抽出完了 - - - - Game successfully installed at %1 - ゲームが %1 に正常にインストールされました - - - - File doesn't appear to be a valid PKG file - ファイルが有効なPKGファイルでないようです - - - - CheatsPatches - - - Cheats / Patches for - Cheats / Patches for - - - - defaultTextEdit_MSG - チート/パッチは実験的です。\n使用には注意してください。\n\nリポジトリを選択し、ダウンロードボタンをクリックしてチートを個別にダウンロードします。\n「Patches」タブでは、すべてのパッチを一度にダウンロードし、使用したいものを選択して選択を保存できます。\n\nチート/パッチを開発していないため、\n問題があればチートの作者に報告してください。\n\n新しいチートを作成しましたか?\nhttps://github.com/shadps4-emu/ps4_cheats を訪問してください。 - - - - No Image Available - 画像は利用できません - - - - Serial: - シリアル: - - - - Version: - バージョン: - - - - Size: - サイズ: - - - - Select Cheat File: - チートファイルを選択: - - - - Repository: - リポジトリ: - - - - Download Cheats - チートをダウンロード - - - - Delete File - ファイルを削除 - - - - No files selected. - ファイルが選択されていません。 - - - - You can delete the cheats you don't want after downloading them. - ダウンロード後に不要なチートを削除できます。 - - - - Do you want to delete the selected file?\n%1 - 選択したファイルを削除しますか?\n%1 - - - - Select Patch File: - パッチファイルを選択: - - - - Download Patches - パッチをダウンロード - - - Save 保存 - - Cheats - チート - - - - Patches - パッチ - - - - Error - エラー - - - - No patch selected. - パッチが選択されていません。 - - - - Unable to open files.json for reading. - files.jsonを読み込み用に開けません。 - - - - No patch file found for the current serial. - 現在のシリアルに対するパッチファイルが見つかりません。 - - - - Unable to open the file for reading. - ファイルを読み込み用に開けません。 - - - - Unable to open the file for writing. - ファイルを記録用に開けません。 - - - - Failed to parse XML: - XMLの解析に失敗しました: - - - - Success - 成功 - - - - Options saved successfully. - オプションが正常に保存されました。 - - - - Invalid Source - 無効なソース - - - - The selected source is invalid. - 選択したソースは無効です。 - - - - File Exists - ファイルが存在します - - - - File already exists. Do you want to replace it? - ファイルはすでに存在します。置き換えますか? - - - - Failed to save file: - ファイルの保存に失敗しました: - - - - Failed to download file: - ファイルのダウンロードに失敗しました: - - - - Cheats Not Found - チートが見つかりません - - - - CheatsNotFound_MSG - このゲームのこのバージョンのチートが選択されたリポジトリに見つかりませんでした。別のリポジトリまたはゲームの別のバージョンを試してください。 - - - - Cheats Downloaded Successfully - チートが正常にダウンロードされました - - - - CheatsDownloadedSuccessfully_MSG - このゲームのこのバージョンのチートをリポジトリから正常にダウンロードしました。 別のリポジトリからのダウンロードも試せます。利用可能であれば、リストからファイルを選択して使用することも可能です。 - - - - Failed to save: - 保存に失敗しました: - - - - Failed to download: - ダウンロードに失敗しました: - - - - Download Complete - ダウンロード完了 - - - - DownloadComplete_MSG - パッチが正常にダウンロードされました! すべてのゲームに利用可能なパッチがダウンロードされました。チートとは異なり、各ゲームごとに個別にダウンロードする必要はありません。 パッチが表示されない場合、特定のシリアル番号とバージョンのゲームには存在しない可能性があります。 - - - - Failed to parse JSON data from HTML. - HTMLからJSONデータの解析に失敗しました。 - - - - Failed to retrieve HTML page. - HTMLページの取得に失敗しました。 - - - - The game is in version: %1 - ゲームのバージョン: %1 - - - - The downloaded patch only works on version: %1 - ダウンロードしたパッチはバージョン: %1 のみ機能します - - - - You may need to update your game. - ゲームを更新する必要があるかもしれません。 - - - - Incompatibility Notice - 互換性のない通知 - - - - Failed to open file: - ファイルを開くのに失敗しました: - - - - XML ERROR: - XMLエラー: - - - - Failed to open files.json for writing - files.jsonを記録用に開けません - - - - Author: - 著者: - - - - Directory does not exist: - ディレクトリが存在しません: - - - - Failed to open files.json for reading. - files.jsonを読み込み用に開けません。 - - - - Name: - 名前: - - - - Can't apply cheats before the game is started - ゲームが開始される前にチートを適用することはできません。 - - - - SettingsDialog - - - Save - 保存 - - - Apply 適用 - Restore Defaults デフォルトに戻す - Close 閉じる - Point your mouse at an option to display its description. オプションにマウスをポイントすると、その説明が表示されます。 - consoleLanguageGroupBox コンソール言語:\nPS4ゲームが使用する言語を設定します。\nこれはゲームがサポートする言語に設定することをお勧めしますが、地域によって異なる場合があります。 - emulatorLanguageGroupBox エミュレーター言語:\nエミュレーターのユーザーインターフェースの言語を設定します。 - fullscreenCheckBox 全画面モードを有効にする:\nゲームウィンドウを自動的に全画面モードにします。\nF11キーを押すことで切り替えることができます。 - separateUpdatesCheckBox Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. - showSplashCheckBox スプラッシュスクリーンを表示:\nゲーム起動中にゲームのスプラッシュスクリーン(特別な画像)を表示します。 - ps4proCheckBox PS4 Proです:\nエミュレーターがPS4 PROとして動作するようにし、これをサポートするゲームで特別な機能を有効にする場合があります。 - discordRPCCheckbox Discord Rich Presenceを有効にする:\nエミュレーターのアイコンと関連情報をDiscordプロフィールに表示します。 - userName ユーザー名:\nPS4のアカウントユーザー名を設定します。これは、一部のゲームで表示される場合があります。 - TrophyKey Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. - logTypeGroupBox ログタイプ:\nパフォーマンスのためにログウィンドウの出力を同期させるかどうかを設定します。エミュレーションに悪影響を及ぼす可能性があります。 - logFilter ログフィルター:\n特定の情報のみを印刷するようにログをフィルタリングします。\n例: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" レベル: Trace, Debug, Info, Warning, Error, Critical - この順序で、特定のレベルはリスト内のすべての前のレベルをサイレンスし、その後のすべてのレベルをログに記録します。 - updaterGroupBox 更新:\nRelease: 非常に古いかもしれないが、より信頼性が高くテスト済みの公式バージョンを毎月リリースします。\nNightly: 最新の機能と修正がすべて含まれていますが、バグが含まれている可能性があり、安定性は低いです。 - GUIgroupBox タイトルミュージックを再生:\nゲームがそれをサポートしている場合、GUIでゲームを選択したときに特別な音楽を再生することを有効にします。 - disableTrophycheckBox Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). - hideCursorGroupBox カーソルを隠す:\nカーソルが消えるタイミングを選択してください:\n決して: いつでもマウスが見えます。\nアイドル: アイダルの後に消えるまでの時間を設定します。\n常に: マウスは決して見えません。 - idleTimeoutGroupBox アイドル後にマウスが消えるまでの時間を設定します。 - backButtonBehaviorGroupBox 戻るボタンの動作:\nコントローラーの戻るボタンを、PS4のタッチパッドの指定された位置をタッチするように設定します。 - enableCompatibilityCheckBox Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. - checkCompatibilityOnStartupCheckBox Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. - updateCompatibilityButton Update Compatibility Database:\nImmediately update the compatibility database. - Never 決して - Idle アイドル - Always 常に - Touchpad Left タッチパッド左 - Touchpad Right タッチパッド右 - Touchpad Center タッチパッド中央 - None なし - graphicsAdapterGroupBox グラフィックデバイス:\n複数のGPUシステムで、ドロップダウンリストからエミュレーターで使用するGPUを選択するか、\n「自動選択」を選択して自動的に決定します。 - resolutionLayout 幅/高さ:\n起動時にエミュレーターウィンドウのサイズを設定します。ゲーム中にサイズ変更できます。\nこれはゲーム内の解像度とは異なります。 - heightDivider Vblankディバイダー:\nエミュレーターが更新されるフレームレートにこの数を掛けます。これを変更すると、ゲームの速度が上がったり、想定外の変更がある場合、ゲームの重要な機能が壊れる可能性があります! - dumpShadersCheckBox シェーダーダンプを有効にする:\n技術的なデバッグの目的で、レンダリング中にゲームのシェーダーをフォルダーに保存します。 - nullGpuCheckBox Null GPUを有効にする:\n技術的なデバッグの目的で、グラフィックスカードがないかのようにゲームのレンダリングを無効にします。 - gameFoldersBox ゲームフォルダ:\nインストールされたゲームを確認するためのフォルダのリスト。 - addFolderButton 追加:\nリストにフォルダを追加します。 - removeFolderButton 削除:\nリストからフォルダを削除します。 - debugDump デバッグダンプを有効にする:\n現在実行中のPS4プログラムのインポートおよびエクスポートシンボルとファイルヘッダー情報をディレクトリに保存します。 - vkValidationCheckBox Vulkanバリデーションレイヤーを有効にする:\nVulkanのレンダリングステータスを検証し、内部状態に関する情報をログに記録するシステムを有効にします。これによりパフォーマンスが低下し、エミュレーションの動作が変わる可能性があります。 - vkSyncValidationCheckBox Vulkan同期バリデーションを有効にする:\nVulkanのレンダリングタスクのタイミングを検証するシステムを有効にします。これによりパフォーマンスが低下し、エミュレーションの動作が変わる可能性があります。 - rdocCheckBox RenderDocデバッグを有効にする:\n有効にすると、エミュレーターはRenderdocとの互換性を提供し、現在レンダリング中のフレームのキャプチャと分析を可能にします。 + + CheatsPatches + + Cheats / Patches for + Cheats / Patches for + + + defaultTextEdit_MSG + チート/パッチは実験的です。\n使用には注意してください。\n\nリポジトリを選択し、ダウンロードボタンをクリックしてチートを個別にダウンロードします。\n「Patches」タブでは、すべてのパッチを一度にダウンロードし、使用したいものを選択して選択を保存できます。\n\nチート/パッチを開発していないため、\n問題があればチートの作者に報告してください。\n\n新しいチートを作成しましたか?\nhttps://github.com/shadps4-emu/ps4_cheats を訪問してください。 + + + No Image Available + 画像は利用できません + + + Serial: + シリアル: + + + Version: + バージョン: + + + Size: + サイズ: + + + Select Cheat File: + チートファイルを選択: + + + Repository: + リポジトリ: + + + Download Cheats + チートをダウンロード + + + Delete File + ファイルを削除 + + + No files selected. + ファイルが選択されていません。 + + + You can delete the cheats you don't want after downloading them. + ダウンロード後に不要なチートを削除できます。 + + + Do you want to delete the selected file?\n%1 + 選択したファイルを削除しますか?\n%1 + + + Select Patch File: + パッチファイルを選択: + + + Download Patches + パッチをダウンロード + + + Save + 保存 + + + Cheats + チート + + + Patches + パッチ + + + Error + エラー + + + No patch selected. + パッチが選択されていません。 + + + Unable to open files.json for reading. + files.jsonを読み込み用に開けません。 + + + No patch file found for the current serial. + 現在のシリアルに対するパッチファイルが見つかりません。 + + + Unable to open the file for reading. + ファイルを読み込み用に開けません。 + + + Unable to open the file for writing. + ファイルを記録用に開けません。 + + + Failed to parse XML: + XMLの解析に失敗しました: + + + Success + 成功 + + + Options saved successfully. + オプションが正常に保存されました。 + + + Invalid Source + 無効なソース + + + The selected source is invalid. + 選択したソースは無効です。 + + + File Exists + ファイルが存在します + + + File already exists. Do you want to replace it? + ファイルはすでに存在します。置き換えますか? + + + Failed to save file: + ファイルの保存に失敗しました: + + + Failed to download file: + ファイルのダウンロードに失敗しました: + + + Cheats Not Found + チートが見つかりません + + + CheatsNotFound_MSG + このゲームのこのバージョンのチートが選択されたリポジトリに見つかりませんでした。別のリポジトリまたはゲームの別のバージョンを試してください。 + + + Cheats Downloaded Successfully + チートが正常にダウンロードされました + + + CheatsDownloadedSuccessfully_MSG + このゲームのこのバージョンのチートをリポジトリから正常にダウンロードしました。 別のリポジトリからのダウンロードも試せます。利用可能であれば、リストからファイルを選択して使用することも可能です。 + + + Failed to save: + 保存に失敗しました: + + + Failed to download: + ダウンロードに失敗しました: + + + Download Complete + ダウンロード完了 + + + DownloadComplete_MSG + パッチが正常にダウンロードされました! すべてのゲームに利用可能なパッチがダウンロードされました。チートとは異なり、各ゲームごとに個別にダウンロードする必要はありません。 パッチが表示されない場合、特定のシリアル番号とバージョンのゲームには存在しない可能性があります。 + + + Failed to parse JSON data from HTML. + HTMLからJSONデータの解析に失敗しました。 + + + Failed to retrieve HTML page. + HTMLページの取得に失敗しました。 + + + The game is in version: %1 + ゲームのバージョン: %1 + + + The downloaded patch only works on version: %1 + ダウンロードしたパッチはバージョン: %1 のみ機能します + + + You may need to update your game. + ゲームを更新する必要があるかもしれません。 + + + Incompatibility Notice + 互換性のない通知 + + + Failed to open file: + ファイルを開くのに失敗しました: + + + XML ERROR: + XMLエラー: + + + Failed to open files.json for writing + files.jsonを記録用に開けません + + + Author: + 著者: + + + Directory does not exist: + ディレクトリが存在しません: + + + Failed to open files.json for reading. + files.jsonを読み込み用に開けません。 + + + Name: + 名前: + + + Can't apply cheats before the game is started + ゲームが開始される前にチートを適用することはできません。 + + GameListFrame - Icon アイコン - Name 名前 - Serial シリアル - Compatibility Compatibility - Region 地域 - Firmware ファームウェア - Size サイズ - Version バージョン - Path パス - Play Time プレイ時間 - Never Played Never Played - h h - m m - s s - Compatibility is untested Compatibility is untested - Game does not initialize properly / crashes the emulator Game does not initialize properly / crashes the emulator - Game boots, but only displays a blank screen Game boots, but only displays a blank screen - Game displays an image but does not go past the menu Game displays an image but does not go past the menu - Game has game-breaking glitches or unplayable performance Game has game-breaking glitches or unplayable performance - Game can be completed with playable performance and no major glitches Game can be completed with playable performance and no major glitches @@ -1508,127 +1210,102 @@ CheckUpdate - Auto Updater 自動アップデーター - Error エラー - Network error: ネットワークエラー: - Failed to parse update information. アップデート情報の解析に失敗しました。 - No pre-releases found. プレリリースは見つかりませんでした。 - Invalid release data. リリースデータが無効です。 - No download URL found for the specified asset. 指定されたアセットのダウンロードURLが見つかりませんでした。 - Your version is already up to date! あなたのバージョンはすでに最新です! - Update Available アップデートがあります - Update Channel アップデートチャネル - Current Version 現在のバージョン - Latest Version 最新バージョン - Do you want to update? アップデートしますか? - Show Changelog 変更ログを表示 - Check for Updates at Startup 起動時に更新確認 - Update アップデート - No いいえ - Hide Changelog 変更ログを隠す - Changes 変更点 - Network error occurred while trying to access the URL URLにアクセス中にネットワークエラーが発生しました - Download Complete ダウンロード完了 - The update has been downloaded, press OK to install. アップデートがダウンロードされました。インストールするにはOKを押してください。 - Failed to save the update file at 更新ファイルの保存に失敗しました - Starting Update... アップデートを開始しています... - Failed to create the update script file アップデートスクリプトファイルの作成に失敗しました @@ -1636,29 +1313,24 @@ GameListUtils - B B - KB KB - MB MB - GB GB - TB TB - + \ No newline at end of file diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts index ab6404a7e..410f9bead 100644 --- a/src/qt_gui/translations/ko_KR.ts +++ b/src/qt_gui/translations/ko_KR.ts @@ -6,22 +6,18 @@ AboutDialog - About shadPS4 About shadPS4 - shadPS4 shadPS4 - shadPS4 is an experimental open-source emulator for the PlayStation 4. shadPS4 is an experimental open-source emulator for the PlayStation 4. - This software should not be used to play games you have not legally obtained. This software should not be used to play games you have not legally obtained. @@ -29,7 +25,6 @@ ElfViewer - Open Folder Open Folder @@ -37,17 +32,14 @@ GameInfoClass - Loading game list, please wait :3 Loading game list, please wait :3 - Cancel Cancel - Loading... Loading... @@ -55,12 +47,10 @@ InstallDirSelect - shadPS4 - Choose directory shadPS4 - Choose directory - Select which directory you want to install to. Select which directory you want to install to. @@ -68,27 +58,22 @@ GameInstallDialog - shadPS4 - Choose directory shadPS4 - Choose directory - Directory to install games Directory to install games - Browse Browse - Error Error - The value for location to install games is not valid. The value for location to install games is not valid. @@ -96,167 +81,134 @@ GuiContextMenus - Create Shortcut Create Shortcut - Cheats / Patches 치트 / 패치 - SFO Viewer SFO Viewer - Trophy Viewer Trophy Viewer - Open Folder... Open Folder... - Open Game Folder Open Game Folder - Open Save Data Folder Open Save Data Folder - Open Log Folder Open Log Folder - Copy info... Copy info... - Copy Name Copy Name - Copy Serial Copy Serial - Copy All Copy All - Delete... Delete... - Delete Game Delete Game - Delete Update Delete Update - Delete DLC Delete DLC - Compatibility... Compatibility... - Update database Update database - View report View report - Submit a report Submit a report - Shortcut creation Shortcut creation - Shortcut created successfully! Shortcut created successfully! - Error Error - Error creating shortcut! Error creating shortcut! - Install PKG Install PKG - Game Game - requiresEnableSeparateUpdateFolder_MSG This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. - This game has no update to delete! This game has no update to delete! - - + Update Update - This game has no DLC to delete! This game has no DLC to delete! - DLC DLC - Delete %1 Delete %1 - Are you sure you want to delete %1's %2 directory? Are you sure you want to delete %1's %2 directory? @@ -264,205 +216,285 @@ MainWindow - Open/Add Elf Folder Open/Add Elf Folder - Install Packages (PKG) Install Packages (PKG) - Boot Game Boot Game - Check for Updates Check for Updates - About shadPS4 About shadPS4 - Configure... Configure... - Install application from a .pkg file Install application from a .pkg file - Recent Games Recent Games - Exit Exit - Exit shadPS4 Exit shadPS4 - Exit the application. Exit the application. - Show Game List Show Game List - Game List Refresh Game List Refresh - Tiny Tiny - Small Small - Medium Medium - Large Large - List View List View - Grid View Grid View - Elf Viewer Elf Viewer - Game Install Directory Game Install Directory - Download Cheats/Patches 치트 / 패치 다운로드 - Dump Game List Dump Game List - PKG Viewer PKG Viewer - Search... Search... - File File - View View - Game List Icons Game List Icons - Game List Mode Game List Mode - Settings Settings - Utils Utils - Themes Themes - Help Help - Dark Dark - Light Light - Green Green - Blue Blue - Violet Violet - toolBar toolBar + + Game List + Game List + + + * Unsupported Vulkan Version + * Unsupported Vulkan Version + + + Download Cheats For All Installed Games + Download Cheats For All Installed Games + + + Download Patches For All Games + Download Patches For All Games + + + Download Complete + Download Complete + + + You have downloaded cheats for all the games you have installed. + You have downloaded cheats for all the games you have installed. + + + Patches Downloaded Successfully! + Patches Downloaded Successfully! + + + All Patches available for all games have been downloaded. + All Patches available for all games have been downloaded. + + + Games: + Games: + + + PKG File (*.PKG) + PKG File (*.PKG) + + + ELF files (*.bin *.elf *.oelf) + ELF files (*.bin *.elf *.oelf) + + + Game Boot + Game Boot + + + Only one file can be selected! + Only one file can be selected! + + + PKG Extraction + PKG Extraction + + + Patch detected! + Patch detected! + + + PKG and Game versions match: + PKG and Game versions match: + + + Would you like to overwrite? + Would you like to overwrite? + + + PKG Version %1 is older than installed version: + PKG Version %1 is older than installed version: + + + Game is installed: + Game is installed: + + + Would you like to install Patch: + Would you like to install Patch: + + + DLC Installation + DLC Installation + + + Would you like to install DLC: %1? + Would you like to install DLC: %1? + + + DLC already installed: + DLC already installed: + + + Game already installed + Game already installed + + + PKG is a patch, please install the game first! + PKG is a patch, please install the game first! + + + PKG ERROR + PKG ERROR + + + Extracting PKG %1/%2 + Extracting PKG %1/%2 + + + Extraction Finished + Extraction Finished + + + Game successfully installed at %1 + Game successfully installed at %1 + + + File doesn't appear to be a valid PKG file + File doesn't appear to be a valid PKG file + PKGViewer - Open Folder Open Folder @@ -470,7 +502,6 @@ TrophyViewer - Trophy Viewer Trophy Viewer @@ -478,1029 +509,700 @@ SettingsDialog - Settings Settings - General General - System System - Console Language Console Language - Emulator Language Emulator Language - Emulator Emulator - Enable Fullscreen Enable Fullscreen - Enable Separate Update Folder Enable Separate Update Folder - Show Splash Show Splash - Is PS4 Pro Is PS4 Pro - Enable Discord Rich Presence Enable Discord Rich Presence - Username Username - Trophy Key Trophy Key - Trophy Trophy - Logger Logger - Log Type Log Type - Log Filter Log Filter - Input Input - Cursor Cursor - Hide Cursor Hide Cursor - Hide Cursor Idle Timeout Hide Cursor Idle Timeout - s s - Controller Controller - Back Button Behavior Back Button Behavior - Graphics Graphics - Graphics Device Graphics Device - Width Width - Height Height - Vblank Divider Vblank Divider - Advanced Advanced - Enable Shaders Dumping Enable Shaders Dumping - Enable NULL GPU Enable NULL GPU - Paths Paths - Game Folders Game Folders - Add... Add... - Remove Remove - Debug Debug - Enable Debug Dumping Enable Debug Dumping - Enable Vulkan Validation Layers Enable Vulkan Validation Layers - Enable Vulkan Synchronization Validation Enable Vulkan Synchronization Validation - Enable RenderDoc Debugging Enable RenderDoc Debugging - Update Update - Check for Updates at Startup Check for Updates at Startup - Update Channel Update Channel - Check for Updates Check for Updates - GUI Settings GUI Settings - Disable Trophy Pop-ups Disable Trophy Pop-ups - Play title music Play title music - Update Compatibility Database On Startup Update Compatibility Database On Startup - Game Compatibility Game Compatibility - Display Compatibility Data Display Compatibility Data - Update Compatibility Database Update Compatibility Database - Volume 음량 - Audio Backend Audio Backend - - - MainWindow - - Game List - Game List - - - - * Unsupported Vulkan Version - * Unsupported Vulkan Version - - - - Download Cheats For All Installed Games - Download Cheats For All Installed Games - - - - Download Patches For All Games - Download Patches For All Games - - - - Download Complete - Download Complete - - - - You have downloaded cheats for all the games you have installed. - You have downloaded cheats for all the games you have installed. - - - - Patches Downloaded Successfully! - Patches Downloaded Successfully! - - - - All Patches available for all games have been downloaded. - All Patches available for all games have been downloaded. - - - - Games: - Games: - - - - PKG File (*.PKG) - PKG File (*.PKG) - - - - ELF files (*.bin *.elf *.oelf) - ELF files (*.bin *.elf *.oelf) - - - - Game Boot - Game Boot - - - - Only one file can be selected! - Only one file can be selected! - - - - PKG Extraction - PKG Extraction - - - - Patch detected! - Patch detected! - - - - PKG and Game versions match: - PKG and Game versions match: - - - - Would you like to overwrite? - Would you like to overwrite? - - - - PKG Version %1 is older than installed version: - PKG Version %1 is older than installed version: - - - - Game is installed: - Game is installed: - - - - Would you like to install Patch: - Would you like to install Patch: - - - - DLC Installation - DLC Installation - - - - Would you like to install DLC: %1? - Would you like to install DLC: %1? - - - - DLC already installed: - DLC already installed: - - - - Game already installed - Game already installed - - - - PKG is a patch, please install the game first! - PKG is a patch, please install the game first! - - - - PKG ERROR - PKG ERROR - - - - Extracting PKG %1/%2 - Extracting PKG %1/%2 - - - - Extraction Finished - Extraction Finished - - - - Game successfully installed at %1 - Game successfully installed at %1 - - - - File doesn't appear to be a valid PKG file - File doesn't appear to be a valid PKG file - - - - CheatsPatches - - - Cheats / Patches for - Cheats / Patches for - - - - defaultTextEdit_MSG - Cheats/Patches are experimental.\nUse with caution.\n\nDownload cheats individually by selecting the repository and clicking the download button.\nIn the Patches tab, you can download all patches at once, choose which ones you want to use, and save your selection.\n\nSince we do not develop the Cheats/Patches,\nplease report issues to the cheat author.\n\nCreated a new cheat? Visit:\nhttps://github.com/shadps4-emu/ps4_cheats - - - - No Image Available - No Image Available - - - - Serial: - Serial: - - - - Version: - Version: - - - - Size: - Size: - - - - Select Cheat File: - Select Cheat File: - - - - Repository: - Repository: - - - - Download Cheats - Download Cheats - - - - Delete File - Delete File - - - - No files selected. - No files selected. - - - - You can delete the cheats you don't want after downloading them. - You can delete the cheats you don't want after downloading them. - - - - Do you want to delete the selected file?\n%1 - Do you want to delete the selected file?\n%1 - - - - Select Patch File: - Select Patch File: - - - - Download Patches - Download Patches - - - Save Save - - Cheats - Cheats - - - - Patches - Patches - - - - Error - Error - - - - No patch selected. - No patch selected. - - - - Unable to open files.json for reading. - Unable to open files.json for reading. - - - - No patch file found for the current serial. - No patch file found for the current serial. - - - - Unable to open the file for reading. - Unable to open the file for reading. - - - - Unable to open the file for writing. - Unable to open the file for writing. - - - - Failed to parse XML: - Failed to parse XML: - - - - Success - Success - - - - Options saved successfully. - Options saved successfully. - - - - Invalid Source - Invalid Source - - - - The selected source is invalid. - The selected source is invalid. - - - - File Exists - File Exists - - - - File already exists. Do you want to replace it? - File already exists. Do you want to replace it? - - - - Failed to save file: - Failed to save file: - - - - Failed to download file: - Failed to download file: - - - - Cheats Not Found - Cheats Not Found - - - - CheatsNotFound_MSG - No Cheats found for this game in this version of the selected repository,try another repository or a different version of the game. - - - - Cheats Downloaded Successfully - Cheats Downloaded Successfully - - - - CheatsDownloadedSuccessfully_MSG - You have successfully downloaded the cheats for this version of the game from the selected repository. You can try downloading from another repository, if it is available it will also be possible to use it by selecting the file from the list. - - - - Failed to save: - Failed to save: - - - - Failed to download: - Failed to download: - - - - Download Complete - Download Complete - - - - DownloadComplete_MSG - Patches Downloaded Successfully! All Patches available for all games have been downloaded, there is no need to download them individually for each game as happens in Cheats. If the patch does not appear, it may be that it does not exist for the specific serial and version of the game. - - - - Failed to parse JSON data from HTML. - Failed to parse JSON data from HTML. - - - - Failed to retrieve HTML page. - Failed to retrieve HTML page. - - - - The game is in version: %1 - The game is in version: %1 - - - - The downloaded patch only works on version: %1 - The downloaded patch only works on version: %1 - - - - You may need to update your game. - You may need to update your game. - - - - Incompatibility Notice - Incompatibility Notice - - - - Failed to open file: - Failed to open file: - - - - XML ERROR: - XML ERROR: - - - - Failed to open files.json for writing - Failed to open files.json for writing - - - - Author: - Author: - - - - Directory does not exist: - Directory does not exist: - - - - Failed to open files.json for reading. - Failed to open files.json for reading. - - - - Name: - Name: - - - - Can't apply cheats before the game is started - Can't apply cheats before the game is started. - - - - SettingsDialog - - - Save - Save - - - Apply Apply - Restore Defaults Restore Defaults - Close Close - Point your mouse at an option to display its description. Point your mouse at an option to display its description. - consoleLanguageGroupBox Console Language:\nSets the language that the PS4 game uses.\nIt's recommended to set this to a language the game supports, which will vary by region. - emulatorLanguageGroupBox Emulator Language:\nSets the language of the emulator's user interface. - fullscreenCheckBox Enable Full Screen:\nAutomatically puts the game window into full-screen mode.\nThis can be toggled by pressing the F11 key. - separateUpdatesCheckBox Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. - showSplashCheckBox Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. - ps4proCheckBox Is PS4 Pro:\nMakes the emulator act as a PS4 PRO, which may enable special features in games that support it. - discordRPCCheckbox Discord Rich Presence 활성화:\nDiscord 프로필에 에뮬레이터 아이콘과 관련 정보를 표시합니다. - userName Username:\nSets the PS4's account username, which may be displayed by some games. - TrophyKey Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. - logTypeGroupBox Log Type:\nSets whether to synchronize the output of the log window for performance. May have adverse effects on emulation. - logFilter Log Filter:\nFilters the log to only print specific information.\nExamples: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Levels: Trace, Debug, Info, Warning, Error, Critical - in this order, a specific level silences all levels preceding it in the list and logs every level after it. - updaterGroupBox Update:\nRelease: Official versions released every month that may be very outdated, but are more reliable and tested.\nNightly: Development versions that have all the latest features and fixes, but may contain bugs and are less stable. - GUIgroupBox Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI. - disableTrophycheckBox Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). - hideCursorGroupBox Hide Cursor:\nChoose when the cursor will disappear:\nNever: You will always see the mouse.\nidle: Set a time for it to disappear after being idle.\nAlways: you will never see the mouse. - idleTimeoutGroupBox Set a time for the mouse to disappear after being after being idle. - backButtonBehaviorGroupBox Back Button Behavior:\nSets the controller's back button to emulate tapping the specified position on the PS4 touchpad. - enableCompatibilityCheckBox Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. - checkCompatibilityOnStartupCheckBox Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. - updateCompatibilityButton Update Compatibility Database:\nImmediately update the compatibility database. - Never Never - Idle Idle - Always Always - Touchpad Left Touchpad Left - Touchpad Right Touchpad Right - Touchpad Center Touchpad Center - None None - graphicsAdapterGroupBox Graphics Device:\nOn multiple GPU systems, select the GPU the emulator will use from the drop down list,\nor select "Auto Select" to automatically determine it. - resolutionLayout Width/Height:\nSets the size of the emulator window at launch, which can be resized during gameplay.\nThis is different from the in-game resolution. - heightDivider Vblank Divider:\nThe frame rate at which the emulator refreshes at is multiplied by this number. Changing this may have adverse effects, such as increasing the game speed, or breaking critical game functionality that does not expect this to change! - dumpShadersCheckBox Enable Shaders Dumping:\nFor the sake of technical debugging, saves the games shaders to a folder as they render. - nullGpuCheckBox Enable Null GPU:\nFor the sake of technical debugging, disables game rendering as if there were no graphics card. - gameFoldersBox Game Folders:\nThe list of folders to check for installed games. - addFolderButton Add:\nAdd a folder to the list. - removeFolderButton Remove:\nRemove a folder from the list. - debugDump Enable Debug Dumping:\nSaves the import and export symbols and file header information of the currently running PS4 program to a directory. - vkValidationCheckBox Enable Vulkan Validation Layers:\nEnables a system that validates the state of the Vulkan renderer and logs information about its internal state. This will reduce performance and likely change the behavior of emulation. - vkSyncValidationCheckBox Enable Vulkan Synchronization Validation:\nEnables a system that validates the timing of Vulkan rendering tasks. This will reduce performance and likely change the behavior of emulation. - rdocCheckBox Enable RenderDoc Debugging:\nIf enabled, the emulator will provide compatibility with Renderdoc to allow capture and analysis of the currently rendered frame. + + CheatsPatches + + Cheats / Patches for + Cheats / Patches for + + + defaultTextEdit_MSG + Cheats/Patches are experimental.\nUse with caution.\n\nDownload cheats individually by selecting the repository and clicking the download button.\nIn the Patches tab, you can download all patches at once, choose which ones you want to use, and save your selection.\n\nSince we do not develop the Cheats/Patches,\nplease report issues to the cheat author.\n\nCreated a new cheat? Visit:\nhttps://github.com/shadps4-emu/ps4_cheats + + + No Image Available + No Image Available + + + Serial: + Serial: + + + Version: + Version: + + + Size: + Size: + + + Select Cheat File: + Select Cheat File: + + + Repository: + Repository: + + + Download Cheats + Download Cheats + + + Delete File + Delete File + + + No files selected. + No files selected. + + + You can delete the cheats you don't want after downloading them. + You can delete the cheats you don't want after downloading them. + + + Do you want to delete the selected file?\n%1 + Do you want to delete the selected file?\n%1 + + + Select Patch File: + Select Patch File: + + + Download Patches + Download Patches + + + Save + Save + + + Cheats + Cheats + + + Patches + Patches + + + Error + Error + + + No patch selected. + No patch selected. + + + Unable to open files.json for reading. + Unable to open files.json for reading. + + + No patch file found for the current serial. + No patch file found for the current serial. + + + Unable to open the file for reading. + Unable to open the file for reading. + + + Unable to open the file for writing. + Unable to open the file for writing. + + + Failed to parse XML: + Failed to parse XML: + + + Success + Success + + + Options saved successfully. + Options saved successfully. + + + Invalid Source + Invalid Source + + + The selected source is invalid. + The selected source is invalid. + + + File Exists + File Exists + + + File already exists. Do you want to replace it? + File already exists. Do you want to replace it? + + + Failed to save file: + Failed to save file: + + + Failed to download file: + Failed to download file: + + + Cheats Not Found + Cheats Not Found + + + CheatsNotFound_MSG + No Cheats found for this game in this version of the selected repository,try another repository or a different version of the game. + + + Cheats Downloaded Successfully + Cheats Downloaded Successfully + + + CheatsDownloadedSuccessfully_MSG + You have successfully downloaded the cheats for this version of the game from the selected repository. You can try downloading from another repository, if it is available it will also be possible to use it by selecting the file from the list. + + + Failed to save: + Failed to save: + + + Failed to download: + Failed to download: + + + Download Complete + Download Complete + + + DownloadComplete_MSG + Patches Downloaded Successfully! All Patches available for all games have been downloaded, there is no need to download them individually for each game as happens in Cheats. If the patch does not appear, it may be that it does not exist for the specific serial and version of the game. + + + Failed to parse JSON data from HTML. + Failed to parse JSON data from HTML. + + + Failed to retrieve HTML page. + Failed to retrieve HTML page. + + + The game is in version: %1 + The game is in version: %1 + + + The downloaded patch only works on version: %1 + The downloaded patch only works on version: %1 + + + You may need to update your game. + You may need to update your game. + + + Incompatibility Notice + Incompatibility Notice + + + Failed to open file: + Failed to open file: + + + XML ERROR: + XML ERROR: + + + Failed to open files.json for writing + Failed to open files.json for writing + + + Author: + Author: + + + Directory does not exist: + Directory does not exist: + + + Failed to open files.json for reading. + Failed to open files.json for reading. + + + Name: + Name: + + + Can't apply cheats before the game is started + Can't apply cheats before the game is started. + + GameListFrame - Icon Icon - Name Name - Serial Serial - Compatibility Compatibility - Region Region - Firmware Firmware - Size Size - Version Version - Path Path - Play Time Play Time - Never Played Never Played - h h - m m - s s - Compatibility is untested Compatibility is untested - Game does not initialize properly / crashes the emulator Game does not initialize properly / crashes the emulator - Game boots, but only displays a blank screen Game boots, but only displays a blank screen - Game displays an image but does not go past the menu Game displays an image but does not go past the menu - Game has game-breaking glitches or unplayable performance Game has game-breaking glitches or unplayable performance - Game can be completed with playable performance and no major glitches Game can be completed with playable performance and no major glitches @@ -1508,127 +1210,102 @@ CheckUpdate - Auto Updater Auto Updater - Error Error - Network error: Network error: - Failed to parse update information. Failed to parse update information. - No pre-releases found. No pre-releases found. - Invalid release data. Invalid release data. - No download URL found for the specified asset. No download URL found for the specified asset. - Your version is already up to date! Your version is already up to date! - Update Available Update Available - Update Channel Update Channel - Current Version Current Version - Latest Version Latest Version - Do you want to update? Do you want to update? - Show Changelog Show Changelog - Check for Updates at Startup Check for Updates at Startup - Update Update - No No - Hide Changelog Hide Changelog - Changes Changes - Network error occurred while trying to access the URL Network error occurred while trying to access the URL - Download Complete Download Complete - The update has been downloaded, press OK to install. The update has been downloaded, press OK to install. - Failed to save the update file at Failed to save the update file at - Starting Update... Starting Update... - Failed to create the update script file Failed to create the update script file @@ -1636,29 +1313,24 @@ GameListUtils - B B - KB KB - MB MB - GB GB - TB TB - + \ No newline at end of file diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts index 0b9c5b542..770a9b09e 100644 --- a/src/qt_gui/translations/lt_LT.ts +++ b/src/qt_gui/translations/lt_LT.ts @@ -6,22 +6,18 @@ AboutDialog - About shadPS4 About shadPS4 - shadPS4 shadPS4 - shadPS4 is an experimental open-source emulator for the PlayStation 4. shadPS4 is an experimental open-source emulator for the PlayStation 4. - This software should not be used to play games you have not legally obtained. This software should not be used to play games you have not legally obtained. @@ -29,7 +25,6 @@ ElfViewer - Open Folder Open Folder @@ -37,17 +32,14 @@ GameInfoClass - Loading game list, please wait :3 Loading game list, please wait :3 - Cancel Cancel - Loading... Loading... @@ -55,12 +47,10 @@ InstallDirSelect - shadPS4 - Choose directory shadPS4 - Choose directory - Select which directory you want to install to. Select which directory you want to install to. @@ -68,27 +58,22 @@ GameInstallDialog - shadPS4 - Choose directory shadPS4 - Choose directory - Directory to install games Directory to install games - Browse Browse - Error Error - The value for location to install games is not valid. The value for location to install games is not valid. @@ -96,167 +81,134 @@ GuiContextMenus - Create Shortcut Create Shortcut - Apgaulės / Pleistrai Cheats / Patches - SFO Viewer SFO Viewer - Trophy Viewer Trophy Viewer - Open Folder... Atidaryti Katalogą... - Open Game Folder Atidaryti Žaidimo Katalogą - Open Save Data Folder Atidaryti Išsaugotų Duomenų Katalogą - Open Log Folder Atidaryti Žurnalų Katalogą - Copy info... Copy info... - Copy Name Copy Name - Copy Serial Copy Serial - Copy All Copy All - Delete... Delete... - Delete Game Delete Game - Delete Update Delete Update - Delete DLC Delete DLC - Compatibility... Compatibility... - Update database Update database - View report View report - Submit a report Submit a report - Shortcut creation Shortcut creation - Shortcut created successfully! Shortcut created successfully! - Error Error - Error creating shortcut! Error creating shortcut! - Install PKG Install PKG - Game Game - requiresEnableSeparateUpdateFolder_MSG This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. - This game has no update to delete! This game has no update to delete! - - + Update Update - This game has no DLC to delete! This game has no DLC to delete! - DLC DLC - Delete %1 Delete %1 - Are you sure you want to delete %1's %2 directory? Are you sure you want to delete %1's %2 directory? @@ -264,205 +216,285 @@ MainWindow - Open/Add Elf Folder Open/Add Elf Folder - Install Packages (PKG) Install Packages (PKG) - Boot Game Boot Game - Check for Updates Patikrinti atnaujinimus - About shadPS4 About shadPS4 - Configure... Configure... - Install application from a .pkg file Install application from a .pkg file - Recent Games Recent Games - Exit Exit - Exit shadPS4 Exit shadPS4 - Exit the application. Exit the application. - Show Game List Show Game List - Game List Refresh Game List Refresh - Tiny Tiny - Small Small - Medium Medium - Large Large - List View List View - Grid View Grid View - Elf Viewer Elf Viewer - Game Install Directory Game Install Directory - Download Cheats/Patches Atsisiųsti Apgaules / Pleistrus - Dump Game List Dump Game List - PKG Viewer PKG Viewer - Search... Search... - File File - View View - Game List Icons Game List Icons - Game List Mode Game List Mode - Settings Settings - Utils Utils - Themes Themes - Help Pagalba - Dark Dark - Light Light - Green Green - Blue Blue - Violet Violet - toolBar toolBar + + Game List + Žaidimų sąrašas + + + * Unsupported Vulkan Version + * Nepalaikoma Vulkan versija + + + Download Cheats For All Installed Games + Atsisiųsti sukčiavimus visiems įdiegtiems žaidimams + + + Download Patches For All Games + Atsisiųsti pataisas visiems žaidimams + + + Download Complete + Atsisiuntimas baigtas + + + You have downloaded cheats for all the games you have installed. + Jūs atsisiuntėte sukčiavimus visiems jūsų įdiegtiesiems žaidimams. + + + Patches Downloaded Successfully! + Pataisos sėkmingai atsisiųstos! + + + All Patches available for all games have been downloaded. + Visos pataisos visiems žaidimams buvo atsisiųstos. + + + Games: + Žaidimai: + + + PKG File (*.PKG) + PKG failas (*.PKG) + + + ELF files (*.bin *.elf *.oelf) + ELF failai (*.bin *.elf *.oelf) + + + Game Boot + Žaidimo paleidimas + + + Only one file can be selected! + Galite pasirinkti tik vieną failą! + + + PKG Extraction + PKG ištraukimas + + + Patch detected! + Rasta atnaujinimą! + + + PKG and Game versions match: + PKG ir žaidimo versijos sutampa: + + + Would you like to overwrite? + Ar norite perrašyti? + + + PKG Version %1 is older than installed version: + PKG versija %1 yra senesnė nei įdiegta versija: + + + Game is installed: + Žaidimas įdiegtas: + + + Would you like to install Patch: + Ar norite įdiegti atnaujinimą: + + + DLC Installation + DLC diegimas + + + Would you like to install DLC: %1? + Ar norite įdiegti DLC: %1? + + + DLC already installed: + DLC jau įdiegtas: + + + Game already installed + Žaidimas jau įdiegtas + + + PKG is a patch, please install the game first! + PKG yra pataisa, prašome pirmiausia įdiegti žaidimą! + + + PKG ERROR + PKG KLAIDA + + + Extracting PKG %1/%2 + Ekstrakcinis PKG %1/%2 + + + Extraction Finished + Ekstrakcija baigta + + + Game successfully installed at %1 + Žaidimas sėkmingai įdiegtas %1 + + + File doesn't appear to be a valid PKG file + Failas atrodo, kad nėra galiojantis PKG failas + PKGViewer - Open Folder Open Folder @@ -470,7 +502,6 @@ TrophyViewer - Trophy Viewer Trophy Viewer @@ -478,1029 +509,700 @@ SettingsDialog - Settings Settings - General General - System System - Console Language Console Language - Emulator Language Emulator Language - Emulator Emulator - Enable Fullscreen Enable Fullscreen - Enable Separate Update Folder Enable Separate Update Folder - Show Splash Show Splash - Is PS4 Pro Is PS4 Pro - Enable Discord Rich Presence Įjungti Discord Rich Presence - Username Username - Trophy Key Trophy Key - Trophy Trophy - Logger Logger - Log Type Log Type - Log Filter Log Filter - Input Įvestis - Cursor Žymeklis - Hide Cursor Slėpti žymeklį - Hide Cursor Idle Timeout Žymeklio paslėpimo neveikimo laikas - s s - Controller Valdiklis - Back Button Behavior Atgal mygtuko elgsena - Graphics Graphics - Graphics Device Graphics Device - Width Width - Height Height - Vblank Divider Vblank Divider - Advanced Advanced - Enable Shaders Dumping Enable Shaders Dumping - Enable NULL GPU Enable NULL GPU - Paths Keliai - Game Folders Žaidimų aplankai - Add... Pridėti... - Remove Pašalinti - Debug Debug - Enable Debug Dumping Enable Debug Dumping - Enable Vulkan Validation Layers Enable Vulkan Validation Layers - Enable Vulkan Synchronization Validation Enable Vulkan Synchronization Validation - Enable RenderDoc Debugging Enable RenderDoc Debugging - Update Atnaujinimas - Check for Updates at Startup Tikrinti naujinimus paleidus - Update Channel Atnaujinimo Kanalas - Check for Updates Patikrinkite atnaujinimus - GUI Settings GUI Nustatymai - Disable Trophy Pop-ups Disable Trophy Pop-ups - Play title music Groti antraštės muziką - Update Compatibility Database On Startup Update Compatibility Database On Startup - Game Compatibility Game Compatibility - Display Compatibility Data Display Compatibility Data - Update Compatibility Database Update Compatibility Database - Volume Garsumas - Audio Backend Audio Backend - - - MainWindow - - Game List - Žaidimų sąrašas - - - - * Unsupported Vulkan Version - * Nepalaikoma Vulkan versija - - - - Download Cheats For All Installed Games - Atsisiųsti sukčiavimus visiems įdiegtiems žaidimams - - - - Download Patches For All Games - Atsisiųsti pataisas visiems žaidimams - - - - Download Complete - Atsisiuntimas baigtas - - - - You have downloaded cheats for all the games you have installed. - Jūs atsisiuntėte sukčiavimus visiems jūsų įdiegtiesiems žaidimams. - - - - Patches Downloaded Successfully! - Pataisos sėkmingai atsisiųstos! - - - - All Patches available for all games have been downloaded. - Visos pataisos visiems žaidimams buvo atsisiųstos. - - - - Games: - Žaidimai: - - - - PKG File (*.PKG) - PKG failas (*.PKG) - - - - ELF files (*.bin *.elf *.oelf) - ELF failai (*.bin *.elf *.oelf) - - - - Game Boot - Žaidimo paleidimas - - - - Only one file can be selected! - Galite pasirinkti tik vieną failą! - - - - PKG Extraction - PKG ištraukimas - - - - Patch detected! - Rasta atnaujinimą! - - - - PKG and Game versions match: - PKG ir žaidimo versijos sutampa: - - - - Would you like to overwrite? - Ar norite perrašyti? - - - - PKG Version %1 is older than installed version: - PKG versija %1 yra senesnė nei įdiegta versija: - - - - Game is installed: - Žaidimas įdiegtas: - - - - Would you like to install Patch: - Ar norite įdiegti atnaujinimą: - - - - DLC Installation - DLC diegimas - - - - Would you like to install DLC: %1? - Ar norite įdiegti DLC: %1? - - - - DLC already installed: - DLC jau įdiegtas: - - - - Game already installed - Žaidimas jau įdiegtas - - - - PKG is a patch, please install the game first! - PKG yra pataisa, prašome pirmiausia įdiegti žaidimą! - - - - PKG ERROR - PKG KLAIDA - - - - Extracting PKG %1/%2 - Ekstrakcinis PKG %1/%2 - - - - Extraction Finished - Ekstrakcija baigta - - - - Game successfully installed at %1 - Žaidimas sėkmingai įdiegtas %1 - - - - File doesn't appear to be a valid PKG file - Failas atrodo, kad nėra galiojantis PKG failas - - - - CheatsPatches - - - Cheats / Patches for - Cheats / Patches for - - - - defaultTextEdit_MSG - Cheats/Patches yra eksperimentiniai.\nNaudokite atsargiai.\n\nAtsisiųskite cheats atskirai pasirinkdami saugyklą ir paspausdami atsisiuntimo mygtuką.\nPatches skirtuke galite atsisiųsti visus patch’us vienu metu, pasirinkti, kuriuos norite naudoti, ir išsaugoti pasirinkimą.\n\nKadangi mes nekurime Cheats/Patches,\npraneškite problemas cheat autoriui.\n\nSukūrėte naują cheat? Apsilankykite:\nhttps://github.com/shadps4-emu/ps4_cheats - - - - No Image Available - Nuotrauka neprieinama - - - - Serial: - Seriinis numeris: - - - - Version: - Versija: - - - - Size: - Dydis: - - - - Select Cheat File: - Pasirinkite sukčiavimo failą: - - - - Repository: - Saugykla: - - - - Download Cheats - Atsisiųsti sukčiavimus - - - - Delete File - Pašalinti failą - - - - No files selected. - Failai nepasirinkti. - - - - You can delete the cheats you don't want after downloading them. - Galite pašalinti sukčiavimus, kurių nenorite, juos atsisiuntę. - - - - Do you want to delete the selected file?\n%1 - Ar norite ištrinti pasirinktą failą?\n%1 - - - - Select Patch File: - Pasirinkite pataisos failą: - - - - Download Patches - Atsisiųsti pataisas - - - Save Įrašyti - - Cheats - Sukčiavimai - - - - Patches - Pataisos - - - - Error - Klaida - - - - No patch selected. - Nieko nepataisyta. - - - - Unable to open files.json for reading. - Neįmanoma atidaryti files.json skaitymui. - - - - No patch file found for the current serial. - Nepavyko rasti pataisos failo dabartiniam serijiniam numeriui. - - - - Unable to open the file for reading. - Neįmanoma atidaryti failo skaitymui. - - - - Unable to open the file for writing. - Neįmanoma atidaryti failo rašymui. - - - - Failed to parse XML: - Nepavyko išanalizuoti XML: - - - - Success - Sėkmė - - - - Options saved successfully. - Nustatymai sėkmingai išsaugoti. - - - - Invalid Source - Netinkamas šaltinis - - - - The selected source is invalid. - Pasirinktas šaltinis yra netinkamas. - - - - File Exists - Failas egzistuoja - - - - File already exists. Do you want to replace it? - Failas jau egzistuoja. Ar norite jį pakeisti? - - - - Failed to save file: - Nepavyko išsaugoti failo: - - - - Failed to download file: - Nepavyko atsisiųsti failo: - - - - Cheats Not Found - Sukčiavimai nerasti - - - - CheatsNotFound_MSG - Nerasta sukčiavimų šiam žaidimui šioje pasirinktos saugyklos versijoje,bandykite kitą saugyklą arba skirtingą žaidimo versiją. - - - - Cheats Downloaded Successfully - Sukčiavimai sėkmingai atsisiųsti - - - - CheatsDownloadedSuccessfully_MSG - Sėkmingai atsisiuntėte sukčiavimus šios žaidimo versijos iš pasirinktos saugyklos. Galite pabandyti atsisiųsti iš kitos saugyklos, jei ji yra prieinama, taip pat bus galima ją naudoti pasirinkus failą iš sąrašo. - - - - Failed to save: - Nepavyko išsaugoti: - - - - Failed to download: - Nepavyko atsisiųsti: - - - - Download Complete - Atsisiuntimas baigtas - - - - DownloadComplete_MSG - Pataisos sėkmingai atsisiųstos! Visos pataisos visiems žaidimams buvo atsisiųstos, nebėra reikalo jas atsisiųsti atskirai kiekvienam žaidimui, kaip tai vyksta su sukčiavimais. Jei pleistras nepasirodo, gali būti, kad jo nėra tam tikram žaidimo serijos numeriui ir versijai. - - - - Failed to parse JSON data from HTML. - Nepavyko išanalizuoti JSON duomenų iš HTML. - - - - Failed to retrieve HTML page. - Nepavyko gauti HTML puslapio. - - - - The game is in version: %1 - Žaidimas yra versijoje: %1 - - - - The downloaded patch only works on version: %1 - Parsisiųstas pataisas veikia tik versijoje: %1 - - - - You may need to update your game. - Gali tekti atnaujinti savo žaidimą. - - - - Incompatibility Notice - Suderinamumo pranešimas - - - - Failed to open file: - Nepavyko atidaryti failo: - - - - XML ERROR: - XML KLAIDA: - - - - Failed to open files.json for writing - Nepavyko atidaryti files.json rašymui - - - - Author: - Autorius: - - - - Directory does not exist: - Katalogas neegzistuoja: - - - - Failed to open files.json for reading. - Nepavyko atidaryti files.json skaitymui. - - - - Name: - Pavadinimas: - - - - Can't apply cheats before the game is started - Negalima taikyti sukčiavimų prieš pradedant žaidimą. - - - - SettingsDialog - - - Save - Įrašyti - - - Apply Taikyti - Restore Defaults Atkurti numatytuosius nustatymus - Close Uždaryti - Point your mouse at an option to display its description. Žymeklį nukreipkite ant pasirinkimo, kad pamatytumėte jo aprašymą. - consoleLanguageGroupBox Konsole kalba:\nNustato kalbą, kurią naudoja PS4 žaidimai.\nRekomenduojama nustatyti kalbą, kurią palaiko žaidimas, priklausomai nuo regiono. - emulatorLanguageGroupBox Emuliatoriaus kalba:\nNustato emuliatoriaus vartotojo sąsajos kalbą. - fullscreenCheckBox Įjungti visą ekraną:\nAutomatiškai perjungia žaidimo langą į viso ekrano režimą.\nTai galima išjungti paspaudus F11 klavišą. - separateUpdatesCheckBox Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. - showSplashCheckBox Rodyti paleidimo ekraną:\nPaleidimo metu rodo žaidimo paleidimo ekraną (ypatingą vaizdą). - ps4proCheckBox Ar PS4 Pro:\nPadaro, kad emuliatorius veiktų kaip PS4 PRO, kas gali įjungti specialias funkcijas žaidimuose, kurie tai palaiko. - discordRPCCheckbox Įjungti Discord Rich Presence:\nRodo emuliatoriaus ikoną ir susijusią informaciją jūsų Discord profilyje. - userName Vartotojo vardas:\nNustato PS4 paskyros vartotojo vardą, kuris gali būti rodomas kai kuriuose žaidimuose. - TrophyKey Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. - logTypeGroupBox Žurnalo tipas:\nNustato, ar sinchronizuoti žurnalo lango išvestį našumui. Tai gali turėti neigiamą poveikį emuliacijai. - logFilter Žurnalo filtras:\nFiltruojamas žurnalas, kad būtų spausdinama tik konkreti informacija.\nPavyzdžiai: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Lygiai: Trace, Debug, Info, Warning, Error, Critical - šia tvarka, konkretus lygis nutildo visus ankstesnius lygius sąraše ir registruoja visus vėlesnius. - updaterGroupBox Atnaujinti:\nRelease: Oficialios versijos, išleidžiamos kiekvieną mėnesį, kurios gali būti labai pasenusios, tačiau yra patikimos ir išbandytos.\nNightly: Vystymo versijos, kuriose yra visos naujausios funkcijos ir taisymai, tačiau gali turėti klaidų ir būti mažiau stabilios. - GUIgroupBox Groti antraščių muziką:\nJei žaidimas tai palaiko, įjungia specialios muzikos grojimą, kai pasirinkite žaidimą GUI. - disableTrophycheckBox Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). - hideCursorGroupBox Slėpti žymeklį:\nPasirinkite, kada žymeklis dings:\nNiekuomet: Visada matysite pelę.\nNeaktyvus: Nustatykite laiką, po kurio ji dings, kai bus neaktyvi.\nVisada: niekada nematysite pelės. - idleTimeoutGroupBox Nustatykite laiką, po kurio pelė dings, kai bus neaktyvi. - backButtonBehaviorGroupBox Atgal mygtuko elgesys:\nNustato valdiklio atgal mygtuką imituoti paspaudimą nurodytoje vietoje PS4 jutiklinėje plokštėje. - enableCompatibilityCheckBox Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. - checkCompatibilityOnStartupCheckBox Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. - updateCompatibilityButton Update Compatibility Database:\nImmediately update the compatibility database. - Never Niekada - Idle Neaktyvus - Always Visada - Touchpad Left Jutiklinis Paviršius Kairėje - Touchpad Right Jutiklinis Paviršius Dešinėje - Touchpad Center Jutiklinis Paviršius Centre - None Nieko - graphicsAdapterGroupBox Grafikos įrenginys:\nDaugiagrafikėse sistemose pasirinkite GPU, kurį emuliatorius naudos iš išskleidžiamojo sąrašo,\n arba pasirinkite "Auto Select", kad jis būtų nustatytas automatiškai. - resolutionLayout Plotis/Aukštis:\nNustato emuliatoriaus lango dydį paleidimo metu, kurį galima keisti žaidimo metu.\nTai skiriasi nuo žaidimo rezoliucijos. - heightDivider Vblank daliklis:\nKadrų dažnis, kuriuo emuliatorius atnaujinamas, dauginamas iš šio skaičiaus. Pakeitus tai gali turėti neigiamą poveikį, pvz., padidinti žaidimo greitį arba sukelti kritinių žaidimo funkcijų sugadinimą, kurios to nesitikėjo! - dumpShadersCheckBox Įjungti šešėlių išmetimą:\nTechninio derinimo tikslais saugo žaidimo šešėlius į aplanką juos renderuojant. - nullGpuCheckBox Įjungti tuščią GPU:\nTechninio derinimo tikslais išjungia žaidimo renderiavimą, tarsi nebūtų grafikos plokštės. - gameFoldersBox Žaidimų aplankai:\nAplankų sąrašas, kurį reikia patikrinti, ar yra įdiegtų žaidimų. - addFolderButton Pridėti:\nPridėti aplanką į sąrašą. - removeFolderButton Pašalinti:\nPašalinti aplanką iš sąrašo. - debugDump Įjungti derinimo išmetimą:\nIšsaugo importo ir eksporto simbolius bei failo antraštės informaciją apie šiuo metu vykdomą PS4 programą į katalogą. - vkValidationCheckBox Įjungti Vulkan patvirtinimo sluoksnius:\nĮjungia sistemą, kuri patvirtina Vulkan renderio būseną ir registruoja informaciją apie jo vidinę būseną. Tai sumažins našumą ir tikriausiai pakeis emuliacijos elgesį. - vkSyncValidationCheckBox Įjungti Vulkan sinchronizacijos patvirtinimą:\nĮjungia sistemą, kuri patvirtina Vulkan renderavimo užduočių laiką. Tai sumažins našumą ir tikriausiai pakeis emuliacijos elgesį. - rdocCheckBox Įjungti RenderDoc derinimą:\nJei įjungta, emuliatorius suteiks suderinamumą su Renderdoc, kad būtų galima užfiksuoti ir analizuoti šiuo metu renderuojamą kadrą. + + CheatsPatches + + Cheats / Patches for + Cheats / Patches for + + + defaultTextEdit_MSG + Cheats/Patches yra eksperimentiniai.\nNaudokite atsargiai.\n\nAtsisiųskite cheats atskirai pasirinkdami saugyklą ir paspausdami atsisiuntimo mygtuką.\nPatches skirtuke galite atsisiųsti visus patch’us vienu metu, pasirinkti, kuriuos norite naudoti, ir išsaugoti pasirinkimą.\n\nKadangi mes nekurime Cheats/Patches,\npraneškite problemas cheat autoriui.\n\nSukūrėte naują cheat? Apsilankykite:\nhttps://github.com/shadps4-emu/ps4_cheats + + + No Image Available + Nuotrauka neprieinama + + + Serial: + Seriinis numeris: + + + Version: + Versija: + + + Size: + Dydis: + + + Select Cheat File: + Pasirinkite sukčiavimo failą: + + + Repository: + Saugykla: + + + Download Cheats + Atsisiųsti sukčiavimus + + + Delete File + Pašalinti failą + + + No files selected. + Failai nepasirinkti. + + + You can delete the cheats you don't want after downloading them. + Galite pašalinti sukčiavimus, kurių nenorite, juos atsisiuntę. + + + Do you want to delete the selected file?\n%1 + Ar norite ištrinti pasirinktą failą?\n%1 + + + Select Patch File: + Pasirinkite pataisos failą: + + + Download Patches + Atsisiųsti pataisas + + + Save + Įrašyti + + + Cheats + Sukčiavimai + + + Patches + Pataisos + + + Error + Klaida + + + No patch selected. + Nieko nepataisyta. + + + Unable to open files.json for reading. + Neįmanoma atidaryti files.json skaitymui. + + + No patch file found for the current serial. + Nepavyko rasti pataisos failo dabartiniam serijiniam numeriui. + + + Unable to open the file for reading. + Neįmanoma atidaryti failo skaitymui. + + + Unable to open the file for writing. + Neįmanoma atidaryti failo rašymui. + + + Failed to parse XML: + Nepavyko išanalizuoti XML: + + + Success + Sėkmė + + + Options saved successfully. + Nustatymai sėkmingai išsaugoti. + + + Invalid Source + Netinkamas šaltinis + + + The selected source is invalid. + Pasirinktas šaltinis yra netinkamas. + + + File Exists + Failas egzistuoja + + + File already exists. Do you want to replace it? + Failas jau egzistuoja. Ar norite jį pakeisti? + + + Failed to save file: + Nepavyko išsaugoti failo: + + + Failed to download file: + Nepavyko atsisiųsti failo: + + + Cheats Not Found + Sukčiavimai nerasti + + + CheatsNotFound_MSG + Nerasta sukčiavimų šiam žaidimui šioje pasirinktos saugyklos versijoje,bandykite kitą saugyklą arba skirtingą žaidimo versiją. + + + Cheats Downloaded Successfully + Sukčiavimai sėkmingai atsisiųsti + + + CheatsDownloadedSuccessfully_MSG + Sėkmingai atsisiuntėte sukčiavimus šios žaidimo versijos iš pasirinktos saugyklos. Galite pabandyti atsisiųsti iš kitos saugyklos, jei ji yra prieinama, taip pat bus galima ją naudoti pasirinkus failą iš sąrašo. + + + Failed to save: + Nepavyko išsaugoti: + + + Failed to download: + Nepavyko atsisiųsti: + + + Download Complete + Atsisiuntimas baigtas + + + DownloadComplete_MSG + Pataisos sėkmingai atsisiųstos! Visos pataisos visiems žaidimams buvo atsisiųstos, nebėra reikalo jas atsisiųsti atskirai kiekvienam žaidimui, kaip tai vyksta su sukčiavimais. Jei pleistras nepasirodo, gali būti, kad jo nėra tam tikram žaidimo serijos numeriui ir versijai. + + + Failed to parse JSON data from HTML. + Nepavyko išanalizuoti JSON duomenų iš HTML. + + + Failed to retrieve HTML page. + Nepavyko gauti HTML puslapio. + + + The game is in version: %1 + Žaidimas yra versijoje: %1 + + + The downloaded patch only works on version: %1 + Parsisiųstas pataisas veikia tik versijoje: %1 + + + You may need to update your game. + Gali tekti atnaujinti savo žaidimą. + + + Incompatibility Notice + Suderinamumo pranešimas + + + Failed to open file: + Nepavyko atidaryti failo: + + + XML ERROR: + XML KLAIDA: + + + Failed to open files.json for writing + Nepavyko atidaryti files.json rašymui + + + Author: + Autorius: + + + Directory does not exist: + Katalogas neegzistuoja: + + + Failed to open files.json for reading. + Nepavyko atidaryti files.json skaitymui. + + + Name: + Pavadinimas: + + + Can't apply cheats before the game is started + Negalima taikyti sukčiavimų prieš pradedant žaidimą. + + GameListFrame - Icon Ikona - Name Vardas - Serial Serijinis numeris - Compatibility Compatibility - Region Regionas - Firmware Firmvare - Size Dydis - Version Versija - Path Kelias - Play Time Žaidimo laikas - Never Played Never Played - h h - m m - s s - Compatibility is untested Compatibility is untested - Game does not initialize properly / crashes the emulator Game does not initialize properly / crashes the emulator - Game boots, but only displays a blank screen Game boots, but only displays a blank screen - Game displays an image but does not go past the menu Game displays an image but does not go past the menu - Game has game-breaking glitches or unplayable performance Game has game-breaking glitches or unplayable performance - Game can be completed with playable performance and no major glitches Game can be completed with playable performance and no major glitches @@ -1508,127 +1210,102 @@ CheckUpdate - Auto Updater Automatinis atnaujinimas - Error Klaida - Network error: Tinklo klaida: - Failed to parse update information. Nepavyko išanalizuoti atnaujinimo informacijos. - No pre-releases found. Išankstinių leidimų nerasta. - Invalid release data. Neteisingi leidimo duomenys. - No download URL found for the specified asset. Nerasta atsisiuntimo URL nurodytam turtui. - Your version is already up to date! Jūsų versija jau atnaujinta! - Update Available Prieinama atnaujinimas - Update Channel Atnaujinimo Kanalas - Current Version Esama versija - Latest Version Paskutinė versija - Do you want to update? Ar norite atnaujinti? - Show Changelog Rodyti pakeitimų sąrašą - Check for Updates at Startup Tikrinti naujinimus paleidus - Update Atnaujinti - No Ne - Hide Changelog Slėpti pakeitimų sąrašą - Changes Pokyčiai - Network error occurred while trying to access the URL Tinklo klaida bandant pasiekti URL - Download Complete Atsisiuntimas baigtas - The update has been downloaded, press OK to install. Atnaujinimas buvo atsisiųstas, paspauskite OK, kad įdiegtumėte. - Failed to save the update file at Nepavyko išsaugoti atnaujinimo failo - Starting Update... Pradedama atnaujinimas... - Failed to create the update script file Nepavyko sukurti atnaujinimo scenarijaus failo @@ -1636,29 +1313,24 @@ GameListUtils - B B - KB KB - MB MB - GB GB - TB TB - + \ No newline at end of file diff --git a/src/qt_gui/translations/nb.ts b/src/qt_gui/translations/nb.ts index 4d3c4f5af..711542773 100644 --- a/src/qt_gui/translations/nb.ts +++ b/src/qt_gui/translations/nb.ts @@ -6,22 +6,18 @@ AboutDialog - About shadPS4 Om shadPS4 - shadPS4 shadPS4 - shadPS4 is an experimental open-source emulator for the PlayStation 4. shadPS4 er en eksperimentell åpen kildekode-etterligner for PlayStation 4. - This software should not be used to play games you have not legally obtained. Denne programvaren skal ikke brukes til å spille spill du ikke har fått lovlig. @@ -29,7 +25,6 @@ ElfViewer - Open Folder Åpne mappe @@ -37,17 +32,14 @@ GameInfoClass - Loading game list, please wait :3 Laster spill-liste, vennligst vent :3 - Cancel Avbryt - Loading... Laster... @@ -55,12 +47,10 @@ InstallDirSelect - shadPS4 - Choose directory shadPS4 - Velg mappe - Select which directory you want to install to. Velg hvilken mappe du vil installere til. @@ -68,27 +58,22 @@ GameInstallDialog - shadPS4 - Choose directory shadPS4 - Velg mappe - Directory to install games Mappe for å installere spill - Browse Bla gjennom - Error Feil - The value for location to install games is not valid. Stien for å installere spillet er ikke gyldig. @@ -96,167 +81,134 @@ GuiContextMenus - Create Shortcut Lag snarvei - Cheats / Patches Juks / Programrettelse - SFO Viewer SFO viser - Trophy Viewer Trofé viser - Open Folder... Åpne mappen... - Open Game Folder Åpne spillmappen - Open Save Data Folder Åpne lagrede datamappen - Open Log Folder Åpne loggmappen - Copy info... Kopier info... - Copy Name Kopier navn - Copy Serial Kopier serienummer - Copy All Kopier alle - Delete... Slett... - Delete Game Slett spill - Delete Update Slett oppdatering - Delete DLC Slett DLC - Compatibility... Compatibility... - Update database Update database - View report View report - Submit a report Submit a report - Shortcut creation Snarvei opprettelse - Shortcut created successfully! Snarvei opprettet! - Error Feil - Error creating shortcut! Feil ved opprettelse av snarvei! - Install PKG Installer PKG - Game Spill - requiresEnableSeparateUpdateFolder_MSG Denne funksjonen krever 'Aktiver seperat oppdateringsmappe' konfigurasjonsalternativet. Hvis du vil bruke denne funksjonen, må du aktiver den. - This game has no update to delete! Dette spillet har ingen oppdatering å slette! - - + Update Oppdater - This game has no DLC to delete! Dette spillet har ingen DLC å slette! - DLC DLC - Delete %1 Slett %1 - Are you sure you want to delete %1's %2 directory? Er du sikker på at du vil slette %1's %2 directory? @@ -264,205 +216,285 @@ MainWindow - Open/Add Elf Folder Åpne/Legg til Elf-mappe - Install Packages (PKG) Installer pakker (PKG) - Boot Game Start spill - Check for Updates Se etter oppdateringer - About shadPS4 Om shadPS4 - Configure... Konfigurer... - Install application from a .pkg file Installer fra en .pkg fil - Recent Games Nylige spill - Exit Avslutt - Exit shadPS4 Avslutt shadPS4 - Exit the application. Avslutt programmet. - Show Game List Vis spill-listen - Game List Refresh Oppdater spill-listen - Tiny Bitteliten - Small Liten - Medium Medium - Large Stor - List View Liste-visning - Grid View Rute-visning - Elf Viewer Elf-visning - Game Install Directory Spillinstallasjons-mappe - Download Cheats/Patches Last ned juks/programrettelse - Dump Game List Dump spill-liste - PKG Viewer PKG viser - Search... Søk... - File Fil - View Oversikt - Game List Icons Spill-liste ikoner - Game List Mode Spill-liste modus - Settings Innstillinger - Utils Verktøy - Themes Tema - Help Hjelp - Dark Mørk - Light Lys - Green Grønn - Blue Blå - Violet Lilla - toolBar Verktøylinje + + Game List + Spill-liste + + + * Unsupported Vulkan Version + * Ustøttet Vulkan-versjon + + + Download Cheats For All Installed Games + Last ned juks for alle installerte spill + + + Download Patches For All Games + Last ned programrettelser for alle spill + + + Download Complete + Nedlasting fullført + + + You have downloaded cheats for all the games you have installed. + Du har lastet ned juks for alle spillene du har installert. + + + Patches Downloaded Successfully! + Programrettelser ble lastet ned! + + + All Patches available for all games have been downloaded. + Programrettelser tilgjengelige for alle spill har blitt lastet ned. + + + Games: + Spill: + + + PKG File (*.PKG) + PKG-fil (*.PKG) + + + ELF files (*.bin *.elf *.oelf) + ELF-filer (*.bin *.elf *.oelf) + + + Game Boot + Spilloppstart + + + Only one file can be selected! + Kun én fil kan velges! + + + PKG Extraction + PKG-utpakking + + + Patch detected! + Programrettelse oppdaget! + + + PKG and Game versions match: + PKG og spillversjoner stemmer overens: + + + Would you like to overwrite? + Ønsker du å overskrive? + + + PKG Version %1 is older than installed version: + PKG-versjon %1 er eldre enn installert versjon: + + + Game is installed: + Spillet er installert: + + + Would you like to install Patch: + Ønsker du å installere programrettelsen: + + + DLC Installation + DLC installasjon + + + Would you like to install DLC: %1? + Ønsker du å installere DLC: %1? + + + DLC already installed: + DLC allerede installert: + + + Game already installed + Spillet er allerede installert + + + PKG is a patch, please install the game first! + PKG er en programrettelse, vennligst installer spillet først! + + + PKG ERROR + PKG FEIL + + + Extracting PKG %1/%2 + Pakker ut PKG %1/%2 + + + Extraction Finished + Utpakking fullført + + + Game successfully installed at %1 + Spillet ble installert i %1 + + + File doesn't appear to be a valid PKG file + Filen ser ikke ut til å være en gyldig PKG-fil + PKGViewer - Open Folder Åpne mappe @@ -470,7 +502,6 @@ TrophyViewer - Trophy Viewer Trofé viser @@ -478,1029 +509,700 @@ SettingsDialog - Settings Innstillinger - General Generell - System System - Console Language Konsollspråk - Emulator Language Etterlignerspråk - Emulator Etterligner - Enable Fullscreen Aktiver fullskjerm - Enable Separate Update Folder Aktiver seperat oppdateringsmappe - Show Splash Vis velkomstbilde - Is PS4 Pro Er PS4 Pro - Enable Discord Rich Presence Aktiver Discord Rich Presence - Username Brukernavn - Trophy Key Trophy Key - Trophy Trophy - Logger Logger - Log Type Logg type - Log Filter Logg filter - Input Inndata - Cursor Musepeker - Hide Cursor Skjul musepeker - Hide Cursor Idle Timeout Skjul musepeker ved inaktivitet - s s - Controller Kontroller - Back Button Behavior Tilbakeknapp atferd - Graphics Grafikk - Graphics Device Grafikkenhet - Width Bredde - Height Høyde - Vblank Divider Vblank skillelinje - Advanced Avansert - Enable Shaders Dumping Aktiver dumping av skyggelegger - Enable NULL GPU Aktiver NULL GPU - Paths Stier - Game Folders Spillmapper - Add... Legg til... - Remove Fjern - Debug Feilretting - Enable Debug Dumping Aktiver dumping av feilretting - Enable Vulkan Validation Layers Aktiver Vulkan valideringslag - Enable Vulkan Synchronization Validation Aktiver Vulkan synkroniseringslag - Enable RenderDoc Debugging Aktiver RenderDoc feilretting - Update Oppdatering - Check for Updates at Startup Se etter oppdateringer ved oppstart - Update Channel Oppdateringskanal - Check for Updates Se etter oppdateringer - GUI Settings GUI-innstillinger - Disable Trophy Pop-ups Deaktiver trofé hurtigmeny - Play title music Spill tittelmusikk - Update Compatibility Database On Startup Oppdater kompatibilitets-database ved oppstart - Game Compatibility Spill kompatibilitet - Display Compatibility Data Vis kompatibilitets-data - Update Compatibility Database Oppdater kompatibilitets-database - Volume Volum - Audio Backend Audio Backend - - - MainWindow - - Game List - Spill-liste - - - - * Unsupported Vulkan Version - * Ustøttet Vulkan-versjon - - - - Download Cheats For All Installed Games - Last ned juks for alle installerte spill - - - - Download Patches For All Games - Last ned programrettelser for alle spill - - - - Download Complete - Nedlasting fullført - - - - You have downloaded cheats for all the games you have installed. - Du har lastet ned juks for alle spillene du har installert. - - - - Patches Downloaded Successfully! - Programrettelser ble lastet ned! - - - - All Patches available for all games have been downloaded. - Programrettelser tilgjengelige for alle spill har blitt lastet ned. - - - - Games: - Spill: - - - - PKG File (*.PKG) - PKG-fil (*.PKG) - - - - ELF files (*.bin *.elf *.oelf) - ELF-filer (*.bin *.elf *.oelf) - - - - Game Boot - Spilloppstart - - - - Only one file can be selected! - Kun én fil kan velges! - - - - PKG Extraction - PKG-utpakking - - - - Patch detected! - Programrettelse oppdaget! - - - - PKG and Game versions match: - PKG og spillversjoner stemmer overens: - - - - Would you like to overwrite? - Ønsker du å overskrive? - - - - PKG Version %1 is older than installed version: - PKG-versjon %1 er eldre enn installert versjon: - - - - Game is installed: - Spillet er installert: - - - - Would you like to install Patch: - Ønsker du å installere programrettelsen: - - - - DLC Installation - DLC installasjon - - - - Would you like to install DLC: %1? - Ønsker du å installere DLC: %1? - - - - DLC already installed: - DLC allerede installert: - - - - Game already installed - Spillet er allerede installert - - - - PKG is a patch, please install the game first! - PKG er en programrettelse, vennligst installer spillet først! - - - - PKG ERROR - PKG FEIL - - - - Extracting PKG %1/%2 - Pakker ut PKG %1/%2 - - - - Extraction Finished - Utpakking fullført - - - - Game successfully installed at %1 - Spillet ble installert i %1 - - - - File doesn't appear to be a valid PKG file - Filen ser ikke ut til å være en gyldig PKG-fil - - - - CheatsPatches - - - Cheats / Patches for - Juks / Programrettelser for - - - - defaultTextEdit_MSG - Juks/programrettelse er eksperimentelle.\nBruk med forsiktighet.\n\nLast ned juks individuelt ved å velge pakkebrønn og klikke på nedlastingsknappen.\nPå fanen programrettelse kan du laste ned alle programrettelser samtidig, velge hvilke du ønsker å bruke, og lagre valget ditt.\n\nSiden vi ikke utvikler Juks/Programrettelse,\nvær vennlig å rapportere problemer til juks/programrettelse utvikleren.\n\nHar du laget en ny juks? Besøk:\nhttps://github.com/shadps4-emu/ps4_cheats - - - - No Image Available - Ingen bilde tilgjengelig - - - - Serial: - Serienummer: - - - - Version: - Versjon: - - - - Size: - Størrelse: - - - - Select Cheat File: - Velg juksefil: - - - - Repository: - Pakkebrønn: - - - - Download Cheats - Last ned juks - - - - Delete File - Slett fil - - - - No files selected. - Ingen filer valgt. - - - - You can delete the cheats you don't want after downloading them. - Du kan slette juks du ikke ønsker etter å ha lastet dem ned. - - - - Do you want to delete the selected file?\n%1 - Ønsker du å slette den valgte filen?\n%1 - - - - Select Patch File: - Velg programrettelse-filen: - - - - Download Patches - Last ned programrettelser - - - Save Lagre - - Cheats - Juks - - - - Patches - Programrettelse - - - - Error - Feil - - - - No patch selected. - Ingen programrettelse valgt. - - - - Unable to open files.json for reading. - Kan ikke åpne files.json for lesing. - - - - No patch file found for the current serial. - Ingen programrettelse-fil funnet for det aktuelle serienummeret. - - - - Unable to open the file for reading. - Kan ikke åpne filen for lesing. - - - - Unable to open the file for writing. - Kan ikke åpne filen for skriving. - - - - Failed to parse XML: - Feil ved tolkning av XML: - - - - Success - Vellykket - - - - Options saved successfully. - Alternativer ble lagret. - - - - Invalid Source - Ugyldig kilde - - - - The selected source is invalid. - Den valgte kilden er ugyldig. - - - - File Exists - Filen eksisterer - - - - File already exists. Do you want to replace it? - Filen eksisterer allerede. Ønsker du å erstatte den? - - - - Failed to save file: - Kunne ikke lagre filen: - - - - Failed to download file: - Kunne ikke laste ned filen: - - - - Cheats Not Found - Fant ikke juks - - - - CheatsNotFound_MSG - Ingen juks funnet for dette spillet i denne versjonen av den valgte pakkebrønnen,prøv en annen pakkebrønn eller en annen versjon av spillet. - - - - Cheats Downloaded Successfully - Juks ble lastet ned - - - - CheatsDownloadedSuccessfully_MSG - Du har lastet ned juks for denne versjonen av spillet fra den valgte pakkebrønnen. Du kan prøve å laste ned fra en annen pakkebrønn, hvis det er tilgjengelig, vil det også være mulig å bruke det ved å velge filen fra listen. - - - - Failed to save: - Kunne ikke lagre: - - - - Failed to download: - Kunne ikke laste ned: - - - - Download Complete - Nedlasting fullført - - - - DownloadComplete_MSG - Programrettelser ble lastet ned! Alle programrettelsene tilgjengelige for alle spill har blitt lastet ned, det er ikke nødvendig å laste dem ned individuelt for hvert spill som skjer med juks. Hvis programrettelsen ikke vises, kan det hende at den ikke finnes for den spesifikke serienummeret og versjonen av spillet. - - - - Failed to parse JSON data from HTML. - Kunne ikke analysere JSON-data fra HTML. - - - - Failed to retrieve HTML page. - Kunne ikke hente HTML-side. - - - - The game is in version: %1 - Spillet er i versjon: %1 - - - - The downloaded patch only works on version: %1 - Den nedlastede programrettelsen fungerer bare på versjon: %1 - - - - You may need to update your game. - Du må kanskje oppdatere spillet ditt. - - - - Incompatibility Notice - Inkompatibilitets-varsel - - - - Failed to open file: - Kunne ikke åpne filen: - - - - XML ERROR: - XML FEIL: - - - - Failed to open files.json for writing - Kunne ikke åpne files.json for skriving - - - - Author: - Forfatter: - - - - Directory does not exist: - Mappen eksisterer ikke: - - - - Failed to open files.json for reading. - Kunne ikke åpne files.json for lesing. - - - - Name: - Navn: - - - - Can't apply cheats before the game is started - Kan ikke bruke juks før spillet er startet. - - - - SettingsDialog - - - Save - Lagre - - - Apply Bruk - Restore Defaults Gjenopprett standardinnstillinger - Close Lukk - Point your mouse at an option to display its description. Pek musen over et alternativ for å vise beskrivelsen. - consoleLanguageGroupBox Konsollspråk:\nAngir språket som PS4-spillet bruker.\nDet anbefales å sette dette til et språk som spillet støtter, noe som kan variere avhengig av region. - emulatorLanguageGroupBox Etterlignerspråket:\nAngir språket for etterlignerens brukergrensesnitt. - fullscreenCheckBox Aktiver fullskjerm:\nSetter spillvinduet automatisk i fullskjermmodus.\nDette kan slås av ved å trykke på F11-tasten. - separateUpdatesCheckBox Aktiver separat oppdateringsmappe:\nAktiverer installering av spill i en egen mappe for enkel administrasjon. - showSplashCheckBox Vis velkomstbilde:\nViser spillets velkomstbilde (et spesialbilde) når spillet starter. - ps4proCheckBox Er PS4 Pro:\nFår etterligneren til å fungere som en PS4 PRO, noe som kan aktivere spesielle funksjoner i spill som støtter dette. - discordRPCCheckbox Aktiver Discord Rich Presence:\nViser etterlignerikonet og relevant informasjon på Discord-profilen din. - userName Brukernavn:\nAngir brukernavnet for PS4-kontoen, som kan vises av enkelte spill. - TrophyKey Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. - logTypeGroupBox Logg type:\nAngir om loggvinduets utdata skal synkroniseres for ytelse. Kan ha negative effekter for etterligneren. - logFilter Logg filter:\nFiltrerer loggen for å kun skrive ut spesifikk informasjon.\nEksempler: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Nivåer: Trace, Debug, Info, Warning, Error, Critical - i denne rekkefølgen, et spesifikt nivå demper alle tidligere nivåer i listen og logger alle nivåer etter det. - updaterGroupBox Oppdatering:\nRelease: Offisielle versjoner utgitt hver måned som kan være veldig utdaterte, men er mer pålitelige og testet.\nNightly: Utviklingsversjoner som har alle de nyeste funksjonene og feilrettingene, men som kan inneholde feil og er mindre stabile. - GUIgroupBox Spille tittelmusikk:\nHvis et spill støtter det, så aktiveres det spesiell musikk når du velger spillet i menyen. - disableTrophycheckBox Deaktiver trofé hurtigmeny:\nDeaktiver trofévarsler i spillet. Trofé-fremgang kan fortsatt ved help av troféviseren (høyreklikk på spillet i hovedvinduet). - hideCursorGroupBox Skjul musepeker:\nVelg når musepekeren skal forsvinne:\nAldri: Du vil alltid se musepekeren.\nInaktiv: Sett en tid for at den skal forsvinne etter å ha vært inaktiv.\nAlltid: du vil aldri se musepekeren. - idleTimeoutGroupBox Sett en tid for når musepekeren forsvinner etter å ha vært inaktiv. - backButtonBehaviorGroupBox Atferd for tilbaketasten:\nSetter tilbaketasten på kontrolleren til å imitere et trykk på den angitte posisjonen på PS4s berøringsplate. - enableCompatibilityCheckBox Vis kompatibilitets-data:\nViser informasjon om spillkompatibilitet i tabellvisning. Aktiver "Oppdater kompatibilitets-data ved oppstart" for oppdatert informasjon. - checkCompatibilityOnStartupCheckBox Oppdater kompatibilitets-data ved oppstart:\nOppdaterer kompatibilitets-databasen automatisk når shadPS4 starter. - updateCompatibilityButton Oppdater kompatibilitets-database:\nOppdater kompatibilitets-databasen nå. - Never Aldri - Idle Inaktiv - Always Alltid - Touchpad Left Berøringsplate Venstre - Touchpad Right Berøringsplate Høyre - Touchpad Center Berøringsplate Midt - None Ingen - graphicsAdapterGroupBox Grafikkenhet:\nI systemer med flere GPU-er, velg GPU-en etterligneren skal bruke fra rullegardinlisten,\neller velg "Auto Select" for å velge automatisk. - resolutionLayout Bredde/Høyde:\nAngir størrelsen på etterlignerkvinduet ved oppstart, som kan endres under spillingen.\nDette er forskjellig fra oppløsningen i spillet. - heightDivider Vblank skillelinje:\nBildehastigheten som etterligneren oppdaterer ved, multipliseres med dette tallet. Endring av dette kan ha negative effekter, som å øke hastigheten av spillet, eller ødelegge kritisk spillfunksjonalitet som ikke forventer at dette endres! - dumpShadersCheckBox Aktiver dumping av skyggelegger:\nFor teknisk feilsøking lagrer skyggeleggerne fra spillet i en mappe mens de gjengis. - nullGpuCheckBox Aktiver Null GPU:\nFor teknisk feilsøking deaktiverer spillets-gjengivelse som om det ikke var noe grafikkort. - gameFoldersBox Spillmapper:\nListen over mapper som brukes for å se etter installerte spill. - addFolderButton Legg til:\nLegg til en mappe til listen. - removeFolderButton Fjern:\nFjern en mappe fra listen. - debugDump Aktiver dumping av feilsøking:\nLagrer import- og eksport-symbolene og filoverskriftsinformasjonen til det nåværende kjørende PS4-programmet i en katalog. - vkValidationCheckBox Aktiver Vulkan valideringslag:\nAktiverer et system som validerer tilstanden til Vulkan-gjengiveren og logger informasjon om dens indre tilstand. Dette vil redusere ytelsen og sannsynligvis endre etterlignerens atferd. - vkSyncValidationCheckBox Aktiver Vulkan synkronisering validering:\nAktiverer et system som validerer frekvens tiden av Vulkan-gjengivelsensoppgaver. Dette vil redusere ytelsen og sannsynligvis endre etterlignerens atferd. - rdocCheckBox Aktiver RenderDoc feilsøking:\nHvis aktivert, vil etterligneren gi kompatibilitet med Renderdoc for å tillate opptak og analyse av det nåværende gjengitte bildet. + + CheatsPatches + + Cheats / Patches for + Juks / Programrettelser for + + + defaultTextEdit_MSG + Juks/programrettelse er eksperimentelle.\nBruk med forsiktighet.\n\nLast ned juks individuelt ved å velge pakkebrønn og klikke på nedlastingsknappen.\nPå fanen programrettelse kan du laste ned alle programrettelser samtidig, velge hvilke du ønsker å bruke, og lagre valget ditt.\n\nSiden vi ikke utvikler Juks/Programrettelse,\nvær vennlig å rapportere problemer til juks/programrettelse utvikleren.\n\nHar du laget en ny juks? Besøk:\nhttps://github.com/shadps4-emu/ps4_cheats + + + No Image Available + Ingen bilde tilgjengelig + + + Serial: + Serienummer: + + + Version: + Versjon: + + + Size: + Størrelse: + + + Select Cheat File: + Velg juksefil: + + + Repository: + Pakkebrønn: + + + Download Cheats + Last ned juks + + + Delete File + Slett fil + + + No files selected. + Ingen filer valgt. + + + You can delete the cheats you don't want after downloading them. + Du kan slette juks du ikke ønsker etter å ha lastet dem ned. + + + Do you want to delete the selected file?\n%1 + Ønsker du å slette den valgte filen?\n%1 + + + Select Patch File: + Velg programrettelse-filen: + + + Download Patches + Last ned programrettelser + + + Save + Lagre + + + Cheats + Juks + + + Patches + Programrettelse + + + Error + Feil + + + No patch selected. + Ingen programrettelse valgt. + + + Unable to open files.json for reading. + Kan ikke åpne files.json for lesing. + + + No patch file found for the current serial. + Ingen programrettelse-fil funnet for det aktuelle serienummeret. + + + Unable to open the file for reading. + Kan ikke åpne filen for lesing. + + + Unable to open the file for writing. + Kan ikke åpne filen for skriving. + + + Failed to parse XML: + Feil ved tolkning av XML: + + + Success + Vellykket + + + Options saved successfully. + Alternativer ble lagret. + + + Invalid Source + Ugyldig kilde + + + The selected source is invalid. + Den valgte kilden er ugyldig. + + + File Exists + Filen eksisterer + + + File already exists. Do you want to replace it? + Filen eksisterer allerede. Ønsker du å erstatte den? + + + Failed to save file: + Kunne ikke lagre filen: + + + Failed to download file: + Kunne ikke laste ned filen: + + + Cheats Not Found + Fant ikke juks + + + CheatsNotFound_MSG + Ingen juks funnet for dette spillet i denne versjonen av den valgte pakkebrønnen,prøv en annen pakkebrønn eller en annen versjon av spillet. + + + Cheats Downloaded Successfully + Juks ble lastet ned + + + CheatsDownloadedSuccessfully_MSG + Du har lastet ned juks for denne versjonen av spillet fra den valgte pakkebrønnen. Du kan prøve å laste ned fra en annen pakkebrønn, hvis det er tilgjengelig, vil det også være mulig å bruke det ved å velge filen fra listen. + + + Failed to save: + Kunne ikke lagre: + + + Failed to download: + Kunne ikke laste ned: + + + Download Complete + Nedlasting fullført + + + DownloadComplete_MSG + Programrettelser ble lastet ned! Alle programrettelsene tilgjengelige for alle spill har blitt lastet ned, det er ikke nødvendig å laste dem ned individuelt for hvert spill som skjer med juks. Hvis programrettelsen ikke vises, kan det hende at den ikke finnes for den spesifikke serienummeret og versjonen av spillet. + + + Failed to parse JSON data from HTML. + Kunne ikke analysere JSON-data fra HTML. + + + Failed to retrieve HTML page. + Kunne ikke hente HTML-side. + + + The game is in version: %1 + Spillet er i versjon: %1 + + + The downloaded patch only works on version: %1 + Den nedlastede programrettelsen fungerer bare på versjon: %1 + + + You may need to update your game. + Du må kanskje oppdatere spillet ditt. + + + Incompatibility Notice + Inkompatibilitets-varsel + + + Failed to open file: + Kunne ikke åpne filen: + + + XML ERROR: + XML FEIL: + + + Failed to open files.json for writing + Kunne ikke åpne files.json for skriving + + + Author: + Forfatter: + + + Directory does not exist: + Mappen eksisterer ikke: + + + Failed to open files.json for reading. + Kunne ikke åpne files.json for lesing. + + + Name: + Navn: + + + Can't apply cheats before the game is started + Kan ikke bruke juks før spillet er startet. + + GameListFrame - Icon Ikon - Name Navn - Serial Serienummer - Compatibility Kompatibilitet - Region Region - Firmware Fastvare - Size Størrelse - Version Versjon - Path Sti - Play Time Spilletid - Never Played Aldri spilt - h h - m m - s s - Compatibility is untested kompatibilitet er utestet - Game does not initialize properly / crashes the emulator Spillet initialiseres ikke riktig / krasjer etterligneren - Game boots, but only displays a blank screen Spillet starter, men viser bare en tom skjerm - Game displays an image but does not go past the menu Spillet viser et bilde, men går ikke forbi menyen - Game has game-breaking glitches or unplayable performance Spillet har spillbrytende feil eller uspillbar ytelse - Game can be completed with playable performance and no major glitches Spillet kan fullføres med spillbar ytelse og ingen store feil @@ -1508,127 +1210,102 @@ CheckUpdate - Auto Updater Automatisk oppdatering - Error Feil - Network error: Nettverksfeil: - Failed to parse update information. Kunne ikke analysere oppdaterings-informasjonen. - No pre-releases found. Fant ingen forhåndsutgivelser. - Invalid release data. Ugyldige utgivelsesdata. - No download URL found for the specified asset. Ingen nedlastings-URL funnet for den spesifiserte ressursen. - Your version is already up to date! Din versjon er allerede oppdatert! - Update Available Oppdatering tilgjengelig - Update Channel Oppdateringskanal - Current Version Gjeldende versjon - Latest Version Nyeste versjon - Do you want to update? Vil du oppdatere? - Show Changelog Vis endringslogg - Check for Updates at Startup Se etter oppdateringer ved oppstart - Update Oppdater - No Nei - Hide Changelog Skjul endringslogg - Changes Endringer - Network error occurred while trying to access the URL Nettverksfeil oppstod mens vi prøvde å få tilgang til URL - Download Complete Nedlasting fullført - The update has been downloaded, press OK to install. Oppdateringen har blitt lastet ned, trykk OK for å installere. - Failed to save the update file at Kunne ikke lagre oppdateringsfilen på - Starting Update... Starter oppdatering... - Failed to create the update script file Kunne ikke opprette oppdateringsskriptfilen @@ -1636,29 +1313,24 @@ GameListUtils - B B - KB KB - MB MB - GB GB - TB TB - + \ No newline at end of file diff --git a/src/qt_gui/translations/nl.ts b/src/qt_gui/translations/nl.ts index 0cb890186..2aa996413 100644 --- a/src/qt_gui/translations/nl.ts +++ b/src/qt_gui/translations/nl.ts @@ -6,22 +6,18 @@ AboutDialog - About shadPS4 About shadPS4 - shadPS4 shadPS4 - shadPS4 is an experimental open-source emulator for the PlayStation 4. shadPS4 is an experimental open-source emulator for the PlayStation 4. - This software should not be used to play games you have not legally obtained. This software should not be used to play games you have not legally obtained. @@ -29,7 +25,6 @@ ElfViewer - Open Folder Open Folder @@ -37,17 +32,14 @@ GameInfoClass - Loading game list, please wait :3 Loading game list, please wait :3 - Cancel Cancel - Loading... Loading... @@ -55,12 +47,10 @@ InstallDirSelect - shadPS4 - Choose directory shadPS4 - Choose directory - Select which directory you want to install to. Select which directory you want to install to. @@ -68,27 +58,22 @@ GameInstallDialog - shadPS4 - Choose directory shadPS4 - Choose directory - Directory to install games Directory to install games - Browse Browse - Error Error - The value for location to install games is not valid. The value for location to install games is not valid. @@ -96,167 +81,134 @@ GuiContextMenus - Create Shortcut Create Shortcut - Cheats / Patches Cheats / Patches - SFO Viewer SFO Viewer - Trophy Viewer Trophy Viewer - Open Folder... Map openen... - Open Game Folder Open Spelmap - Open Save Data Folder Open Map voor Opslagdata - Open Log Folder Open Logmap - Copy info... Copy info... - Copy Name Copy Name - Copy Serial Copy Serial - Copy All Copy All - Delete... Delete... - Delete Game Delete Game - Delete Update Delete Update - Delete DLC Delete DLC - Compatibility... Compatibility... - Update database Update database - View report View report - Submit a report Submit a report - Shortcut creation Shortcut creation - Shortcut created successfully! Shortcut created successfully! - Error Error - Error creating shortcut! Error creating shortcut! - Install PKG Install PKG - Game Game - requiresEnableSeparateUpdateFolder_MSG This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. - This game has no update to delete! This game has no update to delete! - - + Update Update - This game has no DLC to delete! This game has no DLC to delete! - DLC DLC - Delete %1 Delete %1 - Are you sure you want to delete %1's %2 directory? Are you sure you want to delete %1's %2 directory? @@ -264,205 +216,285 @@ MainWindow - Open/Add Elf Folder Open/Add Elf Folder - Install Packages (PKG) Install Packages (PKG) - Boot Game Boot Game - Check for Updates Controleren op updates - About shadPS4 About shadPS4 - Configure... Configure... - Install application from a .pkg file Install application from a .pkg file - Recent Games Recent Games - Exit Exit - Exit shadPS4 Exit shadPS4 - Exit the application. Exit the application. - Show Game List Show Game List - Game List Refresh Game List Refresh - Tiny Tiny - Small Small - Medium Medium - Large Large - List View List View - Grid View Grid View - Elf Viewer Elf Viewer - Game Install Directory Game Install Directory - Download Cheats/Patches Download Cheats/Patches - Dump Game List Dump Game List - PKG Viewer PKG Viewer - Search... Search... - File File - View View - Game List Icons Game List Icons - Game List Mode Game List Mode - Settings Settings - Utils Utils - Themes Themes - Help Help - Dark Dark - Light Light - Green Green - Blue Blue - Violet Violet - toolBar toolBar + + Game List + Lijst met spellen + + + * Unsupported Vulkan Version + * Niet ondersteunde Vulkan-versie + + + Download Cheats For All Installed Games + Download cheats voor alle geïnstalleerde spellen + + + Download Patches For All Games + Download patches voor alle spellen + + + Download Complete + Download voltooid + + + You have downloaded cheats for all the games you have installed. + Je hebt cheats gedownload voor alle spellen die je hebt geïnstalleerd. + + + Patches Downloaded Successfully! + Patches succesvol gedownload! + + + All Patches available for all games have been downloaded. + Alle patches voor alle spellen zijn gedownload. + + + Games: + Spellen: + + + PKG File (*.PKG) + PKG-bestand (*.PKG) + + + ELF files (*.bin *.elf *.oelf) + ELF-bestanden (*.bin *.elf *.oelf) + + + Game Boot + Spelopstart + + + Only one file can be selected! + Je kunt slechts één bestand selecteren! + + + PKG Extraction + PKG-extractie + + + Patch detected! + Patch gedetecteerd! + + + PKG and Game versions match: + PKG- en gameversies komen overeen: + + + Would you like to overwrite? + Wilt u overschrijven? + + + PKG Version %1 is older than installed version: + PKG-versie %1 is ouder dan de geïnstalleerde versie: + + + Game is installed: + Game is geïnstalleerd: + + + Would you like to install Patch: + Wilt u de patch installeren: + + + DLC Installation + DLC-installatie + + + Would you like to install DLC: %1? + Wilt u DLC installeren: %1? + + + DLC already installed: + DLC al geïnstalleerd: + + + Game already installed + Game al geïnstalleerd + + + PKG is a patch, please install the game first! + PKG is een patch, installeer eerst het spel! + + + PKG ERROR + PKG FOUT + + + Extracting PKG %1/%2 + PKG %1/%2 aan het extraheren + + + Extraction Finished + Extractie voltooid + + + Game successfully installed at %1 + Spel succesvol geïnstalleerd op %1 + + + File doesn't appear to be a valid PKG file + Het bestand lijkt geen geldig PKG-bestand te zijn + PKGViewer - Open Folder Open Folder @@ -470,7 +502,6 @@ TrophyViewer - Trophy Viewer Trophy Viewer @@ -478,1029 +509,700 @@ SettingsDialog - Settings Settings - General General - System System - Console Language Console Language - Emulator Language Emulator Language - Emulator Emulator - Enable Fullscreen Enable Fullscreen - Enable Separate Update Folder Enable Separate Update Folder - Show Splash Show Splash - Is PS4 Pro Is PS4 Pro - Enable Discord Rich Presence Discord Rich Presence inschakelen - Username Username - Trophy Key Trophy Key - Trophy Trophy - Logger Logger - Log Type Log Type - Log Filter Log Filter - Input Invoer - Cursor Cursor - Hide Cursor Cursor verbergen - Hide Cursor Idle Timeout Inactiviteit timeout voor het verbergen van de cursor - s s - Controller Controller - Back Button Behavior Achterknop gedrag - Graphics Graphics - Graphics Device Graphics Device - Width Width - Height Height - Vblank Divider Vblank Divider - Advanced Advanced - Enable Shaders Dumping Enable Shaders Dumping - Enable NULL GPU Enable NULL GPU - Paths Pad - Game Folders Spelmappen - Add... Toevoegen... - Remove Verwijderen - Debug Debug - Enable Debug Dumping Enable Debug Dumping - Enable Vulkan Validation Layers Enable Vulkan Validation Layers - Enable Vulkan Synchronization Validation Enable Vulkan Synchronization Validation - Enable RenderDoc Debugging Enable RenderDoc Debugging - Update Bijwerken - Check for Updates at Startup Bij opstart op updates controleren - Update Channel Updatekanaal - Check for Updates Controleren op updates - GUI Settings GUI-Instellingen - Disable Trophy Pop-ups Disable Trophy Pop-ups - Play title music Titelmuziek afspelen - Update Compatibility Database On Startup Update Compatibility Database On Startup - Game Compatibility Game Compatibility - Display Compatibility Data Display Compatibility Data - Update Compatibility Database Update Compatibility Database - Volume Volume - Audio Backend Audio Backend - - - MainWindow - - Game List - Lijst met spellen - - - - * Unsupported Vulkan Version - * Niet ondersteunde Vulkan-versie - - - - Download Cheats For All Installed Games - Download cheats voor alle geïnstalleerde spellen - - - - Download Patches For All Games - Download patches voor alle spellen - - - - Download Complete - Download voltooid - - - - You have downloaded cheats for all the games you have installed. - Je hebt cheats gedownload voor alle spellen die je hebt geïnstalleerd. - - - - Patches Downloaded Successfully! - Patches succesvol gedownload! - - - - All Patches available for all games have been downloaded. - Alle patches voor alle spellen zijn gedownload. - - - - Games: - Spellen: - - - - PKG File (*.PKG) - PKG-bestand (*.PKG) - - - - ELF files (*.bin *.elf *.oelf) - ELF-bestanden (*.bin *.elf *.oelf) - - - - Game Boot - Spelopstart - - - - Only one file can be selected! - Je kunt slechts één bestand selecteren! - - - - PKG Extraction - PKG-extractie - - - - Patch detected! - Patch gedetecteerd! - - - - PKG and Game versions match: - PKG- en gameversies komen overeen: - - - - Would you like to overwrite? - Wilt u overschrijven? - - - - PKG Version %1 is older than installed version: - PKG-versie %1 is ouder dan de geïnstalleerde versie: - - - - Game is installed: - Game is geïnstalleerd: - - - - Would you like to install Patch: - Wilt u de patch installeren: - - - - DLC Installation - DLC-installatie - - - - Would you like to install DLC: %1? - Wilt u DLC installeren: %1? - - - - DLC already installed: - DLC al geïnstalleerd: - - - - Game already installed - Game al geïnstalleerd - - - - PKG is a patch, please install the game first! - PKG is een patch, installeer eerst het spel! - - - - PKG ERROR - PKG FOUT - - - - Extracting PKG %1/%2 - PKG %1/%2 aan het extraheren - - - - Extraction Finished - Extractie voltooid - - - - Game successfully installed at %1 - Spel succesvol geïnstalleerd op %1 - - - - File doesn't appear to be a valid PKG file - Het bestand lijkt geen geldig PKG-bestand te zijn - - - - CheatsPatches - - - Cheats / Patches for - Cheats / Patches for - - - - defaultTextEdit_MSG - Cheats/Patches zijn experimenteel.\nGebruik met voorzichtigheid.\n\nDownload cheats afzonderlijk door het repository te selecteren en op de downloadknop te klikken.\nOp het tabblad Patches kun je alle patches tegelijk downloaden, kiezen welke je wilt gebruiken en je selectie opslaan.\n\nAangezien wij de Cheats/Patches niet ontwikkelen,\nmeld problemen bij de auteur van de cheat.\n\nHeb je een nieuwe cheat gemaakt? Bezoek:\nhttps://github.com/shadps4-emu/ps4_cheats - - - - No Image Available - Geen afbeelding beschikbaar - - - - Serial: - Serie: - - - - Version: - Versie: - - - - Size: - Grootte: - - - - Select Cheat File: - Selecteer cheatbestand: - - - - Repository: - Repository: - - - - Download Cheats - Download cheats - - - - Delete File - Bestand verwijderen - - - - No files selected. - Geen bestanden geselecteerd. - - - - You can delete the cheats you don't want after downloading them. - Je kunt de cheats die je niet wilt verwijderen nadat je ze hebt gedownload. - - - - Do you want to delete the selected file?\n%1 - Wil je het geselecteerde bestand verwijderen?\n%1 - - - - Select Patch File: - Selecteer patchbestand: - - - - Download Patches - Download patches - - - Save Opslaan - - Cheats - Cheats - - - - Patches - Patches - - - - Error - Fout - - - - No patch selected. - Geen patch geselecteerd. - - - - Unable to open files.json for reading. - Kan files.json niet openen voor lezen. - - - - No patch file found for the current serial. - Geen patchbestand gevonden voor het huidige serienummer. - - - - Unable to open the file for reading. - Kan het bestand niet openen voor lezen. - - - - Unable to open the file for writing. - Kan het bestand niet openen voor schrijven. - - - - Failed to parse XML: - XML parsing mislukt: - - - - Success - Succes - - - - Options saved successfully. - Opties succesvol opgeslagen. - - - - Invalid Source - Ongeldige bron - - - - The selected source is invalid. - De geselecteerde bron is ongeldig. - - - - File Exists - Bestand bestaat - - - - File already exists. Do you want to replace it? - Bestand bestaat al. Wil je het vervangen? - - - - Failed to save file: - Kan bestand niet opslaan: - - - - Failed to download file: - Kan bestand niet downloaden: - - - - Cheats Not Found - Cheats niet gevonden - - - - CheatsNotFound_MSG - Geen cheats gevonden voor deze game in deze versie van de geselecteerde repository.Probeer een andere repository of een andere versie van het spel. - - - - Cheats Downloaded Successfully - Cheats succesvol gedownload - - - - CheatsDownloadedSuccessfully_MSG - Je hebt cheats succesvol gedownload voor deze versie van het spel uit de geselecteerde repository. Je kunt proberen te downloaden van een andere repository. Als deze beschikbaar is, kan het ook worden gebruikt door het bestand uit de lijst te selecteren. - - - - Failed to save: - Opslaan mislukt: - - - - Failed to download: - Downloaden mislukt: - - - - Download Complete - Download voltooid - - - - DownloadComplete_MSG - Patches succesvol gedownload! Alle beschikbare patches voor alle spellen zijn gedownload. Het is niet nodig om ze afzonderlijk te downloaden voor elk spel dat cheats heeft. Als de patch niet verschijnt, kan het zijn dat deze niet bestaat voor het specifieke serienummer en de versie van het spel. - - - - Failed to parse JSON data from HTML. - Kan JSON-gegevens uit HTML niet parseren. - - - - Failed to retrieve HTML page. - Kan HTML-pagina niet ophalen. - - - - The game is in version: %1 - Het spel is in versie: %1 - - - - The downloaded patch only works on version: %1 - De gedownloade patch werkt alleen op versie: %1 - - - - You may need to update your game. - Misschien moet je je spel bijwerken. - - - - Incompatibility Notice - Incompatibiliteitsmelding - - - - Failed to open file: - Kan bestand niet openen: - - - - XML ERROR: - XML FOUT: - - - - Failed to open files.json for writing - Kan files.json niet openen voor schrijven - - - - Author: - Auteur: - - - - Directory does not exist: - Map bestaat niet: - - - - Failed to open files.json for reading. - Kan files.json niet openen voor lezen. - - - - Name: - Naam: - - - - Can't apply cheats before the game is started - Je kunt geen cheats toepassen voordat het spel is gestart. - - - - SettingsDialog - - - Save - Opslaan - - - Apply Toepassen - Restore Defaults Standaardinstellingen herstellen - Close Sluiten - Point your mouse at an option to display its description. Wijzig de muisaanwijzer naar een optie om de beschrijving weer te geven. - consoleLanguageGroupBox Console Taal:\nStelt de taal in die het PS4-spel gebruikt.\nHet wordt aanbevolen om dit in te stellen op een taal die het spel ondersteunt, wat kan variëren per regio. - emulatorLanguageGroupBox Emulator Taal:\nStelt de taal van de gebruikersinterface van de emulator in. - fullscreenCheckBox Volledig scherm inschakelen:\nZet het gamevenster automatisch in de volledig scherm modus.\nDit kan worden omgeschakeld door op de F11-toets te drukken. - separateUpdatesCheckBox Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. - showSplashCheckBox Opstartscherm weergeven:\nToont het opstartscherm van het spel (een speciale afbeelding) tijdens het starten van het spel. - ps4proCheckBox Is PS4 Pro:\nLaat de emulator zich gedragen als een PS4 PRO, wat speciale functies kan inschakelen in games die dit ondersteunen. - discordRPCCheckbox Discord Rich Presence inschakelen:\nToont het emulatoricoon en relevante informatie op je Discord-profiel. - userName Gebruikersnaam:\nStelt de gebruikersnaam van het PS4-account in, die door sommige games kan worden weergegeven. - TrophyKey Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. - logTypeGroupBox Logtype:\nStelt in of de uitvoer van het logvenster moet worden gesynchroniseerd voor prestaties. Kan nadelige effecten hebben op emulatie. - logFilter Logfilter:\nFiltert het logboek om alleen specifieke informatie af te drukken.\nVoorbeelden: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Niveaus: Trace, Debug, Info, Waarschuwing, Fout, Kritiek - in deze volgorde, een specifiek niveau dempt alle voorgaande niveaus in de lijst en logt alle niveaus daarna. - updaterGroupBox Updateren:\nRelease: Officiële versies die elke maand worden uitgebracht, die zeer verouderd kunnen zijn, maar betrouwbaar en getest zijn.\nNightly: Ontwikkelingsversies die alle nieuwste functies en bugfixes bevatten, maar mogelijk bugs bevatten en minder stabiel zijn. - GUIgroupBox Speel titelsong:\nAls een game dit ondersteunt, wordt speciale muziek afgespeeld wanneer je het spel in de GUI selecteert. - disableTrophycheckBox Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). - hideCursorGroupBox Verberg cursor:\nKies wanneer de cursor verdwijnt:\nNooit: Je ziet altijd de muis.\nInactief: Stel een tijd in waarna deze verdwijnt na inactiviteit.\nAltijd: je ziet de muis nooit. - idleTimeoutGroupBox Stel een tijd in voor wanneer de muis verdwijnt na inactiviteit. - backButtonBehaviorGroupBox Gedrag van de terugknop:\nStelt de terugknop van de controller in om een aanraking op de opgegeven positie op de PS4-touchpad na te bootsen. - enableCompatibilityCheckBox Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. - checkCompatibilityOnStartupCheckBox Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. - updateCompatibilityButton Update Compatibility Database:\nImmediately update the compatibility database. - Never Nooit - Idle Inactief - Always Altijd - Touchpad Left Touchpad Links - Touchpad Right Touchpad Rechts - Touchpad Center Touchpad Midden - None Geen - graphicsAdapterGroupBox Grafische adapter:\nIn systemen met meerdere GPU's, kies de GPU die de emulator uit de vervolgkeuzelijst moet gebruiken,\nof kies "Auto Select" om dit automatisch in te stellen. - resolutionLayout Breedte/Hoogte:\nStelt de grootte van het emulatorvenster bij het opstarten in, wat tijdens het spelen kan worden gewijzigd.\nDit is anders dan de resolutie in de game. - heightDivider Vblank deler:\nDe frame-rate waartegen de emulator wordt vernieuwd, vermenigvuldigd met dit getal. Dit veranderen kan nadelige effecten hebben, zoals het versnellen van het spel of het verpesten van kritieke gamefunctionaliteiten die niet verwachtten dat dit zou veranderen! - dumpShadersCheckBox Shaderdump inschakelen:\nVoor technische foutopsporing slaat het de shaders van de game op in een map terwijl ze worden gerenderd. - nullGpuCheckBox Null GPU inschakelen:\nVoor technische foutopsporing schakelt de game-rendering uit alsof er geen grafische kaart is. - gameFoldersBox Spelmap:\nDe lijst met mappen om te controleren op geïnstalleerde spellen. - addFolderButton Toevoegen:\nVoeg een map toe aan de lijst. - removeFolderButton Verwijderen:\nVerwijder een map uit de lijst. - debugDump Foutopsporing dump inschakelen:\nSlaat de import- en export-symbolen en de bestandsheaderinformatie van de momenteel draaiende PS4-toepassing op in een map. - vkValidationCheckBox Vulkan validatielaag inschakelen:\nSchakelt een systeem in dat de status van de Vulkan-renderer valideert en informatie over de interne status ervan logt. Dit zal de prestaties verlagen en waarschijnlijk het emulatiegedrag veranderen. - vkSyncValidationCheckBox Vulkan synchronisatievalidatie inschakelen:\nSchakelt een systeem in dat de timing van Vulkan-renderingtaken valideert. Dit zal de prestaties verlagen en waarschijnlijk het emulatiegedrag veranderen. - rdocCheckBox RenderDoc foutopsporing inschakelen:\nAls ingeschakeld, biedt de emulator compatibiliteit met Renderdoc om de momenteel gerenderde frame vast te leggen en te analyseren. + + CheatsPatches + + Cheats / Patches for + Cheats / Patches for + + + defaultTextEdit_MSG + Cheats/Patches zijn experimenteel.\nGebruik met voorzichtigheid.\n\nDownload cheats afzonderlijk door het repository te selecteren en op de downloadknop te klikken.\nOp het tabblad Patches kun je alle patches tegelijk downloaden, kiezen welke je wilt gebruiken en je selectie opslaan.\n\nAangezien wij de Cheats/Patches niet ontwikkelen,\nmeld problemen bij de auteur van de cheat.\n\nHeb je een nieuwe cheat gemaakt? Bezoek:\nhttps://github.com/shadps4-emu/ps4_cheats + + + No Image Available + Geen afbeelding beschikbaar + + + Serial: + Serie: + + + Version: + Versie: + + + Size: + Grootte: + + + Select Cheat File: + Selecteer cheatbestand: + + + Repository: + Repository: + + + Download Cheats + Download cheats + + + Delete File + Bestand verwijderen + + + No files selected. + Geen bestanden geselecteerd. + + + You can delete the cheats you don't want after downloading them. + Je kunt de cheats die je niet wilt verwijderen nadat je ze hebt gedownload. + + + Do you want to delete the selected file?\n%1 + Wil je het geselecteerde bestand verwijderen?\n%1 + + + Select Patch File: + Selecteer patchbestand: + + + Download Patches + Download patches + + + Save + Opslaan + + + Cheats + Cheats + + + Patches + Patches + + + Error + Fout + + + No patch selected. + Geen patch geselecteerd. + + + Unable to open files.json for reading. + Kan files.json niet openen voor lezen. + + + No patch file found for the current serial. + Geen patchbestand gevonden voor het huidige serienummer. + + + Unable to open the file for reading. + Kan het bestand niet openen voor lezen. + + + Unable to open the file for writing. + Kan het bestand niet openen voor schrijven. + + + Failed to parse XML: + XML parsing mislukt: + + + Success + Succes + + + Options saved successfully. + Opties succesvol opgeslagen. + + + Invalid Source + Ongeldige bron + + + The selected source is invalid. + De geselecteerde bron is ongeldig. + + + File Exists + Bestand bestaat + + + File already exists. Do you want to replace it? + Bestand bestaat al. Wil je het vervangen? + + + Failed to save file: + Kan bestand niet opslaan: + + + Failed to download file: + Kan bestand niet downloaden: + + + Cheats Not Found + Cheats niet gevonden + + + CheatsNotFound_MSG + Geen cheats gevonden voor deze game in deze versie van de geselecteerde repository.Probeer een andere repository of een andere versie van het spel. + + + Cheats Downloaded Successfully + Cheats succesvol gedownload + + + CheatsDownloadedSuccessfully_MSG + Je hebt cheats succesvol gedownload voor deze versie van het spel uit de geselecteerde repository. Je kunt proberen te downloaden van een andere repository. Als deze beschikbaar is, kan het ook worden gebruikt door het bestand uit de lijst te selecteren. + + + Failed to save: + Opslaan mislukt: + + + Failed to download: + Downloaden mislukt: + + + Download Complete + Download voltooid + + + DownloadComplete_MSG + Patches succesvol gedownload! Alle beschikbare patches voor alle spellen zijn gedownload. Het is niet nodig om ze afzonderlijk te downloaden voor elk spel dat cheats heeft. Als de patch niet verschijnt, kan het zijn dat deze niet bestaat voor het specifieke serienummer en de versie van het spel. + + + Failed to parse JSON data from HTML. + Kan JSON-gegevens uit HTML niet parseren. + + + Failed to retrieve HTML page. + Kan HTML-pagina niet ophalen. + + + The game is in version: %1 + Het spel is in versie: %1 + + + The downloaded patch only works on version: %1 + De gedownloade patch werkt alleen op versie: %1 + + + You may need to update your game. + Misschien moet je je spel bijwerken. + + + Incompatibility Notice + Incompatibiliteitsmelding + + + Failed to open file: + Kan bestand niet openen: + + + XML ERROR: + XML FOUT: + + + Failed to open files.json for writing + Kan files.json niet openen voor schrijven + + + Author: + Auteur: + + + Directory does not exist: + Map bestaat niet: + + + Failed to open files.json for reading. + Kan files.json niet openen voor lezen. + + + Name: + Naam: + + + Can't apply cheats before the game is started + Je kunt geen cheats toepassen voordat het spel is gestart. + + GameListFrame - Icon Pictogram - Name Naam - Serial Serienummer - Compatibility Compatibility - Region Regio - Firmware Firmware - Size Grootte - Version Versie - Path Pad - Play Time Speeltijd - Never Played Never Played - h h - m m - s s - Compatibility is untested Compatibility is untested - Game does not initialize properly / crashes the emulator Game does not initialize properly / crashes the emulator - Game boots, but only displays a blank screen Game boots, but only displays a blank screen - Game displays an image but does not go past the menu Game displays an image but does not go past the menu - Game has game-breaking glitches or unplayable performance Game has game-breaking glitches or unplayable performance - Game can be completed with playable performance and no major glitches Game can be completed with playable performance and no major glitches @@ -1508,127 +1210,102 @@ CheckUpdate - Auto Updater Automatische updater - Error Fout - Network error: Netwerkfout: - Failed to parse update information. Kon update-informatie niet parseren. - No pre-releases found. Geen pre-releases gevonden. - Invalid release data. Ongeldige releasegegevens. - No download URL found for the specified asset. Geen download-URL gevonden voor het opgegeven bestand. - Your version is already up to date! Uw versie is al up-to-date! - Update Available Update beschikbaar - Update Channel Updatekanaal - Current Version Huidige versie - Latest Version Laatste versie - Do you want to update? Wilt u updaten? - Show Changelog Toon changelog - Check for Updates at Startup Bij opstart op updates controleren - Update Bijwerken - No Nee - Hide Changelog Verberg changelog - Changes Wijzigingen - Network error occurred while trying to access the URL Netwerkfout opgetreden tijdens toegang tot de URL - Download Complete Download compleet - The update has been downloaded, press OK to install. De update is gedownload, druk op OK om te installeren. - Failed to save the update file at Kon het updatebestand niet opslaan op - Starting Update... Starten van update... - Failed to create the update script file Kon het update-scriptbestand niet maken @@ -1636,29 +1313,24 @@ GameListUtils - B B - KB KB - MB MB - GB GB - TB TB - + \ No newline at end of file diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index 1aed08394..20c9861c3 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -6,22 +6,18 @@ AboutDialog - About shadPS4 O programie - shadPS4 shadPS4 - shadPS4 is an experimental open-source emulator for the PlayStation 4. shadPS4 to eksperymentalny otwartoźródłowy emulator konsoli PlayStation 4. - This software should not be used to play games you have not legally obtained. To oprogramowanie nie służy do grania w gry pochodzące z nielegalnego źródła. @@ -29,7 +25,6 @@ ElfViewer - Open Folder Otwórz folder @@ -37,17 +32,14 @@ GameInfoClass - Loading game list, please wait :3 Ładowanie listy gier, proszę poczekaj :3 - Cancel Anuluj - Loading... Ładowanie... @@ -55,12 +47,10 @@ InstallDirSelect - shadPS4 - Choose directory shadPS4 - Wybierz katalog - Select which directory you want to install to. Select which directory you want to install to. @@ -68,27 +58,22 @@ GameInstallDialog - shadPS4 - Choose directory shadPS4 - Wybierz katalog - Directory to install games Katalog do instalacji gier - Browse Przeglądaj - Error Błąd - The value for location to install games is not valid. Podana ścieżka do instalacji gier nie jest prawidłowa. @@ -96,167 +81,134 @@ GuiContextMenus - Create Shortcut Utwórz skrót - Cheats / Patches Kody / poprawki - SFO Viewer Menedżer plików SFO - Trophy Viewer Menedżer trofeów - Open Folder... Otwórz Folder... - Open Game Folder Otwórz Katalog Gry - Open Save Data Folder Otwórz Folder Danych Zapisów - Open Log Folder Otwórz Folder Dziennika - Copy info... Kopiuj informacje... - Copy Name Kopiuj nazwę - Copy Serial Kopiuj numer seryjny - Copy All Kopiuj wszystko - Delete... Delete... - Delete Game Delete Game - Delete Update Delete Update - Delete DLC Delete DLC - Compatibility... Compatibility... - Update database Update database - View report View report - Submit a report Submit a report - Shortcut creation Tworzenie skrótu - Shortcut created successfully! Utworzenie skrótu zakończone pomyślnie! - Error Błąd - Error creating shortcut! Utworzenie skrótu zakończone niepowodzeniem! - Install PKG Zainstaluj PKG - Game Game - requiresEnableSeparateUpdateFolder_MSG This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. - This game has no update to delete! This game has no update to delete! - - + Update Update - This game has no DLC to delete! This game has no DLC to delete! - DLC DLC - Delete %1 Delete %1 - Are you sure you want to delete %1's %2 directory? Are you sure you want to delete %1's %2 directory? @@ -264,205 +216,285 @@ MainWindow - Open/Add Elf Folder Otwórz/Dodaj folder Elf - Install Packages (PKG) Zainstaluj paczkę (PKG) - Boot Game Uruchom grę - Check for Updates Sprawdź aktualizacje - About shadPS4 O programie - Configure... Konfiguruj... - Install application from a .pkg file Zainstaluj aplikacje z pliku .pkg - Recent Games Ostatnie gry - Exit Wyjdź - Exit shadPS4 Wyjdź z shadPS4 - Exit the application. Wyjdź z aplikacji. - Show Game List Pokaż listę gier - Game List Refresh Odśwież listę gier - Tiny Malutkie - Small Małe - Medium Średnie - Large Wielkie - List View Widok listy - Grid View Widok siatki - Elf Viewer Menedżer plików ELF - Game Install Directory Katalog zainstalowanych gier - Download Cheats/Patches Pobierz kody / poprawki - Dump Game List Zgraj listę gier - PKG Viewer Menedżer plików PKG - Search... Szukaj... - File Plik - View Widok - Game List Icons Ikony w widoku listy - Game List Mode Tryb listy gier - Settings Ustawienia - Utils Narzędzia - Themes Motywy - Help Pomoc - Dark Ciemny - Light Jasny - Green Zielony - Blue Niebieski - Violet Fioletowy - toolBar Pasek narzędzi + + Game List + Lista gier + + + * Unsupported Vulkan Version + * Nieobsługiwana wersja Vulkan + + + Download Cheats For All Installed Games + Pobierz kody do wszystkich zainstalowanych gier + + + Download Patches For All Games + Pobierz poprawki do wszystkich gier + + + Download Complete + Pobieranie zakończone + + + You have downloaded cheats for all the games you have installed. + Pobrałeś kody do wszystkich zainstalowanych gier. + + + Patches Downloaded Successfully! + Poprawki pobrane pomyślnie! + + + All Patches available for all games have been downloaded. + Wszystkie poprawki dostępne dla wszystkich gier zostały pobrane. + + + Games: + Gry: + + + PKG File (*.PKG) + Plik PKG (*.PKG) + + + ELF files (*.bin *.elf *.oelf) + Pliki ELF (*.bin *.elf *.oelf) + + + Game Boot + Uruchomienie gry + + + Only one file can be selected! + Można wybrać tylko jeden plik! + + + PKG Extraction + Wypakowywanie PKG + + + Patch detected! + Wykryto łatkę! + + + PKG and Game versions match: + Wersje PKG i gry są zgodne: + + + Would you like to overwrite? + Czy chcesz nadpisać? + + + PKG Version %1 is older than installed version: + Wersja PKG %1 jest starsza niż zainstalowana wersja: + + + Game is installed: + Gra jest zainstalowana: + + + Would you like to install Patch: + Czy chcesz zainstalować łatkę: + + + DLC Installation + Instalacja DLC + + + Would you like to install DLC: %1? + Czy chcesz zainstalować DLC: %1? + + + DLC already installed: + DLC już zainstalowane: + + + Game already installed + Gra już zainstalowana + + + PKG is a patch, please install the game first! + PKG jest poprawką, proszę najpierw zainstalować grę! + + + PKG ERROR + BŁĄD PKG + + + Extracting PKG %1/%2 + Wypakowywanie PKG %1/%2 + + + Extraction Finished + Wypakowywanie zakończone + + + Game successfully installed at %1 + Gra pomyślnie zainstalowana w %1 + + + File doesn't appear to be a valid PKG file + Plik nie wydaje się być prawidłowym plikiem PKG + PKGViewer - Open Folder Otwórz folder @@ -470,7 +502,6 @@ TrophyViewer - Trophy Viewer Menedżer trofeów @@ -478,1029 +509,700 @@ SettingsDialog - Settings Ustawienia - General Ogólne - System System - Console Language Język konsoli - Emulator Language Język emulatora - Emulator Emulator - Enable Fullscreen Włącz pełny ekran - Enable Separate Update Folder Enable Separate Update Folder - Show Splash Pokaż ekran powitania - Is PS4 Pro Emulacja PS4 Pro - Enable Discord Rich Presence Włącz Discord Rich Presence - Username Nazwa użytkownika - Trophy Key Trophy Key - Trophy Trophy - Logger Dziennik zdarzeń - Log Type Typ dziennika - Log Filter Filtrowanie dziennika - Input Wejście - Cursor Kursor - Hide Cursor Ukryj kursor - Hide Cursor Idle Timeout Czas oczekiwania na ukrycie kursora przy bezczynności - s s - Controller Kontroler - Back Button Behavior Zachowanie przycisku wstecz - Graphics Grafika - Graphics Device Karta graficzna - Width Szerokość - Height Wysokość - Vblank Divider Dzielnik przerwy pionowej (Vblank) - Advanced Zaawansowane - Enable Shaders Dumping Włącz zgrywanie cieni - Enable NULL GPU Wyłącz kartę graficzną - Paths Ścieżki - Game Folders Foldery gier - Add... Dodaj... - Remove Usuń - Debug Debugowanie - Enable Debug Dumping Włącz zgrywanie debugowania - Enable Vulkan Validation Layers Włącz warstwy walidacji Vulkan - Enable Vulkan Synchronization Validation Włącz walidację synchronizacji Vulkan - Enable RenderDoc Debugging Włącz debugowanie RenderDoc - Update Aktualizacja - Check for Updates at Startup Sprawdź aktualizacje przy starcie - Update Channel Kanał Aktualizacji - Check for Updates Sprawdź aktualizacje - GUI Settings Ustawienia Interfejsu - Disable Trophy Pop-ups Disable Trophy Pop-ups - Play title music Odtwórz muzykę tytułową - Update Compatibility Database On Startup Update Compatibility Database On Startup - Game Compatibility Game Compatibility - Display Compatibility Data Display Compatibility Data - Update Compatibility Database Update Compatibility Database - Volume Głośność - Audio Backend Audio Backend - - - MainWindow - - Game List - Lista gier - - - - * Unsupported Vulkan Version - * Nieobsługiwana wersja Vulkan - - - - Download Cheats For All Installed Games - Pobierz kody do wszystkich zainstalowanych gier - - - - Download Patches For All Games - Pobierz poprawki do wszystkich gier - - - - Download Complete - Pobieranie zakończone - - - - You have downloaded cheats for all the games you have installed. - Pobrałeś kody do wszystkich zainstalowanych gier. - - - - Patches Downloaded Successfully! - Poprawki pobrane pomyślnie! - - - - All Patches available for all games have been downloaded. - Wszystkie poprawki dostępne dla wszystkich gier zostały pobrane. - - - - Games: - Gry: - - - - PKG File (*.PKG) - Plik PKG (*.PKG) - - - - ELF files (*.bin *.elf *.oelf) - Pliki ELF (*.bin *.elf *.oelf) - - - - Game Boot - Uruchomienie gry - - - - Only one file can be selected! - Można wybrać tylko jeden plik! - - - - PKG Extraction - Wypakowywanie PKG - - - - Patch detected! - Wykryto łatkę! - - - - PKG and Game versions match: - Wersje PKG i gry są zgodne: - - - - Would you like to overwrite? - Czy chcesz nadpisać? - - - - PKG Version %1 is older than installed version: - Wersja PKG %1 jest starsza niż zainstalowana wersja: - - - - Game is installed: - Gra jest zainstalowana: - - - - Would you like to install Patch: - Czy chcesz zainstalować łatkę: - - - - DLC Installation - Instalacja DLC - - - - Would you like to install DLC: %1? - Czy chcesz zainstalować DLC: %1? - - - - DLC already installed: - DLC już zainstalowane: - - - - Game already installed - Gra już zainstalowana - - - - PKG is a patch, please install the game first! - PKG jest poprawką, proszę najpierw zainstalować grę! - - - - PKG ERROR - BŁĄD PKG - - - - Extracting PKG %1/%2 - Wypakowywanie PKG %1/%2 - - - - Extraction Finished - Wypakowywanie zakończone - - - - Game successfully installed at %1 - Gra pomyślnie zainstalowana w %1 - - - - File doesn't appear to be a valid PKG file - Plik nie wydaje się być prawidłowym plikiem PKG - - - - CheatsPatches - - - Cheats / Patches for - Cheats / Patches for - - - - defaultTextEdit_MSG - Cheaty/Patche są eksperymentalne.\nUżywaj ich ostrożnie.\n\nPobierz cheaty pojedynczo, wybierając repozytorium i klikając przycisk pobierania.\nNa zakładce Patches możesz pobrać wszystkie patche jednocześnie, wybrać, które chcesz używać, i zapisać wybór.\n\nPonieważ nie rozwijamy Cheats/Patches,\nproszę zgłosić problemy do autora cheatu.\n\nStworzyłeś nowy cheat? Odwiedź:\nhttps://github.com/shadps4-emu/ps4_cheats - - - - No Image Available - Brak dostępnego obrazu - - - - Serial: - Numer seryjny: - - - - Version: - Wersja: - - - - Size: - Rozmiar: - - - - Select Cheat File: - Wybierz plik kodu: - - - - Repository: - Repozytorium: - - - - Download Cheats - Pobierz kody - - - - Remove Old Files - Usuń stare pliki - - - - Do you want to delete the files after downloading them? - Czy chcesz usunąć pliki po ich pobraniu? - - - - Do you want to delete the files after downloading them?\n%1 - Czy chcesz usunąć pliki po ich pobraniu?\n%1 - - - - Do you want to delete the selected file?\n%1 - Czy chcesz usunąć wybrany plik?\n%1 - - - - Select Patch File: - Wybierz plik poprawki: - - - - Download Patches - Pobierz poprawki - - - Save Zapisz - - Cheats - Kody - - - - Patches - Poprawki - - - - Error - Błąd - - - - No patch selected. - Nie wybrano poprawki. - - - - Unable to open files.json for reading. - Nie można otworzyć pliku files.json do odczytu. - - - - No patch file found for the current serial. - Nie znaleziono pliku poprawki dla bieżącego numeru seryjnego. - - - - Unable to open the file for reading. - Nie można otworzyć pliku do odczytu. - - - - Unable to open the file for writing. - Nie można otworzyć pliku do zapisu. - - - - Failed to parse XML: - Nie udało się przeanalizować XML: - - - - Success - Sukces - - - - Options saved successfully. - Opcje zostały pomyślnie zapisane. - - - - Invalid Source - Nieprawidłowe źródło - - - - The selected source is invalid. - Wybrane źródło jest nieprawidłowe. - - - - File Exists - Plik istnieje - - - - File already exists. Do you want to replace it? - Plik już istnieje. Czy chcesz go zastąpić? - - - - Failed to save file: - Nie udało się zapisać pliku: - - - - Failed to download file: - Nie udało się pobrać pliku: - - - - Cheats Not Found - Nie znaleziono kodów - - - - CheatsNotFound_MSG - Nie znaleziono kodów do tej gry w tej wersji wybranego repozytorium. Spróbuj innego repozytorium lub innej wersji gry. - - - - Cheats Downloaded Successfully - Kody pobrane pomyślnie - - - - CheatsDownloadedSuccessfully_MSG - Pomyślnie pobrano kody dla tej wersji gry z wybranego repozytorium. Możesz spróbować pobrać z innego repozytorium. Jeśli jest dostępne, możesz również użyć go, wybierając plik z listy. - - - - Failed to save: - Nie udało się zapisać: - - - - Failed to download: - Nie udało się pobrać: - - - - Download Complete - Pobieranie zakończone - - - - DownloadComplete_MSG - Poprawki zostały pomyślnie pobrane! Wszystkie dostępne poprawki dla wszystkich gier zostały pobrane. Nie ma potrzeby pobierania ich osobno dla każdej gry, która ma kody. Jeśli poprawka się nie pojawia, możliwe, że nie istnieje dla konkretnego numeru seryjnego i wersji gry. - - - - Failed to parse JSON data from HTML. - Nie udało się przeanalizować danych JSON z HTML. - - - - Failed to retrieve HTML page. - Nie udało się pobrać strony HTML. - - - - The game is in version: %1 - Gra jest w wersji: %1 - - - - The downloaded patch only works on version: %1 - Pobrana łatka działa tylko w wersji: %1 - - - - You may need to update your game. - Możesz potrzebować zaktualizować swoją grę. - - - - Incompatibility Notice - Powiadomienie o niezgodności - - - - Failed to open file: - Nie udało się otworzyć pliku: - - - - XML ERROR: - BŁĄD XML: - - - - Failed to open files.json for writing - Nie udało się otworzyć pliku files.json do zapisu - - - - Author: - Autor: - - - - Directory does not exist: - Katalog nie istnieje: - - - - Directory does not exist: %1 - Katalog nie istnieje: %1 - - - - Failed to parse JSON: - Nie udało się przeanlizować JSON: - - - - Can't apply cheats before the game is started - Nie można zastosować kodów przed uruchomieniem gry. - - - - SettingsDialog - - - Save - Zapisz - - - Apply Zastosuj - Restore Defaults Przywróć ustawienia domyślne - Close Zamknij - Point your mouse at an option to display its description. Najedź kursorem na opcję, aby wyświetlić jej opis. - consoleLanguageGroupBox Język konsoli:\nUstala język, który używa gra PS4.\nZaleca się ustawienie tego na język, który obsługuje gra, co może się różnić w zależności od regionu. - emulatorLanguageGroupBox Język emulatora:\nUstala język interfejsu użytkownika emulatora. - fullscreenCheckBox Włącz tryb pełnoekranowy:\nAutomatycznie przełącza okno gry w tryb pełnoekranowy.\nMożna to wyłączyć naciskając klawisz F11. - separateUpdatesCheckBox Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. - showSplashCheckBox Wyświetl ekran powitalny:\nPodczas uruchamiania gry wyświetla ekran powitalny (specjalny obraz). - ps4proCheckBox Czy PS4 Pro:\nSprawia, że emulator działa jak PS4 PRO, co może aktywować specjalne funkcje w grach, które to obsługują. - discordRPCCheckbox Włącz Discord Rich Presence:\nWyświetla ikonę emuladora i odpowiednie informacje na twoim profilu Discord. - userName Nazwa użytkownika:\nUstala nazwę użytkownika konta PS4, która może być wyświetlana w niektórych grach. - TrophyKey Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. - logTypeGroupBox Typ logu:\nUstala, czy synchronizować wyjście okna dziennika dla wydajności. Może to mieć negatywny wpływ na emulację. - logFilter Filtr logu:\nFiltruje dziennik, aby drukować tylko określone informacje.\nPrzykłady: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Poziomy: Trace, Debug, Info, Warning, Error, Critical - w tej kolejności, konkretny poziom wycisza wszystkie wcześniejsze poziomy w liście i rejestruje wszystkie poziomy później. - updaterGroupBox Aktualizator:\nRelease: Oficjalne wersje wydawane co miesiąc, które mogą być bardzo przestarzałe, ale są niezawodne i przetestowane.\nNightly: Wersje rozwojowe, które zawierają wszystkie najnowsze funkcje i poprawki błędów, ale mogą mieć błędy i być mniej stabilne. - GUIgroupBox Odtwórz muzykę tytułową:\nJeśli gra to obsługuje, aktywuje odtwarzanie specjalnej muzyki podczas wybierania gry w GUI. - disableTrophycheckBox Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). - hideCursorGroupBox Ukryj kursor:\nWybierz, kiedy kursor zniknie:\nNigdy: Zawsze będziesz widział myszkę.\nNieaktywny: Ustaw czas, po którym zniknie po bezczynności.\nZawsze: nigdy nie zobaczysz myszki. - idleTimeoutGroupBox Ustaw czas, po którym mysz zniknie po bezczynności. - backButtonBehaviorGroupBox Zachowanie przycisku Wstecz:\nUstawia przycisk Wstecz kontrolera tak, aby emulował dotknięcie określonego miejsca na panelu dotykowym PS4. - enableCompatibilityCheckBox Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. - checkCompatibilityOnStartupCheckBox Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. - updateCompatibilityButton Update Compatibility Database:\nImmediately update the compatibility database. - Never Nigdy - Idle Bezczynny - Always Zawsze - Touchpad Left Touchpad Lewy - Touchpad Right Touchpad Prawy - Touchpad Center Touchpad Środkowy - None Brak - graphicsAdapterGroupBox Urządzenie graficzne:\nW systemach z wieloma GPU, wybierz GPU, który emulator ma używać z rozwijanego menu,\n lub wybierz "Auto Select", aby ustawić go automatycznie. - resolutionLayout Szerokość/Wysokość:\nUstala rozmiar okna emulatora podczas uruchamiania, który może być zmieniany w trakcie gry.\nTo różni się od rozdzielczości w grze. - heightDivider Dzielnik Vblank:\nWskaźnik klatek, z jakim emulator jest odświeżany, pomnożony przez tę liczbę. Zmiana tego może mieć negatywne skutki, takie jak przyspieszenie gry lub zniszczenie krytycznej funkcjonalności gry, która nie spodziewa się, że to zostanie zmienione! - dumpShadersCheckBox Włącz zrzucanie shaderów:\nDla technicznego debugowania zapisuje shadery z gry w folderze podczas renderowania. - nullGpuCheckBox Włącz Null GPU:\nDla technicznego debugowania dezaktywuje renderowanie gry tak, jakby nie było karty graficznej. - gameFoldersBox Foldery gier:\nLista folderów do sprawdzenia zainstalowanych gier. - addFolderButton Dodaj:\nDodaj folder do listy. - removeFolderButton Usuń:\nUsuń folder z listy. - debugDump Włącz zrzut debugowania:\nZapisuje symbole importu i eksportu oraz informacje nagłówkowe pliku dla aktualnie działającej aplikacji PS4 w katalogu. - vkValidationCheckBox Włącz warstwę walidacji Vulkan:\nWłącza system, który waliduje stan renderera Vulkan i loguje informacje o jego wewnętrznym stanie. Zmniejszy to wydajność i prawdopodobnie zmieni zachowanie emulacji. - vkSyncValidationCheckBox Włącz walidację synchronizacji Vulkan:\nWłącza system, który waliduje timing zadań renderowania Vulkan. Zmniejszy to wydajność i prawdopodobnie zmieni zachowanie emulacji. - rdocCheckBox Włącz debugowanie RenderDoc:\nJeśli włączone, emulator zapewnia kompatybilność z Renderdoc, aby umożliwić nagrywanie i analizowanie aktualnie renderowanej klatki. + + CheatsPatches + + Cheats / Patches for + Cheats / Patches for + + + defaultTextEdit_MSG + Cheaty/Patche są eksperymentalne.\nUżywaj ich ostrożnie.\n\nPobierz cheaty pojedynczo, wybierając repozytorium i klikając przycisk pobierania.\nNa zakładce Patches możesz pobrać wszystkie patche jednocześnie, wybrać, które chcesz używać, i zapisać wybór.\n\nPonieważ nie rozwijamy Cheats/Patches,\nproszę zgłosić problemy do autora cheatu.\n\nStworzyłeś nowy cheat? Odwiedź:\nhttps://github.com/shadps4-emu/ps4_cheats + + + No Image Available + Brak dostępnego obrazu + + + Serial: + Numer seryjny: + + + Version: + Wersja: + + + Size: + Rozmiar: + + + Select Cheat File: + Wybierz plik kodu: + + + Repository: + Repozytorium: + + + Download Cheats + Pobierz kody + + + Remove Old Files + Usuń stare pliki + + + Do you want to delete the files after downloading them? + Czy chcesz usunąć pliki po ich pobraniu? + + + Do you want to delete the files after downloading them?\n%1 + Czy chcesz usunąć pliki po ich pobraniu?\n%1 + + + Do you want to delete the selected file?\n%1 + Czy chcesz usunąć wybrany plik?\n%1 + + + Select Patch File: + Wybierz plik poprawki: + + + Download Patches + Pobierz poprawki + + + Save + Zapisz + + + Cheats + Kody + + + Patches + Poprawki + + + Error + Błąd + + + No patch selected. + Nie wybrano poprawki. + + + Unable to open files.json for reading. + Nie można otworzyć pliku files.json do odczytu. + + + No patch file found for the current serial. + Nie znaleziono pliku poprawki dla bieżącego numeru seryjnego. + + + Unable to open the file for reading. + Nie można otworzyć pliku do odczytu. + + + Unable to open the file for writing. + Nie można otworzyć pliku do zapisu. + + + Failed to parse XML: + Nie udało się przeanalizować XML: + + + Success + Sukces + + + Options saved successfully. + Opcje zostały pomyślnie zapisane. + + + Invalid Source + Nieprawidłowe źródło + + + The selected source is invalid. + Wybrane źródło jest nieprawidłowe. + + + File Exists + Plik istnieje + + + File already exists. Do you want to replace it? + Plik już istnieje. Czy chcesz go zastąpić? + + + Failed to save file: + Nie udało się zapisać pliku: + + + Failed to download file: + Nie udało się pobrać pliku: + + + Cheats Not Found + Nie znaleziono kodów + + + CheatsNotFound_MSG + Nie znaleziono kodów do tej gry w tej wersji wybranego repozytorium. Spróbuj innego repozytorium lub innej wersji gry. + + + Cheats Downloaded Successfully + Kody pobrane pomyślnie + + + CheatsDownloadedSuccessfully_MSG + Pomyślnie pobrano kody dla tej wersji gry z wybranego repozytorium. Możesz spróbować pobrać z innego repozytorium. Jeśli jest dostępne, możesz również użyć go, wybierając plik z listy. + + + Failed to save: + Nie udało się zapisać: + + + Failed to download: + Nie udało się pobrać: + + + Download Complete + Pobieranie zakończone + + + DownloadComplete_MSG + Poprawki zostały pomyślnie pobrane! Wszystkie dostępne poprawki dla wszystkich gier zostały pobrane. Nie ma potrzeby pobierania ich osobno dla każdej gry, która ma kody. Jeśli poprawka się nie pojawia, możliwe, że nie istnieje dla konkretnego numeru seryjnego i wersji gry. + + + Failed to parse JSON data from HTML. + Nie udało się przeanalizować danych JSON z HTML. + + + Failed to retrieve HTML page. + Nie udało się pobrać strony HTML. + + + The game is in version: %1 + Gra jest w wersji: %1 + + + The downloaded patch only works on version: %1 + Pobrana łatka działa tylko w wersji: %1 + + + You may need to update your game. + Możesz potrzebować zaktualizować swoją grę. + + + Incompatibility Notice + Powiadomienie o niezgodności + + + Failed to open file: + Nie udało się otworzyć pliku: + + + XML ERROR: + BŁĄD XML: + + + Failed to open files.json for writing + Nie udało się otworzyć pliku files.json do zapisu + + + Author: + Autor: + + + Directory does not exist: + Katalog nie istnieje: + + + Directory does not exist: %1 + Katalog nie istnieje: %1 + + + Failed to parse JSON: + Nie udało się przeanlizować JSON: + + + Can't apply cheats before the game is started + Nie można zastosować kodów przed uruchomieniem gry. + + GameListFrame - Icon Ikona - Name Nazwa - Serial Numer seryjny - Compatibility Compatibility - Region Region - Firmware Oprogramowanie - Size Rozmiar - Version Wersja - Path Ścieżka - Play Time Czas gry - Never Played Never Played - h h - m m - s s - Compatibility is untested Compatibility is untested - Game does not initialize properly / crashes the emulator Game does not initialize properly / crashes the emulator - Game boots, but only displays a blank screen Game boots, but only displays a blank screen - Game displays an image but does not go past the menu Game displays an image but does not go past the menu - Game has game-breaking glitches or unplayable performance Game has game-breaking glitches or unplayable performance - Game can be completed with playable performance and no major glitches Game can be completed with playable performance and no major glitches @@ -1508,127 +1210,102 @@ CheckUpdate - Auto Updater Automatyczne aktualizacje - Error Błąd - Network error: Błąd sieci: - Failed to parse update information. Nie udało się sparsować informacji o aktualizacji. - No pre-releases found. Nie znaleziono wersji przedpremierowych. - Invalid release data. Nieprawidłowe dane wydania. - No download URL found for the specified asset. Nie znaleziono adresu URL do pobrania dla określonego zasobu. - Your version is already up to date! Twoja wersja jest już aktualna! - Update Available Dostępna aktualizacja - Update Channel Kanał Aktualizacji - Current Version Aktualna wersja - Latest Version Ostatnia wersja - Do you want to update? Czy chcesz zaktualizować? - Show Changelog Pokaż zmiany - Check for Updates at Startup Sprawdź aktualizacje przy starcie - Update Aktualizuj - No Nie - Hide Changelog Ukryj zmiany - Changes Zmiany - Network error occurred while trying to access the URL Błąd sieci wystąpił podczas próby uzyskania dostępu do URL - Download Complete Pobieranie zakończone - The update has been downloaded, press OK to install. Aktualizacja została pobrana, naciśnij OK, aby zainstalować. - Failed to save the update file at Nie udało się zapisać pliku aktualizacji w - Starting Update... Rozpoczynanie aktualizacji... - Failed to create the update script file Nie udało się utworzyć pliku skryptu aktualizacji @@ -1636,29 +1313,24 @@ GameListUtils - B B - KB KB - MB MB - GB GB - TB TB - + \ No newline at end of file diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index cce66c105..2d623dfbf 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -6,22 +6,18 @@ AboutDialog - About shadPS4 Sobre o shadPS4 - shadPS4 shadPS4 - shadPS4 is an experimental open-source emulator for the PlayStation 4. shadPS4 é um emulador experimental de código-fonte aberto para o PlayStation 4. - This software should not be used to play games you have not legally obtained. Este software não deve ser usado para jogar jogos piratas. @@ -29,7 +25,6 @@ ElfViewer - Open Folder Abrir Pasta @@ -37,17 +32,14 @@ GameInfoClass - Loading game list, please wait :3 Carregando a lista de jogos, por favor aguarde :3 - Cancel Cancelar - Loading... Carregando... @@ -55,12 +47,10 @@ InstallDirSelect - shadPS4 - Choose directory shadPS4 - Escolha o diretório - Select which directory you want to install to. Selecione o diretório em que você deseja instalar. @@ -68,27 +58,22 @@ GameInstallDialog - shadPS4 - Choose directory shadPS4 - Escolha o diretório - Directory to install games Diretório para instalar jogos - Browse Procurar - Error Erro - The value for location to install games is not valid. O diretório da instalação dos jogos não é válido. @@ -96,373 +81,420 @@ GuiContextMenus - Create Shortcut Criar Atalho - Cheats / Patches Cheats / Patches - SFO Viewer Visualizador de SFO - Trophy Viewer Visualizador de Troféu - Open Folder... Abrir Pasta... - Open Game Folder Abrir Pasta do Jogo - Open Save Data Folder Abrir Pasta de Save - Open Log Folder Abrir Pasta de Log - Copy info... Copiar informação... - Copy Name Copiar Nome - Copy Serial Copiar Serial - Copy All Copiar Tudo - Delete... Deletar... - Delete Game Deletar Jogo - Delete Update Deletar Atualização - Delete DLC Deletar DLC - Compatibility... Compatibilidade... - Update database Atualizar banco de dados - View report Ver status - Submit a report Enviar status - Shortcut creation Criação de atalho - Shortcut created successfully! Atalho criado com sucesso! - Error Erro - Error creating shortcut! Erro ao criar atalho! - Install PKG Instalar PKG - Game Jogo - requiresEnableSeparateUpdateFolder_MSG Este recurso requer a opção de configuração 'Habilitar Pasta de Atualização Separada' para funcionar. Se você quiser usar este recurso, habilite-o. - This game has no update to delete! Este jogo não tem atualização para excluir! - - + Update Atualização - This game has no DLC to delete! Este jogo não tem DLC para excluir! - DLC DLC - Delete %1 Deletar %1 - Are you sure you want to delete %1's %2 directory? Tem certeza de que deseja excluir o diretório %2 de %1 ? - + MainWindow - Open/Add Elf Folder Abrir/Adicionar pasta Elf - Install Packages (PKG) Instalar Pacotes (PKG) - Boot Game Iniciar Jogo - Check for Updates Verificar atualizações - About shadPS4 Sobre o shadPS4 - Configure... Configurar... - Install application from a .pkg file Instalar aplicação de um arquivo .pkg - Recent Games Jogos Recentes - Exit Sair - Exit shadPS4 Sair do shadPS4 - Exit the application. Sair da aplicação. - Show Game List Mostrar Lista de Jogos - Game List Refresh Atualizar Lista de Jogos - Tiny Muito pequeno - Small Pequeno - Medium Médio - Large Grande - List View Visualizar em Lista - Grid View Visualizar em Grade - Elf Viewer Visualizador de Elf - Game Install Directory Diretório de Instalação de Jogos - Download Cheats/Patches Baixar Cheats/Patches - Dump Game List Dumpar Lista de Jogos - PKG Viewer Visualizador de PKG - Search... Pesquisar... - File Arquivo - View Ver - Game List Icons Ícones da Lista de Jogos - Game List Mode Modo da Lista de Jogos - Settings Configurações - Utils Utilitários - Themes Temas - Help Ajuda - Dark Escuro - Light Claro - Green Verde - Blue Azul - Violet Violeta - toolBar Barra de Ferramentas + + Game List + Lista de Jogos + + + * Unsupported Vulkan Version + * Versão Vulkan não suportada + + + Download Cheats For All Installed Games + Baixar Cheats para Todos os Jogos Instalados + + + Download Patches For All Games + Baixar Patches para Todos os Jogos + + + Download Complete + Download Completo + + + You have downloaded cheats for all the games you have installed. + Você baixou cheats para todos os jogos que instalou. + + + Patches Downloaded Successfully! + Patches Baixados com Sucesso! + + + All Patches available for all games have been downloaded. + Todos os patches disponíveis para todos os jogos foram baixados. + + + Games: + Jogos: + + + PKG File (*.PKG) + Arquivo PKG (*.PKG) + + + ELF files (*.bin *.elf *.oelf) + Arquivos ELF (*.bin *.elf *.oelf) + + + Game Boot + Inicialização do Jogo + + + Only one file can be selected! + Apenas um arquivo pode ser selecionado! + + + PKG Extraction + Extração de PKG + + + Patch detected! + Atualização detectada! + + + PKG and Game versions match: + As versões do PKG e do Jogo são igual: + + + Would you like to overwrite? + Gostaria de substituir? + + + PKG Version %1 is older than installed version: + Versão do PKG %1 é mais antiga do que a versão instalada: + + + Game is installed: + Jogo instalado: + + + Would you like to install Patch: + Você gostaria de instalar a atualização: + + + DLC Installation + Instalação de DLC + + + Would you like to install DLC: %1? + Você gostaria de instalar o DLC: %1? + + + DLC already installed: + DLC já instalada: + + + Game already installed + O jogo já está instalado: + + + PKG is a patch, please install the game first! + O PKG é um patch, por favor, instale o jogo primeiro! + + + PKG ERROR + ERRO de PKG + + + Extracting PKG %1/%2 + Extraindo PKG %1/%2 + + + Extraction Finished + Extração Concluída + + + Game successfully installed at %1 + Jogo instalado com sucesso em %1 + + + File doesn't appear to be a valid PKG file + O arquivo não parece ser um arquivo PKG válido + PKGViewer - Open Folder Abrir Pasta @@ -470,7 +502,6 @@ TrophyViewer - Trophy Viewer Visualizador de Troféu @@ -478,1029 +509,700 @@ SettingsDialog - Settings Configurações - General Geral - System Sistema - Console Language Idioma do Console - Emulator Language Idioma do Emulador - Emulator Emulador - Enable Fullscreen Ativar Tela Cheia - Enable Separate Update Folder Habilitar pasta de atualização separada - Show Splash Mostrar Splash Inicial - Is PS4 Pro Modo PS4 Pro - Enable Discord Rich Presence Ativar Discord Rich Presence - Username Nome de usuário - Trophy Key Trophy Key - Trophy Troféus - Logger Registro - Log Type Tipo de Registro - Log Filter Filtro do Registro - Input Entradas - Cursor Cursor - Hide Cursor Ocultar Cursor - Hide Cursor Idle Timeout Tempo de Inatividade para Ocultar Cursor - s s - Controller Controle - Back Button Behavior Comportamento do botão Voltar - Graphics Gráficos - Graphics Device Placa de Vídeo - Width Largura - Height Altura - Vblank Divider Divisor Vblank - Advanced Avançado - Enable Shaders Dumping Ativar Dumping de Shaders - Enable NULL GPU Ativar GPU NULA - Paths Pastas - Game Folders Pastas dos Jogos - Add... Adicionar... - Remove Remover - Debug Depuração - Enable Debug Dumping Ativar Depuração de Dumping - Enable Vulkan Validation Layers Ativar Camadas de Validação do Vulkan - Enable Vulkan Synchronization Validation Ativar Validação de Sincronização do Vulkan - Enable RenderDoc Debugging Ativar Depuração por RenderDoc - Update Atualização - Check for Updates at Startup Verificar Atualizações ao Iniciar - Update Channel Canal de Atualização - Check for Updates Verificar atualizações - GUI Settings Configurações da Interface - Disable Trophy Pop-ups Desabilitar Pop-ups dos Troféus - Play title music Reproduzir música de abertura - Update Compatibility Database On Startup Atualizar Compatibilidade ao Inicializar - Game Compatibility Compatibilidade dos Jogos - Display Compatibility Data Exibir Dados de Compatibilidade - Update Compatibility Database Atualizar Lista de Compatibilidade - Volume Volume - Audio Backend Backend de Áudio - - - MainWindow - - Game List - Lista de Jogos - - - - * Unsupported Vulkan Version - * Versão Vulkan não suportada - - - - Download Cheats For All Installed Games - Baixar Cheats para Todos os Jogos Instalados - - - - Download Patches For All Games - Baixar Patches para Todos os Jogos - - - - Download Complete - Download Completo - - - - You have downloaded cheats for all the games you have installed. - Você baixou cheats para todos os jogos que instalou. - - - - Patches Downloaded Successfully! - Patches Baixados com Sucesso! - - - - All Patches available for all games have been downloaded. - Todos os patches disponíveis para todos os jogos foram baixados. - - - - Games: - Jogos: - - - - PKG File (*.PKG) - Arquivo PKG (*.PKG) - - - - ELF files (*.bin *.elf *.oelf) - Arquivos ELF (*.bin *.elf *.oelf) - - - - Game Boot - Inicialização do Jogo - - - - Only one file can be selected! - Apenas um arquivo pode ser selecionado! - - - - PKG Extraction - Extração de PKG - - - - Patch detected! - Atualização detectada! - - - - PKG and Game versions match: - As versões do PKG e do Jogo são igual: - - - - Would you like to overwrite? - Gostaria de substituir? - - - - PKG Version %1 is older than installed version: - Versão do PKG %1 é mais antiga do que a versão instalada: - - - - Game is installed: - Jogo instalado: - - - - Would you like to install Patch: - Você gostaria de instalar a atualização: - - - - DLC Installation - Instalação de DLC - - - - Would you like to install DLC: %1? - Você gostaria de instalar o DLC: %1? - - - - DLC already installed: - DLC já instalada: - - - - Game already installed - O jogo já está instalado: - - - - PKG is a patch, please install the game first! - O PKG é um patch, por favor, instale o jogo primeiro! - - - - PKG ERROR - ERRO de PKG - - - - Extracting PKG %1/%2 - Extraindo PKG %1/%2 - - - - Extraction Finished - Extração Concluída - - - - Game successfully installed at %1 - Jogo instalado com sucesso em %1 - - - - File doesn't appear to be a valid PKG file - O arquivo não parece ser um arquivo PKG válido - - - - CheatsPatches - - - Cheats / Patches for - Cheats / Patches para - - - - defaultTextEdit_MSG - Cheats/Patches são experimentais.\nUse com cautela.\n\nBaixe os cheats individualmente selecionando o repositório e clicando no botão de download.\nNa aba Patches, você pode baixar todos os Patches de uma vez, escolha qual deseja usar e salve a opção.\n\nComo não desenvolvemos os Cheats/Patches,\npor favor, reporte problemas relacionados ao autor do cheat.\n\nCriou um novo cheat? Visite:\nhttps://github.com/shadps4-emu/ps4_cheats - - - - No Image Available - Imagem Não Disponível - - - - Serial: - Serial: - - - - Version: - Versão: - - - - Size: - Tamanho: - - - - Select Cheat File: - Selecione o Arquivo de Cheat: - - - - Repository: - Repositório: - - - - Download Cheats - Baixar Cheats - - - - Delete File - Excluir Arquivo - - - - No files selected. - Nenhum arquivo selecionado. - - - - You can delete the cheats you don't want after downloading them. - Você pode excluir os cheats que não deseja após baixá-las. - - - - Do you want to delete the selected file?\n%1 - Deseja excluir o arquivo selecionado?\n%1 - - - - Select Patch File: - Selecione o Arquivo de Patch: - - - - Download Patches - Baixar Patches - - - Save Salvar - - Cheats - Cheats - - - - Patches - Patches - - - - Error - Erro - - - - No patch selected. - Nenhum patch selecionado. - - - - Unable to open files.json for reading. - Não foi possível abrir files.json para leitura. - - - - No patch file found for the current serial. - Nenhum arquivo de patch encontrado para o serial atual. - - - - Unable to open the file for reading. - Não foi possível abrir o arquivo para leitura. - - - - Unable to open the file for writing. - Não foi possível abrir o arquivo para gravação. - - - - Failed to parse XML: - Falha ao analisar XML: - - - - Success - Sucesso - - - - Options saved successfully. - Opções salvas com sucesso. - - - - Invalid Source - Fonte Inválida - - - - The selected source is invalid. - A fonte selecionada é inválida. - - - - File Exists - Arquivo Existe - - - - File already exists. Do you want to replace it? - O arquivo já existe. Deseja substituí-lo? - - - - Failed to save file: - Falha ao salvar o arquivo: - - - - Failed to download file: - Falha ao baixar o arquivo: - - - - Cheats Not Found - Cheats Não Encontrados - - - - CheatsNotFound_MSG - Nenhum cheat encontrado para este jogo nesta versão do repositório selecionado, tente outro repositório ou uma versão diferente do jogo. - - - - Cheats Downloaded Successfully - Cheats Baixados com Sucesso - - - - CheatsDownloadedSuccessfully_MSG - Você baixou os cheats com sucesso. Para esta versão do jogo a partir do repositório selecionado. Você pode tentar baixar de outro repositório, se estiver disponível, também será possível usá-lo selecionando o arquivo da lista. - - - - Failed to save: - Falha ao salvar: - - - - Failed to download: - Falha ao baixar: - - - - Download Complete - Download Completo - - - - DownloadComplete_MSG - Patches Baixados com Sucesso! Todos os patches disponíveis para todos os jogos foram baixados, não é necessário baixá-los individualmente para cada jogo como acontece com os Cheats. Se o patch não aparecer, pode ser que ele não exista para o número de série e a versão específicos do jogo. - - - - Failed to parse JSON data from HTML. - Falha ao analisar dados JSON do HTML. - - - - Failed to retrieve HTML page. - Falha ao recuperar a página HTML. - - - - The game is in version: %1 - O jogo está na versão: %1 - - - - The downloaded patch only works on version: %1 - O patch baixado só funciona na versão: %1 - - - - You may need to update your game. - Talvez você precise atualizar seu jogo. - - - - Incompatibility Notice - Aviso de incompatibilidade - - - - Failed to open file: - Falha ao abrir o arquivo: - - - - XML ERROR: - ERRO de XML: - - - - Failed to open files.json for writing - Falha ao abrir files.json para gravação - - - - Author: - Autor: - - - - Directory does not exist: - O Diretório não existe: - - - - Failed to open files.json for reading. - Falha ao abrir files.json para leitura. - - - - Name: - Nome: - - - - Can't apply cheats before the game is started - Não é possível aplicar cheats antes que o jogo comece. - - - - SettingsDialog - - - Save - Salvar - - - Apply Aplicar - Restore Defaults Restaurar Padrões - Close Fechar - Point your mouse at an option to display its description. Passe o mouse sobre uma opção para exibir sua descrição. - consoleLanguageGroupBox Idioma do console:\nDefine o idioma usado pelo jogo no PS4.\nRecomenda-se configurá-lo para um idioma que o jogo suporte, o que pode variar conforme a região. - emulatorLanguageGroupBox Idioma do emulador:\nDefine o idioma da interface do emulador. - fullscreenCheckBox Ativar Tela Cheia:\nMove automaticamente a janela do jogo para o modo tela cheia.\nIsso pode ser alterado pressionando a tecla F11. - separateUpdatesCheckBox Habilitar pasta de atualização separada:\nPermite instalar atualizações de jogos em uma pasta separada para fácil gerenciamento. - showSplashCheckBox Mostrar Splash Inicial:\nExibe a tela inicial do jogo (imagem especial) ao iniciar o jogo. - ps4proCheckBox Modo PS4 Pro:\nFaz o emulador agir como um PS4 PRO, o que pode ativar recursos especiais em jogos que o suportam. - discordRPCCheckbox Ativar Discord Rich Presence:\nExibe o ícone do emulador e informações relevantes no seu perfil do Discord. - userName Nome de usuário:\nDefine o nome de usuário da conta PS4 que pode ser exibido por alguns jogos. - TrophyKey Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. - logTypeGroupBox Tipo de Registro:\nDefine se a saída da janela de log deve ser sincronizada para melhorar o desempenho. Isso pode impactar negativamente a emulação. - logFilter Filtro de Registro:\nImprime apenas informações específicas.\nExemplos: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical"\nNíveis: Trace, Debug, Info, Warning, Error, Critical - assim, um nível específico desativa todos os níveis anteriores na lista e registra todos os níveis subsequentes. - updaterGroupBox Atualizações:\nRelease: Versões oficiais que são lançadas todo mês e podem ser bastante antigas, mas são mais confiáveis e testadas.\nNightly: Versões de desenvolvimento que têm todos os novos recursos e correções, mas podem ter bugs e ser instáveis. - GUIgroupBox Reproduzir música de abertura:\nSe o jogo suportar, ativa a reprodução de uma música especial ao selecionar o jogo na interface do menu. - disableTrophycheckBox Desabilitar pop-ups dos troféus:\nDesabilite notificações de troféus no jogo. O progresso do troféu ainda pode ser rastreado usando o Trophy Viewer (clique com o botão direito do mouse no jogo na janela principal). - hideCursorGroupBox Ocultar Cursor:\nEscolha quando o cursor desaparecerá:\nNunca: Você sempre verá o mouse.\nParado: Defina um tempo para ele desaparecer após ficar inativo.\nSempre: Você nunca verá o mouse. - idleTimeoutGroupBox Defina um tempo em segundos para o mouse desaparecer após ficar inativo. - backButtonBehaviorGroupBox Comportamento do botão Voltar:\nDefine o botão Voltar do controle para emular o toque na posição especificada no touchpad do PS4. - enableCompatibilityCheckBox Exibir Dados de Compatibilidade:\nExibe informações de compatibilidade dos jogos na janela principal.\nHabilitar "Atualizar Compatibilidade ao Inicializar" para obter informações atualizadas. - checkCompatibilityOnStartupCheckBox Atualizar Compatibilidade ao inicializar:\nAtualiza automaticamente o banco de dados de compatibilidade quando o SHADPS4 é iniciado. - updateCompatibilityButton Atualizar Lista de Compatibilidade:\nAtualizar imediatamente o banco de dados de compatibilidade. - Never Nunca - Idle Parado - Always Sempre - Touchpad Left Touchpad Esquerdo - Touchpad Right Touchpad Direito - Touchpad Center Touchpad Centro - None Nenhum - graphicsAdapterGroupBox Placa de Vídeo:\nEm sistemas com múltiplas GPUs, escolha qual GPU o emulador usará da lista suspensa,\nou escolha "Auto Select" para que ele determine automaticamente. - resolutionLayout Largura/Altura:\nDefine o tamanho da janela do emulador no momento da inicialização, que pode ser redimensionado durante o jogo.\nIsso é diferente da resolução dentro do jogo. - heightDivider Divisor Vblank:\nA taxa de quadros que o emulador atualiza é multiplicada por este número. Mudar isso pode ter efeitos negativos, como aumentar a velocidade do jogo ou quebrar funções vitais do jogo que não esperam que isso mude! - dumpShadersCheckBox Ativar Dumping de Shaders:\nArmazena os shaders do jogo em uma pasta durante a renderização para fins de depuração técnica. - nullGpuCheckBox Ativar GPU NULA:\nDesativa a renderização do jogo para fins de depuração técnica, como se não houvesse nenhuma placa gráfica. - gameFoldersBox Pastas dos jogos:\nA lista de pastas para verificar se há jogos instalados. - addFolderButton Adicionar:\nAdicione uma pasta à lista. - removeFolderButton Remover:\nRemove uma pasta da lista. - debugDump Ativar Depuração de Dumping:\nArmazena os símbolos de importação e exportação e as informações do cabeçalho do arquivo do programa PS4 atual em um diretório. - vkValidationCheckBox Ativar Camadas de Validação do Vulkan:\nAtiva um sistema que valida o estado do renderizador Vulkan e registra informações sobre seu estado interno.\nIsso diminui o desempenho e pode alterar o comportamento da emulação. - vkSyncValidationCheckBox Ativar Validação de Sincronização do Vulkan:\nAtiva um sistema que valida o agendamento de tarefas de renderização Vulkan.\nIsso diminui o desempenho e pode alterar o comportamento da emulação. - rdocCheckBox Ativar depuração por RenderDoc:\nSe ativado, permite que o emulador tenha compatibilidade com RenderDoc para gravação e análise do quadro renderizado atual. + + CheatsPatches + + Cheats / Patches for + Cheats / Patches para + + + defaultTextEdit_MSG + Cheats/Patches são experimentais.\nUse com cautela.\n\nBaixe os cheats individualmente selecionando o repositório e clicando no botão de download.\nNa aba Patches, você pode baixar todos os Patches de uma vez, escolha qual deseja usar e salve a opção.\n\nComo não desenvolvemos os Cheats/Patches,\npor favor, reporte problemas relacionados ao autor do cheat.\n\nCriou um novo cheat? Visite:\nhttps://github.com/shadps4-emu/ps4_cheats + + + No Image Available + Imagem Não Disponível + + + Serial: + Serial: + + + Version: + Versão: + + + Size: + Tamanho: + + + Select Cheat File: + Selecione o Arquivo de Cheat: + + + Repository: + Repositório: + + + Download Cheats + Baixar Cheats + + + Delete File + Excluir Arquivo + + + No files selected. + Nenhum arquivo selecionado. + + + You can delete the cheats you don't want after downloading them. + Você pode excluir os cheats que não deseja após baixá-las. + + + Do you want to delete the selected file?\n%1 + Deseja excluir o arquivo selecionado?\n%1 + + + Select Patch File: + Selecione o Arquivo de Patch: + + + Download Patches + Baixar Patches + + + Save + Salvar + + + Cheats + Cheats + + + Patches + Patches + + + Error + Erro + + + No patch selected. + Nenhum patch selecionado. + + + Unable to open files.json for reading. + Não foi possível abrir files.json para leitura. + + + No patch file found for the current serial. + Nenhum arquivo de patch encontrado para o serial atual. + + + Unable to open the file for reading. + Não foi possível abrir o arquivo para leitura. + + + Unable to open the file for writing. + Não foi possível abrir o arquivo para gravação. + + + Failed to parse XML: + Falha ao analisar XML: + + + Success + Sucesso + + + Options saved successfully. + Opções salvas com sucesso. + + + Invalid Source + Fonte Inválida + + + The selected source is invalid. + A fonte selecionada é inválida. + + + File Exists + Arquivo Existe + + + File already exists. Do you want to replace it? + O arquivo já existe. Deseja substituí-lo? + + + Failed to save file: + Falha ao salvar o arquivo: + + + Failed to download file: + Falha ao baixar o arquivo: + + + Cheats Not Found + Cheats Não Encontrados + + + CheatsNotFound_MSG + Nenhum cheat encontrado para este jogo nesta versão do repositório selecionado, tente outro repositório ou uma versão diferente do jogo. + + + Cheats Downloaded Successfully + Cheats Baixados com Sucesso + + + CheatsDownloadedSuccessfully_MSG + Você baixou os cheats com sucesso. Para esta versão do jogo a partir do repositório selecionado. Você pode tentar baixar de outro repositório, se estiver disponível, também será possível usá-lo selecionando o arquivo da lista. + + + Failed to save: + Falha ao salvar: + + + Failed to download: + Falha ao baixar: + + + Download Complete + Download Completo + + + DownloadComplete_MSG + Patches Baixados com Sucesso! Todos os patches disponíveis para todos os jogos foram baixados, não é necessário baixá-los individualmente para cada jogo como acontece com os Cheats. Se o patch não aparecer, pode ser que ele não exista para o número de série e a versão específicos do jogo. + + + Failed to parse JSON data from HTML. + Falha ao analisar dados JSON do HTML. + + + Failed to retrieve HTML page. + Falha ao recuperar a página HTML. + + + The game is in version: %1 + O jogo está na versão: %1 + + + The downloaded patch only works on version: %1 + O patch baixado só funciona na versão: %1 + + + You may need to update your game. + Talvez você precise atualizar seu jogo. + + + Incompatibility Notice + Aviso de incompatibilidade + + + Failed to open file: + Falha ao abrir o arquivo: + + + XML ERROR: + ERRO de XML: + + + Failed to open files.json for writing + Falha ao abrir files.json para gravação + + + Author: + Autor: + + + Directory does not exist: + O Diretório não existe: + + + Failed to open files.json for reading. + Falha ao abrir files.json para leitura. + + + Name: + Nome: + + + Can't apply cheats before the game is started + Não é possível aplicar cheats antes que o jogo comece. + + GameListFrame - Icon Icone - Name Nome - Serial Serial - Compatibility Compatibilidade - Region Região - Firmware Firmware - Size Tamanho - Version Versão - Path Diretório - Play Time Tempo Jogado - Never Played Nunca jogado - h h - m m - s s - Compatibility is untested Compatibilidade não testada - Game does not initialize properly / crashes the emulator Jogo não inicializa corretamente / trava o emulador - Game boots, but only displays a blank screen O jogo inicializa, mas exibe apenas uma tela vazia - Game displays an image but does not go past the menu Jogo exibe imagem mas não passa do menu - Game has game-breaking glitches or unplayable performance O jogo tem falhas que interrompem o jogo ou desempenho injogável - Game can be completed with playable performance and no major glitches O jogo pode ser concluído com desempenho jogável e sem grandes falhas @@ -1508,127 +1210,102 @@ CheckUpdate - Auto Updater Atualizador automático - Error Erro - Network error: Erro de rede: - Failed to parse update information. Falha ao analisar as informações de atualização. - No pre-releases found. Nenhuma pre-release encontrada. - Invalid release data. Dados da release inválidos. - No download URL found for the specified asset. Nenhuma URL de download encontrada para o asset especificado. - Your version is already up to date! Sua versão já está atualizada! - Update Available Atualização disponível - Update Channel Canal de Atualização - Current Version Versão atual - Latest Version Última versão - Do you want to update? Você quer atualizar? - Show Changelog Mostrar Changelog - Check for Updates at Startup Verificar Atualizações ao Iniciar - Update Atualizar - No Não - Hide Changelog Ocultar Changelog - Changes Alterações - Network error occurred while trying to access the URL Ocorreu um erro de rede ao tentar acessar o URL - Download Complete Download Completo - The update has been downloaded, press OK to install. A atualização foi baixada, pressione OK para instalar. - Failed to save the update file at Falha ao salvar o arquivo de atualização em - Starting Update... Iniciando atualização... - Failed to create the update script file Falha ao criar o arquivo de script de atualização @@ -1636,29 +1313,24 @@ GameListUtils - B B - KB KB - MB MB - GB GB - TB TB - + \ No newline at end of file diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index 63df2ff80..fbbabfac0 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -6,22 +6,18 @@ AboutDialog - About shadPS4 About shadPS4 - shadPS4 shadPS4 - shadPS4 is an experimental open-source emulator for the PlayStation 4. shadPS4 is an experimental open-source emulator for the PlayStation 4. - This software should not be used to play games you have not legally obtained. This software should not be used to play games you have not legally obtained. @@ -29,7 +25,6 @@ ElfViewer - Open Folder Open Folder @@ -37,17 +32,14 @@ GameInfoClass - Loading game list, please wait :3 Loading game list, please wait :3 - Cancel Cancel - Loading... Loading... @@ -55,12 +47,10 @@ InstallDirSelect - shadPS4 - Choose directory shadPS4 - Choose directory - Select which directory you want to install to. Select which directory you want to install to. @@ -68,27 +58,22 @@ GameInstallDialog - shadPS4 - Choose directory shadPS4 - Choose directory - Directory to install games Directory to install games - Browse Browse - Error Error - The value for location to install games is not valid. The value for location to install games is not valid. @@ -96,167 +81,134 @@ GuiContextMenus - Create Shortcut Create Shortcut - Trapaças / Patches Coduri / Patch-uri - SFO Viewer SFO Viewer - Trophy Viewer Trophy Viewer - Open Folder... Deschide Folder... - Open Game Folder Deschide Folder Joc - Open Save Data Folder Deschide Folder Date Salvate - Open Log Folder Deschide Folder Jurnal - Copy info... Copy info... - Copy Name Copy Name - Copy Serial Copy Serial - Copy All Copy All - Delete... Delete... - Delete Game Delete Game - Delete Update Delete Update - Delete DLC Delete DLC - Compatibility... Compatibility... - Update database Update database - View report View report - Submit a report Submit a report - Shortcut creation Shortcut creation - Shortcut created successfully! Shortcut created successfully! - Error Error - Error creating shortcut! Error creating shortcut! - Install PKG Install PKG - Game Game - requiresEnableSeparateUpdateFolder_MSG This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. - This game has no update to delete! This game has no update to delete! - - + Update Update - This game has no DLC to delete! This game has no DLC to delete! - DLC DLC - Delete %1 Delete %1 - Are you sure you want to delete %1's %2 directory? Are you sure you want to delete %1's %2 directory? @@ -264,205 +216,285 @@ MainWindow - Open/Add Elf Folder Open/Add Elf Folder - Install Packages (PKG) Install Packages (PKG) - Boot Game Boot Game - Check for Updates Verifică actualizările - About shadPS4 About shadPS4 - Configure... Configure... - Install application from a .pkg file Install application from a .pkg file - Recent Games Recent Games - Exit Exit - Exit shadPS4 Exit shadPS4 - Exit the application. Exit the application. - Show Game List Show Game List - Game List Refresh Game List Refresh - Tiny Tiny - Small Small - Medium Medium - Large Large - List View List View - Grid View Grid View - Elf Viewer Elf Viewer - Game Install Directory Game Install Directory - Download Cheats/Patches Descarcă Coduri / Patch-uri - Dump Game List Dump Game List - PKG Viewer PKG Viewer - Search... Search... - File File - View View - Game List Icons Game List Icons - Game List Mode Game List Mode - Settings Settings - Utils Utils - Themes Themes - Help Ajutor - Dark Dark - Light Light - Green Green - Blue Blue - Violet Violet - toolBar toolBar + + Game List + Lista jocurilor + + + * Unsupported Vulkan Version + * Versiune Vulkan nesuportată + + + Download Cheats For All Installed Games + Descarcă Cheats pentru toate jocurile instalate + + + Download Patches For All Games + Descarcă Patches pentru toate jocurile + + + Download Complete + Descărcare completă + + + You have downloaded cheats for all the games you have installed. + Ai descărcat cheats pentru toate jocurile instalate. + + + Patches Downloaded Successfully! + Patches descărcate cu succes! + + + All Patches available for all games have been downloaded. + Toate Patches disponibile pentru toate jocurile au fost descărcate. + + + Games: + Jocuri: + + + PKG File (*.PKG) + Fișier PKG (*.PKG) + + + ELF files (*.bin *.elf *.oelf) + Fișiere ELF (*.bin *.elf *.oelf) + + + Game Boot + Boot Joc + + + Only one file can be selected! + Numai un fișier poate fi selectat! + + + PKG Extraction + Extracție PKG + + + Patch detected! + Patch detectat! + + + PKG and Game versions match: + Versiunile PKG și ale jocului sunt compatibile: + + + Would you like to overwrite? + Doriți să suprascrieți? + + + PKG Version %1 is older than installed version: + Versiunea PKG %1 este mai veche decât versiunea instalată: + + + Game is installed: + Jocul este instalat: + + + Would you like to install Patch: + Doriți să instalați patch-ul: + + + DLC Installation + Instalare DLC + + + Would you like to install DLC: %1? + Doriți să instalați DLC-ul: %1? + + + DLC already installed: + DLC deja instalat: + + + Game already installed + Jocul deja instalat + + + PKG is a patch, please install the game first! + PKG este un patch, te rugăm să instalezi mai întâi jocul! + + + PKG ERROR + EROARE PKG + + + Extracting PKG %1/%2 + Extracție PKG %1/%2 + + + Extraction Finished + Extracție terminată + + + Game successfully installed at %1 + Jocul a fost instalat cu succes la %1 + + + File doesn't appear to be a valid PKG file + Fișierul nu pare să fie un fișier PKG valid + PKGViewer - Open Folder Open Folder @@ -470,7 +502,6 @@ TrophyViewer - Trophy Viewer Trophy Viewer @@ -478,1029 +509,700 @@ SettingsDialog - Settings Settings - General General - System System - Console Language Console Language - Emulator Language Emulator Language - Emulator Emulator - Enable Fullscreen Enable Fullscreen - Enable Separate Update Folder Enable Separate Update Folder - Show Splash Show Splash - Is PS4 Pro Is PS4 Pro - Enable Discord Rich Presence Activați Discord Rich Presence - Username Username - Trophy Key Trophy Key - Trophy Trophy - Logger Logger - Log Type Log Type - Log Filter Log Filter - Input Introducere - Cursor Cursor - Hide Cursor Ascunde cursorul - Hide Cursor Idle Timeout Timeout pentru ascunderea cursorului inactiv - s s - Controller Controler - Back Button Behavior Comportament buton înapoi - Graphics Graphics - Graphics Device Graphics Device - Width Width - Height Height - Vblank Divider Vblank Divider - Advanced Advanced - Enable Shaders Dumping Enable Shaders Dumping - Enable NULL GPU Enable NULL GPU - Paths Trasee - Game Folders Dosare de joc - Add... Adaugă... - Remove Eliminare - Debug Debug - Enable Debug Dumping Enable Debug Dumping - Enable Vulkan Validation Layers Enable Vulkan Validation Layers - Enable Vulkan Synchronization Validation Enable Vulkan Synchronization Validation - Enable RenderDoc Debugging Enable RenderDoc Debugging - Update Actualizare - Check for Updates at Startup Verifică actualizări la pornire - Update Channel Canal de Actualizare - Check for Updates Verifică actualizări - GUI Settings Setări GUI - Disable Trophy Pop-ups Disable Trophy Pop-ups - Play title music Redă muzica titlului - Update Compatibility Database On Startup Update Compatibility Database On Startup - Game Compatibility Game Compatibility - Display Compatibility Data Display Compatibility Data - Update Compatibility Database Update Compatibility Database - Volume Volum - Audio Backend Audio Backend - - - MainWindow - - Game List - Lista jocurilor - - - - * Unsupported Vulkan Version - * Versiune Vulkan nesuportată - - - - Download Cheats For All Installed Games - Descarcă Cheats pentru toate jocurile instalate - - - - Download Patches For All Games - Descarcă Patches pentru toate jocurile - - - - Download Complete - Descărcare completă - - - - You have downloaded cheats for all the games you have installed. - Ai descărcat cheats pentru toate jocurile instalate. - - - - Patches Downloaded Successfully! - Patches descărcate cu succes! - - - - All Patches available for all games have been downloaded. - Toate Patches disponibile pentru toate jocurile au fost descărcate. - - - - Games: - Jocuri: - - - - PKG File (*.PKG) - Fișier PKG (*.PKG) - - - - ELF files (*.bin *.elf *.oelf) - Fișiere ELF (*.bin *.elf *.oelf) - - - - Game Boot - Boot Joc - - - - Only one file can be selected! - Numai un fișier poate fi selectat! - - - - PKG Extraction - Extracție PKG - - - - Patch detected! - Patch detectat! - - - - PKG and Game versions match: - Versiunile PKG și ale jocului sunt compatibile: - - - - Would you like to overwrite? - Doriți să suprascrieți? - - - - PKG Version %1 is older than installed version: - Versiunea PKG %1 este mai veche decât versiunea instalată: - - - - Game is installed: - Jocul este instalat: - - - - Would you like to install Patch: - Doriți să instalați patch-ul: - - - - DLC Installation - Instalare DLC - - - - Would you like to install DLC: %1? - Doriți să instalați DLC-ul: %1? - - - - DLC already installed: - DLC deja instalat: - - - - Game already installed - Jocul deja instalat - - - - PKG is a patch, please install the game first! - PKG este un patch, te rugăm să instalezi mai întâi jocul! - - - - PKG ERROR - EROARE PKG - - - - Extracting PKG %1/%2 - Extracție PKG %1/%2 - - - - Extraction Finished - Extracție terminată - - - - Game successfully installed at %1 - Jocul a fost instalat cu succes la %1 - - - - File doesn't appear to be a valid PKG file - Fișierul nu pare să fie un fișier PKG valid - - - - CheatsPatches - - - Cheats / Patches for - Cheats / Patches for - - - - defaultTextEdit_MSG - Cheats/Patches sunt experimentale.\nUtilizați cu prudență.\n\nDescărcați cheats individual prin selectarea depozitului și făcând clic pe butonul de descărcare.\nÎn fila Patches, puteți descărca toate patch-urile deodată, alege pe cele pe care doriți să le utilizați și salvați selecția.\n\nDeoarece nu dezvoltăm Cheats/Patches,\nte rugăm să raportezi problemele autorului cheat-ului.\n\nAi creat un nou cheat? Vizitează:\nhttps://github.com/shadps4-emu/ps4_cheats - - - - No Image Available - Nu este disponibilă imaginea - - - - Serial: - Serial: - - - - Version: - Versiune: - - - - Size: - Dimensiune: - - - - Select Cheat File: - Selectează fișierul Cheat: - - - - Repository: - Repository: - - - - Download Cheats - Descarcă Cheats - - - - Delete File - Șterge Fișierul - - - - No files selected. - Nu sunt fișiere selectate. - - - - You can delete the cheats you don't want after downloading them. - Poti șterge cheats-urile pe care nu le dorești după ce le-ai descărcat. - - - - Do you want to delete the selected file?\n%1 - Vrei să ștergi fișierul selectat?\n%1 - - - - Select Patch File: - Selectează fișierul Patch: - - - - Download Patches - Descarcă Patches - - - Save Salvează - - Cheats - Cheats - - - - Patches - Patches - - - - Error - Eroare - - - - No patch selected. - Nu este selectat niciun patch. - - - - Unable to open files.json for reading. - Imposibil de deschis files.json pentru citire. - - - - No patch file found for the current serial. - Nu s-a găsit niciun fișier patch pentru serialul curent. - - - - Unable to open the file for reading. - Imposibil de deschis fișierul pentru citire. - - - - Unable to open the file for writing. - Imposibil de deschis fișierul pentru scriere. - - - - Failed to parse XML: - Nu s-a reușit pararea XML: - - - - Success - Succes - - - - Options saved successfully. - Opțiunile au fost salvate cu succes. - - - - Invalid Source - Sursă invalidă - - - - The selected source is invalid. - Sursa selectată este invalidă. - - - - File Exists - Fișier existent - - - - File already exists. Do you want to replace it? - Fișierul există deja. Vrei să-l înlocuiești? - - - - Failed to save file: - Nu s-a reușit salvarea fișierului: - - - - Failed to download file: - Nu s-a reușit descărcarea fișierului: - - - - Cheats Not Found - Cheats Nu au fost găsite - - - - CheatsNotFound_MSG - Nu au fost găsite cheats pentru acest joc în această versiune a repository-ului selectat, încearcă un alt repository sau o versiune diferită a jocului. - - - - Cheats Downloaded Successfully - Cheats descărcate cu succes - - - - CheatsDownloadedSuccessfully_MSG - Ai descărcat cu succes cheats-urile pentru această versiune a jocului din repository-ul selectat. Poți încerca descărcarea din alt repository; dacă este disponibil, va fi posibil să-l folosești selectând fișierul din listă. - - - - Failed to save: - Nu s-a reușit salvarea: - - - - Failed to download: - Nu s-a reușit descărcarea: - - - - Download Complete - Descărcare completă - - - - DownloadComplete_MSG - Patches descărcate cu succes! Toate Patches disponibile pentru toate jocurile au fost descărcate; nu este nevoie să le descarci individual pentru fiecare joc, așa cum se întâmplă cu Cheats. Dacă patch-ul nu apare, este posibil să nu existe pentru seria și versiunea specifică a jocului. - - - - Failed to parse JSON data from HTML. - Nu s-a reușit pararea datelor JSON din HTML. - - - - Failed to retrieve HTML page. - Nu s-a reușit obținerea paginii HTML. - - - - The game is in version: %1 - Jocul este în versiunea: %1 - - - - The downloaded patch only works on version: %1 - Patch-ul descărcat funcționează doar pe versiunea: %1 - - - - You may need to update your game. - Este posibil să trebuiască să actualizezi jocul tău. - - - - Incompatibility Notice - Avertizare de incompatibilitate - - - - Failed to open file: - Nu s-a reușit deschiderea fișierului: - - - - XML ERROR: - EROARE XML: - - - - Failed to open files.json for writing - Nu s-a reușit deschiderea files.json pentru scriere - - - - Author: - Autor: - - - - Directory does not exist: - Directorul nu există: - - - - Failed to open files.json for reading. - Nu s-a reușit deschiderea files.json pentru citire. - - - - Name: - Nume: - - - - Can't apply cheats before the game is started - Nu poți aplica cheats înainte ca jocul să înceapă. - - - - SettingsDialog - - - Save - Salvează - - - Apply Aplică - Restore Defaults Restabilește Impozitivele - Close Închide - Point your mouse at an option to display its description. Indicați mouse-ul asupra unei opțiuni pentru a afișa descrierea acesteia. - consoleLanguageGroupBox Limba consolei:\nSetează limba pe care o folosește jocul PS4.\nSe recomandă să setezi această opțiune pe o limbă pe care jocul o suportă, ceea ce poate varia în funcție de regiune. - emulatorLanguageGroupBox Limba emulatorului:\nSetează limba interfeței utilizatorului a emulatorului. - fullscreenCheckBox Activează modul pe ecran complet:\nPune automat fereastra jocului în modul pe ecran complet.\nAceasta poate fi dezactivată apăsând tasta F11. - separateUpdatesCheckBox Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. - showSplashCheckBox Afișează ecranul de încărcare:\nAfișează ecranul de încărcare al jocului (o imagine specială) în timp ce jocul pornește. - ps4proCheckBox Este PS4 Pro:\nFace ca emulatorul să se comporte ca un PS4 PRO, ceea ce poate activa funcții speciale în jocurile care o suportă. - discordRPCCheckbox Activați Discord Rich Presence:\nAfișează pictograma emulatorului și informații relevante pe profilul dumneavoastră Discord. - userName Nume utilizator:\nSetează numele de utilizator al contului PS4, care poate fi afișat de unele jocuri. - TrophyKey Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. - logTypeGroupBox Tip jurnal:\nSetează dacă să sincronizezi ieșirea ferestrei de jurnal pentru performanță. Aceasta poate avea efecte adverse asupra emulării. - logFilter Filtrul jurnalului:\nFiltrează jurnalul pentru a imprima doar informații specifice.\nExemple: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Niveluri: Trace, Debug, Info, Warning, Error, Critical - în această ordine, un nivel specific reduce toate nivelurile anterioare din listă și înregistrează toate nivelurile ulterioare. - updaterGroupBox Actualizare:\nRelease: Versiuni oficiale lansate în fiecare lună, care pot fi foarte învechite, dar sunt mai fiabile și testate.\nNightly: Versiuni de dezvoltare care conțin toate cele mai recente funcții și corecții, dar pot conține erori și sunt mai puțin stabile. - GUIgroupBox Redă muzica titlului:\nDacă un joc o suportă, activează redarea muzicii speciale când selectezi jocul în GUI. - disableTrophycheckBox Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). - hideCursorGroupBox Ascunde cursorul:\nAlegeți când va dispărea cursorul:\nNiciodată: Vei vedea întotdeauna mouse-ul.\nInactiv: Setează un timp pentru a dispărea după inactivitate.\nÎntotdeauna: nu vei vedea niciodată mouse-ul. - idleTimeoutGroupBox Setați un timp pentru ca mouse-ul să dispară după ce a fost inactiv. - backButtonBehaviorGroupBox Comportamentul butonului înapoi:\nSetează butonul înapoi al controlerului să imite atingerea poziției specificate pe touchpad-ul PS4. - enableCompatibilityCheckBox Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. - checkCompatibilityOnStartupCheckBox Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. - updateCompatibilityButton Update Compatibility Database:\nImmediately update the compatibility database. - Never Niciodată - Idle Inactiv - Always Întotdeauna - Touchpad Left Touchpad Stânga - Touchpad Right Touchpad Dreapta - Touchpad Center Centru Touchpad - None Niciunul - graphicsAdapterGroupBox Dispozitiv grafic:\nPe sistemele cu mai multe GPU-uri, alege GPU-ul pe care emulatorul îl va folosi din lista derulantă,\nsau selectează "Auto Select" pentru a-l determina automat. - resolutionLayout Lățime/Înălțime:\nSetează dimensiunea ferestrei emulatorului la lansare, care poate fi redimensionată în timpul jocului.\nAceasta este diferită de rezoluția din joc. - heightDivider Împărțitor Vblank:\nRata de cadre cu care emulatorul se reîmprospătează este multiplicată cu acest număr. Schimbarea acestuia poate avea efecte adverse, cum ar fi creșterea vitezei jocului sau distrugerea funcționalității critice a jocului care nu se așteaptă ca aceasta să se schimbe! - dumpShadersCheckBox Activează salvarea shaderelor:\nÎn scopuri de depanare tehnică, salvează shader-urile jocului într-un folder pe măsură ce sunt randate. - nullGpuCheckBox Activează GPU Null:\nÎn scopuri de depanare tehnică, dezactivează redarea jocului ca și cum nu ar exista o placă grafică. - gameFoldersBox Folderele jocurilor:\nLista folderelor pentru a verifica jocurile instalate. - addFolderButton Adăugați:\nAdăugați un folder la listă. - removeFolderButton Eliminați:\nÎndepărtați un folder din listă. - debugDump Activează salvarea pentru depanare:\nSalvează simbolurile de import și export și informațiile din antetul fișierului pentru aplicația PS4 care rulează în prezent într-un director. - vkValidationCheckBox Activează straturile de validare Vulkan:\nActivează un sistem care validează starea renderer-ului Vulkan și înregistrează informații despre starea sa internă. Aceasta va reduce performanța și probabil va schimba comportamentul emulării. - vkSyncValidationCheckBox Activează validarea sincronizării Vulkan:\nActivează un sistem care validează sincronizarea sarcinilor de redare Vulkan. Aceasta va reduce performanța și probabil va schimba comportamentul emulării. - rdocCheckBox Activează depanarea RenderDoc:\nDacă este activat, emulatorul va oferi compatibilitate cu Renderdoc pentru a permite capturarea și analiza cadrului redat în prezent. + + CheatsPatches + + Cheats / Patches for + Cheats / Patches for + + + defaultTextEdit_MSG + Cheats/Patches sunt experimentale.\nUtilizați cu prudență.\n\nDescărcați cheats individual prin selectarea depozitului și făcând clic pe butonul de descărcare.\nÎn fila Patches, puteți descărca toate patch-urile deodată, alege pe cele pe care doriți să le utilizați și salvați selecția.\n\nDeoarece nu dezvoltăm Cheats/Patches,\nte rugăm să raportezi problemele autorului cheat-ului.\n\nAi creat un nou cheat? Vizitează:\nhttps://github.com/shadps4-emu/ps4_cheats + + + No Image Available + Nu este disponibilă imaginea + + + Serial: + Serial: + + + Version: + Versiune: + + + Size: + Dimensiune: + + + Select Cheat File: + Selectează fișierul Cheat: + + + Repository: + Repository: + + + Download Cheats + Descarcă Cheats + + + Delete File + Șterge Fișierul + + + No files selected. + Nu sunt fișiere selectate. + + + You can delete the cheats you don't want after downloading them. + Poti șterge cheats-urile pe care nu le dorești după ce le-ai descărcat. + + + Do you want to delete the selected file?\n%1 + Vrei să ștergi fișierul selectat?\n%1 + + + Select Patch File: + Selectează fișierul Patch: + + + Download Patches + Descarcă Patches + + + Save + Salvează + + + Cheats + Cheats + + + Patches + Patches + + + Error + Eroare + + + No patch selected. + Nu este selectat niciun patch. + + + Unable to open files.json for reading. + Imposibil de deschis files.json pentru citire. + + + No patch file found for the current serial. + Nu s-a găsit niciun fișier patch pentru serialul curent. + + + Unable to open the file for reading. + Imposibil de deschis fișierul pentru citire. + + + Unable to open the file for writing. + Imposibil de deschis fișierul pentru scriere. + + + Failed to parse XML: + Nu s-a reușit pararea XML: + + + Success + Succes + + + Options saved successfully. + Opțiunile au fost salvate cu succes. + + + Invalid Source + Sursă invalidă + + + The selected source is invalid. + Sursa selectată este invalidă. + + + File Exists + Fișier existent + + + File already exists. Do you want to replace it? + Fișierul există deja. Vrei să-l înlocuiești? + + + Failed to save file: + Nu s-a reușit salvarea fișierului: + + + Failed to download file: + Nu s-a reușit descărcarea fișierului: + + + Cheats Not Found + Cheats Nu au fost găsite + + + CheatsNotFound_MSG + Nu au fost găsite cheats pentru acest joc în această versiune a repository-ului selectat, încearcă un alt repository sau o versiune diferită a jocului. + + + Cheats Downloaded Successfully + Cheats descărcate cu succes + + + CheatsDownloadedSuccessfully_MSG + Ai descărcat cu succes cheats-urile pentru această versiune a jocului din repository-ul selectat. Poți încerca descărcarea din alt repository; dacă este disponibil, va fi posibil să-l folosești selectând fișierul din listă. + + + Failed to save: + Nu s-a reușit salvarea: + + + Failed to download: + Nu s-a reușit descărcarea: + + + Download Complete + Descărcare completă + + + DownloadComplete_MSG + Patches descărcate cu succes! Toate Patches disponibile pentru toate jocurile au fost descărcate; nu este nevoie să le descarci individual pentru fiecare joc, așa cum se întâmplă cu Cheats. Dacă patch-ul nu apare, este posibil să nu existe pentru seria și versiunea specifică a jocului. + + + Failed to parse JSON data from HTML. + Nu s-a reușit pararea datelor JSON din HTML. + + + Failed to retrieve HTML page. + Nu s-a reușit obținerea paginii HTML. + + + The game is in version: %1 + Jocul este în versiunea: %1 + + + The downloaded patch only works on version: %1 + Patch-ul descărcat funcționează doar pe versiunea: %1 + + + You may need to update your game. + Este posibil să trebuiască să actualizezi jocul tău. + + + Incompatibility Notice + Avertizare de incompatibilitate + + + Failed to open file: + Nu s-a reușit deschiderea fișierului: + + + XML ERROR: + EROARE XML: + + + Failed to open files.json for writing + Nu s-a reușit deschiderea files.json pentru scriere + + + Author: + Autor: + + + Directory does not exist: + Directorul nu există: + + + Failed to open files.json for reading. + Nu s-a reușit deschiderea files.json pentru citire. + + + Name: + Nume: + + + Can't apply cheats before the game is started + Nu poți aplica cheats înainte ca jocul să înceapă. + + GameListFrame - Icon Icon - Name Nume - Serial Serie - Compatibility Compatibility - Region Regiune - Firmware Firmware - Size Dimensiune - Version Versiune - Path Drum - Play Time Timp de Joacă - Never Played Never Played - h h - m m - s s - Compatibility is untested Compatibility is untested - Game does not initialize properly / crashes the emulator Game does not initialize properly / crashes the emulator - Game boots, but only displays a blank screen Game boots, but only displays a blank screen - Game displays an image but does not go past the menu Game displays an image but does not go past the menu - Game has game-breaking glitches or unplayable performance Game has game-breaking glitches or unplayable performance - Game can be completed with playable performance and no major glitches Game can be completed with playable performance and no major glitches @@ -1508,127 +1210,102 @@ CheckUpdate - Auto Updater Actualizator automat - Error Eroare - Network error: Eroare de rețea: - Failed to parse update information. Nu s-au putut analiza informațiile de actualizare. - No pre-releases found. Nu au fost găsite pre-lansări. - Invalid release data. Datele versiunii sunt invalide. - No download URL found for the specified asset. Nu s-a găsit URL de descărcare pentru resursa specificată. - Your version is already up to date! Versiunea ta este deja actualizată! - Update Available Actualizare disponibilă - Update Channel Canal de Actualizare - Current Version Versiunea curentă - Latest Version Ultima versiune - Do you want to update? Doriți să actualizați? - Show Changelog Afișați jurnalul de modificări - Check for Updates at Startup Verifică actualizări la pornire - Update Actualizare - No Nu - Hide Changelog Ascunde jurnalul de modificări - Changes Modificări - Network error occurred while trying to access the URL A apărut o eroare de rețea în timpul încercării de a accesa URL-ul - Download Complete Descărcare completă - The update has been downloaded, press OK to install. Actualizarea a fost descărcată, apăsați OK pentru a instala. - Failed to save the update file at Nu s-a putut salva fișierul de actualizare la - Starting Update... Încep actualizarea... - Failed to create the update script file Nu s-a putut crea fișierul script de actualizare @@ -1636,29 +1313,24 @@ GameListUtils - B B - KB KB - MB MB - GB GB - TB TB - + \ No newline at end of file diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index 88eff1aeb..e914e4d49 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -6,22 +6,18 @@ AboutDialog - About shadPS4 О shadPS4 - shadPS4 shadPS4 - shadPS4 is an experimental open-source emulator for the PlayStation 4. shadPS4 это экспериментальный эмулятор с открытым исходным кодом для PlayStation 4. - This software should not be used to play games you have not legally obtained. Это програмное обеспечение не должно использоваться для запуска игр, которые вы получили нелегально. @@ -29,7 +25,6 @@ ElfViewer - Open Folder Открыть папку @@ -37,17 +32,14 @@ GameInfoClass - Loading game list, please wait :3 Загрузка списка игр, пожалуйста подождите :3 - Cancel Отмена - Loading... Загрузка... @@ -55,12 +47,10 @@ InstallDirSelect - shadPS4 - Choose directory shadPS4 - Выберите папку - Select which directory you want to install to. Выберите папку, в которую вы хотите установить. @@ -68,27 +58,22 @@ GameInstallDialog - shadPS4 - Choose directory shadPS4 - Выберите папку - Directory to install games Папка для установки игр - Browse Обзор - Error Ошибка - The value for location to install games is not valid. Недопустимое значение местоположения для установки игр. @@ -96,167 +81,134 @@ GuiContextMenus - Create Shortcut Создать ярлык - Cheats / Patches Читы и патчи - SFO Viewer Просмотр SFO - Trophy Viewer Просмотр трофеев - Open Folder... Открыть папку... - Open Game Folder Открыть папку с игрой - Open Save Data Folder Открыть папку сохранений - Open Log Folder Открыть папку логов - Copy info... Копировать информацию... - Copy Name Копировать имя - Copy Serial Копировать серийный номер - Copy All Копировать все - Delete... Удаление... - Delete Game Удалить игру - Delete Update Удалить обновление - Delete DLC Удалить DLC - Compatibility... Совместимость... - Update database Обновить базу данных - View report Посмотреть отчет - Submit a report Отправить отчет - Shortcut creation Создание ярлыка - Shortcut created successfully! Ярлык создан успешно! - Error Ошибка - Error creating shortcut! Ошибка создания ярлыка! - Install PKG Установить PKG - Game Игры - requiresEnableSeparateUpdateFolder_MSG Эта функция требует включения настройки 'Отдельная папка обновлений'. Если вы хотите использовать эту функцию, пожалуйста включите её. - This game has no update to delete! У этой игры нет обновлений для удаления! - - + Update Обновления - This game has no DLC to delete! У этой игры нет DLC для удаления! - DLC DLC - Delete %1 Удалить %1 - Are you sure you want to delete %1's %2 directory? Вы уверены, что хотите удалить папку %2 %1? @@ -264,205 +216,285 @@ MainWindow - Open/Add Elf Folder Открыть/Добавить папку Elf - Install Packages (PKG) Установить пакеты (PKG) - Boot Game Запустить игру - Check for Updates Проверить обновления - About shadPS4 О shadPS4 - Configure... Настроить... - Install application from a .pkg file Установить приложение из файла .pkg - Recent Games Недавние игры - Exit Выход - Exit shadPS4 Выйти из shadPS4 - Exit the application. Выйти из приложения. - Show Game List Показать список игр - Game List Refresh Обновить список игр - Tiny Крошечный - Small Маленький - Medium Средний - Large Большой - List View Список - Grid View Сетка - Elf Viewer Elf - Game Install Directory Каталог установки игры - Download Cheats/Patches Скачать читы или патчи - Dump Game List Дамп списка игр - PKG Viewer Просмотр PKG - Search... Поиск... - File Файл - View Вид - Game List Icons Размер иконок списка игр - Game List Mode Вид списка игр - Settings Настройки - Utils Утилиты - Themes Темы - Help Помощь - Dark Темная - Light Светлая - Green Зеленая - Blue Синяя - Violet Фиолетовая - toolBar Панель инструментов + + Game List + Список игр + + + * Unsupported Vulkan Version + * Неподдерживаемая версия Vulkan + + + Download Cheats For All Installed Games + Скачать читы для всех установленных игр + + + Download Patches For All Games + Скачать патчи для всех игр + + + Download Complete + Скачивание завершено + + + You have downloaded cheats for all the games you have installed. + Вы скачали читы для всех установленных игр. + + + Patches Downloaded Successfully! + Патчи успешно скачаны! + + + All Patches available for all games have been downloaded. + Все доступные патчи для всех игр были скачаны. + + + Games: + Игры: + + + PKG File (*.PKG) + Файл PKG (*.PKG) + + + ELF files (*.bin *.elf *.oelf) + Файлы ELF (*.bin *.elf *.oelf) + + + Game Boot + Запуск игры + + + Only one file can be selected! + Можно выбрать только один файл! + + + PKG Extraction + Извлечение PKG + + + Patch detected! + Обнаружен патч! + + + PKG and Game versions match: + Версии PKG и игры совпадают: + + + Would you like to overwrite? + Хотите перезаписать? + + + PKG Version %1 is older than installed version: + Версия PKG %1 старее установленной версии: + + + Game is installed: + Игра установлена: + + + Would you like to install Patch: + Хотите установить патч: + + + DLC Installation + Установка DLC + + + Would you like to install DLC: %1? + Вы хотите установить DLC: %1? + + + DLC already installed: + DLC уже установлен: + + + Game already installed + Игра уже установлена + + + PKG is a patch, please install the game first! + PKG - это патч, сначала установите игру! + + + PKG ERROR + ОШИБКА PKG + + + Extracting PKG %1/%2 + Извлечение PKG %1/%2 + + + Extraction Finished + Извлечение завершено + + + Game successfully installed at %1 + Игра успешно установлена в %1 + + + File doesn't appear to be a valid PKG file + Файл не является допустимым файлом PKG + PKGViewer - Open Folder Открыть папку @@ -470,7 +502,6 @@ TrophyViewer - Trophy Viewer Просмотр трофеев @@ -478,1029 +509,700 @@ SettingsDialog - Settings Настройки - General Общее - System Система - Console Language Язык консоли - Emulator Language Язык эмулятора - Emulator Эмулятор - Enable Fullscreen Полноэкранный режим - Enable Separate Update Folder Отдельная папка обновлений - Show Splash Показывать заставку - Is PS4 Pro Режим PS4 Pro - Enable Discord Rich Presence Включить Discord Rich Presence - Username Имя пользователя - Trophy Key Trophy Key - Trophy Trophy - Logger Логирование - Log Type Тип логов - Log Filter Фильтр логов - Input Ввод - Cursor Курсор мыши - Hide Cursor Скрывать курсор - Hide Cursor Idle Timeout Время скрытия курсора при бездействии - s сек - Controller Контроллер - Back Button Behavior Поведение кнопки назад - Graphics Графика - Graphics Device Графическое устройство - Width Ширина - Height Высота - Vblank Divider Делитель Vblank - Advanced Продвинутые - Enable Shaders Dumping Включить дамп шейдеров - Enable NULL GPU Включить NULL GPU - Paths Пути - Game Folders Игровые папки - Add... Добавить... - Remove Удалить - Debug Отладка - Enable Debug Dumping Включить отладочные дампы - Enable Vulkan Validation Layers Включить слои валидации Vulkan - Enable Vulkan Synchronization Validation Включить валидацию синхронизации Vulkan - Enable RenderDoc Debugging Включить отладку RenderDoc - Update Обновление - Check for Updates at Startup Проверка при запуске - Update Channel Канал обновления - Check for Updates Проверить обновления - GUI Settings Интерфейс - Disable Trophy Pop-ups Отключить уведомления о трофеях - Play title music Играть заглавную музыку - Update Compatibility Database On Startup Обновлять базу совместимости при запуске - Game Compatibility Совместимость игр - Display Compatibility Data Показывать данные совместимости - Update Compatibility Database Обновить базу совместимости - Volume Громкость - Audio Backend Звуковая Подсистема - - - MainWindow - - Game List - Список игр - - - - * Unsupported Vulkan Version - * Неподдерживаемая версия Vulkan - - - - Download Cheats For All Installed Games - Скачать читы для всех установленных игр - - - - Download Patches For All Games - Скачать патчи для всех игр - - - - Download Complete - Скачивание завершено - - - - You have downloaded cheats for all the games you have installed. - Вы скачали читы для всех установленных игр. - - - - Patches Downloaded Successfully! - Патчи успешно скачаны! - - - - All Patches available for all games have been downloaded. - Все доступные патчи для всех игр были скачаны. - - - - Games: - Игры: - - - - PKG File (*.PKG) - Файл PKG (*.PKG) - - - - ELF files (*.bin *.elf *.oelf) - Файлы ELF (*.bin *.elf *.oelf) - - - - Game Boot - Запуск игры - - - - Only one file can be selected! - Можно выбрать только один файл! - - - - PKG Extraction - Извлечение PKG - - - - Patch detected! - Обнаружен патч! - - - - PKG and Game versions match: - Версии PKG и игры совпадают: - - - - Would you like to overwrite? - Хотите перезаписать? - - - - PKG Version %1 is older than installed version: - Версия PKG %1 старее установленной версии: - - - - Game is installed: - Игра установлена: - - - - Would you like to install Patch: - Хотите установить патч: - - - - DLC Installation - Установка DLC - - - - Would you like to install DLC: %1? - Вы хотите установить DLC: %1? - - - - DLC already installed: - DLC уже установлен: - - - - Game already installed - Игра уже установлена - - - - PKG is a patch, please install the game first! - PKG - это патч, сначала установите игру! - - - - PKG ERROR - ОШИБКА PKG - - - - Extracting PKG %1/%2 - Извлечение PKG %1/%2 - - - - Extraction Finished - Извлечение завершено - - - - Game successfully installed at %1 - Игра успешно установлена в %1 - - - - File doesn't appear to be a valid PKG file - Файл не является допустимым файлом PKG - - - - CheatsPatches - - - Cheats / Patches for - Читы и патчи для - - - - defaultTextEdit_MSG - Читы и патчи экспериментальны.\nИспользуйте с осторожностью.\n\nСкачивайте читы, выбрав репозиторий и нажав на кнопку загрузки.\nВо вкладке "Патчи" вы можете скачать все патчи сразу, выбирать какие вы хотите использовать, и сохранять выбор.\n\nПоскольку мы не разрабатываем читы/патчи,\nпожалуйста сообщайте о проблемах автору чита/патча.\n\nСоздали новый чит? Посетите:\nhttps://github.com/shadps4-emu/ps4_cheats - - - - No Image Available - Изображение недоступно - - - - Serial: - Серийный номер: - - - - Version: - Версия: - - - - Size: - Размер: - - - - Select Cheat File: - Выберите файл чита: - - - - Repository: - Репозиторий: - - - - Download Cheats - Скачать читы - - - - Delete File - Удалить файл - - - - No files selected. - Файлы не выбраны. - - - - You can delete the cheats you don't want after downloading them. - Вы можете удалить ненужные читы после их скачивания. - - - - Do you want to delete the selected file?\n%1 - Вы хотите удалить выбранный файл?\n%1 - - - - Select Patch File: - Выберите файл патча: - - - - Download Patches - Скачать патчи - - - Save Сохранить - - Cheats - Читы - - - - Patches - Патчи - - - - Error - Ошибка - - - - No patch selected. - Патч не выбран. - - - - Unable to open files.json for reading. - Не удалось открыть файл files.json для чтения. - - - - No patch file found for the current serial. - Не найден файл патча для текущего серийного номера. - - - - Unable to open the file for reading. - Не удалось открыть файл для чтения. - - - - Unable to open the file for writing. - Не удалось открыть файл для записи. - - - - Failed to parse XML: - Не удалось разобрать XML: - - - - Success - Успех - - - - Options saved successfully. - Опции успешно сохранены. - - - - Invalid Source - Неверный источник - - - - The selected source is invalid. - Выбранный источник недействителен. - - - - File Exists - Файл существует - - - - File already exists. Do you want to replace it? - Файл уже существует. Хотите заменить его? - - - - Failed to save file: - Не удалось сохранить файл: - - - - Failed to download file: - Не удалось скачать файл: - - - - Cheats Not Found - Читы не найдены - - - - CheatsNotFound_MSG - Читы не найдены для этой игры в выбранном репозитории. Попробуйте другой репозиторий или другую версию игры. - - - - Cheats Downloaded Successfully - Читы успешно скачаны - - - - CheatsDownloadedSuccessfully_MSG - Вы успешно скачали читы для этой версии игры из выбранного репозитория. Вы можете попробовать скачать из другого репозитория. Если он доступен, его также можно будет использовать, выбрав файл из списка. - - - - Failed to save: - Не удалось сохранить: - - - - Failed to download: - Не удалось скачать: - - - - Download Complete - Скачивание завершено - - - - DownloadComplete_MSG - Патчи успешно скачаны! Все доступные патчи для всех игр были скачаны, нет необходимости скачивать их по отдельности для каждой игры, как это происходит с читами. Если патч не появляется, возможно, его не существует для конкретного серийного номера и версии игры. - - - - Failed to parse JSON data from HTML. - Не удалось разобрать данные JSON из HTML. - - - - Failed to retrieve HTML page. - Не удалось получить HTML-страницу. - - - - The game is in version: %1 - Игра в версии: %1 - - - - The downloaded patch only works on version: %1 - Скачанный патч работает только на версии: %1 - - - - You may need to update your game. - Вам может понадобиться обновить игру. - - - - Incompatibility Notice - Уведомление о несовместимости - - - - Failed to open file: - Не удалось открыть файл: - - - - XML ERROR: - ОШИБКА XML: - - - - Failed to open files.json for writing - Не удалось открыть файл files.json для записи - - - - Author: - Автор: - - - - Directory does not exist: - Каталог не существует: - - - - Failed to open files.json for reading. - Не удалось открыть файл files.json для чтения. - - - - Name: - Имя: - - - - Can't apply cheats before the game is started - Невозможно применить читы до начала игры - - - - SettingsDialog - - - Save - Сохранить - - - Apply Применить - Restore Defaults По умолчанию - Close Закрыть - Point your mouse at an option to display its description. Наведите указатель мыши на опцию, чтобы отобразить ее описание. - consoleLanguageGroupBox Язык консоли:\nУстановите язык, который будет использоваться в играх PS4.\nРекомендуется устанавливать язык который поддерживается игрой, так как он может отличаться в зависимости от региона. - emulatorLanguageGroupBox Язык эмулятора:\nУстановите язык пользовательского интерфейса эмулятора. - fullscreenCheckBox Полноэкранный режим:\nАвтоматически переводит игровое окно в полноэкранный режим.\nВы можете отключить это, нажав клавишу F11. - separateUpdatesCheckBox Отдельная папка обновлений:\nПозволяет устанавливать обновления игры в отдельную папку для удобства. - showSplashCheckBox Показывать заставку:\nОтображает заставку игры (специальное изображение) во время запуска. - ps4proCheckBox Режим PS4 Pro:\nЗаставляет эмулятор работать как PS4 Pro, что может включить специальные функции в играх, поддерживающих это. - discordRPCCheckbox Включить Discord Rich Presence:\nОтображает значок эмулятора и соответствующую информацию в вашем профиле Discord. - userName Имя пользователя:\nУстановите имя пользователя аккаунта PS4. Это может отображаться в некоторых играх. - TrophyKey Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. - logTypeGroupBox Тип логов:\nУстановите, синхронизировать ли вывод окна логов ради производительности. Это может негативно сказаться на эмуляции. - logFilter Фильтр логов:\nФильтрует логи, чтобы показывать только определенную информацию.\nПримеры: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Уровни: Trace, Debug, Info, Warning, Error, Critical - в этом порядке, конкретный уровень глушит все предыдущие уровни в списке и показывает все последующие уровни. - updaterGroupBox Обновление:\nRelease: Официальные версии, которые выпускаются каждый месяц и могут быть очень старыми, но они более надежные и проверенные.\nNightly: Версии разработки, которые содержат все последние функции и исправления, но могут содержать ошибки и менее стабильны. - GUIgroupBox Играть заглавную музыку:\nВключает воспроизведение специальной музыки при выборе игры в списке, если она это поддерживает. - disableTrophycheckBox Отключить уведомления о трофеях:\nОтключает внутриигровые уведомления о трофеях. Прогресс трофеев по прежнему можно отслеживать в меню Просмотр трофеев (правая кнопка мыши по игре в главном окне). - hideCursorGroupBox Скрывать курсор:\nВыберите, когда курсор будет скрыт:\nНикогда: Вы всегда будете видеть курсор.\nПри бездействии: Установите время, через которое курсор будет скрыт при бездействии.\nВсегда: Курсор всегда будет скрыт. - idleTimeoutGroupBox Установите время, через которое курсор исчезнет при бездействии. - backButtonBehaviorGroupBox Поведение кнопки «Назад»:\nНастраивает кнопку «Назад» контроллера на эмуляцию нажатия на указанную область на сенсорной панели контроллера PS4. - enableCompatibilityCheckBox Показывать данные совместимости:\nПоказывает информацию о совместимости игр в таблице. Включите «Обновлять базу совместимости при запуске» для получения актуальной информации. - checkCompatibilityOnStartupCheckBox Обновлять базу совместимости при запуске:\nАвтоматически обновлять базу данных совместимости при запуске shadPS4. - updateCompatibilityButton Обновить базу совместимости:\nНемедленно обновить базу данных совместимости. - Never Никогда - Idle При бездействии - Always Всегда - Touchpad Left Тачпад слева - Touchpad Right Тачпад справа - Touchpad Center Центр тачпада - None Нет - graphicsAdapterGroupBox Графическое устройство:\nВ системах с несколькими GPU выберите GPU, который будет использовать эмулятор.\nВыберите "Auto Select", чтобы определить его автоматически. - resolutionLayout Ширина/Высота:\nУстановите размер окна эмулятора при запуске, который может быть изменен во время игры.\nЭто отличается от разрешения в игре. - heightDivider Делитель Vblank:\nЧастота кадров, с которой обновляется эмулятор, умножается на это число. Изменение этого параметра может иметь негативные последствия, такие как увеличение скорости игры или нарушение критических функций игры, которые этого не ожидают! - dumpShadersCheckBox Включить дамп шейдеров:\nДля технической отладки сохраняет шейдеры игр в папку во время рендеринга. - nullGpuCheckBox Включить NULL GPU:\nДля технической отладки отключает рендеринг игры так, как будто графической карты нет. - gameFoldersBox Игровые папки:\nСписок папок для проверки установленных игр. - addFolderButton Добавить:\nДобавить папку в список. - removeFolderButton Удалить:\nУдалить папку из списка. - debugDump Включить отладочные дампы:\nСохраняет символы импорта, экспорта и информацию о заголовке файла текущей исполняемой программы PS4 в папку. - vkValidationCheckBox Включить слои валидации Vulkan:\nВключает систему, которая проверяет состояние рендерера Vulkan и логирует информацию о его внутреннем состоянии. Это снизит производительность и, вероятно, изменит поведение эмуляции. - vkSyncValidationCheckBox Включить валидацию синхронизации Vulkan:\nВключает систему, которая проверяет тайминг задач рендеринга Vulkan. Это снизит производительность и, вероятно, изменит поведение эмуляции. - rdocCheckBox Включить отладку RenderDoc:\nЕсли включено, эмулятор обеспечит совместимость с Renderdoc, позволяя захватывать и анализировать текущие кадры во время рендеринга. + + CheatsPatches + + Cheats / Patches for + Читы и патчи для + + + defaultTextEdit_MSG + Читы и патчи экспериментальны.\nИспользуйте с осторожностью.\n\nСкачивайте читы, выбрав репозиторий и нажав на кнопку загрузки.\nВо вкладке "Патчи" вы можете скачать все патчи сразу, выбирать какие вы хотите использовать, и сохранять выбор.\n\nПоскольку мы не разрабатываем читы/патчи,\nпожалуйста сообщайте о проблемах автору чита/патча.\n\nСоздали новый чит? Посетите:\nhttps://github.com/shadps4-emu/ps4_cheats + + + No Image Available + Изображение недоступно + + + Serial: + Серийный номер: + + + Version: + Версия: + + + Size: + Размер: + + + Select Cheat File: + Выберите файл чита: + + + Repository: + Репозиторий: + + + Download Cheats + Скачать читы + + + Delete File + Удалить файл + + + No files selected. + Файлы не выбраны. + + + You can delete the cheats you don't want after downloading them. + Вы можете удалить ненужные читы после их скачивания. + + + Do you want to delete the selected file?\n%1 + Вы хотите удалить выбранный файл?\n%1 + + + Select Patch File: + Выберите файл патча: + + + Download Patches + Скачать патчи + + + Save + Сохранить + + + Cheats + Читы + + + Patches + Патчи + + + Error + Ошибка + + + No patch selected. + Патч не выбран. + + + Unable to open files.json for reading. + Не удалось открыть файл files.json для чтения. + + + No patch file found for the current serial. + Не найден файл патча для текущего серийного номера. + + + Unable to open the file for reading. + Не удалось открыть файл для чтения. + + + Unable to open the file for writing. + Не удалось открыть файл для записи. + + + Failed to parse XML: + Не удалось разобрать XML: + + + Success + Успех + + + Options saved successfully. + Опции успешно сохранены. + + + Invalid Source + Неверный источник + + + The selected source is invalid. + Выбранный источник недействителен. + + + File Exists + Файл существует + + + File already exists. Do you want to replace it? + Файл уже существует. Хотите заменить его? + + + Failed to save file: + Не удалось сохранить файл: + + + Failed to download file: + Не удалось скачать файл: + + + Cheats Not Found + Читы не найдены + + + CheatsNotFound_MSG + Читы не найдены для этой игры в выбранном репозитории. Попробуйте другой репозиторий или другую версию игры. + + + Cheats Downloaded Successfully + Читы успешно скачаны + + + CheatsDownloadedSuccessfully_MSG + Вы успешно скачали читы для этой версии игры из выбранного репозитория. Вы можете попробовать скачать из другого репозитория. Если он доступен, его также можно будет использовать, выбрав файл из списка. + + + Failed to save: + Не удалось сохранить: + + + Failed to download: + Не удалось скачать: + + + Download Complete + Скачивание завершено + + + DownloadComplete_MSG + Патчи успешно скачаны! Все доступные патчи для всех игр были скачаны, нет необходимости скачивать их по отдельности для каждой игры, как это происходит с читами. Если патч не появляется, возможно, его не существует для конкретного серийного номера и версии игры. + + + Failed to parse JSON data from HTML. + Не удалось разобрать данные JSON из HTML. + + + Failed to retrieve HTML page. + Не удалось получить HTML-страницу. + + + The game is in version: %1 + Игра в версии: %1 + + + The downloaded patch only works on version: %1 + Скачанный патч работает только на версии: %1 + + + You may need to update your game. + Вам может понадобиться обновить игру. + + + Incompatibility Notice + Уведомление о несовместимости + + + Failed to open file: + Не удалось открыть файл: + + + XML ERROR: + ОШИБКА XML: + + + Failed to open files.json for writing + Не удалось открыть файл files.json для записи + + + Author: + Автор: + + + Directory does not exist: + Каталог не существует: + + + Failed to open files.json for reading. + Не удалось открыть файл files.json для чтения. + + + Name: + Имя: + + + Can't apply cheats before the game is started + Невозможно применить читы до начала игры + + GameListFrame - Icon Иконка - Name Название - Serial Серийный номер - Compatibility Совместимость - Region Регион - Firmware Прошивка - Size Размер - Version Версия - Path Путь - Play Time Времени в игре - Never Played Вы не играли - h ч - m м - s с - Compatibility is untested Совместимость не проверена - Game does not initialize properly / crashes the emulator Игра не иницализируется правильно / крашит эмулятор - Game boots, but only displays a blank screen Игра запускается, но показывает только пустой экран - Game displays an image but does not go past the menu Игра показывает картинку, но не проходит дальше меню - Game has game-breaking glitches or unplayable performance Игра имеет ломающие игру глюки или плохую производительность - Game can be completed with playable performance and no major glitches Игра может быть пройдена с хорошей производительностью и без серьезных сбоев @@ -1508,127 +1210,102 @@ CheckUpdate - Auto Updater Автообновление - Error Ошибка - Network error: Сетевая ошибка: - Failed to parse update information. Не удалось разобрать информацию об обновлении. - No pre-releases found. Предварительных версий не найдено. - Invalid release data. Недопустимые данные релиза. - No download URL found for the specified asset. Не найден URL для загрузки указанного ресурса. - Your version is already up to date! Ваша версия уже обновлена! - Update Available Доступно обновление - Update Channel Канал обновления - Current Version Текущая версия - Latest Version Последняя версия - Do you want to update? Вы хотите обновиться? - Show Changelog Показать журнал изменений - Check for Updates at Startup Проверка при запуске - Update Обновить - No Нет - Hide Changelog Скрыть журнал изменений - Changes Журнал изменений - Network error occurred while trying to access the URL Произошла сетевая ошибка при попытке доступа к URL - Download Complete Скачивание завершено - The update has been downloaded, press OK to install. Обновление загружено, нажмите OK для установки. - Failed to save the update file at Не удалось сохранить файл обновления в - Starting Update... Начало обновления... - Failed to create the update script file Не удалось создать файл скрипта обновления @@ -1636,29 +1313,24 @@ GameListUtils - B Б - KB КБ - MB МБ - GB ГБ - TB ТБ - + \ No newline at end of file diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index 1df2a40e2..80f3876d4 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -6,22 +6,18 @@ AboutDialog - About shadPS4 Rreth shadPS4 - shadPS4 shadPS4 - shadPS4 is an experimental open-source emulator for the PlayStation 4. shadPS4 është një emulator eksperimental me burim të hapur për PlayStation 4. - This software should not be used to play games you have not legally obtained. Ky program nuk duhet përdorur për të luajtur lojëra që nuk ke marrë ligjërisht. @@ -29,7 +25,6 @@ ElfViewer - Open Folder Hap Dosjen @@ -37,17 +32,14 @@ GameInfoClass - Loading game list, please wait :3 Po ngarkohet lista e lojërave, të lutem prit :3 - Cancel Anulo - Loading... Duke ngarkuar... @@ -55,12 +47,10 @@ InstallDirSelect - shadPS4 - Choose directory shadPS4 - Përzgjidh dosjen - Select which directory you want to install to. Përzgjidh në cilën dosje do që të instalosh. @@ -68,27 +58,22 @@ GameInstallDialog - shadPS4 - Choose directory shadPS4 - Përzgjidh dosjen - Directory to install games Dosja ku do instalohen lojërat - Browse Shfleto - Error Gabim - The value for location to install games is not valid. Vlera për vendndodhjen e instalimit të lojërave nuk është e vlefshme. @@ -96,167 +81,134 @@ GuiContextMenus - Create Shortcut Krijo Shkurtore - Cheats / Patches Mashtrime / Arna - SFO Viewer Shikuesi i SFO - Trophy Viewer Shikuesi i Trofeve - Open Folder... Hap Dosjen... - Open Game Folder Hap Dosjen e Lojës - Open Save Data Folder Hap Dosjen e të Dhënave të Ruajtura - Open Log Folder Hap Dosjen e Ditarit - Copy info... Kopjo informacionin... - Copy Name Kopjo Emrin - Copy Serial Kopjo Serikun - Copy All Kopjo të Gjitha - Delete... Fshi... - Delete Game Fshi lojën - Delete Update Fshi përditësimin - Delete DLC Fshi DLC-në - Compatibility... Compatibility... - Update database Update database - View report View report - Submit a report Submit a report - Shortcut creation Krijimi i shkurtores - Shortcut created successfully! Shkurtorja u krijua me sukses! - Error Gabim - Error creating shortcut! Gabim në krijimin e shkurtores! - Install PKG Instalo PKG - Game Loja - requiresEnableSeparateUpdateFolder_MSG Kjo veçori kërkon cilësimin 'Aktivizo dosjen e ndarë të përditësimit' për të punuar. Në qoftë se do ta përdorësh këtë veçori, të lutem aktivizoje. - This game has no update to delete! Kjo lojë nuk ka përditësim për të fshirë! - - + Update Përditësim - This game has no DLC to delete! Kjo lojë nuk ka DLC për të fshirë! - DLC DLC - Delete %1 Fshi %1 - Are you sure you want to delete %1's %2 directory? Je i sigurt që do të fsish dosjen %2 të %1? @@ -264,205 +216,285 @@ MainWindow - Open/Add Elf Folder Hap/Shto Dosje ELF - Install Packages (PKG) Instalo Paketat (PKG) - Boot Game Nis Lojën - Check for Updates Kontrollo për përditësime - About shadPS4 Rreth shadPS4 - Configure... Konfiguro... - Install application from a .pkg file Instalo aplikacionin nga një skedar .pkg - Recent Games Lojërat e fundit - Exit Dil - Exit shadPS4 Dil nga shadPS4 - Exit the application. Dil nga aplikacioni. - Show Game List Shfaq Listën e Lojërave - Game List Refresh Rifresko Listën e Lojërave - Tiny Të vockla - Small Të vogla - Medium Të mesme - Large Të mëdha - List View Pamja e Listës - Grid View Pamja e Rrjetës - Elf Viewer Shikuesi i Elf - Game Install Directory Dosja e Instalimit të Lojës - Download Cheats/Patches Shkarko Mashtrime/Arna - Dump Game List Zbraz Listën e Lojërave - PKG Viewer Shikuesi i PKG - Search... Kërko... - File Skedari - View Pamja - Game List Icons Ikonat e Listës së Lojërave - Game List Mode Mënyra e Listës së Lojërave - Settings Cilësimet - Utils Shërbimet - Themes Motivet - Help Ndihmë - Dark E errët - Light E çelët - Green E gjelbër - Blue E kaltër - Violet Vjollcë - toolBar Shiriti i veglave + + Game List + Lista e lojërave + + + * Unsupported Vulkan Version + * Version i pambështetur i Vulkan + + + Download Cheats For All Installed Games + Shkarko Mashtrime Për Të Gjitha Lojërat e Instaluara + + + Download Patches For All Games + Shkarko Arna Për Të Gjitha Lojërat e Instaluara + + + Download Complete + Shkarkimi Përfundoi + + + You have downloaded cheats for all the games you have installed. + Ke shkarkuar mashtrimet për të gjitha lojërat që ke instaluar. + + + Patches Downloaded Successfully! + Arnat u shkarkuan me sukses! + + + All Patches available for all games have been downloaded. + Të gjitha arnat e ofruara për të gjitha lojërat janë shkarkuar. + + + Games: + Lojërat: + + + PKG File (*.PKG) + Skedar PKG (*.PKG) + + + ELF files (*.bin *.elf *.oelf) + Skedarë ELF (*.bin *.elf *.oelf) + + + Game Boot + Nis Lojën + + + Only one file can be selected! + Mund të përzgjidhet vetëm një skedar! + + + PKG Extraction + Nxjerrja e PKG-së + + + Patch detected! + U zbulua një arnë! + + + PKG and Game versions match: + PKG-ja dhe versioni i Lojës përputhen: + + + Would you like to overwrite? + Dëshiron të mbishkruash? + + + PKG Version %1 is older than installed version: + Versioni %1 i PKG-së është më i vjetër se versioni i instaluar: + + + Game is installed: + Loja është instaluar: + + + Would you like to install Patch: + Dëshiron të instalosh Arnën: + + + DLC Installation + Instalimi i DLC-ve + + + Would you like to install DLC: %1? + Dëshiron të instalosh DLC-në: %1? + + + DLC already installed: + DLC-ja është instaluar tashmë: + + + Game already installed + Loja është instaluar tashmë + + + PKG is a patch, please install the game first! + PKG-ja është një arnë, të lutem instalo lojën fillimisht! + + + PKG ERROR + GABIM PKG + + + Extracting PKG %1/%2 + Po nxirret PKG-ja %1/%2 + + + Extraction Finished + Nxjerrja Përfundoi + + + Game successfully installed at %1 + Loja u instalua me sukses në %1 + + + File doesn't appear to be a valid PKG file + Skedari nuk duket si skedar PKG i vlefshëm + PKGViewer - Open Folder Hap Dosjen @@ -470,7 +502,6 @@ TrophyViewer - Trophy Viewer Shikuesi i Trofeve @@ -478,1029 +509,700 @@ SettingsDialog - Settings Cilësimet - General Të përgjithshme - System Sistemi - Console Language Gjuha e Konsolës - Emulator Language Gjuha e emulatorit - Emulator Emulatori - Enable Fullscreen Aktivizo Ekranin e plotë - Enable Separate Update Folder Aktivizo dosjen e ndarë të përditësimit - Show Splash Shfaq Pamjen e nisjes - Is PS4 Pro Mënyra PS4 Pro - Enable Discord Rich Presence Aktivizo Discord Rich Presence - Username Përdoruesi - Trophy Key Trophy Key - Trophy Trophy - Logger Regjistruesi i ditarit - Log Type Lloji i Ditarit - Log Filter Filtri i Ditarit - Input Hyrja - Cursor Kursori - Hide Cursor Fshih kursorin - Hide Cursor Idle Timeout Koha për fshehjen e kursorit joaktiv - s s - Controller Dorezë - Back Button Behavior Sjellja e butonit mbrapa - Graphics Grafika - Graphics Device Pajisja e Grafikës - Width Gjerësia - Height Lartësia - Vblank Divider Ndarës Vblank - Advanced Të përparuara - Enable Shaders Dumping Aktivizo Zbrazjen e Shaders-ave - Enable NULL GPU Aktivizo GPU-në NULL - Paths Shtigjet - Game Folders Dosjet e lojës - Add... Shto... - Remove Hiq - Debug Korrigjim - Enable Debug Dumping Aktivizo Zbrazjen për Korrigjim - Enable Vulkan Validation Layers Aktivizo Shtresat e Vlefshmërisë Vulkan - Enable Vulkan Synchronization Validation Aktivizo Vërtetimin e Sinkronizimit Vulkan - Enable RenderDoc Debugging Aktivizo Korrigjimin RenderDoc - Update Përditëso - Check for Updates at Startup Kontrollo për përditësime në nisje - Update Channel Kanali i përditësimit - Check for Updates Kontrollo për përditësime - GUI Settings Cilësimet e GUI - Disable Trophy Pop-ups Çaktivizo njoftimet për Trofetë - Play title music Luaj muzikën e titullit - Update Compatibility Database On Startup Përditëso bazën e të dhënave të përputhshmërisë gjatë nisjes - Game Compatibility Përputhshmëria e lojës - Display Compatibility Data Shfaq të dhënat e përputhshmërisë - Update Compatibility Database Përditëso bazën e të dhënave të përputhshmërisë - Volume Vëllimi i zërit - Audio Backend Audio Backend - - - MainWindow - - Game List - Lista e lojërave - - - - * Unsupported Vulkan Version - * Version i pambështetur i Vulkan - - - - Download Cheats For All Installed Games - Shkarko Mashtrime Për Të Gjitha Lojërat e Instaluara - - - - Download Patches For All Games - Shkarko Arna Për Të Gjitha Lojërat e Instaluara - - - - Download Complete - Shkarkimi Përfundoi - - - - You have downloaded cheats for all the games you have installed. - Ke shkarkuar mashtrimet për të gjitha lojërat që ke instaluar. - - - - Patches Downloaded Successfully! - Arnat u shkarkuan me sukses! - - - - All Patches available for all games have been downloaded. - Të gjitha arnat e ofruara për të gjitha lojërat janë shkarkuar. - - - - Games: - Lojërat: - - - - PKG File (*.PKG) - Skedar PKG (*.PKG) - - - - ELF files (*.bin *.elf *.oelf) - Skedarë ELF (*.bin *.elf *.oelf) - - - - Game Boot - Nis Lojën - - - - Only one file can be selected! - Mund të përzgjidhet vetëm një skedar! - - - - PKG Extraction - Nxjerrja e PKG-së - - - - Patch detected! - U zbulua një arnë! - - - - PKG and Game versions match: - PKG-ja dhe versioni i Lojës përputhen: - - - - Would you like to overwrite? - Dëshiron të mbishkruash? - - - - PKG Version %1 is older than installed version: - Versioni %1 i PKG-së është më i vjetër se versioni i instaluar: - - - - Game is installed: - Loja është instaluar: - - - - Would you like to install Patch: - Dëshiron të instalosh Arnën: - - - - DLC Installation - Instalimi i DLC-ve - - - - Would you like to install DLC: %1? - Dëshiron të instalosh DLC-në: %1? - - - - DLC already installed: - DLC-ja është instaluar tashmë: - - - - Game already installed - Loja është instaluar tashmë - - - - PKG is a patch, please install the game first! - PKG-ja është një arnë, të lutem instalo lojën fillimisht! - - - - PKG ERROR - GABIM PKG - - - - Extracting PKG %1/%2 - Po nxirret PKG-ja %1/%2 - - - - Extraction Finished - Nxjerrja Përfundoi - - - - Game successfully installed at %1 - Loja u instalua me sukses në %1 - - - - File doesn't appear to be a valid PKG file - Skedari nuk duket si skedar PKG i vlefshëm - - - - CheatsPatches - - - Cheats / Patches for - Mashtrime / Arna për - - - - defaultTextEdit_MSG - Mashtrimet/Arnat janë eksperimentale.\nPërdori me kujdes.\n\nShkarko mashtrimet individualisht duke zgjedhur depon dhe duke klikuar butonin e shkarkimit.\nNë skedën Arna, mund t'i shkarkosh të gjitha arnat menjëherë, të zgjidhësh cilat dëshiron të përdorësh dhe të ruash zgjedhjen tënde.\n\nMeqenëse ne nuk zhvillojmë Mashtrimet/Arnat,\ntë lutem raporto problemet te autori i mashtrimit.\n\nKe krijuar një mashtrim të ri? Vizito:\nhttps://github.com/shadps4-emu/ps4_cheats - - - - No Image Available - Nuk ofrohet asnjë imazh - - - - Serial: - Seriku: - - - - Version: - Versioni: - - - - Size: - Madhësia: - - - - Select Cheat File: - Përzgjidh Skedarin e Mashtrimit: - - - - Repository: - Depo: - - - - Download Cheats - Shkarko Mashtrimet - - - - Delete File - Fshi Skedarin - - - - No files selected. - Nuk u zgjodh asnjë skedar. - - - - You can delete the cheats you don't want after downloading them. - Mund t'i fshish mashtrimet që nuk dëshiron pasi t'i kesh shkarkuar. - - - - Do you want to delete the selected file?\n%1 - Dëshiron të fshish skedarin e përzgjedhur?\n%1 - - - - Select Patch File: - Përzgjidh Skedarin e Arnës: - - - - Download Patches - Shkarko Arnat - - - Save Ruaj - - Cheats - Mashtrime - - - - Patches - Arna - - - - Error - Gabim - - - - No patch selected. - Asnjë arnë e përzgjedhur. - - - - Unable to open files.json for reading. - files.json nuk mund të hapet për lexim. - - - - No patch file found for the current serial. - Nuk u gjet asnjë skedar patch për serikun aktual. - - - - Unable to open the file for reading. - Skedari nuk mund të hapet për lexim. - - - - Unable to open the file for writing. - Skedari nuk mund të hapet për shkrim. - - - - Failed to parse XML: - Analiza e XML-së dështoi: - - - - Success - Sukses - - - - Options saved successfully. - Rregullimet u ruajtën me sukses. - - - - Invalid Source - Burim i pavlefshëm - - - - The selected source is invalid. - Burimi i përzgjedhur është i pavlefshëm. - - - - File Exists - Skedari Ekziston - - - - File already exists. Do you want to replace it? - Skedari ekziston tashmë. Dëshiron ta zëvendësosh? - - - - Failed to save file: - Ruajtja e skedarit dështoi: - - - - Failed to download file: - Shkarkimi i skedarit dështoi: - - - - Cheats Not Found - Mashtrimet nuk u gjetën - - - - CheatsNotFound_MSG - Nuk u gjetën mashtrime për këtë lojë në këtë version të depove të përzgjedhura, provo një depo tjetër ose një version tjetër të lojës. - - - - Cheats Downloaded Successfully - Mashtrimet u shkarkuan me sukses - - - - CheatsDownloadedSuccessfully_MSG - Ke shkarkuar me sukses mashtrimet për këtë version të lojës nga depoja e përzgjedhur. Mund të provosh të shkarkosh nga një depo tjetër, nëse ofrohet do të jetë e mundur gjithashtu ta përdorësh duke përzgjedhur skedarin nga lista. - - - - Failed to save: - Ruajtja dështoi: - - - - Failed to download: - Shkarkimi dështoi: - - - - Download Complete - Shkarkimi përfundoi - - - - DownloadComplete_MSG - Arnat u shkarkuan me sukses! Të gjitha arnat e ofruara për të gjitha lojërat janë shkarkuar, nuk ka nevojë t'i shkarkosh ato individualisht për secilën lojë siç ndodh me Mashtrimet. Nëse arna nuk shfaqet, mund të mos ekzistojë për numrin e serikut dhe versionin specifik të lojës. - - - - Failed to parse JSON data from HTML. - Analiza e të dhënave JSON nga HTML dështoi. - - - - Failed to retrieve HTML page. - Gjetja e faqes HTML dështoi. - - - - The game is in version: %1 - Loja është në versionin: %1 - - - - The downloaded patch only works on version: %1 - Arna e shkarkuar funksionon vetëm në versionin: %1 - - - - You may need to update your game. - Mund të duhet të përditësosh lojën tënde. - - - - Incompatibility Notice - Njoftim për papajtueshmëri - - - - Failed to open file: - Hapja e skedarit dështoi: - - - - XML ERROR: - GABIM XML: - - - - Failed to open files.json for writing - Hapja e files.json për shkrim dështoi - - - - Author: - Autori: - - - - Directory does not exist: - Dosja nuk ekziston: - - - - Failed to open files.json for reading. - Hapja e files.json për lexim dështoi. - - - - Name: - Emri: - - - - Can't apply cheats before the game is started - Nuk mund të zbatohen mashtrime para fillimit të lojës. - - - - SettingsDialog - - - Save - Ruaj - - - Apply Zbato - Restore Defaults Rikthe paracaktimet - Close Mbyll - Point your mouse at an option to display its description. Vendos miun mbi një rregullim për të shfaqur përshkrimin e tij. - consoleLanguageGroupBox Gjuha e konsolës:\nPërcakton gjuhën që përdor loja PS4.\nKëshillohet të caktosh një gjuhë që loja mbështet, e cila do të ndryshojë sipas rajonit. - emulatorLanguageGroupBox Gjuha e emulatorit:\nPërcakton gjuhën e ndërfaqes së përdoruesit të emulatorit. - fullscreenCheckBox Aktivizo ekranin e plotë:\nVendos automatikisht dritaren e lojës në mënyrën e ekranit të plotë.\nKjo mund të aktivizohet duke shtypur tastin F11. - separateUpdatesCheckBox Aktivizo dosjen e ndarë të përditësimit:\nAktivizon instalimin e përditësimeve të lojërave në dosje të veçanta për menaxhim më të lehtë.\nKjo mund të krijohet manualisht duke shtuar përditësimin e shpaketuar në dosjen e lojës me emrin "CUSA00000-UPDATE" ku ID-ja CUSA përputhet me ID-në e lojës. - showSplashCheckBox Shfaq ekranin e ngarkesës:\nShfaq ekranin e ngarkesës së lojës (një pamje e veçantë) gjatë fillimit të lojës. - ps4proCheckBox Është PS4 Pro:\nBën që emulatori të veprojë si një PS4 PRO, gjë që mund të aktivizojë veçori të veçanta në lojrat që e mbështesin. - discordRPCCheckbox Aktivizo Discord Rich Presence:\nShfaq ikonën e emulatorit dhe informacionin përkatës në profilin tënd në Discord. - userName Përdoruesi:\nPërcakton emrin e përdoruesit të llogarisë PS4, i cili mund të shfaqet nga disa lojra. - TrophyKey Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. - logTypeGroupBox Lloji i ditarit:\nPërcakton nëse të sinkronizohet dalja e dritares së ditarit për performancë. Mund të ketë efekte të këqija në emulim. - logFilter Filtri i ditarit:\nFiltron ditarin për të shfaqur vetëm informacione specifike.\nShembuj: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Nivelet: Trace, Debug, Info, Warning, Error, Critical - në këtë rend, një nivel specifik hesht të gjitha nivelet përpara në listë dhe regjistron çdo nivel pas atij. - updaterGroupBox Përditësimi:\nRelease: Versionet zyrtare të lëshuara çdo muaj që mund të jenë shumë të vjetra, por janë më të besueshme dhe të provuara.\nNightly: Versionet e zhvillimit që kanë të gjitha veçoritë dhe rregullimet më të fundit, por mund të përmbajnë gabime dhe janë më pak të qëndrueshme. - GUIgroupBox Luaj muzikën e titullit:\nNëse një lojë e mbështet, aktivizohet luajtja e muzikës të veçantë kur të zgjidhësh lojën në GUI. - disableTrophycheckBox Çaktivizo njoftimet për Trofetë:\nÇaktivizo njoftimet për trofetë gjatë lojës. Përparimi i trofeve mund të ndiqet duke përdorur Shikuesin e Trofeve (kliko me të djathtën mbi lojën në dritaren kryesore). - hideCursorGroupBox Fsheh kursorin:\nZgjidh kur do të fshihet kursori:\nKurrë: Do ta shohësh gjithmonë miun.\nInaktiv: Vendos një kohë për ta fshehur pas mosveprimit.\nGjithmonë: nuk do ta shohësh kurrë miun. - idleTimeoutGroupBox Koha për fshehjen e kursorit joaktiv:\nKohëzgjatja (në sekonda) pas së cilës kursori që nuk ka qënë në veprim fshihet. - backButtonBehaviorGroupBox Sjellja e butonit mbrapa:\nLejon të përcaktohet se në cilën pjesë të tastierës prekëse do të imitojë një prekje butoni mprapa. - enableCompatibilityCheckBox Shfaq të dhënat e përputhshmërisë:\nShfaq informacionin e përputhshmërisë së lojës në formë tabele. Aktivizo 'Përditëso përputhshmërinë gjatë nisjes' për të marrë informacion të përditësuar. - checkCompatibilityOnStartupCheckBox Përditëso përputhshmërinë gjatë nisjes:\nPërditëson automatikisht bazën e të dhënave të përputhshmërisë kur shadPS4 niset. - updateCompatibilityButton Përditëso bazën e të dhënave të përputhshmërisë:\nPërditëso menjëherë bazën e të dhënave të përputhshmërisë. - Never Kurrë - Idle Joaktiv - Always Gjithmonë - Touchpad Left Tastiera prekëse majtas - Touchpad Right Tastiera prekëse djathtas - Touchpad Center Tastiera prekëse në qendër - None Asnjë - graphicsAdapterGroupBox Pajisja grafike:\nNë sistemet me GPU të shumëfishta, zgjidh GPU-në që do të përdorë emulatori nga lista rënëse,\nose zgjidh "Auto Select" për ta përcaktuar automatikisht. - resolutionLayout Gjerësia/Lartësia:\nPërcakton madhësinë e dritares së emulatorit në nisje, e cila mund të rregullohet gjatë lojës.\nKjo është ndryshe nga rezolucioni në lojë. - heightDivider Ndarësi Vblank:\nFrekuenca pamore me të cilën rifreskohet emulatori shumëzohet me këtë numër. Ndryshimi i këtij mund të ketë efekte të këqija, si rritja e shpejtësisë së lojës ose prishja e punimit thelbësor të lojës që nuk e pret këtë ndryshim! - dumpShadersCheckBox Aktivizo zbrazjen e shaders-ave:\nPër qëllime të korrigjimit teknik, ruan shaders-at e lojës në një dosje ndërsa ato pasqyrohen. - nullGpuCheckBox Aktivizo GPU-në Null:\nPër qëllime të korrigjimit teknik, çaktivizon pasqyrimin e lojës sikur nuk ka një kartë grafike. - gameFoldersBox Dosjet e lojërave:\nLista e dosjeve për të kontrolluar lojërat e instaluara. - addFolderButton Shto:\nShto një dosje në listë. - removeFolderButton Hiq:\nHiq një dosje nga lista. - debugDump Aktivizo zbrazjen për korrigjim:\nRuan simbolet e importit dhe eksportit dhe informacionin e kreut të skedarit për aplikacionin PS4 që po ekzekutohet në një dosje. - vkValidationCheckBox Aktivizo shtresat e vlefshmërisë Vulkan:\nAktivizon një sistem që vërteton gjendjen e pasqyruesit Vulkan dhe regjistron informacionin në lidhje me gjendjen e tij të brendshme. Kjo do të ul performancën dhe ndoshta do të ndryshojë sjelljen e emulimit. - vkSyncValidationCheckBox Aktivizo vërtetimin e sinkronizimit Vulkan:\nAktivizon një sistem që vërteton kohën e detyrave të pasqyrimit Vulkan. Kjo do të ul performancën dhe ndoshta do të ndryshojë sjelljen e emulimit. - rdocCheckBox Aktivizo korrigjimin RenderDoc:\nNëse aktivizohet, emulatori do të ofrojë pajtueshmëri me Renderdoc për të lejuar kapjen dhe analizën e pamjes të pasqyruar në moment. + + CheatsPatches + + Cheats / Patches for + Mashtrime / Arna për + + + defaultTextEdit_MSG + Mashtrimet/Arnat janë eksperimentale.\nPërdori me kujdes.\n\nShkarko mashtrimet individualisht duke zgjedhur depon dhe duke klikuar butonin e shkarkimit.\nNë skedën Arna, mund t'i shkarkosh të gjitha arnat menjëherë, të zgjidhësh cilat dëshiron të përdorësh dhe të ruash zgjedhjen tënde.\n\nMeqenëse ne nuk zhvillojmë Mashtrimet/Arnat,\ntë lutem raporto problemet te autori i mashtrimit.\n\nKe krijuar një mashtrim të ri? Vizito:\nhttps://github.com/shadps4-emu/ps4_cheats + + + No Image Available + Nuk ofrohet asnjë imazh + + + Serial: + Seriku: + + + Version: + Versioni: + + + Size: + Madhësia: + + + Select Cheat File: + Përzgjidh Skedarin e Mashtrimit: + + + Repository: + Depo: + + + Download Cheats + Shkarko Mashtrimet + + + Delete File + Fshi Skedarin + + + No files selected. + Nuk u zgjodh asnjë skedar. + + + You can delete the cheats you don't want after downloading them. + Mund t'i fshish mashtrimet që nuk dëshiron pasi t'i kesh shkarkuar. + + + Do you want to delete the selected file?\n%1 + Dëshiron të fshish skedarin e përzgjedhur?\n%1 + + + Select Patch File: + Përzgjidh Skedarin e Arnës: + + + Download Patches + Shkarko Arnat + + + Save + Ruaj + + + Cheats + Mashtrime + + + Patches + Arna + + + Error + Gabim + + + No patch selected. + Asnjë arnë e përzgjedhur. + + + Unable to open files.json for reading. + files.json nuk mund të hapet për lexim. + + + No patch file found for the current serial. + Nuk u gjet asnjë skedar patch për serikun aktual. + + + Unable to open the file for reading. + Skedari nuk mund të hapet për lexim. + + + Unable to open the file for writing. + Skedari nuk mund të hapet për shkrim. + + + Failed to parse XML: + Analiza e XML-së dështoi: + + + Success + Sukses + + + Options saved successfully. + Rregullimet u ruajtën me sukses. + + + Invalid Source + Burim i pavlefshëm + + + The selected source is invalid. + Burimi i përzgjedhur është i pavlefshëm. + + + File Exists + Skedari Ekziston + + + File already exists. Do you want to replace it? + Skedari ekziston tashmë. Dëshiron ta zëvendësosh? + + + Failed to save file: + Ruajtja e skedarit dështoi: + + + Failed to download file: + Shkarkimi i skedarit dështoi: + + + Cheats Not Found + Mashtrimet nuk u gjetën + + + CheatsNotFound_MSG + Nuk u gjetën mashtrime për këtë lojë në këtë version të depove të përzgjedhura, provo një depo tjetër ose një version tjetër të lojës. + + + Cheats Downloaded Successfully + Mashtrimet u shkarkuan me sukses + + + CheatsDownloadedSuccessfully_MSG + Ke shkarkuar me sukses mashtrimet për këtë version të lojës nga depoja e përzgjedhur. Mund të provosh të shkarkosh nga një depo tjetër, nëse ofrohet do të jetë e mundur gjithashtu ta përdorësh duke përzgjedhur skedarin nga lista. + + + Failed to save: + Ruajtja dështoi: + + + Failed to download: + Shkarkimi dështoi: + + + Download Complete + Shkarkimi përfundoi + + + DownloadComplete_MSG + Arnat u shkarkuan me sukses! Të gjitha arnat e ofruara për të gjitha lojërat janë shkarkuar, nuk ka nevojë t'i shkarkosh ato individualisht për secilën lojë siç ndodh me Mashtrimet. Nëse arna nuk shfaqet, mund të mos ekzistojë për numrin e serikut dhe versionin specifik të lojës. + + + Failed to parse JSON data from HTML. + Analiza e të dhënave JSON nga HTML dështoi. + + + Failed to retrieve HTML page. + Gjetja e faqes HTML dështoi. + + + The game is in version: %1 + Loja është në versionin: %1 + + + The downloaded patch only works on version: %1 + Arna e shkarkuar funksionon vetëm në versionin: %1 + + + You may need to update your game. + Mund të duhet të përditësosh lojën tënde. + + + Incompatibility Notice + Njoftim për papajtueshmëri + + + Failed to open file: + Hapja e skedarit dështoi: + + + XML ERROR: + GABIM XML: + + + Failed to open files.json for writing + Hapja e files.json për shkrim dështoi + + + Author: + Autori: + + + Directory does not exist: + Dosja nuk ekziston: + + + Failed to open files.json for reading. + Hapja e files.json për lexim dështoi. + + + Name: + Emri: + + + Can't apply cheats before the game is started + Nuk mund të zbatohen mashtrime para fillimit të lojës. + + GameListFrame - Icon Ikona - Name Emri - Serial Seriku - Compatibility Përputhshmëria - Region Rajoni - Firmware Firmueri - Size Madhësia - Version Versioni - Path Shtegu - Play Time Koha e luajtjes - Never Played Nuk është luajtur kurrë - h h - m m - s s - Compatibility is untested Përputhshmëria nuk është e testuar - Game does not initialize properly / crashes the emulator Loja nuk niset siç duhet / rrëzon emulatorin - Game boots, but only displays a blank screen Loja niset, por shfaq vetëm një ekran të zbrazët - Game displays an image but does not go past the menu Loja shfaq një imazh, por nuk kalon përtej menysë - Game has game-breaking glitches or unplayable performance Loja ka probleme kritike ose performancë të papërshtatshme për lojë - Game can be completed with playable performance and no major glitches Loja mund të përfundohet me performancë të luajtshme dhe pa probleme të mëdha @@ -1508,127 +1210,102 @@ CheckUpdate - Auto Updater Përditësues automatik - Error Gabim - Network error: Gabim rrjeti: - Failed to parse update information. Analizimi i informacionit të përditësimit deshtoi. - No pre-releases found. Nuk u gjetën botime paraprake. - Invalid release data. Të dhënat e lëshimit janë të pavlefshme. - No download URL found for the specified asset. Nuk u gjet URL-ja e shkarkimit për burimin e specifikuar. - Your version is already up to date! Versioni jotë është i përditësuar tashmë! - Update Available Ofrohet një përditësim - Update Channel Kanali i përditësimit - Current Version Versioni i tanishëm - Latest Version Versioni më i fundit - Do you want to update? Do të përditësosh? - Show Changelog Trego ndryshimet - Check for Updates at Startup Kontrollo për përditësime në nisje - Update Përditëso - No Jo - Hide Changelog Fshih ndryshimet - Changes Ndryshimet - Network error occurred while trying to access the URL Ka ndodhur një gabim rrjeti gjatë përpjekjes për të qasur në URL-në - Download Complete Shkarkimi përfundoi - The update has been downloaded, press OK to install. Përditësimi është shkarkuar, shtyp OK për ta instaluar. - Failed to save the update file at Dështoi ruajtja e skedarit të përditësimit në - Starting Update... Po fillon përditësimi... - Failed to create the update script file Krijimi i skedarit skript të përditësimit dështoi @@ -1636,29 +1313,24 @@ GameListUtils - B B - KB KB - MB MB - GB GB - TB TB - + - + \ No newline at end of file diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index a03a48660..48f291e99 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -6,22 +6,18 @@ AboutDialog - About shadPS4 shadPS4 Hakkında - shadPS4 shadPS4 - shadPS4 is an experimental open-source emulator for the PlayStation 4. shadPS4, PlayStation 4 için deneysel bir açık kaynak kodlu emülatördür. - This software should not be used to play games you have not legally obtained. Bu yazılım, yasal olarak edinmediğiniz oyunları oynamak için kullanılmamalıdır. @@ -29,7 +25,6 @@ ElfViewer - Open Folder Klasörü Aç @@ -37,17 +32,14 @@ GameInfoClass - Loading game list, please wait :3 Oyun listesi yükleniyor, lütfen bekleyin :3 - Cancel İptal - Loading... Yükleniyor... @@ -55,12 +47,10 @@ InstallDirSelect - shadPS4 - Choose directory shadPS4 - Klasörü Seç - Select which directory you want to install to. Select which directory you want to install to. @@ -68,27 +58,22 @@ GameInstallDialog - shadPS4 - Choose directory shadPS4 - Klasörü Seç - Directory to install games Oyunların yükleneceği klasör - Browse Gözat - Error Hata - The value for location to install games is not valid. Oyunların yükleneceği konum için girilen klasör geçerli değil. @@ -96,167 +81,134 @@ GuiContextMenus - Create Shortcut Kısayol Oluştur - Cheats / Patches Hileler / Yamanlar - SFO Viewer SFO Görüntüleyici - Trophy Viewer Kupa Görüntüleyici - Open Folder... Klasörü Aç... - Open Game Folder Oyun Klasörünü Aç - Open Save Data Folder Kaydetme Verileri Klasörünü Aç - Open Log Folder Log Klasörünü Aç - Copy info... Bilgiyi Kopyala... - Copy Name Adı Kopyala - Copy Serial Seri Numarasını Kopyala - Copy All Tümünü Kopyala - Delete... Delete... - Delete Game Delete Game - Delete Update Delete Update - Delete DLC Delete DLC - Compatibility... Compatibility... - Update database Update database - View report View report - Submit a report Submit a report - Shortcut creation Kısayol oluşturma - Shortcut created successfully! Kısayol başarıyla oluşturuldu! - Error Hata - Error creating shortcut! Kısayol oluşturulurken hata oluştu! - Install PKG PKG Yükle - Game Game - requiresEnableSeparateUpdateFolder_MSG This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. - This game has no update to delete! This game has no update to delete! - - + Update Update - This game has no DLC to delete! This game has no DLC to delete! - DLC DLC - Delete %1 Delete %1 - Are you sure you want to delete %1's %2 directory? Are you sure you want to delete %1's %2 directory? @@ -264,205 +216,285 @@ MainWindow - Open/Add Elf Folder Elf Klasörünü Aç/Ekle - Install Packages (PKG) Paketleri Kur (PKG) - Boot Game Oyunu Başlat - Check for Updates Güncellemeleri kontrol et - About shadPS4 shadPS4 Hakkında - Configure... Yapılandır... - Install application from a .pkg file .pkg dosyasından uygulama yükle - Recent Games Son Oyunlar - Exit Çıkış - Exit shadPS4 shadPS4'ten Çık - Exit the application. Uygulamadan çık. - Show Game List Oyun Listesini Göster - Game List Refresh Oyun Listesini Yenile - Tiny Küçük - Small Ufak - Medium Orta - Large Büyük - List View Liste Görünümü - Grid View Izgara Görünümü - Elf Viewer Elf Görüntüleyici - Game Install Directory Oyun Kurulum Klasörü - Download Cheats/Patches Hileleri/Yamaları İndir - Dump Game List Oyun Listesini Kaydet - PKG Viewer PKG Görüntüleyici - Search... Ara... - File Dosya - View Görünüm - Game List Icons Oyun Listesi Simgeleri - Game List Mode Oyun Listesi Modu - Settings Ayarlar - Utils Yardımcı Araçlar - Themes Temalar - Help Yardım - Dark Koyu - Light Açık - Green Yeşil - Blue Mavi - Violet Mor - toolBar Araç Çubuğu + + Game List + Oyun Listesi + + + * Unsupported Vulkan Version + * Desteklenmeyen Vulkan Sürümü + + + Download Cheats For All Installed Games + Tüm Yüklenmiş Oyunlar İçin Hileleri İndir + + + Download Patches For All Games + Tüm Oyunlar İçin Yamaları İndir + + + Download Complete + İndirme Tamamlandı + + + You have downloaded cheats for all the games you have installed. + Yüklediğiniz tüm oyunlar için hileleri indirdiniz. + + + Patches Downloaded Successfully! + Yamalar Başarıyla İndirildi! + + + All Patches available for all games have been downloaded. + Tüm oyunlar için mevcut tüm yamalar indirildi. + + + Games: + Oyunlar: + + + PKG File (*.PKG) + PKG Dosyası (*.PKG) + + + ELF files (*.bin *.elf *.oelf) + ELF Dosyaları (*.bin *.elf *.oelf) + + + Game Boot + Oyun Başlatma + + + Only one file can be selected! + Sadece bir dosya seçilebilir! + + + PKG Extraction + PKG Çıkartma + + + Patch detected! + Yama tespit edildi! + + + PKG and Game versions match: + PKG ve oyun sürümleri uyumlu: + + + Would you like to overwrite? + Üzerine yazmak ister misiniz? + + + PKG Version %1 is older than installed version: + PKG Sürümü %1, kurulu sürümden daha eski: + + + Game is installed: + Oyun yüklendi: + + + Would you like to install Patch: + Yamanın yüklenmesini ister misiniz: + + + DLC Installation + DLC Yükleme + + + Would you like to install DLC: %1? + DLC'yi yüklemek ister misiniz: %1? + + + DLC already installed: + DLC zaten yüklü: + + + Game already installed + Oyun zaten yüklü + + + PKG is a patch, please install the game first! + PKG bir yama, lütfen önce oyunu yükleyin! + + + PKG ERROR + PKG HATASI + + + Extracting PKG %1/%2 + PKG Çıkarılıyor %1/%2 + + + Extraction Finished + Çıkarma Tamamlandı + + + Game successfully installed at %1 + Oyun başarıyla %1 konumuna yüklendi + + + File doesn't appear to be a valid PKG file + Dosya geçerli bir PKG dosyası gibi görünmüyor + PKGViewer - Open Folder Klasörü Aç @@ -470,7 +502,6 @@ TrophyViewer - Trophy Viewer Kupa Görüntüleyici @@ -478,1029 +509,700 @@ SettingsDialog - Settings Ayarlar - General Genel - System Sistem - Console Language Konsol Dili - Emulator Language Emülatör Dili - Emulator Emülatör - Enable Fullscreen Tam Ekranı Etkinleştir - Enable Separate Update Folder Enable Separate Update Folder - Show Splash Başlangıç Ekranını Göster - Is PS4 Pro PS4 Pro - Enable Discord Rich Presence Discord Rich Presence'i etkinleştir - Username Kullanıcı Adı - Trophy Key Trophy Key - Trophy Trophy - Logger Kayıt Tutucu - Log Type Kayıt Türü - Log Filter Kayıt Filtresi - Input Girdi - Cursor İmleç - Hide Cursor İmleci Gizle - Hide Cursor Idle Timeout İmleç İçin Hareketsizlik Zaman Aşımı - s s - Controller Kontrolcü - Back Button Behavior Geri Dön Butonu Davranışı - Graphics Grafikler - Graphics Device Grafik Cihazı - Width Genişlik - Height Yükseklik - Vblank Divider Vblank Bölücü - Advanced Gelişmiş - Enable Shaders Dumping Shader Kaydını Etkinleştir - Enable NULL GPU NULL GPU'yu Etkinleştir - Paths Yollar - Game Folders Oyun Klasörleri - Add... Ekle... - Remove Kaldır - Debug Hata Ayıklama - Enable Debug Dumping Hata Ayıklama Dökümü Etkinleştir - Enable Vulkan Validation Layers Vulkan Doğrulama Katmanlarını Etkinleştir - Enable Vulkan Synchronization Validation Vulkan Senkronizasyon Doğrulamasını Etkinleştir - Enable RenderDoc Debugging RenderDoc Hata Ayıklamayı Etkinleştir - Update Güncelle - Check for Updates at Startup Başlangıçta güncellemeleri kontrol et - Update Channel Güncelleme Kanalı - Check for Updates Güncellemeleri Kontrol Et - GUI Settings GUI Ayarları - Disable Trophy Pop-ups Disable Trophy Pop-ups - Play title music Başlık müziğini çal - Update Compatibility Database On Startup Update Compatibility Database On Startup - Game Compatibility Game Compatibility - Display Compatibility Data Display Compatibility Data - Update Compatibility Database Update Compatibility Database - Volume Ses seviyesi - Audio Backend Audio Backend - - - MainWindow - - Game List - Oyun Listesi - - - - * Unsupported Vulkan Version - * Desteklenmeyen Vulkan Sürümü - - - - Download Cheats For All Installed Games - Tüm Yüklenmiş Oyunlar İçin Hileleri İndir - - - - Download Patches For All Games - Tüm Oyunlar İçin Yamaları İndir - - - - Download Complete - İndirme Tamamlandı - - - - You have downloaded cheats for all the games you have installed. - Yüklediğiniz tüm oyunlar için hileleri indirdiniz. - - - - Patches Downloaded Successfully! - Yamalar Başarıyla İndirildi! - - - - All Patches available for all games have been downloaded. - Tüm oyunlar için mevcut tüm yamalar indirildi. - - - - Games: - Oyunlar: - - - - PKG File (*.PKG) - PKG Dosyası (*.PKG) - - - - ELF files (*.bin *.elf *.oelf) - ELF Dosyaları (*.bin *.elf *.oelf) - - - - Game Boot - Oyun Başlatma - - - - Only one file can be selected! - Sadece bir dosya seçilebilir! - - - - PKG Extraction - PKG Çıkartma - - - - Patch detected! - Yama tespit edildi! - - - - PKG and Game versions match: - PKG ve oyun sürümleri uyumlu: - - - - Would you like to overwrite? - Üzerine yazmak ister misiniz? - - - - PKG Version %1 is older than installed version: - PKG Sürümü %1, kurulu sürümden daha eski: - - - - Game is installed: - Oyun yüklendi: - - - - Would you like to install Patch: - Yamanın yüklenmesini ister misiniz: - - - - DLC Installation - DLC Yükleme - - - - Would you like to install DLC: %1? - DLC'yi yüklemek ister misiniz: %1? - - - - DLC already installed: - DLC zaten yüklü: - - - - Game already installed - Oyun zaten yüklü - - - - PKG is a patch, please install the game first! - PKG bir yama, lütfen önce oyunu yükleyin! - - - - PKG ERROR - PKG HATASI - - - - Extracting PKG %1/%2 - PKG Çıkarılıyor %1/%2 - - - - Extraction Finished - Çıkarma Tamamlandı - - - - Game successfully installed at %1 - Oyun başarıyla %1 konumuna yüklendi - - - - File doesn't appear to be a valid PKG file - Dosya geçerli bir PKG dosyası gibi görünmüyor - - - - CheatsPatches - - - Cheats / Patches for - Cheats / Patches for - - - - defaultTextEdit_MSG - Cheats/Patches deneysel niteliktedir.\nDikkatli kullanın.\n\nCheat'leri ayrı ayrı indirerek, depo seçerek ve indirme düğmesine tıklayarak indirin.\nPatches sekmesinde tüm patch'leri bir kerede indirebilir, hangi patch'leri kullanmak istediğinizi seçebilir ve seçiminizi kaydedebilirsiniz.\n\nCheats/Patches'i geliştirmediğimiz için,\nproblemleri cheat yazarına bildirin.\n\nYeni bir cheat mi oluşturduğunuz? Şu adresi ziyaret edin:\nhttps://github.com/shadps4-emu/ps4_cheats - - - - No Image Available - Görüntü Mevcut Değil - - - - Serial: - Seri Numarası: - - - - Version: - Sürüm: - - - - Size: - Boyut: - - - - Select Cheat File: - Hile Dosyasını Seçin: - - - - Repository: - Depo: - - - - Download Cheats - Hileleri İndir - - - - Delete File - Dosyayı Sil - - - - No files selected. - Hiçbir dosya seçilmedi. - - - - You can delete the cheats you don't want after downloading them. - İndirdikten sonra istemediğiniz hileleri silebilirsiniz. - - - - Do you want to delete the selected file?\n%1 - Seçilen dosyayı silmek istiyor musunuz?\n%1 - - - - Select Patch File: - Yama Dosyasını Seçin: - - - - Download Patches - Yamaları İndir - - - Save Kaydet - - Cheats - Hileler - - - - Patches - Yamalar - - - - Error - Hata - - - - No patch selected. - Hiç yama seçilmedi. - - - - Unable to open files.json for reading. - files.json dosyası okumak için açılamadı. - - - - No patch file found for the current serial. - Mevcut seri numarası için hiç yama dosyası bulunamadı. - - - - Unable to open the file for reading. - Dosya okumak için açılamadı. - - - - Unable to open the file for writing. - Dosya yazmak için açılamadı. - - - - Failed to parse XML: - XML ayrıştırılamadı: - - - - Success - Başarı - - - - Options saved successfully. - Ayarlar başarıyla kaydedildi. - - - - Invalid Source - Geçersiz Kaynak - - - - The selected source is invalid. - Seçilen kaynak geçersiz. - - - - File Exists - Dosya Var - - - - File already exists. Do you want to replace it? - Dosya zaten var. Üzerine yazmak ister misiniz? - - - - Failed to save file: - Dosya kaydedilemedi: - - - - Failed to download file: - Dosya indirilemedi: - - - - Cheats Not Found - Hileler Bulunamadı - - - - CheatsNotFound_MSG - Bu oyun için seçilen depoda hile bulunamadı.Başka bir depo veya oyun sürümü deneyin. - - - - Cheats Downloaded Successfully - Hileler Başarıyla İndirildi - - - - CheatsDownloadedSuccessfully_MSG - Bu oyun sürümü için hileleri başarıyla indirdiniz. Başka bir depodan indirmeyi deneyebilirsiniz. Eğer mevcutsa, listeden dosyayı seçerek de kullanılabilir. - - - - Failed to save: - Kaydedilemedi: - - - - Failed to download: - İndirilemedi: - - - - Download Complete - İndirme Tamamlandı - - - - DownloadComplete_MSG - Yamalar başarıyla indirildi! Tüm oyunlar için mevcut tüm yamalar indirildi, her oyun için ayrı ayrı indirme yapmanız gerekmez, hilelerle olduğu gibi. Yamanın görünmemesi durumunda, belirli seri numarası ve oyun sürümü için mevcut olmayabilir. - - - - Failed to parse JSON data from HTML. - HTML'den JSON verileri ayrıştırılamadı. - - - - Failed to retrieve HTML page. - HTML sayfası alınamadı. - - - - The game is in version: %1 - Oyun sürümde: %1 - - - - The downloaded patch only works on version: %1 - İndirilen yamanın sadece sürümde çalışıyor: %1 - - - - You may need to update your game. - Oyunuzu güncellemeniz gerekebilir. - - - - Incompatibility Notice - Uyumsuzluk Bildirimi - - - - Failed to open file: - Dosya açılamadı: - - - - XML ERROR: - XML HATASI: - - - - Failed to open files.json for writing - files.json dosyası yazmak için açılamadı - - - - Author: - Yazar: - - - - Directory does not exist: - Klasör mevcut değil: - - - - Failed to open files.json for reading. - files.json dosyası okumak için açılamadı. - - - - Name: - İsim: - - - - Can't apply cheats before the game is started - Hileleri oyuna başlamadan önce uygulayamazsınız. - - - - SettingsDialog - - - Save - Kaydet - - - Apply Uygula - Restore Defaults Varsayılanları Geri Yükle - Close Kapat - Point your mouse at an option to display its description. Seçenek üzerinde farenizi tutarak açıklamasını görüntüleyin. - consoleLanguageGroupBox Konsol Dili:\nPS4 oyununun kullandığı dili ayarlar.\nBu seçeneği, oyunun desteklediği bir dilde ayarlamanız önerilir; bu durum bölgeye göre değişebilir. - emulatorLanguageGroupBox Emülatör Dili:\nEmülatörün kullanıcı arayüzünün dilini ayarlar. - fullscreenCheckBox Tam Ekranı Etkinleştir:\nOyun penceresini otomatik olarak tam ekran moduna alır.\nBu, F11 tuşuna basarak geçiş yapılabilir. - separateUpdatesCheckBox Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. - showSplashCheckBox Açılış Ekranını Göster:\nOyun açılırken (özel bir görüntü) açılış ekranını gösterir. - ps4proCheckBox PS4 Pro:\nEmülatörü bir PS4 PRO gibi çalıştırır; bu, bunu destekleyen oyunlarda özel özellikleri etkinleştirebilir. - discordRPCCheckbox Discord Rich Presence'i etkinleştir:\nEmülatör simgesini ve Discord profilinizdeki ilgili bilgileri gösterir. - userName Kullanıcı Adı:\nBazı oyunlar tarafından gösterilebilen PS4 hesabının kullanıcı adını ayarlar. - TrophyKey Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. - logTypeGroupBox Günlük Türü:\nPerformans için günlük penceresi çıkışını senkronize etme durumunu ayarlar. Bu, emülasyonda olumsuz etkilere yol açabilir. - logFilter Günlük Filtre:\nSadece belirli bilgileri yazdırmak için günlüğü filtreler.\nÖrnekler: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Düzeyler: Trace, Debug, Info, Warning, Error, Critical - bu sırada, belirli bir seviye listede önceki tüm seviyeleri susturur ve sonraki tüm seviyeleri kaydeder. - updaterGroupBox Güncelleme:\nRelease: Her ay yayınlanan resmi sürümler; çok eski olabilirler, ancak daha güvenilirdir ve test edilmiştir.\nNightly: Tüm en son özellikler ve düzeltmeler ile birlikte geliştirme sürümleri; hatalar içerebilir ve daha az kararlıdırlar. - GUIgroupBox Başlık Müziklerini Çal:\nEğer bir oyun bunu destekliyorsa, GUI'de oyunu seçtiğinizde özel müziklerin çalmasını etkinleştirir. - disableTrophycheckBox Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). - hideCursorGroupBox İmleci gizle:\nİmlecin ne zaman kaybolacağını seçin:\nAsla: Fareyi her zaman göreceksiniz.\nPasif: Hareketsiz kaldıktan sonra kaybolması için bir süre belirleyin.\nHer zaman: fareyi asla göremeyeceksiniz. - idleTimeoutGroupBox Hareket etmeden sonra imlecin kaybolacağı süreyi ayarlayın. - backButtonBehaviorGroupBox Geri düğmesi davranışı:\nKontrol cihazındaki geri düğmesini, PS4'ün dokunmatik panelindeki belirlenen noktaya dokunmak için ayarlar. - enableCompatibilityCheckBox Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. - checkCompatibilityOnStartupCheckBox Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. - updateCompatibilityButton Update Compatibility Database:\nImmediately update the compatibility database. - Never Asla - Idle Boşta - Always Her zaman - Touchpad Left Dokunmatik Yüzey Sol - Touchpad Right Dokunmatik Yüzey Sağ - Touchpad Center Dokunmatik Yüzey Orta - None Yok - graphicsAdapterGroupBox Grafik Aygıtı:\nBirden fazla GPU'ya sahip sistemlerde, emülatörün kullanacağı GPU'yu açılır listeden seçin,\nor "Auto Select" seçeneğini seçerek otomatik olarak belirlenmesini sağlayın. - resolutionLayout Genişlik/Yükseklik:\nEmülatör penceresinin açılışta boyutunu ayarlar; bu, oyun sırasında yeniden boyutlandırılabilir.\nBu, oyundaki çözünürlükten farklıdır. - heightDivider Vblank Bölücü:\nEmülatörün yenileme hızı bu sayı ile çarpılır. Bu değerin değiştirilmesi olumsuz etkilere yol açabilir; oyun hızını artırabilir veya oyunun beklemediği kritik işlevselliği bozabilir! - dumpShadersCheckBox Shader'ları Dışa Aktarmayı Etkinleştir:\nTeknik hata ayıklama amacıyla, shader'ları render edildikçe bir klasöre kaydeder. - nullGpuCheckBox Null GPU'yu Etkinleştir:\nTeknik hata ayıklama amacıyla, oyunun render edilmesini grafik kartı yokmuş gibi devre dışı bırakır. - gameFoldersBox Oyun klasörleri:\nYüklenmiş oyunları kontrol etmek için klasörlerin listesi. - addFolderButton Ekle:\nListeye bir klasör ekle. - removeFolderButton Kaldır:\nListeden bir klasörü kaldır. - debugDump Hata Ayıklama için Dışa Aktarmayı Etkinleştir:\nŞu anda çalışan PS4 uygulaması için içe aktarılan ve dışa aktarılan sembolleri ve dosya başlık bilgilerini bir dizine kaydedin. - vkValidationCheckBox Vulkan Doğrulama Katmanlarını Etkinleştir:\nVulkan renderlayıcısının durumunu doğrulayan ve iç durum hakkında bilgi kaydeden bir sistemi etkinleştirir. Bu, performansı düşürür ve muhtemelen emülasyon davranışını değiştirir. - vkSyncValidationCheckBox Vulkan Senkronizasyon Doğrulamasını Etkinleştir:\nVulkan renderlama görevlerinin senkronizasyonunu doğrulayan bir sistemi etkinleştirir. Bu, performansı düşürür ve muhtemelen emülasyon davranışını değiştirir. - rdocCheckBox RenderDoc Hata Ayıklamayı Etkinleştir:\nEğer etkinleştirilirse, emülatör mevcut render edilmiş çerçeveyi yakalamak ve analiz etmek için Renderdoc ile uyumluluk sunar. + + CheatsPatches + + Cheats / Patches for + Cheats / Patches for + + + defaultTextEdit_MSG + Cheats/Patches deneysel niteliktedir.\nDikkatli kullanın.\n\nCheat'leri ayrı ayrı indirerek, depo seçerek ve indirme düğmesine tıklayarak indirin.\nPatches sekmesinde tüm patch'leri bir kerede indirebilir, hangi patch'leri kullanmak istediğinizi seçebilir ve seçiminizi kaydedebilirsiniz.\n\nCheats/Patches'i geliştirmediğimiz için,\nproblemleri cheat yazarına bildirin.\n\nYeni bir cheat mi oluşturduğunuz? Şu adresi ziyaret edin:\nhttps://github.com/shadps4-emu/ps4_cheats + + + No Image Available + Görüntü Mevcut Değil + + + Serial: + Seri Numarası: + + + Version: + Sürüm: + + + Size: + Boyut: + + + Select Cheat File: + Hile Dosyasını Seçin: + + + Repository: + Depo: + + + Download Cheats + Hileleri İndir + + + Delete File + Dosyayı Sil + + + No files selected. + Hiçbir dosya seçilmedi. + + + You can delete the cheats you don't want after downloading them. + İndirdikten sonra istemediğiniz hileleri silebilirsiniz. + + + Do you want to delete the selected file?\n%1 + Seçilen dosyayı silmek istiyor musunuz?\n%1 + + + Select Patch File: + Yama Dosyasını Seçin: + + + Download Patches + Yamaları İndir + + + Save + Kaydet + + + Cheats + Hileler + + + Patches + Yamalar + + + Error + Hata + + + No patch selected. + Hiç yama seçilmedi. + + + Unable to open files.json for reading. + files.json dosyası okumak için açılamadı. + + + No patch file found for the current serial. + Mevcut seri numarası için hiç yama dosyası bulunamadı. + + + Unable to open the file for reading. + Dosya okumak için açılamadı. + + + Unable to open the file for writing. + Dosya yazmak için açılamadı. + + + Failed to parse XML: + XML ayrıştırılamadı: + + + Success + Başarı + + + Options saved successfully. + Ayarlar başarıyla kaydedildi. + + + Invalid Source + Geçersiz Kaynak + + + The selected source is invalid. + Seçilen kaynak geçersiz. + + + File Exists + Dosya Var + + + File already exists. Do you want to replace it? + Dosya zaten var. Üzerine yazmak ister misiniz? + + + Failed to save file: + Dosya kaydedilemedi: + + + Failed to download file: + Dosya indirilemedi: + + + Cheats Not Found + Hileler Bulunamadı + + + CheatsNotFound_MSG + Bu oyun için seçilen depoda hile bulunamadı.Başka bir depo veya oyun sürümü deneyin. + + + Cheats Downloaded Successfully + Hileler Başarıyla İndirildi + + + CheatsDownloadedSuccessfully_MSG + Bu oyun sürümü için hileleri başarıyla indirdiniz. Başka bir depodan indirmeyi deneyebilirsiniz. Eğer mevcutsa, listeden dosyayı seçerek de kullanılabilir. + + + Failed to save: + Kaydedilemedi: + + + Failed to download: + İndirilemedi: + + + Download Complete + İndirme Tamamlandı + + + DownloadComplete_MSG + Yamalar başarıyla indirildi! Tüm oyunlar için mevcut tüm yamalar indirildi, her oyun için ayrı ayrı indirme yapmanız gerekmez, hilelerle olduğu gibi. Yamanın görünmemesi durumunda, belirli seri numarası ve oyun sürümü için mevcut olmayabilir. + + + Failed to parse JSON data from HTML. + HTML'den JSON verileri ayrıştırılamadı. + + + Failed to retrieve HTML page. + HTML sayfası alınamadı. + + + The game is in version: %1 + Oyun sürümde: %1 + + + The downloaded patch only works on version: %1 + İndirilen yamanın sadece sürümde çalışıyor: %1 + + + You may need to update your game. + Oyunuzu güncellemeniz gerekebilir. + + + Incompatibility Notice + Uyumsuzluk Bildirimi + + + Failed to open file: + Dosya açılamadı: + + + XML ERROR: + XML HATASI: + + + Failed to open files.json for writing + files.json dosyası yazmak için açılamadı + + + Author: + Yazar: + + + Directory does not exist: + Klasör mevcut değil: + + + Failed to open files.json for reading. + files.json dosyası okumak için açılamadı. + + + Name: + İsim: + + + Can't apply cheats before the game is started + Hileleri oyuna başlamadan önce uygulayamazsınız. + + GameListFrame - Icon Simge - Name Ad - Serial Seri Numarası - Compatibility Compatibility - Region Bölge - Firmware Yazılım - Size Boyut - Version Sürüm - Path Yol - Play Time Oynama Süresi - Never Played Never Played - h h - m m - s s - Compatibility is untested Compatibility is untested - Game does not initialize properly / crashes the emulator Game does not initialize properly / crashes the emulator - Game boots, but only displays a blank screen Game boots, but only displays a blank screen - Game displays an image but does not go past the menu Game displays an image but does not go past the menu - Game has game-breaking glitches or unplayable performance Game has game-breaking glitches or unplayable performance - Game can be completed with playable performance and no major glitches Game can be completed with playable performance and no major glitches @@ -1508,127 +1210,102 @@ CheckUpdate - Auto Updater Otomatik Güncelleyici - Error Hata - Network error: Ağ hatası: - Failed to parse update information. Güncelleme bilgilerini ayrıştırma başarısız oldu. - No pre-releases found. Ön sürüm bulunamadı. - Invalid release data. Geçersiz sürüm verisi. - No download URL found for the specified asset. Belirtilen varlık için hiçbir indirme URL'si bulunamadı. - Your version is already up to date! Versiyonunuz zaten güncel! - Update Available Güncelleme Mevcut - Update Channel Güncelleme Kanalı - Current Version Mevcut Versiyon - Latest Version Son Versiyon - Do you want to update? Güncellemek istiyor musunuz? - Show Changelog Değişiklik Günlüğünü Göster - Check for Updates at Startup Başlangıçta güncellemeleri kontrol et - Update Güncelle - No Hayır - Hide Changelog Değişiklik Günlüğünü Gizle - Changes Değişiklikler - Network error occurred while trying to access the URL URL'ye erişmeye çalışırken bir ağ hatası oluştu - Download Complete İndirme Tamamlandı - The update has been downloaded, press OK to install. Güncelleme indirildi, yüklemek için Tamam'a basın. - Failed to save the update file at Güncelleme dosyası kaydedilemedi - Starting Update... Güncelleme Başlatılıyor... - Failed to create the update script file Güncelleme betiği dosyası oluşturulamadı @@ -1636,29 +1313,24 @@ GameListUtils - B B - KB KB - MB MB - GB GB - TB TB - + \ No newline at end of file diff --git a/src/qt_gui/translations/uk_UA.ts b/src/qt_gui/translations/uk_UA.ts index 7e0a58ffb..03aca7cd9 100644 --- a/src/qt_gui/translations/uk_UA.ts +++ b/src/qt_gui/translations/uk_UA.ts @@ -6,22 +6,18 @@ AboutDialog - About shadPS4 Про shadPS4 - shadPS4 shadPS4 - shadPS4 is an experimental open-source emulator for the PlayStation 4. shadPS4 - це експериментальний емулятор з відкритим вихідним кодом для PlayStation 4. - This software should not be used to play games you have not legally obtained. Це програмне забезпечення не повинно використовуватися для запуску ігор, котрі ви отримали не легально. @@ -29,7 +25,6 @@ ElfViewer - Open Folder Відкрити папку @@ -37,17 +32,14 @@ GameInfoClass - Loading game list, please wait :3 Завантажуємо список ігор, будь ласка, зачекайте :3 - Cancel Відмінити - Loading... Завантаження... @@ -55,12 +47,10 @@ InstallDirSelect - shadPS4 - Choose directory shadPS4 - Виберіть папку - Select which directory you want to install to. Виберіть папку, до якої ви хочете встановити. @@ -68,27 +58,22 @@ GameInstallDialog - shadPS4 - Choose directory shadPS4 - Виберіть папку - Directory to install games Папка для встановлення ігор - Browse Обрати - Error Помилка - The value for location to install games is not valid. Не коректне значення розташування для встановлення ігор. @@ -96,167 +81,134 @@ GuiContextMenus - Create Shortcut Створити Ярлик - Cheats / Patches Чити та Патчі - SFO Viewer Перегляд SFO - Trophy Viewer Перегляд трофеїв - Open Folder... Відкрити Папку... - Open Game Folder Відкрити папку з грою - Open Save Data Folder Відкрити Папку Збережених Даних - Open Log Folder Відкрити Папку Логів - Copy info... Копіювати інформацію... - Copy Name Копіювати Ім’я - Copy Serial Копіювати серійний номер - Copy All Копіювати все - Delete... Видалення... - Delete Game Видалити гру - Delete Update Видалити оновлення - Delete DLC Видалити DLC - Compatibility... Compatibility... - Update database Update database - View report View report - Submit a report Submit a report - Shortcut creation Створення ярлика - Shortcut created successfully! Ярлик створений успішно! - Error Помилка - Error creating shortcut! Помилка при створенні ярлика! - Install PKG Встановити PKG - Game Ігри - requiresEnableSeparateUpdateFolder_MSG Ця функція потребує увімкнути опцію 'Окрема папка оновлень'. Якщо ви хочете використовувати цю функцію, будь ласка, увімкніть її. - This game has no update to delete! Ця гра не має оновлень для видалення! - - + Update Оновлення - This game has no DLC to delete! Ця гра не має DLC для видалення! - DLC DLC - Delete %1 Видалити %1 - Are you sure you want to delete %1's %2 directory? Ви впевнені, що хочете видалити папку %1 з папки %2?? @@ -264,205 +216,285 @@ MainWindow - Open/Add Elf Folder Відкрити/Додати папку Elf - Install Packages (PKG) Встановити пакети (PKG) - Boot Game Запустити гру - Check for Updates Перевити наявність оновлень - About shadPS4 Про shadPS4 - Configure... Налаштувати... - Install application from a .pkg file Встановити додаток з файлу .pkg - Recent Games Нещодавні ігри - Exit Вихід - Exit shadPS4 Вийти з shadPS4 - Exit the application. Вийти з додатку. - Show Game List Показати список ігор - Game List Refresh Оновити список ігор - Tiny Крихітний - Small Маленький - Medium Середній - Large Великий - List View Список - Grid View Сітка - Elf Viewer Elf - Game Install Directory Каталог встановлення гри - Download Cheats/Patches Завантажити Чити або Патчі - Dump Game List Дамп списку ігор - PKG Viewer Перегляд PKG - Search... Пошук... - File Файл - View Вид - Game List Icons Розмір значків списку игр - Game List Mode Вид списку ігор - Settings Налаштування - Utils Утиліти - Themes Теми - Help Допомога - Dark Темна - Light Світла - Green Зелена - Blue Синя - Violet Фіолетова - toolBar Панель інструментів + + Game List + Список ігор + + + * Unsupported Vulkan Version + * Непідтримувана версія Vulkan + + + Download Cheats For All Installed Games + Завантажити чити для всіх встановлених ігор + + + Download Patches For All Games + Завантажити патчі для всіх ігор + + + Download Complete + Завантаження завершено + + + You have downloaded cheats for all the games you have installed. + Ви завантажили чити для всіх встановлених ігор. + + + Patches Downloaded Successfully! + Патчі успішно завантажено! + + + All Patches available for all games have been downloaded. + Завантажено всі доступні патчі для всіх ігор. + + + Games: + Ігри: + + + PKG File (*.PKG) + Файл PKG (*.PKG) + + + ELF files (*.bin *.elf *.oelf) + Файл ELF (*.bin *.elf *.oelf) + + + Game Boot + Запуск гри + + + Only one file can be selected! + Можна вибрати лише один файл! + + + PKG Extraction + Видобуток PKG + + + Patch detected! + Виявлено патч! + + + PKG and Game versions match: + Версії PKG та гри збігаються: + + + Would you like to overwrite? + Бажаєте перезаписати? + + + PKG Version %1 is older than installed version: + Версія PKG %1 старіша за встановлену версію: + + + Game is installed: + Гра встановлена: + + + Would you like to install Patch: + Бажаєте встановити патч: + + + DLC Installation + Встановлення DLC + + + Would you like to install DLC: %1? + Ви бажаєте встановити DLC: %1?? + + + DLC already installed: + DLC вже встановлено: + + + Game already installed + Гра вже встановлена + + + PKG is a patch, please install the game first! + PKG - це патч, будь ласка, спочатку встановіть гру! + + + PKG ERROR + ПОМИЛКА PKG + + + Extracting PKG %1/%2 + Вилучення PKG %1/%2 + + + Extraction Finished + Вилучення завершено + + + Game successfully installed at %1 + Гру успішно встановлено у %1 + + + File doesn't appear to be a valid PKG file + Файл не є дійсним PKG-файлом + PKGViewer - Open Folder Відкрити папку @@ -470,7 +502,6 @@ TrophyViewer - Trophy Viewer Трофеї @@ -478,1029 +509,700 @@ SettingsDialog - Settings Налаштування - General Загальні - System Система - Console Language Мова консолі - Emulator Language Мова емулятора - Emulator Емулятор - Enable Fullscreen Увімкнути повноекранний режим - Enable Separate Update Folder Увімкнути окрему папку оновлень - Show Splash Показувати заставку - Is PS4 Pro Режим PS4 Pro - Enable Discord Rich Presence Увімкнути Discord Rich Presence - Username Ім'я користувача - Trophy Key Trophy Key - Trophy Trophy - Logger Логування - Log Type Тип логів - Log Filter Фільтр логів - Input Введення - Cursor Курсор миші - Hide Cursor Приховати курсор - Hide Cursor Idle Timeout Тайм-аут приховування курсора при бездіяльності - s s - Controller Контролер - Back Button Behavior Поведінка кнопки назад - Graphics Графіка - Graphics Device Графічний пристрій - Width Ширина - Height Висота - Vblank Divider Розділювач Vblank - Advanced Розширені - Enable Shaders Dumping Увімкнути дамп шейдерів - Enable NULL GPU Увімкнути NULL GPU - Paths Шляхи - Game Folders Ігрові папки - Add... Додати... - Remove Видалити - Debug Налагодження - Enable Debug Dumping Увімкнути налагоджувальні дампи - Enable Vulkan Validation Layers Увімкнути шари валідації Vulkan - Enable Vulkan Synchronization Validation Увімкнути валідацію синхронізації Vulkan - Enable RenderDoc Debugging Увімкнути налагодження RenderDoc - Update Оновлення - Check for Updates at Startup Перевірка оновлень під час запуску - Update Channel Канал оновлення - Check for Updates Перевірити оновлення - GUI Settings Інтерфейс - Disable Trophy Pop-ups Disable Trophy Pop-ups - Play title music Програвати заголовну музику - Update Compatibility Database On Startup Update Compatibility Database On Startup - Game Compatibility Game Compatibility - Display Compatibility Data Display Compatibility Data - Update Compatibility Database Update Compatibility Database - Volume Гучність - Audio Backend Audio Backend - - - MainWindow - - Game List - Список ігор - - - - * Unsupported Vulkan Version - * Непідтримувана версія Vulkan - - - - Download Cheats For All Installed Games - Завантажити чити для всіх встановлених ігор - - - - Download Patches For All Games - Завантажити патчі для всіх ігор - - - - Download Complete - Завантаження завершено - - - - You have downloaded cheats for all the games you have installed. - Ви завантажили чити для всіх встановлених ігор. - - - - Patches Downloaded Successfully! - Патчі успішно завантажено! - - - - All Patches available for all games have been downloaded. - Завантажено всі доступні патчі для всіх ігор. - - - - Games: - Ігри: - - - - PKG File (*.PKG) - Файл PKG (*.PKG) - - - - ELF files (*.bin *.elf *.oelf) - Файл ELF (*.bin *.elf *.oelf) - - - - Game Boot - Запуск гри - - - - Only one file can be selected! - Можна вибрати лише один файл! - - - - PKG Extraction - Видобуток PKG - - - - Patch detected! - Виявлено патч! - - - - PKG and Game versions match: - Версії PKG та гри збігаються: - - - - Would you like to overwrite? - Бажаєте перезаписати? - - - - PKG Version %1 is older than installed version: - Версія PKG %1 старіша за встановлену версію: - - - - Game is installed: - Гра встановлена: - - - - Would you like to install Patch: - Бажаєте встановити патч: - - - - DLC Installation - Встановлення DLC - - - - Would you like to install DLC: %1? - Ви бажаєте встановити DLC: %1?? - - - - DLC already installed: - DLC вже встановлено: - - - - Game already installed - Гра вже встановлена - - - - PKG is a patch, please install the game first! - PKG - це патч, будь ласка, спочатку встановіть гру! - - - - PKG ERROR - ПОМИЛКА PKG - - - - Extracting PKG %1/%2 - Вилучення PKG %1/%2 - - - - Extraction Finished - Вилучення завершено - - - - Game successfully installed at %1 - Гру успішно встановлено у %1 - - - - File doesn't appear to be a valid PKG file - Файл не є дійсним PKG-файлом - - - - CheatsPatches - - - Cheats / Patches for - Cheats / Patches for - - - - defaultTextEdit_MSG - Чити та Патчі є експериментальними.\nВикористовуйте з обережністю.\n\nЗавантажуйте чити окремо, вибравши репозиторій і натиснувши кнопку завантаження.\nУ вкладці "Патчі" ви можете завантажити всі патчі відразу, вибрати, які з них ви хочете використовувати, і зберегти свій вибір.\n\nОскільки ми не займаємося розробкою читів/патчів,\nбудь ласка, повідомляйте про проблеми автору чита/патча.\n\nСтворили новий чит? Відвідайте:\nhttps://github.com/shadps4-emu/ps4_cheats - - - - No Image Available - Зображення відсутнє - - - - Serial: - Серійний номер: - - - - Version: - Версія: - - - - Size: - Розмір: - - - - Select Cheat File: - Виберіть файл читу: - - - - Repository: - Репозиторій: - - - - Download Cheats - Завантажити чити - - - - Delete File - Видалити файл - - - - No files selected. - Файли не вибрані. - - - - You can delete the cheats you don't want after downloading them. - Ви можете видалити непотрібні чити після їх завантаження. - - - - Do you want to delete the selected file?\n%1 - Ви хочете видалити вибраний файл?\n%1 - - - - Select Patch File: - Виберіть файл патчу: - - - - Download Patches - Завантажити патчі - - - Save Зберегти - - Cheats - Чити - - - - Patches - Патчі - - - - Error - Помилка - - - - No patch selected. - Патч не вибрано. - - - - Unable to open files.json for reading. - Не вдалось відкрити files.json для читання. - - - - No patch file found for the current serial. - Файл патча для поточного серійного номера не знайдено. - - - - Unable to open the file for reading. - Не вдалося відкрити файл для читання. - - - - Unable to open the file for writing. - Не вдалось відкрити файл для запису. - - - - Failed to parse XML: - Не вдалося розібрати XML: - - - - Success - Успіх - - - - Options saved successfully. - Параметри успішно збережено. - - - - Invalid Source - Неправильне джерело - - - - The selected source is invalid. - Вибране джерело є недійсним. - - - - File Exists - Файл існує - - - - File already exists. Do you want to replace it? - Файл вже існує. Ви хочете замінити його? - - - - Failed to save file: - Не вдалося зберегти файл: - - - - Failed to download file: - Не вдалося завантажити файл: - - - - Cheats Not Found - Читів не знайдено - - - - CheatsNotFound_MSG - У вибраному репозиторії не знайдено Читів для цієї гри, спробуйте інший репозиторій або іншу версію гри. - - - - Cheats Downloaded Successfully - Чити успішно завантажено - - - - CheatsDownloadedSuccessfully_MSG - Ви успішно завантажили чити для цієї версії гри з обраного репозиторія. Ви можете спробувати завантажити з іншого репозиторія, якщо він буде доступним, ви також зможете скористатися ним, вибравши файл зі списку. - - - - Failed to save: - Не вдалося зберегти: - - - - Failed to download: - Не вдалося завантажити: - - - - Download Complete - Заватнаження завершено - - - - DownloadComplete_MSG - Патчі успішно завантажено! Всі доступні патчі для усіх ігор, завантажено, немає необхідності завантажувати їх окремо для кожної гри, як це відбувається у випадку з читами. Якщо патч не з’являється, можливо, його не існує для конкретного серійного номера та версії гри. Можливо, необхідно оновити гру. - - - - Failed to parse JSON data from HTML. - Не вдалося розібрати JSON-дані з HTML. - - - - Failed to retrieve HTML page. - Не вдалося отримати HTML-сторінку. - - - - The game is in version: %1 - Гра у версії: %1 - - - - The downloaded patch only works on version: %1 - Завантажений патч працює лише на версії: %1 - - - - You may need to update your game. - Можливо, вам потрібно оновити гру. - - - - Incompatibility Notice - Повідомлення про несумісність - - - - Failed to open file: - Не вдалося відкрити файл: - - - - XML ERROR: - ПОМИЛКА XML: - - - - Failed to open files.json for writing - Не вдалося відкрити files.json для запису - - - - Author: - Автор: - - - - Directory does not exist: - Каталогу не існує: - - - - Failed to open files.json for reading. - Не вдалося відкрити files.json для читання. - - - - Name: - Ім'я: - - - - Can't apply cheats before the game is started - Неможливо застосовувати чити до початку гри. - - - - SettingsDialog - - - Save - Зберегти - - - Apply Застосувати - Restore Defaults За замовчуванням - Close Закрити - Point your mouse at an option to display its description. Наведіть курсор миші на опцію, щоб відобразити її опис. - consoleLanguageGroupBox Мова консолі:\nВстановіть мову, яка буде використовуватись у іграх PS4.\nРекомендується встановити мову котра підтримується грою, оскільки вона може відрізнятися в залежності від регіону. - emulatorLanguageGroupBox Мова емулятора:\nВстановіть мову користувацького інтерфейсу емулятора. - fullscreenCheckBox Повноекранний режим:\nАвтоматично переводить вікно гри у повноекранний режим.\nВи можете відключити це, натиснувши клавішу F11. - separateUpdatesCheckBox Окрема папка для оновлень:\nДає змогу встановлювати оновлення гри в окрему папку для зручності. - showSplashCheckBox Показувати заставку:\nВідображає заставку гри (спеціальне зображення) під час запуску гри. - ps4proCheckBox Режим PS4 Pro:\nЗмушує емулятор працювати як PS4 Pro, що може ввімкнути спеціальні функції в іграх, які підтримують це. - discordRPCCheckbox Увімкнути Discord Rich Presence:\nВідображає значок емулятора та відповідну інформацію у вашому профілі Discord. - userName Ім'я користувача:\nВстановіть ім'я користувача акаунта PS4. Це може відображатися в деяких іграх. - TrophyKey Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. - logTypeGroupBox Тип логів:\nВстановіть, чи синхронізувати виведення вікна логів заради продуктивності. Це може негативно вплинути на емуляцію. - logFilter Фільтр логів:\nФільтрує логи, щоб показувати тільки певну інформацію.\nПриклади: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Рівні: Trace, Debug, Info, Warning, Error, Critical - у цьому порядку, конкретний рівень глушить усі попередні рівні у списку і показує всі наступні рівні. - updaterGroupBox Оновлення:\nRelease: Офіційні версії, які випускаються щомісяця і можуть бути дуже старими, але вони більш надійні та перевірені.\nNightly: Версії для розробників, які мають усі найновіші функції та виправлення, але можуть містити помилки та є менш стабільними. - GUIgroupBox Грати заголовну музику:\nВмикає відтворення спеціальної музики під час вибору гри в списку, якщо вона це підтримує. - disableTrophycheckBox Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). - hideCursorGroupBox Приховувати курсор:\nВиберіть, коли курсор зникне:\nНіколи: Ви завжди будете бачити мишу.\nПри бездіяльності: Встановіть час, через який курсор зникне в разі бездіяльності.\nЗавжди: Ви ніколи не будете бачити мишу. - idleTimeoutGroupBox Встановіть час, через який курсор зникне в разі бездіяльності. - backButtonBehaviorGroupBox Поведінка кнопки «Назад»:\nНалаштовує кнопку «Назад» контролера на емуляцію натискання на зазначену область на сенсорній панелі контролера PS4. - enableCompatibilityCheckBox Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. - checkCompatibilityOnStartupCheckBox Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. - updateCompatibilityButton Update Compatibility Database:\nImmediately update the compatibility database. - Never Ніколи - Idle При бездіяльності - Always Завжди - Touchpad Left Тачпад ліворуч - Touchpad Right Тачпад праворуч - Touchpad Center Тачпад по центру - None Ні - graphicsAdapterGroupBox Графічний пристрій:\nУ системах із кількома GPU виберіть з випадаючого списку GPU, який буде використовувати емулятор,\nабо виберіть "Auto Select", щоб визначити його автоматично. - resolutionLayout Ширина/Висота:\nВстановіть розмір вікна емулятора під час запуску, який може бути змінений під час гри.\nЦе відрізняється від роздільної здатності в грі. - heightDivider Розділювач Vblank:\nЧастота кадрів, з якою оновлюється емулятор, множиться на це число. Зміна цього параметра може мати негативні наслідки, такі як збільшення швидкості гри або порушення критичних функцій гри, які цього не очікують! - dumpShadersCheckBox Увімкнути дамп шейдерів:\nДля технічного налагодження зберігає шейдери ігор у папку під час рендерингу. - nullGpuCheckBox Увімкнути NULL GPU:\nДля технічного налагодження відключає рендеринг гри так, ніби графічної карти немає. - gameFoldersBox Ігрові папки:\nСписок папок для перевірки встановлених ігор. - addFolderButton Додати:\nДодати папку в список. - removeFolderButton Видалити:\nВидалити папку зі списку. - debugDump Увімкнути налагоджувальні дампи:\nЗберігає символи імпорту, експорту та інформацію про заголовок файлу поточної виконуваної програми PS4 у папку. - vkValidationCheckBox Увімкнути шари валідації Vulkan:\nВключає систему, яка перевіряє стан рендерера Vulkan і логує інформацію про його внутрішній стан. Це знизить продуктивність і, ймовірно, змінить поведінку емуляції. - vkSyncValidationCheckBox Увімкнути валідацію синхронізації Vulkan:\nВключає систему, яка перевіряє таймінг завдань рендерингу Vulkan. Це знизить продуктивність і, ймовірно, змінить поведінку емуляції. - rdocCheckBox Увімкнути налагодження RenderDoc:\nЯкщо увімкнено, емулятор забезпечить сумісність із Renderdoc, даючи змогу захоплювати й аналізувати поточні кадри під час рендерингу. + + CheatsPatches + + Cheats / Patches for + Cheats / Patches for + + + defaultTextEdit_MSG + Чити та Патчі є експериментальними.\nВикористовуйте з обережністю.\n\nЗавантажуйте чити окремо, вибравши репозиторій і натиснувши кнопку завантаження.\nУ вкладці "Патчі" ви можете завантажити всі патчі відразу, вибрати, які з них ви хочете використовувати, і зберегти свій вибір.\n\nОскільки ми не займаємося розробкою читів/патчів,\nбудь ласка, повідомляйте про проблеми автору чита/патча.\n\nСтворили новий чит? Відвідайте:\nhttps://github.com/shadps4-emu/ps4_cheats + + + No Image Available + Зображення відсутнє + + + Serial: + Серійний номер: + + + Version: + Версія: + + + Size: + Розмір: + + + Select Cheat File: + Виберіть файл читу: + + + Repository: + Репозиторій: + + + Download Cheats + Завантажити чити + + + Delete File + Видалити файл + + + No files selected. + Файли не вибрані. + + + You can delete the cheats you don't want after downloading them. + Ви можете видалити непотрібні чити після їх завантаження. + + + Do you want to delete the selected file?\n%1 + Ви хочете видалити вибраний файл?\n%1 + + + Select Patch File: + Виберіть файл патчу: + + + Download Patches + Завантажити патчі + + + Save + Зберегти + + + Cheats + Чити + + + Patches + Патчі + + + Error + Помилка + + + No patch selected. + Патч не вибрано. + + + Unable to open files.json for reading. + Не вдалось відкрити files.json для читання. + + + No patch file found for the current serial. + Файл патча для поточного серійного номера не знайдено. + + + Unable to open the file for reading. + Не вдалося відкрити файл для читання. + + + Unable to open the file for writing. + Не вдалось відкрити файл для запису. + + + Failed to parse XML: + Не вдалося розібрати XML: + + + Success + Успіх + + + Options saved successfully. + Параметри успішно збережено. + + + Invalid Source + Неправильне джерело + + + The selected source is invalid. + Вибране джерело є недійсним. + + + File Exists + Файл існує + + + File already exists. Do you want to replace it? + Файл вже існує. Ви хочете замінити його? + + + Failed to save file: + Не вдалося зберегти файл: + + + Failed to download file: + Не вдалося завантажити файл: + + + Cheats Not Found + Читів не знайдено + + + CheatsNotFound_MSG + У вибраному репозиторії не знайдено Читів для цієї гри, спробуйте інший репозиторій або іншу версію гри. + + + Cheats Downloaded Successfully + Чити успішно завантажено + + + CheatsDownloadedSuccessfully_MSG + Ви успішно завантажили чити для цієї версії гри з обраного репозиторія. Ви можете спробувати завантажити з іншого репозиторія, якщо він буде доступним, ви також зможете скористатися ним, вибравши файл зі списку. + + + Failed to save: + Не вдалося зберегти: + + + Failed to download: + Не вдалося завантажити: + + + Download Complete + Заватнаження завершено + + + DownloadComplete_MSG + Патчі успішно завантажено! Всі доступні патчі для усіх ігор, завантажено, немає необхідності завантажувати їх окремо для кожної гри, як це відбувається у випадку з читами. Якщо патч не з’являється, можливо, його не існує для конкретного серійного номера та версії гри. Можливо, необхідно оновити гру. + + + Failed to parse JSON data from HTML. + Не вдалося розібрати JSON-дані з HTML. + + + Failed to retrieve HTML page. + Не вдалося отримати HTML-сторінку. + + + The game is in version: %1 + Гра у версії: %1 + + + The downloaded patch only works on version: %1 + Завантажений патч працює лише на версії: %1 + + + You may need to update your game. + Можливо, вам потрібно оновити гру. + + + Incompatibility Notice + Повідомлення про несумісність + + + Failed to open file: + Не вдалося відкрити файл: + + + XML ERROR: + ПОМИЛКА XML: + + + Failed to open files.json for writing + Не вдалося відкрити files.json для запису + + + Author: + Автор: + + + Directory does not exist: + Каталогу не існує: + + + Failed to open files.json for reading. + Не вдалося відкрити files.json для читання. + + + Name: + Ім'я: + + + Can't apply cheats before the game is started + Неможливо застосовувати чити до початку гри. + + GameListFrame - Icon Значок - Name Назва - Serial Серійний номер - Compatibility Compatibility - Region Регіон - Firmware Прошивка - Size Розмір - Version Версія - Path Шлях - Play Time Час у грі - Never Played Never Played - h h - m m - s s - Compatibility is untested Compatibility is untested - Game does not initialize properly / crashes the emulator Game does not initialize properly / crashes the emulator - Game boots, but only displays a blank screen Game boots, but only displays a blank screen - Game displays an image but does not go past the menu Game displays an image but does not go past the menu - Game has game-breaking glitches or unplayable performance Game has game-breaking glitches or unplayable performance - Game can be completed with playable performance and no major glitches Game can be completed with playable performance and no major glitches @@ -1508,127 +1210,102 @@ CheckUpdate - Auto Updater Автооновлення - Error Помилка - Network error: Мережева помилка: - Failed to parse update information. Не вдалося розібрати інформацію про оновлення. - No pre-releases found. Попередніх версій не знайдено. - Invalid release data. Неприпустимі дані релізу. - No download URL found for the specified asset. Не знайдено URL для завантаження зазначеного ресурсу. - Your version is already up to date! Вашу версію вже оновлено! - Update Available Доступне оновлення - Update Channel Канал оновлення - Current Version Поточна версія - Latest Version Остання версія - Do you want to update? Ви хочете оновитися? - Show Changelog Показати журнал змін - Check for Updates at Startup Перевірка оновлень під час запуску - Update Оновитись - No Ні - Hide Changelog Приховати журнал змін - Changes Журнал змін - Network error occurred while trying to access the URL Сталася мережева помилка під час спроби доступу до URL - Download Complete Завантаження завершено - The update has been downloaded, press OK to install. Оновлення завантажено, натисніть OK для встановлення. - Failed to save the update file at Не вдалося зберегти файл оновлення в - Starting Update... Початок оновлення... - Failed to create the update script file Не вдалося створити файл скрипта оновлення @@ -1636,29 +1313,24 @@ GameListUtils - B B - KB KB - MB MB - GB GB - TB TB - + \ No newline at end of file diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index 997c3d3f9..3f92c1836 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -6,22 +6,18 @@ AboutDialog - About shadPS4 About shadPS4 - shadPS4 shadPS4 - shadPS4 is an experimental open-source emulator for the PlayStation 4. shadPS4 is an experimental open-source emulator for the PlayStation 4. - This software should not be used to play games you have not legally obtained. This software should not be used to play games you have not legally obtained. @@ -29,7 +25,6 @@ ElfViewer - Open Folder Open Folder @@ -37,17 +32,14 @@ GameInfoClass - Loading game list, please wait :3 Loading game list, please wait :3 - Cancel Cancel - Loading... Loading... @@ -55,12 +47,10 @@ InstallDirSelect - shadPS4 - Choose directory shadPS4 - Choose directory - Select which directory you want to install to. Select which directory you want to install to. @@ -68,27 +58,22 @@ GameInstallDialog - shadPS4 - Choose directory shadPS4 - Choose directory - Directory to install games Directory to install games - Browse Browse - Error Error - The value for location to install games is not valid. The value for location to install games is not valid. @@ -96,167 +81,134 @@ GuiContextMenus - Create Shortcut Create Shortcut - Cheats / Patches Mẹo / Bản vá - SFO Viewer SFO Viewer - Trophy Viewer Trophy Viewer - Open Folder... Mở Thư Mục... - Open Game Folder Mở Thư Mục Trò Chơi - Open Save Data Folder Mở Thư Mục Dữ Liệu Lưu - Open Log Folder Mở Thư Mục Nhật Ký - Copy info... Copy info... - Copy Name Copy Name - Copy Serial Copy Serial - Copy All Copy All - Delete... Delete... - Delete Game Delete Game - Delete Update Delete Update - Delete DLC Delete DLC - Compatibility... Compatibility... - Update database Update database - View report View report - Submit a report Submit a report - Shortcut creation Shortcut creation - Shortcut created successfully! Shortcut created successfully! - Error Error - Error creating shortcut! Error creating shortcut! - Install PKG Install PKG - Game Game - requiresEnableSeparateUpdateFolder_MSG This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. - This game has no update to delete! This game has no update to delete! - - + Update Update - This game has no DLC to delete! This game has no DLC to delete! - DLC DLC - Delete %1 Delete %1 - Are you sure you want to delete %1's %2 directory? Are you sure you want to delete %1's %2 directory? @@ -264,205 +216,285 @@ MainWindow - Open/Add Elf Folder Open/Add Elf Folder - Install Packages (PKG) Install Packages (PKG) - Boot Game Boot Game - Check for Updates Kiểm tra bản cập nhật - About shadPS4 About shadPS4 - Configure... Configure... - Install application from a .pkg file Install application from a .pkg file - Recent Games Recent Games - Exit Exit - Exit shadPS4 Exit shadPS4 - Exit the application. Exit the application. - Show Game List Show Game List - Game List Refresh Game List Refresh - Tiny Tiny - Small Small - Medium Medium - Large Large - List View List View - Grid View Grid View - Elf Viewer Elf Viewer - Game Install Directory Game Install Directory - Download Cheats/Patches Tải Mẹo / Bản vá - Dump Game List Dump Game List - PKG Viewer PKG Viewer - Search... Search... - File File - View View - Game List Icons Game List Icons - Game List Mode Game List Mode - Settings Settings - Utils Utils - Themes Themes - Help Giúp đỡ - Dark Dark - Light Light - Green Green - Blue Blue - Violet Violet - toolBar toolBar + + Game List + Danh sách trò chơi + + + * Unsupported Vulkan Version + * Phiên bản Vulkan không được hỗ trợ + + + Download Cheats For All Installed Games + Tải xuống cheat cho tất cả các trò chơi đã cài đặt + + + Download Patches For All Games + Tải xuống bản vá cho tất cả các trò chơi + + + Download Complete + Tải xuống hoàn tất + + + You have downloaded cheats for all the games you have installed. + Bạn đã tải xuống cheat cho tất cả các trò chơi mà bạn đã cài đặt. + + + Patches Downloaded Successfully! + Bản vá đã tải xuống thành công! + + + All Patches available for all games have been downloaded. + Tất cả các bản vá có sẵn cho tất cả các trò chơi đã được tải xuống. + + + Games: + Trò chơi: + + + PKG File (*.PKG) + Tệp PKG (*.PKG) + + + ELF files (*.bin *.elf *.oelf) + Tệp ELF (*.bin *.elf *.oelf) + + + Game Boot + Khởi động trò chơi + + + Only one file can be selected! + Chỉ có thể chọn một tệp duy nhất! + + + PKG Extraction + Giải nén PKG + + + Patch detected! + Đã phát hiện bản vá! + + + PKG and Game versions match: + Các phiên bản PKG và trò chơi khớp nhau: + + + Would you like to overwrite? + Bạn có muốn ghi đè không? + + + PKG Version %1 is older than installed version: + Phiên bản PKG %1 cũ hơn phiên bản đã cài đặt: + + + Game is installed: + Trò chơi đã được cài đặt: + + + Would you like to install Patch: + Bạn có muốn cài đặt bản vá: + + + DLC Installation + Cài đặt DLC + + + Would you like to install DLC: %1? + Bạn có muốn cài đặt DLC: %1? + + + DLC already installed: + DLC đã được cài đặt: + + + Game already installed + Trò chơi đã được cài đặt + + + PKG is a patch, please install the game first! + PKG là bản vá, vui lòng cài đặt trò chơi trước! + + + PKG ERROR + LOI PKG + + + Extracting PKG %1/%2 + Đang giải nén PKG %1/%2 + + + Extraction Finished + Giải nén hoàn tất + + + Game successfully installed at %1 + Trò chơi đã được cài đặt thành công tại %1 + + + File doesn't appear to be a valid PKG file + Tệp không có vẻ là tệp PKG hợp lệ + PKGViewer - Open Folder Open Folder @@ -470,7 +502,6 @@ TrophyViewer - Trophy Viewer Trophy Viewer @@ -478,1029 +509,700 @@ SettingsDialog - Settings Settings - General General - System System - Console Language Console Language - Emulator Language Emulator Language - Emulator Emulator - Enable Fullscreen Enable Fullscreen - Enable Separate Update Folder Enable Separate Update Folder - Show Splash Show Splash - Is PS4 Pro Is PS4 Pro - Enable Discord Rich Presence Bật Discord Rich Presence - Username Username - Trophy Key Trophy Key - Trophy Trophy - Logger Logger - Log Type Log Type - Log Filter Log Filter - Input Đầu vào - Cursor Con trỏ - Hide Cursor Ẩn con trỏ - Hide Cursor Idle Timeout Thời gian chờ ẩn con trỏ - s s - Controller Điều khiển - Back Button Behavior Hành vi nút quay lại - Graphics Graphics - Graphics Device Graphics Device - Width Width - Height Height - Vblank Divider Vblank Divider - Advanced Advanced - Enable Shaders Dumping Enable Shaders Dumping - Enable NULL GPU Enable NULL GPU - Paths Đường dẫn - Game Folders Thư mục trò chơi - Add... Thêm... - Remove Xóa - Debug Debug - Enable Debug Dumping Enable Debug Dumping - Enable Vulkan Validation Layers Enable Vulkan Validation Layers - Enable Vulkan Synchronization Validation Enable Vulkan Synchronization Validation - Enable RenderDoc Debugging Enable RenderDoc Debugging - Update Cập nhật - Check for Updates at Startup Kiểm tra cập nhật khi khởi động - Update Channel Kênh Cập Nhật - Check for Updates Kiểm tra cập nhật - GUI Settings Cài đặt GUI - Disable Trophy Pop-ups Disable Trophy Pop-ups - Play title music Phát nhạc tiêu đề - Update Compatibility Database On Startup Update Compatibility Database On Startup - Game Compatibility Game Compatibility - Display Compatibility Data Display Compatibility Data - Update Compatibility Database Update Compatibility Database - Volume Âm lượng - Audio Backend Audio Backend - - - MainWindow - - Game List - Danh sách trò chơi - - - - * Unsupported Vulkan Version - * Phiên bản Vulkan không được hỗ trợ - - - - Download Cheats For All Installed Games - Tải xuống cheat cho tất cả các trò chơi đã cài đặt - - - - Download Patches For All Games - Tải xuống bản vá cho tất cả các trò chơi - - - - Download Complete - Tải xuống hoàn tất - - - - You have downloaded cheats for all the games you have installed. - Bạn đã tải xuống cheat cho tất cả các trò chơi mà bạn đã cài đặt. - - - - Patches Downloaded Successfully! - Bản vá đã tải xuống thành công! - - - - All Patches available for all games have been downloaded. - Tất cả các bản vá có sẵn cho tất cả các trò chơi đã được tải xuống. - - - - Games: - Trò chơi: - - - - PKG File (*.PKG) - Tệp PKG (*.PKG) - - - - ELF files (*.bin *.elf *.oelf) - Tệp ELF (*.bin *.elf *.oelf) - - - - Game Boot - Khởi động trò chơi - - - - Only one file can be selected! - Chỉ có thể chọn một tệp duy nhất! - - - - PKG Extraction - Giải nén PKG - - - - Patch detected! - Đã phát hiện bản vá! - - - - PKG and Game versions match: - Các phiên bản PKG và trò chơi khớp nhau: - - - - Would you like to overwrite? - Bạn có muốn ghi đè không? - - - - PKG Version %1 is older than installed version: - Phiên bản PKG %1 cũ hơn phiên bản đã cài đặt: - - - - Game is installed: - Trò chơi đã được cài đặt: - - - - Would you like to install Patch: - Bạn có muốn cài đặt bản vá: - - - - DLC Installation - Cài đặt DLC - - - - Would you like to install DLC: %1? - Bạn có muốn cài đặt DLC: %1? - - - - DLC already installed: - DLC đã được cài đặt: - - - - Game already installed - Trò chơi đã được cài đặt - - - - PKG is a patch, please install the game first! - PKG là bản vá, vui lòng cài đặt trò chơi trước! - - - - PKG ERROR - LOI PKG - - - - Extracting PKG %1/%2 - Đang giải nén PKG %1/%2 - - - - Extraction Finished - Giải nén hoàn tất - - - - Game successfully installed at %1 - Trò chơi đã được cài đặt thành công tại %1 - - - - File doesn't appear to be a valid PKG file - Tệp không có vẻ là tệp PKG hợp lệ - - - - CheatsPatches - - - Cheats / Patches for - Cheats / Patches for - - - - defaultTextEdit_MSG - Cheats/Patches là các tính năng thử nghiệm.\nHãy sử dụng cẩn thận.\n\nTải xuống các cheat riêng lẻ bằng cách chọn kho lưu trữ và nhấp vào nút tải xuống.\nTại tab Patches, bạn có thể tải xuống tất cả các patch cùng một lúc, chọn cái nào bạn muốn sử dụng và lưu lựa chọn của mình.\n\nVì chúng tôi không phát triển Cheats/Patches,\nxin vui lòng báo cáo các vấn đề cho tác giả cheat.\n\nBạn đã tạo ra một cheat mới? Truy cập:\nhttps://github.com/shadps4-emu/ps4_cheats - - - - No Image Available - Không có hình ảnh - - - - Serial: - Số seri: - - - - Version: - Phiên bản: - - - - Size: - Kích thước: - - - - Select Cheat File: - Chọn tệp Cheat: - - - - Repository: - Kho lưu trữ: - - - - Download Cheats - Tải xuống Cheat - - - - Delete File - Xóa tệp - - - - No files selected. - Không có tệp nào được chọn. - - - - You can delete the cheats you don't want after downloading them. - Bạn có thể xóa các cheat không muốn sau khi tải xuống. - - - - Do you want to delete the selected file?\n%1 - Bạn có muốn xóa tệp đã chọn?\n%1 - - - - Select Patch File: - Chọn tệp Bản vá: - - - - Download Patches - Tải xuống Bản vá - - - Save Lưu - - Cheats - Cheat - - - - Patches - Bản vá - - - - Error - Lỗi - - - - No patch selected. - Không có bản vá nào được chọn. - - - - Unable to open files.json for reading. - Không thể mở files.json để đọc. - - - - No patch file found for the current serial. - Không tìm thấy tệp bản vá cho số seri hiện tại. - - - - Unable to open the file for reading. - Không thể mở tệp để đọc. - - - - Unable to open the file for writing. - Không thể mở tệp để ghi. - - - - Failed to parse XML: - Không thể phân tích XML: - - - - Success - Thành công - - - - Options saved successfully. - Các tùy chọn đã được lưu thành công. - - - - Invalid Source - Nguồn không hợp lệ - - - - The selected source is invalid. - Nguồn đã chọn không hợp lệ. - - - - File Exists - Tệp đã tồn tại - - - - File already exists. Do you want to replace it? - Tệp đã tồn tại. Bạn có muốn thay thế nó không? - - - - Failed to save file: - Không thể lưu tệp: - - - - Failed to download file: - Không thể tải xuống tệp: - - - - Cheats Not Found - Không tìm thấy Cheat - - - - CheatsNotFound_MSG - Không tìm thấy Cheat cho trò chơi này trong phiên bản kho lưu trữ đã chọn,hãy thử kho lưu trữ khác hoặc phiên bản khác của trò chơi. - - - - Cheats Downloaded Successfully - Cheat đã tải xuống thành công - - - - CheatsDownloadedSuccessfully_MSG - Bạn đã tải xuống các cheat thành công. Cho phiên bản trò chơi này từ kho lưu trữ đã chọn. Bạn có thể thử tải xuống từ kho lưu trữ khác, nếu có, bạn cũng có thể sử dụng bằng cách chọn tệp từ danh sách. - - - - Failed to save: - Không thể lưu: - - - - Failed to download: - Không thể tải xuống: - - - - Download Complete - Tải xuống hoàn tất - - - - DownloadComplete_MSG - Bản vá đã tải xuống thành công! Tất cả các bản vá có sẵn cho tất cả các trò chơi đã được tải xuống, không cần tải xuống riêng lẻ cho mỗi trò chơi như trong Cheat. Nếu bản vá không xuất hiện, có thể là nó không tồn tại cho số seri và phiên bản cụ thể của trò chơi. - - - - Failed to parse JSON data from HTML. - Không thể phân tích dữ liệu JSON từ HTML. - - - - Failed to retrieve HTML page. - Không thể lấy trang HTML. - - - - The game is in version: %1 - Trò chơi đang ở phiên bản: %1 - - - - The downloaded patch only works on version: %1 - Patch đã tải về chỉ hoạt động trên phiên bản: %1 - - - - You may need to update your game. - Bạn có thể cần cập nhật trò chơi của mình. - - - - Incompatibility Notice - Thông báo không tương thích - - - - Failed to open file: - Không thể mở tệp: - - - - XML ERROR: - LỖI XML: - - - - Failed to open files.json for writing - Không thể mở files.json để ghi - - - - Author: - Tác giả: - - - - Directory does not exist: - Thư mục không tồn tại: - - - - Failed to open files.json for reading. - Không thể mở files.json để đọc. - - - - Name: - Tên: - - - - Can't apply cheats before the game is started - Không thể áp dụng cheat trước khi trò chơi bắt đầu. - - - - SettingsDialog - - - Save - Lưu - - - Apply Áp dụng - Restore Defaults Khôi phục cài đặt mặc định - Close Đóng - Point your mouse at an option to display its description. Di chuyển chuột đến tùy chọn để hiển thị mô tả của nó. - consoleLanguageGroupBox Ngôn ngữ console:\nChọn ngôn ngữ mà trò chơi PS4 sẽ sử dụng.\nKhuyên bạn nên đặt tùy chọn này thành một ngôn ngữ mà trò chơi hỗ trợ, có thể thay đổi tùy theo vùng. - emulatorLanguageGroupBox Ngôn ngữ của trình giả lập:\nChọn ngôn ngữ của giao diện người dùng của trình giả lập. - fullscreenCheckBox Bật chế độ toàn màn hình:\nTự động đặt cửa sổ trò chơi ở chế độ toàn màn hình.\nĐiều này có thể bị vô hiệu hóa bằng cách nhấn phím F11. - separateUpdatesCheckBox Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. - showSplashCheckBox Hiển thị màn hình khởi động:\nHiển thị màn hình khởi động của trò chơi (một hình ảnh đặc biệt) trong khi trò chơi khởi động. - ps4proCheckBox Là PS4 Pro:\nKhiến trình giả lập hoạt động như một PS4 PRO, điều này có thể kích hoạt các tính năng đặc biệt trong các trò chơi hỗ trợ điều này. - discordRPCCheckbox Bật Discord Rich Presence:\nHiển thị biểu tượng trình giả lập và thông tin liên quan trên hồ sơ Discord của bạn. - userName Tên người dùng:\nChọn tên người dùng của tài khoản PS4, có thể được một số trò chơi hiển thị. - TrophyKey Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. - logTypeGroupBox Loại nhật ký:\nChọn xem có đồng bộ hóa đầu ra cửa sổ nhật ký cho hiệu suất hay không. Điều này có thể có tác động tiêu cực đến việc giả lập. - logFilter Bộ lọc nhật ký:\nLọc nhật ký để in chỉ thông tin cụ thể.\nVí dụ: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Các mức: Trace, Debug, Info, Warning, Error, Critical - theo thứ tự này, một mức cụ thể làm tắt tất cả các mức trước trong danh sách và ghi lại tất cả các mức sau đó. - updaterGroupBox Cập nhật:\nRelease: Các phiên bản chính thức được phát hành hàng tháng; có thể khá cũ nhưng đáng tin cậy hơn và đã được thử nghiệm.\nNightly: Các phiên bản phát triển có tất cả các tính năng và sửa lỗi mới nhất; có thể có lỗi và ít ổn định hơn. - GUIgroupBox Phát nhạc tiêu đề trò chơi:\nNếu một trò chơi hỗ trợ điều này, hãy kích hoạt phát nhạc đặc biệt khi bạn chọn trò chơi trong GUI. - disableTrophycheckBox Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). - hideCursorGroupBox Ẩn con trỏ:\nChọn khi nào con trỏ sẽ biến mất:\nKhông bao giờ: Bạn sẽ luôn thấy chuột.\nKhông hoạt động: Đặt một khoảng thời gian để nó biến mất sau khi không hoạt động.\nLuôn luôn: bạn sẽ không bao giờ thấy chuột. - idleTimeoutGroupBox Đặt thời gian để chuột biến mất sau khi không hoạt động. - backButtonBehaviorGroupBox Hành vi nút quay lại:\nĐặt nút quay lại của tay cầm để mô phỏng việc chạm vào vị trí đã chỉ định trên touchpad của PS4. - enableCompatibilityCheckBox Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. - checkCompatibilityOnStartupCheckBox Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. - updateCompatibilityButton Update Compatibility Database:\nImmediately update the compatibility database. - Never Không bao giờ - Idle Nhàn rỗi - Always Luôn luôn - Touchpad Left Touchpad Trái - Touchpad Right Touchpad Phải - Touchpad Center Giữa Touchpad - None Không có - graphicsAdapterGroupBox Thiết bị đồ họa:\nTrên các hệ thống có GPU đa năng, hãy chọn GPU mà trình giả lập sẽ sử dụng từ danh sách thả xuống,\hoặc chọn "Auto Select" để tự động xác định. - resolutionLayout Chiều rộng/Cao:\nChọn kích thước cửa sổ của trình giả lập khi khởi động, có thể điều chỉnh trong quá trình chơi.\nĐiều này khác với độ phân giải trong trò chơi. - heightDivider Bộ chia Vblank:\nTốc độ khung hình mà trình giả lập làm mới được nhân với số này. Thay đổi này có thể có tác động tiêu cực như tăng tốc độ trò chơi hoặc làm hỏng chức năng quan trọng mà trò chơi không mong đợi thay đổi điều này! - dumpShadersCheckBox Bật xuất shader:\nĐể mục đích gỡ lỗi kỹ thuật, lưu shader của trò chơi vào một thư mục khi chúng được kết xuất. - nullGpuCheckBox Bật GPU Null:\nĐể mục đích gỡ lỗi kỹ thuật, vô hiệu hóa việc kết xuất trò chơi như thể không có card đồ họa. - gameFoldersBox Thư mục trò chơi:\nDanh sách các thư mục để kiểm tra các trò chơi đã cài đặt. - addFolderButton Thêm:\nThêm một thư mục vào danh sách. - removeFolderButton Xóa:\nXóa một thư mục khỏi danh sách. - debugDump Bật xuất gỡ lỗi:\nLưu biểu tượng nhập và xuất và thông tin tiêu đề tệp cho ứng dụng PS4 hiện đang chạy vào một thư mục. - vkValidationCheckBox Bật lớp xác thực Vulkan:\nKích hoạt một hệ thống xác thực trạng thái của bộ kết xuất Vulkan và ghi lại thông tin về trạng thái nội bộ của nó. Điều này sẽ giảm hiệu suất và có thể thay đổi hành vi của việc giả lập. - vkSyncValidationCheckBox Bật xác thực đồng bộ Vulkan:\nKích hoạt một hệ thống xác thực thời gian của nhiệm vụ kết xuất Vulkan. Điều này sẽ giảm hiệu suất và có thể thay đổi hành vi của việc giả lập. - rdocCheckBox Bật gỡ lỗi RenderDoc:\nNếu được kích hoạt, trình giả lập sẽ cung cấp tính tương thích với Renderdoc để cho phép bắt và phân tích khung hình hiện tại đang được kết xuất. + + CheatsPatches + + Cheats / Patches for + Cheats / Patches for + + + defaultTextEdit_MSG + Cheats/Patches là các tính năng thử nghiệm.\nHãy sử dụng cẩn thận.\n\nTải xuống các cheat riêng lẻ bằng cách chọn kho lưu trữ và nhấp vào nút tải xuống.\nTại tab Patches, bạn có thể tải xuống tất cả các patch cùng một lúc, chọn cái nào bạn muốn sử dụng và lưu lựa chọn của mình.\n\nVì chúng tôi không phát triển Cheats/Patches,\nxin vui lòng báo cáo các vấn đề cho tác giả cheat.\n\nBạn đã tạo ra một cheat mới? Truy cập:\nhttps://github.com/shadps4-emu/ps4_cheats + + + No Image Available + Không có hình ảnh + + + Serial: + Số seri: + + + Version: + Phiên bản: + + + Size: + Kích thước: + + + Select Cheat File: + Chọn tệp Cheat: + + + Repository: + Kho lưu trữ: + + + Download Cheats + Tải xuống Cheat + + + Delete File + Xóa tệp + + + No files selected. + Không có tệp nào được chọn. + + + You can delete the cheats you don't want after downloading them. + Bạn có thể xóa các cheat không muốn sau khi tải xuống. + + + Do you want to delete the selected file?\n%1 + Bạn có muốn xóa tệp đã chọn?\n%1 + + + Select Patch File: + Chọn tệp Bản vá: + + + Download Patches + Tải xuống Bản vá + + + Save + Lưu + + + Cheats + Cheat + + + Patches + Bản vá + + + Error + Lỗi + + + No patch selected. + Không có bản vá nào được chọn. + + + Unable to open files.json for reading. + Không thể mở files.json để đọc. + + + No patch file found for the current serial. + Không tìm thấy tệp bản vá cho số seri hiện tại. + + + Unable to open the file for reading. + Không thể mở tệp để đọc. + + + Unable to open the file for writing. + Không thể mở tệp để ghi. + + + Failed to parse XML: + Không thể phân tích XML: + + + Success + Thành công + + + Options saved successfully. + Các tùy chọn đã được lưu thành công. + + + Invalid Source + Nguồn không hợp lệ + + + The selected source is invalid. + Nguồn đã chọn không hợp lệ. + + + File Exists + Tệp đã tồn tại + + + File already exists. Do you want to replace it? + Tệp đã tồn tại. Bạn có muốn thay thế nó không? + + + Failed to save file: + Không thể lưu tệp: + + + Failed to download file: + Không thể tải xuống tệp: + + + Cheats Not Found + Không tìm thấy Cheat + + + CheatsNotFound_MSG + Không tìm thấy Cheat cho trò chơi này trong phiên bản kho lưu trữ đã chọn,hãy thử kho lưu trữ khác hoặc phiên bản khác của trò chơi. + + + Cheats Downloaded Successfully + Cheat đã tải xuống thành công + + + CheatsDownloadedSuccessfully_MSG + Bạn đã tải xuống các cheat thành công. Cho phiên bản trò chơi này từ kho lưu trữ đã chọn. Bạn có thể thử tải xuống từ kho lưu trữ khác, nếu có, bạn cũng có thể sử dụng bằng cách chọn tệp từ danh sách. + + + Failed to save: + Không thể lưu: + + + Failed to download: + Không thể tải xuống: + + + Download Complete + Tải xuống hoàn tất + + + DownloadComplete_MSG + Bản vá đã tải xuống thành công! Tất cả các bản vá có sẵn cho tất cả các trò chơi đã được tải xuống, không cần tải xuống riêng lẻ cho mỗi trò chơi như trong Cheat. Nếu bản vá không xuất hiện, có thể là nó không tồn tại cho số seri và phiên bản cụ thể của trò chơi. + + + Failed to parse JSON data from HTML. + Không thể phân tích dữ liệu JSON từ HTML. + + + Failed to retrieve HTML page. + Không thể lấy trang HTML. + + + The game is in version: %1 + Trò chơi đang ở phiên bản: %1 + + + The downloaded patch only works on version: %1 + Patch đã tải về chỉ hoạt động trên phiên bản: %1 + + + You may need to update your game. + Bạn có thể cần cập nhật trò chơi của mình. + + + Incompatibility Notice + Thông báo không tương thích + + + Failed to open file: + Không thể mở tệp: + + + XML ERROR: + LỖI XML: + + + Failed to open files.json for writing + Không thể mở files.json để ghi + + + Author: + Tác giả: + + + Directory does not exist: + Thư mục không tồn tại: + + + Failed to open files.json for reading. + Không thể mở files.json để đọc. + + + Name: + Tên: + + + Can't apply cheats before the game is started + Không thể áp dụng cheat trước khi trò chơi bắt đầu. + + GameListFrame - Icon Biểu tượng - Name Tên - Serial Số seri - Compatibility Compatibility - Region Khu vực - Firmware Phần mềm - Size Kích thước - Version Phiên bản - Path Đường dẫn - Play Time Thời gian chơi - Never Played Never Played - h h - m m - s s - Compatibility is untested Compatibility is untested - Game does not initialize properly / crashes the emulator Game does not initialize properly / crashes the emulator - Game boots, but only displays a blank screen Game boots, but only displays a blank screen - Game displays an image but does not go past the menu Game displays an image but does not go past the menu - Game has game-breaking glitches or unplayable performance Game has game-breaking glitches or unplayable performance - Game can be completed with playable performance and no major glitches Game can be completed with playable performance and no major glitches @@ -1508,127 +1210,102 @@ CheckUpdate - Auto Updater Trình cập nhật tự động - Error Lỗi - Network error: Lỗi mạng: - Failed to parse update information. Không thể phân tích thông tin cập nhật. - No pre-releases found. Không tìm thấy bản phát hành trước. - Invalid release data. Dữ liệu bản phát hành không hợp lệ. - No download URL found for the specified asset. Không tìm thấy URL tải xuống cho tài sản đã chỉ định. - Your version is already up to date! Phiên bản của bạn đã được cập nhật! - Update Available Có bản cập nhật - Update Channel Kênh Cập Nhật - Current Version Phiên bản hiện tại - Latest Version Phiên bản mới nhất - Do you want to update? Bạn có muốn cập nhật không? - Show Changelog Hiện nhật ký thay đổi - Check for Updates at Startup Kiểm tra cập nhật khi khởi động - Update Cập nhật - No Không - Hide Changelog Ẩn nhật ký thay đổi - Changes Thay đổi - Network error occurred while trying to access the URL Xảy ra lỗi mạng khi cố gắng truy cập URL - Download Complete Tải xuống hoàn tất - The update has been downloaded, press OK to install. Bản cập nhật đã được tải xuống, nhấn OK để cài đặt. - Failed to save the update file at Không thể lưu tệp cập nhật tại - Starting Update... Đang bắt đầu cập nhật... - Failed to create the update script file Không thể tạo tệp kịch bản cập nhật @@ -1636,29 +1313,24 @@ GameListUtils - B B - KB KB - MB MB - GB GB - TB TB - + \ No newline at end of file diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index fecb8857f..60e50a2bc 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -6,22 +6,18 @@ AboutDialog - About shadPS4 关于 shadPS4 - shadPS4 shadPS4 - shadPS4 is an experimental open-source emulator for the PlayStation 4. shadPS4 是一款实验性质的开源 PlayStation 4 模拟器软件。 - This software should not be used to play games you have not legally obtained. 本软件不得用于运行未经合法授权而获得的游戏。 @@ -29,7 +25,6 @@ ElfViewer - Open Folder 打开文件夹 @@ -37,17 +32,14 @@ GameInfoClass - Loading game list, please wait :3 加载游戏列表中, 请稍等 :3 - Cancel 取消 - Loading... 加载中... @@ -55,12 +47,10 @@ InstallDirSelect - shadPS4 - Choose directory shadPS4 - 选择文件目录 - Select which directory you want to install to. 选择你想要安装到的目录。 @@ -68,27 +58,22 @@ GameInstallDialog - shadPS4 - Choose directory shadPS4 - 选择文件目录 - Directory to install games 要安装游戏的目录 - Browse 浏览 - Error 错误 - The value for location to install games is not valid. 游戏安装位置无效。 @@ -96,167 +81,134 @@ GuiContextMenus - Create Shortcut 创建快捷方式 - Cheats / Patches 作弊码/补丁 - SFO Viewer SFO 查看器 - Trophy Viewer 奖杯查看器 - Open Folder... 打开文件夹... - Open Game Folder 打开游戏文件夹 - Open Save Data Folder 打开存档数据文件夹 - Open Log Folder 打开日志文件夹 - Copy info... 复制信息... - Copy Name 复制名称 - Copy Serial 复制序列号 - Copy All 复制全部 - Delete... 删除... - Delete Game 删除游戏 - Delete Update 删除更新 - Delete DLC 删除 DLC - Compatibility... 兼容性... - Update database 更新数据库 - View report 查看报告 - Submit a report 提交报告 - Shortcut creation 创建快捷方式 - Shortcut created successfully! 创建快捷方式成功! - Error 错误 - Error creating shortcut! 创建快捷方式出错! - Install PKG 安装 PKG - Game 游戏 - requiresEnableSeparateUpdateFolder_MSG 这个功能需要“启用单独的更新目录”配置选项才能正常运行,如果你想要使用这个功能,请启用它。 - This game has no update to delete! 这个游戏没有更新可以删除! - - + Update 更新 - This game has no DLC to delete! 这个游戏没有 DLC 可以删除! - DLC DLC - Delete %1 删除 %1 - Are you sure you want to delete %1's %2 directory? 你确定要删除 %1 的%2目录? @@ -264,205 +216,285 @@ MainWindow - Open/Add Elf Folder 打开/添加 Elf 文件夹 - Install Packages (PKG) 安装 Packages (PKG) - Boot Game 启动游戏 - Check for Updates 检查更新 - About shadPS4 关于 shadPS4 - Configure... 设置... - Install application from a .pkg file 从 .pkg 文件安装应用程序 - Recent Games 最近启动的游戏 - Exit 退出 - Exit shadPS4 退出 shadPS4 - Exit the application. 退出应用程序。 - Show Game List 显示游戏列表 - Game List Refresh 刷新游戏列表 - Tiny 微小 - Small - Medium - Large - List View 列表视图 - Grid View 表格视图 - Elf Viewer Elf 查看器 - Game Install Directory 游戏安装目录 - Download Cheats/Patches 下载作弊码/补丁 - Dump Game List 导出游戏列表 - PKG Viewer PKG 查看器 - Search... 搜索... - File 文件 - View 显示 - Game List Icons 游戏列表图标 - Game List Mode 游戏列表模式 - Settings 设置 - Utils 工具 - Themes 主题 - Help 帮助 - Dark Dark - Light Light - Green Green - Blue Blue - Violet Violet - toolBar 工具栏 + + Game List + 游戏列表 + + + * Unsupported Vulkan Version + * 不支持的 Vulkan 版本 + + + Download Cheats For All Installed Games + 下载所有已安装游戏的作弊码 + + + Download Patches For All Games + 下载所有游戏的补丁 + + + Download Complete + 下载完成 + + + You have downloaded cheats for all the games you have installed. + 您已下载了所有已安装游戏的作弊码。 + + + Patches Downloaded Successfully! + 补丁下载成功! + + + All Patches available for all games have been downloaded. + 所有游戏的可用补丁都已下载。 + + + Games: + 游戏: + + + PKG File (*.PKG) + PKG 文件 (*.PKG) + + + ELF files (*.bin *.elf *.oelf) + ELF 文件 (*.bin *.elf *.oelf) + + + Game Boot + 启动游戏 + + + Only one file can be selected! + 只能选择一个文件! + + + PKG Extraction + PKG 解压 + + + Patch detected! + 检测到补丁! + + + PKG and Game versions match: + PKG 和游戏版本匹配: + + + Would you like to overwrite? + 您想要覆盖吗? + + + PKG Version %1 is older than installed version: + PKG 版本 %1 比已安装版本更旧: + + + Game is installed: + 游戏已安装: + + + Would you like to install Patch: + 您想安装补丁吗: + + + DLC Installation + DLC 安装 + + + Would you like to install DLC: %1? + 您想安装 DLC:%1 吗? + + + DLC already installed: + DLC 已经安装: + + + Game already installed + 游戏已经安装 + + + PKG is a patch, please install the game first! + PKG 是一个补丁,请先安装游戏! + + + PKG ERROR + PKG 错误 + + + Extracting PKG %1/%2 + 正在解压 PKG %1/%2 + + + Extraction Finished + 解压完成 + + + Game successfully installed at %1 + 游戏成功安装在 %1 + + + File doesn't appear to be a valid PKG file + 文件似乎不是有效的 PKG 文件 + PKGViewer - Open Folder 打开文件夹 @@ -470,7 +502,6 @@ TrophyViewer - Trophy Viewer 奖杯查看器 @@ -478,1029 +509,700 @@ SettingsDialog - Settings 设置 - General 常规 - System 系统 - Console Language 主机语言 - Emulator Language 模拟器语言 - Emulator 模拟器 - Enable Fullscreen 启用全屏 - Enable Separate Update Folder 启用单独的更新目录 - Show Splash 显示启动画面 - Is PS4 Pro 模拟 PS4 Pro - Enable Discord Rich Presence 启用 Discord Rich Presence - Username 用户名 - Trophy Key Trophy Key - Trophy Trophy - Logger 日志 - Log Type 日志类型 - Log Filter 日志过滤 - Input 输入 - Cursor 光标 - Hide Cursor 隐藏光标 - Hide Cursor Idle Timeout 光标隐藏闲置时长 - s - Controller 手柄 - Back Button Behavior 返回按钮行为 - Graphics 图像 - Graphics Device 图形设备 - Width 宽度 - Height 高度 - Vblank Divider Vblank Divider - Advanced 高级 - Enable Shaders Dumping 启用着色器转储 - Enable NULL GPU 启用 NULL GPU - Paths 路径 - Game Folders 游戏文件夹 - Add... 添加... - Remove 删除 - Debug 调试 - Enable Debug Dumping 启用调试转储 - Enable Vulkan Validation Layers 启用 Vulkan 验证层 - Enable Vulkan Synchronization Validation 启用 Vulkan 同步验证 - Enable RenderDoc Debugging 启用 RenderDoc 调试 - Update 更新 - Check for Updates at Startup 启动时检查更新 - Update Channel 更新频道 - Check for Updates 检查更新 - GUI Settings 界面设置 - Disable Trophy Pop-ups 禁止弹出奖杯 - Play title music 播放标题音乐 - Update Compatibility Database On Startup 启动时更新兼容性数据库 - Game Compatibility 游戏兼容性 - Display Compatibility Data 显示兼容性数据 - Update Compatibility Database 更新兼容性数据库 - Volume 音量 - Audio Backend 音频后端 - - - MainWindow - - Game List - 游戏列表 - - - - * Unsupported Vulkan Version - * 不支持的 Vulkan 版本 - - - - Download Cheats For All Installed Games - 下载所有已安装游戏的作弊码 - - - - Download Patches For All Games - 下载所有游戏的补丁 - - - - Download Complete - 下载完成 - - - - You have downloaded cheats for all the games you have installed. - 您已下载了所有已安装游戏的作弊码。 - - - - Patches Downloaded Successfully! - 补丁下载成功! - - - - All Patches available for all games have been downloaded. - 所有游戏的可用补丁都已下载。 - - - - Games: - 游戏: - - - - PKG File (*.PKG) - PKG 文件 (*.PKG) - - - - ELF files (*.bin *.elf *.oelf) - ELF 文件 (*.bin *.elf *.oelf) - - - - Game Boot - 启动游戏 - - - - Only one file can be selected! - 只能选择一个文件! - - - - PKG Extraction - PKG 解压 - - - - Patch detected! - 检测到补丁! - - - - PKG and Game versions match: - PKG 和游戏版本匹配: - - - - Would you like to overwrite? - 您想要覆盖吗? - - - - PKG Version %1 is older than installed version: - PKG 版本 %1 比已安装版本更旧: - - - - Game is installed: - 游戏已安装: - - - - Would you like to install Patch: - 您想安装补丁吗: - - - - DLC Installation - DLC 安装 - - - - Would you like to install DLC: %1? - 您想安装 DLC:%1 吗? - - - - DLC already installed: - DLC 已经安装: - - - - Game already installed - 游戏已经安装 - - - - PKG is a patch, please install the game first! - PKG 是一个补丁,请先安装游戏! - - - - PKG ERROR - PKG 错误 - - - - Extracting PKG %1/%2 - 正在解压 PKG %1/%2 - - - - Extraction Finished - 解压完成 - - - - Game successfully installed at %1 - 游戏成功安装在 %1 - - - - File doesn't appear to be a valid PKG file - 文件似乎不是有效的 PKG 文件 - - - - CheatsPatches - - - Cheats / Patches for - 作弊码/补丁: - - - - defaultTextEdit_MSG - 作弊码/补丁是实验性的。\n请小心使用。\n\n通过选择存储库并点击下载按钮,下载该游戏的作弊码。\n在“补丁”选项卡中,您可以一次性下载所有补丁,选择要使用的补丁并保存选择。\n\n由于我们不开发作弊码/补丁,\n请将问题报告给作弊码/补丁的作者。\n\n创建了新的作弊码/补丁?欢迎提交到我们的仓库:\nhttps://github.com/shadps4-emu/ps4_cheats - - - - No Image Available - 没有可用的图片 - - - - Serial: - 序列号: - - - - Version: - 版本: - - - - Size: - 大小: - - - - Select Cheat File: - 选择作弊码文件: - - - - Repository: - 存储库: - - - - Download Cheats - 下载作弊码 - - - - Delete File - 删除文件 - - - - No files selected. - 没有选择文件。 - - - - You can delete the cheats you don't want after downloading them. - 您可以在下载后删除不想要的作弊码。 - - - - Do you want to delete the selected file?\n%1 - 您要删除选中的文件吗?\n%1 - - - - Select Patch File: - 选择补丁文件: - - - - Download Patches - 下载补丁 - - - Save 保存 - - Cheats - 作弊码 - - - - Patches - 补丁 - - - - Error - 错误 - - - - No patch selected. - 没有选择补丁。 - - - - Unable to open files.json for reading. - 无法打开 files.json 进行读取。 - - - - No patch file found for the current serial. - 未找到当前序列号的补丁文件。 - - - - Unable to open the file for reading. - 无法打开文件进行读取。 - - - - Unable to open the file for writing. - 无法打开文件进行写入。 - - - - Failed to parse XML: - 解析 XML 失败: - - - - Success - 成功 - - - - Options saved successfully. - 选项已成功保存。 - - - - Invalid Source - 无效的来源 - - - - The selected source is invalid. - 选择的来源无效。 - - - - File Exists - 文件已存在 - - - - File already exists. Do you want to replace it? - 文件已存在,您要替换它吗? - - - - Failed to save file: - 保存文件失败: - - - - Failed to download file: - 下载文件失败: - - - - Cheats Not Found - 未找到作弊码 - - - - CheatsNotFound_MSG - 在所选存储库的版本中找不到该游戏的作弊码,请尝试其他存储库或游戏版本。 - - - - Cheats Downloaded Successfully - 作弊码下载成功 - - - - CheatsDownloadedSuccessfully_MSG - 您已从所选存储库中成功下载了该游戏版本的作弊码。您还可以尝试从其他存储库下载,或通过从列表中选择文件来使用它们。 - - - - Failed to save: - 保存失败: - - - - Failed to download: - 下载失败: - - - - Download Complete - 下载完成 - - - - DownloadComplete_MSG - 补丁下载成功!所有可用的补丁已下载完成,无需像作弊码那样单独下载每个游戏的补丁。如果补丁没有出现,可能是该补丁不适用于当前游戏的序列号和版本。 - - - - Failed to parse JSON data from HTML. - 无法解析 HTML 中的 JSON 数据。 - - - - Failed to retrieve HTML page. - 无法获取 HTML 页面。 - - - - The game is in version: %1 - 游戏版本:%1 - - - - The downloaded patch only works on version: %1 - 下载的补丁仅适用于版本:%1 - - - - You may need to update your game. - 您可能需要更新您的游戏。 - - - - Incompatibility Notice - 不兼容通知 - - - - Failed to open file: - 无法打开文件: - - - - XML ERROR: - XML 错误: - - - - Failed to open files.json for writing - 无法打开 files.json 进行写入 - - - - Author: - 作者: - - - - Directory does not exist: - 目录不存在: - - - - Failed to open files.json for reading. - 无法打开 files.json 进行读取。 - - - - Name: - 名称: - - - - Can't apply cheats before the game is started - 在游戏启动之前无法应用作弊码。 - - - - SettingsDialog - - - Save - 保存 - - - Apply 应用 - Restore Defaults 恢复默认 - Close 关闭 - Point your mouse at an option to display its description. 将鼠标指针指向选项以显示其描述。 - consoleLanguageGroupBox 主机语言:\n设置 PS4 游戏中使用的语言。\n建议设置为支持的语言,这将因地区而异。 - emulatorLanguageGroupBox 模拟器语言:\n设置模拟器用户界面的语言。 - fullscreenCheckBox 启用全屏:\n以全屏模式启动游戏。\n您可以按 F11 键切换回窗口模式。 - separateUpdatesCheckBox 启用单独的更新目录:\n启用安装游戏更新到一个单独的目录中以更便于管理。 - showSplashCheckBox 显示启动画面:\n在游戏启动时显示游戏的启动画面(特殊图像)。 - ps4proCheckBox 模拟 PS4 Pro:\n使模拟器作为 PS4 Pro 运行,可以在支持的游戏中激活特殊功能。 - discordRPCCheckbox 启用 Discord Rich Presence:\n在您的 Discord 个人资料上显示模拟器图标和相关信息。 - userName 用户名:\n设置 PS4 帐户的用户名,某些游戏中可能会显示此名称。 - TrophyKey Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. - logTypeGroupBox 日志类型:\n设置日志窗口输出的同步方式以提高性能。可能会对模拟产生不良影响。 - logFilter 日志过滤器:\n过滤日志,仅打印特定信息。\n例如:"Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" 级别: Trace, Debug, Info, Warning, Error, Critical - 按此顺序,特定级别将静默列表中所有先前的级别,并记录所有后续级别。 - updaterGroupBox 更新:\nRelease:每月发布的官方版本可能非常过时,但更可靠且经过测试。\nNightly:包含所有最新功能和修复的开发版本,但可能包含错误且稳定性较低。 - GUIgroupBox 播放标题音乐:\n如果游戏支持,在图形界面选择游戏时播放特殊音乐。 - disableTrophycheckBox 禁止弹出奖杯:\n禁用游戏内奖杯通知。可以在奖杯查看器中继续跟踪奖杯进度(在主窗口中右键点击游戏)。 - hideCursorGroupBox 隐藏光标:\n选择光标何时消失:\n从不: 始终显示光标。\闲置: 光标在闲置若干秒后消失。\n始终: 始终显示光标。 - idleTimeoutGroupBox 光标隐藏闲置时长:\n光标自动隐藏之前的闲置时长。 - backButtonBehaviorGroupBox 返回按钮行为:\n设置手柄的返回按钮模拟在 PS4 触控板上指定位置的点击。 - enableCompatibilityCheckBox 显示兼容性数据:\n在列表视图中显示游戏兼容性信息。启用“启动时更新兼容性数据库”以获取最新信息。 - checkCompatibilityOnStartupCheckBox 启动时更新兼容性数据库:\n当 shadPS4 启动时自动更新兼容性数据库。 - updateCompatibilityButton 更新兼容性数据库:\n立即更新兼容性数据库。 - Never 从不 - Idle 闲置 - Always 始终 - Touchpad Left 触控板左侧 - Touchpad Right 触控板右侧 - Touchpad Center 触控板中间 - None - graphicsAdapterGroupBox 图形设备:\n在具有多个 GPU 的系统中,从下拉列表中选择要使用的 GPU,\n或者选择“自动选择”由模拟器决定。 - resolutionLayout 宽度/高度:\n设置启动游戏时的窗口大小,游戏过程中可以调整。\n这与游戏内的分辨率不同。 - heightDivider Vblank Divider:\n模拟器刷新的帧率会乘以此数字。改变此项可能会导致游戏速度加快,或破坏游戏中不期望此变化的关键功能! - dumpShadersCheckBox 启用着色器转储:\n用于技术调试,在渲染期间将游戏着色器保存到文件夹中。 - nullGpuCheckBox 启用 NULL GPU:\n用于技术调试,禁用游戏渲染,就像没有显卡一样。 - gameFoldersBox 游戏文件夹:\n检查已安装游戏的文件夹列表。 - addFolderButton 添加:\n将文件夹添加到列表。 - removeFolderButton 移除:\n从列表中移除文件夹。 - debugDump 启用调试转储:\n将当前正在运行的 PS4 程序的导入和导出符号及文件头信息保存到目录中。 - vkValidationCheckBox 启用 Vulkan 验证层:\n启用一个系统来验证 Vulkan 渲染器的状态并记录其内部状态的信息。\n这将降低性能并可能改变模拟的行为。 - vkSyncValidationCheckBox 启用 Vulkan 同步验证:\n启用一个系统来验证 Vulkan 渲染任务的时间。\n这将降低性能并可能改变模拟的行为。 - rdocCheckBox 启用 RenderDoc 调试:\n启用后模拟器将提供与 Renderdoc 的兼容性,允许在渲染过程中捕获和分析当前渲染的帧。 + + CheatsPatches + + Cheats / Patches for + 作弊码/补丁: + + + defaultTextEdit_MSG + 作弊码/补丁是实验性的。\n请小心使用。\n\n通过选择存储库并点击下载按钮,下载该游戏的作弊码。\n在“补丁”选项卡中,您可以一次性下载所有补丁,选择要使用的补丁并保存选择。\n\n由于我们不开发作弊码/补丁,\n请将问题报告给作弊码/补丁的作者。\n\n创建了新的作弊码/补丁?欢迎提交到我们的仓库:\nhttps://github.com/shadps4-emu/ps4_cheats + + + No Image Available + 没有可用的图片 + + + Serial: + 序列号: + + + Version: + 版本: + + + Size: + 大小: + + + Select Cheat File: + 选择作弊码文件: + + + Repository: + 存储库: + + + Download Cheats + 下载作弊码 + + + Delete File + 删除文件 + + + No files selected. + 没有选择文件。 + + + You can delete the cheats you don't want after downloading them. + 您可以在下载后删除不想要的作弊码。 + + + Do you want to delete the selected file?\n%1 + 您要删除选中的文件吗?\n%1 + + + Select Patch File: + 选择补丁文件: + + + Download Patches + 下载补丁 + + + Save + 保存 + + + Cheats + 作弊码 + + + Patches + 补丁 + + + Error + 错误 + + + No patch selected. + 没有选择补丁。 + + + Unable to open files.json for reading. + 无法打开 files.json 进行读取。 + + + No patch file found for the current serial. + 未找到当前序列号的补丁文件。 + + + Unable to open the file for reading. + 无法打开文件进行读取。 + + + Unable to open the file for writing. + 无法打开文件进行写入。 + + + Failed to parse XML: + 解析 XML 失败: + + + Success + 成功 + + + Options saved successfully. + 选项已成功保存。 + + + Invalid Source + 无效的来源 + + + The selected source is invalid. + 选择的来源无效。 + + + File Exists + 文件已存在 + + + File already exists. Do you want to replace it? + 文件已存在,您要替换它吗? + + + Failed to save file: + 保存文件失败: + + + Failed to download file: + 下载文件失败: + + + Cheats Not Found + 未找到作弊码 + + + CheatsNotFound_MSG + 在所选存储库的版本中找不到该游戏的作弊码,请尝试其他存储库或游戏版本。 + + + Cheats Downloaded Successfully + 作弊码下载成功 + + + CheatsDownloadedSuccessfully_MSG + 您已从所选存储库中成功下载了该游戏版本的作弊码。您还可以尝试从其他存储库下载,或通过从列表中选择文件来使用它们。 + + + Failed to save: + 保存失败: + + + Failed to download: + 下载失败: + + + Download Complete + 下载完成 + + + DownloadComplete_MSG + 补丁下载成功!所有可用的补丁已下载完成,无需像作弊码那样单独下载每个游戏的补丁。如果补丁没有出现,可能是该补丁不适用于当前游戏的序列号和版本。 + + + Failed to parse JSON data from HTML. + 无法解析 HTML 中的 JSON 数据。 + + + Failed to retrieve HTML page. + 无法获取 HTML 页面。 + + + The game is in version: %1 + 游戏版本:%1 + + + The downloaded patch only works on version: %1 + 下载的补丁仅适用于版本:%1 + + + You may need to update your game. + 您可能需要更新您的游戏。 + + + Incompatibility Notice + 不兼容通知 + + + Failed to open file: + 无法打开文件: + + + XML ERROR: + XML 错误: + + + Failed to open files.json for writing + 无法打开 files.json 进行写入 + + + Author: + 作者: + + + Directory does not exist: + 目录不存在: + + + Failed to open files.json for reading. + 无法打开 files.json 进行读取。 + + + Name: + 名称: + + + Can't apply cheats before the game is started + 在游戏启动之前无法应用作弊码。 + + GameListFrame - Icon 图标 - Name 名称 - Serial 序列号 - Compatibility 兼容性 - Region 区域 - Firmware 固件 - Size 大小 - Version 版本 - Path 路径 - Play Time 游戏时间 - Never Played 未玩过 - h 小时 - m 分钟 - s - Compatibility is untested 兼容性未经测试 - Game does not initialize properly / crashes the emulator 游戏无法正确初始化/模拟器崩溃 - Game boots, but only displays a blank screen 游戏启动,但只显示白屏 - Game displays an image but does not go past the menu 游戏显示图像但无法通过菜单页面 - Game has game-breaking glitches or unplayable performance 游戏有严重的 Bug 或太卡无法游玩 - Game can be completed with playable performance and no major glitches 游戏能在可玩的性能下完成且没有重大 Bug @@ -1508,127 +1210,102 @@ CheckUpdate - Auto Updater 自动更新程序 - Error 错误 - Network error: 网络错误: - Failed to parse update information. 无法解析更新信息。 - No pre-releases found. 未找到预发布版本。 - Invalid release data. 无效的发布数据。 - No download URL found for the specified asset. 未找到指定资源的下载地址。 - Your version is already up to date! 您的版本已经是最新的! - Update Available 可用更新 - Update Channel 更新频道 - Current Version 当前版本 - Latest Version 最新版本 - Do you want to update? 您想要更新吗? - Show Changelog 显示更新日志 - Check for Updates at Startup 启动时检查更新 - Update 更新 - No - Hide Changelog 隐藏更新日志 - Changes 更新日志 - Network error occurred while trying to access the URL 尝试访问网址时发生网络错误 - Download Complete 下载完成 - The update has been downloaded, press OK to install. 更新已下载,请按 OK 安装。 - Failed to save the update file at 无法保存更新文件到 - Starting Update... 正在开始更新... - Failed to create the update script file 无法创建更新脚本文件 @@ -1636,29 +1313,24 @@ GameListUtils - B B - KB KB - MB MB - GB GB - TB TB - + - + \ No newline at end of file diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index 293ed81a6..d1e822b5c 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -6,22 +6,18 @@ AboutDialog - About shadPS4 About shadPS4 - shadPS4 shadPS4 - shadPS4 is an experimental open-source emulator for the PlayStation 4. shadPS4 is an experimental open-source emulator for the PlayStation 4. - This software should not be used to play games you have not legally obtained. This software should not be used to play games you have not legally obtained. @@ -29,7 +25,6 @@ ElfViewer - Open Folder Open Folder @@ -37,17 +32,14 @@ GameInfoClass - Loading game list, please wait :3 Loading game list, please wait :3 - Cancel Cancel - Loading... Loading... @@ -55,12 +47,10 @@ InstallDirSelect - shadPS4 - Choose directory shadPS4 - Choose directory - Select which directory you want to install to. Select which directory you want to install to. @@ -68,27 +58,22 @@ GameInstallDialog - shadPS4 - Choose directory shadPS4 - Choose directory - Directory to install games Directory to install games - Browse Browse - Error Error - The value for location to install games is not valid. The value for location to install games is not valid. @@ -96,167 +81,134 @@ GuiContextMenus - Create Shortcut Create Shortcut - Cheats / Patches Zuòbì / Xiūbǔ chéngshì - SFO Viewer SFO Viewer - Trophy Viewer Trophy Viewer - Open Folder... 打開資料夾... - Open Game Folder 打開遊戲資料夾 - Open Save Data Folder 打開存檔資料夾 - Open Log Folder 打開日誌資料夾 - Copy info... Copy info... - Copy Name Copy Name - Copy Serial Copy Serial - Copy All Copy All - Delete... Delete... - Delete Game Delete Game - Delete Update Delete Update - Delete DLC Delete DLC - Compatibility... Compatibility... - Update database Update database - View report View report - Submit a report Submit a report - Shortcut creation Shortcut creation - Shortcut created successfully! Shortcut created successfully! - Error Error - Error creating shortcut! Error creating shortcut! - Install PKG Install PKG - Game Game - requiresEnableSeparateUpdateFolder_MSG This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. - This game has no update to delete! This game has no update to delete! - - + Update Update - This game has no DLC to delete! This game has no DLC to delete! - DLC DLC - Delete %1 Delete %1 - Are you sure you want to delete %1's %2 directory? Are you sure you want to delete %1's %2 directory? @@ -264,205 +216,285 @@ MainWindow - Open/Add Elf Folder Open/Add Elf Folder - Install Packages (PKG) Install Packages (PKG) - Boot Game Boot Game - Check for Updates 檢查更新 - About shadPS4 About shadPS4 - Configure... Configure... - Install application from a .pkg file Install application from a .pkg file - Recent Games Recent Games - Exit Exit - Exit shadPS4 Exit shadPS4 - Exit the application. Exit the application. - Show Game List Show Game List - Game List Refresh Game List Refresh - Tiny Tiny - Small Small - Medium Medium - Large Large - List View List View - Grid View Grid View - Elf Viewer Elf Viewer - Game Install Directory Game Install Directory - Download Cheats/Patches Xiàzài Zuòbì / Xiūbǔ chéngshì - Dump Game List Dump Game List - PKG Viewer PKG Viewer - Search... Search... - File File - View View - Game List Icons Game List Icons - Game List Mode Game List Mode - Settings Settings - Utils Utils - Themes Themes - Help 幫助 - Dark Dark - Light Light - Green Green - Blue Blue - Violet Violet - toolBar toolBar + + Game List + 遊戲列表 + + + * Unsupported Vulkan Version + * 不支援的 Vulkan 版本 + + + Download Cheats For All Installed Games + 下載所有已安裝遊戲的作弊碼 + + + Download Patches For All Games + 下載所有遊戲的修補檔 + + + Download Complete + 下載完成 + + + You have downloaded cheats for all the games you have installed. + 您已經下載了所有已安裝遊戲的作弊碼。 + + + Patches Downloaded Successfully! + 修補檔下載成功! + + + All Patches available for all games have been downloaded. + 所有遊戲的修補檔已經下載完成。 + + + Games: + 遊戲: + + + PKG File (*.PKG) + PKG 檔案 (*.PKG) + + + ELF files (*.bin *.elf *.oelf) + ELF 檔案 (*.bin *.elf *.oelf) + + + Game Boot + 遊戲啟動 + + + Only one file can be selected! + 只能選擇一個檔案! + + + PKG Extraction + PKG 解壓縮 + + + Patch detected! + 檢測到補丁! + + + PKG and Game versions match: + PKG 和遊戲版本匹配: + + + Would you like to overwrite? + 您想要覆蓋嗎? + + + PKG Version %1 is older than installed version: + PKG 版本 %1 比已安裝版本更舊: + + + Game is installed: + 遊戲已安裝: + + + Would you like to install Patch: + 您想要安裝補丁嗎: + + + DLC Installation + DLC 安裝 + + + Would you like to install DLC: %1? + 您想要安裝 DLC: %1 嗎? + + + DLC already installed: + DLC 已經安裝: + + + Game already installed + 遊戲已經安裝 + + + PKG is a patch, please install the game first! + PKG 是修補檔,請先安裝遊戲! + + + PKG ERROR + PKG 錯誤 + + + Extracting PKG %1/%2 + 正在解壓縮 PKG %1/%2 + + + Extraction Finished + 解壓縮完成 + + + Game successfully installed at %1 + 遊戲成功安裝於 %1 + + + File doesn't appear to be a valid PKG file + 檔案似乎不是有效的 PKG 檔案 + PKGViewer - Open Folder Open Folder @@ -470,7 +502,6 @@ TrophyViewer - Trophy Viewer Trophy Viewer @@ -478,1029 +509,700 @@ SettingsDialog - Settings Settings - General General - System System - Console Language Console Language - Emulator Language Emulator Language - Emulator Emulator - Enable Fullscreen Enable Fullscreen - Enable Separate Update Folder Enable Separate Update Folder - Show Splash Show Splash - Is PS4 Pro Is PS4 Pro - Enable Discord Rich Presence 啟用 Discord Rich Presence - Username Username - Trophy Key Trophy Key - Trophy Trophy - Logger Logger - Log Type Log Type - Log Filter Log Filter - Input 輸入 - Cursor 游標 - Hide Cursor 隱藏游標 - Hide Cursor Idle Timeout 游標空閒超時隱藏 - s s - Controller 控制器 - Back Button Behavior 返回按鈕行為 - Graphics Graphics - Graphics Device Graphics Device - Width Width - Height Height - Vblank Divider Vblank Divider - Advanced Advanced - Enable Shaders Dumping Enable Shaders Dumping - Enable NULL GPU Enable NULL GPU - Paths 路徑 - Game Folders 遊戲資料夾 - Add... 添加... - Remove 刪除 - Debug Debug - Enable Debug Dumping Enable Debug Dumping - Enable Vulkan Validation Layers Enable Vulkan Validation Layers - Enable Vulkan Synchronization Validation Enable Vulkan Synchronization Validation - Enable RenderDoc Debugging Enable RenderDoc Debugging - Update 更新 - Check for Updates at Startup 啟動時檢查更新 - Update Channel 更新頻道 - Check for Updates 檢查更新 - GUI Settings 介面設置 - Disable Trophy Pop-ups Disable Trophy Pop-ups - Play title music 播放標題音樂 - Update Compatibility Database On Startup Update Compatibility Database On Startup - Game Compatibility Game Compatibility - Display Compatibility Data Display Compatibility Data - Update Compatibility Database Update Compatibility Database - Volume 音量 - Audio Backend Audio Backend - - - MainWindow - - Game List - 遊戲列表 - - - - * Unsupported Vulkan Version - * 不支援的 Vulkan 版本 - - - - Download Cheats For All Installed Games - 下載所有已安裝遊戲的作弊碼 - - - - Download Patches For All Games - 下載所有遊戲的修補檔 - - - - Download Complete - 下載完成 - - - - You have downloaded cheats for all the games you have installed. - 您已經下載了所有已安裝遊戲的作弊碼。 - - - - Patches Downloaded Successfully! - 修補檔下載成功! - - - - All Patches available for all games have been downloaded. - 所有遊戲的修補檔已經下載完成。 - - - - Games: - 遊戲: - - - - PKG File (*.PKG) - PKG 檔案 (*.PKG) - - - - ELF files (*.bin *.elf *.oelf) - ELF 檔案 (*.bin *.elf *.oelf) - - - - Game Boot - 遊戲啟動 - - - - Only one file can be selected! - 只能選擇一個檔案! - - - - PKG Extraction - PKG 解壓縮 - - - - Patch detected! - 檢測到補丁! - - - - PKG and Game versions match: - PKG 和遊戲版本匹配: - - - - Would you like to overwrite? - 您想要覆蓋嗎? - - - - PKG Version %1 is older than installed version: - PKG 版本 %1 比已安裝版本更舊: - - - - Game is installed: - 遊戲已安裝: - - - - Would you like to install Patch: - 您想要安裝補丁嗎: - - - - DLC Installation - DLC 安裝 - - - - Would you like to install DLC: %1? - 您想要安裝 DLC: %1 嗎? - - - - DLC already installed: - DLC 已經安裝: - - - - Game already installed - 遊戲已經安裝 - - - - PKG is a patch, please install the game first! - PKG 是修補檔,請先安裝遊戲! - - - - PKG ERROR - PKG 錯誤 - - - - Extracting PKG %1/%2 - 正在解壓縮 PKG %1/%2 - - - - Extraction Finished - 解壓縮完成 - - - - Game successfully installed at %1 - 遊戲成功安裝於 %1 - - - - File doesn't appear to be a valid PKG file - 檔案似乎不是有效的 PKG 檔案 - - - - CheatsPatches - - - Cheats / Patches for - Cheats / Patches for - - - - defaultTextEdit_MSG - 作弊/補丁為實驗性功能。\n請小心使用。\n\n透過選擇儲存庫並點擊下載按鈕來單獨下載作弊程式。\n在“補丁”標籤頁中,您可以一次下載所有補丁,選擇要使用的補丁並保存您的選擇。\n\n由於我們不開發作弊/補丁,\n請將問題報告給作弊程式的作者。\n\n創建了新的作弊程式?請訪問:\nhttps://github.com/shadps4-emu/ps4_cheats - - - - No Image Available - 沒有可用的圖片 - - - - Serial: - 序號: - - - - Version: - 版本: - - - - Size: - 大小: - - - - Select Cheat File: - 選擇作弊檔案: - - - - Repository: - 儲存庫: - - - - Download Cheats - 下載作弊碼 - - - - Delete File - 刪除檔案 - - - - No files selected. - 沒有選擇檔案。 - - - - You can delete the cheats you don't want after downloading them. - 您可以在下載後刪除不需要的作弊碼。 - - - - Do you want to delete the selected file?\n%1 - 您是否要刪除選定的檔案?\n%1 - - - - Select Patch File: - 選擇修補檔案: - - - - Download Patches - 下載修補檔 - - - Save 儲存 - - Cheats - 作弊碼 - - - - Patches - 修補檔 - - - - Error - 錯誤 - - - - No patch selected. - 未選擇修補檔。 - - - - Unable to open files.json for reading. - 無法打開 files.json 進行讀取。 - - - - No patch file found for the current serial. - 找不到當前序號的修補檔。 - - - - Unable to open the file for reading. - 無法打開檔案進行讀取。 - - - - Unable to open the file for writing. - 無法打開檔案進行寫入。 - - - - Failed to parse XML: - 解析 XML 失敗: - - - - Success - 成功 - - - - Options saved successfully. - 選項已成功儲存。 - - - - Invalid Source - 無效的來源 - - - - The selected source is invalid. - 選擇的來源無效。 - - - - File Exists - 檔案已存在 - - - - File already exists. Do you want to replace it? - 檔案已存在。您是否希望替換它? - - - - Failed to save file: - 無法儲存檔案: - - - - Failed to download file: - 無法下載檔案: - - - - Cheats Not Found - 未找到作弊碼 - - - - CheatsNotFound_MSG - 在此版本的儲存庫中未找到該遊戲的作弊碼,請嘗試另一個儲存庫或不同版本的遊戲。 - - - - Cheats Downloaded Successfully - 作弊碼下載成功 - - - - CheatsDownloadedSuccessfully_MSG - 您已成功下載該遊戲版本的作弊碼 從選定的儲存庫中。 您可以嘗試從其他儲存庫下載,如果可用,您也可以選擇從列表中選擇檔案來使用它。 - - - - Failed to save: - 儲存失敗: - - - - Failed to download: - 下載失敗: - - - - Download Complete - 下載完成 - - - - DownloadComplete_MSG - 修補檔下載成功!所有遊戲的修補檔已下載完成,無需像作弊碼那樣為每個遊戲單獨下載。如果補丁未顯示,可能是該補丁不適用於特定的序號和遊戲版本。 - - - - Failed to parse JSON data from HTML. - 無法從 HTML 解析 JSON 數據。 - - - - Failed to retrieve HTML page. - 無法檢索 HTML 頁面。 - - - - The game is in version: %1 - 遊戲版本: %1 - - - - The downloaded patch only works on version: %1 - 下載的補丁僅適用於版本: %1 - - - - You may need to update your game. - 您可能需要更新遊戲。 - - - - Incompatibility Notice - 不相容通知 - - - - Failed to open file: - 無法打開檔案: - - - - XML ERROR: - XML 錯誤: - - - - Failed to open files.json for writing - 無法打開 files.json 進行寫入 - - - - Author: - 作者: - - - - Directory does not exist: - 目錄不存在: - - - - Failed to open files.json for reading. - 無法打開 files.json 進行讀取。 - - - - Name: - 名稱: - - - - Can't apply cheats before the game is started - 在遊戲開始之前無法應用作弊。 - - - - SettingsDialog - - - Save - 儲存 - - - Apply 應用 - Restore Defaults 還原預設值 - Close 關閉 - Point your mouse at an option to display its description. 將鼠標指向選項以顯示其描述。 - consoleLanguageGroupBox 主機語言:\n設定PS4遊戲使用的語言。\n建議將其設置為遊戲支持的語言,這會因地區而異。 - emulatorLanguageGroupBox 模擬器語言:\n設定模擬器的用戶介面的語言。 - fullscreenCheckBox 啟用全螢幕:\n自動將遊戲視窗設置為全螢幕模式。\n可以按F11鍵進行切換。 - separateUpdatesCheckBox Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. - showSplashCheckBox 顯示啟動畫面:\n在遊戲啟動時顯示遊戲的啟動畫面(特殊圖片)。 - ps4proCheckBox 為PS4 Pro:\n讓模擬器像PS4 PRO一樣運作,這可能啟用支持此功能的遊戲中的特殊功能。 - discordRPCCheckbox 啟用 Discord Rich Presence:\n在您的 Discord 個人檔案上顯示模擬器圖標和相關信息。 - userName 用戶名:\n設定PS4帳號的用戶名,某些遊戲中可能會顯示。 - TrophyKey Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. - logTypeGroupBox 日誌類型:\n設定是否同步日誌窗口的輸出以提高性能。可能對模擬產生不良影響。 - logFilter 日誌過濾器:\n過濾日誌以僅打印特定信息。\n範例:"Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" 等級: Trace, Debug, Info, Warning, Error, Critical - 以此順序,特定級別靜音所有前面的級別,並記錄其後的每個級別。 - updaterGroupBox 更新:\nRelease: 每月發布的官方版本,可能非常舊,但更可靠且經過測試。\nNightly: 開發版本,擁有所有最新的功能和修復,但可能包含錯誤,穩定性較差。 - GUIgroupBox 播放標題音樂:\n如果遊戲支持,啟用在GUI中選擇遊戲時播放特殊音樂。 - disableTrophycheckBox Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). - hideCursorGroupBox 隱藏游標:\n選擇游標何時消失:\n從不: 您將始終看到滑鼠。\n閒置: 設定在閒置後消失的時間。\n始終: 您將永遠看不到滑鼠。 - idleTimeoutGroupBox 設定滑鼠在閒置後消失的時間。 - backButtonBehaviorGroupBox 返回按鈕行為:\n設定控制器的返回按鈕模擬在 PS4 觸控板上指定位置的觸碰。 - enableCompatibilityCheckBox Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. - checkCompatibilityOnStartupCheckBox Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. - updateCompatibilityButton Update Compatibility Database:\nImmediately update the compatibility database. - Never 從不 - Idle 閒置 - Always 始終 - Touchpad Left 觸控板左側 - Touchpad Right 觸控板右側 - Touchpad Center 觸控板中間 - None - graphicsAdapterGroupBox 圖形設備:\n在多GPU系統中,從下拉列表中選擇模擬器將使用的GPU,\n或選擇「自動選擇」以自動確定。 - resolutionLayout 寬度/高度:\n設定模擬器啟動時的窗口大小,可以在遊戲過程中調整。\n這與遊戲內解析度不同。 - heightDivider Vblank分隔符:\n模擬器的幀速率將乘以這個數字。更改此數字可能會有不良影響,例如增加遊戲速度,或破壞不預期此變化的關鍵遊戲功能! - dumpShadersCheckBox 啟用著色器轉儲:\n為了技術調試,將遊戲的著色器在渲染時保存到文件夾中。 - nullGpuCheckBox 啟用空GPU:\n為了技術調試,禁用遊戲渲染,彷彿沒有顯示卡。 - gameFoldersBox 遊戲資料夾:\n檢查已安裝遊戲的資料夾列表。 - addFolderButton 添加:\n將資料夾添加到列表。 - removeFolderButton 移除:\n從列表中移除資料夾。 - debugDump 啟用調試轉儲:\n將當前運行的PS4程序的輸入和輸出符號及文件頭信息保存到目錄中。 - vkValidationCheckBox 啟用Vulkan驗證層:\n啟用一個系統來驗證Vulkan渲染器的狀態並記錄其內部狀態的信息。這將降低性能並可能改變模擬行為。 - vkSyncValidationCheckBox 啟用Vulkan同步驗證:\n啟用一個系統來驗證Vulkan渲染任務的時間。這將降低性能並可能改變模擬行為。 - rdocCheckBox 啟用RenderDoc調試:\n如果啟用,模擬器將提供與Renderdoc的兼容性,以允許捕獲和分析當前渲染的幀。 + + CheatsPatches + + Cheats / Patches for + Cheats / Patches for + + + defaultTextEdit_MSG + 作弊/補丁為實驗性功能。\n請小心使用。\n\n透過選擇儲存庫並點擊下載按鈕來單獨下載作弊程式。\n在“補丁”標籤頁中,您可以一次下載所有補丁,選擇要使用的補丁並保存您的選擇。\n\n由於我們不開發作弊/補丁,\n請將問題報告給作弊程式的作者。\n\n創建了新的作弊程式?請訪問:\nhttps://github.com/shadps4-emu/ps4_cheats + + + No Image Available + 沒有可用的圖片 + + + Serial: + 序號: + + + Version: + 版本: + + + Size: + 大小: + + + Select Cheat File: + 選擇作弊檔案: + + + Repository: + 儲存庫: + + + Download Cheats + 下載作弊碼 + + + Delete File + 刪除檔案 + + + No files selected. + 沒有選擇檔案。 + + + You can delete the cheats you don't want after downloading them. + 您可以在下載後刪除不需要的作弊碼。 + + + Do you want to delete the selected file?\n%1 + 您是否要刪除選定的檔案?\n%1 + + + Select Patch File: + 選擇修補檔案: + + + Download Patches + 下載修補檔 + + + Save + 儲存 + + + Cheats + 作弊碼 + + + Patches + 修補檔 + + + Error + 錯誤 + + + No patch selected. + 未選擇修補檔。 + + + Unable to open files.json for reading. + 無法打開 files.json 進行讀取。 + + + No patch file found for the current serial. + 找不到當前序號的修補檔。 + + + Unable to open the file for reading. + 無法打開檔案進行讀取。 + + + Unable to open the file for writing. + 無法打開檔案進行寫入。 + + + Failed to parse XML: + 解析 XML 失敗: + + + Success + 成功 + + + Options saved successfully. + 選項已成功儲存。 + + + Invalid Source + 無效的來源 + + + The selected source is invalid. + 選擇的來源無效。 + + + File Exists + 檔案已存在 + + + File already exists. Do you want to replace it? + 檔案已存在。您是否希望替換它? + + + Failed to save file: + 無法儲存檔案: + + + Failed to download file: + 無法下載檔案: + + + Cheats Not Found + 未找到作弊碼 + + + CheatsNotFound_MSG + 在此版本的儲存庫中未找到該遊戲的作弊碼,請嘗試另一個儲存庫或不同版本的遊戲。 + + + Cheats Downloaded Successfully + 作弊碼下載成功 + + + CheatsDownloadedSuccessfully_MSG + 您已成功下載該遊戲版本的作弊碼 從選定的儲存庫中。 您可以嘗試從其他儲存庫下載,如果可用,您也可以選擇從列表中選擇檔案來使用它。 + + + Failed to save: + 儲存失敗: + + + Failed to download: + 下載失敗: + + + Download Complete + 下載完成 + + + DownloadComplete_MSG + 修補檔下載成功!所有遊戲的修補檔已下載完成,無需像作弊碼那樣為每個遊戲單獨下載。如果補丁未顯示,可能是該補丁不適用於特定的序號和遊戲版本。 + + + Failed to parse JSON data from HTML. + 無法從 HTML 解析 JSON 數據。 + + + Failed to retrieve HTML page. + 無法檢索 HTML 頁面。 + + + The game is in version: %1 + 遊戲版本: %1 + + + The downloaded patch only works on version: %1 + 下載的補丁僅適用於版本: %1 + + + You may need to update your game. + 您可能需要更新遊戲。 + + + Incompatibility Notice + 不相容通知 + + + Failed to open file: + 無法打開檔案: + + + XML ERROR: + XML 錯誤: + + + Failed to open files.json for writing + 無法打開 files.json 進行寫入 + + + Author: + 作者: + + + Directory does not exist: + 目錄不存在: + + + Failed to open files.json for reading. + 無法打開 files.json 進行讀取。 + + + Name: + 名稱: + + + Can't apply cheats before the game is started + 在遊戲開始之前無法應用作弊。 + + GameListFrame - Icon 圖示 - Name 名稱 - Serial 序號 - Compatibility Compatibility - Region 區域 - Firmware 固件 - Size 大小 - Version 版本 - Path 路徑 - Play Time 遊玩時間 - Never Played Never Played - h h - m m - s s - Compatibility is untested Compatibility is untested - Game does not initialize properly / crashes the emulator Game does not initialize properly / crashes the emulator - Game boots, but only displays a blank screen Game boots, but only displays a blank screen - Game displays an image but does not go past the menu Game displays an image but does not go past the menu - Game has game-breaking glitches or unplayable performance Game has game-breaking glitches or unplayable performance - Game can be completed with playable performance and no major glitches Game can be completed with playable performance and no major glitches @@ -1508,127 +1210,102 @@ CheckUpdate - Auto Updater 自動更新程式 - Error 錯誤 - Network error: 網路錯誤: - Failed to parse update information. 無法解析更新資訊。 - No pre-releases found. 未找到預發布版本。 - Invalid release data. 無效的發行數據。 - No download URL found for the specified asset. 未找到指定資產的下載 URL。 - Your version is already up to date! 您的版本已經是最新的! - Update Available 可用更新 - Update Channel 更新頻道 - Current Version 當前版本 - Latest Version 最新版本 - Do you want to update? 您想要更新嗎? - Show Changelog 顯示變更日誌 - Check for Updates at Startup 啟動時檢查更新 - Update 更新 - No - Hide Changelog 隱藏變更日誌 - Changes 變更 - Network error occurred while trying to access the URL 嘗試訪問 URL 時發生網路錯誤 - Download Complete 下載完成 - The update has been downloaded, press OK to install. 更新已下載,按 OK 安裝。 - Failed to save the update file at 無法將更新文件保存到 - Starting Update... 正在開始更新... - Failed to create the update script file 無法創建更新腳本文件 @@ -1636,29 +1313,24 @@ GameListUtils - B B - KB KB - MB MB - GB GB - TB TB - + \ No newline at end of file From c08fc85b72786ad7cc2c688eefe75e50ab84b4ec Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Mon, 6 Jan 2025 21:00:07 -0800 Subject: [PATCH 369/549] renderer_vulkan: Fix null buffer views with wrong format. (#2079) --- src/video_core/buffer_cache/buffer_cache.cpp | 13 +--------- src/video_core/buffer_cache/buffer_cache.h | 5 ---- .../renderer_vulkan/vk_rasterizer.cpp | 24 ++++++++++++------- 3 files changed, 16 insertions(+), 26 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index d5ebd85fc..3a210957e 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -34,21 +34,10 @@ BufferCache::BufferCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& s // Ensure the first slot is used for the null buffer const auto null_id = - slot_buffers.insert(instance, scheduler, MemoryUsage::DeviceLocal, 0, ReadFlags, 1); + slot_buffers.insert(instance, scheduler, MemoryUsage::DeviceLocal, 0, ReadFlags, 16); ASSERT(null_id.index == 0); const vk::Buffer& null_buffer = slot_buffers[null_id].buffer; Vulkan::SetObjectName(instance.GetDevice(), null_buffer, "Null Buffer"); - - const vk::BufferViewCreateInfo null_view_ci = { - .buffer = null_buffer, - .format = vk::Format::eR8Unorm, - .offset = 0, - .range = VK_WHOLE_SIZE, - }; - const auto [null_view_result, null_view] = instance.GetDevice().createBufferView(null_view_ci); - ASSERT_MSG(null_view_result == vk::Result::eSuccess, "Failed to create null buffer view."); - null_buffer_view = null_view; - Vulkan::SetObjectName(instance.GetDevice(), null_buffer_view, "Null Buffer View"); } BufferCache::~BufferCache() = default; diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index c367795f1..f29a37b63 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -71,10 +71,6 @@ public: return slot_buffers[id]; } - [[nodiscard]] vk::BufferView& NullBufferView() { - return null_buffer_view; - } - /// Invalidates any buffer in the logical page range. void InvalidateMemory(VAddr device_addr, u64 size); @@ -160,7 +156,6 @@ private: std::shared_mutex mutex; Common::SlotVector slot_buffers; RangeSet gpu_modified_ranges; - vk::BufferView null_buffer_view; MemoryTracker memory_tracker; PageTable page_table; }; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 6e628239b..e8616550b 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -537,6 +537,7 @@ void Rasterizer::BindBuffers(const Shader::Info& stage, Shader::Backend::Binding } // Second pass to re-bind buffers that were updated after binding + auto& null_buffer = buffer_cache.GetBuffer(VideoCore::NULL_BUFFER_ID); for (u32 i = 0; i < buffer_bindings.size(); i++) { const auto& [buffer_id, vsharp] = buffer_bindings[i]; const auto& desc = stage.buffers[i]; @@ -548,7 +549,6 @@ void Rasterizer::BindBuffers(const Shader::Info& stage, Shader::Backend::Binding } else if (instance.IsNullDescriptorSupported()) { buffer_infos.emplace_back(VK_NULL_HANDLE, 0, VK_WHOLE_SIZE); } else { - auto& null_buffer = buffer_cache.GetBuffer(VideoCore::NULL_BUFFER_ID); buffer_infos.emplace_back(null_buffer.Handle(), 0, VK_WHOLE_SIZE); } } else { @@ -582,17 +582,19 @@ void Rasterizer::BindBuffers(const Shader::Info& stage, Shader::Backend::Binding ++binding.buffer; } - const auto null_buffer_view = - instance.IsNullDescriptorSupported() ? VK_NULL_HANDLE : buffer_cache.NullBufferView(); for (u32 i = 0; i < texbuffer_bindings.size(); i++) { const auto& [buffer_id, vsharp] = texbuffer_bindings[i]; const auto& desc = stage.texture_buffers[i]; - vk::BufferView& buffer_view = buffer_views.emplace_back(null_buffer_view); + // Fallback format for null buffer view; never used in valid buffer case. + const auto data_fmt = vsharp.GetDataFmt() != AmdGpu::DataFormat::FormatInvalid + ? vsharp.GetDataFmt() + : AmdGpu::DataFormat::Format8; + const u32 fmt_stride = AmdGpu::NumBits(data_fmt) >> 3; + vk::BufferView buffer_view; if (buffer_id) { const u32 alignment = instance.TexelBufferMinAlignment(); const auto [vk_buffer, offset] = buffer_cache.ObtainBuffer( vsharp.base_address, vsharp.GetSize(), desc.is_written, true, buffer_id); - const u32 fmt_stride = AmdGpu::NumBits(vsharp.GetDataFmt()) >> 3; const u32 buf_stride = vsharp.GetStride(); ASSERT_MSG(buf_stride % fmt_stride == 0, "Texel buffer stride must match format stride"); @@ -600,9 +602,8 @@ void Rasterizer::BindBuffers(const Shader::Info& stage, Shader::Backend::Binding const u32 adjust = offset - offset_aligned; ASSERT(adjust % fmt_stride == 0); push_data.AddTexelOffset(binding.buffer, buf_stride / fmt_stride, adjust / fmt_stride); - buffer_view = - vk_buffer->View(offset_aligned, vsharp.GetSize() + adjust, desc.is_written, - vsharp.GetDataFmt(), vsharp.GetNumberFmt()); + buffer_view = vk_buffer->View(offset_aligned, vsharp.GetSize() + adjust, + desc.is_written, data_fmt, vsharp.GetNumberFmt()); if (auto barrier = vk_buffer->GetBarrier(desc.is_written ? vk::AccessFlagBits2::eShaderWrite : vk::AccessFlagBits2::eShaderRead, @@ -612,6 +613,11 @@ void Rasterizer::BindBuffers(const Shader::Info& stage, Shader::Backend::Binding if (desc.is_written) { texture_cache.InvalidateMemoryFromGPU(vsharp.base_address, vsharp.GetSize()); } + } else if (instance.IsNullDescriptorSupported()) { + buffer_view = VK_NULL_HANDLE; + } else { + buffer_view = + null_buffer.View(0, fmt_stride, desc.is_written, data_fmt, vsharp.GetNumberFmt()); } set_writes.push_back({ @@ -621,7 +627,7 @@ void Rasterizer::BindBuffers(const Shader::Info& stage, Shader::Backend::Binding .descriptorCount = 1, .descriptorType = desc.is_written ? vk::DescriptorType::eStorageTexelBuffer : vk::DescriptorType::eUniformTexelBuffer, - .pTexelBufferView = &buffer_view, + .pTexelBufferView = &buffer_views.emplace_back(buffer_view), }); ++binding.buffer; } From 869bf85e9a99eadd5e4f2d9fefa4f2d8164db3ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A5IGA?= <164882787+Xphalnos@users.noreply.github.com> Date: Tue, 7 Jan 2025 08:07:41 +0100 Subject: [PATCH 370/549] CI: Switch to Windows 2025 & Reduce Warnings (#2052) --- .github/workflows/build.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3b5690438..5c4a34469 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,14 +14,14 @@ env: jobs: reuse: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 continue-on-error: true steps: - uses: actions/checkout@v4 - uses: fsfe/reuse-action@v5 clang-format: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 continue-on-error: true steps: - uses: actions/checkout@v4 @@ -39,7 +39,7 @@ jobs: run: ./.ci/clang-format.sh get-info: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 outputs: date: ${{ steps.vars.outputs.date }} shorthash: ${{ steps.vars.outputs.shorthash }} @@ -57,7 +57,7 @@ jobs: echo "fullhash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT windows-sdl: - runs-on: windows-latest + runs-on: windows-2025 needs: get-info steps: - uses: actions/checkout@v4 @@ -101,7 +101,7 @@ jobs: path: ${{github.workspace}}/build/shadPS4.exe windows-qt: - runs-on: windows-latest + runs-on: windows-2025 needs: get-info steps: - uses: actions/checkout@v4 From 7bf467c65304fb66bf96ee3e660032bfee8b4725 Mon Sep 17 00:00:00 2001 From: Daniel Nylander Date: Tue, 7 Jan 2025 08:55:53 +0100 Subject: [PATCH 371/549] Updated Swedish translation with additional strings (#2081) * Adding Swedish translation * Updated Swedish translation with additional strings Updated the Swedish translations using lupdate to found additional strings cd src/qt_gui/treanslations lupdate ../../../../shadPS4/ -tr-function-alias QT_TRANSLATE_NOOP+=TRANSLATE,QT_TRANSLATE_NOOP+=TRANSLATE_SV,QT_TRANSLATE_NOOP+=TRANSLATE_STR,QT_TRANSLATE_NOOP+=TRANSLATE_FS,QT_TRANSLATE_N_NOOP3+=TRANSLATE_FMT,QT_TRANSLATE_NOOP+=TRANSLATE_NOOP,translate+=TRANSLATE_PLURAL_STR,translate+=TRANSLATE_PLURAL_FS -no-obsolete -locations none -source-language en -ts sv.ts * Update sv.ts --- src/qt_gui/translations/sv.ts | 2739 +++++++++++++++++---------------- 1 file changed, 1409 insertions(+), 1330 deletions(-) diff --git a/src/qt_gui/translations/sv.ts b/src/qt_gui/translations/sv.ts index 756119ba4..c1c1204f8 100644 --- a/src/qt_gui/translations/sv.ts +++ b/src/qt_gui/translations/sv.ts @@ -4,1333 +4,1412 @@ - AboutDialog - - About shadPS4 - Om shadPS4 - - - shadPS4 - shadPS4 - - - shadPS4 is an experimental open-source emulator for the PlayStation 4. - shadPS4 är en experimentell emulator för PlayStation 4 baserad på öppen källkod. - - - This software should not be used to play games you have not legally obtained. - Denna programvara bör inte användas för att spela spel som du inte legalt äger. - - - - ElfViewer - - Open Folder - Öppna mapp - - - - GameInfoClass - - Loading game list, please wait :3 - Läser in spellistan, vänta :3 - - - Cancel - Avbryt - - - Loading... - Läser in... - - - - InstallDirSelect - - shadPS4 - Choose directory - shadPS4 - Välj katalog - - - Select which directory you want to install to. - Välj vilken katalog som du vill installera till. - - - - GameInstallDialog - - shadPS4 - Choose directory - shadPS4 - Välj katalog - - - Directory to install games - Katalog att installera spel till - - - Browse - Bläddra - - - Error - Fel - - - The value for location to install games is not valid. - Värdet för platsen att installera spel till är inte giltig. - - - - GuiContextMenus - - Create Shortcut - Skapa genväg - - - Cheats / Patches - Fusk / Patchar - - - SFO Viewer - SFO-visare - - - Trophy Viewer - Trofé-visare - - - Open Folder... - Öppna mapp... - - - Open Game Folder - Öppna spelmapp - - - Open Save Data Folder - Öppna mapp för sparat data - - - Open Log Folder - Öppna loggmapp - - - Copy info... - Kopiera till... - - - Copy Name - Kopiera namn - - - Copy Serial - Kopiera serienummer - - - Copy All - Kopiera alla - - - Delete... - Ta bort... - - - Delete Game - Ta bort spel - - - Delete Update - Ta bort uppdatering - - - Delete DLC - Ta bort DLC - - - Compatibility... - Kompatibilitet... - - - Update database - Uppdatera databasen - - - View report - Visa rapport - - - Submit a report - Skicka en rapport - - - Shortcut creation - Skapa genväg - - - Shortcut created successfully! - Genvägen skapades! - - - Error - Fel - - - Error creating shortcut! - Fel vid skapandet av genväg! - - - Install PKG - Installera PKG - - - Game - Spel - - - requiresEnableSeparateUpdateFolder_MSG - Denna funktion kräver konfigurationsalternativet 'Aktivera separat uppdateringsmapp' för att fungera. Om du vill använda denna funktion, aktivera den - - - This game has no update to delete! - Detta spel har ingen uppdatering att ta bort! - - - Update - Uppdatera - - - This game has no DLC to delete! - Detta spel har inga DLC att ta bort! - - - DLC - DLC - - - Delete %1 - Ta bort %1 - - - Are you sure you want to delete %1's %2 directory? - Är du säker på att du vill ta bort %1s %2-katalog? - - - - MainWindow - - Open/Add Elf Folder - Öppna/Lägg till Elf-mapp - - - Install Packages (PKG) - Installera paket (PKG) - - - Boot Game - Starta spel - - - Check for Updates - Leta efter uppdateringar - - - About shadPS4 - Om shadPS4 - - - Configure... - Konfigurera... - - - Install application from a .pkg file - Installera program från en .pkg-fil - - - Recent Games - Senaste spel - - - Exit - Avsluta - - - Exit shadPS4 - Avsluta shadPS4 - - - Exit the application. - Avsluta programmet. - - - Show Game List - Visa spellista - - - Game List Refresh - Uppdatera spellista - - - Tiny - Mycket små - - - Small - Små - - - Medium - Medel - - - Large - Stora - - - List View - Listvy - - - Grid View - Rutnätsvy - - - Elf Viewer - Elf-visare - - - Game Install Directory - Installationskatalog för spel - - - Download Cheats/Patches - Hämta fusk/patchar - - - Dump Game List - Dumpa spellista - - - PKG Viewer - PKG-visare - - - Search... - Sök... - - - File - Arkiv - - - View - Visa - - - Game List Icons - Ikoner för spellista - - - Game List Mode - Läge för spellista - - - Settings - Inställningar - - - Utils - Verktyg - - - Themes - Teman - - - Help - Hjälp - - - Dark - Mörk - - - Light - Ljus - - - Green - Grön - - - Blue - Blå - - - Violet - Lila - - - toolBar - toolBar - - - Game List - Spellista - - - * Unsupported Vulkan Version - * Vulkan-versionen stöds inte - - - Download Cheats For All Installed Games - Hämta fusk för alla installerade spel - - - Download Patches For All Games - Hämta patchar för alla spel - - - Download Complete - Hämtning färdig - - - You have downloaded cheats for all the games you have installed. - Du har hämtat fusk till alla spelen som du har installerade. - - - Patches Downloaded Successfully! - Patchar hämtades ner! - - - All Patches available for all games have been downloaded. - Alla patchar tillgängliga för alla spel har hämtats ner. - - - Games: - Spel: - - - PKG File (*.PKG) - PKG-fil (*.PKG) - - - ELF files (*.bin *.elf *.oelf) - ELF-filer (*.bin *.elf *.oelf) - - - Game Boot - Starta spel - - - Only one file can be selected! - Endast en fil kan väljas! - - - PKG Extraction - PKG-extrahering - - - Patch detected! - Patch upptäcktes! - - - PKG and Game versions match: - PKG och spelversioner matchar: - - - Would you like to overwrite? - Vill du skriva över? - - - PKG Version %1 is older than installed version: - PKG-versionen %1 är äldre än installerad version: - - - Game is installed: - Spelet är installerat: - - - Would you like to install Patch: - Vill du installera patch: - - - DLC Installation - DLC-installation - - - Would you like to install DLC: %1? - Vill du installera DLC: %1? - - - DLC already installed: - DLC redan installerat: - - - Game already installed - Spelet redan installerat - - - PKG is a patch, please install the game first! - PKH är en patch. Installera spelet först! - - - PKG ERROR - PKG-FEL - - - Extracting PKG %1/%2 - Extraherar PKG %1/%2 - - - Extraction Finished - Extrahering färdig - - - Game successfully installed at %1 - Spelet installerades i %1 - - - File doesn't appear to be a valid PKG file - Filen verkar inte vara en giltig PKG-fil - - - - PKGViewer - - Open Folder - Öppna mapp - - - - TrophyViewer - - Trophy Viewer - Trofé-visare - - - - SettingsDialog - - Settings - Inställningar - - - General - Allmänt - - - System - System - - - Console Language - Konsollspråk - - - Emulator Language - Emulatorspråk - - - Emulator - Emulator - - - Enable Fullscreen - Aktivera helskärm - - - Enable Separate Update Folder - Aktivera separat uppdateringsmapp - - - Show Splash - Visa startskärm - - - Is PS4 Pro - Är PS4 Pro - - - Enable Discord Rich Presence - Aktivera Discord Rich Presence - - - Username - Användarnamn - - - Trophy Key - Trofényckel - - - Trophy - Trofé - - - Logger - Loggning - - - Log Type - Loggtyp - - - Log Filter - Loggfilter - - - Input - Inmatning - - - Cursor - Pekare - - - Hide Cursor - Dölj pekare - - - Hide Cursor Idle Timeout - Dölj pekare vid overksam - - - s - s - - - Controller - Handkontroller - - - Back Button Behavior - Beteende för bakåtknapp - - - Graphics - Grafik - - - Graphics Device - Grafikenhet - - - Width - Bredd - - - Height - Höjd - - - Vblank Divider - Vblank Divider - - - Advanced - Avancerat - - - Enable Shaders Dumping - Aktivera Shaders Dumping - - - Enable NULL GPU - Aktivera NULL GPU - - - Paths - Sökvägar - - - Game Folders - Spelmappar - - - Add... - Lägg till... - - - Remove - Ta bort - - - Debug - Felsök - - - Enable Debug Dumping - Aktivera felsökningsdumpning - - - Enable Vulkan Validation Layers - Aktivera Vulkan Validation Layers - - - Enable Vulkan Synchronization Validation - Aktivera Vulkan Synchronization Validation - - - Enable RenderDoc Debugging - Aktivera RenderDoc-felsökning - - - Update - Uppdatera - - - Check for Updates at Startup - Leta efter uppdateringar vid uppstart - - - Update Channel - Uppdateringskanal - - - Check for Updates - Leta efter uppdateringar - - - GUI Settings - Gränssnittsinställningar - - - Disable Trophy Pop-ups - Inaktivera popup för troféer - - - Play title music - Spela titelmusik - - - Update Compatibility Database On Startup - Uppdatera databas vid uppstart - - - Game Compatibility - Spelkompatibilitet - - - Display Compatibility Data - Visa kompatibilitetsdata - - - Update Compatibility Database - Uppdatera kompatibilitetsdatabasen - - - Volume - Volym - - - Audio Backend - Ljudbakände - - - Save - Spara - - - Apply - Verkställ - - - Restore Defaults - Återställ till standard - - - Close - Stäng - - - Point your mouse at an option to display its description. - Peka din mus på ett alternativ för att visa dess beskrivning. - - - consoleLanguageGroupBox - Konsollspråk:\nStäller in språket som PS4-spelet använder.\nDet rekommenderas att ställa in detta till ett språk som spelet har stöd för, vilket kan skilja sig mellan regioner - - - emulatorLanguageGroupBox - Emulatorspråk:\nStäller in språket för emulatorns användargränssnitt - - - fullscreenCheckBox - Aktivera helskärm:\nStäller automatiskt in spelfönstret till helskämsläget.\nDetta kan växlas genom att trycka på F11-tangenten - - - separateUpdatesCheckBox - Aktivera separat uppdateringsmapp:\nAktiverar installation av speluppdateringar i en separat mapp för enkel hantering.\nDetta kan skapas manuellt genom att lägga till uppackad uppdatering till spelmappen med namnet "CUSA00000-UPDATE" där CUSA ID matchar spelets id - - - showSplashCheckBox - Visa startskärm:\nVisar spelets startskärm (en speciell bild) när spelet startas - - - ps4proCheckBox - Är PS4 Pro:\nGör att emulatorn fungerar som en PS4 PRO, som kan aktivera speciella funktioner i spel som har stöds för det - - - discordRPCCheckbox - Aktivera Discord Rich Presence:\nVisar emulatorikonen och relevant information på din Discord-profil - - - userName - Användarnamn:\nStäller in PS4ans användarkonto, som kan visas av vissa spel - - - TrophyKey - Trofényckel:\nNyckel som används för att avkryptera troféer. Måste hämtas från din konsoll (jailbroken).\nMåste innehålla endast hex-tecken - - - logTypeGroupBox - Loggtyp:\nStäller in huruvida synkronisering av utdata för loggfönstret för prestanda. Kan ha inverkan på emulationen - - - logFilter - Loggfilter:\nFiltrera loggen till att endast skriva ut specifik information.\nExempel: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical"\nNivåer: Trace, Debug, Info, Warning, Error, Critical - i den ordningen, en specifik nivå som tystar alla nivåer före den i listan och loggar allting efter den - - - updaterGroupBox - updaterGroupBox - - - GUIgroupBox - Spela upp titelmusik:\nOm ett spel har stöd för det kan speciell musik spelas upp från spelet i gränssnittet - - - disableTrophycheckBox - Inaktivera popup för troféer:\nInaktivera troféeaviseringar i spel. Troféförlopp kan fortfarande följas med Troféevisaren (högerklicka på spelet i huvudfönstret) - - - hideCursorGroupBox - Dölj pekare:\nVälj när muspekaren ska försvinna:\nAldrig: Du kommer alltid se muspekaren.\nOverksam: Ställ in en tid för när den ska försvinna efter den inte använts.\nAlltid: du kommer aldrig se muspekaren - - - idleTimeoutGroupBox - Dölj pekare vid overksam:\nLängden (sekunder) efter vilken som muspekaren som har varit overksam döljer sig själv - - - backButtonBehaviorGroupBox - Beteende för bakåtknapp:\nStäller in handkontrollerns bakåtknapp för att emulera ett tryck på angivna positionen på PS4ns touchpad - - - enableCompatibilityCheckBox - Visa kompatibilitetsdata:\nVisar information om spelkompatibilitet i tabellvyn. Aktivera "Uppdatera kompatibilitet vid uppstart" för att få uppdaterad information - - - checkCompatibilityOnStartupCheckBox - Uppdatera kompatibilitet vid uppstart:\nUppdatera automatiskt kompatibilitetsdatabasen när shadPS4 startar - - - updateCompatibilityButton - Uppdatera kompatibilitetsdatabasen:\nUppdaterar kompatibilitetsdatabasen direkt - - - Never - Aldrig - - - Idle - Overksam - - - Always - Alltid - - - Touchpad Left - Touchpad vänster - - - Touchpad Right - Touchpad höger - - - Touchpad Center - Touchpad mitten - - - None - Ingen - - - graphicsAdapterGroupBox - Grafikenhet:\nFör system med flera GPUer kan du välja den GPU som emulatorn ska använda från rullgardinsmenyn,\neller välja "Auto Select" för att automatiskt bestämma det - - - resolutionLayout - Bredd/Höjd:\nStäller in storleken för emulatorfönstret vid uppstart, som kan storleksändras under spelning.\nDetta är inte det samma som spelupplösningen - - - heightDivider - Vblank Divider:\nBildfrekvensen som emulatorn uppdaterar vid multipliceras med detta tal. Ändra detta kan ha inverkan på saker, såsom ökad spelhastighet eller göra sönder kritisk spelfunktionalitet, som inte förväntar sig denna ändring - - - dumpShadersCheckBox - Aktivera Shaders Dumping:\nFör teknisk felsökning, sparar spelets shaders till en mapp när de renderas - - - nullGpuCheckBox - Aktivera Null GPU:\nFör teknisk felsökning, inaktiverar spelrenderingen som om det inte fanns något grafikkort - - - gameFoldersBox - Spelmappar:\nListan över mappar att leta i efter installerade spel - - - addFolderButton - Aktivera separat uppdateringsmapp:\nAktiverar installation av speluppdateringar till en separat mapp för enkel hantering.\nDetta kan manuellt skapas genom att lägga till den uppackade uppdateringen till spelmappen med namnet "CUSA00000-UPDATE" där CUSA ID matchar spelets id - - - removeFolderButton - Ta bort:\nTa bort en mapp från listan - - - debugDump - Aktivera felsökningsdumpning:\nSparar import och export av symboler och fil-header-information för aktuellt körande PS4-program till en katalog - - - vkValidationCheckBox - Aktivera Vulkan Validation Layers:\nAktiverar ett system som validerar tillståndet för Vulkan renderer och loggar information om dess interna tillstånd.\nDetta kommer minska prestandan och antagligen ändra beteendet för emuleringen - - - vkSyncValidationCheckBox - Aktivera Vulkan Synchronization Validation:\nAktiverar ett system som validerar timing för Vulkan rendering tasks.\nDetta kommer minska prestandan och antagligen ändra beteendet för emuleringen - - - rdocCheckBox - Aktivera RenderDoc-felsökning:\nOm aktiverad kommer emulatorn att tillhandahålla kompatibilitet med Renderdoc för att tillåta fångst och analys för aktuell renderad bildruta - - - - CheatsPatches - - Cheats / Patches for - Fusk / Patchar för - - - defaultTextEdit_MSG - Fusk/Patchar är experimentella.\nAnvänd med försiktighet.\n\nHämta fusk individuellt genom att välja förrådet och klicka på hämtningsknappen.\nUnder Patchar-fliken kan du hämta alla patchar på en gång, välj vilken du vill använda och spara ditt val.\n\nEftersom vi inte utvecklar fusk eller patchar,\nrapportera gärna problem till fuskets upphovsperson.\n\nSkapat ett nytt fusk? Besök:\nhttps://github.com/shadps4-emu/ps4_cheats - - - No Image Available - Ingen bild tillgänglig - - - Serial: - Serienummer: - - - Version: - Version: - - - Size: - Storlek: - - - Select Cheat File: - Välj fuskfil: - - - Repository: - Förråd: - - - Download Cheats - Hämta fusk - - - Delete File - Ta bort fil - - - No files selected. - Inga filer valda. - - - You can delete the cheats you don't want after downloading them. - Du kan ta bort fusk som du inte vill ha efter de hämtats ner. - - - Do you want to delete the selected file?\n%1 - Vill du ta bort markerade filen?\n%1 - - - Select Patch File: - Välj patchfil: - - - Download Patches - Hämta patchar - - - Save - Spara - - - Cheats - Fusk - - - Patches - Patchar - - - Error - Fel - - - No patch selected. - Ingen patch vald. - - - Unable to open files.json for reading. - Kunde inte öppna files.json för läsning. - - - No patch file found for the current serial. - Ingen patchfil hittades för aktuella serienumret. - - - Unable to open the file for reading. - Kunde inte öppna filen för läsning. - - - Unable to open the file for writing. - Kunde inte öppna filen för skrivning. - - - Failed to parse XML: - Misslyckades med att tolka XML: - - - Success - Lyckades - - - Options saved successfully. - Inställningarna sparades. - - - Invalid Source - Ogiltig källa - - - The selected source is invalid. - Vald källa är ogiltig. - - - File Exists - Filen finns - - - File already exists. Do you want to replace it? - Filen finns redan. Vill du ersätta den? - - - Failed to save file: - Misslyckades med att spara fil: - - - Failed to download file: - Misslyckades med att hämta filen: - - - Cheats Not Found - Fusk hittades inte - - - CheatsNotFound_MSG - Inga fusk hittades för detta spel i denna version av det valda förrådet. Prova ett annat förråd eller en annan version av spelet - - - Cheats Downloaded Successfully - Fusk hämtades ner - - - CheatsDownloadedSuccessfully_MSG - Du har hämtat ner fusken för denna version av spelet från valt förråd. Du kan försöka att hämta från andra förråd, om de är tillgängliga så kan det vara möjligt att använda det genom att välja det genom att välja filen från listan - - - Failed to save: - Misslyckades med att spara: - - - Failed to download: - Misslyckades med att hämta: - - - Download Complete - Hämtning färdig - - - DownloadComplete_MSG - Patchhämtningen är färdig! Alla patchar tillgängliga för alla spel har hämtats och de behövs inte hämtas individuellt för varje spel som med fusk. Om patchen inte dyker upp kan det bero på att den inte finns för det specifika serienumret och versionen av spelet - - - Failed to parse JSON data from HTML. - Misslyckades med att tolka JSON-data från HTML. - - - Failed to retrieve HTML page. - Misslyckades med att hämta HTML-sida. - - - The game is in version: %1 - Spelet är i version: %1 - - - The downloaded patch only works on version: %1 - Hämtad patch fungerar endast på version: %1 - - - You may need to update your game. - Du kan behöva uppdatera ditt spel. - - - Incompatibility Notice - Inkompatibilitetsmeddelande - - - Failed to open file: - Misslyckades med att öppna filen: - - - XML ERROR: - XML-FEL: - - - Failed to open files.json for writing - Misslyckades med att öppna files.json för skrivning - - - Author: - Upphovsperson: - - - Directory does not exist: - Katalogen finns inte: - - - Failed to open files.json for reading. - Misslyckades med att öppna files.json för läsning. - - - Name: - Namn: - - - Can't apply cheats before the game is started - Kan inte tillämpa fusk innan spelet är startat - - - - GameListFrame - - Icon - Ikon - - - Name - Namn - - - Serial - Serienummer - - - Compatibility - Kompatibilitet - - - Region - Region - - - Firmware - Firmware - - - Size - Storlek - - - Version - Version - - - Path - Sökväg - - - Play Time - Speltid - - - Never Played - Aldrig spelat - - - h - h - - - m - m - - - s - s - - - Compatibility is untested - Kompatibilitet är otestat - - - Game does not initialize properly / crashes the emulator - Spelet initierar inte korrekt / kraschar emulatorn - - - Game boots, but only displays a blank screen - Spelet startar men visar endast en blank skärm - - - Game displays an image but does not go past the menu - Spelet visar en bild men kommer inte förbi menyn - - - Game has game-breaking glitches or unplayable performance - Spelet har allvarliga problem eller ospelbar prestanda - - - Game can be completed with playable performance and no major glitches - Spelet kan spelas klart med spelbar prestanda och utan större problem - - - - CheckUpdate - - Auto Updater - Automatisk uppdatering - - - Error - Fel - - - Network error: - Nätverksfel: - - - Failed to parse update information. - Misslyckades med att tolka uppdateringsinformationen. - - - No pre-releases found. - Inga förutgåva hittades. - - - Invalid release data. - Ogiltig release-data. - - - No download URL found for the specified asset. - Ingen hämtnings-URL hittades för angiven tillgång. - - - Your version is already up to date! - Din version är redan den senaste! - - - Update Available - Uppdatering tillgänglig - - - Update Channel - Uppdateringskanal - - - Current Version - Aktuell version - - - Latest Version - Senaste version - - - Do you want to update? - Vill du uppdatera? - - - Show Changelog - Visa ändringslogg - - - Check for Updates at Startup - Leta efter uppdateringar vid uppstart - - - Update - Uppdatera - - - No - Nej - - - Hide Changelog - Dölj ändringslogg - - - Changes - Ändringar - - - Network error occurred while trying to access the URL - Nätverksfel inträffade vid försök att komma åt URL:en - - - Download Complete - Hämtning färdig - - - The update has been downloaded, press OK to install. - Uppdateringen har hämtats. Tryck på Ok för att installera. - - - Failed to save the update file at - Misslyckades med att spara uppdateringsfilen i - - - Starting Update... - Startar uppdatering... - - - Failed to create the update script file - Misslyckades med att skapa uppdateringsskriptfil - - - - GameListUtils - - B - B - - - KB - KB - - - MB - MB - - - GB - GB - - - TB - TB - - - \ No newline at end of file + AboutDialog + + About shadPS4 + Om shadPS4 + + + shadPS4 + shadPS4 + + + shadPS4 is an experimental open-source emulator for the PlayStation 4. + shadPS4 är en experimentell emulator för PlayStation 4 baserad på öppen källkod. + + + This software should not be used to play games you have not legally obtained. + Denna programvara bör inte användas för att spela spel som du inte legalt äger. + + + + CheatsPatches + + Cheats / Patches for + Fusk / Patchar för + + + defaultTextEdit_MSG + Fusk/Patchar är experimentella.\nAnvänd med försiktighet.\n\nHämta fusk individuellt genom att välja förrådet och klicka på hämtningsknappen.\nUnder Patchar-fliken kan du hämta alla patchar på en gång, välj vilken du vill använda och spara ditt val.\n\nEftersom vi inte utvecklar fusk eller patchar,\nrapportera gärna problem till fuskets upphovsperson.\n\nSkapat ett nytt fusk? Besök:\nhttps://github.com/shadps4-emu/ps4_cheats + + + No Image Available + Ingen bild tillgänglig + + + Serial: + Serienummer: + + + Version: + Version: + + + Size: + Storlek: + + + Select Cheat File: + Välj fuskfil: + + + Repository: + Förråd: + + + Download Cheats + Hämta fusk + + + Delete File + Ta bort fil + + + No files selected. + Inga filer valda. + + + You can delete the cheats you don't want after downloading them. + Du kan ta bort fusk som du inte vill ha efter de hämtats ner. + + + Do you want to delete the selected file?\n%1 + Vill du ta bort markerade filen?\n%1 + + + Select Patch File: + Välj patchfil: + + + Download Patches + Hämta patchar + + + Save + Spara + + + Cheats + Fusk + + + Patches + Patchar + + + Error + Fel + + + No patch selected. + Ingen patch vald. + + + Unable to open files.json for reading. + Kunde inte öppna files.json för läsning. + + + No patch file found for the current serial. + Ingen patchfil hittades för aktuella serienumret. + + + Unable to open the file for reading. + Kunde inte öppna filen för läsning. + + + Unable to open the file for writing. + Kunde inte öppna filen för skrivning. + + + Failed to parse XML: + Misslyckades med att tolka XML: + + + Success + Lyckades + + + Options saved successfully. + Inställningarna sparades. + + + Invalid Source + Ogiltig källa + + + The selected source is invalid. + Vald källa är ogiltig. + + + File Exists + Filen finns + + + File already exists. Do you want to replace it? + Filen finns redan. Vill du ersätta den? + + + Failed to save file: + Misslyckades med att spara fil: + + + Failed to download file: + Misslyckades med att hämta filen: + + + Cheats Not Found + Fusk hittades inte + + + CheatsNotFound_MSG + Inga fusk hittades för detta spel i denna version av det valda förrådet. Prova ett annat förråd eller en annan version av spelet + + + Cheats Downloaded Successfully + Fusk hämtades ner + + + CheatsDownloadedSuccessfully_MSG + Du har hämtat ner fusken för denna version av spelet från valt förråd. Du kan försöka att hämta från andra förråd, om de är tillgängliga så kan det vara möjligt att använda det genom att välja det genom att välja filen från listan + + + Failed to save: + Misslyckades med att spara: + + + Failed to download: + Misslyckades med att hämta: + + + Download Complete + Hämtning färdig + + + DownloadComplete_MSG + Patchhämtningen är färdig! Alla patchar tillgängliga för alla spel har hämtats och de behövs inte hämtas individuellt för varje spel som med fusk. Om patchen inte dyker upp kan det bero på att den inte finns för det specifika serienumret och versionen av spelet + + + Failed to parse JSON data from HTML. + Misslyckades med att tolka JSON-data från HTML. + + + Failed to retrieve HTML page. + Misslyckades med att hämta HTML-sida. + + + The game is in version: %1 + Spelet är i version: %1 + + + The downloaded patch only works on version: %1 + Hämtad patch fungerar endast på version: %1 + + + You may need to update your game. + Du kan behöva uppdatera ditt spel. + + + Incompatibility Notice + Inkompatibilitetsmeddelande + + + Failed to open file: + Misslyckades med att öppna filen: + + + XML ERROR: + XML-FEL: + + + Failed to open files.json for writing + Misslyckades med att öppna files.json för skrivning + + + Author: + Upphovsperson: + + + Directory does not exist: + Katalogen finns inte: + + + Failed to open files.json for reading. + Misslyckades med att öppna files.json för läsning. + + + Name: + Namn: + + + Can't apply cheats before the game is started + Kan inte tillämpa fusk innan spelet är startat + + + Error: + Fel: + + + ERROR + FEL + + + + CheckUpdate + + Auto Updater + Automatisk uppdatering + + + Error + Fel + + + Network error: + Nätverksfel: + + + Failed to parse update information. + Misslyckades med att tolka uppdateringsinformationen. + + + No pre-releases found. + Inga förutgåva hittades. + + + Invalid release data. + Ogiltig release-data. + + + No download URL found for the specified asset. + Ingen hämtnings-URL hittades för angiven tillgång. + + + Your version is already up to date! + Din version är redan den senaste! + + + Update Available + Uppdatering tillgänglig + + + Update Channel + Uppdateringskanal + + + Current Version + Aktuell version + + + Latest Version + Senaste version + + + Do you want to update? + Vill du uppdatera? + + + Show Changelog + Visa ändringslogg + + + Check for Updates at Startup + Leta efter uppdateringar vid uppstart + + + Update + Uppdatera + + + No + Nej + + + Hide Changelog + Dölj ändringslogg + + + Changes + Ändringar + + + Network error occurred while trying to access the URL + Nätverksfel inträffade vid försök att komma åt URL:en + + + Download Complete + Hämtning färdig + + + The update has been downloaded, press OK to install. + Uppdateringen har hämtats. Tryck på Ok för att installera. + + + Failed to save the update file at + Misslyckades med att spara uppdateringsfilen i + + + Starting Update... + Startar uppdatering... + + + Failed to create the update script file + Misslyckades med att skapa uppdateringsskriptfil + + + + CompatibilityInfoClass + + Fetching compatibility data, please wait + Hämtar kompatibilitetsdata, vänta + + + Cancel + Avbryt + + + Loading... + Läser in... + + + Error + Fel + + + Unable to update compatibility data! Try again later. + Kunde inte uppdatera kompatibilitetsdata! Försök igen senare. + + + Unable to open compatibility.json for writing. + Kunde inte öppna compatibility.json för skrivning. + + + + ElfViewer + + Open Folder + Öppna mapp + + + + GameInfoClass + + Loading game list, please wait :3 + Läser in spellistan, vänta :3 + + + Cancel + Avbryt + + + Loading... + Läser in... + + + + GameInstallDialog + + shadPS4 - Choose directory + shadPS4 - Välj katalog + + + Directory to install games + Katalog att installera spel till + + + Browse + Bläddra + + + Error + Fel + + + Directory to install DLC + Katalog för att installera DLC + + + + GameListFrame + + Icon + Ikon + + + Name + Namn + + + Serial + Serienummer + + + Compatibility + Kompatibilitet + + + Region + Region + + + Firmware + Firmware + + + Size + Storlek + + + Version + Version + + + Path + Sökväg + + + Play Time + Speltid + + + Never Played + Aldrig spelat + + + h + h + + + m + m + + + s + s + + + Compatibility is untested + Kompatibilitet är otestat + + + Game does not initialize properly / crashes the emulator + Spelet initierar inte korrekt / kraschar emulatorn + + + Game boots, but only displays a blank screen + Spelet startar men visar endast en blank skärm + + + Game displays an image but does not go past the menu + Spelet visar en bild men kommer inte förbi menyn + + + Game has game-breaking glitches or unplayable performance + Spelet har allvarliga problem eller ospelbar prestanda + + + Game can be completed with playable performance and no major glitches + Spelet kan spelas klart med spelbar prestanda och utan större problem + + + Click to go to issue + Klicka för att gå till problem + + + Last updated + Senast uppdaterad + + + + GameListUtils + + B + B + + + KB + KB + + + MB + MB + + + GB + GB + + + TB + TB + + + + GuiContextMenus + + Create Shortcut + Skapa genväg + + + Cheats / Patches + Fusk / Patchar + + + SFO Viewer + SFO-visare + + + Trophy Viewer + Trofé-visare + + + Open Folder... + Öppna mapp... + + + Open Game Folder + Öppna spelmapp + + + Open Save Data Folder + Öppna mapp för sparat data + + + Open Log Folder + Öppna loggmapp + + + Copy info... + Kopiera till... + + + Copy Name + Kopiera namn + + + Copy Serial + Kopiera serienummer + + + Copy All + Kopiera alla + + + Delete... + Ta bort... + + + Delete Game + Ta bort spel + + + Delete Update + Ta bort uppdatering + + + Delete DLC + Ta bort DLC + + + Compatibility... + Kompatibilitet... + + + Update database + Uppdatera databasen + + + View report + Visa rapport + + + Submit a report + Skicka en rapport + + + Shortcut creation + Skapa genväg + + + Shortcut created successfully! + Genvägen skapades! + + + Error + Fel + + + Error creating shortcut! + Fel vid skapandet av genväg! + + + Install PKG + Installera PKG + + + Game + Spel + + + This game has no update to delete! + Detta spel har ingen uppdatering att ta bort! + + + Update + Uppdatera + + + This game has no DLC to delete! + Detta spel har inga DLC att ta bort! + + + DLC + DLC + + + Delete %1 + Ta bort %1 + + + Are you sure you want to delete %1's %2 directory? + Är du säker på att du vill ta bort %1s %2-katalog? + + + Failed to convert icon. + Misslyckades med att konvertera ikon. + + + + InstallDirSelect + + shadPS4 - Choose directory + shadPS4 - Välj katalog + + + Select which directory you want to install to. + Välj vilken katalog som du vill installera till. + + + + MainWindow + + Open/Add Elf Folder + Öppna/Lägg till Elf-mapp + + + Install Packages (PKG) + Installera paket (PKG) + + + Boot Game + Starta spel + + + Check for Updates + Leta efter uppdateringar + + + About shadPS4 + Om shadPS4 + + + Configure... + Konfigurera... + + + Install application from a .pkg file + Installera program från en .pkg-fil + + + Recent Games + Senaste spel + + + Exit + Avsluta + + + Exit shadPS4 + Avsluta shadPS4 + + + Exit the application. + Avsluta programmet. + + + Show Game List + Visa spellista + + + Game List Refresh + Uppdatera spellista + + + Tiny + Mycket små + + + Small + Små + + + Medium + Medel + + + Large + Stora + + + List View + Listvy + + + Grid View + Rutnätsvy + + + Elf Viewer + Elf-visare + + + Game Install Directory + Installationskatalog för spel + + + Download Cheats/Patches + Hämta fusk/patchar + + + Dump Game List + Dumpa spellista + + + PKG Viewer + PKG-visare + + + Search... + Sök... + + + File + Arkiv + + + View + Visa + + + Game List Icons + Ikoner för spellista + + + Game List Mode + Läge för spellista + + + Settings + Inställningar + + + Utils + Verktyg + + + Themes + Teman + + + Help + Hjälp + + + Dark + Mörk + + + Light + Ljus + + + Green + Grön + + + Blue + Blå + + + Violet + Lila + + + toolBar + toolBar + + + Game List + Spellista + + + * Unsupported Vulkan Version + * Vulkan-versionen stöds inte + + + Download Cheats For All Installed Games + Hämta fusk för alla installerade spel + + + Download Patches For All Games + Hämta patchar för alla spel + + + Download Complete + Hämtning färdig + + + You have downloaded cheats for all the games you have installed. + Du har hämtat fusk till alla spelen som du har installerade. + + + Patches Downloaded Successfully! + Patchar hämtades ner! + + + All Patches available for all games have been downloaded. + Alla patchar tillgängliga för alla spel har hämtats ner. + + + Games: + Spel: + + + ELF files (*.bin *.elf *.oelf) + ELF-filer (*.bin *.elf *.oelf) + + + Game Boot + Starta spel + + + Only one file can be selected! + Endast en fil kan väljas! + + + PKG Extraction + PKG-extrahering + + + Patch detected! + Patch upptäcktes! + + + PKG and Game versions match: + PKG och spelversioner matchar: + + + Would you like to overwrite? + Vill du skriva över? + + + PKG Version %1 is older than installed version: + PKG-versionen %1 är äldre än installerad version: + + + Game is installed: + Spelet är installerat: + + + Would you like to install Patch: + Vill du installera patch: + + + DLC Installation + DLC-installation + + + Would you like to install DLC: %1? + Vill du installera DLC: %1? + + + DLC already installed: + DLC redan installerat: + + + Game already installed + Spelet redan installerat + + + PKG ERROR + PKG-FEL + + + Extracting PKG %1/%2 + Extraherar PKG %1/%2 + + + Extraction Finished + Extrahering färdig + + + Game successfully installed at %1 + Spelet installerades i %1 + + + File doesn't appear to be a valid PKG file + Filen verkar inte vara en giltig PKG-fil + + + Run Game + Kör spel + + + Eboot.bin file not found + Filen eboot.bin hittades inte + + + PKG File (*.PKG *.pkg) + PKG-fil (*.PKG *.pkg) + + + PKG is a patch or DLC, please install the game first! + PKG är en patch eller DLC. Installera spelet först! + + + Game is already running! + Spelet är redan igång! + + + shadPS4 + shadPS4 + + + + PKGViewer + + Open Folder + Öppna mapp + + + &File + &Arkiv + + + PKG ERROR + PKG-FEL + + + + SettingsDialog + + Settings + Inställningar + + + General + Allmänt + + + System + System + + + Console Language + Konsollspråk + + + Emulator Language + Emulatorspråk + + + Emulator + Emulator + + + Enable Fullscreen + Aktivera helskärm + + + Enable Separate Update Folder + Aktivera separat uppdateringsmapp + + + Show Splash + Visa startskärm + + + Enable Discord Rich Presence + Aktivera Discord Rich Presence + + + Username + Användarnamn + + + Trophy Key + Trofényckel + + + Trophy + Trofé + + + Logger + Loggning + + + Log Type + Loggtyp + + + Log Filter + Loggfilter + + + Input + Inmatning + + + Cursor + Pekare + + + Hide Cursor + Dölj pekare + + + Hide Cursor Idle Timeout + Dölj pekare vid overksam + + + s + s + + + Controller + Handkontroller + + + Back Button Behavior + Beteende för bakåtknapp + + + Graphics + Grafik + + + Graphics Device + Grafikenhet + + + Width + Bredd + + + Height + Höjd + + + Vblank Divider + Vblank Divider + + + Advanced + Avancerat + + + Enable Shaders Dumping + Aktivera Shaders Dumping + + + Enable NULL GPU + Aktivera NULL GPU + + + Paths + Sökvägar + + + Game Folders + Spelmappar + + + Add... + Lägg till... + + + Remove + Ta bort + + + Debug + Felsök + + + Enable Debug Dumping + Aktivera felsökningsdumpning + + + Enable Vulkan Validation Layers + Aktivera Vulkan Validation Layers + + + Enable Vulkan Synchronization Validation + Aktivera Vulkan Synchronization Validation + + + Enable RenderDoc Debugging + Aktivera RenderDoc-felsökning + + + Update + Uppdatera + + + Check for Updates at Startup + Leta efter uppdateringar vid uppstart + + + Update Channel + Uppdateringskanal + + + Check for Updates + Leta efter uppdateringar + + + GUI Settings + Gränssnittsinställningar + + + Disable Trophy Pop-ups + Inaktivera popup för troféer + + + Play title music + Spela titelmusik + + + Update Compatibility Database On Startup + Uppdatera databas vid uppstart + + + Game Compatibility + Spelkompatibilitet + + + Display Compatibility Data + Visa kompatibilitetsdata + + + Update Compatibility Database + Uppdatera kompatibilitetsdatabasen + + + Volume + Volym + + + Save + Spara + + + Apply + Verkställ + + + Restore Defaults + Återställ till standard + + + Close + Stäng + + + Point your mouse at an option to display its description. + Peka din mus på ett alternativ för att visa dess beskrivning. + + + consoleLanguageGroupBox + Konsollspråk:\nStäller in språket som PS4-spelet använder.\nDet rekommenderas att ställa in detta till ett språk som spelet har stöd för, vilket kan skilja sig mellan regioner + + + emulatorLanguageGroupBox + Emulatorspråk:\nStäller in språket för emulatorns användargränssnitt + + + fullscreenCheckBox + Aktivera helskärm:\nStäller automatiskt in spelfönstret till helskämsläget.\nDetta kan växlas genom att trycka på F11-tangenten + + + separateUpdatesCheckBox + Aktivera separat uppdateringsmapp:\nAktiverar installation av speluppdateringar i en separat mapp för enkel hantering.\nDetta kan skapas manuellt genom att lägga till uppackad uppdatering till spelmappen med namnet "CUSA00000-UPDATE" där CUSA ID matchar spelets id + + + showSplashCheckBox + Visa startskärm:\nVisar spelets startskärm (en speciell bild) när spelet startas + + + discordRPCCheckbox + Aktivera Discord Rich Presence:\nVisar emulatorikonen och relevant information på din Discord-profil + + + userName + Användarnamn:\nStäller in PS4ans användarkonto, som kan visas av vissa spel + + + TrophyKey + Trofényckel:\nNyckel som används för att avkryptera troféer. Måste hämtas från din konsoll (jailbroken).\nMåste innehålla endast hex-tecken + + + logTypeGroupBox + Loggtyp:\nStäller in huruvida synkronisering av utdata för loggfönstret för prestanda. Kan ha inverkan på emulationen + + + logFilter + Loggfilter:\nFiltrera loggen till att endast skriva ut specifik information.\nExempel: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical"\nNivåer: Trace, Debug, Info, Warning, Error, Critical - i den ordningen, en specifik nivå som tystar alla nivåer före den i listan och loggar allting efter den + + + updaterGroupBox + updaterGroupBox + + + GUIgroupBox + Spela upp titelmusik:\nOm ett spel har stöd för det kan speciell musik spelas upp från spelet i gränssnittet + + + disableTrophycheckBox + Inaktivera popup för troféer:\nInaktivera troféeaviseringar i spel. Troféförlopp kan fortfarande följas med Troféevisaren (högerklicka på spelet i huvudfönstret) + + + hideCursorGroupBox + Dölj pekare:\nVälj när muspekaren ska försvinna:\nAldrig: Du kommer alltid se muspekaren.\nOverksam: Ställ in en tid för när den ska försvinna efter den inte använts.\nAlltid: du kommer aldrig se muspekaren + + + idleTimeoutGroupBox + Dölj pekare vid overksam:\nLängden (sekunder) efter vilken som muspekaren som har varit overksam döljer sig själv + + + backButtonBehaviorGroupBox + Beteende för bakåtknapp:\nStäller in handkontrollerns bakåtknapp för att emulera ett tryck på angivna positionen på PS4ns touchpad + + + enableCompatibilityCheckBox + Visa kompatibilitetsdata:\nVisar information om spelkompatibilitet i tabellvyn. Aktivera "Uppdatera kompatibilitet vid uppstart" för att få uppdaterad information + + + checkCompatibilityOnStartupCheckBox + Uppdatera kompatibilitet vid uppstart:\nUppdatera automatiskt kompatibilitetsdatabasen när shadPS4 startar + + + updateCompatibilityButton + Uppdatera kompatibilitetsdatabasen:\nUppdaterar kompatibilitetsdatabasen direkt + + + Never + Aldrig + + + Idle + Overksam + + + Always + Alltid + + + Touchpad Left + Touchpad vänster + + + Touchpad Right + Touchpad höger + + + Touchpad Center + Touchpad mitten + + + None + Ingen + + + graphicsAdapterGroupBox + Grafikenhet:\nFör system med flera GPUer kan du välja den GPU som emulatorn ska använda från rullgardinsmenyn,\neller välja "Auto Select" för att automatiskt bestämma det + + + resolutionLayout + Bredd/Höjd:\nStäller in storleken för emulatorfönstret vid uppstart, som kan storleksändras under spelning.\nDetta är inte det samma som spelupplösningen + + + heightDivider + Vblank Divider:\nBildfrekvensen som emulatorn uppdaterar vid multipliceras med detta tal. Ändra detta kan ha inverkan på saker, såsom ökad spelhastighet eller göra sönder kritisk spelfunktionalitet, som inte förväntar sig denna ändring + + + dumpShadersCheckBox + Aktivera Shaders Dumping:\nFör teknisk felsökning, sparar spelets shaders till en mapp när de renderas + + + nullGpuCheckBox + Aktivera Null GPU:\nFör teknisk felsökning, inaktiverar spelrenderingen som om det inte fanns något grafikkort + + + gameFoldersBox + Spelmappar:\nListan över mappar att leta i efter installerade spel + + + addFolderButton + Aktivera separat uppdateringsmapp:\nAktiverar installation av speluppdateringar till en separat mapp för enkel hantering.\nDetta kan manuellt skapas genom att lägga till den uppackade uppdateringen till spelmappen med namnet "CUSA00000-UPDATE" där CUSA ID matchar spelets id + + + removeFolderButton + Ta bort:\nTa bort en mapp från listan + + + debugDump + Aktivera felsökningsdumpning:\nSparar import och export av symboler och fil-header-information för aktuellt körande PS4-program till en katalog + + + vkValidationCheckBox + Aktivera Vulkan Validation Layers:\nAktiverar ett system som validerar tillståndet för Vulkan renderer och loggar information om dess interna tillstånd.\nDetta kommer minska prestandan och antagligen ändra beteendet för emuleringen + + + vkSyncValidationCheckBox + Aktivera Vulkan Synchronization Validation:\nAktiverar ett system som validerar timing för Vulkan rendering tasks.\nDetta kommer minska prestandan och antagligen ändra beteendet för emuleringen + + + rdocCheckBox + Aktivera RenderDoc-felsökning:\nOm aktiverad kommer emulatorn att tillhandahålla kompatibilitet med Renderdoc för att tillåta fångst och analys för aktuell renderad bildruta + + + Release + Release + + + Nightly + Nightly + + + Set the volume of the background music. + Ställ in volymen för bakgrundsmusiken. + + + async + asynk + + + sync + synk + + + Directory to install games + Katalog att installera spel till + + + + TrophyViewer + + Trophy Viewer + Trofé-visare + + + From 86038e6a71258e9a2f34c61911a1c48b4c627950 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Tue, 7 Jan 2025 01:36:14 -0800 Subject: [PATCH 372/549] shader_recompiler: Fix V_CMP_U_F32 (#2082) --- src/shader_recompiler/frontend/translate/vector_alu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shader_recompiler/frontend/translate/vector_alu.cpp b/src/shader_recompiler/frontend/translate/vector_alu.cpp index 2b32ca2ce..7fa83eebb 100644 --- a/src/shader_recompiler/frontend/translate/vector_alu.cpp +++ b/src/shader_recompiler/frontend/translate/vector_alu.cpp @@ -904,7 +904,7 @@ void Translator::V_CMP_F32(ConditionOp op, bool set_exec, const GcnInst& inst) { case ConditionOp::GE: return ir.FPGreaterThanEqual(src0, src1); case ConditionOp::U: - return ir.LogicalNot(ir.LogicalAnd(ir.FPIsNan(src0), ir.FPIsNan(src1))); + return ir.LogicalOr(ir.FPIsNan(src0), ir.FPIsNan(src1)); default: UNREACHABLE(); } From c3ecf599ad779361a44d2dd6b6823a8c1a6f93da Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Tue, 7 Jan 2025 10:50:54 +0100 Subject: [PATCH 373/549] Add motion controls toggle (#2029) * Add motion controls toggle * clang --- src/common/config.cpp | 11 +++++++++++ src/common/config.h | 4 +++- src/input/controller.cpp | 31 +++++++++++++++---------------- src/qt_gui/settings_dialog.cpp | 3 +++ src/qt_gui/settings_dialog.ui | 7 +++++++ 5 files changed, 39 insertions(+), 17 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index 9e2cc0020..1838f35fc 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -47,6 +47,7 @@ static std::string updateChannel; static std::string backButtonBehavior = "left"; static bool useSpecialPad = false; static int specialPadClass = 1; +static bool isMotionControlsEnabled = true; static bool isDebugDump = false; static bool isShaderDebug = false; static bool isShowSplash = false; @@ -172,6 +173,10 @@ int getSpecialPadClass() { return specialPadClass; } +bool getIsMotionControlsEnabled() { + return isMotionControlsEnabled; +} + bool debugDump() { return isDebugDump; } @@ -368,6 +373,10 @@ void setSpecialPadClass(int type) { specialPadClass = type; } +void setIsMotionControlsEnabled(bool use) { + isMotionControlsEnabled = use; +} + void setSeparateUpdateEnabled(bool use) { separateupdatefolder = use; } @@ -594,6 +603,7 @@ void load(const std::filesystem::path& path) { backButtonBehavior = toml::find_or(input, "backButtonBehavior", "left"); useSpecialPad = toml::find_or(input, "useSpecialPad", false); specialPadClass = toml::find_or(input, "specialPadClass", 1); + isMotionControlsEnabled = toml::find_or(input, "isMotionControlsEnabled", true); } if (data.contains("GPU")) { @@ -709,6 +719,7 @@ void save(const std::filesystem::path& path) { data["Input"]["backButtonBehavior"] = backButtonBehavior; data["Input"]["useSpecialPad"] = useSpecialPad; data["Input"]["specialPadClass"] = specialPadClass; + data["Input"]["isMotionControlsEnabled"] = isMotionControlsEnabled; data["GPU"]["screenWidth"] = screenWidth; data["GPU"]["screenHeight"] = screenHeight; data["GPU"]["nullGpu"] = isNullGpu; diff --git a/src/common/config.h b/src/common/config.h index 2b9a35449..d2860bec5 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -38,6 +38,7 @@ int getCursorHideTimeout(); std::string getBackButtonBehavior(); bool getUseSpecialPad(); int getSpecialPadClass(); +bool getIsMotionControlsEnabled(); u32 getScreenWidth(); u32 getScreenHeight(); @@ -84,6 +85,7 @@ void setCursorHideTimeout(int newcursorHideTimeout); void setBackButtonBehavior(const std::string& type); void setUseSpecialPad(bool use); void setSpecialPadClass(int type); +void setIsMotionControlsEnabled(bool use); void setLogType(const std::string& type); void setLogFilter(const std::string& type); @@ -139,4 +141,4 @@ void setDefaultValues(); // settings u32 GetLanguage(); -}; // namespace Config \ No newline at end of file +}; // namespace Config diff --git a/src/input/controller.cpp b/src/input/controller.cpp index 366d80f8f..eb43e6adf 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include "common/config.h" #include "common/logging/log.h" #include "core/libraries/kernel/time.h" #include "core/libraries/pad/pad.h" @@ -189,11 +190,6 @@ void GameController::CalculateOrientation(Libraries::Pad::OrbisFVector3& acceler gz += Kp * ez + Ki * eInt[2]; //// Integrate rate of change of quaternion - // float pa = q2, pb = q3, pc = q4; - // q1 += (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * deltaTime); - // q2 += (pa * gx + pb * gz - pc * gy) * (0.5f * deltaTime); - // q3 += (pb * gy - pa * gz + pc * gx) * (0.5f * deltaTime); - // q4 += (pc * gz + pa * gy - pb * gx) * (0.5f * deltaTime); q1 += (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * deltaTime); q2 += (q1 * gx + q3 * gz - q4 * gy) * (0.5f * deltaTime); q3 += (q1 * gy - q2 * gz + q4 * gx) * (0.5f * deltaTime); @@ -247,18 +243,21 @@ void GameController::TryOpenSDLController() { int gamepad_count; SDL_JoystickID* gamepads = SDL_GetGamepads(&gamepad_count); m_sdl_gamepad = gamepad_count > 0 ? SDL_OpenGamepad(gamepads[0]) : nullptr; - if (SDL_SetGamepadSensorEnabled(m_sdl_gamepad, SDL_SENSOR_GYRO, true)) { - gyro_poll_rate = SDL_GetGamepadSensorDataRate(m_sdl_gamepad, SDL_SENSOR_GYRO); - LOG_INFO(Input, "Gyro initialized, poll rate: {}", gyro_poll_rate); - } else { - LOG_ERROR(Input, "Failed to initialize gyro controls for gamepad"); - } - if (SDL_SetGamepadSensorEnabled(m_sdl_gamepad, SDL_SENSOR_ACCEL, true)) { - accel_poll_rate = SDL_GetGamepadSensorDataRate(m_sdl_gamepad, SDL_SENSOR_ACCEL); - LOG_INFO(Input, "Accel initialized, poll rate: {}", accel_poll_rate); - } else { - LOG_ERROR(Input, "Failed to initialize accel controls for gamepad"); + if (Config::getIsMotionControlsEnabled()) { + if (SDL_SetGamepadSensorEnabled(m_sdl_gamepad, SDL_SENSOR_GYRO, true)) { + gyro_poll_rate = SDL_GetGamepadSensorDataRate(m_sdl_gamepad, SDL_SENSOR_GYRO); + LOG_INFO(Input, "Gyro initialized, poll rate: {}", gyro_poll_rate); + } else { + LOG_ERROR(Input, "Failed to initialize gyro controls for gamepad"); + } + if (SDL_SetGamepadSensorEnabled(m_sdl_gamepad, SDL_SENSOR_ACCEL, true)) { + accel_poll_rate = SDL_GetGamepadSensorDataRate(m_sdl_gamepad, SDL_SENSOR_ACCEL); + LOG_INFO(Input, "Accel initialized, poll rate: {}", accel_poll_rate); + } else { + LOG_ERROR(Input, "Failed to initialize accel controls for gamepad"); + } } + SDL_free(gamepads); SetLightBarRGB(0, 0, 255); diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index 6d76a5318..1a03345e4 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -339,6 +339,8 @@ void SettingsDialog::LoadValuesFromConfig() { toml::find_or(data, "Input", "backButtonBehavior", "left")); int index = ui->backButtonBehaviorComboBox->findData(backButtonBehavior); ui->backButtonBehaviorComboBox->setCurrentIndex(index != -1 ? index : 0); + ui->motionControlsCheckBox->setChecked( + toml::find_or(data, "Input", "isMotionControlsEnabled", true)); ui->removeFolderButton->setEnabled(!ui->gameFoldersListWidget->selectedItems().isEmpty()); ResetInstallFolders(); @@ -532,6 +534,7 @@ void SettingsDialog::UpdateSettings() { const QVector TouchPadIndex = {"left", "center", "right", "none"}; Config::setBackButtonBehavior(TouchPadIndex[ui->backButtonBehaviorComboBox->currentIndex()]); + Config::setIsMotionControlsEnabled(ui->motionControlsCheckBox->isChecked()); Config::setFullscreenMode(ui->fullscreenCheckBox->isChecked()); Config::setisTrophyPopupDisabled(ui->disableTrophycheckBox->isChecked()); Config::setPlayBGM(ui->playBGMCheckBox->isChecked()); diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index 2e7e3db37..cefe1f7c7 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -815,6 +815,13 @@ + + + + Enable Motion Controls + + + From b0d7feb2929a03b7bccd2adb799009487b4fd59b Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Tue, 7 Jan 2025 02:21:49 -0800 Subject: [PATCH 374/549] video_core: Implement conversion for uncommon/unsupported number formats. (#2047) * video_core: Implement conversion for uncommon/unsupported number formats. * shader_recompiler: Reinterpret image sample output as well. * liverpool_to_vk: Remove mappings for remapped number formats. These were poorly supported by drivers anyway. * resource_tracking_pass: Fix image write swizzle mistake. * amdgpu: Add missing specialization and move format mapping data to types * reinterpret: Fix U/SToF input type. --- .../frontend/translate/export.cpp | 9 +- .../ir/passes/resource_tracking_pass.cpp | 522 +++++++++--------- src/shader_recompiler/ir/reinterpret.h | 64 ++- src/shader_recompiler/runtime_info.h | 1 + src/shader_recompiler/specialization.h | 4 + src/video_core/amdgpu/liverpool.h | 8 +- src/video_core/amdgpu/pixel_format.cpp | 2 +- src/video_core/amdgpu/resource.h | 102 +--- src/video_core/amdgpu/types.h | 122 +++- .../renderer_vulkan/liverpool_to_vk.cpp | 33 +- .../renderer_vulkan/vk_graphics_pipeline.h | 1 + .../renderer_vulkan/vk_pipeline_cache.cpp | 3 + 12 files changed, 486 insertions(+), 385 deletions(-) diff --git a/src/shader_recompiler/frontend/translate/export.cpp b/src/shader_recompiler/frontend/translate/export.cpp index 83240e17f..38ff9ae14 100644 --- a/src/shader_recompiler/frontend/translate/export.cpp +++ b/src/shader_recompiler/frontend/translate/export.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "shader_recompiler/frontend/translate/translate.h" +#include "shader_recompiler/ir/reinterpret.h" #include "shader_recompiler/runtime_info.h" namespace Shader::Gcn { @@ -31,14 +32,16 @@ void Translator::EmitExport(const GcnInst& inst) { return; } const u32 index = u32(attrib) - u32(IR::Attribute::RenderTarget0); - const auto [r, g, b, a] = runtime_info.fs_info.color_buffers[index].swizzle; + const auto col_buf = runtime_info.fs_info.color_buffers[index]; + const auto converted = IR::ApplyWriteNumberConversion(ir, value, col_buf.num_conversion); + const auto [r, g, b, a] = col_buf.swizzle; const std::array swizzle_array = {r, g, b, a}; const auto swizzled_comp = swizzle_array[comp]; if (u32(swizzled_comp) < u32(AmdGpu::CompSwizzle::Red)) { - ir.SetAttribute(attrib, value, comp); + ir.SetAttribute(attrib, converted, comp); return; } - ir.SetAttribute(attrib, value, u32(swizzled_comp) - u32(AmdGpu::CompSwizzle::Red)); + ir.SetAttribute(attrib, converted, u32(swizzled_comp) - u32(AmdGpu::CompSwizzle::Red)); }; const auto unpack = [&](u32 idx) { diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index 636752912..f7040ad75 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -301,8 +301,7 @@ s32 TryHandleInlineCbuf(IR::Inst& inst, Info& info, Descriptors& descriptors, }); } -void PatchBufferInstruction(IR::Block& block, IR::Inst& inst, Info& info, - Descriptors& descriptors) { +void PatchBufferSharp(IR::Block& block, IR::Inst& inst, Info& info, Descriptors& descriptors) { s32 binding{}; AmdGpu::Buffer buffer; if (binding = TryHandleInlineCbuf(inst, info, descriptors, buffer); binding == -1) { @@ -317,19 +316,191 @@ void PatchBufferInstruction(IR::Block& block, IR::Inst& inst, Info& info, }); } - // Update buffer descriptor format. - const auto inst_info = inst.Flags(); - // Replace handle with binding index in buffer resource list. IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; inst.SetArg(0, ir.Imm32(binding)); +} + +void PatchTextureBufferSharp(IR::Block& block, IR::Inst& inst, Info& info, + Descriptors& descriptors) { + const IR::Inst* handle = inst.Arg(0).InstRecursive(); + const IR::Inst* producer = handle->Arg(0).InstRecursive(); + const auto sharp = TrackSharp(producer, info); + const s32 binding = descriptors.Add(TextureBufferResource{ + .sharp_idx = sharp, + .is_written = inst.GetOpcode() == IR::Opcode::StoreBufferFormatF32, + }); + + // Replace handle with binding index in texture buffer resource list. + IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; + inst.SetArg(0, ir.Imm32(binding)); +} + +void PatchImageSharp(IR::Block& block, IR::Inst& inst, Info& info, Descriptors& descriptors) { + const auto pred = [](const IR::Inst* inst) -> std::optional { + const auto opcode = inst->GetOpcode(); + if (opcode == IR::Opcode::CompositeConstructU32x2 || // IMAGE_SAMPLE (image+sampler) + opcode == IR::Opcode::ReadConst || // IMAGE_LOAD (image only) + opcode == IR::Opcode::GetUserData) { + return inst; + } + return std::nullopt; + }; + const auto result = IR::BreadthFirstSearch(&inst, pred); + ASSERT_MSG(result, "Unable to find image sharp source"); + const IR::Inst* producer = result.value(); + const bool has_sampler = producer->GetOpcode() == IR::Opcode::CompositeConstructU32x2; + const auto tsharp_handle = has_sampler ? producer->Arg(0).InstRecursive() : producer; + + // Read image sharp. + const auto tsharp = TrackSharp(tsharp_handle, info); + const auto inst_info = inst.Flags(); + auto image = info.ReadUdSharp(tsharp); + if (!image.Valid()) { + LOG_ERROR(Render_Vulkan, "Shader compiled with unbound image!"); + image = AmdGpu::Image::Null(); + } + ASSERT(image.GetType() != AmdGpu::ImageType::Invalid); + const bool is_read = inst.GetOpcode() == IR::Opcode::ImageRead; + const bool is_written = inst.GetOpcode() == IR::Opcode::ImageWrite; + + // Patch image instruction if image is FMask. + if (image.IsFmask()) { + ASSERT_MSG(!is_written, "FMask storage instructions are not supported"); + + IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; + switch (inst.GetOpcode()) { + case IR::Opcode::ImageRead: + case IR::Opcode::ImageSampleRaw: { + IR::F32 fmaskx = ir.BitCast(ir.Imm32(0x76543210)); + IR::F32 fmasky = ir.BitCast(ir.Imm32(0xfedcba98)); + inst.ReplaceUsesWith(ir.CompositeConstruct(fmaskx, fmasky)); + return; + } + case IR::Opcode::ImageQueryLod: + inst.ReplaceUsesWith(ir.Imm32(1)); + return; + case IR::Opcode::ImageQueryDimensions: { + IR::Value dims = ir.CompositeConstruct(ir.Imm32(static_cast(image.width)), // x + ir.Imm32(static_cast(image.width)), // y + ir.Imm32(1), ir.Imm32(1)); // depth, mip + inst.ReplaceUsesWith(dims); + + // Track FMask resource to do specialization. + descriptors.Add(FMaskResource{ + .sharp_idx = tsharp, + }); + return; + } + default: + UNREACHABLE_MSG("Can't patch fmask instruction {}", inst.GetOpcode()); + } + } + + u32 image_binding = descriptors.Add(ImageResource{ + .sharp_idx = tsharp, + .is_depth = bool(inst_info.is_depth), + .is_atomic = IsImageAtomicInstruction(inst), + .is_array = bool(inst_info.is_array), + .is_read = is_read, + .is_written = is_written, + }); + + IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; + + if (inst.GetOpcode() == IR::Opcode::ImageSampleRaw) { + // Read sampler sharp. + const auto [sampler_binding, sampler] = [&] -> std::pair { + ASSERT(producer->GetOpcode() == IR::Opcode::CompositeConstructU32x2); + const IR::Value& handle = producer->Arg(1); + // Inline sampler resource. + if (handle.IsImmediate()) { + LOG_WARNING(Render_Vulkan, "Inline sampler detected"); + const auto inline_sampler = AmdGpu::Sampler{.raw0 = handle.U32()}; + const auto binding = descriptors.Add(SamplerResource{ + .sharp_idx = std::numeric_limits::max(), + .inline_sampler = inline_sampler, + }); + return {binding, inline_sampler}; + } + // Normal sampler resource. + const auto ssharp_handle = handle.InstRecursive(); + const auto& [ssharp_ud, disable_aniso] = TryDisableAnisoLod0(ssharp_handle); + const auto ssharp = TrackSharp(ssharp_ud, info); + const auto binding = descriptors.Add(SamplerResource{ + .sharp_idx = ssharp, + .associated_image = image_binding, + .disable_aniso = disable_aniso, + }); + return {binding, info.ReadUdSharp(ssharp)}; + }(); + // Patch image and sampler handle. + inst.SetArg(0, ir.Imm32(image_binding | sampler_binding << 16)); + } else { + // Patch image handle. + inst.SetArg(0, ir.Imm32(image_binding)); + } +} + +void PatchDataRingAccess(IR::Block& block, IR::Inst& inst, Info& info, Descriptors& descriptors) { + // Insert gds binding in the shader if it doesn't exist already. + // The buffer is used for append/consume counters. + constexpr static AmdGpu::Buffer GdsSharp{.base_address = 1}; + const u32 binding = descriptors.Add(BufferResource{ + .used_types = IR::Type::U32, + .inline_cbuf = GdsSharp, + .is_gds_buffer = true, + .is_written = true, + }); + + const auto pred = [](const IR::Inst* inst) -> std::optional { + if (inst->GetOpcode() == IR::Opcode::GetUserData) { + return inst; + } + return std::nullopt; + }; + + // Attempt to deduce the GDS address of counter at compile time. + const u32 gds_addr = [&] { + const IR::Value& gds_offset = inst.Arg(0); + if (gds_offset.IsImmediate()) { + // Nothing to do, offset is known. + return gds_offset.U32() & 0xFFFF; + } + const auto result = IR::BreadthFirstSearch(&inst, pred); + ASSERT_MSG(result, "Unable to track M0 source"); + + // M0 must be set by some user data register. + const IR::Inst* prod = gds_offset.InstRecursive(); + const u32 ud_reg = u32(result.value()->Arg(0).ScalarReg()); + u32 m0_val = info.user_data[ud_reg] >> 16; + if (prod->GetOpcode() == IR::Opcode::IAdd32) { + m0_val += prod->Arg(1).U32(); + } + return m0_val & 0xFFFF; + }(); + + // Patch instruction. + IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; + inst.SetArg(0, ir.Imm32(gds_addr >> 2)); + inst.SetArg(1, ir.Imm32(binding)); +} + +void PatchBufferArgs(IR::Block& block, IR::Inst& inst, Info& info) { + const auto handle = inst.Arg(0); + const auto buffer_res = info.buffers[handle.U32()]; + const auto buffer = buffer_res.GetSharp(info); + ASSERT(!buffer.add_tid_enable); - // Address of constant buffer reads can be calculated at IR emittion time. + // Address of constant buffer reads can be calculated at IR emission time. if (inst.GetOpcode() == IR::Opcode::ReadConstBuffer) { return; } + IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; + const auto inst_info = inst.Flags(); + const IR::U32 index_stride = ir.Imm32(buffer.index_stride); const IR::U32 element_size = ir.Imm32(buffer.element_size); @@ -366,21 +537,27 @@ void PatchBufferInstruction(IR::Block& block, IR::Inst& inst, Info& info, inst.SetArg(1, address); } -void PatchTextureBufferInstruction(IR::Block& block, IR::Inst& inst, Info& info, - Descriptors& descriptors) { - const IR::Inst* handle = inst.Arg(0).InstRecursive(); - const IR::Inst* producer = handle->Arg(0).InstRecursive(); - const auto sharp = TrackSharp(producer, info); - const auto buffer = info.ReadUdSharp(sharp); - const s32 binding = descriptors.Add(TextureBufferResource{ - .sharp_idx = sharp, - .is_written = inst.GetOpcode() == IR::Opcode::StoreBufferFormatF32, - }); +void PatchTextureBufferArgs(IR::Block& block, IR::Inst& inst, Info& info) { + const auto handle = inst.Arg(0); + const auto buffer_res = info.texture_buffers[handle.U32()]; + const auto buffer = buffer_res.GetSharp(info); - // Replace handle with binding index in texture buffer resource list. - IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; - inst.SetArg(0, ir.Imm32(binding)); ASSERT(!buffer.swizzle_enable && !buffer.add_tid_enable); + IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; + + if (inst.GetOpcode() == IR::Opcode::StoreBufferFormatF32) { + const auto swizzled = ApplySwizzle(ir, inst.Arg(2), buffer.DstSelect()); + const auto converted = + ApplyWriteNumberConversionVec4(ir, swizzled, buffer.GetNumberConversion()); + inst.SetArg(2, converted); + } else if (inst.GetOpcode() == IR::Opcode::LoadBufferFormatF32) { + const auto inst_info = inst.Flags(); + const auto texel = ir.LoadBufferFormat(inst.Arg(0), inst.Arg(1), inst_info); + const auto swizzled = ApplySwizzle(ir, texel, buffer.DstSelect()); + const auto converted = + ApplyReadNumberConversionVec4(ir, swizzled, buffer.GetNumberConversion()); + inst.ReplaceUsesWith(converted); + } } IR::Value PatchCubeCoord(IR::IREmitter& ir, const IR::Value& s, const IR::Value& t, @@ -409,39 +586,14 @@ IR::Value PatchCubeCoord(IR::IREmitter& ir, const IR::Value& s, const IR::Value& } } -void PatchImageSampleInstruction(IR::Block& block, IR::Inst& inst, Info& info, - Descriptors& descriptors, const IR::Inst* producer, - const u32 image_binding, const AmdGpu::Image& image) { - // Read sampler sharp. This doesn't exist for IMAGE_LOAD/IMAGE_STORE instructions - const auto [sampler_binding, sampler] = [&] -> std::pair { - ASSERT(producer->GetOpcode() == IR::Opcode::CompositeConstructU32x2); - const IR::Value& handle = producer->Arg(1); - // Inline sampler resource. - if (handle.IsImmediate()) { - LOG_WARNING(Render_Vulkan, "Inline sampler detected"); - const auto inline_sampler = AmdGpu::Sampler{.raw0 = handle.U32()}; - const auto binding = descriptors.Add(SamplerResource{ - .sharp_idx = std::numeric_limits::max(), - .inline_sampler = inline_sampler, - }); - return {binding, inline_sampler}; - } - // Normal sampler resource. - const auto ssharp_handle = handle.InstRecursive(); - const auto& [ssharp_ud, disable_aniso] = TryDisableAnisoLod0(ssharp_handle); - const auto ssharp = TrackSharp(ssharp_ud, info); - const auto binding = descriptors.Add(SamplerResource{ - .sharp_idx = ssharp, - .associated_image = image_binding, - .disable_aniso = disable_aniso, - }); - return {binding, info.ReadUdSharp(ssharp)}; - }(); +void PatchImageSampleArgs(IR::Block& block, IR::Inst& inst, Info& info, + const AmdGpu::Image& image) { + const auto handle = inst.Arg(0); + const auto sampler_res = info.samplers[(handle.U32() >> 16) & 0xFFFF]; + auto sampler = sampler_res.GetSharp(info); IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; - const auto inst_info = inst.Flags(); - const IR::U32 handle = ir.Imm32(image_binding | sampler_binding << 16); IR::Inst* body1 = inst.Arg(1).InstRecursive(); IR::Inst* body2 = inst.Arg(2).InstRecursive(); @@ -539,8 +691,7 @@ void PatchImageSampleInstruction(IR::Block& block, IR::Inst& inst, Info& info, // Query dimensions of image if needed for normalization. // We can't use the image sharp because it could be bound to a different image later. const auto dimensions = - unnormalized ? ir.ImageQueryDimension(ir.Imm32(image_binding), ir.Imm32(0u), ir.Imm1(false)) - : IR::Value{}; + unnormalized ? ir.ImageQueryDimension(handle, ir.Imm32(0u), ir.Imm1(false)) : IR::Value{}; const auto get_coord = [&](u32 coord_idx, u32 dim_idx) -> IR::Value { const auto coord = get_addr_reg(coord_idx); if (unnormalized) { @@ -589,7 +740,7 @@ void PatchImageSampleInstruction(IR::Block& block, IR::Inst& inst, Info& info, : IR::F32{}; const IR::F32 lod_clamp = inst_info.has_lod_clamp ? get_addr_reg(addr_reg++) : IR::F32{}; - auto new_inst = [&] -> IR::Value { + auto texel = [&] -> IR::Value { if (inst_info.is_gather) { if (inst_info.is_depth) { return ir.ImageGatherDref(handle, coords, offset, dref, inst_info); @@ -611,94 +762,30 @@ void PatchImageSampleInstruction(IR::Block& block, IR::Inst& inst, Info& info, } return ir.ImageSampleImplicitLod(handle, coords, bias, offset, inst_info); }(); - inst.ReplaceUsesWithAndRemove(new_inst); + + const auto converted = ApplyReadNumberConversionVec4(ir, texel, image.GetNumberConversion()); + inst.ReplaceUsesWith(converted); } -void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descriptors& descriptors) { - const auto pred = [](const IR::Inst* inst) -> std::optional { - const auto opcode = inst->GetOpcode(); - if (opcode == IR::Opcode::CompositeConstructU32x2 || // IMAGE_SAMPLE (image+sampler) - opcode == IR::Opcode::ReadConst || // IMAGE_LOAD (image only) - opcode == IR::Opcode::GetUserData) { - return inst; - } - return std::nullopt; - }; - const auto result = IR::BreadthFirstSearch(&inst, pred); - ASSERT_MSG(result, "Unable to find image sharp source"); - const IR::Inst* producer = result.value(); - const bool has_sampler = producer->GetOpcode() == IR::Opcode::CompositeConstructU32x2; - const auto tsharp_handle = has_sampler ? producer->Arg(0).InstRecursive() : producer; - - // Read image sharp. - const auto tsharp = TrackSharp(tsharp_handle, info); - const auto inst_info = inst.Flags(); - auto image = info.ReadUdSharp(tsharp); - if (!image.Valid()) { - LOG_ERROR(Render_Vulkan, "Shader compiled with unbound image!"); - image = AmdGpu::Image::Null(); - } - ASSERT(image.GetType() != AmdGpu::ImageType::Invalid); - const bool is_read = inst.GetOpcode() == IR::Opcode::ImageRead; - const bool is_written = inst.GetOpcode() == IR::Opcode::ImageWrite; - - // Patch image instruction if image is FMask. - if (image.IsFmask()) { - ASSERT_MSG(!is_written, "FMask storage instructions are not supported"); - - IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; - switch (inst.GetOpcode()) { - case IR::Opcode::ImageRead: - case IR::Opcode::ImageSampleRaw: { - IR::F32 fmaskx = ir.BitCast(ir.Imm32(0x76543210)); - IR::F32 fmasky = ir.BitCast(ir.Imm32(0xfedcba98)); - inst.ReplaceUsesWith(ir.CompositeConstruct(fmaskx, fmasky)); - return; - } - case IR::Opcode::ImageQueryLod: - inst.ReplaceUsesWith(ir.Imm32(1)); - return; - case IR::Opcode::ImageQueryDimensions: { - IR::Value dims = ir.CompositeConstruct(ir.Imm32(static_cast(image.width)), // x - ir.Imm32(static_cast(image.width)), // y - ir.Imm32(1), ir.Imm32(1)); // depth, mip - inst.ReplaceUsesWith(dims); - - // Track FMask resource to do specialization. - descriptors.Add(FMaskResource{ - .sharp_idx = tsharp, - }); - return; - } - default: - UNREACHABLE_MSG("Can't patch fmask instruction {}", inst.GetOpcode()); - } - } - - u32 image_binding = descriptors.Add(ImageResource{ - .sharp_idx = tsharp, - .is_depth = bool(inst_info.is_depth), - .is_atomic = IsImageAtomicInstruction(inst), - .is_array = bool(inst_info.is_array), - .is_read = is_read, - .is_written = is_written, - }); - - // Sample instructions must be resolved into a new instruction using address register data. - if (inst.GetOpcode() == IR::Opcode::ImageSampleRaw) { - PatchImageSampleInstruction(block, inst, info, descriptors, producer, image_binding, image); - return; - } - - // Patch image handle - IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; - inst.SetArg(0, ir.Imm32(image_binding)); - - // No need to patch coordinates if we are just querying. +void PatchImageArgs(IR::Block& block, IR::Inst& inst, Info& info) { + // Nothing to patch for dimension query. if (inst.GetOpcode() == IR::Opcode::ImageQueryDimensions) { return; } + const auto handle = inst.Arg(0); + const auto image_res = info.images[handle.U32() & 0xFFFF]; + auto image = image_res.GetSharp(info); + + // Sample instructions must be handled separately using address register data. + if (inst.GetOpcode() == IR::Opcode::ImageSampleRaw) { + PatchImageSampleArgs(block, inst, info, image); + return; + } + + IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; + const auto inst_info = inst.Flags(); + // Now that we know the image type, adjust texture coordinate vector. IR::Inst* body = inst.Arg(1).InstRecursive(); const auto [coords, arg] = [&] -> std::pair { @@ -719,152 +806,77 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip case AmdGpu::ImageType::Color3D: // x, y, z, [lod] return {ir.CompositeConstruct(body->Arg(0), body->Arg(1), body->Arg(2)), body->Arg(3)}; case AmdGpu::ImageType::Cube: // x, y, face, [lod] - return {PatchCubeCoord(ir, body->Arg(0), body->Arg(1), body->Arg(2), is_written, - inst_info.is_array), + return {PatchCubeCoord(ir, body->Arg(0), body->Arg(1), body->Arg(2), + inst.GetOpcode() == IR::Opcode::ImageWrite, inst_info.is_array), body->Arg(3)}; default: UNREACHABLE_MSG("Unknown image type {}", image.GetType()); } }(); - inst.SetArg(1, coords); - if (inst_info.has_lod) { - ASSERT(inst.GetOpcode() == IR::Opcode::ImageRead || - inst.GetOpcode() == IR::Opcode::ImageWrite); - ASSERT(image.GetType() != AmdGpu::ImageType::Color2DMsaa && - image.GetType() != AmdGpu::ImageType::Color2DMsaaArray); - inst.SetArg(2, arg); - } else if ((image.GetType() == AmdGpu::ImageType::Color2DMsaa || - image.GetType() == AmdGpu::ImageType::Color2DMsaaArray) && - (inst.GetOpcode() == IR::Opcode::ImageRead || - inst.GetOpcode() == IR::Opcode::ImageWrite)) { - inst.SetArg(3, arg); - } -} + const auto has_ms = image.GetType() == AmdGpu::ImageType::Color2DMsaa || + image.GetType() == AmdGpu::ImageType::Color2DMsaaArray; + ASSERT(!inst_info.has_lod || !has_ms); + const auto lod = inst_info.has_lod ? IR::U32{arg} : IR::U32{}; + const auto ms = has_ms ? IR::U32{arg} : IR::U32{}; -void PatchTextureBufferInterpretation(IR::Block& block, IR::Inst& inst, Info& info) { - const auto binding = inst.Arg(0).U32(); - const auto buffer_res = info.texture_buffers[binding]; - const auto buffer = buffer_res.GetSharp(info); - if (!buffer.Valid()) { - // Don't need to swizzle invalid buffer. - return; - } - - IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; - if (inst.GetOpcode() == IR::Opcode::StoreBufferFormatF32) { - inst.SetArg(2, ApplySwizzle(ir, inst.Arg(2), buffer.DstSelect())); - } else if (inst.GetOpcode() == IR::Opcode::LoadBufferFormatF32) { - const auto inst_info = inst.Flags(); - const auto texel = ir.LoadBufferFormat(inst.Arg(0), inst.Arg(1), inst_info); - const auto swizzled = ApplySwizzle(ir, texel, buffer.DstSelect()); - inst.ReplaceUsesWith(swizzled); - } -} - -void PatchImageInterpretation(IR::Block& block, IR::Inst& inst, Info& info) { - const auto binding = inst.Arg(0).U32(); - const auto image_res = info.images[binding & 0xFFFF]; - const auto image = image_res.GetSharp(info); - if (!image.Valid() || !image_res.IsStorage(image)) { - // Don't need to swizzle invalid or non-storage image. - return; - } - - IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; - if (inst.GetOpcode() == IR::Opcode::ImageWrite) { - inst.SetArg(4, ApplySwizzle(ir, inst.Arg(4), image.DstSelect())); - } else if (inst.GetOpcode() == IR::Opcode::ImageRead) { - const auto inst_info = inst.Flags(); - const auto lod = inst.Arg(2); - const auto ms = inst.Arg(3); - const auto texel = - ir.ImageRead(inst.Arg(0), inst.Arg(1), lod.IsEmpty() ? IR::U32{} : IR::U32{lod}, - ms.IsEmpty() ? IR::U32{} : IR::U32{ms}, inst_info); - const auto swizzled = ApplySwizzle(ir, texel, image.DstSelect()); - inst.ReplaceUsesWith(swizzled); - } -} - -void PatchDataRingInstruction(IR::Block& block, IR::Inst& inst, Info& info, - Descriptors& descriptors) { - // Insert gds binding in the shader if it doesn't exist already. - // The buffer is used for append/consume counters. - constexpr static AmdGpu::Buffer GdsSharp{.base_address = 1}; - const u32 binding = descriptors.Add(BufferResource{ - .used_types = IR::Type::U32, - .inline_cbuf = GdsSharp, - .is_gds_buffer = true, - .is_written = true, - }); - - const auto pred = [](const IR::Inst* inst) -> std::optional { - if (inst->GetOpcode() == IR::Opcode::GetUserData) { - return inst; + const auto is_storage = image_res.IsStorage(image); + if (inst.GetOpcode() == IR::Opcode::ImageRead) { + auto texel = ir.ImageRead(handle, coords, lod, ms, inst_info); + if (is_storage) { + // Storage image requires shader swizzle. + texel = ApplySwizzle(ir, texel, image.DstSelect()); } - return std::nullopt; - }; + const auto converted = + ApplyReadNumberConversionVec4(ir, texel, image.GetNumberConversion()); + inst.ReplaceUsesWith(converted); + } else { + inst.SetArg(1, coords); + if (inst.GetOpcode() == IR::Opcode::ImageWrite) { + inst.SetArg(2, lod); + inst.SetArg(3, ms); - // Attempt to deduce the GDS address of counter at compile time. - const u32 gds_addr = [&] { - const IR::Value& gds_offset = inst.Arg(0); - if (gds_offset.IsImmediate()) { - // Nothing to do, offset is known. - return gds_offset.U32() & 0xFFFF; + auto texel = inst.Arg(4); + if (is_storage) { + // Storage image requires shader swizzle. + texel = ApplySwizzle(ir, texel, image.DstSelect()); + } + const auto converted = + ApplyWriteNumberConversionVec4(ir, texel, image.GetNumberConversion()); + inst.SetArg(4, converted); } - const auto result = IR::BreadthFirstSearch(&inst, pred); - ASSERT_MSG(result, "Unable to track M0 source"); - - // M0 must be set by some user data register. - const IR::Inst* prod = gds_offset.InstRecursive(); - const u32 ud_reg = u32(result.value()->Arg(0).ScalarReg()); - u32 m0_val = info.user_data[ud_reg] >> 16; - if (prod->GetOpcode() == IR::Opcode::IAdd32) { - m0_val += prod->Arg(1).U32(); - } - return m0_val & 0xFFFF; - }(); - - // Patch instruction. - IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; - inst.SetArg(0, ir.Imm32(gds_addr >> 2)); - inst.SetArg(1, ir.Imm32(binding)); + } } void ResourceTrackingPass(IR::Program& program) { // Iterate resource instructions and patch them after finding the sharp. auto& info = program.info; + // Pass 1: Track resource sharps Descriptors descriptors{info}; for (IR::Block* const block : program.blocks) { for (IR::Inst& inst : block->Instructions()) { if (IsBufferInstruction(inst)) { - PatchBufferInstruction(*block, inst, info, descriptors); - continue; - } - if (IsTextureBufferInstruction(inst)) { - PatchTextureBufferInstruction(*block, inst, info, descriptors); - continue; - } - if (IsImageInstruction(inst)) { - PatchImageInstruction(*block, inst, info, descriptors); - continue; - } - if (IsDataRingInstruction(inst)) { - PatchDataRingInstruction(*block, inst, info, descriptors); + PatchBufferSharp(*block, inst, info, descriptors); + } else if (IsTextureBufferInstruction(inst)) { + PatchTextureBufferSharp(*block, inst, info, descriptors); + } else if (IsImageInstruction(inst)) { + PatchImageSharp(*block, inst, info, descriptors); + } else if (IsDataRingInstruction(inst)) { + PatchDataRingAccess(*block, inst, info, descriptors); } } } - // Second pass to reinterpret format read/write where needed, since we now know - // the bindings and their properties. + + // Pass 2: Patch instruction args for (IR::Block* const block : program.blocks) { for (IR::Inst& inst : block->Instructions()) { - if (IsTextureBufferInstruction(inst)) { - PatchTextureBufferInterpretation(*block, inst, info); - continue; - } - if (IsImageInstruction(inst)) { - PatchImageInterpretation(*block, inst, info); + if (IsBufferInstruction(inst)) { + PatchBufferArgs(*block, inst, info); + } else if (IsTextureBufferInstruction(inst)) { + PatchTextureBufferArgs(*block, inst, info); + } else if (IsImageInstruction(inst)) { + PatchImageArgs(*block, inst, info); } } } diff --git a/src/shader_recompiler/ir/reinterpret.h b/src/shader_recompiler/ir/reinterpret.h index 73d587a56..b65b19928 100644 --- a/src/shader_recompiler/ir/reinterpret.h +++ b/src/shader_recompiler/ir/reinterpret.h @@ -4,7 +4,7 @@ #pragma once #include "shader_recompiler/ir/ir_emitter.h" -#include "video_core/amdgpu/resource.h" +#include "video_core/amdgpu/types.h" namespace Shader::IR { @@ -21,4 +21,66 @@ inline Value ApplySwizzle(IREmitter& ir, const Value& vector, const AmdGpu::Comp return swizzled; } +/// Applies a number conversion in the read direction. +inline F32 ApplyReadNumberConversion(IREmitter& ir, const F32& value, + const AmdGpu::NumberConversion& conversion) { + switch (conversion) { + case AmdGpu::NumberConversion::None: + return value; + case AmdGpu::NumberConversion::UintToUscaled: + return ir.ConvertUToF(32, 32, ir.BitCast(value)); + case AmdGpu::NumberConversion::SintToSscaled: + return ir.ConvertSToF(32, 32, ir.BitCast(value)); + case AmdGpu::NumberConversion::UnormToUbnorm: + // Convert 0...1 to -1...1 + return ir.FPSub(ir.FPMul(value, ir.Imm32(2.f)), ir.Imm32(1.f)); + default: + UNREACHABLE(); + } +} + +inline Value ApplyReadNumberConversionVec4(IREmitter& ir, const Value& value, + const AmdGpu::NumberConversion& conversion) { + if (conversion == AmdGpu::NumberConversion::None) { + return value; + } + const auto x = ApplyReadNumberConversion(ir, F32{ir.CompositeExtract(value, 0)}, conversion); + const auto y = ApplyReadNumberConversion(ir, F32{ir.CompositeExtract(value, 1)}, conversion); + const auto z = ApplyReadNumberConversion(ir, F32{ir.CompositeExtract(value, 2)}, conversion); + const auto w = ApplyReadNumberConversion(ir, F32{ir.CompositeExtract(value, 3)}, conversion); + return ir.CompositeConstruct(x, y, z, w); +} + +/// Applies a number conversion in the write direction. +inline F32 ApplyWriteNumberConversion(IREmitter& ir, const F32& value, + const AmdGpu::NumberConversion& conversion) { + switch (conversion) { + case AmdGpu::NumberConversion::None: + return value; + case AmdGpu::NumberConversion::UintToUscaled: + // Need to return float type to maintain IR semantics. + return ir.BitCast(U32{ir.ConvertFToU(32, value)}); + case AmdGpu::NumberConversion::SintToSscaled: + // Need to return float type to maintain IR semantics. + return ir.BitCast(U32{ir.ConvertFToS(32, value)}); + case AmdGpu::NumberConversion::UnormToUbnorm: + // Convert -1...1 to 0...1 + return ir.FPDiv(ir.FPAdd(value, ir.Imm32(1.f)), ir.Imm32(2.f)); + default: + UNREACHABLE(); + } +} + +inline Value ApplyWriteNumberConversionVec4(IREmitter& ir, const Value& value, + const AmdGpu::NumberConversion& conversion) { + if (conversion == AmdGpu::NumberConversion::None) { + return value; + } + const auto x = ApplyWriteNumberConversion(ir, F32{ir.CompositeExtract(value, 0)}, conversion); + const auto y = ApplyWriteNumberConversion(ir, F32{ir.CompositeExtract(value, 1)}, conversion); + const auto z = ApplyWriteNumberConversion(ir, F32{ir.CompositeExtract(value, 2)}, conversion); + const auto w = ApplyWriteNumberConversion(ir, F32{ir.CompositeExtract(value, 3)}, conversion); + return ir.CompositeConstruct(x, y, z, w); +} + } // namespace Shader::IR diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index 781a0b14a..cf49b0879 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -180,6 +180,7 @@ struct FragmentRuntimeInfo { std::array inputs; struct PsColorBuffer { AmdGpu::NumberFormat num_format; + AmdGpu::NumberConversion num_conversion; AmdGpu::CompMapping swizzle; auto operator<=>(const PsColorBuffer&) const noexcept = default; diff --git a/src/shader_recompiler/specialization.h b/src/shader_recompiler/specialization.h index f8a86c63b..f58d2e2d3 100644 --- a/src/shader_recompiler/specialization.h +++ b/src/shader_recompiler/specialization.h @@ -32,6 +32,7 @@ struct BufferSpecialization { struct TextureBufferSpecialization { bool is_integer = false; AmdGpu::CompMapping dst_select{}; + AmdGpu::NumberConversion num_conversion{}; auto operator<=>(const TextureBufferSpecialization&) const = default; }; @@ -41,6 +42,7 @@ struct ImageSpecialization { bool is_integer = false; bool is_storage = false; AmdGpu::CompMapping dst_select{}; + AmdGpu::NumberConversion num_conversion{}; auto operator<=>(const ImageSpecialization&) const = default; }; @@ -107,6 +109,7 @@ struct StageSpecialization { [](auto& spec, const auto& desc, AmdGpu::Buffer sharp) { spec.is_integer = AmdGpu::IsInteger(sharp.GetNumberFmt()); spec.dst_select = sharp.DstSelect(); + spec.num_conversion = sharp.GetNumberConversion(); }); ForEachSharp(binding, images, info->images, [](auto& spec, const auto& desc, AmdGpu::Image sharp) { @@ -116,6 +119,7 @@ struct StageSpecialization { if (spec.is_storage) { spec.dst_select = sharp.DstSelect(); } + spec.num_conversion = sharp.GetNumberConversion(); }); ForEachSharp(binding, fmasks, info->fmasks, [](auto& spec, const auto& desc, AmdGpu::Image sharp) { diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index 0f1783057..837b73d89 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -20,9 +20,9 @@ #include "common/types.h" #include "common/unique_function.h" #include "shader_recompiler/params.h" -#include "types.h" #include "video_core/amdgpu/pixel_format.h" #include "video_core/amdgpu/resource.h" +#include "video_core/amdgpu/types.h" namespace Vulkan { class Rasterizer; @@ -902,6 +902,10 @@ struct Liverpool { : info.number_type.Value()); } + [[nodiscard]] NumberConversion GetNumberConversion() const { + return MapNumberConversion(info.number_type); + } + [[nodiscard]] CompMapping Swizzle() const { // clang-format off static constexpr std::array, 4> mrt_swizzles{{ @@ -938,7 +942,7 @@ struct Liverpool { const auto swap_idx = static_cast(info.comp_swap.Value()); const auto components_idx = NumComponents(info.format) - 1; const auto mrt_swizzle = mrt_swizzles[swap_idx][components_idx]; - return RemapComponents(info.format, mrt_swizzle); + return RemapSwizzle(info.format, mrt_swizzle); } }; diff --git a/src/video_core/amdgpu/pixel_format.cpp b/src/video_core/amdgpu/pixel_format.cpp index b13fc2d11..881c33e44 100644 --- a/src/video_core/amdgpu/pixel_format.cpp +++ b/src/video_core/amdgpu/pixel_format.cpp @@ -100,7 +100,7 @@ std::string_view NameOf(NumberFormat fmt) { return "Srgb"; case NumberFormat::Ubnorm: return "Ubnorm"; - case NumberFormat::UbnromNz: + case NumberFormat::UbnormNz: return "UbnormNz"; case NumberFormat::Ubint: return "Ubint"; diff --git a/src/video_core/amdgpu/resource.h b/src/video_core/amdgpu/resource.h index 1d9673850..ffee7964a 100644 --- a/src/video_core/amdgpu/resource.h +++ b/src/video_core/amdgpu/resource.h @@ -11,96 +11,6 @@ namespace AmdGpu { -enum class CompSwizzle : u32 { - Zero = 0, - One = 1, - Red = 4, - Green = 5, - Blue = 6, - Alpha = 7, -}; - -struct CompMapping { - CompSwizzle r : 3; - CompSwizzle g : 3; - CompSwizzle b : 3; - CompSwizzle a : 3; - - auto operator<=>(const CompMapping& other) const = default; - - template - [[nodiscard]] std::array Apply(const std::array& data) const { - return { - ApplySingle(data, r), - ApplySingle(data, g), - ApplySingle(data, b), - ApplySingle(data, a), - }; - } - -private: - template - T ApplySingle(const std::array& data, const CompSwizzle swizzle) const { - switch (swizzle) { - case CompSwizzle::Zero: - return T(0); - case CompSwizzle::One: - return T(1); - case CompSwizzle::Red: - return data[0]; - case CompSwizzle::Green: - return data[1]; - case CompSwizzle::Blue: - return data[2]; - case CompSwizzle::Alpha: - return data[3]; - default: - UNREACHABLE(); - } - } -}; - -inline DataFormat RemapDataFormat(const DataFormat format) { - switch (format) { - case DataFormat::Format11_11_10: - return DataFormat::Format10_11_11; - case DataFormat::Format10_10_10_2: - return DataFormat::Format2_10_10_10; - case DataFormat::Format5_5_5_1: - return DataFormat::Format1_5_5_5; - default: - return format; - } -} - -inline NumberFormat RemapNumberFormat(const NumberFormat format) { - return format; -} - -inline CompMapping RemapComponents(const DataFormat format, const CompMapping components) { - switch (format) { - case DataFormat::Format11_11_10: { - CompMapping result; - result.r = components.b; - result.g = components.g; - result.b = components.r; - result.a = components.a; - return result; - } - case DataFormat::Format10_10_10_2: - case DataFormat::Format5_5_5_1: { - CompMapping result; - result.r = components.a; - result.g = components.b; - result.b = components.g; - result.a = components.r; - return result; - } - default: - return components; - } -} - // Table 8.5 Buffer Resource Descriptor [Sea Islands Series Instruction Set Architecture] struct Buffer { u64 base_address : 44; @@ -140,7 +50,7 @@ struct Buffer { .b = CompSwizzle(dst_sel_z), .a = CompSwizzle(dst_sel_w), }; - return RemapComponents(DataFormat(data_format), dst_sel); + return RemapSwizzle(DataFormat(data_format), dst_sel); } NumberFormat GetNumberFmt() const noexcept { @@ -151,6 +61,10 @@ struct Buffer { return RemapDataFormat(DataFormat(data_format)); } + NumberConversion GetNumberConversion() const noexcept { + return MapNumberConversion(NumberFormat(num_format)); + } + u32 GetStride() const noexcept { return stride; } @@ -305,7 +219,7 @@ struct Image { .b = CompSwizzle(dst_sel_z), .a = CompSwizzle(dst_sel_w), }; - return RemapComponents(DataFormat(data_format), dst_sel); + return RemapSwizzle(DataFormat(data_format), dst_sel); } u32 Pitch() const { @@ -354,6 +268,10 @@ struct Image { return RemapNumberFormat(NumberFormat(num_format)); } + NumberConversion GetNumberConversion() const noexcept { + return MapNumberConversion(NumberFormat(num_format)); + } + TilingMode GetTilingMode() const { if (tiling_index >= 0 && tiling_index <= 7) { return tiling_index == 5 ? TilingMode::Texture_MicroTiled diff --git a/src/video_core/amdgpu/types.h b/src/video_core/amdgpu/types.h index fa8491665..a19e53256 100644 --- a/src/video_core/amdgpu/types.h +++ b/src/video_core/amdgpu/types.h @@ -5,6 +5,7 @@ #include #include +#include "common/assert.h" #include "common/types.h" namespace AmdGpu { @@ -177,11 +178,130 @@ enum class NumberFormat : u32 { Float = 7, Srgb = 9, Ubnorm = 10, - UbnromNz = 11, + UbnormNz = 11, Ubint = 12, Ubscaled = 13, }; +enum class CompSwizzle : u32 { + Zero = 0, + One = 1, + Red = 4, + Green = 5, + Blue = 6, + Alpha = 7, +}; + +enum class NumberConversion : u32 { + None, + UintToUscaled, + SintToSscaled, + UnormToUbnorm, +}; + +struct CompMapping { + CompSwizzle r : 3; + CompSwizzle g : 3; + CompSwizzle b : 3; + CompSwizzle a : 3; + + auto operator<=>(const CompMapping& other) const = default; + + template + [[nodiscard]] std::array Apply(const std::array& data) const { + return { + ApplySingle(data, r), + ApplySingle(data, g), + ApplySingle(data, b), + ApplySingle(data, a), + }; + } + +private: + template + T ApplySingle(const std::array& data, const CompSwizzle swizzle) const { + switch (swizzle) { + case CompSwizzle::Zero: + return T(0); + case CompSwizzle::One: + return T(1); + case CompSwizzle::Red: + return data[0]; + case CompSwizzle::Green: + return data[1]; + case CompSwizzle::Blue: + return data[2]; + case CompSwizzle::Alpha: + return data[3]; + default: + UNREACHABLE(); + } + } +}; + +inline DataFormat RemapDataFormat(const DataFormat format) { + switch (format) { + case DataFormat::Format11_11_10: + return DataFormat::Format10_11_11; + case DataFormat::Format10_10_10_2: + return DataFormat::Format2_10_10_10; + case DataFormat::Format5_5_5_1: + return DataFormat::Format1_5_5_5; + default: + return format; + } +} + +inline NumberFormat RemapNumberFormat(const NumberFormat format) { + switch (format) { + case NumberFormat::Uscaled: + return NumberFormat::Uint; + case NumberFormat::Sscaled: + return NumberFormat::Sint; + case NumberFormat::Ubnorm: + return NumberFormat::Unorm; + default: + return format; + } +} + +inline CompMapping RemapSwizzle(const DataFormat format, const CompMapping swizzle) { + switch (format) { + case DataFormat::Format11_11_10: { + CompMapping result; + result.r = swizzle.b; + result.g = swizzle.g; + result.b = swizzle.r; + result.a = swizzle.a; + return result; + } + case DataFormat::Format10_10_10_2: + case DataFormat::Format5_5_5_1: { + CompMapping result; + result.r = swizzle.a; + result.g = swizzle.b; + result.b = swizzle.g; + result.a = swizzle.r; + return result; + } + default: + return swizzle; + } +} + +inline NumberConversion MapNumberConversion(const NumberFormat format) { + switch (format) { + case NumberFormat::Uscaled: + return NumberConversion::UintToUscaled; + case NumberFormat::Sscaled: + return NumberConversion::SintToSscaled; + case NumberFormat::Ubnorm: + return NumberConversion::UnormToUbnorm; + default: + return NumberConversion::None; + } +} + } // namespace AmdGpu template <> diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp index 690d26cfc..9695e127f 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp @@ -447,7 +447,7 @@ static constexpr vk::FormatFeatureFlags2 GetNumberFormatFeatureFlags( case AmdGpu::NumberFormat::Srgb: return ImageRead | Mrt; case AmdGpu::NumberFormat::Ubnorm: - case AmdGpu::NumberFormat::UbnromNz: + case AmdGpu::NumberFormat::UbnormNz: case AmdGpu::NumberFormat::Ubint: case AmdGpu::NumberFormat::Ubscaled: return ImageRead; @@ -468,6 +468,7 @@ static constexpr SurfaceFormatInfo CreateSurfaceFormatInfo(const AmdGpu::DataFor } std::span SurfaceFormats() { + // Uscaled, Sscaled, and Ubnorm formats are automatically remapped and handled in shader. static constexpr std::array formats{ // Invalid CreateSurfaceFormatInfo(AmdGpu::DataFormat::FormatInvalid, AmdGpu::NumberFormat::Unorm, @@ -490,7 +491,7 @@ std::span SurfaceFormats() { vk::Format::eUndefined), CreateSurfaceFormatInfo(AmdGpu::DataFormat::FormatInvalid, AmdGpu::NumberFormat::Ubnorm, vk::Format::eUndefined), - CreateSurfaceFormatInfo(AmdGpu::DataFormat::FormatInvalid, AmdGpu::NumberFormat::UbnromNz, + CreateSurfaceFormatInfo(AmdGpu::DataFormat::FormatInvalid, AmdGpu::NumberFormat::UbnormNz, vk::Format::eUndefined), CreateSurfaceFormatInfo(AmdGpu::DataFormat::FormatInvalid, AmdGpu::NumberFormat::Ubint, vk::Format::eUndefined), @@ -501,10 +502,6 @@ std::span SurfaceFormats() { vk::Format::eR8Unorm), CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8, AmdGpu::NumberFormat::Snorm, vk::Format::eR8Snorm), - CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8, AmdGpu::NumberFormat::Uscaled, - vk::Format::eR8Uscaled), - CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8, AmdGpu::NumberFormat::Sscaled, - vk::Format::eR8Sscaled), CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8, AmdGpu::NumberFormat::Uint, vk::Format::eR8Uint), CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8, AmdGpu::NumberFormat::Sint, @@ -516,10 +513,6 @@ std::span SurfaceFormats() { vk::Format::eR16Unorm), CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16, AmdGpu::NumberFormat::Snorm, vk::Format::eR16Snorm), - CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16, AmdGpu::NumberFormat::Uscaled, - vk::Format::eR16Uscaled), - CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16, AmdGpu::NumberFormat::Sscaled, - vk::Format::eR16Sscaled), CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16, AmdGpu::NumberFormat::Uint, vk::Format::eR16Uint), CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16, AmdGpu::NumberFormat::Sint, @@ -531,10 +524,6 @@ std::span SurfaceFormats() { vk::Format::eR8G8Unorm), CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8, AmdGpu::NumberFormat::Snorm, vk::Format::eR8G8Snorm), - CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8, AmdGpu::NumberFormat::Uscaled, - vk::Format::eR8G8Uscaled), - CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8, AmdGpu::NumberFormat::Sscaled, - vk::Format::eR8G8Sscaled), CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8, AmdGpu::NumberFormat::Uint, vk::Format::eR8G8Uint), CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8, AmdGpu::NumberFormat::Sint, @@ -553,10 +542,6 @@ std::span SurfaceFormats() { vk::Format::eR16G16Unorm), CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16_16, AmdGpu::NumberFormat::Snorm, vk::Format::eR16G16Snorm), - CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16_16, AmdGpu::NumberFormat::Uscaled, - vk::Format::eR16G16Uscaled), - CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16_16, AmdGpu::NumberFormat::Sscaled, - vk::Format::eR16G16Sscaled), CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16_16, AmdGpu::NumberFormat::Uint, vk::Format::eR16G16Uint), CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16_16, AmdGpu::NumberFormat::Sint, @@ -573,10 +558,6 @@ std::span SurfaceFormats() { vk::Format::eA2B10G10R10UnormPack32), CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format2_10_10_10, AmdGpu::NumberFormat::Snorm, vk::Format::eA2B10G10R10SnormPack32), - CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format2_10_10_10, AmdGpu::NumberFormat::Uscaled, - vk::Format::eA2B10G10R10UscaledPack32), - CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format2_10_10_10, AmdGpu::NumberFormat::Sscaled, - vk::Format::eA2B10G10R10SscaledPack32), CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format2_10_10_10, AmdGpu::NumberFormat::Uint, vk::Format::eA2B10G10R10UintPack32), CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format2_10_10_10, AmdGpu::NumberFormat::Sint, @@ -586,10 +567,6 @@ std::span SurfaceFormats() { vk::Format::eR8G8B8A8Unorm), CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8_8_8, AmdGpu::NumberFormat::Snorm, vk::Format::eR8G8B8A8Snorm), - CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8_8_8, AmdGpu::NumberFormat::Uscaled, - vk::Format::eR8G8B8A8Uscaled), - CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8_8_8, AmdGpu::NumberFormat::Sscaled, - vk::Format::eR8G8B8A8Sscaled), CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8_8_8, AmdGpu::NumberFormat::Uint, vk::Format::eR8G8B8A8Uint), CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8_8_8, AmdGpu::NumberFormat::Sint, @@ -608,10 +585,6 @@ std::span SurfaceFormats() { vk::Format::eR16G16B16A16Unorm), CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16_16_16_16, AmdGpu::NumberFormat::Snorm, vk::Format::eR16G16B16A16Snorm), - CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16_16_16_16, - AmdGpu::NumberFormat::Uscaled, vk::Format::eR16G16B16A16Uscaled), - CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16_16_16_16, - AmdGpu::NumberFormat::Sscaled, vk::Format::eR16G16B16A16Sscaled), CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16_16_16_16, AmdGpu::NumberFormat::Uint, vk::Format::eR16G16B16A16Uint), CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16_16_16_16, AmdGpu::NumberFormat::Sint, diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index c8f4999b1..fa10831a0 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -32,6 +32,7 @@ struct GraphicsPipelineKey { u32 num_color_attachments; std::array color_formats; std::array color_num_formats; + std::array color_num_conversions; std::array color_swizzles; vk::Format depth_format; vk::Format stencil_format; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index ba069dae1..9cfc7c277 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -168,6 +168,7 @@ const Shader::RuntimeInfo& PipelineCache::BuildRuntimeInfo(Stage stage, LogicalS for (u32 i = 0; i < Shader::MaxColorBuffers; i++) { info.fs_info.color_buffers[i] = { .num_format = graphics_key.color_num_formats[i], + .num_conversion = graphics_key.color_num_conversions[i], .swizzle = graphics_key.color_swizzles[i], }; } @@ -302,6 +303,7 @@ bool PipelineCache::RefreshGraphicsKey() { key.num_color_attachments = 0; key.color_formats.fill(vk::Format::eUndefined); key.color_num_formats.fill(AmdGpu::NumberFormat::Unorm); + key.color_num_conversions.fill(AmdGpu::NumberConversion::None); key.blend_controls.fill({}); key.write_masks.fill({}); key.color_swizzles.fill({}); @@ -330,6 +332,7 @@ bool PipelineCache::RefreshGraphicsKey() { key.color_formats[remapped_cb] = LiverpoolToVK::SurfaceFormat(col_buf.GetDataFmt(), col_buf.GetNumberFmt()); key.color_num_formats[remapped_cb] = col_buf.GetNumberFmt(); + key.color_num_conversions[remapped_cb] = col_buf.GetNumberConversion(); key.color_swizzles[remapped_cb] = col_buf.Swizzle(); } From 32fc983ef86b7c0b1bbff973a0130f4582a79b04 Mon Sep 17 00:00:00 2001 From: F1219R <109141852+F1219R@users.noreply.github.com> Date: Tue, 7 Jan 2025 12:58:33 +0100 Subject: [PATCH 375/549] Update sq translation (#2084) * Update sq translation * Update sq translation --- src/qt_gui/translations/sq.ts | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index 80f3876d4..d4936170b 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -146,19 +146,19 @@ Compatibility... - Compatibility... + Përputhshmëria... Update database - Update database + Përditëso bazën e të dhënave View report - View report + Shiko raportin Submit a report - Submit a report + Paraqit një raport Shortcut creation @@ -285,15 +285,15 @@ List View - Pamja e Listës + Pamja me List Grid View - Pamja e Rrjetës + Pamja me Rrjetë Elf Viewer - Shikuesi i Elf + Shikuesi i ELF Game Install Directory @@ -381,15 +381,15 @@ Download Cheats For All Installed Games - Shkarko Mashtrime Për Të Gjitha Lojërat e Instaluara + Shkarko mashtrime për të gjitha lojërat e instaluara Download Patches For All Games - Shkarko Arna Për Të Gjitha Lojërat e Instaluara + Shkarko arna për të gjitha lojërat e instaluara Download Complete - Shkarkimi Përfundoi + Shkarkimi përfundoi You have downloaded cheats for all the games you have installed. @@ -558,11 +558,11 @@ Trophy Key - Trophy Key + Çelësi i Trofeve Trophy - Trophy + Trofeu Logger @@ -690,7 +690,7 @@ GUI Settings - Cilësimet e GUI + Cilësimet e GUI-së Disable Trophy Pop-ups @@ -778,7 +778,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. + Çelësi i Trofeve:\nÇelësi përdoret për të deshifruar trofetë. Duhet të merret nga konsola jote me jailbreak.\nDuhet të përmbajë vetëm karaktere hex. logTypeGroupBox @@ -802,7 +802,7 @@ hideCursorGroupBox - Fsheh kursorin:\nZgjidh kur do të fshihet kursori:\nKurrë: Do ta shohësh gjithmonë miun.\nInaktiv: Vendos një kohë për ta fshehur pas mosveprimit.\nGjithmonë: nuk do ta shohësh kurrë miun. + Fsheh kursorin:\nZgjidh kur do të fshihet kursori:\nKurrë: Do ta shohësh gjithmonë miun.\nJoaktiv: Vendos një kohë për ta fshehur pas mosveprimit.\nGjithmonë: nuk do ta shohësh kurrë miun. idleTimeoutGroupBox @@ -1172,7 +1172,7 @@ h - h + o m @@ -1333,4 +1333,4 @@ TB - \ No newline at end of file + From 4df0d9c035f957e91f4615bbc3cff7e4a554e4e0 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Tue, 7 Jan 2025 14:30:05 +0100 Subject: [PATCH 376/549] Add support for true fullscreen (#2016) * Support for true fullscreen * clang * Re-add mistakenly deleted line * Size I adjusted the size of the entire screen. trophies font size and added a margin so it wouldn't be so spaced out. --------- Co-authored-by: DanielSvoboda --- src/common/config.cpp | 15 +++++++++++-- src/common/config.h | 7 +++--- src/main.cpp | 2 +- src/qt_gui/main.cpp | 4 ++-- src/qt_gui/settings_dialog.cpp | 5 ++++- src/qt_gui/settings_dialog.ui | 40 +++++++++++++++++++++++++++++++++- src/sdl_window.cpp | 18 ++++++++++++++- 7 files changed, 80 insertions(+), 11 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index 1838f35fc..b46ab8d6e 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -33,6 +33,7 @@ namespace Config { static bool isNeo = false; static bool isFullscreen = false; +static std::string fullscreenMode = "borderless"; static bool playBGM = false; static bool isTrophyPopupDisabled = false; static int BGMvolume = 50; @@ -105,10 +106,14 @@ bool isNeoModeConsole() { return isNeo; } -bool isFullscreenMode() { +bool getIsFullscreen() { return isFullscreen; } +std::string getFullscreenMode() { + return fullscreenMode; +} + bool getisTrophyPopupDisabled() { return isTrophyPopupDisabled; } @@ -309,10 +314,14 @@ void setVblankDiv(u32 value) { vblankDivider = value; } -void setFullscreenMode(bool enable) { +void setIsFullscreen(bool enable) { isFullscreen = enable; } +void setFullscreenMode(std::string mode) { + fullscreenMode = mode; +} + void setisTrophyPopupDisabled(bool disable) { isTrophyPopupDisabled = disable; } @@ -575,6 +584,7 @@ void load(const std::filesystem::path& path) { isNeo = toml::find_or(general, "isPS4Pro", false); isFullscreen = toml::find_or(general, "Fullscreen", false); + fullscreenMode = toml::find_or(general, "FullscreenMode", "borderless"); playBGM = toml::find_or(general, "playBGM", false); isTrophyPopupDisabled = toml::find_or(general, "isTrophyPopupDisabled", false); BGMvolume = toml::find_or(general, "BGMvolume", 50); @@ -701,6 +711,7 @@ void save(const std::filesystem::path& path) { data["General"]["isPS4Pro"] = isNeo; data["General"]["Fullscreen"] = isFullscreen; + data["General"]["FullscreenMode"] = fullscreenMode; data["General"]["isTrophyPopupDisabled"] = isTrophyPopupDisabled; data["General"]["playBGM"] = playBGM; data["General"]["BGMvolume"] = BGMvolume; diff --git a/src/common/config.h b/src/common/config.h index d2860bec5..6e6a5d960 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -17,9 +17,9 @@ void saveMainWindow(const std::filesystem::path& path); std::string getTrophyKey(); void setTrophyKey(std::string key); - +bool getIsFullscreen(); +std::string getFullscreenMode(); bool isNeoModeConsole(); -bool isFullscreenMode(); bool getPlayBGM(); int getBGMvolume(); bool getisTrophyPopupDisabled(); @@ -66,7 +66,8 @@ void setVblankDiv(u32 value); void setGpuId(s32 selectedGpuId); void setScreenWidth(u32 width); void setScreenHeight(u32 height); -void setFullscreenMode(bool enable); +void setIsFullscreen(bool enable); +void setFullscreenMode(std::string mode); void setisTrophyPopupDisabled(bool disable); void setPlayBGM(bool enable); void setBGMvolume(int volume); diff --git a/src/main.cpp b/src/main.cpp index bdbab89c9..54772870c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -86,7 +86,7 @@ int main(int argc, char* argv[]) { exit(1); } // Set fullscreen mode without saving it to config file - Config::setFullscreenMode(is_fullscreen); + Config::setIsFullscreen(is_fullscreen); }}, {"--fullscreen", [&](int& i) { arg_map["-f"](i); }}, {"--add-game-folder", diff --git a/src/qt_gui/main.cpp b/src/qt_gui/main.cpp index ac731fdce..2d524e199 100644 --- a/src/qt_gui/main.cpp +++ b/src/qt_gui/main.cpp @@ -97,7 +97,7 @@ int main(int argc, char* argv[]) { exit(1); } // Set fullscreen mode without saving it to config file - Config::setFullscreenMode(is_fullscreen); + Config::setIsFullscreen(is_fullscreen); }}, {"--fullscreen", [&](int& i) { arg_map["-f"](i); }}, {"--add-game-folder", @@ -190,4 +190,4 @@ int main(int argc, char* argv[]) { // Show the main window and run the Qt application m_main_window->show(); return a.exec(); -} \ No newline at end of file +} diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index 1a03345e4..193ce5cd8 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -300,6 +300,8 @@ void SettingsDialog::LoadValuesFromConfig() { ui->discordRPCCheckbox->setChecked( toml::find_or(data, "General", "enableDiscordRPC", true)); ui->fullscreenCheckBox->setChecked(toml::find_or(data, "General", "Fullscreen", false)); + ui->fullscreenModeComboBox->setCurrentText(QString::fromStdString( + toml::find_or(data, "General", "FullscreenMode", "Borderless"))); ui->separateUpdatesCheckBox->setChecked( toml::find_or(data, "General", "separateUpdateEnabled", false)); ui->showSplashCheckBox->setChecked(toml::find_or(data, "General", "showSplash", false)); @@ -534,8 +536,9 @@ void SettingsDialog::UpdateSettings() { const QVector TouchPadIndex = {"left", "center", "right", "none"}; Config::setBackButtonBehavior(TouchPadIndex[ui->backButtonBehaviorComboBox->currentIndex()]); + Config::setIsFullscreen(ui->fullscreenCheckBox->isChecked()); + Config::setFullscreenMode(ui->fullscreenModeComboBox->currentText().toStdString()); Config::setIsMotionControlsEnabled(ui->motionControlsCheckBox->isChecked()); - Config::setFullscreenMode(ui->fullscreenCheckBox->isChecked()); Config::setisTrophyPopupDisabled(ui->disableTrophycheckBox->isChecked()); Config::setPlayBGM(ui->playBGMCheckBox->isChecked()); Config::setLogType(ui->logTypeComboBox->currentText().toStdString()); diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index cefe1f7c7..fbfee5a99 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -12,7 +12,7 @@ 0 0 970 - 670 + 750 @@ -133,6 +133,35 @@ Enable Fullscreen + + + + + Fullscreen Mode + + + + + + + 0 + 0 + + + + + Borderless + + + + + True + + + + + + @@ -536,6 +565,9 @@ 0 + + 80 + @@ -566,6 +598,12 @@ 0 + + + 10 + false + + diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index d694b0939..318b3349b 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -93,7 +93,23 @@ WindowSDL::WindowSDL(s32 width_, s32 height_, Input::GameController* controller_ } SDL_SetWindowMinimumSize(window, 640, 360); - SDL_SetWindowFullscreen(window, Config::isFullscreenMode()); + + bool error = false; + const SDL_DisplayID displayIndex = SDL_GetDisplayForWindow(window); + if (displayIndex < 0) { + LOG_ERROR(Frontend, "Error getting display index: {}", SDL_GetError()); + error = true; + } + const SDL_DisplayMode* displayMode; + if ((displayMode = SDL_GetCurrentDisplayMode(displayIndex)) == 0) { + LOG_ERROR(Frontend, "Error getting display mode: {}", SDL_GetError()); + error = true; + } + if (!error) { + SDL_SetWindowFullscreenMode(window, + Config::getFullscreenMode() == "True" ? displayMode : NULL); + } + SDL_SetWindowFullscreen(window, Config::getIsFullscreen()); SDL_InitSubSystem(SDL_INIT_GAMEPAD); controller->TryOpenSDLController(); From a4c18b14344d678334471410f2aadeea29184a47 Mon Sep 17 00:00:00 2001 From: Florian Piesche Date: Tue, 7 Jan 2025 14:05:15 +0000 Subject: [PATCH 377/549] Move release info into metainfo.xml (#2085) --- dist/net.shadps4.shadPS4.metainfo.xml | 28 ++++++++++++++++++++++++--- dist/net.shadps4.shadPS4.releases.xml | 23 ---------------------- 2 files changed, 25 insertions(+), 26 deletions(-) delete mode 100644 dist/net.shadps4.shadPS4.releases.xml diff --git a/dist/net.shadps4.shadPS4.metainfo.xml b/dist/net.shadps4.shadPS4.metainfo.xml index bef0bec4c..d5ad992e3 100644 --- a/dist/net.shadps4.shadPS4.metainfo.xml +++ b/dist/net.shadps4.shadPS4.metainfo.xml @@ -36,10 +36,32 @@ Game - - - + + + https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.5.0 + + https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.4.0 + + + https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.3.0 + + + https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.2.0 + + + https://github.com/shadps4-emu/shadPS4/releases/tag/0.1.0 + + + https://github.com/shadps4-emu/shadPS4/releases/tag/v0.0.3 + + + https://github.com/shadps4-emu/shadPS4/releases/tag/v0.0.2 + + + https://github.com/shadps4-emu/shadPS4/releases/tag/v0.0.1 + + diff --git a/dist/net.shadps4.shadPS4.releases.xml b/dist/net.shadps4.shadPS4.releases.xml deleted file mode 100644 index 8da203fe4..000000000 --- a/dist/net.shadps4.shadPS4.releases.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.4.0 - - - https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.3.0 - - - https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.2.0 - - - https://github.com/shadps4-emu/shadPS4/releases/tag/0.1.0 - - - https://github.com/shadps4-emu/shadPS4/releases/tag/v0.0.3 - - - https://github.com/shadps4-emu/shadPS4/releases/tag/v0.0.2 - - - https://github.com/shadps4-emu/shadPS4/releases/tag/v0.0.1 - - From c055c80c6fb23cb29042cd38f3f1ae4c82fe3a55 Mon Sep 17 00:00:00 2001 From: jarred wilson <20207921+jardon@users.noreply.github.com> Date: Tue, 7 Jan 2025 09:11:01 -0600 Subject: [PATCH 378/549] Remove releases.xml references (#2087) --- CMakeLists.txt | 1 - REUSE.toml | 1 - 2 files changed, 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 886824934..8ec04dad5 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1042,7 +1042,6 @@ install(TARGETS shadps4 BUNDLE DESTINATION .) if (ENABLE_QT_GUI AND CMAKE_SYSTEM_NAME STREQUAL "Linux") install(FILES "dist/net.shadps4.shadPS4.desktop" DESTINATION "share/applications") - install(FILES "dist/net.shadps4.shadPS4.releases.xml" DESTINATION "share/metainfo/releases") install(FILES "dist/net.shadps4.shadPS4.metainfo.xml" DESTINATION "share/metainfo") install(FILES ".github/shadps4.png" DESTINATION "share/icons/hicolor/512x512/apps" RENAME "net.shadps4.shadPS4.png") install(FILES "src/images/net.shadps4.shadPS4.svg" DESTINATION "share/icons/hicolor/scalable/apps") diff --git a/REUSE.toml b/REUSE.toml index cba63adf1..55d76673d 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -11,7 +11,6 @@ path = [ "dist/net.shadps4.shadPS4.desktop", "dist/net.shadps4.shadPS4_metadata.pot", "dist/net.shadps4.shadPS4.metainfo.xml", - "dist/net.shadps4.shadPS4.releases.xml", "documents/changelog.md", "documents/Quickstart/2.png", "documents/Screenshots/*", From 3e5d4bb69c84486e9e2cd57c56b3b1a2e743b3fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quang=20Ng=C3=B4?= Date: Tue, 7 Jan 2025 22:36:56 +0700 Subject: [PATCH 379/549] Fix double closing tag in metainfo.xml (#2090) --- dist/net.shadps4.shadPS4.metainfo.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/dist/net.shadps4.shadPS4.metainfo.xml b/dist/net.shadps4.shadPS4.metainfo.xml index d5ad992e3..384cf75e8 100644 --- a/dist/net.shadps4.shadPS4.metainfo.xml +++ b/dist/net.shadps4.shadPS4.metainfo.xml @@ -62,7 +62,6 @@ https://github.com/shadps4-emu/shadPS4/releases/tag/v0.0.1 - keyboard From af8c748e9cb3edc78a3f0e189fbba41bfdba0786 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Tue, 7 Jan 2025 07:37:08 -0800 Subject: [PATCH 380/549] elf_info: Fix GCC build. (#2089) --- src/common/elf_info.h | 2 +- src/core/libraries/kernel/process.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/elf_info.h b/src/common/elf_info.h index 02eefbb7a..cb32679bb 100644 --- a/src/common/elf_info.h +++ b/src/common/elf_info.h @@ -111,7 +111,7 @@ public: return raw_firmware_ver; } - [[nodiscard]] const PSFAttributes& PSFAttributes() const { + [[nodiscard]] const PSFAttributes& GetPSFAttributes() const { ASSERT(initialized); return psf_attributes; } diff --git a/src/core/libraries/kernel/process.cpp b/src/core/libraries/kernel/process.cpp index 791a98a36..c21257c50 100644 --- a/src/core/libraries/kernel/process.cpp +++ b/src/core/libraries/kernel/process.cpp @@ -15,7 +15,7 @@ namespace Libraries::Kernel { int PS4_SYSV_ABI sceKernelIsNeoMode() { LOG_DEBUG(Kernel_Sce, "called"); return Config::isNeoModeConsole() && - Common::ElfInfo::Instance().PSFAttributes().support_neo_mode; + Common::ElfInfo::Instance().GetPSFAttributes().support_neo_mode; } int PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(int* ver) { From 8f5bcb0f1c3008f969f7a3a72a987edb2b1ba2a9 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Wed, 8 Jan 2025 03:23:40 -0800 Subject: [PATCH 381/549] file_sys: Consolidate separate update directory handling. (#2041) --- src/core/file_sys/fs.cpp | 44 +++++++++++-- src/core/file_sys/fs.h | 6 +- src/core/libraries/kernel/file_system.cpp | 76 +++-------------------- src/emulator.cpp | 40 +++--------- src/emulator.h | 2 +- 5 files changed, 61 insertions(+), 107 deletions(-) diff --git a/src/core/file_sys/fs.cpp b/src/core/file_sys/fs.cpp index bf340e9e3..7d456780b 100644 --- a/src/core/file_sys/fs.cpp +++ b/src/core/file_sys/fs.cpp @@ -40,7 +40,8 @@ void MntPoints::UnmountAll() { m_mnt_pairs.clear(); } -std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_read_only) { +std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_read_only, + bool force_base_path) { // Evil games like Turok2 pass double slashes e.g /app0//game.kpf std::string corrected_path(path); size_t pos = corrected_path.find("//"); @@ -72,7 +73,7 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_rea patch_path /= rel_path; if ((corrected_path.starts_with("/app0") || corrected_path.starts_with("/hostapp")) && - std::filesystem::exists(patch_path)) { + !force_base_path && std::filesystem::exists(patch_path)) { return patch_path; } @@ -132,8 +133,10 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_rea return std::optional(current_path); }; - if (const auto path = search(patch_path)) { - return *path; + if (!force_base_path) { + if (const auto path = search(patch_path)) { + return *path; + } } if (const auto path = search(host_path)) { return *path; @@ -144,6 +147,39 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_rea return host_path; } +// TODO: Does not handle mount points inside mount points. +void MntPoints::IterateDirectory(std::string_view guest_directory, + const IterateDirectoryCallback& callback) { + const auto base_path = GetHostPath(guest_directory, nullptr, true); + const auto patch_path = GetHostPath(guest_directory, nullptr, false); + // Only need to consider patch path if it exists and does not resolve to the same as base. + const auto apply_patch = base_path != patch_path && std::filesystem::exists(patch_path); + + // Pass 1: Any files that existed in the base directory, using patch directory if needed. + if (std::filesystem::exists(base_path)) { + for (const auto& entry : std::filesystem::directory_iterator(base_path)) { + if (apply_patch) { + const auto patch_entry_path = patch_path / entry.path().filename(); + if (std::filesystem::exists(patch_entry_path)) { + callback(patch_entry_path, !std::filesystem::is_directory(patch_entry_path)); + continue; + } + } + callback(entry.path(), !entry.is_directory()); + } + } + + // Pass 2: Any files that exist only in the patch directory. + if (apply_patch) { + for (const auto& entry : std::filesystem::directory_iterator(patch_path)) { + const auto base_entry_path = base_path / entry.path().filename(); + if (!std::filesystem::exists(base_entry_path)) { + callback(entry.path(), !entry.is_directory()); + } + } + } +} + int HandleTable::CreateHandle() { std::scoped_lock lock{m_mutex}; diff --git a/src/core/file_sys/fs.h b/src/core/file_sys/fs.h index 56df32ad0..6638b48e8 100644 --- a/src/core/file_sys/fs.h +++ b/src/core/file_sys/fs.h @@ -36,7 +36,11 @@ public: void UnmountAll(); std::filesystem::path GetHostPath(std::string_view guest_directory, - bool* is_read_only = nullptr); + bool* is_read_only = nullptr, bool force_base_path = false); + using IterateDirectoryCallback = + std::function; + void IterateDirectory(std::string_view guest_directory, + const IterateDirectoryCallback& callback); const MntPair* GetMountFromHostPath(const std::string& host_path) { std::scoped_lock lock{m_mutex}; diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index 57efbb631..2eb5d1621 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -46,17 +46,6 @@ static std::map available_device = { namespace Libraries::Kernel { -auto GetDirectoryEntries(const std::filesystem::path& path) { - std::vector files; - for (const auto& entry : std::filesystem::directory_iterator(path)) { - auto& dir_entry = files.emplace_back(); - dir_entry.name = entry.path().filename().string(); - dir_entry.isFile = !std::filesystem::is_directory(entry.path().string()); - } - - return files; -} - int PS4_SYSV_ABI sceKernelOpen(const char* raw_path, int flags, u16 mode) { LOG_INFO(Kernel_Fs, "path = {} flags = {:#x} mode = {}", raw_path, flags, mode); auto* h = Common::Singleton::Instance(); @@ -115,7 +104,12 @@ int PS4_SYSV_ABI sceKernelOpen(const char* raw_path, int flags, u16 mode) { if (create) { return handle; // dir already exists } else { - file->dirents = GetDirectoryEntries(file->m_host_name); + mnt->IterateDirectory(file->m_guest_name, + [&file](const auto& ent_path, const auto ent_is_file) { + auto& dir_entry = file->dirents.emplace_back(); + dir_entry.name = ent_path.filename().string(); + dir_entry.isFile = ent_is_file; + }); file->dirents_index = 0; } } @@ -695,66 +689,12 @@ static int GetDents(int fd, char* buf, int nbytes, s64* basep) { return sizeof(OrbisKernelDirent); } -static int HandleSeparateUpdateDents(int fd, char* buf, int nbytes, s64* basep) { - int dir_entries = 0; - - auto* h = Common::Singleton::Instance(); - auto* mnt = Common::Singleton::Instance(); - auto* file = h->GetFile(fd); - auto update_dir_name = std::string{fmt::UTF(file->m_host_name.u8string()).data}; - auto mount = mnt->GetMountFromHostPath(update_dir_name); - auto suffix = std::string{fmt::UTF(mount->host_path.u8string()).data}; - - size_t pos = update_dir_name.find("-UPDATE"); - if (pos != std::string::npos) { - update_dir_name.erase(pos, 7); - auto guest_name = mount->mount + "/" + update_dir_name.substr(suffix.size() + 1); - int descriptor; - - auto existent_folder = h->GetFile(update_dir_name); - if (!existent_folder) { - u32 handle = h->CreateHandle(); - auto* new_file = h->GetFile(handle); - new_file->type = Core::FileSys::FileType::Directory; - new_file->m_guest_name = guest_name; - new_file->m_host_name = update_dir_name; - if (!std::filesystem::is_directory(new_file->m_host_name)) { - h->DeleteHandle(handle); - return dir_entries; - } else { - new_file->dirents = GetDirectoryEntries(new_file->m_host_name); - new_file->dirents_index = 0; - } - new_file->is_opened = true; - descriptor = h->GetFileDescriptor(new_file); - } else { - descriptor = h->GetFileDescriptor(existent_folder); - } - - dir_entries = GetDents(descriptor, buf, nbytes, basep); - if (dir_entries == ORBIS_OK && existent_folder) { - existent_folder->dirents_index = 0; - file->dirents_index = 0; - } - } - - return dir_entries; -} - int PS4_SYSV_ABI sceKernelGetdents(int fd, char* buf, int nbytes) { - int a = GetDents(fd, buf, nbytes, nullptr); - if (a == ORBIS_OK) { - return HandleSeparateUpdateDents(fd, buf, nbytes, nullptr); - } - return a; + return GetDents(fd, buf, nbytes, nullptr); } int PS4_SYSV_ABI sceKernelGetdirentries(int fd, char* buf, int nbytes, s64* basep) { - int a = GetDents(fd, buf, nbytes, basep); - if (a == ORBIS_OK) { - return HandleSeparateUpdateDents(fd, buf, nbytes, basep); - } - return a; + return GetDents(fd, buf, nbytes, basep); } s64 PS4_SYSV_ABI sceKernelPwrite(int d, void* buf, size_t nbytes, s64 offset) { diff --git a/src/emulator.cpp b/src/emulator.cpp index 5d037e26c..dbe693340 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -217,41 +217,15 @@ void Emulator::Run(const std::filesystem::path& file) { linker->LoadModule(eboot_path); // check if we have system modules to load - LoadSystemModules(eboot_path, game_info.game_serial); + LoadSystemModules(game_info.game_serial); // Load all prx from game's sce_module folder - std::vector modules_to_load; - std::filesystem::path game_module_folder = file.parent_path() / "sce_module"; - if (std::filesystem::is_directory(game_module_folder)) { - for (const auto& entry : std::filesystem::directory_iterator(game_module_folder)) { - if (entry.is_regular_file()) { - modules_to_load.push_back(entry.path()); - } + mnt->IterateDirectory("/app0/sce_module", [this](const auto& path, const auto is_file) { + if (is_file) { + LOG_INFO(Loader, "Loading {}", fmt::UTF(path.u8string())); + linker->LoadModule(path); } - } - - // Load all prx from separate update's sce_module folder - std::filesystem::path game_patch_folder = game_folder; - game_patch_folder += "-UPDATE"; - std::filesystem::path update_module_folder = game_patch_folder / "sce_module"; - if (std::filesystem::is_directory(update_module_folder)) { - for (const auto& entry : std::filesystem::directory_iterator(update_module_folder)) { - auto it = std::find_if(modules_to_load.begin(), modules_to_load.end(), - [&entry](const std::filesystem::path& p) { - return p.filename() == entry.path().filename(); - }); - if (it != modules_to_load.end()) { - *it = entry.path(); - } else { - modules_to_load.push_back(entry.path()); - } - } - } - - for (const auto& module_path : modules_to_load) { - LOG_INFO(Loader, "Loading {}", fmt::UTF(module_path.u8string())); - linker->LoadModule(module_path); - } + }); #ifdef ENABLE_DISCORD_RPC // Discord RPC @@ -278,7 +252,7 @@ void Emulator::Run(const std::filesystem::path& file) { std::exit(0); } -void Emulator::LoadSystemModules(const std::filesystem::path& file, std::string game_serial) { +void Emulator::LoadSystemModules(const std::string& game_serial) { constexpr std::array ModulesToLoad{ {{"libSceNgs2.sprx", &Libraries::Ngs2::RegisterlibSceNgs2}, {"libSceUlt.sprx", nullptr}, diff --git a/src/emulator.h b/src/emulator.h index e973e9022..a08ab43c3 100644 --- a/src/emulator.h +++ b/src/emulator.h @@ -29,7 +29,7 @@ public: void UpdatePlayTime(const std::string& serial); private: - void LoadSystemModules(const std::filesystem::path& file, std::string game_serial); + void LoadSystemModules(const std::string& game_serial); Core::MemoryManager* memory; Input::GameController* controller; From e791ff4c5c6f467d0fc718545fa517fe4a0b37c1 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Wed, 8 Jan 2025 03:50:39 -0800 Subject: [PATCH 382/549] externals: Update discord-rpc. (#2094) --- externals/CMakeLists.txt | 2 -- externals/discord-rpc | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 4350948b7..4ce5636d8 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -213,9 +213,7 @@ endif() # Discord RPC if (ENABLE_DISCORD_RPC) - set(BUILD_EXAMPLES OFF) add_subdirectory(discord-rpc) - target_include_directories(discord-rpc INTERFACE discord-rpc/include) endif() # GCN Headers diff --git a/externals/discord-rpc b/externals/discord-rpc index 4ec218155..51b09d426 160000 --- a/externals/discord-rpc +++ b/externals/discord-rpc @@ -1 +1 @@ -Subproject commit 4ec218155d73bcb8022f8f7ca72305d801f84beb +Subproject commit 51b09d426a4a1bcfa6ee6d4894e57d669f4a2e65 From fc50567fc2e5be7f061f22981e6f98082bc9fe27 Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Wed, 8 Jan 2025 06:08:54 -0600 Subject: [PATCH 383/549] Unmap Fixes (#2080) * Fix unmapping reserved memory * Fix bug with unmapping before reserve * Clang * Ignore free memory pages * Handle pooled memory --- src/core/memory.cpp | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 1327ede5f..619941000 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -171,10 +171,11 @@ int MemoryManager::PoolReserve(void** out_addr, VAddr virtual_addr, size_t size, // Fixed mapping means the virtual address must exactly match the provided one. if (True(flags & MemoryMapFlags::Fixed)) { - const auto& vma = FindVMA(mapped_addr)->second; + auto& vma = FindVMA(mapped_addr)->second; // If the VMA is mapped, unmap the region first. if (vma.IsMapped()) { UnmapMemoryImpl(mapped_addr, size); + vma = FindVMA(mapped_addr)->second; } const size_t remaining_size = vma.base + vma.size - mapped_addr; ASSERT_MSG(vma.type == VMAType::Free && remaining_size >= size); @@ -208,10 +209,11 @@ int MemoryManager::Reserve(void** out_addr, VAddr virtual_addr, size_t size, Mem // Fixed mapping means the virtual address must exactly match the provided one. if (True(flags & MemoryMapFlags::Fixed)) { - const auto& vma = FindVMA(mapped_addr)->second; + auto& vma = FindVMA(mapped_addr)->second; // If the VMA is mapped, unmap the region first. if (vma.IsMapped()) { UnmapMemoryImpl(mapped_addr, size); + vma = FindVMA(mapped_addr)->second; } const size_t remaining_size = vma.base + vma.size - mapped_addr; ASSERT_MSG(vma.type == VMAType::Free && remaining_size >= size); @@ -393,14 +395,18 @@ s32 MemoryManager::UnmapMemoryImpl(VAddr virtual_addr, size_t size) { ASSERT_MSG(vma_base.Contains(virtual_addr, size), "Existing mapping does not contain requested unmap range"); + const auto type = vma_base.type; + if (type == VMAType::Free) { + return ORBIS_OK; + } + const auto vma_base_addr = vma_base.base; const auto vma_base_size = vma_base.size; const auto phys_base = vma_base.phys_base; const bool is_exec = vma_base.is_exec; const auto start_in_vma = virtual_addr - vma_base_addr; - const auto type = vma_base.type; const bool has_backing = type == VMAType::Direct || type == VMAType::File; - if (type == VMAType::Direct) { + if (type == VMAType::Direct || type == VMAType::Pooled) { rasterizer->UnmapMemory(virtual_addr, size); } if (type == VMAType::Flexible) { @@ -418,10 +424,12 @@ s32 MemoryManager::UnmapMemoryImpl(VAddr virtual_addr, size_t size) { MergeAdjacent(vma_map, new_it); bool readonly_file = vma.prot == MemoryProt::CpuRead && type == VMAType::File; - // Unmap the memory region. - impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + size, phys_base, is_exec, - has_backing, readonly_file); - TRACK_FREE(virtual_addr, "VMEM"); + if (type != VMAType::Reserved && type != VMAType::PoolReserved) { + // Unmap the memory region. + impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + size, phys_base, + is_exec, has_backing, readonly_file); + TRACK_FREE(virtual_addr, "VMEM"); + } return ORBIS_OK; } From 0eee36cbc7b6e6e7b605f532925845ec30630c7c Mon Sep 17 00:00:00 2001 From: tomboylover93 <95257311+tomboylover93@users.noreply.github.com> Date: Wed, 8 Jan 2025 04:41:01 -0800 Subject: [PATCH 384/549] ci: Add GCC build job for Linux (#2027) * Add GCC CI build job * gcc-ci: Change Clang CI job naming to avoid confusion * gcc-ci: Remove GCC CI job from pre-release This also removes the packaging step for linux-sdl-gcc and linux-qt-gcc so that the only available artifacts for download are compiled with Clang * gcc-ci: Remove -clang prefix from Clang build jobs * hot-fix * specify gcc-14 * hot-fix: use system rapidjson * use rapidjson-dev * revert "use system rapidjson" --- .github/workflows/build.yml | 72 +++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5c4a34469..c36c026fc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -376,6 +376,78 @@ jobs: name: shadps4-linux-qt-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }} path: Shadps4-qt.AppImage + linux-sdl-gcc: + runs-on: ubuntu-24.04 + needs: get-info + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install dependencies + run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 gcc-14 build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev + + - name: Cache CMake Configuration + uses: actions/cache@v4 + env: + cache-name: ${{ runner.os }}-sdl-cache-cmake-configuration + with: + path: | + ${{github.workspace}}/build + key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }} + restore-keys: | + ${{ env.cache-name }}- + + - name: Cache CMake Build + uses: hendrikmuhs/ccache-action@v1.2.14 + env: + cache-name: ${{ runner.os }}-sdl-cache-cmake-build + with: + append-timestamp: false + key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }} + + - name: Configure CMake + run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + + - name: Build + run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc) + + linux-qt-gcc: + runs-on: ubuntu-24.04 + needs: get-info + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install dependencies + run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 gcc-14 build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev libudev-dev + + - name: Cache CMake Configuration + uses: actions/cache@v4 + env: + cache-name: ${{ runner.os }}-qt-cache-cmake-configuration + with: + path: | + ${{github.workspace}}/build + key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }} + restore-keys: | + ${{ env.cache-name }}- + + - name: Cache CMake Build + uses: hendrikmuhs/ccache-action@v1.2.14 + env: + cache-name: ${{ runner.os }}-qt-cache-cmake-build + with: + append-timestamp: false + key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }} + + - name: Configure CMake + run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + + - name: Build + run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc) + pre-release: if: github.ref == 'refs/heads/main' && github.repository == 'shadps4-emu/shadPS4' && github.event_name == 'push' needs: [get-info, windows-sdl, windows-qt, macos-sdl, macos-qt, linux-sdl, linux-qt] From 65f9bbbfed83cc6504332012681bcda0874e7268 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Wed, 8 Jan 2025 09:14:06 -0800 Subject: [PATCH 385/549] shader_recompiler: Ignore exec mask for scalar instructions. (#2097) --- .../frontend/control_flow_graph.cpp | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/shader_recompiler/frontend/control_flow_graph.cpp b/src/shader_recompiler/frontend/control_flow_graph.cpp index 0816ec088..ec5c117f7 100644 --- a/src/shader_recompiler/frontend/control_flow_graph.cpp +++ b/src/shader_recompiler/frontend/control_flow_graph.cpp @@ -47,13 +47,26 @@ static IR::Condition MakeCondition(const GcnInst& inst) { } } -static bool IgnoresExecMask(Opcode opcode) { - switch (opcode) { - case Opcode::V_WRITELANE_B32: +static bool IgnoresExecMask(const GcnInst& inst) { + // EXEC mask does not affect scalar instructions or branches. + switch (inst.category) { + case InstCategory::ScalarALU: + case InstCategory::ScalarMemory: + case InstCategory::FlowControl: return true; default: - return false; + break; } + // Read/Write Lane instructions are not affected either. + switch (inst.opcode) { + case Opcode::V_READLANE_B32: + case Opcode::V_WRITELANE_B32: + case Opcode::V_READFIRSTLANE_B32: + return true; + default: + break; + } + return false; } static constexpr size_t LabelReserveSize = 32; @@ -147,8 +160,7 @@ void CFG::EmitDivergenceLabels() { // If all instructions in the scope ignore exec masking, we shouldn't insert a // scope. const auto start = inst_list.begin() + curr_begin + 1; - if (!std::ranges::all_of(start, inst_list.begin() + index, IgnoresExecMask, - &GcnInst::opcode)) { + if (!std::ranges::all_of(start, inst_list.begin() + index, IgnoresExecMask)) { // Add a label to the instruction right after the open scope call. // It is the start of a new basic block. const auto& save_inst = inst_list[curr_begin]; From 93402620de7897a273f4ed980daec660d1b83a1c Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Thu, 9 Jan 2025 03:42:07 -0300 Subject: [PATCH 386/549] GUI: Open Log Location - Button (#2102) --- src/qt_gui/settings_dialog.cpp | 11 +++++++++++ src/qt_gui/settings_dialog.ui | 7 +++++++ src/qt_gui/translations/ar.ts | 4 ++++ src/qt_gui/translations/da_DK.ts | 4 ++++ src/qt_gui/translations/de.ts | 4 ++++ src/qt_gui/translations/el.ts | 4 ++++ src/qt_gui/translations/en.ts | 4 ++++ src/qt_gui/translations/es_ES.ts | 4 ++++ src/qt_gui/translations/fa_IR.ts | 4 ++++ src/qt_gui/translations/fi.ts | 4 ++++ src/qt_gui/translations/fr.ts | 4 ++++ src/qt_gui/translations/hu_HU.ts | 4 ++++ src/qt_gui/translations/id.ts | 4 ++++ src/qt_gui/translations/it.ts | 4 ++++ src/qt_gui/translations/ja_JP.ts | 4 ++++ src/qt_gui/translations/ko_KR.ts | 4 ++++ src/qt_gui/translations/lt_LT.ts | 4 ++++ src/qt_gui/translations/nb.ts | 4 ++++ src/qt_gui/translations/nl.ts | 4 ++++ src/qt_gui/translations/pl_PL.ts | 4 ++++ src/qt_gui/translations/pt_BR.ts | 4 ++++ src/qt_gui/translations/ro_RO.ts | 4 ++++ src/qt_gui/translations/ru_RU.ts | 4 ++++ src/qt_gui/translations/sq.ts | 4 ++++ src/qt_gui/translations/sv.ts | 4 ++++ src/qt_gui/translations/tr_TR.ts | 4 ++++ src/qt_gui/translations/uk_UA.ts | 4 ++++ src/qt_gui/translations/vi_VN.ts | 4 ++++ src/qt_gui/translations/zh_CN.ts | 4 ++++ src/qt_gui/translations/zh_TW.ts | 4 ++++ 30 files changed, 130 insertions(+) diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index 193ce5cd8..3f4970dad 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -17,6 +17,7 @@ #ifdef ENABLE_UPDATER #include "check_update.h" #endif +#include #include #include "background_music_player.h" #include "common/logging/backend.h" @@ -203,6 +204,16 @@ SettingsDialog::SettingsDialog(std::span physical_devices, }); } + // DEBUG TAB + { + connect(ui->OpenLogLocationButton, &QPushButton::clicked, this, []() { + QString userPath; + Common::FS::PathToQString(userPath, + Common::FS::GetUserPath(Common::FS::PathType::UserDir)); + QDesktopServices::openUrl(QUrl::fromLocalFile(userPath + "/log")); + }); + } + // Descriptions { // General diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index fbfee5a99..089158fd3 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -1394,6 +1394,13 @@ + + + + Open Log Location + + + diff --git a/src/qt_gui/translations/ar.ts b/src/qt_gui/translations/ar.ts index c1964356a..4fc9c2de1 100644 --- a/src/qt_gui/translations/ar.ts +++ b/src/qt_gui/translations/ar.ts @@ -576,6 +576,10 @@ Log Filter مرشح السجل + + Open Log Location + افتح موقع السجل + Input إدخال diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts index 51fa2ca5f..ef1ae27a3 100644 --- a/src/qt_gui/translations/da_DK.ts +++ b/src/qt_gui/translations/da_DK.ts @@ -576,6 +576,10 @@ Log Filter Log Filter + + Open Log Location + Åbn logplacering + Input Indtastning diff --git a/src/qt_gui/translations/de.ts b/src/qt_gui/translations/de.ts index 1653298cf..2fc6a29fe 100644 --- a/src/qt_gui/translations/de.ts +++ b/src/qt_gui/translations/de.ts @@ -576,6 +576,10 @@ Log Filter Log-Filter + + Open Log Location + Protokollspeicherort öffnen + Input Eingabe diff --git a/src/qt_gui/translations/el.ts b/src/qt_gui/translations/el.ts index 6226e1292..8d3885808 100644 --- a/src/qt_gui/translations/el.ts +++ b/src/qt_gui/translations/el.ts @@ -576,6 +576,10 @@ Log Filter Log Filter + + Open Log Location + Άνοιγμα τοποθεσίας αρχείου καταγραφής + Input Είσοδος diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index 1f932ea97..0262ee149 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -576,6 +576,10 @@ Log Filter Log Filter + + Open Log Location + Open Log Location + Input Input diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index 021b39ed8..a25ff639e 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -576,6 +576,10 @@ Log Filter Filtro de registro + + Open Log Location + Abrir ubicación del registro + Input Entrada diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index ee37cd22a..52aa4b17c 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -576,6 +576,10 @@ Log Filter Log فیلتر + + Open Log Location + باز کردن مکان گزارش + Input ورودی diff --git a/src/qt_gui/translations/fi.ts b/src/qt_gui/translations/fi.ts index 47c38fc46..97fee5dfa 100644 --- a/src/qt_gui/translations/fi.ts +++ b/src/qt_gui/translations/fi.ts @@ -576,6 +576,10 @@ Log Filter Lokisuodatin + + Open Log Location + Avaa lokin sijainti + Input Syöttö diff --git a/src/qt_gui/translations/fr.ts b/src/qt_gui/translations/fr.ts index c2a2a64d7..d25ad30f4 100644 --- a/src/qt_gui/translations/fr.ts +++ b/src/qt_gui/translations/fr.ts @@ -576,6 +576,10 @@ Log Filter Filtre + + Open Log Location + Ouvrir l'emplacement du journal + Input Entrée diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index 677302e01..6ecc3fc90 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -576,6 +576,10 @@ Log Filter Naplózási Filter + + Open Log Location + Napló helyének megnyitása + Input Bemenet diff --git a/src/qt_gui/translations/id.ts b/src/qt_gui/translations/id.ts index 7a1391c0e..fc5ad4a99 100644 --- a/src/qt_gui/translations/id.ts +++ b/src/qt_gui/translations/id.ts @@ -576,6 +576,10 @@ Log Filter Log Filter + + Open Log Location + Buka Lokasi Log + Input Masukan diff --git a/src/qt_gui/translations/it.ts b/src/qt_gui/translations/it.ts index 8bdb7a8fd..f7ba3661b 100644 --- a/src/qt_gui/translations/it.ts +++ b/src/qt_gui/translations/it.ts @@ -576,6 +576,10 @@ Log Filter Filtro Log + + Open Log Location + Apri posizione del registro + Input Input diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index f34747549..21c8145ed 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -576,6 +576,10 @@ Log Filter ログフィルター + + Open Log Location + ログの場所を開く + Input 入力 diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts index 410f9bead..fea8d55bc 100644 --- a/src/qt_gui/translations/ko_KR.ts +++ b/src/qt_gui/translations/ko_KR.ts @@ -576,6 +576,10 @@ Log Filter Log Filter + + Open Log Location + 로그 위치 열기 + Input Input diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts index 770a9b09e..eaf51a975 100644 --- a/src/qt_gui/translations/lt_LT.ts +++ b/src/qt_gui/translations/lt_LT.ts @@ -576,6 +576,10 @@ Log Filter Log Filter + + Open Log Location + Atidaryti žurnalo vietą + Input Įvestis diff --git a/src/qt_gui/translations/nb.ts b/src/qt_gui/translations/nb.ts index 711542773..83dbf7dd8 100644 --- a/src/qt_gui/translations/nb.ts +++ b/src/qt_gui/translations/nb.ts @@ -576,6 +576,10 @@ Log Filter Logg filter + + Open Log Location + Åpne loggplassering + Input Inndata diff --git a/src/qt_gui/translations/nl.ts b/src/qt_gui/translations/nl.ts index 2aa996413..3142a17e5 100644 --- a/src/qt_gui/translations/nl.ts +++ b/src/qt_gui/translations/nl.ts @@ -576,6 +576,10 @@ Log Filter Log Filter + + Open Log Location + Loglocatie openen + Input Invoer diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index 20c9861c3..378673a30 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -576,6 +576,10 @@ Log Filter Filtrowanie dziennika + + Open Log Location + Otwórz lokalizację dziennika + Input Wejście diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index 2d623dfbf..5d9c84769 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -576,6 +576,10 @@ Log Filter Filtro do Registro + + Open Log Location + Abrir local do log + Input Entradas diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index fbbabfac0..71354fb06 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -576,6 +576,10 @@ Log Filter Log Filter + + Open Log Location + Deschide locația jurnalului + Input Introducere diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index e914e4d49..0e803ea42 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -576,6 +576,10 @@ Log Filter Фильтр логов + + Open Log Location + Открыть местоположение журнала + Input Ввод diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index d4936170b..7354b4bd9 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -576,6 +576,10 @@ Log Filter Filtri i Ditarit + + Open Log Location + Hap vendndodhjen e regjistrit + Input Hyrja diff --git a/src/qt_gui/translations/sv.ts b/src/qt_gui/translations/sv.ts index c1c1204f8..3a6f060cb 100644 --- a/src/qt_gui/translations/sv.ts +++ b/src/qt_gui/translations/sv.ts @@ -1064,6 +1064,10 @@ Log Filter Loggfilter + + Open Log Location + Öppna loggplats + Input Inmatning diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index 48f291e99..4596000f2 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -576,6 +576,10 @@ Log Filter Kayıt Filtresi + + Open Log Location + Günlük Konumunu Aç + Input Girdi diff --git a/src/qt_gui/translations/uk_UA.ts b/src/qt_gui/translations/uk_UA.ts index 03aca7cd9..5b260050e 100644 --- a/src/qt_gui/translations/uk_UA.ts +++ b/src/qt_gui/translations/uk_UA.ts @@ -576,6 +576,10 @@ Log Filter Фільтр логів + + Open Log Location + Відкрити місце розташування журналу + Input Введення diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index 3f92c1836..7fcac6d7e 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -576,6 +576,10 @@ Log Filter Log Filter + + Open Log Location + Mở vị trí nhật ký + Input Đầu vào diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index 60e50a2bc..e71180729 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -576,6 +576,10 @@ Log Filter 日志过滤 + + Open Log Location + 打开日志位置 + Input 输入 diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index d1e822b5c..49d419d8b 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -576,6 +576,10 @@ Log Filter Log Filter + + Open Log Location + 開啟日誌位置 + Input 輸入 From 725814ce012f5246e03a4df23b8f24a9426cb100 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 10 Jan 2025 00:48:12 -0800 Subject: [PATCH 387/549] shader_recompiler: Improvements to array and cube handling. (#2083) * shader_recompiler: Account for instruction array flag in image type. * shader_recompiler: Check da flag for all mimg instructions. * shader_recompiler: Convert cube images into 2D arrays. * shader_recompiler: Move image resource functions into sharp type. * shader_recompiler: Use native AMD cube instructions when possible. * specialization: Fix buffer storage mistake. --- externals/sirit | 2 +- .../backend/spirv/emit_spirv_image.cpp | 23 +++++- .../backend/spirv/emit_spirv_instructions.h | 2 + .../backend/spirv/spirv_emit_context.cpp | 10 +-- .../backend/spirv/spirv_emit_context.h | 1 + .../frontend/translate/translate.h | 3 + .../frontend/translate/vector_alu.cpp | 81 ++++++++++++++++++- .../frontend/translate/vector_memory.cpp | 37 ++++++--- src/shader_recompiler/info.h | 6 -- src/shader_recompiler/ir/ir_emitter.cpp | 13 +-- src/shader_recompiler/ir/ir_emitter.h | 5 +- src/shader_recompiler/ir/opcodes.inc | 4 + .../ir/passes/resource_tracking_pass.cpp | 46 +---------- .../ir/passes/shader_info_collection_pass.cpp | 2 +- src/shader_recompiler/profile.h | 1 + src/shader_recompiler/specialization.h | 4 +- src/video_core/amdgpu/resource.h | 56 +++++++++---- .../renderer_vulkan/vk_compute_pipeline.cpp | 5 +- .../renderer_vulkan/vk_graphics_pipeline.cpp | 5 +- .../renderer_vulkan/vk_instance.cpp | 1 + src/video_core/renderer_vulkan/vk_instance.h | 6 ++ .../renderer_vulkan/vk_pipeline_cache.cpp | 1 + .../renderer_vulkan/vk_rasterizer.cpp | 2 +- src/video_core/texture_cache/image.cpp | 8 +- src/video_core/texture_cache/image_info.cpp | 4 +- src/video_core/texture_cache/image_info.h | 1 - src/video_core/texture_cache/image_view.cpp | 30 ++----- src/video_core/texture_cache/texture_cache.h | 2 +- 28 files changed, 217 insertions(+), 144 deletions(-) diff --git a/externals/sirit b/externals/sirit index 1e74f4ef8..26ad5a9d0 160000 --- a/externals/sirit +++ b/externals/sirit @@ -1 +1 @@ -Subproject commit 1e74f4ef8d2a0e3221a4de51977663f342b53c35 +Subproject commit 26ad5a9d0fe13260b0d7d6c64419d01a196b2e32 diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index c3d937fe7..182437b63 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp @@ -172,20 +172,19 @@ Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, u32 handle, Id lod const auto& texture = ctx.images[handle & 0xFFFF]; const Id image = ctx.OpLoad(texture.image_type, texture.id); const auto sharp = ctx.info.images[handle & 0xFFFF].GetSharp(ctx.info); - const auto type = sharp.GetBoundType(); const Id zero = ctx.u32_zero_value; const auto mips{[&] { return has_mips ? ctx.OpImageQueryLevels(ctx.U32[1], image) : zero; }}; - const bool uses_lod{type != AmdGpu::ImageType::Color2DMsaa && !texture.is_storage}; + const bool uses_lod{texture.bound_type != AmdGpu::ImageType::Color2DMsaa && + !texture.is_storage}; const auto query{[&](Id type) { return uses_lod ? ctx.OpImageQuerySizeLod(type, image, lod) : ctx.OpImageQuerySize(type, image); }}; - switch (type) { + switch (texture.bound_type) { case AmdGpu::ImageType::Color1D: return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[1]), zero, zero, mips()); case AmdGpu::ImageType::Color1DArray: case AmdGpu::ImageType::Color2D: - case AmdGpu::ImageType::Cube: case AmdGpu::ImageType::Color2DMsaa: return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[2]), zero, mips()); case AmdGpu::ImageType::Color2DArray: @@ -257,4 +256,20 @@ void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id ctx.OpImageWrite(image, coords, texel, operands.mask, operands.operands); } +Id EmitCubeFaceCoord(EmitContext& ctx, IR::Inst* inst, Id cube_coords) { + if (ctx.profile.supports_native_cube_calc) { + return ctx.OpCubeFaceCoordAMD(ctx.F32[2], cube_coords); + } else { + UNREACHABLE_MSG("SPIR-V Instruction"); + } +} + +Id EmitCubeFaceIndex(EmitContext& ctx, IR::Inst* inst, Id cube_coords) { + if (ctx.profile.supports_native_cube_calc) { + return ctx.OpCubeFaceIndexAMD(ctx.F32[1], cube_coords); + } else { + UNREACHABLE_MSG("SPIR-V Instruction"); + } +} + } // namespace Shader::Backend::SPIRV diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index 0d9fcff46..37b6f7786 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h @@ -439,6 +439,8 @@ Id EmitImageAtomicAnd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id EmitImageAtomicOr32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value); Id EmitImageAtomicXor32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value); Id EmitImageAtomicExchange32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value); +Id EmitCubeFaceCoord(EmitContext& ctx, IR::Inst* inst, Id cube_coords); +Id EmitCubeFaceIndex(EmitContext& ctx, IR::Inst* inst, Id cube_coords); Id EmitLaneId(EmitContext& ctx); Id EmitWarpId(EmitContext& ctx); Id EmitQuadShuffle(EmitContext& ctx, Id value, Id index); diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 575bf91f7..6151c5c65 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -773,8 +773,8 @@ spv::ImageFormat GetFormat(const AmdGpu::Image& image) { Id ImageType(EmitContext& ctx, const ImageResource& desc, Id sampled_type) { const auto image = desc.GetSharp(ctx.info); const auto format = desc.is_atomic ? GetFormat(image) : spv::ImageFormat::Unknown; - const auto type = image.GetBoundType(); - const u32 sampled = desc.IsStorage(image) ? 2 : 1; + const auto type = image.GetBoundType(desc.is_array); + const u32 sampled = desc.is_written ? 2 : 1; switch (type) { case AmdGpu::ImageType::Color1D: return ctx.TypeImage(sampled_type, spv::Dim::Dim1D, false, false, false, sampled, format); @@ -788,9 +788,6 @@ Id ImageType(EmitContext& ctx, const ImageResource& desc, Id sampled_type) { return ctx.TypeImage(sampled_type, spv::Dim::Dim2D, false, false, true, sampled, format); case AmdGpu::ImageType::Color3D: return ctx.TypeImage(sampled_type, spv::Dim::Dim3D, false, false, false, sampled, format); - case AmdGpu::ImageType::Cube: - return ctx.TypeImage(sampled_type, spv::Dim::Cube, false, desc.is_array, false, sampled, - format); default: break; } @@ -802,7 +799,7 @@ void EmitContext::DefineImagesAndSamplers() { const auto sharp = image_desc.GetSharp(info); const auto nfmt = sharp.GetNumberFmt(); const bool is_integer = AmdGpu::IsInteger(nfmt); - const bool is_storage = image_desc.IsStorage(sharp); + const bool is_storage = image_desc.is_written; const VectorIds& data_types = GetAttributeType(*this, nfmt); const Id sampled_type = data_types[1]; const Id image_type{ImageType(*this, image_desc, sampled_type)}; @@ -817,6 +814,7 @@ void EmitContext::DefineImagesAndSamplers() { .sampled_type = is_storage ? sampled_type : TypeSampledImage(image_type), .pointer_type = pointer_type, .image_type = image_type, + .bound_type = sharp.GetBoundType(image_desc.is_array), .is_integer = is_integer, .is_storage = is_storage, }); diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index 583d96b99..80d0d4d9f 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -222,6 +222,7 @@ public: Id sampled_type; Id pointer_type; Id image_type; + AmdGpu::ImageType bound_type; bool is_integer = false; bool is_storage = false; }; diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index 7a0b736d4..bef61f997 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -301,6 +301,9 @@ private: IR::U32 VMovRelSHelper(u32 src_vgprno, const IR::U32 m0); void VMovRelDHelper(u32 dst_vgprno, const IR::U32 src_val, const IR::U32 m0); + IR::F32 SelectCubeResult(const IR::F32& x, const IR::F32& y, const IR::F32& z, + const IR::F32& x_res, const IR::F32& y_res, const IR::F32& z_res); + void LogMissingOpcode(const GcnInst& inst); private: diff --git a/src/shader_recompiler/frontend/translate/vector_alu.cpp b/src/shader_recompiler/frontend/translate/vector_alu.cpp index 7fa83eebb..375c5f078 100644 --- a/src/shader_recompiler/frontend/translate/vector_alu.cpp +++ b/src/shader_recompiler/frontend/translate/vector_alu.cpp @@ -3,6 +3,7 @@ #include "shader_recompiler/frontend/opcodes.h" #include "shader_recompiler/frontend/translate/translate.h" +#include "shader_recompiler/profile.h" namespace Shader::Gcn { @@ -1042,20 +1043,92 @@ void Translator::V_MAD_U32_U24(const GcnInst& inst) { V_MAD_I32_I24(inst, false); } +IR::F32 Translator::SelectCubeResult(const IR::F32& x, const IR::F32& y, const IR::F32& z, + const IR::F32& x_res, const IR::F32& y_res, + const IR::F32& z_res) { + const auto abs_x = ir.FPAbs(x); + const auto abs_y = ir.FPAbs(y); + const auto abs_z = ir.FPAbs(z); + + const auto z_face_cond{ + ir.LogicalAnd(ir.FPGreaterThanEqual(abs_z, abs_x), ir.FPGreaterThanEqual(abs_z, abs_y))}; + const auto y_face_cond{ir.FPGreaterThanEqual(abs_y, abs_x)}; + + return IR::F32{ir.Select(z_face_cond, z_res, ir.Select(y_face_cond, y_res, x_res))}; +} + void Translator::V_CUBEID_F32(const GcnInst& inst) { - SetDst(inst.dst[0], GetSrc(inst.src[2])); + const auto x = GetSrc(inst.src[0]); + const auto y = GetSrc(inst.src[1]); + const auto z = GetSrc(inst.src[2]); + + IR::F32 result; + if (profile.supports_native_cube_calc) { + result = ir.CubeFaceIndex(ir.CompositeConstruct(x, y, z)); + } else { + const auto x_neg_cond{ir.FPLessThan(x, ir.Imm32(0.f))}; + const auto y_neg_cond{ir.FPLessThan(y, ir.Imm32(0.f))}; + const auto z_neg_cond{ir.FPLessThan(z, ir.Imm32(0.f))}; + const IR::F32 x_face{ir.Select(x_neg_cond, ir.Imm32(5.f), ir.Imm32(4.f))}; + const IR::F32 y_face{ir.Select(y_neg_cond, ir.Imm32(3.f), ir.Imm32(2.f))}; + const IR::F32 z_face{ir.Select(z_neg_cond, ir.Imm32(1.f), ir.Imm32(0.f))}; + + result = SelectCubeResult(x, y, z, x_face, y_face, z_face); + } + SetDst(inst.dst[0], result); } void Translator::V_CUBESC_F32(const GcnInst& inst) { - SetDst(inst.dst[0], GetSrc(inst.src[0])); + const auto x = GetSrc(inst.src[0]); + const auto y = GetSrc(inst.src[1]); + const auto z = GetSrc(inst.src[2]); + + IR::F32 result; + if (profile.supports_native_cube_calc) { + const auto coords{ir.CubeFaceCoord(ir.CompositeConstruct(x, y, z))}; + result = IR::F32{ir.CompositeExtract(coords, 0)}; + } else { + const auto x_neg_cond{ir.FPLessThan(x, ir.Imm32(0.f))}; + const auto z_neg_cond{ir.FPLessThan(z, ir.Imm32(0.f))}; + const IR::F32 x_sc{ir.Select(x_neg_cond, ir.FPNeg(x), x)}; + const IR::F32 z_sc{ir.Select(z_neg_cond, z, ir.FPNeg(z))}; + + result = SelectCubeResult(x, y, z, x_sc, x, z_sc); + } + SetDst(inst.dst[0], result); } void Translator::V_CUBETC_F32(const GcnInst& inst) { - SetDst(inst.dst[0], GetSrc(inst.src[1])); + const auto x = GetSrc(inst.src[0]); + const auto y = GetSrc(inst.src[1]); + const auto z = GetSrc(inst.src[2]); + + IR::F32 result; + if (profile.supports_native_cube_calc) { + const auto coords{ir.CubeFaceCoord(ir.CompositeConstruct(x, y, z))}; + result = IR::F32{ir.CompositeExtract(coords, 1)}; + } else { + const auto y_neg_cond{ir.FPLessThan(y, ir.Imm32(0.f))}; + const IR::F32 x_z_sc{ir.FPNeg(y)}; + const IR::F32 y_sc{ir.Select(y_neg_cond, ir.FPNeg(z), z)}; + + result = SelectCubeResult(x, y, z, x_z_sc, y_sc, x_z_sc); + } + SetDst(inst.dst[0], result); } void Translator::V_CUBEMA_F32(const GcnInst& inst) { - SetDst(inst.dst[0], ir.Imm32(1.f)); + const auto x = GetSrc(inst.src[0]); + const auto y = GetSrc(inst.src[1]); + const auto z = GetSrc(inst.src[2]); + + const auto two{ir.Imm32(4.f)}; + const IR::F32 x_major_axis{ir.FPMul(x, two)}; + const IR::F32 y_major_axis{ir.FPMul(y, two)}; + const IR::F32 z_major_axis{ir.FPMul(z, two)}; + + const auto result{SelectCubeResult(x, y, z, x_major_axis, y_major_axis, z_major_axis)}; + SetDst(inst.dst[0], result); } void Translator::V_BFE_U32(bool is_signed, const GcnInst& inst) { diff --git a/src/shader_recompiler/frontend/translate/vector_memory.cpp b/src/shader_recompiler/frontend/translate/vector_memory.cpp index c5be08b7d..a5b54dff7 100644 --- a/src/shader_recompiler/frontend/translate/vector_memory.cpp +++ b/src/shader_recompiler/frontend/translate/vector_memory.cpp @@ -418,6 +418,7 @@ void Translator::IMAGE_LOAD(bool has_mip, const GcnInst& inst) { IR::TextureInstInfo info{}; info.has_lod.Assign(has_mip); + info.is_array.Assign(mimg.da); const IR::Value texel = ir.ImageRead(handle, body, {}, {}, info); for (u32 i = 0; i < 4; i++) { @@ -442,6 +443,7 @@ void Translator::IMAGE_STORE(bool has_mip, const GcnInst& inst) { IR::TextureInstInfo info{}; info.has_lod.Assign(has_mip); + info.is_array.Assign(mimg.da); boost::container::static_vector comps; for (u32 i = 0; i < 4; i++) { @@ -456,13 +458,18 @@ void Translator::IMAGE_STORE(bool has_mip, const GcnInst& inst) { } void Translator::IMAGE_GET_RESINFO(const GcnInst& inst) { + const auto& mimg = inst.control.mimg; IR::VectorReg dst_reg{inst.dst[0].code}; const IR::ScalarReg tsharp_reg{inst.src[2].code * 4}; const auto flags = ImageResFlags(inst.control.mimg.dmask); const bool has_mips = flags.test(ImageResComponent::MipCount); const IR::U32 lod = ir.GetVectorReg(IR::VectorReg(inst.src[0].code)); const IR::Value tsharp = ir.GetScalarReg(tsharp_reg); - const IR::Value size = ir.ImageQueryDimension(tsharp, lod, ir.Imm1(has_mips)); + + IR::TextureInstInfo info{}; + info.is_array.Assign(mimg.da); + + const IR::Value size = ir.ImageQueryDimension(tsharp, lod, ir.Imm1(has_mips), info); if (flags.test(ImageResComponent::Width)) { ir.SetVectorReg(dst_reg++, IR::U32{ir.CompositeExtract(size, 0)}); @@ -484,6 +491,9 @@ void Translator::IMAGE_ATOMIC(AtomicOp op, const GcnInst& inst) { IR::VectorReg addr_reg{inst.src[0].code}; const IR::ScalarReg tsharp_reg{inst.src[2].code * 4}; + IR::TextureInstInfo info{}; + info.is_array.Assign(mimg.da); + const IR::Value value = ir.GetVectorReg(val_reg); const IR::Value handle = ir.GetScalarReg(tsharp_reg); const IR::Value body = @@ -494,25 +504,25 @@ void Translator::IMAGE_ATOMIC(AtomicOp op, const GcnInst& inst) { case AtomicOp::Swap: return ir.ImageAtomicExchange(handle, body, value, {}); case AtomicOp::Add: - return ir.ImageAtomicIAdd(handle, body, value, {}); + return ir.ImageAtomicIAdd(handle, body, value, info); case AtomicOp::Smin: - return ir.ImageAtomicIMin(handle, body, value, true, {}); + return ir.ImageAtomicIMin(handle, body, value, true, info); case AtomicOp::Umin: - return ir.ImageAtomicUMin(handle, body, value, {}); + return ir.ImageAtomicUMin(handle, body, value, info); case AtomicOp::Smax: - return ir.ImageAtomicIMax(handle, body, value, true, {}); + return ir.ImageAtomicIMax(handle, body, value, true, info); case AtomicOp::Umax: - return ir.ImageAtomicUMax(handle, body, value, {}); + return ir.ImageAtomicUMax(handle, body, value, info); case AtomicOp::And: - return ir.ImageAtomicAnd(handle, body, value, {}); + return ir.ImageAtomicAnd(handle, body, value, info); case AtomicOp::Or: - return ir.ImageAtomicOr(handle, body, value, {}); + return ir.ImageAtomicOr(handle, body, value, info); case AtomicOp::Xor: - return ir.ImageAtomicXor(handle, body, value, {}); + return ir.ImageAtomicXor(handle, body, value, info); case AtomicOp::Inc: - return ir.ImageAtomicInc(handle, body, value, {}); + return ir.ImageAtomicInc(handle, body, value, info); case AtomicOp::Dec: - return ir.ImageAtomicDec(handle, body, value, {}); + return ir.ImageAtomicDec(handle, body, value, info); default: UNREACHABLE(); } @@ -643,11 +653,14 @@ void Translator::IMAGE_GET_LOD(const GcnInst& inst) { IR::VectorReg addr_reg{inst.src[0].code}; const IR::ScalarReg tsharp_reg{inst.src[2].code * 4}; + IR::TextureInstInfo info{}; + info.is_array.Assign(mimg.da); + const IR::Value handle = ir.GetScalarReg(tsharp_reg); const IR::Value body = ir.CompositeConstruct( ir.GetVectorReg(addr_reg), ir.GetVectorReg(addr_reg + 1), ir.GetVectorReg(addr_reg + 2), ir.GetVectorReg(addr_reg + 3)); - const IR::Value lod = ir.ImageQueryLod(handle, body, {}); + const IR::Value lod = ir.ImageQueryLod(handle, body, info); ir.SetVectorReg(dst_reg++, IR::F32{ir.CompositeExtract(lod, 0)}); ir.SetVectorReg(dst_reg++, IR::F32{ir.CompositeExtract(lod, 1)}); } diff --git a/src/shader_recompiler/info.h b/src/shader_recompiler/info.h index b6ac12785..aeff346fa 100644 --- a/src/shader_recompiler/info.h +++ b/src/shader_recompiler/info.h @@ -70,14 +70,8 @@ struct ImageResource { bool is_depth{}; bool is_atomic{}; bool is_array{}; - bool is_read{}; bool is_written{}; - [[nodiscard]] bool IsStorage(const AmdGpu::Image& image) const noexcept { - // Need cube as storage when used with ImageRead. - return is_written || (is_read && image.GetBoundType() == AmdGpu::ImageType::Cube); - } - [[nodiscard]] constexpr AmdGpu::Image GetSharp(const Info& info) const noexcept; }; using ImageResourceList = boost::container::small_vector; diff --git a/src/shader_recompiler/ir/ir_emitter.cpp b/src/shader_recompiler/ir/ir_emitter.cpp index 823f9bdcd..8626bdfd1 100644 --- a/src/shader_recompiler/ir/ir_emitter.cpp +++ b/src/shader_recompiler/ir/ir_emitter.cpp @@ -1732,11 +1732,6 @@ Value IREmitter::ImageGatherDref(const Value& handle, const Value& coords, const return Inst(Opcode::ImageGatherDref, Flags{info}, handle, coords, offset, dref); } -Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod, - const IR::U1& skip_mips) { - return Inst(Opcode::ImageQueryDimensions, handle, lod, skip_mips); -} - Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod, const IR::U1& skip_mips, TextureInstInfo info) { return Inst(Opcode::ImageQueryDimensions, Flags{info}, handle, lod, skip_mips); @@ -1763,6 +1758,14 @@ void IREmitter::ImageWrite(const Value& handle, const Value& coords, const U32& Inst(Opcode::ImageWrite, Flags{info}, handle, coords, lod, multisampling, color); } +[[nodiscard]] Value IREmitter::CubeFaceCoord(const Value& cube_coords) { + return Inst(Opcode::CubeFaceCoord, cube_coords); +} + +[[nodiscard]] F32 IREmitter::CubeFaceIndex(const Value& cube_coords) { + return Inst(Opcode::CubeFaceIndex, cube_coords); +} + // Debug print maps to SPIRV's NonSemantic DebugPrintf instruction // Renderdoc will hook in its own implementation of the SPIRV instruction // Renderdoc accepts format specifiers, e.g. %u, listed here: diff --git a/src/shader_recompiler/ir/ir_emitter.h b/src/shader_recompiler/ir/ir_emitter.h index 9aab9459b..783709775 100644 --- a/src/shader_recompiler/ir/ir_emitter.h +++ b/src/shader_recompiler/ir/ir_emitter.h @@ -324,8 +324,6 @@ public: const F32& dref, const F32& lod, const Value& offset, TextureInstInfo info); - [[nodiscard]] Value ImageQueryDimension(const Value& handle, const U32& lod, - const U1& skip_mips); [[nodiscard]] Value ImageQueryDimension(const Value& handle, const U32& lod, const U1& skip_mips, TextureInstInfo info); @@ -344,6 +342,9 @@ public: void ImageWrite(const Value& handle, const Value& coords, const U32& lod, const U32& multisampling, const Value& color, TextureInstInfo info); + [[nodiscard]] Value CubeFaceCoord(const Value& cube_coords); + [[nodiscard]] F32 CubeFaceIndex(const Value& cube_coords); + void EmitVertex(); void EmitPrimitive(); diff --git a/src/shader_recompiler/ir/opcodes.inc b/src/shader_recompiler/ir/opcodes.inc index 6242a230e..19f45418f 100644 --- a/src/shader_recompiler/ir/opcodes.inc +++ b/src/shader_recompiler/ir/opcodes.inc @@ -374,6 +374,10 @@ OPCODE(ImageAtomicOr32, U32, Opaq OPCODE(ImageAtomicXor32, U32, Opaque, Opaque, U32, ) OPCODE(ImageAtomicExchange32, U32, Opaque, Opaque, U32, ) +// Cube operations - optional, usable if profile.supports_native_cube_calc +OPCODE(CubeFaceCoord, F32x2, F32x3, ) +OPCODE(CubeFaceIndex, F32, F32x3, ) + // Warp operations OPCODE(LaneId, U32, ) OPCODE(WarpId, U32, ) diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index f7040ad75..c00b8e6bb 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -161,10 +161,9 @@ public: u32 Add(const ImageResource& desc) { const u32 index{Add(image_resources, desc, [&desc](const auto& existing) { - return desc.sharp_idx == existing.sharp_idx; + return desc.sharp_idx == existing.sharp_idx && desc.is_array == existing.is_array; })}; auto& image = image_resources[index]; - image.is_read |= desc.is_read; image.is_written |= desc.is_written; return index; } @@ -361,7 +360,6 @@ void PatchImageSharp(IR::Block& block, IR::Inst& inst, Info& info, Descriptors& image = AmdGpu::Image::Null(); } ASSERT(image.GetType() != AmdGpu::ImageType::Invalid); - const bool is_read = inst.GetOpcode() == IR::Opcode::ImageRead; const bool is_written = inst.GetOpcode() == IR::Opcode::ImageWrite; // Patch image instruction if image is FMask. @@ -402,7 +400,6 @@ void PatchImageSharp(IR::Block& block, IR::Inst& inst, Info& info, Descriptors& .is_depth = bool(inst_info.is_depth), .is_atomic = IsImageAtomicInstruction(inst), .is_array = bool(inst_info.is_array), - .is_read = is_read, .is_written = is_written, }); @@ -560,32 +557,6 @@ void PatchTextureBufferArgs(IR::Block& block, IR::Inst& inst, Info& info) { } } -IR::Value PatchCubeCoord(IR::IREmitter& ir, const IR::Value& s, const IR::Value& t, - const IR::Value& z, bool is_written, bool is_array) { - // When cubemap is written with imageStore it is treated like 2DArray. - if (is_written) { - return ir.CompositeConstruct(s, t, z); - } - - ASSERT(s.Type() == IR::Type::F32); // in case of fetched image need to adjust the code below - - // We need to fix x and y coordinate, - // because the s and t coordinate will be scaled and plus 1.5 by v_madak_f32. - // We already force the scale value to be 1.0 when handling v_cubema_f32, - // here we subtract 1.5 to recover the original value. - const IR::Value x = ir.FPSub(IR::F32{s}, ir.Imm32(1.5f)); - const IR::Value y = ir.FPSub(IR::F32{t}, ir.Imm32(1.5f)); - if (is_array) { - const IR::U32 array_index = ir.ConvertFToU(32, IR::F32{z}); - const IR::U32 face_id = ir.BitwiseAnd(array_index, ir.Imm32(7u)); - const IR::U32 slice_id = ir.ShiftRightLogical(array_index, ir.Imm32(3u)); - return ir.CompositeConstruct(x, y, ir.ConvertIToF(32, 32, false, face_id), - ir.ConvertIToF(32, 32, false, slice_id)); - } else { - return ir.CompositeConstruct(x, y, z); - } -} - void PatchImageSampleArgs(IR::Block& block, IR::Inst& inst, Info& info, const AmdGpu::Image& image) { const auto handle = inst.Arg(0); @@ -649,7 +620,6 @@ void PatchImageSampleArgs(IR::Block& block, IR::Inst& inst, Info& info, case AmdGpu::ImageType::Color2DMsaa: return ir.CompositeConstruct(read(0), read(8)); case AmdGpu::ImageType::Color3D: - case AmdGpu::ImageType::Cube: return ir.CompositeConstruct(read(0), read(8), read(16)); default: UNREACHABLE(); @@ -675,7 +645,6 @@ void PatchImageSampleArgs(IR::Block& block, IR::Inst& inst, Info& info, return {ir.CompositeConstruct(get_addr_reg(addr_reg - 4), get_addr_reg(addr_reg - 3)), ir.CompositeConstruct(get_addr_reg(addr_reg - 2), get_addr_reg(addr_reg - 1))}; case AmdGpu::ImageType::Color3D: - case AmdGpu::ImageType::Cube: // (du/dx, dv/dx, dw/dx), (du/dy, dv/dy, dw/dy) addr_reg = addr_reg + 6; return {ir.CompositeConstruct(get_addr_reg(addr_reg - 6), get_addr_reg(addr_reg - 5), @@ -691,7 +660,8 @@ void PatchImageSampleArgs(IR::Block& block, IR::Inst& inst, Info& info, // Query dimensions of image if needed for normalization. // We can't use the image sharp because it could be bound to a different image later. const auto dimensions = - unnormalized ? ir.ImageQueryDimension(handle, ir.Imm32(0u), ir.Imm1(false)) : IR::Value{}; + unnormalized ? ir.ImageQueryDimension(handle, ir.Imm32(0u), ir.Imm1(false), inst_info) + : IR::Value{}; const auto get_coord = [&](u32 coord_idx, u32 dim_idx) -> IR::Value { const auto coord = get_addr_reg(coord_idx); if (unnormalized) { @@ -724,10 +694,6 @@ void PatchImageSampleArgs(IR::Block& block, IR::Inst& inst, Info& info, addr_reg = addr_reg + 3; return ir.CompositeConstruct(get_coord(addr_reg - 3, 0), get_coord(addr_reg - 2, 1), get_coord(addr_reg - 1, 2)); - case AmdGpu::ImageType::Cube: // x, y, face - addr_reg = addr_reg + 3; - return PatchCubeCoord(ir, get_coord(addr_reg - 3, 0), get_coord(addr_reg - 2, 1), - get_addr_reg(addr_reg - 1), false, inst_info.is_array); default: UNREACHABLE(); } @@ -805,10 +771,6 @@ void PatchImageArgs(IR::Block& block, IR::Inst& inst, Info& info) { [[fallthrough]]; case AmdGpu::ImageType::Color3D: // x, y, z, [lod] return {ir.CompositeConstruct(body->Arg(0), body->Arg(1), body->Arg(2)), body->Arg(3)}; - case AmdGpu::ImageType::Cube: // x, y, face, [lod] - return {PatchCubeCoord(ir, body->Arg(0), body->Arg(1), body->Arg(2), - inst.GetOpcode() == IR::Opcode::ImageWrite, inst_info.is_array), - body->Arg(3)}; default: UNREACHABLE_MSG("Unknown image type {}", image.GetType()); } @@ -820,7 +782,7 @@ void PatchImageArgs(IR::Block& block, IR::Inst& inst, Info& info) { const auto lod = inst_info.has_lod ? IR::U32{arg} : IR::U32{}; const auto ms = has_ms ? IR::U32{arg} : IR::U32{}; - const auto is_storage = image_res.IsStorage(image); + const auto is_storage = image_res.is_written; if (inst.GetOpcode() == IR::Opcode::ImageRead) { auto texel = ir.ImageRead(handle, coords, lod, ms, inst_info); if (is_storage) { diff --git a/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp b/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp index c34b59b88..7fd5b75ff 100644 --- a/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp +++ b/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp @@ -5,7 +5,7 @@ namespace Shader::Optimization { -void Visit(Info& info, IR::Inst& inst) { +void Visit(Info& info, const IR::Inst& inst) { switch (inst.GetOpcode()) { case IR::Opcode::GetAttribute: case IR::Opcode::GetAttributeU32: diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index fc8c5956e..f8878d442 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h @@ -24,6 +24,7 @@ struct Profile { bool support_explicit_workgroup_layout{}; bool support_legacy_vertex_attributes{}; bool supports_image_load_store_lod{}; + bool supports_native_cube_calc{}; bool has_broken_spirv_clamp{}; bool lower_left_origin_mode{}; bool needs_manual_interpolation{}; diff --git a/src/shader_recompiler/specialization.h b/src/shader_recompiler/specialization.h index f58d2e2d3..c03621c50 100644 --- a/src/shader_recompiler/specialization.h +++ b/src/shader_recompiler/specialization.h @@ -113,9 +113,9 @@ struct StageSpecialization { }); ForEachSharp(binding, images, info->images, [](auto& spec, const auto& desc, AmdGpu::Image sharp) { - spec.type = sharp.GetBoundType(); + spec.type = sharp.GetBoundType(desc.is_array); spec.is_integer = AmdGpu::IsInteger(sharp.GetNumberFmt()); - spec.is_storage = desc.IsStorage(sharp); + spec.is_storage = desc.is_written; if (spec.is_storage) { spec.dst_select = sharp.DstSelect(); } diff --git a/src/video_core/amdgpu/resource.h b/src/video_core/amdgpu/resource.h index ffee7964a..3dc1eadde 100644 --- a/src/video_core/amdgpu/resource.h +++ b/src/video_core/amdgpu/resource.h @@ -226,15 +226,13 @@ struct Image { return pitch + 1; } - u32 NumLayers(bool is_array) const { - u32 slices = GetType() == ImageType::Color3D ? 1 : depth + 1; - if (GetType() == ImageType::Cube) { - if (is_array) { - slices = last_array + 1; - ASSERT(slices % 6 == 0); - } else { - slices = 6; - } + [[nodiscard]] u32 NumLayers() const noexcept { + u32 slices = depth + 1; + const auto img_type = static_cast(type); + if (img_type == ImageType::Color3D) { + slices = 1; + } else if (img_type == ImageType::Cube) { + slices *= 6; } if (pow2pad) { slices = std::bit_ceil(slices); @@ -257,7 +255,8 @@ struct Image { } ImageType GetType() const noexcept { - return static_cast(type); + const auto img_type = static_cast(type); + return img_type == ImageType::Cube ? ImageType::Color2DArray : img_type; } DataFormat GetDataFmt() const noexcept { @@ -289,13 +288,40 @@ struct Image { GetDataFmt() <= DataFormat::FormatFmask64_8; } - bool IsPartialCubemap() const { - const auto viewed_slice = last_array - base_array + 1; - return GetType() == ImageType::Cube && viewed_slice < 6; + [[nodiscard]] ImageType GetBoundType(const bool is_array) const noexcept { + const auto base_type = GetType(); + if (base_type == ImageType::Color1DArray && !is_array) { + return ImageType::Color1D; + } + if (base_type == ImageType::Color2DArray && !is_array) { + return ImageType::Color2D; + } + if (base_type == ImageType::Color2DMsaaArray && !is_array) { + return ImageType::Color2DMsaa; + } + return base_type; } - ImageType GetBoundType() const noexcept { - return IsPartialCubemap() ? ImageType::Color2DArray : GetType(); + [[nodiscard]] u32 NumViewLevels(const bool is_array) const noexcept { + switch (GetBoundType(is_array)) { + case ImageType::Color2DMsaa: + case ImageType::Color2DMsaaArray: + return 1; + default: + return last_level - base_level + 1; + } + } + + [[nodiscard]] u32 NumViewLayers(const bool is_array) const noexcept { + switch (GetBoundType(is_array)) { + case ImageType::Color1D: + case ImageType::Color2D: + case ImageType::Color2DMsaa: + case ImageType::Color3D: + return 1; + default: + return last_array - base_array + 1; + } } }; static_assert(sizeof(Image) == 32); // 256bits diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp index b24767e8a..23faacfc2 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp @@ -59,9 +59,8 @@ ComputePipeline::ComputePipeline(const Instance& instance_, Scheduler& scheduler for (const auto& image : info->images) { bindings.push_back({ .binding = binding++, - .descriptorType = image.IsStorage(image.GetSharp(*info)) - ? vk::DescriptorType::eStorageImage - : vk::DescriptorType::eSampledImage, + .descriptorType = image.is_written ? vk::DescriptorType::eStorageImage + : vk::DescriptorType::eSampledImage, .descriptorCount = 1, .stageFlags = vk::ShaderStageFlagBits::eCompute, }); diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 0ca1bed8b..0154172d9 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -367,9 +367,8 @@ void GraphicsPipeline::BuildDescSetLayout() { for (const auto& image : stage->images) { bindings.push_back({ .binding = binding++, - .descriptorType = image.IsStorage(image.GetSharp(*stage)) - ? vk::DescriptorType::eStorageImage - : vk::DescriptorType::eSampledImage, + .descriptorType = image.is_written ? vk::DescriptorType::eStorageImage + : vk::DescriptorType::eSampledImage, .descriptorCount = 1, .stageFlags = gp_stage_flags, }); diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 9bc627830..6c3e066c6 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -271,6 +271,7 @@ bool Instance::CreateDevice() { maintenance5 = add_extension(VK_KHR_MAINTENANCE_5_EXTENSION_NAME); legacy_vertex_attributes = add_extension(VK_EXT_LEGACY_VERTEX_ATTRIBUTES_EXTENSION_NAME); image_load_store_lod = add_extension(VK_AMD_SHADER_IMAGE_LOAD_STORE_LOD_EXTENSION_NAME); + amd_gcn_shader = add_extension(VK_AMD_GCN_SHADER_EXTENSION_NAME); // These extensions are promoted by Vulkan 1.3, but for greater compatibility we use Vulkan 1.2 // with extensions. diff --git a/src/video_core/renderer_vulkan/vk_instance.h b/src/video_core/renderer_vulkan/vk_instance.h index 4e091824d..8928b4267 100644 --- a/src/video_core/renderer_vulkan/vk_instance.h +++ b/src/video_core/renderer_vulkan/vk_instance.h @@ -159,6 +159,11 @@ public: return image_load_store_lod; } + /// Returns true when VK_AMD_gcn_shader is supported. + bool IsAmdGcnShaderSupported() const { + return amd_gcn_shader; + } + /// Returns true when geometry shaders are supported by the device bool IsGeometryStageSupported() const { return features.geometryShader; @@ -334,6 +339,7 @@ private: bool list_restart{}; bool legacy_vertex_attributes{}; bool image_load_store_lod{}; + bool amd_gcn_shader{}; u64 min_imported_host_pointer_alignment{}; u32 subgroup_size{}; bool tooling_info{}; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 9cfc7c277..37137bd07 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -204,6 +204,7 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_, .support_explicit_workgroup_layout = true, .support_legacy_vertex_attributes = instance_.IsLegacyVertexAttributesSupported(), .supports_image_load_store_lod = instance_.IsImageLoadStoreLodSupported(), + .supports_native_cube_calc = instance_.IsAmdGcnShaderSupported(), .needs_manual_interpolation = instance.IsFragmentShaderBarycentricSupported() && instance.GetDriverID() == vk::DriverId::eNvidiaProprietary, .needs_lds_barriers = instance.GetDriverID() == vk::DriverId::eNvidiaProprietary || diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index e8616550b..ceb1d094d 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -661,7 +661,7 @@ void Rasterizer::BindTextures(const Shader::Info& stage, Shader::Backend::Bindin if (image->binding.is_bound) { // The image is already bound. In case if it is about to be used as storage we need // to force general layout on it. - image->binding.force_general |= image_desc.IsStorage(tsharp); + image->binding.force_general |= image_desc.is_written; } if (image->binding.is_target) { // The image is already bound as target. Since we read and output to it need to force diff --git a/src/video_core/texture_cache/image.cpp b/src/video_core/texture_cache/image.cpp index 23249bf21..96881c564 100644 --- a/src/video_core/texture_cache/image.cpp +++ b/src/video_core/texture_cache/image.cpp @@ -153,13 +153,7 @@ Image::Image(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_, // the texture cache should re-create the resource with the usage requested vk::ImageCreateFlags flags{vk::ImageCreateFlagBits::eMutableFormat | vk::ImageCreateFlagBits::eExtendedUsage}; - const bool can_be_cube = - (info.type == vk::ImageType::e2D) && - ((info.props.is_pow2 ? (info.resources.layers % 8) : (info.resources.layers % 6)) == 0) && - (info.size.width == info.size.height); - if (info.props.is_cube || can_be_cube) { - flags |= vk::ImageCreateFlagBits::eCubeCompatible; - } else if (info.props.is_volume) { + if (info.props.is_volume) { flags |= vk::ImageCreateFlagBits::e2DArrayCompatible; } diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index bdbaecda6..58c2a8e23 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -37,7 +37,6 @@ static vk::ImageType ConvertImageType(AmdGpu::ImageType type) noexcept { return vk::ImageType::e1D; case AmdGpu::ImageType::Color2D: case AmdGpu::ImageType::Color2DMsaa: - case AmdGpu::ImageType::Cube: case AmdGpu::ImageType::Color2DArray: return vk::ImageType::e2D; case AmdGpu::ImageType::Color3D: @@ -130,7 +129,6 @@ ImageInfo::ImageInfo(const AmdGpu::Image& image, const Shader::ImageResource& de } type = ConvertImageType(image.GetType()); props.is_tiled = image.IsTiled(); - props.is_cube = image.GetType() == AmdGpu::ImageType::Cube; props.is_volume = image.GetType() == AmdGpu::ImageType::Color3D; props.is_pow2 = image.pow2pad; props.is_block = IsBlockCoded(); @@ -139,7 +137,7 @@ ImageInfo::ImageInfo(const AmdGpu::Image& image, const Shader::ImageResource& de size.depth = props.is_volume ? image.depth + 1 : 1; pitch = image.Pitch(); resources.levels = image.NumLevels(); - resources.layers = image.NumLayers(desc.is_array); + resources.layers = image.NumLayers(); num_samples = image.NumSamples(); num_bits = NumBits(image.GetDataFmt()); diff --git a/src/video_core/texture_cache/image_info.h b/src/video_core/texture_cache/image_info.h index 6faca49c5..123540c1e 100644 --- a/src/video_core/texture_cache/image_info.h +++ b/src/video_core/texture_cache/image_info.h @@ -61,7 +61,6 @@ struct ImageInfo { } meta_info{}; struct { - u32 is_cube : 1; u32 is_volume : 1; u32 is_tiled : 1; u32 is_pow2 : 1; diff --git a/src/video_core/texture_cache/image_view.cpp b/src/video_core/texture_cache/image_view.cpp index 68b116558..569238168 100644 --- a/src/video_core/texture_cache/image_view.cpp +++ b/src/video_core/texture_cache/image_view.cpp @@ -20,8 +20,6 @@ vk::ImageViewType ConvertImageViewType(AmdGpu::ImageType type) { case AmdGpu::ImageType::Color2D: case AmdGpu::ImageType::Color2DMsaa: return vk::ImageViewType::e2D; - case AmdGpu::ImageType::Cube: - return vk::ImageViewType::eCube; case AmdGpu::ImageType::Color2DArray: return vk::ImageViewType::e2DArray; case AmdGpu::ImageType::Color3D: @@ -32,7 +30,7 @@ vk::ImageViewType ConvertImageViewType(AmdGpu::ImageType type) { } ImageViewInfo::ImageViewInfo(const AmdGpu::Image& image, const Shader::ImageResource& desc) noexcept - : is_storage{desc.IsStorage(image)} { + : is_storage{desc.is_written} { const auto dfmt = image.GetDataFmt(); auto nfmt = image.GetNumberFmt(); if (is_storage && nfmt == AmdGpu::NumberFormat::Srgb) { @@ -42,30 +40,12 @@ ImageViewInfo::ImageViewInfo(const AmdGpu::Image& image, const Shader::ImageReso if (desc.is_depth) { format = Vulkan::LiverpoolToVK::PromoteFormatToDepth(format); } + range.base.level = image.base_level; range.base.layer = image.base_array; - if (image.GetType() == AmdGpu::ImageType::Color2DMsaa || - image.GetType() == AmdGpu::ImageType::Color2DMsaaArray) { - range.extent.levels = 1; - } else { - range.extent.levels = image.last_level - image.base_level + 1; - } - range.extent.layers = image.last_array - image.base_array + 1; - type = ConvertImageViewType(image.GetBoundType()); - - // Adjust view type for arrays - if (type == vk::ImageViewType::eCube) { - if (desc.is_array) { - type = vk::ImageViewType::eCubeArray; - } else { - // Some games try to bind an array of cubemaps while shader reads only single one. - range.extent.layers = std::min(range.extent.layers, 6u); - } - } - if (type == vk::ImageViewType::e3D && range.extent.layers > 1) { - // Some games pass incorrect layer count for 3D textures so we need to fixup it. - range.extent.layers = 1; - } + range.extent.levels = image.NumViewLevels(desc.is_array); + range.extent.layers = image.NumViewLayers(desc.is_array); + type = ConvertImageViewType(image.GetBoundType(desc.is_array)); if (!is_storage) { mapping = Vulkan::LiverpoolToVK::ComponentMapping(image.DstSelect()); diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 944f021df..69907f000 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -65,7 +65,7 @@ public: struct TextureDesc : public BaseDesc { TextureDesc() = default; TextureDesc(const AmdGpu::Image& image, const Shader::ImageResource& desc) - : BaseDesc{desc.IsStorage(image) ? BindingType::Storage : BindingType::Texture, + : BaseDesc{desc.is_written ? BindingType::Storage : BindingType::Texture, ImageInfo{image, desc}, ImageViewInfo{image, desc}} {} }; From 4563b6379d9927b9f5469250ef0db58440a72c69 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 10 Jan 2025 00:49:08 -0800 Subject: [PATCH 388/549] amdgpu: Handle 8-bit float format case for stencil. (#2092) --- src/video_core/amdgpu/liverpool.h | 3 ++- src/video_core/amdgpu/resource.h | 4 ++-- src/video_core/amdgpu/types.h | 10 +++++++++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index 837b73d89..070253ca9 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -899,7 +899,8 @@ struct Liverpool { // There is a small difference between T# and CB number types, account for it. return RemapNumberFormat(info.number_type == NumberFormat::SnormNz ? NumberFormat::Srgb - : info.number_type.Value()); + : info.number_type.Value(), + info.format); } [[nodiscard]] NumberConversion GetNumberConversion() const { diff --git a/src/video_core/amdgpu/resource.h b/src/video_core/amdgpu/resource.h index 3dc1eadde..467cecf10 100644 --- a/src/video_core/amdgpu/resource.h +++ b/src/video_core/amdgpu/resource.h @@ -54,7 +54,7 @@ struct Buffer { } NumberFormat GetNumberFmt() const noexcept { - return RemapNumberFormat(NumberFormat(num_format)); + return RemapNumberFormat(NumberFormat(num_format), DataFormat(data_format)); } DataFormat GetDataFmt() const noexcept { @@ -264,7 +264,7 @@ struct Image { } NumberFormat GetNumberFmt() const noexcept { - return RemapNumberFormat(NumberFormat(num_format)); + return RemapNumberFormat(NumberFormat(num_format), DataFormat(data_format)); } NumberConversion GetNumberConversion() const noexcept { diff --git a/src/video_core/amdgpu/types.h b/src/video_core/amdgpu/types.h index a19e53256..57f97418a 100644 --- a/src/video_core/amdgpu/types.h +++ b/src/video_core/amdgpu/types.h @@ -252,7 +252,7 @@ inline DataFormat RemapDataFormat(const DataFormat format) { } } -inline NumberFormat RemapNumberFormat(const NumberFormat format) { +inline NumberFormat RemapNumberFormat(const NumberFormat format, const DataFormat data_format) { switch (format) { case NumberFormat::Uscaled: return NumberFormat::Uint; @@ -260,6 +260,14 @@ inline NumberFormat RemapNumberFormat(const NumberFormat format) { return NumberFormat::Sint; case NumberFormat::Ubnorm: return NumberFormat::Unorm; + case NumberFormat::Float: + if (data_format == DataFormat::Format8) { + // Games may ask for 8-bit float when they want to access the stencil component + // of a depth-stencil image. Change to unsigned int to match the stencil format. + // This is also the closest approximation to pass the bits through unconverted. + return NumberFormat::Uint; + } + [[fallthrough]]; default: return format; } From 562ed2a025d76d2578fc43763bbcf6646730ca0d Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 10 Jan 2025 00:52:12 -0800 Subject: [PATCH 389/549] renderer_vulkan: Simplify vertex binding logic and properly handle null buffers. (#2104) * renderer_vulkan: Simplify vertex binding logic and properly handle null buffers. * renderer_vulkan: Remove need for empty bindVertexBuffers2EXT. --- src/video_core/buffer_cache/buffer_cache.cpp | 158 +++++++----------- src/video_core/buffer_cache/buffer_cache.h | 9 +- .../renderer_vulkan/vk_graphics_pipeline.cpp | 81 +++++---- .../renderer_vulkan/vk_graphics_pipeline.h | 9 + .../renderer_vulkan/vk_pipeline_cache.cpp | 8 +- .../renderer_vulkan/vk_rasterizer.cpp | 10 +- 6 files changed, 137 insertions(+), 138 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index 3a210957e..487544a21 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -10,13 +10,13 @@ #include "video_core/amdgpu/liverpool.h" #include "video_core/buffer_cache/buffer_cache.h" #include "video_core/renderer_vulkan/liverpool_to_vk.h" +#include "video_core/renderer_vulkan/vk_graphics_pipeline.h" #include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/texture_cache/texture_cache.h" namespace VideoCore { -static constexpr size_t NumVertexBuffers = 32; static constexpr size_t GdsBufferSize = 64_KB; static constexpr size_t StagingBufferSize = 1_GB; static constexpr size_t UboStreamBufferSize = 64_MB; @@ -89,35 +89,22 @@ void BufferCache::DownloadBufferMemory(Buffer& buffer, VAddr device_addr, u64 si } } -bool BufferCache::BindVertexBuffers( - const Shader::Info& vs_info, const std::optional& fetch_shader) { - boost::container::small_vector attributes; - boost::container::small_vector bindings; - SCOPE_EXIT { - if (instance.IsVertexInputDynamicState()) { - const auto cmdbuf = scheduler.CommandBuffer(); - cmdbuf.setVertexInputEXT(bindings, attributes); - } else if (bindings.empty()) { - // Required to call bindVertexBuffers2EXT at least once in the current command buffer - // with non-null strides without a non-dynamic stride pipeline in between. Thus even - // when nothing is bound we still need to make a dummy call. Non-null strides in turn - // requires a count greater than 0. - const auto cmdbuf = scheduler.CommandBuffer(); - const std::array null_buffers = {GetBuffer(NULL_BUFFER_ID).buffer.buffer}; - constexpr std::array null_offsets = {static_cast(0)}; - cmdbuf.bindVertexBuffers2EXT(0, null_buffers, null_offsets, null_offsets, null_offsets); - } - }; +void BufferCache::BindVertexBuffers(const Vulkan::GraphicsPipeline& pipeline) { + Vulkan::VertexInputs attributes; + Vulkan::VertexInputs bindings; + Vulkan::VertexInputs guest_buffers; + pipeline.GetVertexInputs(attributes, bindings, guest_buffers); - if (!fetch_shader || fetch_shader->attributes.empty()) { - return false; + if (instance.IsVertexInputDynamicState()) { + // Update current vertex inputs. + const auto cmdbuf = scheduler.CommandBuffer(); + cmdbuf.setVertexInputEXT(bindings, attributes); } - std::array host_buffers; - std::array host_offsets; - std::array host_sizes; - std::array host_strides; - boost::container::static_vector guest_buffers; + if (bindings.empty()) { + // If there are no bindings, there is nothing further to do. + return; + } struct BufferRange { VAddr base_address; @@ -125,61 +112,37 @@ bool BufferCache::BindVertexBuffers( vk::Buffer vk_buffer; u64 offset; - size_t GetSize() const { + [[nodiscard]] size_t GetSize() const { return end_address - base_address; } }; - // Calculate buffers memory overlaps - bool has_step_rate = false; - boost::container::static_vector ranges{}; - for (const auto& attrib : fetch_shader->attributes) { - if (attrib.UsesStepRates()) { - has_step_rate = true; - continue; + // Build list of ranges covering the requested buffers + Vulkan::VertexInputs ranges{}; + for (const auto& buffer : guest_buffers) { + if (buffer.GetSize() > 0) { + ranges.emplace_back(buffer.base_address, buffer.base_address + buffer.GetSize()); } + } - const auto& buffer = attrib.GetSharp(vs_info); - if (buffer.GetSize() == 0) { - continue; - } - guest_buffers.emplace_back(buffer); - ranges.emplace_back(buffer.base_address, buffer.base_address + buffer.GetSize()); - attributes.push_back({ - .location = attrib.semantic, - .binding = attrib.semantic, - .format = - Vulkan::LiverpoolToVK::SurfaceFormat(buffer.GetDataFmt(), buffer.GetNumberFmt()), - .offset = 0, + // Merge connecting ranges together + Vulkan::VertexInputs ranges_merged{}; + if (!ranges.empty()) { + std::ranges::sort(ranges, [](const BufferRange& lhv, const BufferRange& rhv) { + return lhv.base_address < rhv.base_address; }); - bindings.push_back({ - .binding = attrib.semantic, - .stride = buffer.GetStride(), - .inputRate = attrib.GetStepRate() == Shader::Gcn::VertexAttribute::InstanceIdType::None - ? vk::VertexInputRate::eVertex - : vk::VertexInputRate::eInstance, - .divisor = 1, - }); - } - if (ranges.empty()) { - return false; - } - - std::ranges::sort(ranges, [](const BufferRange& lhv, const BufferRange& rhv) { - return lhv.base_address < rhv.base_address; - }); - - boost::container::static_vector ranges_merged{ranges[0]}; - for (auto range : ranges) { - auto& prev_range = ranges_merged.back(); - if (prev_range.end_address < range.base_address) { - ranges_merged.emplace_back(range); - } else { - prev_range.end_address = std::max(prev_range.end_address, range.end_address); + ranges_merged.emplace_back(ranges[0]); + for (auto range : ranges) { + auto& prev_range = ranges_merged.back(); + if (prev_range.end_address < range.base_address) { + ranges_merged.emplace_back(range); + } else { + prev_range.end_address = std::max(prev_range.end_address, range.end_address); + } } } - // Map buffers + // Map buffers for merged ranges for (auto& range : ranges_merged) { const auto [buffer, offset] = ObtainBuffer(range.base_address, range.GetSize(), false); range.vk_buffer = buffer->buffer; @@ -187,32 +150,39 @@ bool BufferCache::BindVertexBuffers( } // Bind vertex buffers - const size_t num_buffers = guest_buffers.size(); - for (u32 i = 0; i < num_buffers; ++i) { - const auto& buffer = guest_buffers[i]; - const auto host_buffer = std::ranges::find_if(ranges_merged, [&](const BufferRange& range) { - return (buffer.base_address >= range.base_address && - buffer.base_address < range.end_address); - }); - ASSERT(host_buffer != ranges_merged.cend()); - - host_buffers[i] = host_buffer->vk_buffer; - host_offsets[i] = host_buffer->offset + buffer.base_address - host_buffer->base_address; - host_sizes[i] = buffer.GetSize(); - host_strides[i] = buffer.GetStride(); - } - - if (num_buffers > 0) { - const auto cmdbuf = scheduler.CommandBuffer(); - if (instance.IsVertexInputDynamicState()) { - cmdbuf.bindVertexBuffers(0, num_buffers, host_buffers.data(), host_offsets.data()); + Vulkan::VertexInputs host_buffers; + Vulkan::VertexInputs host_offsets; + Vulkan::VertexInputs host_sizes; + Vulkan::VertexInputs host_strides; + const auto null_buffer = + instance.IsNullDescriptorSupported() ? VK_NULL_HANDLE : GetBuffer(NULL_BUFFER_ID).Handle(); + for (const auto& buffer : guest_buffers) { + if (buffer.GetSize() > 0) { + const auto host_buffer_info = + std::ranges::find_if(ranges_merged, [&](const BufferRange& range) { + return buffer.base_address >= range.base_address && + buffer.base_address < range.end_address; + }); + ASSERT(host_buffer_info != ranges_merged.cend()); + host_buffers.emplace_back(host_buffer_info->vk_buffer); + host_offsets.push_back(host_buffer_info->offset + buffer.base_address - + host_buffer_info->base_address); } else { - cmdbuf.bindVertexBuffers2EXT(0, num_buffers, host_buffers.data(), host_offsets.data(), - host_sizes.data(), host_strides.data()); + host_buffers.emplace_back(null_buffer); + host_offsets.push_back(0); } + host_sizes.push_back(buffer.GetSize()); + host_strides.push_back(buffer.GetStride()); } - return has_step_rate; + const auto cmdbuf = scheduler.CommandBuffer(); + const auto num_buffers = guest_buffers.size(); + if (instance.IsVertexInputDynamicState()) { + cmdbuf.bindVertexBuffers(0, num_buffers, host_buffers.data(), host_offsets.data()); + } else { + cmdbuf.bindVertexBuffers2EXT(0, num_buffers, host_buffers.data(), host_offsets.data(), + host_sizes.data(), host_strides.data()); + } } void BufferCache::BindIndexBuffer(u32 index_offset) { diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index f29a37b63..575ee2c60 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -5,8 +5,6 @@ #include #include -#include -#include #include "common/div_ceil.h" #include "common/slot_vector.h" #include "common/types.h" @@ -26,6 +24,10 @@ struct FetchShaderData; struct Info; } // namespace Shader +namespace Vulkan { +class GraphicsPipeline; +} + namespace VideoCore { using BufferId = Common::SlotId; @@ -75,8 +77,7 @@ public: void InvalidateMemory(VAddr device_addr, u64 size); /// Binds host vertex buffers for the current draw. - bool BindVertexBuffers(const Shader::Info& vs_info, - const std::optional& fetch_shader); + void BindVertexBuffers(const Vulkan::GraphicsPipeline& pipeline); /// Bind host index buffer for the current draw. void BindIndexBuffer(u32 index_offset); diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 0154172d9..2d0b19bbe 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -57,35 +57,11 @@ GraphicsPipeline::GraphicsPipeline( pipeline_layout = std::move(layout); SetObjectName(device, *pipeline_layout, "Graphics PipelineLayout {}", debug_str); - boost::container::static_vector vertex_bindings; - boost::container::static_vector vertex_attributes; - if (fetch_shader && !instance.IsVertexInputDynamicState()) { - const auto& vs_info = GetStage(Shader::LogicalStage::Vertex); - for (const auto& attrib : fetch_shader->attributes) { - if (attrib.UsesStepRates()) { - // Skip attribute binding as the data will be pulled by shader - continue; - } - - const auto buffer = attrib.GetSharp(vs_info); - if (buffer.GetSize() == 0) { - continue; - } - vertex_attributes.push_back({ - .location = attrib.semantic, - .binding = attrib.semantic, - .format = LiverpoolToVK::SurfaceFormat(buffer.GetDataFmt(), buffer.GetNumberFmt()), - .offset = 0, - }); - vertex_bindings.push_back({ - .binding = attrib.semantic, - .stride = buffer.GetStride(), - .inputRate = - attrib.GetStepRate() == Shader::Gcn::VertexAttribute::InstanceIdType::None - ? vk::VertexInputRate::eVertex - : vk::VertexInputRate::eInstance, - }); - } + VertexInputs vertex_attributes; + VertexInputs vertex_bindings; + VertexInputs guest_buffers; + if (!instance.IsVertexInputDynamicState()) { + GetVertexInputs(vertex_attributes, vertex_bindings, guest_buffers); } const vk::PipelineVertexInputStateCreateInfo vertex_input_info = { @@ -161,7 +137,7 @@ GraphicsPipeline::GraphicsPipeline( } if (instance.IsVertexInputDynamicState()) { dynamic_states.push_back(vk::DynamicState::eVertexInputEXT); - } else { + } else if (!vertex_bindings.empty()) { dynamic_states.push_back(vk::DynamicState::eVertexInputBindingStrideEXT); } @@ -329,6 +305,51 @@ GraphicsPipeline::GraphicsPipeline( GraphicsPipeline::~GraphicsPipeline() = default; +template +void GraphicsPipeline::GetVertexInputs(VertexInputs& attributes, + VertexInputs& bindings, + VertexInputs& guest_buffers) const { + if (!fetch_shader || fetch_shader->attributes.empty()) { + return; + } + const auto& vs_info = GetStage(Shader::LogicalStage::Vertex); + for (const auto& attrib : fetch_shader->attributes) { + if (attrib.UsesStepRates()) { + // Skip attribute binding as the data will be pulled by shader. + continue; + } + + const auto& buffer = attrib.GetSharp(vs_info); + attributes.push_back(Attribute{ + .location = attrib.semantic, + .binding = attrib.semantic, + .format = LiverpoolToVK::SurfaceFormat(buffer.GetDataFmt(), buffer.GetNumberFmt()), + .offset = 0, + }); + bindings.push_back(Binding{ + .binding = attrib.semantic, + .stride = buffer.GetStride(), + .inputRate = attrib.GetStepRate() == Shader::Gcn::VertexAttribute::InstanceIdType::None + ? vk::VertexInputRate::eVertex + : vk::VertexInputRate::eInstance, + }); + if constexpr (std::is_same_v) { + bindings.back().divisor = 1; + } + guest_buffers.emplace_back(buffer); + } +} + +// Declare templated GetVertexInputs for necessary types. +template void GraphicsPipeline::GetVertexInputs( + VertexInputs& attributes, + VertexInputs& bindings, + VertexInputs& guest_buffers) const; +template void GraphicsPipeline::GetVertexInputs( + VertexInputs& attributes, + VertexInputs& bindings, + VertexInputs& guest_buffers) const; + void GraphicsPipeline::BuildDescSetLayout() { boost::container::small_vector bindings; u32 binding{}; diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index fa10831a0..f696c1f72 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include "common/types.h" @@ -27,6 +28,9 @@ class DescriptorHeap; using Liverpool = AmdGpu::Liverpool; +template +using VertexInputs = boost::container::static_vector; + struct GraphicsPipelineKey { std::array stage_hashes; u32 num_color_attachments; @@ -100,6 +104,11 @@ public: key.prim_type == AmdGpu::PrimitiveType::QuadList; } + /// Gets the attributes and bindings for vertex inputs. + template + void GetVertexInputs(VertexInputs& attributes, VertexInputs& bindings, + VertexInputs& guest_buffers) const; + private: void BuildDescSetLayout(); diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 37137bd07..f93494389 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -420,17 +420,17 @@ bool PipelineCache::RefreshGraphicsKey() { } } - const auto vs_info = infos[static_cast(Shader::LogicalStage::Vertex)]; + const auto* vs_info = infos[static_cast(Shader::LogicalStage::Vertex)]; if (vs_info && fetch_shader && !instance.IsVertexInputDynamicState()) { + // Without vertex input dynamic state, the pipeline needs to specialize on format. + // Stride will still be handled outside the pipeline using dynamic state. u32 vertex_binding = 0; for (const auto& attrib : fetch_shader->attributes) { if (attrib.UsesStepRates()) { + // Skip attribute binding as the data will be pulled by shader. continue; } const auto& buffer = attrib.GetSharp(*vs_info); - if (buffer.GetSize() == 0) { - continue; - } ASSERT(vertex_binding < MaxVertexBufferCount); key.vertex_buffer_formats[vertex_binding++] = Vulkan::LiverpoolToVK::SurfaceFormat(buffer.GetDataFmt(), buffer.GetNumberFmt()); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index ceb1d094d..920e09131 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -248,9 +248,7 @@ void Rasterizer::Draw(bool is_indexed, u32 index_offset) { return; } - const auto& vs_info = pipeline->GetStage(Shader::LogicalStage::Vertex); - const auto& fetch_shader = pipeline->GetFetchShader(); - buffer_cache.BindVertexBuffers(vs_info, fetch_shader); + buffer_cache.BindVertexBuffers(*pipeline); if (is_indexed) { buffer_cache.BindIndexBuffer(index_offset); } @@ -258,6 +256,8 @@ void Rasterizer::Draw(bool is_indexed, u32 index_offset) { BeginRendering(*pipeline, state); UpdateDynamicState(*pipeline); + const auto& vs_info = pipeline->GetStage(Shader::LogicalStage::Vertex); + const auto& fetch_shader = pipeline->GetFetchShader(); const auto [vertex_offset, instance_offset] = GetDrawOffsets(regs, vs_info, fetch_shader); const auto cmdbuf = scheduler.CommandBuffer(); @@ -292,9 +292,7 @@ void Rasterizer::DrawIndirect(bool is_indexed, VAddr arg_address, u32 offset, u3 return; } - const auto& vs_info = pipeline->GetStage(Shader::LogicalStage::Vertex); - const auto& fetch_shader = pipeline->GetFetchShader(); - buffer_cache.BindVertexBuffers(vs_info, fetch_shader); + buffer_cache.BindVertexBuffers(*pipeline); if (is_indexed) { buffer_cache.BindIndexBuffer(0); } From e656093d8538f584967d3070020759b6e24d4151 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 10 Jan 2025 12:35:03 -0800 Subject: [PATCH 390/549] shader_recompiler: Fix some image view type issues. (#2118) --- .../backend/spirv/emit_spirv_image.cpp | 5 ++--- .../backend/spirv/spirv_emit_context.cpp | 4 ++-- .../backend/spirv/spirv_emit_context.h | 2 +- .../ir/passes/resource_tracking_pass.cpp | 20 ++++++++++--------- src/shader_recompiler/specialization.h | 2 +- src/video_core/amdgpu/resource.h | 17 +++++++++++----- src/video_core/texture_cache/image_view.cpp | 2 +- 7 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index 182437b63..b5ae507a0 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp @@ -174,13 +174,12 @@ Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, u32 handle, Id lod const auto sharp = ctx.info.images[handle & 0xFFFF].GetSharp(ctx.info); const Id zero = ctx.u32_zero_value; const auto mips{[&] { return has_mips ? ctx.OpImageQueryLevels(ctx.U32[1], image) : zero; }}; - const bool uses_lod{texture.bound_type != AmdGpu::ImageType::Color2DMsaa && - !texture.is_storage}; + const bool uses_lod{texture.view_type != AmdGpu::ImageType::Color2DMsaa && !texture.is_storage}; const auto query{[&](Id type) { return uses_lod ? ctx.OpImageQuerySizeLod(type, image, lod) : ctx.OpImageQuerySize(type, image); }}; - switch (texture.bound_type) { + switch (texture.view_type) { case AmdGpu::ImageType::Color1D: return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[1]), zero, zero, mips()); case AmdGpu::ImageType::Color1DArray: diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 6151c5c65..7e86dfb4b 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -773,7 +773,7 @@ spv::ImageFormat GetFormat(const AmdGpu::Image& image) { Id ImageType(EmitContext& ctx, const ImageResource& desc, Id sampled_type) { const auto image = desc.GetSharp(ctx.info); const auto format = desc.is_atomic ? GetFormat(image) : spv::ImageFormat::Unknown; - const auto type = image.GetBoundType(desc.is_array); + const auto type = image.GetViewType(desc.is_array); const u32 sampled = desc.is_written ? 2 : 1; switch (type) { case AmdGpu::ImageType::Color1D: @@ -814,7 +814,7 @@ void EmitContext::DefineImagesAndSamplers() { .sampled_type = is_storage ? sampled_type : TypeSampledImage(image_type), .pointer_type = pointer_type, .image_type = image_type, - .bound_type = sharp.GetBoundType(image_desc.is_array), + .view_type = sharp.GetViewType(image_desc.is_array), .is_integer = is_integer, .is_storage = is_storage, }); diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index 80d0d4d9f..f552055c0 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -222,7 +222,7 @@ public: Id sampled_type; Id pointer_type; Id image_type; - AmdGpu::ImageType bound_type; + AmdGpu::ImageType view_type; bool is_integer = false; bool is_storage = false; }; diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index c00b8e6bb..10d685ed1 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -558,13 +558,14 @@ void PatchTextureBufferArgs(IR::Block& block, IR::Inst& inst, Info& info) { } void PatchImageSampleArgs(IR::Block& block, IR::Inst& inst, Info& info, - const AmdGpu::Image& image) { + const ImageResource& image_res, const AmdGpu::Image& image) { const auto handle = inst.Arg(0); const auto sampler_res = info.samplers[(handle.U32() >> 16) & 0xFFFF]; auto sampler = sampler_res.GetSharp(info); IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; const auto inst_info = inst.Flags(); + const auto view_type = image.GetViewType(image_res.is_array); IR::Inst* body1 = inst.Arg(1).InstRecursive(); IR::Inst* body2 = inst.Arg(2).InstRecursive(); @@ -611,7 +612,7 @@ void PatchImageSampleArgs(IR::Block& block, IR::Inst& inst, Info& info, return ir.BitFieldExtract(IR::U32{arg}, ir.Imm32(off), ir.Imm32(6), true); }; - switch (image.GetType()) { + switch (view_type) { case AmdGpu::ImageType::Color1D: case AmdGpu::ImageType::Color1DArray: return read(0); @@ -631,7 +632,7 @@ void PatchImageSampleArgs(IR::Block& block, IR::Inst& inst, Info& info, if (!inst_info.has_derivatives) { return {}; } - switch (image.GetType()) { + switch (view_type) { case AmdGpu::ImageType::Color1D: case AmdGpu::ImageType::Color1DArray: // du/dx, du/dy @@ -675,7 +676,7 @@ void PatchImageSampleArgs(IR::Block& block, IR::Inst& inst, Info& info, // Now we can load body components as noted in Table 8.9 Image Opcodes with Sampler const IR::Value coords = [&] -> IR::Value { - switch (image.GetType()) { + switch (view_type) { case AmdGpu::ImageType::Color1D: // x addr_reg = addr_reg + 1; return get_coord(addr_reg - 1, 0); @@ -745,17 +746,18 @@ void PatchImageArgs(IR::Block& block, IR::Inst& inst, Info& info) { // Sample instructions must be handled separately using address register data. if (inst.GetOpcode() == IR::Opcode::ImageSampleRaw) { - PatchImageSampleArgs(block, inst, info, image); + PatchImageSampleArgs(block, inst, info, image_res, image); return; } IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; const auto inst_info = inst.Flags(); + const auto view_type = image.GetViewType(image_res.is_array); // Now that we know the image type, adjust texture coordinate vector. IR::Inst* body = inst.Arg(1).InstRecursive(); const auto [coords, arg] = [&] -> std::pair { - switch (image.GetType()) { + switch (view_type) { case AmdGpu::ImageType::Color1D: // x, [lod] return {body->Arg(0), body->Arg(1)}; case AmdGpu::ImageType::Color1DArray: // x, slice, [lod] @@ -772,12 +774,12 @@ void PatchImageArgs(IR::Block& block, IR::Inst& inst, Info& info) { case AmdGpu::ImageType::Color3D: // x, y, z, [lod] return {ir.CompositeConstruct(body->Arg(0), body->Arg(1), body->Arg(2)), body->Arg(3)}; default: - UNREACHABLE_MSG("Unknown image type {}", image.GetType()); + UNREACHABLE_MSG("Unknown image type {}", view_type); } }(); - const auto has_ms = image.GetType() == AmdGpu::ImageType::Color2DMsaa || - image.GetType() == AmdGpu::ImageType::Color2DMsaaArray; + const auto has_ms = view_type == AmdGpu::ImageType::Color2DMsaa || + view_type == AmdGpu::ImageType::Color2DMsaaArray; ASSERT(!inst_info.has_lod || !has_ms); const auto lod = inst_info.has_lod ? IR::U32{arg} : IR::U32{}; const auto ms = has_ms ? IR::U32{arg} : IR::U32{}; diff --git a/src/shader_recompiler/specialization.h b/src/shader_recompiler/specialization.h index c03621c50..18b1df1f9 100644 --- a/src/shader_recompiler/specialization.h +++ b/src/shader_recompiler/specialization.h @@ -113,7 +113,7 @@ struct StageSpecialization { }); ForEachSharp(binding, images, info->images, [](auto& spec, const auto& desc, AmdGpu::Image sharp) { - spec.type = sharp.GetBoundType(desc.is_array); + spec.type = sharp.GetViewType(desc.is_array); spec.is_integer = AmdGpu::IsInteger(sharp.GetNumberFmt()); spec.is_storage = desc.is_written; if (spec.is_storage) { diff --git a/src/video_core/amdgpu/resource.h b/src/video_core/amdgpu/resource.h index 467cecf10..60fb42ca0 100644 --- a/src/video_core/amdgpu/resource.h +++ b/src/video_core/amdgpu/resource.h @@ -254,9 +254,12 @@ struct Image { return 1; } + bool IsCube() const noexcept { + return static_cast(type) == ImageType::Cube; + } + ImageType GetType() const noexcept { - const auto img_type = static_cast(type); - return img_type == ImageType::Cube ? ImageType::Color2DArray : img_type; + return IsCube() ? ImageType::Color2DArray : static_cast(type); } DataFormat GetDataFmt() const noexcept { @@ -288,8 +291,12 @@ struct Image { GetDataFmt() <= DataFormat::FormatFmask64_8; } - [[nodiscard]] ImageType GetBoundType(const bool is_array) const noexcept { + [[nodiscard]] ImageType GetViewType(const bool is_array) const noexcept { const auto base_type = GetType(); + if (IsCube()) { + // Cube needs to remain array type regardless of instruction array specifier. + return base_type; + } if (base_type == ImageType::Color1DArray && !is_array) { return ImageType::Color1D; } @@ -303,7 +310,7 @@ struct Image { } [[nodiscard]] u32 NumViewLevels(const bool is_array) const noexcept { - switch (GetBoundType(is_array)) { + switch (GetViewType(is_array)) { case ImageType::Color2DMsaa: case ImageType::Color2DMsaaArray: return 1; @@ -313,7 +320,7 @@ struct Image { } [[nodiscard]] u32 NumViewLayers(const bool is_array) const noexcept { - switch (GetBoundType(is_array)) { + switch (GetViewType(is_array)) { case ImageType::Color1D: case ImageType::Color2D: case ImageType::Color2DMsaa: diff --git a/src/video_core/texture_cache/image_view.cpp b/src/video_core/texture_cache/image_view.cpp index 569238168..d90b78c67 100644 --- a/src/video_core/texture_cache/image_view.cpp +++ b/src/video_core/texture_cache/image_view.cpp @@ -45,7 +45,7 @@ ImageViewInfo::ImageViewInfo(const AmdGpu::Image& image, const Shader::ImageReso range.base.layer = image.base_array; range.extent.levels = image.NumViewLevels(desc.is_array); range.extent.layers = image.NumViewLayers(desc.is_array); - type = ConvertImageViewType(image.GetBoundType(desc.is_array)); + type = ConvertImageViewType(image.GetViewType(desc.is_array)); if (!is_storage) { mapping = Vulkan::LiverpoolToVK::ComponentMapping(image.DstSelect()); From 4a21d9487132c92a58db45c5dd8ef02d2849468b Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Fri, 10 Jan 2025 17:58:41 -0300 Subject: [PATCH 391/549] Fix -PKG Viewer -Button install (#2113) https://github.com/shadps4-emu/shadPS4/issues/2112 --- src/qt_gui/pkg_viewer.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/qt_gui/pkg_viewer.cpp b/src/qt_gui/pkg_viewer.cpp index 0ffb9b579..b4dd3afdf 100644 --- a/src/qt_gui/pkg_viewer.cpp +++ b/src/qt_gui/pkg_viewer.cpp @@ -47,6 +47,9 @@ PKGViewer::PKGViewer(std::shared_ptr game_info_get, QWidget* pare connect(treeWidget, &QTreeWidget::customContextMenuRequested, this, [=, this](const QPoint& pos) { + if (treeWidget->selectedItems().isEmpty()) { + return; + } m_gui_context_menus.RequestGameMenuPKGViewer(pos, m_full_pkg_list, treeWidget, InstallDragDropPkg); }); From cfaea1ea6d5d59eac1d376a31c6f75fd8868b9a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quang=20Ng=C3=B4?= Date: Sat, 11 Jan 2025 03:59:19 +0700 Subject: [PATCH 392/549] qt_gui: Fix shortcut's name got cut off in some cases (#2116) Example: P.T. -> P --- src/qt_gui/gui_context_menus.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h index bbc84c4fc..0e8675c0c 100644 --- a/src/qt_gui/gui_context_menus.h +++ b/src/qt_gui/gui_context_menus.h @@ -283,7 +283,7 @@ public: #ifdef Q_OS_WIN if (createShortcutWin(linkPath, ebootPath, icoPath, exePath)) { #else - if (createShortcutLinux(linkPath, ebootPath, iconPath)) { + if (createShortcutLinux(linkPath, m_games[itemID].name, ebootPath, iconPath)) { #endif QMessageBox::information( nullptr, tr("Shortcut creation"), @@ -301,7 +301,7 @@ public: #ifdef Q_OS_WIN if (createShortcutWin(linkPath, ebootPath, iconPath, exePath)) { #else - if (createShortcutLinux(linkPath, ebootPath, iconPath)) { + if (createShortcutLinux(linkPath, m_games[itemID].name, ebootPath, iconPath)) { #endif QMessageBox::information( nullptr, tr("Shortcut creation"), @@ -510,8 +510,8 @@ private: return SUCCEEDED(hres); } #else - bool createShortcutLinux(const QString& linkPath, const QString& targetPath, - const QString& iconPath) { + bool createShortcutLinux(const QString& linkPath, const std::string& name, + const QString& targetPath, const QString& iconPath) { QFile shortcutFile(linkPath); if (!shortcutFile.open(QIODevice::WriteOnly | QIODevice::Text)) { QMessageBox::critical(nullptr, "Error", @@ -522,7 +522,7 @@ private: QTextStream out(&shortcutFile); out << "[Desktop Entry]\n"; out << "Version=1.0\n"; - out << "Name=" << QFileInfo(linkPath).baseName() << "\n"; + out << "Name=" << QString::fromStdString(name) << "\n"; out << "Exec=" << QCoreApplication::applicationFilePath() << " \"" << targetPath << "\"\n"; out << "Icon=" << iconPath << "\n"; out << "Terminal=false\n"; From 6ec68f66a9d3c5c9f3124373fffcf5cfe6a65910 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 10 Jan 2025 15:59:20 -0800 Subject: [PATCH 393/549] hotfix: Check correct template for setting binding divisor. --- src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 2d0b19bbe..4ca3a7f27 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -333,7 +333,7 @@ void GraphicsPipeline::GetVertexInputs(VertexInputs& attributes, ? vk::VertexInputRate::eVertex : vk::VertexInputRate::eInstance, }); - if constexpr (std::is_same_v) { + if constexpr (std::is_same_v) { bindings.back().divisor = 1; } guest_buffers.emplace_back(buffer); From 5c845d4ecc3350302f0cc1899b355a055d444d6e Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 10 Jan 2025 16:30:28 -0800 Subject: [PATCH 394/549] hotfix: Constrain view layers to actual layers. --- src/video_core/amdgpu/resource.h | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/video_core/amdgpu/resource.h b/src/video_core/amdgpu/resource.h index 60fb42ca0..744aacdc5 100644 --- a/src/video_core/amdgpu/resource.h +++ b/src/video_core/amdgpu/resource.h @@ -227,11 +227,13 @@ struct Image { } [[nodiscard]] u32 NumLayers() const noexcept { + // Depth is the number of layers for Array images. u32 slices = depth + 1; - const auto img_type = static_cast(type); - if (img_type == ImageType::Color3D) { + if (GetType() == ImageType::Color3D) { + // Depth is the actual texture depth for 3D images. slices = 1; - } else if (img_type == ImageType::Cube) { + } else if (IsCube()) { + // Depth is the number of full cubes for Cube images. slices *= 6; } if (pow2pad) { @@ -315,7 +317,9 @@ struct Image { case ImageType::Color2DMsaaArray: return 1; default: - return last_level - base_level + 1; + // Constrain to actual number of available levels. + const auto max_level = std::min(last_level + 1, NumLevels()); + return max_level > base_level ? max_level - base_level : 1; } } @@ -327,7 +331,9 @@ struct Image { case ImageType::Color3D: return 1; default: - return last_array - base_array + 1; + // Constrain to actual number of available layers. + const auto max_array = std::min(last_array + 1, NumLayers()); + return max_array > base_array ? max_array - base_array : 1; } } }; From 5ac7e70e4be66385881655d84e5f46c04ec7a626 Mon Sep 17 00:00:00 2001 From: DemoJameson Date: Sun, 12 Jan 2025 00:55:10 +0800 Subject: [PATCH 395/549] Update zh_CN.ts (#2122) --- src/qt_gui/translations/zh_CN.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index e71180729..bb4476b9e 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -558,11 +558,11 @@ Trophy Key - Trophy Key + 奖杯密钥 Trophy - Trophy + 奖杯 Logger @@ -782,7 +782,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. + 奖杯密钥:\n用于解密奖杯的密钥。必须从您的越狱主机中获得。\n仅包含十六进制字符。 logTypeGroupBox @@ -1337,4 +1337,4 @@ TB - \ No newline at end of file + From 62bbad62fc9bf23c176b8dd69e16cfef47c5e1f8 Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Sat, 11 Jan 2025 10:58:07 -0600 Subject: [PATCH 396/549] Implement sceNpCmp functions (#2114) --- CMakeLists.txt | 4 +- src/common/logging/filter.cpp | 1 + src/common/logging/types.h | 1 + src/core/libraries/libs.cpp | 2 + src/core/libraries/np_common/np_common.cpp | 7915 +++++++++++++++++ src/core/libraries/np_common/np_common.h | 1245 +++ .../libraries/np_common/np_common_error.h | 9 + src/core/libraries/np_manager/np_manager.cpp | 1 - 8 files changed, 9176 insertions(+), 2 deletions(-) create mode 100644 src/core/libraries/np_common/np_common.cpp create mode 100644 src/core/libraries/np_common/np_common.h create mode 100644 src/core/libraries/np_common/np_common_error.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ec04dad5..aee01a3a5 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -415,7 +415,9 @@ set(VDEC_LIB src/core/libraries/videodec/videodec2_impl.cpp src/core/libraries/videodec/videodec_impl.h ) -set(NP_LIBS src/core/libraries/np_manager/np_manager.cpp +set(NP_LIBS src/core/libraries/np_common/np_common.cpp + src/core/libraries/np_common/np_common.h + src/core/libraries/np_manager/np_manager.cpp src/core/libraries/np_manager/np_manager.h src/core/libraries/np_score/np_score.cpp src/core/libraries/np_score/np_score.h diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index 5ee2dce73..b15fb07be 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -98,6 +98,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Lib, Ssl) \ SUB(Lib, SysModule) \ SUB(Lib, Move) \ + SUB(Lib, NpCommon) \ SUB(Lib, NpManager) \ SUB(Lib, NpScore) \ SUB(Lib, NpTrophy) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index 6829e2d1b..da4cf65e7 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -65,6 +65,7 @@ enum class Class : u8 { Lib_Ssl, ///< The LibSceSsl implementation. Lib_Http, ///< The LibSceHttp implementation. Lib_SysModule, ///< The LibSceSysModule implementation + Lib_NpCommon, ///< The LibSceNpCommon implementation Lib_NpManager, ///< The LibSceNpManager implementation Lib_NpScore, ///< The LibSceNpScore implementation Lib_NpTrophy, ///< The LibSceNpTrophy implementation diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index 69728e523..d03edf28e 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -24,6 +24,7 @@ #include "core/libraries/network/net.h" #include "core/libraries/network/netctl.h" #include "core/libraries/network/ssl.h" +#include "core/libraries/np_common/np_common.h" #include "core/libraries/np_manager/np_manager.h" #include "core/libraries/np_score/np_score.h" #include "core/libraries/np_trophy/np_trophy.h" @@ -72,6 +73,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::SysModule::RegisterlibSceSysmodule(sym); Libraries::Posix::Registerlibsceposix(sym); Libraries::AudioIn::RegisterlibSceAudioIn(sym); + Libraries::NpCommon::RegisterlibSceNpCommon(sym); Libraries::NpManager::RegisterlibSceNpManager(sym); Libraries::NpScore::RegisterlibSceNpScore(sym); Libraries::NpTrophy::RegisterlibSceNpTrophy(sym); diff --git a/src/core/libraries/np_common/np_common.cpp b/src/core/libraries/np_common/np_common.cpp new file mode 100644 index 000000000..1234705cc --- /dev/null +++ b/src/core/libraries/np_common/np_common.cpp @@ -0,0 +1,7915 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" +#include "core/libraries/np_common/np_common.h" +#include "core/libraries/np_common/np_common_error.h" + +namespace Libraries::NpCommon { + +int PS4_SYSV_ABI sceNpCmpNpId(OrbisNpId* np_id1, OrbisNpId* np_id2) { + if (np_id1 == nullptr || np_id2 == nullptr) { + return ORBIS_NP_ERROR_INVALID_ARGUMENT; + } + + // Compare data + if (std::strncmp(np_id1->handle.data, np_id2->handle.data, ORBIS_NP_ONLINEID_MAX_LENGTH) != 0) { + return ORBIS_NP_UTIL_ERROR_NOT_MATCH; + } + + // Compare opt + for (u32 i = 0; i < 8; i++) { + if (np_id1->opt[i] != np_id2->opt[i]) { + return ORBIS_NP_UTIL_ERROR_NOT_MATCH; + } + } + + // Compare reserved + for (u32 i = 0; i < 8; i++) { + if (np_id1->reserved[i] != np_id2->reserved[i]) { + return ORBIS_NP_UTIL_ERROR_NOT_MATCH; + } + } + + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpCmpNpIdInOrder(OrbisNpId* np_id1, OrbisNpId* np_id2, u32* out_result) { + if (np_id1 == nullptr || np_id2 == nullptr || out_result == nullptr) { + return ORBIS_NP_ERROR_INVALID_ARGUMENT; + } + + // Compare data + u32 compare = + std::strncmp(np_id1->handle.data, np_id2->handle.data, ORBIS_NP_ONLINEID_MAX_LENGTH); + if (compare < 0) { + *out_result = -1; + return ORBIS_OK; + } else if (compare > 0) { + *out_result = 1; + return ORBIS_OK; + } + + // Compare opt + for (u32 i = 0; i < 8; i++) { + if (np_id1->opt[i] < np_id2->opt[i]) { + *out_result = -1; + return ORBIS_OK; + } else if (np_id1->opt[i] > np_id2->opt[i]) { + *out_result = 1; + return ORBIS_OK; + } + } + + // Compare reserved + for (u32 i = 0; i < 8; i++) { + if (np_id1->reserved[i] < np_id2->reserved[i]) { + *out_result = -1; + return ORBIS_OK; + } else if (np_id1->reserved[i] > np_id2->reserved[i]) { + *out_result = 1; + return ORBIS_OK; + } + } + + *out_result = 0; + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpCmpOnlineId(OrbisNpOnlineId* online_id1, OrbisNpOnlineId* online_id2) { + if (online_id1 == nullptr || online_id2 == nullptr) { + return ORBIS_NP_ERROR_INVALID_ARGUMENT; + } + + if (std::strncmp(online_id1->data, online_id2->data, ORBIS_NP_ONLINEID_MAX_LENGTH) != 0) { + return ORBIS_NP_UTIL_ERROR_NOT_MATCH; + } + return ORBIS_OK; +} + +int PS4_SYSV_ABI _sceNpAllocatorExConvertAllocator() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _sceNpAllocatorExFree() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _sceNpAllocatorExMalloc() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _sceNpAllocatorExRealloc() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _sceNpAllocatorExStrdup() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _sceNpAllocatorExStrndup() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _sceNpAllocatorFree() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _sceNpAllocatorMalloc() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _sceNpAllocatorRealloc() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _sceNpAllocatorStrdup() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _sceNpAllocatorStrndup() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _sceNpFree() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _sceNpHeapFree() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _sceNpHeapMalloc() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _sceNpHeapRealloc() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _sceNpHeapStrdup() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _sceNpHeapStrndup() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _sceNpMalloc() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _sceNpRealloc() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10Cancelable10IsCanceledEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10Cancelable10LockCancelEPKciS3_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10Cancelable11CheckCancelEPKciS3_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10Cancelable12UnlockCancelEPKciS3_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10Cancelable13SetCancelableEb() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10Cancelable14SetupSubCancelEPS1_PKciS4_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10Cancelable16CleanupSubCancelEPS1_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10Cancelable4InitEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10Cancelable6CancelEij() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10Cancelable7DestroyEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10CancelableC2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10CancelableD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10CancelableD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10CancelableD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10CancelLock3EndEPKciS3_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10CancelLock5BeginEPNS0_6HandleEPKciS5_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10CancelLockC1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10CancelLockC2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10CancelLockD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10CancelLockD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10EventQueue10ClearAbortEt() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10EventQueue10TryDequeueEPvm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10EventQueue4ctorEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10EventQueue4dtorEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10EventQueue4InitEPKcmm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10EventQueue5AbortEt() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10EventQueue7DequeueEPvmj() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10EventQueue7DestroyEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10EventQueue7EnqueueEPKvmj() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10EventQueueC2EP16SceNpAllocatorEx() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10EventQueueD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10EventQueueD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10EventQueueD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10JsonNumber5ClearEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10JsonNumber6SetNumEi() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10JsonNumber6SetNumEj() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10JsonNumber6SetNumEl() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10JsonNumber6SetNumEm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10JsonNumber6SetNumEPKc() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10JsonObject16DeleteFieldValueEPKc() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10JsonObject5ClearEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10JsonParser4InitEPK7JsonDefPNS1_12EventHandlerE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10JsonParser5ParseEPKcm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10JsonParserC2EP16SceNpAllocatorEx() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10JsonParserD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10JsonParserD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10JsonParserD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10JsonString5ClearEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10JsonString6SetStrEPKc() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10MemoryFile4ReadEPNS0_6HandleEPvmPm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10MemoryFile4SyncEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10MemoryFile5CloseEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10MemoryFile5WriteEPNS0_6HandleEPKvmPm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10MemoryFile8TruncateEl() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10MemoryFileC2EP16SceNpAllocatorEx() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10MemoryFileD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10MemoryFileD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np10MemoryFileD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI +_ZN3sce2np12HttpTemplate19SetAuthInfoCallbackEPFii15SceHttpAuthTypePKcPcS5_iPPhPmPiPvESA_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np12HttpTemplate4InitEiPKcib() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np12HttpTemplate7DestroyEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np12HttpTemplateC1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np12HttpTemplateC2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np12HttpTemplateD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np12HttpTemplateD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np12HttpTemplateD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np12StreamBufferixEi() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np12StreamReader4ReadEPNS0_6HandleEPNS0_9StreamCtxEPvmPm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np12StreamReader7ReadAllEPNS0_6HandleEPNS0_9StreamCtxEPvmPm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np12StreamReader7ReadAllEPNS0_6HandleEPvmPm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np12StreamReader8ReadDataEPNS0_6HandleEPNS0_9StreamCtxEPvmPm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np12StreamReader8ReadDataEPNS0_6HandleEPvmPm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np12StreamReader8SkipDataEPNS0_6HandleElPl() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np12StreamReader8SkipDataEPNS0_6HandleEPNS0_9StreamCtxElPl() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np12StreamWriter15WriteFilledDataEPNS0_6HandleEcl() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np12StreamWriter15WriteFilledDataEPNS0_6HandleEPNS0_9StreamCtxEcl() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np12StreamWriter5WriteEPNS0_6HandleEPNS0_9StreamCtxEPKvmPm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np12StreamWriter9WriteDataEPNS0_6HandleEPKvmPm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np12StreamWriter9WriteDataEPNS0_6HandleEPNS0_9StreamCtxEPKvmPm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np12WorkerThread10ThreadMainEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np12WorkerThreadC1EPNS0_9WorkQueueE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np12WorkerThreadC2EPNS0_9WorkQueueE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np12WorkerThreadD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np12WorkerThreadD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np12WorkerThreadD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np13JsonDocParser5ParseEPKcm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np13JsonDocParser9GetResultEPPNS0_10JsonObjectE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np13JsonDocParser9GetResultEPPNS0_9JsonValueE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np13JsonDocParserC2EP16SceNpAllocatorExPK7JsonDef() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np13JsonDocParserD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np13JsonDocParserD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np13JsonDocParserD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np13NpTitleSecret5ClearEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np13NpTitleSecretC1EPKvm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np13NpTitleSecretC1ERK16SceNpTitleSecret() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np13NpTitleSecretC1ERKS1_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np13NpTitleSecretC1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np13NpTitleSecretC2EPKvm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np13NpTitleSecretC2ERK16SceNpTitleSecret() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np13NpTitleSecretC2ERKS1_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np13NpTitleSecretC2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np13NpTitleSecretD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np13NpTitleSecretD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np13NpTitleSecretD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np13RingBufMemory4ctorEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np13RingBufMemory4dtorEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np13RingBufMemory4InitEm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np13RingBufMemory6ExpandEm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np13RingBufMemory6IsInitEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np13RingBufMemory7DestroyEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np13RingBufMemoryC2EP16SceNpAllocatorEx() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np13RingBufMemoryD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np13RingBufMemoryD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np13RingBufMemoryD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np14CalloutContext4InitEPKcimm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np14CalloutContext4InitEPKNS1_5ParamE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np14CalloutContext7DestroyEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np14CalloutContextC1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np14CalloutContextC2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np14CalloutContextD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np14CalloutContextD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np14CalloutContextD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np14JsonDocBuilder12BuildBufSizeEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np14JsonDocBuilder16EscapeJsonStringEPKcPcmPm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np14JsonDocBuilder23EscapeJsonStringBufSizeEPKc() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np14JsonDocBuilder5BuildEPcmPm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np14JsonDocBuilderC1ERKNS0_9JsonValueE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np14JsonDocBuilderC2ERKNS0_9JsonValueE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np14JsonDocBuilderD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np14JsonDocBuilderD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np14JsonDocBuilderD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np15CancelableScope3EndEiPKciS3_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np15CancelableScope5BeginEPNS0_6HandleEPKciS5_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np15CancelableScopeC2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np15CancelableScopeD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np15CancelableScopeD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np15CancelableScopeD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np16StreamReadBufferC2EP16SceNpAllocatorEx() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np16StreamReadBufferD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np16StreamReadBufferD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np18HttpConnectionPool13InvalidateAllEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np18HttpConnectionPool4InitEi() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np18HttpConnectionPool7DestroyEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np18HttpConnectionPoolC1EP16SceNpAllocatorEx() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np18HttpConnectionPoolC2EP16SceNpAllocatorEx() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np18HttpConnectionPoolD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np18HttpConnectionPoolD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np18HttpConnectionPoolD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np18MemoryStreamReader4ReadEPNS0_6HandleEPvmPm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np18MemoryStreamReaderC1EPKvm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np18MemoryStreamReaderC2EPKvm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np18MemoryStreamReaderD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np18MemoryStreamReaderD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np18MemoryStreamReaderD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np18MemoryStreamWriter5WriteEPNS0_6HandleEPKvmPm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np18MemoryStreamWriterC1EPvm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np18MemoryStreamWriterC2EPvm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np18MemoryStreamWriterD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np18MemoryStreamWriterD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np18MemoryStreamWriterD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np20BufferedStreamReader4ReadEPNS0_6HandleEPvmPm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np20BufferedStreamReader5CloseEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np20BufferedStreamReaderC2EP16SceNpAllocatorEx() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np20BufferedStreamReaderD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np20BufferedStreamReaderD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np20BufferedStreamReaderD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc10IpmiClient10DisconnectEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc10IpmiClient11IsConnectedEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc10IpmiClient16invokeSyncMethodEjPKvmPvPmm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc10IpmiClient4ctorEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc10IpmiClient4dtorEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc10IpmiClient4InitEPKNS2_6ConfigE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc10IpmiClient7ConnectEPKvm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc10IpmiClient7DestroyEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc10IpmiClientC1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc10IpmiClientC2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc10IpmiClientD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc10IpmiClientD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc10IpmiClientD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI +_ZN3sce2np3ipc13ServiceClientC1EPNS1_17ServiceIpmiClientEPKNS1_17ServiceClientInfoE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI +_ZN3sce2np3ipc13ServiceClientC2EPNS1_17ServiceIpmiClientEPKNS1_17ServiceClientInfoE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient10DisconnectEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient10EndRequestEii() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient11findServiceEi() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient11InitServiceEi() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient11TermServiceEi() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient11WaitRequestEiij() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient12AbortRequestEii() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient12BeginRequestEii() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient13CreateRequestEPiiPKvm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient13DeleteRequestEii() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient13PollEventFlagEijmjPm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient13WaitEventFlagEijmjPmj() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient14PollEventQueueEiPvm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient15CancelEventFlagEijm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient15RegisterServiceEPKNS1_17ServiceClientInfoE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient16RegisterServicesEPKNS1_17ServiceClientInfoE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient17invokeInitServiceEi() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient17invokeTermServiceEi() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient17UnregisterServiceEi() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient18EndRequestForAsyncEii() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient19WaitRequestForAsyncEiij() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient20AbortRequestForAsyncEii() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI +_ZN3sce2np3ipc17ServiceIpmiClient20BeginRequestForAsyncEiiPN4IPMI6Client12EventNotifeeE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient21CreateRequestForAsyncEPiiPKvm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient21DeleteRequestForAsyncEii() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient4ctorEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient4dtorEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient4InitEPNS2_6ConfigE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient7ConnectEPKvm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient7DestroyEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClientC1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClientC2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClientD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClientD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClientD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np4Cond4ctorEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np4Cond4dtorEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np4Cond4InitEPKcPNS0_5MutexE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np4Cond4WaitEj() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np4Cond6SignalEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np4Cond7DestroyEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np4Cond9SignalAllEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np4CondC1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np4CondC2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np4CondD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np4CondD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np4CondD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np4Path11BuildAppendEPcmcPKcm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np4Path12AddDelimiterEPcmc() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np4Path5ClearEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np4Path6SetStrEPKcm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np4PathD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np4PathD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np4PathD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np4Time10AddMinutesEl() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np4Time10AddSecondsEl() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np4Time12GetUserClockEPS1_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np4Time15AddMicroSecondsEl() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np4Time15GetNetworkClockEPS1_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np4Time20GetDebugNetworkClockEPS1_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np4Time7AddDaysEl() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np4Time8AddHoursEl() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np4TimeplERK10SceRtcTick() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np4TimeplERKS1_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np5Mutex4ctorEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np5Mutex4dtorEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np5Mutex4InitEPKcj() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np5Mutex4LockEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np5Mutex6UnlockEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np5Mutex7DestroyEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np5Mutex7TryLockEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np5MutexC1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np5MutexC2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np5MutexD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np5MutexD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np5MutexD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np5NpEnv8GetNpEnvEPS1_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np6Handle10CancelImplEi() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np6Handle4InitEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np6Handle7DestroyEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np6HandleC1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np6HandleC2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np6HandleD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np6HandleD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np6HandleD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np6ObjectdaEPv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np6ObjectdaEPvR14SceNpAllocator() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np6ObjectdaEPvR16SceNpAllocatorEx() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np6ObjectdlEPv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np6ObjectdlEPvR14SceNpAllocator() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np6ObjectdlEPvR16SceNpAllocatorEx() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np6ObjectnaEmR14SceNpAllocator() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np6ObjectnaEmR16SceNpAllocatorEx() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np6ObjectnwEmR14SceNpAllocator() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np6ObjectnwEmR16SceNpAllocatorEx() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np6Thread12DoThreadMainEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np6Thread4ctorEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np6Thread4dtorEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np6Thread4InitEPKcimm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np6Thread4InitEPKNS1_5ParamE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np6Thread4JoinEPi() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np6Thread5StartEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np6Thread7DestroyEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np6Thread9EntryFuncEPv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np6Thread9GetResultEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np6Thread9IsRunningEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np6ThreadC2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np6ThreadD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np6ThreadD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np6ThreadD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7Callout10IsTimedoutEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7Callout11CalloutFuncEPv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7Callout4StopEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7Callout5StartEjPNS1_7HandlerE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7Callout5StartEmPNS1_7HandlerE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7Callout9IsStartedEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7CalloutC1EPNS0_14CalloutContextE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7CalloutC2EPNS0_14CalloutContextE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7CalloutD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7CalloutD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7CalloutD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7HttpUri5BuildEPKS1_PcmPmj() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7HttpUri5ParseEPS1_PKc() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7HttpUriC1EP16SceNpAllocatorEx() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7HttpUriC2EP16SceNpAllocatorEx() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7HttpUriD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7HttpUriD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7HttpUriD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7RingBuf14CheckinForReadEm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7RingBuf15CheckinForWriteEm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7RingBuf15CheckoutForReadEPm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7RingBuf16CheckoutForWriteEPm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7RingBuf4ctorEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7RingBuf4dtorEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7RingBuf4InitEPvm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7RingBuf4PeekEmPvm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7RingBuf4ReadEPvm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7RingBuf5ClearEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7RingBuf5WriteEPKvm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7RingBuf7DestroyEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7RingBufC1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7RingBufC2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7RingBufD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7RingBufD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np7RingBufD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8HttpFile4ReadEPNS0_6HandleEPvmPm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8HttpFile5CloseEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8HttpFileC2EP16SceNpAllocatorEx() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8HttpFileD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8HttpFileD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8HttpFileD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8JsonBool5ClearEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8JsonBool7SetBoolEb() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8JsonFile5CloseEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8JsonFileD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8JsonFileD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8JsonFileD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8JsonNull5ClearEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8NpCommId5BuildERKS1_Pcm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8NpCommId5ClearEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8NpCommId5ParseEPS1_PKc() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8NpCommId5ParseEPS1_PKcm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8NpCommIdC1ERK20SceNpCommunicationId() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8NpCommIdC1ERKS1_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8NpCommIdC1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8NpCommIdC2ERK20SceNpCommunicationId() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8NpCommIdC2ERKS1_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8NpCommIdC2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8NpCommIdD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8NpCommIdD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8NpCommIdD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8Selector4InitEPKc() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8SelectorD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8SelectorD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8SelectorD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8WorkItem10SetPendingEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8WorkItem10SetRunningEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8WorkItem11SetFinishedEi() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8WorkItem14FinishCallbackEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8WorkItem15RemoveFromQueueEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8WorkItem6CancelEi() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8WorkItem9BindQueueEPNS0_9WorkQueueEi() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8WorkItemC2EPKc() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8WorkItemD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8WorkItemD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np8WorkItemD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9EventFlag3SetEm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9EventFlag4ctorEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9EventFlag4dtorEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9EventFlag4OpenEPKc() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9EventFlag4PollEmjPm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9EventFlag4WaitEmjPmj() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9EventFlag5ClearEm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9EventFlag6CancelEm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9EventFlag6CreateEPKcj() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9EventFlag7DestroyEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9EventFlagC1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9EventFlagC2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9EventFlagD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9EventFlagD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9EventFlagD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9HttpTrans10SetTimeoutEPKNS1_12TimeoutParamE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9HttpTrans11SendRequestEPNS0_6HandleEPKvm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9HttpTrans12RecvResponseEPNS0_6HandleEPvmPm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9HttpTrans12SkipResponseEPNS0_6HandleE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9HttpTrans16AddRequestHeaderEPKcS3_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9HttpTrans16SetRequestHeaderEPKcS3_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9HttpTrans21GetResponseStatusCodeEPNS0_6HandleEPi() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9HttpTrans21SetRequestContentTypeEPKc() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9HttpTrans23SetRequestContentLengthEm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9HttpTrans24GetResponseContentLengthEPNS0_6HandleEPbPm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9HttpTrans4InitERKNS0_12HttpTemplateEPNS0_18HttpConnectionPoolEiPKcm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI +_ZN3sce2np9HttpTrans4InitERKNS0_12HttpTemplateEPNS0_18HttpConnectionPoolEiPKcS8_tS8_m() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9HttpTrans4ReadEPNS0_6HandleEPvmPm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9HttpTrans5WriteEPNS0_6HandleEPKvmPm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9HttpTrans7DestroyEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9HttpTransC1EP16SceNpAllocatorEx() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9HttpTransC2EP16SceNpAllocatorEx() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9HttpTransD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9HttpTransD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9HttpTransD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9JsonArray12AddItemArrayEPPS1_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9JsonArray5ClearEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9JsonValue12GetItemValueEi() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9JsonValue13GetFieldValueEiPPKc() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9JsonValue13GetFieldValueEPKc() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9JsonValueD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9JsonValueD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9JsonValueD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9LocalFile4ReadEPNS0_6HandleEPvmPm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9LocalFile4SeekEliPl() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9LocalFile4SyncEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9LocalFile5CloseEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9LocalFile5WriteEPNS0_6HandleEPKvmPm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9LocalFile6RemoveEPKc() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9LocalFile8TruncateEl() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9LocalFileC1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9LocalFileC2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9LocalFileD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9LocalFileD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9LocalFileD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9NpTitleId5BuildERKS1_Pcm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9NpTitleId5ClearEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9NpTitleId5ParseEPS1_PKc() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9NpTitleId5ParseEPS1_PKcm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9NpTitleIdC1ERK12SceNpTitleId() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9NpTitleIdC1ERKS1_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9NpTitleIdC1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9NpTitleIdC2ERK12SceNpTitleId() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9NpTitleIdC2ERKS1_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9NpTitleIdC2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9NpTitleIdD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9NpTitleIdD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9NpTitleIdD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9RefObject6AddRefEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9RefObject7ReleaseEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9RefObjectC1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9RefObjectC2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9RefObjectD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9RefObjectD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9RefObjectD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9Semaphore4OpenEPKc() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9Semaphore4WaitEj() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9Semaphore6CreateEiiPKc() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9Semaphore6SignalEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9Semaphore7DestroyEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9SemaphoreC1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9SemaphoreC2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9SemaphoreD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9SemaphoreD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9SemaphoreD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue11GetItemByIdEi() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue15GetFinishedItemENS0_14WorkItemStatusE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue16WorkItemFinishedEPNS0_8WorkItemEi() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue17ProcFinishedItemsENS0_14WorkItemStatusE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue18RemoveFinishedItemEPNS0_8WorkItemE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue18WaitForPendingItemEPPNS0_8WorkItemEPb() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue4ctorEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue4dtorEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue4InitEPKcimm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue4InitEPKNS0_6Thread5ParamE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue4StopEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue5StartEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue6CancelEii() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue6IsInitEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue7DestroyEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue7EnqueueEiPNS0_8WorkItemE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue9CancelAllEi() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue9IsRunningEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9WorkQueueC1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9WorkQueueC2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9WorkQueueD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9WorkQueueD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2np9WorkQueueD2Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npeqERK10SceRtcTickRKNS0_4TimeE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npeqERK12SceNpTitleIdRKNS0_9NpTitleIdE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npeqERK16SceNpTitleSecretRKNS0_13NpTitleSecretE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npeqERK20SceNpCommunicationIdRKNS0_8NpCommIdE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npeqERKNS0_13NpTitleSecretERK16SceNpTitleSecret() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npeqERKNS0_13NpTitleSecretES3_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npeqERKNS0_4TimeERK10SceRtcTick() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npeqERKNS0_4TimeES3_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npeqERKNS0_8NpCommIdERK20SceNpCommunicationId() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npeqERKNS0_8NpCommIdES3_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npeqERKNS0_9NpTitleIdERK12SceNpTitleId() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npeqERKNS0_9NpTitleIdES3_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npgeERK10SceRtcTickRKNS0_4TimeE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npgeERKNS0_4TimeERK10SceRtcTick() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npgeERKNS0_4TimeES3_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npgtERK10SceRtcTickRKNS0_4TimeE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npgtERKNS0_4TimeERK10SceRtcTick() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npgtERKNS0_4TimeES3_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npleERK10SceRtcTickRKNS0_4TimeE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npleERKNS0_4TimeERK10SceRtcTick() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npleERKNS0_4TimeES3_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npltERK10SceRtcTickRKNS0_4TimeE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npltERKNS0_4TimeERK10SceRtcTick() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npltERKNS0_4TimeES3_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npneERK10SceRtcTickRKNS0_4TimeE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npneERK12SceNpTitleIdRKNS0_9NpTitleIdE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npneERK16SceNpTitleSecretRKNS0_13NpTitleSecretE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npneERK20SceNpCommunicationIdRKNS0_8NpCommIdE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npneERKNS0_13NpTitleSecretERK16SceNpTitleSecret() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npneERKNS0_13NpTitleSecretES3_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npneERKNS0_4TimeERK10SceRtcTick() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npneERKNS0_4TimeES3_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npneERKNS0_8NpCommIdERK20SceNpCommunicationId() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npneERKNS0_8NpCommIdES3_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npneERKNS0_9NpTitleIdERK12SceNpTitleId() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZN3sce2npneERKNS0_9NpTitleIdES3_() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np10Cancelable6IsInitEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np10EventQueue6IsInitEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np10EventQueue7IsEmptyEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np10JsonNumber5CloneEP16SceNpAllocatorEx() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np10JsonNumber6GetNumEPcm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np10JsonNumber6GetNumEPi() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np10JsonNumber6GetNumEPj() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np10JsonNumber6GetNumEPl() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np10JsonNumber6GetNumEPm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np10JsonNumber9GetNumStrEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np10JsonObject5CloneEP16SceNpAllocatorEx() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np10JsonString5CloneEP16SceNpAllocatorEx() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np10JsonString6GetStrEPcm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np10JsonString6GetStrEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np10JsonString9GetLengthEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np12HttpTemplate6IsInitEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np18HttpConnectionPool6IsInitEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np3ipc10IpmiClient6IsInitEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np3ipc17ServiceIpmiClient6IsInitEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np4Cond6IsInitEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np4Time18ConvertToPosixTimeEPl() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np5Mutex6IsInitEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np6Handle6IsInitEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np6Thread6IsInitEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np7RingBuf11GetDataSizeEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np7RingBuf11GetFreeSizeEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np7RingBuf6IsFullEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np7RingBuf7IsEmptyEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np8JsonBool5CloneEP16SceNpAllocatorEx() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np8JsonBool7GetBoolEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np8JsonNull5CloneEP16SceNpAllocatorEx() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np8NpCommId7IsEmptyEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np9EventFlag6IsInitEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np9HttpTrans6IsInitEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np9JsonArray5CloneEP16SceNpAllocatorEx() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np9JsonValue12GetItemValueEi() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np9NpTitleId7IsEmptyEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZNK3sce2np9Semaphore6IsInitEv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZThn16_N3sce2np10MemoryFile5WriteEPNS0_6HandleEPKvmPm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZThn16_N3sce2np10MemoryFileD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZThn16_N3sce2np10MemoryFileD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZThn16_N3sce2np9HttpTrans5WriteEPNS0_6HandleEPKvmPm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZThn16_N3sce2np9HttpTransD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZThn16_N3sce2np9HttpTransD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZThn16_N3sce2np9LocalFile5WriteEPNS0_6HandleEPKvmPm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZThn16_N3sce2np9LocalFileD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZThn16_N3sce2np9LocalFileD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZThn8_N3sce2np10MemoryFile4ReadEPNS0_6HandleEPvmPm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZThn8_N3sce2np10MemoryFileD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZThn8_N3sce2np10MemoryFileD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZThn8_N3sce2np6Handle10CancelImplEi() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZThn8_N3sce2np6HandleD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZThn8_N3sce2np6HandleD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZThn8_N3sce2np9HttpTrans4ReadEPNS0_6HandleEPvmPm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZThn8_N3sce2np9HttpTransD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZThn8_N3sce2np9HttpTransD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZThn8_N3sce2np9LocalFile4ReadEPNS0_6HandleEPvmPm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZThn8_N3sce2np9LocalFileD0Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZThn8_N3sce2np9LocalFileD1Ev() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZTVN3sce2np10JsonNumberE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZTVN3sce2np10JsonObjectE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZTVN3sce2np10JsonStringE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZTVN3sce2np8JsonBoolE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZTVN3sce2np8JsonNullE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZTVN3sce2np8SelectorE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZTVN3sce2np9JsonArrayE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI _ZTVN3sce2np9JsonValueE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpAllocateKernelMemoryNoAlignment() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpAllocateKernelMemoryWithAlignment() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpArchInit() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpArchTerm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpAtomicCas32() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpAtomicDec32() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpAtomicInc32() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpBase64Decoder() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpBase64Encoder() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpBase64GetDecodeSize() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpBase64UrlDecoder() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpBase64UrlEncoder() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpBase64UrlGetDecodeSize() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpCalloutInitCtx() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpCalloutStartOnCtx() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpCalloutStartOnCtx64() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpCalloutStopOnCtx() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpCalloutTermCtx() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpCancelEventFlag() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpClearEventFlag() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpCloseEventFlag() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpCloseSema() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpCondDestroy() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpCondInit() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpCondSignal() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpCondSignalAll() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpCondSignalTo() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpCondTimedwait() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpCondWait() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpCreateEventFlag() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpCreateSema() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpCreateThread() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpDbgAssignDebugId() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpDbgDumpBinary() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpDbgDumpText() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpDeleteEventFlag() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpDeleteSema() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpEventGetCurrentNetworkTick() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpFreeKernelMemory() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpGetNavSdkVersion() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpGetPlatformType() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpGetProcessId() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpGetRandom() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpGetSdkVersion() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpGetSdkVersionUInt() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpGetSystemClockUsec() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpGlobalHeapGetAllocator() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpGlobalHeapGetAllocatorEx() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpGlobalHeapGetAllocatorExPtr() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpGlobalHeapGetAllocatorPtr() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpHeapDestroy() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpHeapGetAllocator() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpHeapGetStat() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpHeapInit() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpHeapShowStat() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpHexToInt() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpInt32ToStr() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpInt64ToStr() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpIntGetPlatformType() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpIntIsOnlineIdString() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpIntIsValidOnlineId() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpIntSetPlatformType() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpIntToHex() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpIpc2ClientInit() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpIpc2ClientTerm() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpJoinThread() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpJsonParse() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpJsonParseBuf() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpJsonParseBufInit() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpJsonParseEx() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpJsonParseExInit() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpJsonParseInit() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpLwCondDestroy() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpLwCondInit() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpLwCondSignal() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpLwCondSignalAll() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpLwCondSignalTo() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpLwCondWait() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpLwMutexDestroy() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpLwMutexInit() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpLwMutexLock() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpLwMutexTryLock() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpLwMutexUnlock() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpMemoryHeapDestroy() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpMemoryHeapGetAllocator() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpMemoryHeapGetAllocatorEx() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpMemoryHeapInit() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpMutexDestroy() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpMutexInit() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpMutexLock() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpMutexTryLock() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpMutexUnlock() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpOpenEventFlag() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpOpenSema() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpPanic() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpPollEventFlag() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpPollSema() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpRtcConvertToPosixTime() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpRtcFormatRFC3339() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpRtcParseRFC3339() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpServerErrorJsonGetErrorCode() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpServerErrorJsonMultiGetErrorCode() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpServerErrorJsonParse() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpServerErrorJsonParseInit() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpServerErrorJsonParseMultiInit() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpSetEventFlag() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpSetPlatformType() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpSignalSema() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpStrBuildHex() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpStrcpyToBuf() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpStrncpyToBuf() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpStrnParseHex() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpStrParseHex() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpStrToInt32() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpStrToInt64() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpStrToUInt32() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpStrToUInt64() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpThreadGetId() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUInt32ToStr() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUInt64ToStr() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUserGetUserIdList() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilBuildTitleId() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilCanonicalizeNpIdForPs4() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilCanonicalizeNpIdForPsp2() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilCmpAccountId() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilGetDateSetAuto() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilGetDbgCommerce() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilGetEnv() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilGetFakeDisplayNameMode() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilGetFakeRateLimit() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilGetIgnoreNpTitleId() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilGetNpDebug() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilGetNpLanguageCode() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilGetNpLanguageCode2() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilGetNpLanguageCode2Str() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilGetNpLanguageCodeStr() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilGetNpTestPatch() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilGetNthChar() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilGetShareTitleCheck() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilGetSystemLanguage() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilGetTrcNotify() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilGetWebApi2FakeRateLimit() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilGetWebApi2FakeRateLimitTarget() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilGetWebTraceSetting() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilHttpUrlEncode() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilJidToNpId() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilJsonEscape() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilJsonGetOneChar() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilJsonUnescape() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilNpIdToJid() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilNumChars() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilParseJid() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilParseTitleId() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilSerializeJid() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilXmlEscape() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilXmlGetOneChar() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpUtilXmlUnescape() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpWaitEventFlag() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpWaitSema() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpXmlParse() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpXmlParseInit() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_00FD578C2DD966DF() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_0131A2EA80689F4C() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_01443C54863BDD20() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_01BC55BDC5C0ADAD() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_01D1ECF5750F40E8() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_020A479A74F5FBAC() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_024AF5E1D9472AB5() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_027C5D488713A6B3() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_02FE9D94C6858355() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_041F34F1C70D15C1() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_0530B1D276114248() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_065DAA14E9C73AD9() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_06AFF4E5D042BC3E() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_06EE369299F73997() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_07C92D9F8D76B617() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_07E9117498F1E4BF() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_08F3E0AF3664F275() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_0A9937C01EF21375() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_0ACBE6ACCBA3876D() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_0AE07D3354510CE6() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_0AEC3C342AE67B7C() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_0B318420C11E7C23() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_0BB6C37B03F35D89() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_0BBE8A9ACDD90FDF() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_0C7B62905E224E9C() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_0D35913117241AF9() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_0D5EE95CEED879A7() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_0D6FB24B27AB1DA2() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_0DE8032D534AC41C() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_0DF4CCA9DCA9E742() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_0E7449B1D3D98C01() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_0E77094B7750CB37() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_0ECAB397B6D50603() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_0F1DE1D1EADA2948() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_0F8AFEFA1D26BF1A() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_11881710562A6BAD() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_11AFD88BBD0C70DB() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_11E704A30A4B8877() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_125014842452F94B() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_126F0071E11CAC46() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_12926DCF35994B01() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_12CC7ABFBF31618F() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_13C4E51F44592AA2() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_15330E7C56338254() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_1566B358CABF2612() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_1625818F268F45EF() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_16D32B40D28A9AC2() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_183F4483BDBD25CD() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_1887E9E95AF62F3D() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_18A3CE95FD893D3A() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_18B3665E4854E7E9() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_1923B003948AF47E() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_19B533DA4C59A532() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_1BB399772DB68E08() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_1C0AC612D3A2971B() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_1C5599B779990A43() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_1CCBB296B04317BE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_1CD045542FB93002() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_1DECECA673AB77B7() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_1E03E024E26C1A7F() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_1F101732BB0D7E21() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_1F4D153EC3DD47BB() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_1F7C47F63FAF0CBE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_1FBE2EE68C0F31B6() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_2038C1628914B9C9() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_203FCB56FDB86A74() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_20569C107C6CB08C() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_20AB2D734EDE55F0() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_22B1281180FB0A5E() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_22F1AADA66A449AE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_238B215EFFDF3D30() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_24E8EC51D149FA15() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_25728E78A3962C02() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_25E649A1C6891C05() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_264B8A38B577705D() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_266ED08DC1C82A0E() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_27BB4DE62AB58BAD() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_283AA96A196EA2EA() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_285315A390A85A94() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_29049DBB1EF3194E() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_29F7BA9C3732CB47() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_2A732DF331ACCB37() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_2AA01660EC75B6FB() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_2B37CBCE941C1681() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_2CAA3B64D0544E55() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_2CCD79617EC10A75() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_2CD8B69716AC0667() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_2D74F7C0FF9B5E9C() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_2DCA5A8080544E95() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_2E69F2743CE7CE57() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_2EAF1F3BAFF0527D() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_31493E55BB4E8F66() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_317EDCAD00FB5F5E() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_31E01CFA8A18CDA2() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_32AFD782A061B526() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_32B5CDEB093B8189() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_34155152513C93AE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_34E4EFFF8EF6C9FE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_3572FA0D5C54563B() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_367C479B264E0DB9() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_36884FBC964B29CC() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_3860081BB7559949() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_39314F7E674AB132() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_3A02E780FCC556A5() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_3A17B885BA4849B6() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_3A38EACAEA5E23A4() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_3B34A5E07F0DBC1F() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_3B4E8FFC00FC7EA4() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_3BAB18FDA235107A() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_3BDF9996A0A33F11() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_3C1952F1A45CC37A() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_3CA37906CDB05F3B() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_3CDB2908ACEE3A6F() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_3D3ED165F2BDCD33() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_3DA4D7D1575FCDCE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_3DDFB612CD0BC769() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_3E0415E167DEADC7() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_3E7E9F0F1581C1E6() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_3ED389DB8280ED65() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_3F0C7F6C0C35487D() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_3FDA7200389EF0D2() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_3FF3C258BA516E58() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_4029453F628A3C5D() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_405826DDB4AE538E() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_405A926759F25865() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_406608FDEE7AE88A() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_40DDA5558C17DDCF() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_419D12E52FF60664() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_4296E539474BE77F() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_42F41FC563CC3654() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_43CCC86F4C93026A() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_4409F60BDABC65E1() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_4563C70AEC675382() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_45E66370219BD05E() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_466A54F072785696() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_46CD2536976F209A() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_4863717BD2FDD157() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_4902EBD19A263149() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_4904F7FE8D83F40C() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_4A5E13F784ABFCE7() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_4B65EEB135C12781() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_4C19D49978DA85E2() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_4DE5D620FF66F136() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_4E170C12B57A8F9E() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_4E2F3FA405C3260C() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_4EA9350577513B4D() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_4F78EB6FC4B5F21F() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_50348BE4331117B7() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_508C7E8CDD281CAA() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_521C1D2C028F5A7E() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_522FF24A35E67291() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_5470FE90C25CDD4C() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_557F260F9A4ACD18() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_5586F97209F391EB() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_55B2C9B7ADA95C3C() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_55B488A3A540B936() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_5642DFE82AF43143() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_574E046F294AE187() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_578926EBF8AA6CBF() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_585DA5FC650896BC() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_58D6EB27349EC276() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_5906B7317949872D() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_5910B5614335BE70() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_593D7DA8911F08C9() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_59757FE6A93B0D53() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_598E60F862B1141E() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_5A45351666680DAF() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_5AABE9EA702E6A7F() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_5AEA4AE472355B80() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_5B20E53CDE598741() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_5B480B59FAE947E0() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_5B5EEC23690AB9BD() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_5C0AC5B0AF3EDAE0() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_5D2E999BEA0762D4() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_5D55BBFD45110E16() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_5DEE15403D2BB5FD() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_6020C708CA74B130() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_606E1415503C34D2() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_612140E8EE9A693E() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_61F13F551DAF61DF() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_6206D39131752328() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_621D4543EF0344DE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_6259A9A8E56D0273() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_625F9C7016346F4E() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_62EF8DF746CD8C4A() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_636D2A99FD1E6B2B() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_68013EDF66FE7425() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_6971F7067DD639D1() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_69896ADB3AB410B2() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_6A1389AA6E561387() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_6A5560D89F12B2E7() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_6ABF99CF854ABCF1() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_6B4FDDC6500D8DCB() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_6CA11D5B49D1928A() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_6D6C0FB61E6D0715() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_6D750745FE1348F5() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_6E1AF3F9D09914BE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_6E53ED4C08B2A521() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_6EF43ACA1ED6B968() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_6F6FA09F3E1B6A60() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_7035C340C7195901() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_7038E21CB5CF641B() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_706345DCDA5BA44D() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_7120714EBF10BF1F() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_713D28A91BC803DD() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_7153BD76A53AA012() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_715C625CC7041B6B() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_71E467BDB18711D0() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_720D17965C1F4E3F() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_734380C9BCF65B9A() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_73F4C08CCD4BBCCF() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_74403101B7B29D46() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_7525B081ACD66FF4() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_75BF4477C13A05CA() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_7609793F5987C6F7() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_7616ED01B04769AA() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_764F873D91A124D8() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_7706F1E123059565() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_77F2D07EB6D806E6() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_79C3704CDCD59E57() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_79DA0BBA21351545() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_79FA2447B5F3F0C4() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_7A4D6F65FF6195A5() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_7B3195CD114DECE7() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_7B3238F2301AD36D() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_7C77FC70750A3266() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_7D23A9DC459D6D18() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_7D5988C748D0A05F() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_7D9597147A99F4F4() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_7E2953F407DD8346() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_7EE34E5099709B32() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_80470E5511D5CA00() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_807179701C08F069() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_8096E81FFAF24E46() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_80B764F4F1B87042() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_80BF691438AD008B() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_80CF6CFC96012442() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_80EA772F8C0519FD() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_81D0AFD0084D327A() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_821EB8A72176FD67() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_82D2FAB54127273F() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_836AE669C42A59E9() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_8559A25BFEC3518C() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_85C1F66C767A49D2() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_8689ED1383F87BA7() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_8796CD9E5355D3A6() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_87D37EB6DDC19D99() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_880AA48F70F84FDD() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_897B07562093665B() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_8ACAF55F16368087() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_8AE8A5589B30D4E0() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_8AE997909831B331() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_8B2D640BE0D0FB99() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_8B3D9AB4668DAECB() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_8B5EFAAAACE0B46C() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_8C27943F40A988DB() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_8C54096C75F5F2D0() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_8D7663A0A5168814() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_8E618F509994FAD7() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_8F19E6CC064E2B98() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_8F6A8AEAEE922FF5() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_9010E1AD8EBBFBCA() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_90A955A0E7001AE9() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_90F9D6067FEECC05() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_9348F3D19546A1DA() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_93D3C011DB19388A() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_956E7A4FD9F89103() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_95F699E042C3E40F() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_96877B39AA0E8735() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_96CE07C49ED234EA() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_976BB178235B5681() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_978C0B25E588C4D6() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_98BA2612BEF238D6() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_995BDD4931AF9137() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_9966E39A926B7250() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_99C2306F18963464() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_99C92C613B776BA7() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_9A4E4B938CC8AD39() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_9B23F7B4B7F72081() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_9C0EAEEAE705A8DB() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_9D47AC59545DE9E8() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_A13052D8B1B2ACFA() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_A1AA43E3A78F6F62() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_A1E48CDF54649DC9() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_A2E7DEE5B0AF5D14() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_A2F5C7FD9FF113F5() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_A36296E2269D46BC() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_A3EE2A7B9F0D88AF() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_A4471F9F7E0BFA82() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_A449BBA521EA34E1() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_A48E666C334E726C() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_A49B7449B4DDE69C() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_A5748451125C9EA4() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_A690A28D648CC176() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_A6A86DE1B1CBB1D9() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_A8F2BB7B815740A1() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_A93F64C06A6F7397() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_AB35925FC97D6AA3() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_AC014AA2C991FA29() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_AC06E10901404AEB() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_AC75C68813523505() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_AD441BC497082C3E() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_AD4F25F021D354C3() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_ADFA04A85541A4FE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_AE9610A6B5217A23() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_AF201923826F0A58() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_AFC021B4389CA3FA() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_B015E999A3373D8F() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_B0384B86107FC652() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_B0C630653B316563() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_B100DCCD88D5C73D() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_B11A3FEA5E4D9EA4() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_B2E7F8DC199C0B93() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_B3AB61A296F6DDC8() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_B3F32F6AE619EC82() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_B4227AB213BF8CF5() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_B4652BF42B604360() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_B536C1F13BFE97CB() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_B645CC264184BC89() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_B67E17B1582C6FBD() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_B6D047C5D7695A4D() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_B75ED8E1EA62EFC7() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_B7A9A944DBD7E100() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_B7C4E75BE94F31F3() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_B888B1F92C464121() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_B8DEC22564AA057B() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_B9BADD1CBBBAE4F8() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_BAA9F7169C85E59F() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_BAEE5C38908D62DB() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_BCC855EB25183F84() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_BD01F637029C7364() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_BDD29F5AC7077E53() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_BED83DD33ECAD50D() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_BEE7D5D098ABF728() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_C0DB15CCF59AE62C() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_C1C229FEE0FD60FA() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_C228B9AD68298E98() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_C298525CEF6FB283() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_C350F09351F6D6B5() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_C3742E80FA580319() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_C3C9853D5D4D45D4() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_C3F5DAD4FB9FC340() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_C45FB0E4CCE9AED6() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_C4979CB948B7E3C7() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_C49B25BA16CF0B8C() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_C551345D9631201E() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_C57A294421368298() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_C5DC91CAD721D628() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_C6DECEE589135357() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_C81F8B20D67AC78D() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_C820FA56FAC87BEA() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_C878EA9114C5E490() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_C8A813EBFF477509() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_C966A663D5A35482() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_C97C4C67FD3674D3() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_C990550F15848B07() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_CA59737A8EC1BBBE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_CAC5FDE8F80D7B65() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_CB135B30D0639B83() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_CB8A1AAA61F64C3A() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_CB9E674672580757() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_CC2B9D25EAEAAB1D() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_CD1B252BBEDF5B53() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_CF003BE90CBE1A27() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_CF008E34884AC1E2() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_D0B8F4B3A3687AB2() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_D0EE19B8E91F60F5() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_D12B9294BD0E0F56() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_D1CC8626D8FA328B() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_D2FA2BB9EB8B63AC() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_D32197880CF93CEB() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_D326F5C26CC81B8E() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_D4FA06B95A321B7A() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_D52A37A901E04B21() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_D5504DFC399AB400() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_D56105CB27F8F5DC() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_D568AB19235ECB19() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_D6DF7BF6639FE611() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_D8608A903119D746() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_D9E8FC707D59914D() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_D9F079E62DEE5B29() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_DA17CE4F29748536() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_DA40B9EFD7F61185() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_DA6B274FEBC2666A() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_DAD01535C87A51FC() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_DB4511D448510EC4() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_DB8EF1FFFC66269C() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_DBB508FA1B9DA8F7() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_DC59C9B870B729A2() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_DC669ED6CBF6751C() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_DCB8A2849A41C991() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_DD8F9916D7F03AF7() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_DDC33F2F4E480C2A() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_DE0B420BDE8B22D7() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_E0C0BC29898FE370() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_E0CD893E46FB55BA() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_E25530164B7F659F() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_E3682F43FDF76C58() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_E38177E1C78A80FA() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_E3CA74CFF965DF0A() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_E45BB191B49B2ED9() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_E465B9D6B60E6D7D() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_E4D82876C296C38A() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_E4DDB5350FA5B538() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_E54BFF6FB72BC7BE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_E592A93203020BBB() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_E5A44AF6D7D48AFD() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_E639A97CF9FF1430() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_E6AC0179E48A8927() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_E751596682775D83() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_E788B1E52EF82702() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_E94F17613F5C9D31() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_E9590113128D55E0() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_E9E0B0DD12560B16() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_EAF5C8ECE64C7B05() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_EB98BF5C42D4A7EB() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_EBABC4AAC43A468C() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_EBF00085F082CC8B() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_ECB659EE058D06AF() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_ECF096AB751487AE() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_EE5A271701DB33C0() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_EF64CB6A1625248E() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_EF6C8A357C7ED863() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_F00FE94F7E699994() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_F1A51DBA30329038() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_F216E766A90FDC12() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_F2A10584ABE5D82C() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_F2D99D395E5421A3() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_F38001E528BA1371() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_F39EC9C8FA7687B3() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_F3AFFFDCD632775C() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_F3B8DFF33748BFD3() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_F5E47F9550F7A147() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_F6E93714D1A939CF() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_F6FD19AD48E4EF09() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_F744EBFC620F7CBF() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_F76E4525ACBACC7F() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_F7957A48882F42CB() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_F7A80B07809BA838() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_F8571C6CC5B6B59D() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_F9787CFA873836FB() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_FA789F6D34D383F8() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_FABA574083AC1E6C() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_FC04FDBBAE368FB7() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_FD2DAFBF2E40EEE7() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_FD55EE6D35F950AD() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_FE55EE32098D0D58() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_FE79841022E1DA1C() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_FFF4A3E279FB44A7() { + LOG_ERROR(Lib_NpCommon, "(STUBBED) called"); + return ORBIS_OK; +} + +void RegisterlibSceNpCommon(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("i8UmXTSq7N4", "libSceNpCommonCompat", 1, "libSceNpCommon", 1, 1, sceNpCmpNpId); + LIB_FUNCTION("TcwEFnakiSc", "libSceNpCommonCompat", 1, "libSceNpCommon", 1, 1, + sceNpCmpNpIdInOrder); + LIB_FUNCTION("dj+O5aD2a0Q", "libSceNpCommonCompat", 1, "libSceNpCommon", 1, 1, + sceNpCmpOnlineId); + LIB_FUNCTION("0gdlCVNNHCI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _sceNpAllocatorExConvertAllocator); + LIB_FUNCTION("Zh23aSLeeZo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, _sceNpAllocatorExFree); + LIB_FUNCTION("a2qdVU8RWb4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _sceNpAllocatorExMalloc); + LIB_FUNCTION("kKF3w-XkCWA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _sceNpAllocatorExRealloc); + LIB_FUNCTION("Cmd4+m7V00c", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _sceNpAllocatorExStrdup); + LIB_FUNCTION("EziLjfyTnKI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _sceNpAllocatorExStrndup); + LIB_FUNCTION("BztTl7QeYqE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, _sceNpAllocatorFree); + LIB_FUNCTION("mzlILsFx0cU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, _sceNpAllocatorMalloc); + LIB_FUNCTION("VWcTu8wKwlQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _sceNpAllocatorRealloc); + LIB_FUNCTION("c8-4aC9opYE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, _sceNpAllocatorStrdup); + LIB_FUNCTION("vqA9bl6WsF0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _sceNpAllocatorStrndup); + LIB_FUNCTION("z5kwfM5InpI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, _sceNpFree); + LIB_FUNCTION("p1vvpKGRXe4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, _sceNpHeapFree); + LIB_FUNCTION("kwW5qddf+Lo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, _sceNpHeapMalloc); + LIB_FUNCTION("wsfyvM+VbUk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, _sceNpHeapRealloc); + LIB_FUNCTION("atWcfgasESY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, _sceNpHeapStrdup); + LIB_FUNCTION("RzLv+HR5E2A", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, _sceNpHeapStrndup); + LIB_FUNCTION("w2+qV1RJgcI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, _sceNpMalloc); + LIB_FUNCTION("UmzxltBpiiY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, _sceNpRealloc); + LIB_FUNCTION("LJvHO3uCNm4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10Cancelable10IsCanceledEv); + LIB_FUNCTION("fd+grYAEph0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10Cancelable10LockCancelEPKciS3_); + LIB_FUNCTION("IwDQAbQxvD0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10Cancelable11CheckCancelEPKciS3_); + LIB_FUNCTION("-zbpF68OGDs", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10Cancelable12UnlockCancelEPKciS3_); + LIB_FUNCTION("bBLapYYwyr0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10Cancelable13SetCancelableEb); + LIB_FUNCTION("j4gLOIpHgNk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10Cancelable14SetupSubCancelEPS1_PKciS4_); + LIB_FUNCTION("vmt3ZOlQu3o", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10Cancelable16CleanupSubCancelEPS1_); + LIB_FUNCTION("Y7f+qBjKxdo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10Cancelable4InitEv); + LIB_FUNCTION("Jhbrpz0YhHU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10Cancelable6CancelEij); + LIB_FUNCTION("v2yJZLY0w1U", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10Cancelable7DestroyEv); + LIB_FUNCTION("vqekW3s-eFg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10CancelableC2Ev); + LIB_FUNCTION("kdOC-2AE06w", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10CancelableD0Ev); + LIB_FUNCTION("upzdrzOYkS0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10CancelableD1Ev); + LIB_FUNCTION("vZXDqs2x7t0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10CancelableD2Ev); + LIB_FUNCTION("nleHqndSeQ0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10CancelLock3EndEPKciS3_); + LIB_FUNCTION("lJ2Efd9PUKI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10CancelLock5BeginEPNS0_6HandleEPKciS5_); + LIB_FUNCTION("Vq9LKkPXkIQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10CancelLockC1Ev); + LIB_FUNCTION("MecB8wAHCfE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10CancelLockC2Ev); + LIB_FUNCTION("K7FjXiy2z+A", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10CancelLockD1Ev); + LIB_FUNCTION("1iHBAKrdE90", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10CancelLockD2Ev); + LIB_FUNCTION("aoas3bJANfY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10EventQueue10ClearAbortEt); + LIB_FUNCTION("QlP4t2SGZ4I", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10EventQueue10TryDequeueEPvm); + LIB_FUNCTION("xu9qWN0YYC4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10EventQueue4ctorEv); + LIB_FUNCTION("N1gnYosdK7Q", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10EventQueue4dtorEv); + LIB_FUNCTION("b20e017Ei94", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10EventQueue4InitEPKcmm); + LIB_FUNCTION("slmKkuIoC28", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10EventQueue5AbortEt); + LIB_FUNCTION("suxln7PooIo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10EventQueue7DequeueEPvmj); + LIB_FUNCTION("qvpEuKumIGM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10EventQueue7DestroyEv); + LIB_FUNCTION("AV5jHo8O3+E", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10EventQueue7EnqueueEPKvmj); + LIB_FUNCTION("esiO4He2WTU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10EventQueueC2EP16SceNpAllocatorEx); + LIB_FUNCTION("E4uoqSdo8ek", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10EventQueueD0Ev); + LIB_FUNCTION("lQXgvDXBGtA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10EventQueueD1Ev); + LIB_FUNCTION("8kUkQPQP7bA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10EventQueueD2Ev); + LIB_FUNCTION("YHNEgBCSL2o", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10JsonNumber5ClearEv); + LIB_FUNCTION("UgmqDr1BCLw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10JsonNumber6SetNumEi); + LIB_FUNCTION("PccynQ5NdVQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10JsonNumber6SetNumEj); + LIB_FUNCTION("MY0CSk24EcY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10JsonNumber6SetNumEl); + LIB_FUNCTION("qbW7qOvVafI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10JsonNumber6SetNumEm); + LIB_FUNCTION("VyCn9EVJGlU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10JsonNumber6SetNumEPKc); + LIB_FUNCTION("-WgnISXjJ7A", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10JsonObject16DeleteFieldValueEPKc); + LIB_FUNCTION("DiHxx2k5zfM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10JsonObject5ClearEv); + LIB_FUNCTION("AGadQiCfKDY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10JsonParser4InitEPK7JsonDefPNS1_12EventHandlerE); + LIB_FUNCTION("CDzSgHA6hWg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10JsonParser5ParseEPKcm); + LIB_FUNCTION("ZJbPQt+FTnY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10JsonParserC2EP16SceNpAllocatorEx); + LIB_FUNCTION("u+A16O-TAHk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10JsonParserD0Ev); + LIB_FUNCTION("qJb7IXDg9xk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10JsonParserD1Ev); + LIB_FUNCTION("AvvE5A5A6ZA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10JsonParserD2Ev); + LIB_FUNCTION("kXE1imLw7yo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10JsonString5ClearEv); + LIB_FUNCTION("SN4IgvT26To", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10JsonString6SetStrEPKc); + LIB_FUNCTION("EyhtbPFMWNA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10MemoryFile4ReadEPNS0_6HandleEPvmPm); + LIB_FUNCTION("AZTMWob-mog", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10MemoryFile4SyncEv); + LIB_FUNCTION("dl6+SFHLke0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10MemoryFile5CloseEv); + LIB_FUNCTION("r2O0f9X-mqs", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10MemoryFile5WriteEPNS0_6HandleEPKvmPm); + LIB_FUNCTION("1DtavqenQjg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10MemoryFile8TruncateEl); + LIB_FUNCTION("ev77AviWYu8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10MemoryFileC2EP16SceNpAllocatorEx); + LIB_FUNCTION("6Vst7HqJMXU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10MemoryFileD0Ev); + LIB_FUNCTION("ZUf92uPkRuA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10MemoryFileD1Ev); + LIB_FUNCTION("lGjyfcI++PY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np10MemoryFileD2Ev); + LIB_FUNCTION( + "ezJnmv7hkAg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np12HttpTemplate19SetAuthInfoCallbackEPFii15SceHttpAuthTypePKcPcS5_iPPhPmPiPvESA_); + LIB_FUNCTION("iOTsJTR6Y9U", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np12HttpTemplate4InitEiPKcib); + LIB_FUNCTION("73qbxKjBH0o", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np12HttpTemplate7DestroyEv); + LIB_FUNCTION("Vj7HiXK-tTg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np12HttpTemplateC1Ev); + LIB_FUNCTION("hw-UPUK9T+w", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np12HttpTemplateC2Ev); + LIB_FUNCTION("cXYOwTVAuMs", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np12HttpTemplateD0Ev); + LIB_FUNCTION("Bm74HLvoNY4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np12HttpTemplateD1Ev); + LIB_FUNCTION("h6XPsGpHAtc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np12HttpTemplateD2Ev); + LIB_FUNCTION("jr0OcEeQJ8o", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np12StreamBufferixEi); + LIB_FUNCTION("rCRh3V03bPs", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np12StreamReader4ReadEPNS0_6HandleEPNS0_9StreamCtxEPvmPm); + LIB_FUNCTION("2SKuIvr9sYU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np12StreamReader7ReadAllEPNS0_6HandleEPNS0_9StreamCtxEPvmPm); + LIB_FUNCTION("f1ncwa-JXlA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np12StreamReader7ReadAllEPNS0_6HandleEPvmPm); + LIB_FUNCTION("z8qO7hql4Fs", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np12StreamReader8ReadDataEPNS0_6HandleEPNS0_9StreamCtxEPvmPm); + LIB_FUNCTION("oNqSobbGC80", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np12StreamReader8ReadDataEPNS0_6HandleEPvmPm); + LIB_FUNCTION("MSMPXUL5AuM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np12StreamReader8SkipDataEPNS0_6HandleElPl); + LIB_FUNCTION("fJB07vDf7no", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np12StreamReader8SkipDataEPNS0_6HandleEPNS0_9StreamCtxElPl); + LIB_FUNCTION("etMUeqIhN+w", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np12StreamWriter15WriteFilledDataEPNS0_6HandleEcl); + LIB_FUNCTION("SP2010+gtqw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np12StreamWriter15WriteFilledDataEPNS0_6HandleEPNS0_9StreamCtxEcl); + LIB_FUNCTION("Z1MRG-L+V0o", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np12StreamWriter5WriteEPNS0_6HandleEPNS0_9StreamCtxEPKvmPm); + LIB_FUNCTION("vHaV+tsSVu4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np12StreamWriter9WriteDataEPNS0_6HandleEPKvmPm); + LIB_FUNCTION("u9s1aUWSZB0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np12StreamWriter9WriteDataEPNS0_6HandleEPNS0_9StreamCtxEPKvmPm); + LIB_FUNCTION("gimH2zdBANg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np12WorkerThread10ThreadMainEv); + LIB_FUNCTION("YKz2oBW3ZkM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np12WorkerThreadC1EPNS0_9WorkQueueE); + LIB_FUNCTION("L9Ty-fG1IM4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np12WorkerThreadC2EPNS0_9WorkQueueE); + LIB_FUNCTION("f5L6ax7EWHk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np12WorkerThreadD0Ev); + LIB_FUNCTION("PvGTq9AGFfk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np12WorkerThreadD1Ev); + LIB_FUNCTION("+qB+WcQlMio", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np12WorkerThreadD2Ev); + LIB_FUNCTION("4nCyBD9jBus", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np13JsonDocParser5ParseEPKcm); + LIB_FUNCTION("sgh9D+MBBKA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np13JsonDocParser9GetResultEPPNS0_10JsonObjectE); + LIB_FUNCTION("lZWmdDoBDmI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np13JsonDocParser9GetResultEPPNS0_9JsonValueE); + LIB_FUNCTION("yPmQcnrgR2Y", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np13JsonDocParserC2EP16SceNpAllocatorExPK7JsonDef); + LIB_FUNCTION("p5hRe1k4Wlg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np13JsonDocParserD0Ev); + LIB_FUNCTION("iFOXfoXRHFQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np13JsonDocParserD1Ev); + LIB_FUNCTION("xS-Hjw1psYs", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np13JsonDocParserD2Ev); + LIB_FUNCTION("X0vEo7cZamA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np13NpTitleSecret5ClearEv); + LIB_FUNCTION("IjOpzNzl57o", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np13NpTitleSecretC1EPKvm); + LIB_FUNCTION("bC4+qi0mqJE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np13NpTitleSecretC1ERK16SceNpTitleSecret); + LIB_FUNCTION("fYr7Ahl-vNA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np13NpTitleSecretC1ERKS1_); + LIB_FUNCTION("08AQ2wYpzpk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np13NpTitleSecretC1Ev); + LIB_FUNCTION("Ft-VezxSErk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np13NpTitleSecretC2EPKvm); + LIB_FUNCTION("9QN7g5mQgCU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np13NpTitleSecretC2ERK16SceNpTitleSecret); + LIB_FUNCTION("JHG9CTmkdQw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np13NpTitleSecretC2ERKS1_); + LIB_FUNCTION("K1+uzxxReX0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np13NpTitleSecretC2Ev); + LIB_FUNCTION("dJRIc7d5iqU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np13NpTitleSecretD0Ev); + LIB_FUNCTION("XBzzdzT3qyg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np13NpTitleSecretD1Ev); + LIB_FUNCTION("QDlnJL6stA0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np13NpTitleSecretD2Ev); + LIB_FUNCTION("RPv5L-o5qRQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np13RingBufMemory4ctorEv); + LIB_FUNCTION("NfhXX6LFmj8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np13RingBufMemory4dtorEv); + LIB_FUNCTION("BkuxOAPlMMw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np13RingBufMemory4InitEm); + LIB_FUNCTION("do0t--lEKMM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np13RingBufMemory6ExpandEm); + LIB_FUNCTION("zdRXyt-65kA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np13RingBufMemory6IsInitEv); + LIB_FUNCTION("Za00SEoNA2A", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np13RingBufMemory7DestroyEv); + LIB_FUNCTION("lGIw3qfqI60", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np13RingBufMemoryC2EP16SceNpAllocatorEx); + LIB_FUNCTION("70qFzq4z3UI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np13RingBufMemoryD0Ev); + LIB_FUNCTION("C1TJsMv9wb8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np13RingBufMemoryD1Ev); + LIB_FUNCTION("EaxLv8TfsrM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np13RingBufMemoryD2Ev); + LIB_FUNCTION("j6CorpmdjRk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np14CalloutContext4InitEPKcimm); + LIB_FUNCTION("oLpLfV2Ov9A", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np14CalloutContext4InitEPKNS1_5ParamE); + LIB_FUNCTION("C282U0P6Nwg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np14CalloutContext7DestroyEv); + LIB_FUNCTION("dV+zK-Ce-2E", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np14CalloutContextC1Ev); + LIB_FUNCTION("j4IAvbKKTzw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np14CalloutContextC2Ev); + LIB_FUNCTION("WR4mjQeqz6s", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np14CalloutContextD0Ev); + LIB_FUNCTION("S+a+rgnGX8A", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np14CalloutContextD1Ev); + LIB_FUNCTION("wY9g+hVxLTM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np14CalloutContextD2Ev); + LIB_FUNCTION("PYBehFWVd60", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np14JsonDocBuilder12BuildBufSizeEv); + LIB_FUNCTION("cLdoHqi5Ezg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np14JsonDocBuilder16EscapeJsonStringEPKcPcmPm); + LIB_FUNCTION("V5xX2eroaWY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np14JsonDocBuilder23EscapeJsonStringBufSizeEPKc); + LIB_FUNCTION("irex3q-O6po", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np14JsonDocBuilder5BuildEPcmPm); + LIB_FUNCTION("ikFI73f3hP4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np14JsonDocBuilderC1ERKNS0_9JsonValueE); + LIB_FUNCTION("dhJGQPKLmn0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np14JsonDocBuilderC2ERKNS0_9JsonValueE); + LIB_FUNCTION("wDLaq7IgfIc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np14JsonDocBuilderD0Ev); + LIB_FUNCTION("Kfv9jPxf7qA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np14JsonDocBuilderD1Ev); + LIB_FUNCTION("MH0LyghLJEE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np14JsonDocBuilderD2Ev); + LIB_FUNCTION("pCIB7QX5e1g", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np15CancelableScope3EndEiPKciS3_); + LIB_FUNCTION("Etvu03IpTEc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np15CancelableScope5BeginEPNS0_6HandleEPKciS5_); + LIB_FUNCTION("pp88xnRgJrM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np15CancelableScopeC2Ev); + LIB_FUNCTION("E8yuDNYbzl0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np15CancelableScopeD0Ev); + LIB_FUNCTION("km5-rjNjSFk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np15CancelableScopeD1Ev); + LIB_FUNCTION("xpLjHhJBhpo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np15CancelableScopeD2Ev); + LIB_FUNCTION("LCk8T5b1h+4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np16StreamReadBufferC2EP16SceNpAllocatorEx); + LIB_FUNCTION("ZufKqNXItD0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np16StreamReadBufferD1Ev); + LIB_FUNCTION("bH7ljyLOsBw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np16StreamReadBufferD2Ev); + LIB_FUNCTION("et05S+nkWG8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np18HttpConnectionPool13InvalidateAllEv); + LIB_FUNCTION("Vzob5RCgfnY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np18HttpConnectionPool4InitEi); + LIB_FUNCTION("iBdEFRdfpgg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np18HttpConnectionPool7DestroyEv); + LIB_FUNCTION("PznfSvchYJ8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np18HttpConnectionPoolC1EP16SceNpAllocatorEx); + LIB_FUNCTION("-2TYwZ4ERbM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np18HttpConnectionPoolC2EP16SceNpAllocatorEx); + LIB_FUNCTION("5HWP63cOH+w", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np18HttpConnectionPoolD0Ev); + LIB_FUNCTION("kTfkKhcdW5Y", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np18HttpConnectionPoolD1Ev); + LIB_FUNCTION("3MVW8+eWnjs", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np18HttpConnectionPoolD2Ev); + LIB_FUNCTION("ELa6nMcCO9w", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np18MemoryStreamReader4ReadEPNS0_6HandleEPvmPm); + LIB_FUNCTION("UHj0GDTA2CU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np18MemoryStreamReaderC1EPKvm); + LIB_FUNCTION("WXRruhGp9dI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np18MemoryStreamReaderC2EPKvm); + LIB_FUNCTION("gA0CaCjJpg0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np18MemoryStreamReaderD0Ev); + LIB_FUNCTION("oULMh4JVC4o", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np18MemoryStreamReaderD1Ev); + LIB_FUNCTION("rNJ1+3KoZP4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np18MemoryStreamReaderD2Ev); + LIB_FUNCTION("VxKQGrudnzk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np18MemoryStreamWriter5WriteEPNS0_6HandleEPKvmPm); + LIB_FUNCTION("Lkdm2yqZN1c", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np18MemoryStreamWriterC1EPvm); + LIB_FUNCTION("abQ7xd3yVXM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np18MemoryStreamWriterC2EPvm); + LIB_FUNCTION("TXJnPiKuTf8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np18MemoryStreamWriterD0Ev); + LIB_FUNCTION("3VdCUl+DkNw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np18MemoryStreamWriterD1Ev); + LIB_FUNCTION("YmOVGwSJmzk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np18MemoryStreamWriterD2Ev); + LIB_FUNCTION("INZSjlRcuyQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np20BufferedStreamReader4ReadEPNS0_6HandleEPvmPm); + LIB_FUNCTION("3Ku9r8b6gCg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np20BufferedStreamReader5CloseEv); + LIB_FUNCTION("l6s7aomzWGA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np20BufferedStreamReaderC2EP16SceNpAllocatorEx); + LIB_FUNCTION("i28bR54-QFQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np20BufferedStreamReaderD0Ev); + LIB_FUNCTION("Tgr66MThOxA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np20BufferedStreamReaderD1Ev); + LIB_FUNCTION("PHWvRXbOnYs", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np20BufferedStreamReaderD2Ev); + LIB_FUNCTION("7dyKpPHU+Yk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc10IpmiClient10DisconnectEv); + LIB_FUNCTION("prj9aMR74bA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc10IpmiClient11IsConnectedEv); + LIB_FUNCTION("r7UpNm1Po9s", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc10IpmiClient16invokeSyncMethodEjPKvmPvPmm); + LIB_FUNCTION("+EQNga+wsPc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc10IpmiClient4ctorEv); + LIB_FUNCTION("2h59YqPcrdM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc10IpmiClient4dtorEv); + LIB_FUNCTION("iRH-NE2evR4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc10IpmiClient4InitEPKNS2_6ConfigE); + LIB_FUNCTION("CGKtxL26XqI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc10IpmiClient7ConnectEPKvm); + LIB_FUNCTION("+xvhXA8Ci4E", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc10IpmiClient7DestroyEv); + LIB_FUNCTION("6aKYLBS8Di8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc10IpmiClientC1Ev); + LIB_FUNCTION("dqjlsaUX0sc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc10IpmiClientC2Ev); + LIB_FUNCTION("3LuoWoXJ1WI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc10IpmiClientD0Ev); + LIB_FUNCTION("DRbjyNom-BE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc10IpmiClientD1Ev); + LIB_FUNCTION("J1lpiTKAEuk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc10IpmiClientD2Ev); + LIB_FUNCTION( + "aQzxfON3l2Y", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc13ServiceClientC1EPNS1_17ServiceIpmiClientEPKNS1_17ServiceClientInfoE); + LIB_FUNCTION( + "Mx6wrcdGC2w", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc13ServiceClientC2EPNS1_17ServiceIpmiClientEPKNS1_17ServiceClientInfoE); + LIB_FUNCTION("uvYTUK5xYG8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClient10DisconnectEv); + LIB_FUNCTION("fFGPlE0oNhw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClient10EndRequestEii); + LIB_FUNCTION("F2xYmg5DiR4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClient11findServiceEi); + LIB_FUNCTION("G4FYQtsjOX0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClient11InitServiceEi); + LIB_FUNCTION("0rqwC4+sgzU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClient11TermServiceEi); + LIB_FUNCTION("oCx3mVNvqzU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClient11WaitRequestEiij); + LIB_FUNCTION("tQOrMf4KtIo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClient12AbortRequestEii); + LIB_FUNCTION("9aiQo-uRPJY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClient12BeginRequestEii); + LIB_FUNCTION("H35UsHYlhB4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClient13CreateRequestEPiiPKvm); + LIB_FUNCTION("cMj7li0eXgw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClient13DeleteRequestEii); + LIB_FUNCTION("+ZC8QYB-BA8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClient13PollEventFlagEijmjPm); + LIB_FUNCTION("4GZ9O-OrfzE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClient13WaitEventFlagEijmjPmj); + LIB_FUNCTION("q+uCQLffwQE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClient14PollEventQueueEiPvm); + LIB_FUNCTION("bH08FzR5rFU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClient15CancelEventFlagEijm); + LIB_FUNCTION("stUzNgtFmtY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClient15RegisterServiceEPKNS1_17ServiceClientInfoE); + LIB_FUNCTION("fqAS9GQTmOU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClient16RegisterServicesEPKNS1_17ServiceClientInfoE); + LIB_FUNCTION("BJCXJJCi0Zc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClient17invokeInitServiceEi); + LIB_FUNCTION("GuruEy9Q-Zk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClient17invokeTermServiceEi); + LIB_FUNCTION("k9jCtANC+QM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClient17UnregisterServiceEi); + LIB_FUNCTION("8TpAxZoLLRw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClient18EndRequestForAsyncEii); + LIB_FUNCTION("1ONFW86TETY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClient19WaitRequestForAsyncEiij); + LIB_FUNCTION("nQm4o5iOye0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClient20AbortRequestForAsyncEii); + LIB_FUNCTION( + "ktb6iOBLnd4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClient20BeginRequestForAsyncEiiPN4IPMI6Client12EventNotifeeE); + LIB_FUNCTION("v5Z2LAKua28", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClient21CreateRequestForAsyncEPiiPKvm); + LIB_FUNCTION("7oJpAd+vJQA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClient21DeleteRequestForAsyncEii); + LIB_FUNCTION("KxlKRHLf9AY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClient4ctorEv); + LIB_FUNCTION("s+dG6iqG7j0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClient4dtorEv); + LIB_FUNCTION("qFdG8Ucfeqg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClient4InitEPNS2_6ConfigE); + LIB_FUNCTION("NTPZ5GZIA6U", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClient7ConnectEPKvm); + LIB_FUNCTION("IGngArGbzHo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClient7DestroyEv); + LIB_FUNCTION("FubuBXanVWk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClientC1Ev); + LIB_FUNCTION("zARyDXgocuk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClientC2Ev); + LIB_FUNCTION("PmsH4f3z8Yk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClientD0Ev); + LIB_FUNCTION("90XdvAqFFn8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClientD1Ev); + LIB_FUNCTION("agYDXAyL-K8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np3ipc17ServiceIpmiClientD2Ev); + LIB_FUNCTION("n9pzAHeCCVU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np4Cond4ctorEv); + LIB_FUNCTION("BtXPJQEg41Y", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np4Cond4dtorEv); + LIB_FUNCTION("wWTqVcTnep8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np4Cond4InitEPKcPNS0_5MutexE); + LIB_FUNCTION("SLPuaDLbeD4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np4Cond4WaitEj); + LIB_FUNCTION("OQiPXR6gfj0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np4Cond6SignalEv); + LIB_FUNCTION("I5uzTXxbziU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np4Cond7DestroyEv); + LIB_FUNCTION("-hchsElmzXY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np4Cond9SignalAllEv); + LIB_FUNCTION("3z5EPY-ph14", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, _ZN3sce2np4CondC1Ev); + LIB_FUNCTION("6nW8WXQYRgM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, _ZN3sce2np4CondC2Ev); + LIB_FUNCTION("AKiHGWhC2KU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, _ZN3sce2np4CondD0Ev); + LIB_FUNCTION("yX9ISVXv+0M", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, _ZN3sce2np4CondD1Ev); + LIB_FUNCTION("6RQRpTn+-cc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, _ZN3sce2np4CondD2Ev); + LIB_FUNCTION("6r6ssbPbKc4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np4Path11BuildAppendEPcmcPKcm); + LIB_FUNCTION("vfBKsg+lKWc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np4Path12AddDelimiterEPcmc); + LIB_FUNCTION("BqFx1VLEMPk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np4Path5ClearEv); + LIB_FUNCTION("AcG6blobOQE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np4Path6SetStrEPKcm); + LIB_FUNCTION("0fwoTW7gqfM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, _ZN3sce2np4PathD0Ev); + LIB_FUNCTION("-3UvpBs-26g", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, _ZN3sce2np4PathD1Ev); + LIB_FUNCTION("1nF0eXrBZYM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, _ZN3sce2np4PathD2Ev); + LIB_FUNCTION("KhoD7EapiYI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np4Time10AddMinutesEl); + LIB_FUNCTION("PgiCaoqRKKc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np4Time10AddSecondsEl); + LIB_FUNCTION("vINvzJOaqws", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np4Time12GetUserClockEPS1_); + LIB_FUNCTION("dLNhHwYyt4c", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np4Time15AddMicroSecondsEl); + LIB_FUNCTION("WZqwoPoMzFA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np4Time15GetNetworkClockEPS1_); + LIB_FUNCTION("fimORKx4RDg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np4Time20GetDebugNetworkClockEPS1_); + LIB_FUNCTION("++qSDotsHuE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np4Time7AddDaysEl); + LIB_FUNCTION("Zc+a6k6i7gY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np4Time8AddHoursEl); + LIB_FUNCTION("Fgm7cz6AX4k", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np4TimeplERK10SceRtcTick); + LIB_FUNCTION("F9khEfgTmsE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np4TimeplERKS1_); + LIB_FUNCTION("I1kBZV6keO4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np5Mutex4ctorEv); + LIB_FUNCTION("mo+gaebiE+M", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np5Mutex4dtorEv); + LIB_FUNCTION("aTNOl9EB4V4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np5Mutex4InitEPKcj); + LIB_FUNCTION("VM+CXTW4F-s", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np5Mutex4LockEv); + LIB_FUNCTION("eYgHIWx0Hco", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np5Mutex6UnlockEv); + LIB_FUNCTION("RgGW4f0ox1g", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np5Mutex7DestroyEv); + LIB_FUNCTION("TJNrs69haak", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np5Mutex7TryLockEv); + LIB_FUNCTION("O1AvlQU33pI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, _ZN3sce2np5MutexC1Ev); + LIB_FUNCTION("2beu2bHw6qo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, _ZN3sce2np5MutexC2Ev); + LIB_FUNCTION("omf1GoUEJCA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, _ZN3sce2np5MutexD0Ev); + LIB_FUNCTION("9zi9FTPol74", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, _ZN3sce2np5MutexD1Ev); + LIB_FUNCTION("CI7ciM21NXs", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, _ZN3sce2np5MutexD2Ev); + LIB_FUNCTION("uuyEiBHghY4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np5NpEnv8GetNpEnvEPS1_); + LIB_FUNCTION("-c9QK+CpQLg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np6Handle10CancelImplEi); + LIB_FUNCTION("ifqJb-V1QZw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np6Handle4InitEv); + LIB_FUNCTION("1atFu71dFAU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np6Handle7DestroyEv); + LIB_FUNCTION("KUJtztDMJYY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, _ZN3sce2np6HandleC1Ev); + LIB_FUNCTION("OhpofCxYOJc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, _ZN3sce2np6HandleC2Ev); + LIB_FUNCTION("ZOHgNNSZq4Q", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, _ZN3sce2np6HandleD0Ev); + LIB_FUNCTION("YWt5S4-cg9c", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, _ZN3sce2np6HandleD1Ev); + LIB_FUNCTION("dt0A2cWjwLs", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, _ZN3sce2np6HandleD2Ev); + LIB_FUNCTION("1x0jThSUr4w", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np6ObjectdaEPv); + LIB_FUNCTION("4il4PZAZOnQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np6ObjectdaEPvR14SceNpAllocator); + LIB_FUNCTION("q2USyzLF4kI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np6ObjectdaEPvR16SceNpAllocatorEx); + LIB_FUNCTION("CnDHI7sU+l0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np6ObjectdlEPv); + LIB_FUNCTION("05KEwpDf4Ls", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np6ObjectdlEPvR14SceNpAllocator); + LIB_FUNCTION("iwDNdnEGyhI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np6ObjectdlEPvR16SceNpAllocatorEx); + LIB_FUNCTION("V75N47uYdQc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np6ObjectnaEmR14SceNpAllocator); + LIB_FUNCTION("bKMVqRcCQ1U", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np6ObjectnaEmR16SceNpAllocatorEx); + LIB_FUNCTION("0syNkhJANVw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np6ObjectnwEmR14SceNpAllocator); + LIB_FUNCTION("orRb69nSo64", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np6ObjectnwEmR16SceNpAllocatorEx); + LIB_FUNCTION("Ehkz-BkTPwI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np6Thread12DoThreadMainEv); + LIB_FUNCTION("3CJl5ewd7-0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np6Thread4ctorEv); + LIB_FUNCTION("-3gV5N2u-sc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np6Thread4dtorEv); + LIB_FUNCTION("EqX45DhWUpo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np6Thread4InitEPKcimm); + LIB_FUNCTION("OoK0Ah0l1ko", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np6Thread4InitEPKNS1_5ParamE); + LIB_FUNCTION("ne77q1GOlF8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np6Thread4JoinEPi); + LIB_FUNCTION("VNKdE2Dgp0Y", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np6Thread5StartEv); + LIB_FUNCTION("sPti0OkVM8c", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np6Thread7DestroyEv); + LIB_FUNCTION("uphWwLZAuXA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np6Thread9EntryFuncEPv); + LIB_FUNCTION("gnwCmkY-V70", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np6Thread9GetResultEv); + LIB_FUNCTION("qy4V8O+snLU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np6Thread9IsRunningEv); + LIB_FUNCTION("0f3ylOQJwqE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, _ZN3sce2np6ThreadC2Ev); + LIB_FUNCTION("MEYMyfJxWXg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, _ZN3sce2np6ThreadD0Ev); + LIB_FUNCTION("0Q5aKjYErBA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, _ZN3sce2np6ThreadD1Ev); + LIB_FUNCTION("6750DaF5Pas", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, _ZN3sce2np6ThreadD2Ev); + LIB_FUNCTION("xxOTJpEyoj4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7Callout10IsTimedoutEv); + LIB_FUNCTION("Zw3QlKu49eM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7Callout11CalloutFuncEPv); + LIB_FUNCTION("14PDhhMEBKY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7Callout4StopEv); + LIB_FUNCTION("TDuC6To9HJ8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7Callout5StartEjPNS1_7HandlerE); + LIB_FUNCTION("r0PYNWZLZS8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7Callout5StartEmPNS1_7HandlerE); + LIB_FUNCTION("3ErXia+y89M", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7Callout9IsStartedEv); + LIB_FUNCTION("XEXFdmQj5oI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7CalloutC1EPNS0_14CalloutContextE); + LIB_FUNCTION("Bpay3NjseSU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7CalloutC2EPNS0_14CalloutContextE); + LIB_FUNCTION("Fx2UwoQVVmo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7CalloutD0Ev); + LIB_FUNCTION("kUitiIVR43g", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7CalloutD1Ev); + LIB_FUNCTION("ebomQLbpptw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7CalloutD2Ev); + LIB_FUNCTION("YtzL-Rso9bk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7HttpUri5BuildEPKS1_PcmPmj); + LIB_FUNCTION("Xp92SsA5atA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7HttpUri5ParseEPS1_PKc); + LIB_FUNCTION("LL9z5QvmwaA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7HttpUriC1EP16SceNpAllocatorEx); + LIB_FUNCTION("q4G7qxTJWps", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7HttpUriC2EP16SceNpAllocatorEx); + LIB_FUNCTION("w+C8QXqZKSw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7HttpUriD0Ev); + LIB_FUNCTION("wSCKvDDBPy4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7HttpUriD1Ev); + LIB_FUNCTION("D-dT+vERWmU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7HttpUriD2Ev); + LIB_FUNCTION("oaSKGgwTWG0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7RingBuf14CheckinForReadEm); + LIB_FUNCTION("78yvwepeL7U", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7RingBuf15CheckinForWriteEm); + LIB_FUNCTION("d8NGGmSEFfU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7RingBuf15CheckoutForReadEPm); + LIB_FUNCTION("E2QFpAcDPq4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7RingBuf16CheckoutForWriteEPm); + LIB_FUNCTION("1P-MUvbtyTM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7RingBuf4ctorEv); + LIB_FUNCTION("rvz8xYxhMW0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7RingBuf4dtorEv); + LIB_FUNCTION("IL3Wk7QuRhA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7RingBuf4InitEPvm); + LIB_FUNCTION("kDaQLJv89bs", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7RingBuf4PeekEmPvm); + LIB_FUNCTION("Mg-IhL6SWfg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7RingBuf4ReadEPvm); + LIB_FUNCTION("IZOGdJ+LFFU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7RingBuf5ClearEv); + LIB_FUNCTION("8Y5OOBb0B5Y", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7RingBuf5WriteEPKvm); + LIB_FUNCTION("u-TlLaJUJEA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7RingBuf7DestroyEv); + LIB_FUNCTION("L5BnZpuQImk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7RingBufC1Ev); + LIB_FUNCTION("e2a1ZA+lJC4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7RingBufC2Ev); + LIB_FUNCTION("hfJ1gGLgvq8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7RingBufD0Ev); + LIB_FUNCTION("7w+LeZ5ymys", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7RingBufD1Ev); + LIB_FUNCTION("9+NmoosRoBA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np7RingBufD2Ev); + LIB_FUNCTION("d+xJZ63-wrc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8HttpFile4ReadEPNS0_6HandleEPvmPm); + LIB_FUNCTION("jcPO4bt5i3o", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8HttpFile5CloseEv); + LIB_FUNCTION("RXdPqxVnrvo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8HttpFileC2EP16SceNpAllocatorEx); + LIB_FUNCTION("T2w3ndcG-+Q", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8HttpFileD0Ev); + LIB_FUNCTION("6fomUWNk6Xc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8HttpFileD1Ev); + LIB_FUNCTION("WAat5MtCKpc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8HttpFileD2Ev); + LIB_FUNCTION("uDyILPgHF9Q", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8JsonBool5ClearEv); + LIB_FUNCTION("FdpYFbq5C3Q", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8JsonBool7SetBoolEb); + LIB_FUNCTION("mFZezLIogNI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8JsonFile5CloseEv); + LIB_FUNCTION("hqPavTyQlNg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8JsonFileD0Ev); + LIB_FUNCTION("wzqAM7IYGzU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8JsonFileD1Ev); + LIB_FUNCTION("QFYVZvAJNC8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8JsonFileD2Ev); + LIB_FUNCTION("88GKkivBFhI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8JsonNull5ClearEv); + LIB_FUNCTION("WcLP8wPB9X4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8NpCommId5BuildERKS1_Pcm); + LIB_FUNCTION("LnjjzlJ+L5c", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8NpCommId5ClearEv); + LIB_FUNCTION("1TjLUwirok0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8NpCommId5ParseEPS1_PKc); + LIB_FUNCTION("UrJocI5M8GY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8NpCommId5ParseEPS1_PKcm); + LIB_FUNCTION("To1XvNOzjo0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8NpCommIdC1ERK20SceNpCommunicationId); + LIB_FUNCTION("N6SkkX1GkFU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8NpCommIdC1ERKS1_); + LIB_FUNCTION("AQyiYChNI0c", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8NpCommIdC1Ev); + LIB_FUNCTION("WywlusFissg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8NpCommIdC2ERK20SceNpCommunicationId); + LIB_FUNCTION("rB0oqLSjH6g", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8NpCommIdC2ERKS1_); + LIB_FUNCTION("BBtBjx9-bMI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8NpCommIdC2Ev); + LIB_FUNCTION("XeCZTzqIk2k", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8NpCommIdD0Ev); + LIB_FUNCTION("EPJbX73AVeU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8NpCommIdD1Ev); + LIB_FUNCTION("hP18CDS6eBU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8NpCommIdD2Ev); + LIB_FUNCTION("5WuiSZkU3mg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8Selector4InitEPKc); + LIB_FUNCTION("2HkOOhiWK3M", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8SelectorD0Ev); + LIB_FUNCTION("asZdig1mPlA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8SelectorD1Ev); + LIB_FUNCTION("PA9VYFAVKIE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8SelectorD2Ev); + LIB_FUNCTION("2YbS+GhInZQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8WorkItem10SetPendingEv); + LIB_FUNCTION("XUCjhejJvPc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8WorkItem10SetRunningEv); + LIB_FUNCTION("-91vFSqiuKw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8WorkItem11SetFinishedEi); + LIB_FUNCTION("zepqHjfGe0M", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8WorkItem14FinishCallbackEv); + LIB_FUNCTION("3rGzxcMK-Mg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8WorkItem15RemoveFromQueueEv); + LIB_FUNCTION("Oq5aepLkEWg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8WorkItem6CancelEi); + LIB_FUNCTION("gnh2cpEgSS8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8WorkItem9BindQueueEPNS0_9WorkQueueEi); + LIB_FUNCTION("HldN461O2Dw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8WorkItemC2EPKc); + LIB_FUNCTION("Y-I66cSNp+A", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8WorkItemD0Ev); + LIB_FUNCTION("dnwItoXLoy4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8WorkItemD1Ev); + LIB_FUNCTION("ga4OW9MGahU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np8WorkItemD2Ev); + LIB_FUNCTION("8i-vOVRVt5w", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9EventFlag3SetEm); + LIB_FUNCTION("vhbvgH7wWiE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9EventFlag4ctorEv); + LIB_FUNCTION("5nM4Yy92Qwg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9EventFlag4dtorEv); + LIB_FUNCTION("5Wy+JxpCBxg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9EventFlag4OpenEPKc); + LIB_FUNCTION("37Rd2JS+FCM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9EventFlag4PollEmjPm); + LIB_FUNCTION("1s+c3SG0WYc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9EventFlag4WaitEmjPmj); + LIB_FUNCTION("03UlDLFsTfw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9EventFlag5ClearEm); + LIB_FUNCTION("wJ-k9+UShJg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9EventFlag6CancelEm); + LIB_FUNCTION("amFi-Av19hU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9EventFlag6CreateEPKcj); + LIB_FUNCTION("QlaBcxSFPZI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9EventFlag7DestroyEv); + LIB_FUNCTION("cMOgkE2M2e8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9EventFlagC1Ev); + LIB_FUNCTION("Uv1IQpTWecw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9EventFlagC2Ev); + LIB_FUNCTION("uHOOEbuzjEQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9EventFlagD0Ev); + LIB_FUNCTION("WWW4bvT-rSw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9EventFlagD1Ev); + LIB_FUNCTION("RpWWfCEs9xA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9EventFlagD2Ev); + LIB_FUNCTION("jDDvll2aQpQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9HttpTrans10SetTimeoutEPKNS1_12TimeoutParamE); + LIB_FUNCTION("+hKyaJJCE+0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9HttpTrans11SendRequestEPNS0_6HandleEPKvm); + LIB_FUNCTION("EhLaOnhdcXo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9HttpTrans12RecvResponseEPNS0_6HandleEPvmPm); + LIB_FUNCTION("fV+Q5a6p+zQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9HttpTrans12SkipResponseEPNS0_6HandleE); + LIB_FUNCTION("Qfsmqs-bHeY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9HttpTrans16AddRequestHeaderEPKcS3_); + LIB_FUNCTION("6bYsRATI3tQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9HttpTrans16SetRequestHeaderEPKcS3_); + LIB_FUNCTION("WoFp77mNyw0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9HttpTrans21GetResponseStatusCodeEPNS0_6HandleEPi); + LIB_FUNCTION("RJOlguLEy-E", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9HttpTrans21SetRequestContentTypeEPKc); + LIB_FUNCTION("ws3x3yjUyeE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9HttpTrans23SetRequestContentLengthEm); + LIB_FUNCTION("YW09CP0Vrtw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9HttpTrans24GetResponseContentLengthEPNS0_6HandleEPbPm); + LIB_FUNCTION("JEYp0T1VC58", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9HttpTrans4InitERKNS0_12HttpTemplateEPNS0_18HttpConnectionPoolEiPKcm); + LIB_FUNCTION( + "O+FeLkOM7w0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9HttpTrans4InitERKNS0_12HttpTemplateEPNS0_18HttpConnectionPoolEiPKcS8_tS8_m); + LIB_FUNCTION("aWo+7jvpllY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9HttpTrans4ReadEPNS0_6HandleEPvmPm); + LIB_FUNCTION("cocNRQpq+NA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9HttpTrans5WriteEPNS0_6HandleEPKvmPm); + LIB_FUNCTION("2e9GLlHTKA4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9HttpTrans7DestroyEv); + LIB_FUNCTION("sqNxD6H5ZOQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9HttpTransC1EP16SceNpAllocatorEx); + LIB_FUNCTION("HEeXBdgvJI4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9HttpTransC2EP16SceNpAllocatorEx); + LIB_FUNCTION("Pe9fHKX7krE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9HttpTransD0Ev); + LIB_FUNCTION("ls8yIODZmzc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9HttpTransD1Ev); + LIB_FUNCTION("GSVe-aaTiEg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9HttpTransD2Ev); + LIB_FUNCTION("4cIJxNKQK5g", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9JsonArray12AddItemArrayEPPS1_); + LIB_FUNCTION("cWsZswBMjqg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9JsonArray5ClearEv); + LIB_FUNCTION("aCZjveAsynw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9JsonValue12GetItemValueEi); + LIB_FUNCTION("aIV+HI6llz4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9JsonValue13GetFieldValueEiPPKc); + LIB_FUNCTION("BDie4qEtKuA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9JsonValue13GetFieldValueEPKc); + LIB_FUNCTION("LotC9rVP3Lo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9JsonValueD0Ev); + LIB_FUNCTION("hBuLbn3mGBw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9JsonValueD1Ev); + LIB_FUNCTION("FfSNfBmn+K8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9JsonValueD2Ev); + LIB_FUNCTION("PsP6LYRZ7Dc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9LocalFile4ReadEPNS0_6HandleEPvmPm); + LIB_FUNCTION("Flyyg6hzUOM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9LocalFile4SeekEliPl); + LIB_FUNCTION("YtvLEI7uZRI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9LocalFile4SyncEv); + LIB_FUNCTION("9q+h2q5YprU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9LocalFile5CloseEv); + LIB_FUNCTION("0xL7AwgxphE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9LocalFile5WriteEPNS0_6HandleEPKvmPm); + LIB_FUNCTION("haDbtVOmaao", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9LocalFile6RemoveEPKc); + LIB_FUNCTION("Sgo7wy9okFI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9LocalFile8TruncateEl); + LIB_FUNCTION("QWlZu1JZOww", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9LocalFileC1Ev); + LIB_FUNCTION("HP4jsVYqBKg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9LocalFileC2Ev); + LIB_FUNCTION("-n0CR0QxhnY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9LocalFileD0Ev); + LIB_FUNCTION("3eoh4hjcYag", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9LocalFileD1Ev); + LIB_FUNCTION("s-C88O6Y8iU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9LocalFileD2Ev); + LIB_FUNCTION("euE6Yo5hkrY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9NpTitleId5BuildERKS1_Pcm); + LIB_FUNCTION("a76a3D9Adts", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9NpTitleId5ClearEv); + LIB_FUNCTION("4O8lYvForpk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9NpTitleId5ParseEPS1_PKc); + LIB_FUNCTION("-swgMjedLUQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9NpTitleId5ParseEPS1_PKcm); + LIB_FUNCTION("Fcvdbqpwpnw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9NpTitleIdC1ERK12SceNpTitleId); + LIB_FUNCTION("wd+YWDKMTQE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9NpTitleIdC1ERKS1_); + LIB_FUNCTION("-Ja2aT6A3fg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9NpTitleIdC1Ev); + LIB_FUNCTION("9n60S+t4Cxs", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9NpTitleIdC2ERK12SceNpTitleId); + LIB_FUNCTION("IefAhNUAivM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9NpTitleIdC2ERKS1_); + LIB_FUNCTION("OL7DU1kkm+4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9NpTitleIdC2Ev); + LIB_FUNCTION("rFcQRK+GMcQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9NpTitleIdD0Ev); + LIB_FUNCTION("TGJ5bE+Fb1s", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9NpTitleIdD1Ev); + LIB_FUNCTION("XKVRBLdw+7I", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9NpTitleIdD2Ev); + LIB_FUNCTION("zurkNUps5o8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9RefObject6AddRefEv); + LIB_FUNCTION("5tYi1l9CXD0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9RefObject7ReleaseEv); + LIB_FUNCTION("brUrttJp6MM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9RefObjectC1Ev); + LIB_FUNCTION("JRtw5pROOiM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9RefObjectC2Ev); + LIB_FUNCTION("8DrClRz7Z2U", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9RefObjectD0Ev); + LIB_FUNCTION("lPQzOhwPjuw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9RefObjectD1Ev); + LIB_FUNCTION("417JucZaE3g", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9RefObjectD2Ev); + LIB_FUNCTION("EFffsPLsOio", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9Semaphore4OpenEPKc); + LIB_FUNCTION("hQLw6eE4O44", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9Semaphore4WaitEj); + LIB_FUNCTION("wcOCedFKan4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9Semaphore6CreateEiiPKc); + LIB_FUNCTION("b7qnGORh+H4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9Semaphore6SignalEv); + LIB_FUNCTION("Es-CwSVnalY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9Semaphore7DestroyEv); + LIB_FUNCTION("Tuth2BRl4x0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9SemaphoreC1Ev); + LIB_FUNCTION("8k1rNqvczTc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9SemaphoreC2Ev); + LIB_FUNCTION("S6luQz76AQ4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9SemaphoreD0Ev); + LIB_FUNCTION("nW9XeX3eokI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9SemaphoreD1Ev); + LIB_FUNCTION("OukNoRur97E", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9SemaphoreD2Ev); + LIB_FUNCTION("F2umEBpQFHc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9WorkQueue11GetItemByIdEi); + LIB_FUNCTION("wM4q1JMisvA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9WorkQueue15GetFinishedItemENS0_14WorkItemStatusE); + LIB_FUNCTION("UYAD7sUQcYU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9WorkQueue16WorkItemFinishedEPNS0_8WorkItemEi); + LIB_FUNCTION("-9cU3y6rXVM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9WorkQueue17ProcFinishedItemsENS0_14WorkItemStatusE); + LIB_FUNCTION("ovc4ZvD0YjY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9WorkQueue18RemoveFinishedItemEPNS0_8WorkItemE); + LIB_FUNCTION("vPju3W13byw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9WorkQueue18WaitForPendingItemEPPNS0_8WorkItemEPb); + LIB_FUNCTION("XMIv42L5bEA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9WorkQueue4ctorEv); + LIB_FUNCTION("wESN-qrVhOU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9WorkQueue4dtorEv); + LIB_FUNCTION("+dGO+GS2ZXQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9WorkQueue4InitEPKcimm); + LIB_FUNCTION("U0YoWwgg8aI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9WorkQueue4InitEPKNS0_6Thread5ParamE); + LIB_FUNCTION("4DE+nnCVRPA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9WorkQueue4StopEv); + LIB_FUNCTION("VnQolo6vTr4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9WorkQueue5StartEv); + LIB_FUNCTION("laqZEULcfgw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9WorkQueue6CancelEii); + LIB_FUNCTION("CznMfhTIvVY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9WorkQueue6IsInitEv); + LIB_FUNCTION("NeopmYshD0U", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9WorkQueue7DestroyEv); + LIB_FUNCTION("KQSxXJBepQ4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9WorkQueue7EnqueueEiPNS0_8WorkItemE); + LIB_FUNCTION("zmOmSLnqlBQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9WorkQueue9CancelAllEi); + LIB_FUNCTION("eTy3L1azX4E", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9WorkQueue9IsRunningEv); + LIB_FUNCTION("X6NVkdpRnog", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9WorkQueueC1Ev); + LIB_FUNCTION("p+bd65J177I", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9WorkQueueC2Ev); + LIB_FUNCTION("uyNO0GnFhPw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9WorkQueueD0Ev); + LIB_FUNCTION("1QFKnDJxk3A", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9WorkQueueD1Ev); + LIB_FUNCTION("AIDhc3KCK7w", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2np9WorkQueueD2Ev); + LIB_FUNCTION("XLpPRMl5jro", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npeqERK10SceRtcTickRKNS0_4TimeE); + LIB_FUNCTION("6jHOZ6fItFU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npeqERK12SceNpTitleIdRKNS0_9NpTitleIdE); + LIB_FUNCTION("i+xzwYeeEtk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npeqERK16SceNpTitleSecretRKNS0_13NpTitleSecretE); + LIB_FUNCTION("ZWZ9KqoIvQY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npeqERK20SceNpCommunicationIdRKNS0_8NpCommIdE); + LIB_FUNCTION("Vsj50ZwNUFM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npeqERKNS0_13NpTitleSecretERK16SceNpTitleSecret); + LIB_FUNCTION("WM5DPO-LryU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npeqERKNS0_13NpTitleSecretES3_); + LIB_FUNCTION("ps246w9eXI8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npeqERKNS0_4TimeERK10SceRtcTick); + LIB_FUNCTION("UVLmT9lzRYA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npeqERKNS0_4TimeES3_); + LIB_FUNCTION("WaNQzws1ATU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npeqERKNS0_8NpCommIdERK20SceNpCommunicationId); + LIB_FUNCTION("E-mYAG-aa1A", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npeqERKNS0_8NpCommIdES3_); + LIB_FUNCTION("FmDmhB16wwE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npeqERKNS0_9NpTitleIdERK12SceNpTitleId); + LIB_FUNCTION("niXN2N4o3yY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npeqERKNS0_9NpTitleIdES3_); + LIB_FUNCTION("gKruhA35EXQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npgeERK10SceRtcTickRKNS0_4TimeE); + LIB_FUNCTION("1mnghWFX0wQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npgeERKNS0_4TimeERK10SceRtcTick); + LIB_FUNCTION("svAQxJ3yow4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npgeERKNS0_4TimeES3_); + LIB_FUNCTION("oVZ6spoeeN0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npgtERK10SceRtcTickRKNS0_4TimeE); + LIB_FUNCTION("snloJp6qQCc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npgtERKNS0_4TimeERK10SceRtcTick); + LIB_FUNCTION("EFES6UR65oU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npgtERKNS0_4TimeES3_); + LIB_FUNCTION("UIrMxV07mL0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npleERK10SceRtcTickRKNS0_4TimeE); + LIB_FUNCTION("cAeFZE72SXU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npleERKNS0_4TimeERK10SceRtcTick); + LIB_FUNCTION("ttA9TcO06uA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npleERKNS0_4TimeES3_); + LIB_FUNCTION("rVtImV4rxSA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npltERK10SceRtcTickRKNS0_4TimeE); + LIB_FUNCTION("nVB1Nsjwpj0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npltERKNS0_4TimeERK10SceRtcTick); + LIB_FUNCTION("d0zSLZMER34", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npltERKNS0_4TimeES3_); + LIB_FUNCTION("MVY+jtY-WiQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npneERK10SceRtcTickRKNS0_4TimeE); + LIB_FUNCTION("tDs31ASQGV8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npneERK12SceNpTitleIdRKNS0_9NpTitleIdE); + LIB_FUNCTION("OwsjgCQyZUI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npneERK16SceNpTitleSecretRKNS0_13NpTitleSecretE); + LIB_FUNCTION("O5QkjyiPM4c", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npneERK20SceNpCommunicationIdRKNS0_8NpCommIdE); + LIB_FUNCTION("7b5y1XSa+KQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npneERKNS0_13NpTitleSecretERK16SceNpTitleSecret); + LIB_FUNCTION("zbliTwZKRyU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npneERKNS0_13NpTitleSecretES3_); + LIB_FUNCTION("yXMjXN--3rY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npneERKNS0_4TimeERK10SceRtcTick); + LIB_FUNCTION("cnoM7EjlLe4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npneERKNS0_4TimeES3_); + LIB_FUNCTION("SM7OEf11LCA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npneERKNS0_8NpCommIdERK20SceNpCommunicationId); + LIB_FUNCTION("QQCqBHk79sI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npneERKNS0_8NpCommIdES3_); + LIB_FUNCTION("ONgEITYl9mA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npneERKNS0_9NpTitleIdERK12SceNpTitleId); + LIB_FUNCTION("9pp9-dwqIHM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZN3sce2npneERKNS0_9NpTitleIdES3_); + LIB_FUNCTION("KyDWNwpREH4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZNK3sce2np10Cancelable6IsInitEv); + LIB_FUNCTION("VI8AHrfLdqY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZNK3sce2np10EventQueue6IsInitEv); + LIB_FUNCTION("jxPY-0x8e-M", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZNK3sce2np10EventQueue7IsEmptyEv); + LIB_FUNCTION("COxqqhvLSyM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZNK3sce2np10JsonNumber5CloneEP16SceNpAllocatorEx); + LIB_FUNCTION("m+dAaZ5pyO4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZNK3sce2np10JsonNumber6GetNumEPcm); + LIB_FUNCTION("Sk8AdNQUDm8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZNK3sce2np10JsonNumber6GetNumEPi); + LIB_FUNCTION("nHgo2VpnCB8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZNK3sce2np10JsonNumber6GetNumEPj); + LIB_FUNCTION("Agsyrf4L8uA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZNK3sce2np10JsonNumber6GetNumEPl); + LIB_FUNCTION("P2cGbJ5nD1w", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZNK3sce2np10JsonNumber6GetNumEPm); + LIB_FUNCTION("EcboqmwkrMY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZNK3sce2np9JsonArray5CloneEP16SceNpAllocatorEx); + LIB_FUNCTION("JcAsZlyr3Mo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZNK3sce2np9JsonValue12GetItemValueEi); + LIB_FUNCTION("XZTZqqSVGlY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZNK3sce2np9NpTitleId7IsEmptyEv); + LIB_FUNCTION("sreH33xjV0A", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZNK3sce2np9Semaphore6IsInitEv); + LIB_FUNCTION("QwO4sr6XzSY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZThn16_N3sce2np10MemoryFile5WriteEPNS0_6HandleEPKvmPm); + LIB_FUNCTION("ojBk-UJxzWw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZThn16_N3sce2np10MemoryFileD0Ev); + LIB_FUNCTION("8S1mWU-N9kM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZThn16_N3sce2np10MemoryFileD1Ev); + LIB_FUNCTION("eRlqlofFKYg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZThn16_N3sce2np9HttpTrans5WriteEPNS0_6HandleEPKvmPm); + LIB_FUNCTION("zWIFe+d77PU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZThn16_N3sce2np9HttpTransD0Ev); + LIB_FUNCTION("GG1Y+vBUkdU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZThn16_N3sce2np9HttpTransD1Ev); + LIB_FUNCTION("+3ySpB1buMs", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZThn16_N3sce2np9LocalFile5WriteEPNS0_6HandleEPKvmPm); + LIB_FUNCTION("hSnLhjGefsU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZThn16_N3sce2np9LocalFileD0Ev); + LIB_FUNCTION("q3s6++iIzjE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZThn16_N3sce2np9LocalFileD1Ev); + LIB_FUNCTION("E6GYo9uzjds", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZThn8_N3sce2np10MemoryFile4ReadEPNS0_6HandleEPvmPm); + LIB_FUNCTION("7bzUdBtIQhE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZThn8_N3sce2np10MemoryFileD0Ev); + LIB_FUNCTION("lNs-oTKpG9s", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZThn8_N3sce2np10MemoryFileD1Ev); + LIB_FUNCTION("xDrWJARfCbk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZThn8_N3sce2np6Handle10CancelImplEi); + LIB_FUNCTION("YqMS-iAjFY8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZThn8_N3sce2np6HandleD0Ev); + LIB_FUNCTION("lUsG1QfgVN4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZThn8_N3sce2np6HandleD1Ev); + LIB_FUNCTION("G+v692ul7MA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZThn8_N3sce2np9HttpTrans4ReadEPNS0_6HandleEPvmPm); + LIB_FUNCTION("sGhCzaJf+jQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZThn8_N3sce2np9HttpTransD0Ev); + LIB_FUNCTION("PUqCtFwnNvA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZThn8_N3sce2np9HttpTransD1Ev); + LIB_FUNCTION("NtsHoOq2ao4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZThn8_N3sce2np9LocalFile4ReadEPNS0_6HandleEPvmPm); + LIB_FUNCTION("Gh35wbyg4U8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZThn8_N3sce2np9LocalFileD0Ev); + LIB_FUNCTION("kD3l0P19Wzg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZThn8_N3sce2np9LocalFileD1Ev); + LIB_FUNCTION("IvTsS4VJq1w", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZTVN3sce2np10JsonNumberE); + LIB_FUNCTION("aLGD1kOLQXE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZTVN3sce2np10JsonObjectE); + LIB_FUNCTION("1At86OClqtY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZTVN3sce2np10JsonStringE); + LIB_FUNCTION("jsHe99x6l0w", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZTVN3sce2np8JsonBoolE); + LIB_FUNCTION("A742Lh-FnVE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZTVN3sce2np8JsonNullE); + LIB_FUNCTION("FfXZGW1TMvo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZTVN3sce2np8SelectorE); + LIB_FUNCTION("0qrLVqNUn2Y", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZTVN3sce2np9JsonArrayE); + LIB_FUNCTION("S8TLtKfZCfc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + _ZTVN3sce2np9JsonValueE); + LIB_FUNCTION("MWPOkqzYss0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpAllocateKernelMemoryNoAlignment); + LIB_FUNCTION("gMlY6eewr-c", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpAllocateKernelMemoryWithAlignment); + LIB_FUNCTION("jGF+MaB4b-M", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpArchInit); + LIB_FUNCTION("UskWpVWxSvg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpArchTerm); + LIB_FUNCTION("+9+kKMY9YIw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpAtomicCas32); + LIB_FUNCTION("Yohe0MMDfj0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpAtomicDec32); + LIB_FUNCTION("pfJgSA4jO3M", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpAtomicInc32); + LIB_FUNCTION("l67qBmMmKP4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpBase64Decoder); + LIB_FUNCTION("pu39pU8UgCo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpBase64Encoder); + LIB_FUNCTION("a5IfPlpchXI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpBase64GetDecodeSize); + LIB_FUNCTION("moGcgMNTHvQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpBase64UrlDecoder); + LIB_FUNCTION("IeNj+OcWgU8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpBase64UrlEncoder); + LIB_FUNCTION("7BjZKcN+oZ4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpBase64UrlGetDecodeSize); + LIB_FUNCTION("9+m5nRdJ-wQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpCalloutInitCtx); + LIB_FUNCTION("fClnlkZmA6k", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpCalloutStartOnCtx); + LIB_FUNCTION("lpr66Gby8dQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpCalloutStartOnCtx64); + LIB_FUNCTION("in19gH7G040", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpCalloutStopOnCtx); + LIB_FUNCTION("AqJ4xkWsV+I", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpCalloutTermCtx); + LIB_FUNCTION("kb2thTuS8t8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpCancelEventFlag); + LIB_FUNCTION("9pLoHoPMxeg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpClearEventFlag); + LIB_FUNCTION("+nmn+Z0nWDo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpCloseEventFlag); + LIB_FUNCTION("8hPzfjZzV88", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpCloseSema); + LIB_FUNCTION("i8UmXTSq7N4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpCmpNpId); + LIB_FUNCTION("TcwEFnakiSc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpCmpNpIdInOrder); + LIB_FUNCTION("dj+O5aD2a0Q", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpCmpOnlineId); + LIB_FUNCTION("1a+iY5YUJcI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpCondDestroy); + LIB_FUNCTION("q2tsVO3lM4A", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpCondInit); + LIB_FUNCTION("uMJFOA62mVU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpCondSignal); + LIB_FUNCTION("bsjWg59A7aE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpCondSignalAll); + LIB_FUNCTION("bAHIOyNnx5Y", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpCondSignalTo); + LIB_FUNCTION("ss2xO9IJxKQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpCondTimedwait); + LIB_FUNCTION("fZShld2PQ7w", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpCondWait); + LIB_FUNCTION("6jFWpAfqAcc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpCreateEventFlag); + LIB_FUNCTION("LHZtCT2W1Pw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpCreateSema); + LIB_FUNCTION("fhJ5uKzcn0w", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpCreateThread); + LIB_FUNCTION("90pmGqDK4BI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpDbgAssignDebugId); + LIB_FUNCTION("Etq15-l9yko", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpDbgDumpBinary); + LIB_FUNCTION("ZaKa5x61hGA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpDbgDumpText); + LIB_FUNCTION("sjnIeFCuTD0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpDeleteEventFlag); + LIB_FUNCTION("xPrF2nGPBXQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpDeleteSema); + LIB_FUNCTION("OQTweRLgFr8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpEventGetCurrentNetworkTick); + LIB_FUNCTION("vjwlDmsGtME", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpFreeKernelMemory); + LIB_FUNCTION("QmDEFikd3VA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpGetNavSdkVersion); + LIB_FUNCTION("sXVQUIGmk2U", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpGetPlatformType); + LIB_FUNCTION("Z3mnqcGmf8E", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpGetProcessId); + LIB_FUNCTION("pJlGhXEt5CU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpGetRandom); + LIB_FUNCTION("Pglk7zFj0DI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpGetSdkVersion); + LIB_FUNCTION("ljqnF0hmLjo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpGetSdkVersionUInt); + LIB_FUNCTION("PVVsRmMkO1g", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpGetSystemClockUsec); + LIB_FUNCTION("-gN6uE+zWng", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpGlobalHeapGetAllocator); + LIB_FUNCTION("VUHUasztbUY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpGlobalHeapGetAllocatorEx); + LIB_FUNCTION("P4YpPziLBd4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpGlobalHeapGetAllocatorExPtr); + LIB_FUNCTION("DI5n4aOdxmk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpGlobalHeapGetAllocatorPtr); + LIB_FUNCTION("wVdn78HKc30", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpHeapDestroy); + LIB_FUNCTION("lvek8w7yqyE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpHeapGetAllocator); + LIB_FUNCTION("2jdHoPpS+W0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpHeapGetStat); + LIB_FUNCTION("B+yGIX1+BTI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpHeapInit); + LIB_FUNCTION("evz0-93ucJc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpHeapShowStat); + LIB_FUNCTION("Hvpr+otU4bo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpHexToInt); + LIB_FUNCTION("5y0wMPQkaeU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpInt32ToStr); + LIB_FUNCTION("HoPC33siDD4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpInt64ToStr); + LIB_FUNCTION("G6qytFoBJ-w", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpIntGetPlatformType); + LIB_FUNCTION("fY4XQoA20i8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpIntIsOnlineIdString); + LIB_FUNCTION("hkeX9iuCwlI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpIntIsValidOnlineId); + LIB_FUNCTION("X6emt+LbSEI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpIntSetPlatformType); + LIB_FUNCTION("TWPY1x1Atys", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpIntToHex); + LIB_FUNCTION("kgDwlmy78k0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpIpc2ClientInit); + LIB_FUNCTION("CI2p6Viee9w", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpIpc2ClientTerm); + LIB_FUNCTION("EjMsfO3GCIA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpJoinThread); + LIB_FUNCTION("vJGDnNh4I0g", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpJsonParse); + LIB_FUNCTION("RgfCYkjW7As", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpJsonParseBuf); + LIB_FUNCTION("SnAdybtBK3o", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpJsonParseBufInit); + LIB_FUNCTION("p5ZkSMRR7AU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpJsonParseEx); + LIB_FUNCTION("nhgjiwPUIzI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpJsonParseExInit); + LIB_FUNCTION("teVnFAL6GNY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpJsonParseInit); + LIB_FUNCTION("zNb6IxegrCE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpLwCondDestroy); + LIB_FUNCTION("++eqYdzB8Go", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpLwCondInit); + LIB_FUNCTION("Xkn6VoN-wuQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpLwCondSignal); + LIB_FUNCTION("FJ4DCt8VzVE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpLwCondSignalAll); + LIB_FUNCTION("Bwi+EP8VQ+g", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpLwCondSignalTo); + LIB_FUNCTION("ExeLuE3EQCQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpLwCondWait); + LIB_FUNCTION("4zxevggtYrQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpLwMutexDestroy); + LIB_FUNCTION("1CiXI-MyEKs", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpLwMutexInit); + LIB_FUNCTION("18j+qk6dRwk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpLwMutexLock); + LIB_FUNCTION("hp0kVgu5Fxw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpLwMutexTryLock); + LIB_FUNCTION("CQG2oyx1-nM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpLwMutexUnlock); + LIB_FUNCTION("dfXSH2Tsjkw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpMemoryHeapDestroy); + LIB_FUNCTION("FaMNvjMA6to", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpMemoryHeapGetAllocator); + LIB_FUNCTION("xHAiSVEEjSI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpMemoryHeapGetAllocatorEx); + LIB_FUNCTION("kZizwrFvWZY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpMemoryHeapInit); + LIB_FUNCTION("lQ11BpMM4LU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpMutexDestroy); + LIB_FUNCTION("uEwag-0YZPc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpMutexInit); + LIB_FUNCTION("r9Bet+s6fKc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpMutexLock); + LIB_FUNCTION("DuslmoqQ+nk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpMutexTryLock); + LIB_FUNCTION("oZyb9ktuCpA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpMutexUnlock); + LIB_FUNCTION("5DkyduAF2rs", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpOpenEventFlag); + LIB_FUNCTION("-blITIdtUd0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpOpenSema); + LIB_FUNCTION("ZoXUrTiwKNw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpPanic); + LIB_FUNCTION("9YmBJ8KF9eI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpPollEventFlag); + LIB_FUNCTION("xmF0yIF4iXc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpPollSema); + LIB_FUNCTION("VMjIo2Z-aW0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpRtcConvertToPosixTime); + LIB_FUNCTION("W0YWLVDndx0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpRtcFormatRFC3339); + LIB_FUNCTION("LtkeQwMIEWY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpRtcParseRFC3339); + LIB_FUNCTION("0lZHbA-HRD0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpServerErrorJsonGetErrorCode); + LIB_FUNCTION("cRabutqUG7c", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpServerErrorJsonMultiGetErrorCode); + LIB_FUNCTION("WSQxnAVLKgw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpServerErrorJsonParse); + LIB_FUNCTION("UbStlMKTBeU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpServerErrorJsonParseInit); + LIB_FUNCTION("hbe+DdooIi4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpServerErrorJsonParseMultiInit); + LIB_FUNCTION("29ftOGIrUCo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpSetEventFlag); + LIB_FUNCTION("m9JzZSoDVFY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpSetPlatformType); + LIB_FUNCTION("-W28+9p1CKI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpSignalSema); + LIB_FUNCTION("i5TP5NLmkoQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpStrBuildHex); + LIB_FUNCTION("ivnnssCwjGI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpStrcpyToBuf); + LIB_FUNCTION("PHrpHMSU8Cs", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpStrncpyToBuf); + LIB_FUNCTION("h1SWCcBdImo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpStrnParseHex); + LIB_FUNCTION("DUHzVPNlugg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpStrParseHex); + LIB_FUNCTION("fElyBSn-l24", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpStrToInt32); + LIB_FUNCTION("CwqYdG4TrjA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpStrToInt64); + LIB_FUNCTION("uj86YxCYid0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpStrToUInt32); + LIB_FUNCTION("Ted2YU9lv94", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpStrToUInt64); + LIB_FUNCTION("yvaNTRiKXmo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpThreadGetId); + LIB_FUNCTION("rRN89jBArEM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpUInt32ToStr); + LIB_FUNCTION("QjNUYQbGoHA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpUInt64ToStr); + LIB_FUNCTION("Gh74vNl06sg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpUserGetUserIdList); + LIB_FUNCTION("N3tAHlBnowE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpUtilBuildTitleId); + LIB_FUNCTION("4mEAk-UKVNw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpUtilCanonicalizeNpIdForPs4); + LIB_FUNCTION("N3FB4r8JoRE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpUtilCanonicalizeNpIdForPsp2); + LIB_FUNCTION("xPRHNaD3kTc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpUtilCmpAccountId); + LIB_FUNCTION("owm52JoZ8uc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpUtilGetDateSetAuto); + LIB_FUNCTION("1Gfhi+tZ9IE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpUtilGetDbgCommerce); + LIB_FUNCTION("kBON3bAtfGs", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpUtilGetEnv); + LIB_FUNCTION("MUj0IV6XFGs", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpUtilGetFakeDisplayNameMode); + LIB_FUNCTION("O86rgZ2azfg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpUtilGetFakeRateLimit); + LIB_FUNCTION("FrxliFYAO8Y", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpUtilGetIgnoreNpTitleId); + LIB_FUNCTION("GRvK1ZE+FEQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpUtilGetNpDebug); + LIB_FUNCTION("OFiFmfsADas", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpUtilGetNpLanguageCode); + LIB_FUNCTION("X9CqyP164Hc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpUtilGetNpLanguageCode2); + LIB_FUNCTION("Fxux7Ob+Ynk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpUtilGetNpLanguageCode2Str); + LIB_FUNCTION("RfiA17kV+xs", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpUtilGetNpLanguageCodeStr); + LIB_FUNCTION("OA8f3KF9JsM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpUtilGetNpTestPatch); + LIB_FUNCTION("KCk4OGu8+sc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpUtilGetNthChar); + LIB_FUNCTION("fB5hE65pzbU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpUtilGetShareTitleCheck); + LIB_FUNCTION("SXUNKr9Zkv0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpUtilGetSystemLanguage); + LIB_FUNCTION("AjzLvR0g5Zs", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpUtilGetTrcNotify); + LIB_FUNCTION("pmHBFJyju9E", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpUtilGetWebApi2FakeRateLimit); + LIB_FUNCTION("ZRxKp9vjcNc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpUtilGetWebApi2FakeRateLimitTarget); + LIB_FUNCTION("4CqfNm3pisU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpUtilGetWebTraceSetting); + LIB_FUNCTION("ajoqGz0D9Dw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpUtilHttpUrlEncode); + LIB_FUNCTION("458yjI+OECI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpUtilJidToNpId); + LIB_FUNCTION("EftEB4kmkSg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpUtilJsonEscape); + LIB_FUNCTION("vj04qzp7uKY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpUtilJsonGetOneChar); + LIB_FUNCTION("4YJ5gYtRAAE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpUtilJsonUnescape); + LIB_FUNCTION("KyB1IAY2BiU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpUtilNpIdToJid); + LIB_FUNCTION("c+ssxRf1Si0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpUtilNumChars); + LIB_FUNCTION("oz2SlXNAnuI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpUtilParseJid); + LIB_FUNCTION("EfnfZtjjyR0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpUtilParseTitleId); + LIB_FUNCTION("okX7IjW0QsI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpUtilSerializeJid); + LIB_FUNCTION("5bBPLZV49kY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpUtilXmlEscape); + LIB_FUNCTION("Ls4eWDrbNmg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, + sceNpUtilXmlGetOneChar); + LIB_FUNCTION("+0rj9KhmYb0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpUtilXmlUnescape); + LIB_FUNCTION("ZbdPHUm7jOY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpWaitEventFlag); + LIB_FUNCTION("6adrFGe2cpU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpWaitSema); + LIB_FUNCTION("fEcrs9UPPyo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpXmlParse); + LIB_FUNCTION("MCLGkfBmw4c", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, sceNpXmlParseInit); + LIB_FUNCTION("AP1XjC3ZZt8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_00FD578C2DD966DF); + LIB_FUNCTION("ATGi6oBon0w", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_0131A2EA80689F4C); + LIB_FUNCTION("AUQ8VIY73SA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_01443C54863BDD20); + LIB_FUNCTION("AbxVvcXAra0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_01BC55BDC5C0ADAD); + LIB_FUNCTION("AdHs9XUPQOg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_01D1ECF5750F40E8); + LIB_FUNCTION("AgpHmnT1+6w", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_020A479A74F5FBAC); + LIB_FUNCTION("Akr14dlHKrU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_024AF5E1D9472AB5); + LIB_FUNCTION("AnxdSIcTprM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_027C5D488713A6B3); + LIB_FUNCTION("Av6dlMaFg1U", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_02FE9D94C6858355); + LIB_FUNCTION("BB808ccNFcE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_041F34F1C70D15C1); + LIB_FUNCTION("BTCx0nYRQkg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_0530B1D276114248); + LIB_FUNCTION("Bl2qFOnHOtk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_065DAA14E9C73AD9); + LIB_FUNCTION("Bq-05dBCvD4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_06AFF4E5D042BC3E); + LIB_FUNCTION("Bu42kpn3OZc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_06EE369299F73997); + LIB_FUNCTION("B8ktn412thc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_07C92D9F8D76B617); + LIB_FUNCTION("B+kRdJjx5L8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_07E9117498F1E4BF); + LIB_FUNCTION("CPPgrzZk8nU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_08F3E0AF3664F275); + LIB_FUNCTION("Cpk3wB7yE3U", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_0A9937C01EF21375); + LIB_FUNCTION("CsvmrMujh20", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_0ACBE6ACCBA3876D); + LIB_FUNCTION("CuB9M1RRDOY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_0AE07D3354510CE6); + LIB_FUNCTION("Cuw8NCrme3w", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_0AEC3C342AE67B7C); + LIB_FUNCTION("CzGEIMEefCM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_0B318420C11E7C23); + LIB_FUNCTION("C7bDewPzXYk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_0BB6C37B03F35D89); + LIB_FUNCTION("C76Kms3ZD98", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_0BBE8A9ACDD90FDF); + LIB_FUNCTION("DHtikF4iTpw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_0C7B62905E224E9C); + LIB_FUNCTION("DTWRMRckGvk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_0D35913117241AF9); + LIB_FUNCTION("DV7pXO7Yeac", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_0D5EE95CEED879A7); + LIB_FUNCTION("DW+ySyerHaI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_0D6FB24B27AB1DA2); + LIB_FUNCTION("DegDLVNKxBw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_0DE8032D534AC41C); + LIB_FUNCTION("DfTMqdyp50I", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_0DF4CCA9DCA9E742); + LIB_FUNCTION("DnRJsdPZjAE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_0E7449B1D3D98C01); + LIB_FUNCTION("DncJS3dQyzc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_0E77094B7750CB37); + LIB_FUNCTION("Dsqzl7bVBgM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_0ECAB397B6D50603); + LIB_FUNCTION("Dx3h0eraKUg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_0F1DE1D1EADA2948); + LIB_FUNCTION("D4r++h0mvxo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_0F8AFEFA1D26BF1A); + LIB_FUNCTION("EYgXEFYqa60", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_11881710562A6BAD); + LIB_FUNCTION("Ea-Yi70McNs", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_11AFD88BBD0C70DB); + LIB_FUNCTION("EecEowpLiHc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_11E704A30A4B8877); + LIB_FUNCTION("ElAUhCRS+Us", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_125014842452F94B); + LIB_FUNCTION("Em8AceEcrEY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_126F0071E11CAC46); + LIB_FUNCTION("EpJtzzWZSwE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_12926DCF35994B01); + LIB_FUNCTION("Esx6v78xYY8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_12CC7ABFBF31618F); + LIB_FUNCTION("E8TlH0RZKqI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_13C4E51F44592AA2); + LIB_FUNCTION("FTMOfFYzglQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_15330E7C56338254); + LIB_FUNCTION("FWazWMq-JhI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_1566B358CABF2612); + LIB_FUNCTION("FiWBjyaPRe8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_1625818F268F45EF); + LIB_FUNCTION("FtMrQNKKmsI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_16D32B40D28A9AC2); + LIB_FUNCTION("GD9Eg729Jc0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_183F4483BDBD25CD); + LIB_FUNCTION("GIfp6Vr2Lz0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_1887E9E95AF62F3D); + LIB_FUNCTION("GKPOlf2JPTo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_18A3CE95FD893D3A); + LIB_FUNCTION("GLNmXkhU5+k", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_18B3665E4854E7E9); + LIB_FUNCTION("GSOwA5SK9H4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_1923B003948AF47E); + LIB_FUNCTION("GbUz2kxZpTI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_19B533DA4C59A532); + LIB_FUNCTION("G7OZdy22jgg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_1BB399772DB68E08); + LIB_FUNCTION("HArGEtOilxs", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_1C0AC612D3A2971B); + LIB_FUNCTION("HFWZt3mZCkM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_1C5599B779990A43); + LIB_FUNCTION("HMuylrBDF74", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_1CCBB296B04317BE); + LIB_FUNCTION("HNBFVC+5MAI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_1CD045542FB93002); + LIB_FUNCTION("HezspnOrd7c", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_1DECECA673AB77B7); + LIB_FUNCTION("HgPgJOJsGn8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_1E03E024E26C1A7F); + LIB_FUNCTION("HxAXMrsNfiE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_1F101732BB0D7E21); + LIB_FUNCTION("H00VPsPdR7s", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_1F4D153EC3DD47BB); + LIB_FUNCTION("H3xH9j+vDL4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_1F7C47F63FAF0CBE); + LIB_FUNCTION("H74u5owPMbY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_1FBE2EE68C0F31B6); + LIB_FUNCTION("IDjBYokUuck", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_2038C1628914B9C9); + LIB_FUNCTION("ID-LVv24anQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_203FCB56FDB86A74); + LIB_FUNCTION("IFacEHxssIw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_20569C107C6CB08C); + LIB_FUNCTION("IKstc07eVfA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_20AB2D734EDE55F0); + LIB_FUNCTION("IrEoEYD7Cl4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_22B1281180FB0A5E); + LIB_FUNCTION("IvGq2makSa4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_22F1AADA66A449AE); + LIB_FUNCTION("I4shXv-fPTA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_238B215EFFDF3D30); + LIB_FUNCTION("JOjsUdFJ+hU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_24E8EC51D149FA15); + LIB_FUNCTION("JXKOeKOWLAI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_25728E78A3962C02); + LIB_FUNCTION("JeZJocaJHAU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_25E649A1C6891C05); + LIB_FUNCTION("JkuKOLV3cF0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_264B8A38B577705D); + LIB_FUNCTION("Jm7QjcHIKg4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_266ED08DC1C82A0E); + LIB_FUNCTION("J7tN5iq1i60", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_27BB4DE62AB58BAD); + LIB_FUNCTION("KDqpahluouo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_283AA96A196EA2EA); + LIB_FUNCTION("KFMVo5CoWpQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_285315A390A85A94); + LIB_FUNCTION("KQSdux7zGU4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_29049DBB1EF3194E); + LIB_FUNCTION("Kfe6nDcyy0c", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_29F7BA9C3732CB47); + LIB_FUNCTION("KnMt8zGsyzc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_2A732DF331ACCB37); + LIB_FUNCTION("KqAWYOx1tvs", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_2AA01660EC75B6FB); + LIB_FUNCTION("KzfLzpQcFoE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_2B37CBCE941C1681); + LIB_FUNCTION("LKo7ZNBUTlU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_2CAA3B64D0544E55); + LIB_FUNCTION("LM15YX7BCnU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_2CCD79617EC10A75); + LIB_FUNCTION("LNi2lxasBmc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_2CD8B69716AC0667); + LIB_FUNCTION("LXT3wP+bXpw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_2D74F7C0FF9B5E9C); + LIB_FUNCTION("LcpagIBUTpU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_2DCA5A8080544E95); + LIB_FUNCTION("LmnydDznzlc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_2E69F2743CE7CE57); + LIB_FUNCTION("Lq8fO6-wUn0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_2EAF1F3BAFF0527D); + LIB_FUNCTION("MUk+VbtOj2Y", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_31493E55BB4E8F66); + LIB_FUNCTION("MX7crQD7X14", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_317EDCAD00FB5F5E); + LIB_FUNCTION("MeAc+ooYzaI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_31E01CFA8A18CDA2); + LIB_FUNCTION("Mq-XgqBhtSY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_32AFD782A061B526); + LIB_FUNCTION("MrXN6wk7gYk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_32B5CDEB093B8189); + LIB_FUNCTION("NBVRUlE8k64", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_34155152513C93AE); + LIB_FUNCTION("NOTv-472yf4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_34E4EFFF8EF6C9FE); + LIB_FUNCTION("NXL6DVxUVjs", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_3572FA0D5C54563B); + LIB_FUNCTION("NnxHmyZODbk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_367C479B264E0DB9); + LIB_FUNCTION("NohPvJZLKcw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_36884FBC964B29CC); + LIB_FUNCTION("OGAIG7dVmUk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_3860081BB7559949); + LIB_FUNCTION("OTFPfmdKsTI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_39314F7E674AB132); + LIB_FUNCTION("OgLngPzFVqU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_3A02E780FCC556A5); + LIB_FUNCTION("Ohe4hbpISbY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_3A17B885BA4849B6); + LIB_FUNCTION("OjjqyupeI6Q", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_3A38EACAEA5E23A4); + LIB_FUNCTION("OzSl4H8NvB8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_3B34A5E07F0DBC1F); + LIB_FUNCTION("O06P-AD8fqQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_3B4E8FFC00FC7EA4); + LIB_FUNCTION("O6sY-aI1EHo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_3BAB18FDA235107A); + LIB_FUNCTION("O9+ZlqCjPxE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_3BDF9996A0A33F11); + LIB_FUNCTION("PBlS8aRcw3o", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_3C1952F1A45CC37A); + LIB_FUNCTION("PKN5Bs2wXzs", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_3CA37906CDB05F3B); + LIB_FUNCTION("PNspCKzuOm8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_3CDB2908ACEE3A6F); + LIB_FUNCTION("PT7RZfK9zTM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_3D3ED165F2BDCD33); + LIB_FUNCTION("PaTX0Vdfzc4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_3DA4D7D1575FCDCE); + LIB_FUNCTION("Pd+2Es0Lx2k", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_3DDFB612CD0BC769); + LIB_FUNCTION("PgQV4Wfercc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_3E0415E167DEADC7); + LIB_FUNCTION("Pn6fDxWBweY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_3E7E9F0F1581C1E6); + LIB_FUNCTION("PtOJ24KA7WU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_3ED389DB8280ED65); + LIB_FUNCTION("Pwx-bAw1SH0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_3F0C7F6C0C35487D); + LIB_FUNCTION("P9pyADie8NI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_3FDA7200389EF0D2); + LIB_FUNCTION("P-PCWLpRblg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_3FF3C258BA516E58); + LIB_FUNCTION("QClFP2KKPF0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_4029453F628A3C5D); + LIB_FUNCTION("QFgm3bSuU44", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_405826DDB4AE538E); + LIB_FUNCTION("QFqSZ1nyWGU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_405A926759F25865); + LIB_FUNCTION("QGYI-e566Io", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_406608FDEE7AE88A); + LIB_FUNCTION("QN2lVYwX3c8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_40DDA5558C17DDCF); + LIB_FUNCTION("QZ0S5S-2BmQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_419D12E52FF60664); + LIB_FUNCTION("QpblOUdL538", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_4296E539474BE77F); + LIB_FUNCTION("QvQfxWPMNlQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_42F41FC563CC3654); + LIB_FUNCTION("Q8zIb0yTAmo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_43CCC86F4C93026A); + LIB_FUNCTION("RAn2C9q8ZeE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_4409F60BDABC65E1); + LIB_FUNCTION("RWPHCuxnU4I", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_4563C70AEC675382); + LIB_FUNCTION("ReZjcCGb0F4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_45E66370219BD05E); + LIB_FUNCTION("RmpU8HJ4VpY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_466A54F072785696); + LIB_FUNCTION("Rs0lNpdvIJo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_46CD2536976F209A); + LIB_FUNCTION("SGNxe9L90Vc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_4863717BD2FDD157); + LIB_FUNCTION("SQLr0ZomMUk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_4902EBD19A263149); + LIB_FUNCTION("SQT3-o2D9Aw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_4904F7FE8D83F40C); + LIB_FUNCTION("Sl4T94Sr-Oc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_4A5E13F784ABFCE7); + LIB_FUNCTION("S2XusTXBJ4E", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_4B65EEB135C12781); + LIB_FUNCTION("TBnUmXjaheI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_4C19D49978DA85E2); + LIB_FUNCTION("TeXWIP9m8TY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_4DE5D620FF66F136); + LIB_FUNCTION("ThcMErV6j54", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_4E170C12B57A8F9E); + LIB_FUNCTION("Ti8-pAXDJgw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_4E2F3FA405C3260C); + LIB_FUNCTION("Tqk1BXdRO00", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_4EA9350577513B4D); + LIB_FUNCTION("T3jrb8S18h8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_4F78EB6FC4B5F21F); + LIB_FUNCTION("UDSL5DMRF7c", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_50348BE4331117B7); + LIB_FUNCTION("UIx+jN0oHKo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_508C7E8CDD281CAA); + LIB_FUNCTION("UhwdLAKPWn4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_521C1D2C028F5A7E); + LIB_FUNCTION("Ui-ySjXmcpE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_522FF24A35E67291); + LIB_FUNCTION("VHD+kMJc3Uw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_5470FE90C25CDD4C); + LIB_FUNCTION("VX8mD5pKzRg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_557F260F9A4ACD18); + LIB_FUNCTION("VYb5cgnzkes", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_5586F97209F391EB); + LIB_FUNCTION("VbLJt62pXDw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_55B2C9B7ADA95C3C); + LIB_FUNCTION("VbSIo6VAuTY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_55B488A3A540B936); + LIB_FUNCTION("VkLf6Cr0MUM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_5642DFE82AF43143); + LIB_FUNCTION("V04EbylK4Yc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_574E046F294AE187); + LIB_FUNCTION("V4km6-iqbL8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_578926EBF8AA6CBF); + LIB_FUNCTION("WF2l-GUIlrw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_585DA5FC650896BC); + LIB_FUNCTION("WNbrJzSewnY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_58D6EB27349EC276); + LIB_FUNCTION("WQa3MXlJhy0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_5906B7317949872D); + LIB_FUNCTION("WRC1YUM1vnA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_5910B5614335BE70); + LIB_FUNCTION("WT19qJEfCMk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_593D7DA8911F08C9); + LIB_FUNCTION("WXV-5qk7DVM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_59757FE6A93B0D53); + LIB_FUNCTION("WY5g+GKxFB4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_598E60F862B1141E); + LIB_FUNCTION("WkU1FmZoDa8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_5A45351666680DAF); + LIB_FUNCTION("Wqvp6nAuan8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_5AABE9EA702E6A7F); + LIB_FUNCTION("WupK5HI1W4A", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_5AEA4AE472355B80); + LIB_FUNCTION("WyDlPN5Zh0E", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_5B20E53CDE598741); + LIB_FUNCTION("W0gLWfrpR+A", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_5B480B59FAE947E0); + LIB_FUNCTION("W17sI2kKub0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_5B5EEC23690AB9BD); + LIB_FUNCTION("XArFsK8+2uA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_5C0AC5B0AF3EDAE0); + LIB_FUNCTION("XS6Zm+oHYtQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_5D2E999BEA0762D4); + LIB_FUNCTION("XVW7-UURDhY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_5D55BBFD45110E16); + LIB_FUNCTION("Xe4VQD0rtf0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_5DEE15403D2BB5FD); + LIB_FUNCTION("YCDHCMp0sTA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_6020C708CA74B130); + LIB_FUNCTION("YG4UFVA8NNI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_606E1415503C34D2); + LIB_FUNCTION("YSFA6O6aaT4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_612140E8EE9A693E); + LIB_FUNCTION("YfE-VR2vYd8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_61F13F551DAF61DF); + LIB_FUNCTION("YgbTkTF1Iyg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_6206D39131752328); + LIB_FUNCTION("Yh1FQ+8DRN4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_621D4543EF0344DE); + LIB_FUNCTION("YlmpqOVtAnM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_6259A9A8E56D0273); + LIB_FUNCTION("Yl+ccBY0b04", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_625F9C7016346F4E); + LIB_FUNCTION("Yu+N90bNjEo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_62EF8DF746CD8C4A); + LIB_FUNCTION("Y20qmf0eays", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_636D2A99FD1E6B2B); + LIB_FUNCTION("aAE+32b+dCU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_68013EDF66FE7425); + LIB_FUNCTION("aXH3Bn3WOdE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_6971F7067DD639D1); + LIB_FUNCTION("aYlq2zq0ELI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_69896ADB3AB410B2); + LIB_FUNCTION("ahOJqm5WE4c", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_6A1389AA6E561387); + LIB_FUNCTION("alVg2J8Ssuc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_6A5560D89F12B2E7); + LIB_FUNCTION("ar+Zz4VKvPE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_6ABF99CF854ABCF1); + LIB_FUNCTION("a0-dxlANjcs", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_6B4FDDC6500D8DCB); + LIB_FUNCTION("bKEdW0nRkoo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_6CA11D5B49D1928A); + LIB_FUNCTION("bWwPth5tBxU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_6D6C0FB61E6D0715); + LIB_FUNCTION("bXUHRf4TSPU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_6D750745FE1348F5); + LIB_FUNCTION("bhrz+dCZFL4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_6E1AF3F9D09914BE); + LIB_FUNCTION("blPtTAiypSE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_6E53ED4C08B2A521); + LIB_FUNCTION("bvQ6yh7WuWg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_6EF43ACA1ED6B968); + LIB_FUNCTION("b2+gnz4bamA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_6F6FA09F3E1B6A60); + LIB_FUNCTION("cDXDQMcZWQE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_7035C340C7195901); + LIB_FUNCTION("cDjiHLXPZBs", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_7038E21CB5CF641B); + LIB_FUNCTION("cGNF3NpbpE0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_706345DCDA5BA44D); + LIB_FUNCTION("cSBxTr8Qvx8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_7120714EBF10BF1F); + LIB_FUNCTION("cT0oqRvIA90", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_713D28A91BC803DD); + LIB_FUNCTION("cVO9dqU6oBI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_7153BD76A53AA012); + LIB_FUNCTION("cVxiXMcEG2s", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_715C625CC7041B6B); + LIB_FUNCTION("ceRnvbGHEdA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_71E467BDB18711D0); + LIB_FUNCTION("cg0XllwfTj8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_720D17965C1F4E3F); + LIB_FUNCTION("c0OAybz2W5o", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_734380C9BCF65B9A); + LIB_FUNCTION("c-TAjM1LvM8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_73F4C08CCD4BBCCF); + LIB_FUNCTION("dEAxAbeynUY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_74403101B7B29D46); + LIB_FUNCTION("dSWwgazWb-Q", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_7525B081ACD66FF4); + LIB_FUNCTION("db9Ed8E6Bco", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_75BF4477C13A05CA); + LIB_FUNCTION("dgl5P1mHxvc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_7609793F5987C6F7); + LIB_FUNCTION("dhbtAbBHaao", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_7616ED01B04769AA); + LIB_FUNCTION("dk+HPZGhJNg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_764F873D91A124D8); + LIB_FUNCTION("dwbx4SMFlWU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_7706F1E123059565); + LIB_FUNCTION("d-LQfrbYBuY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_77F2D07EB6D806E6); + LIB_FUNCTION("ecNwTNzVnlc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_79C3704CDCD59E57); + LIB_FUNCTION("edoLuiE1FUU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_79DA0BBA21351545); + LIB_FUNCTION("efokR7Xz8MQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_79FA2447B5F3F0C4); + LIB_FUNCTION("ek1vZf9hlaU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_7A4D6F65FF6195A5); + LIB_FUNCTION("ezGVzRFN7Oc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_7B3195CD114DECE7); + LIB_FUNCTION("ezI48jAa020", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_7B3238F2301AD36D); + LIB_FUNCTION("fHf8cHUKMmY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_7C77FC70750A3266); + LIB_FUNCTION("fSOp3EWdbRg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_7D23A9DC459D6D18); + LIB_FUNCTION("fVmIx0jQoF8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_7D5988C748D0A05F); + LIB_FUNCTION("fZWXFHqZ9PQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_7D9597147A99F4F4); + LIB_FUNCTION("filT9Afdg0Y", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_7E2953F407DD8346); + LIB_FUNCTION("fuNOUJlwmzI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_7EE34E5099709B32); + LIB_FUNCTION("gEcOVRHVygA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_80470E5511D5CA00); + LIB_FUNCTION("gHF5cBwI8Gk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_807179701C08F069); + LIB_FUNCTION("gJboH-ryTkY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_8096E81FFAF24E46); + LIB_FUNCTION("gLdk9PG4cEI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_80B764F4F1B87042); + LIB_FUNCTION("gL9pFDitAIs", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_80BF691438AD008B); + LIB_FUNCTION("gM9s-JYBJEI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_80CF6CFC96012442); + LIB_FUNCTION("gOp3L4wFGf0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_80EA772F8C0519FD); + LIB_FUNCTION("gdCv0AhNMno", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_81D0AFD0084D327A); + LIB_FUNCTION("gh64pyF2-Wc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_821EB8A72176FD67); + LIB_FUNCTION("gtL6tUEnJz8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_82D2FAB54127273F); + LIB_FUNCTION("g2rmacQqWek", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_836AE669C42A59E9); + LIB_FUNCTION("hVmiW-7DUYw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_8559A25BFEC3518C); + LIB_FUNCTION("hcH2bHZ6SdI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_85C1F66C767A49D2); + LIB_FUNCTION("hontE4P4e6c", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_8689ED1383F87BA7); + LIB_FUNCTION("h5bNnlNV06Y", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_8796CD9E5355D3A6); + LIB_FUNCTION("h9N+tt3BnZk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_87D37EB6DDC19D99); + LIB_FUNCTION("iAqkj3D4T90", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_880AA48F70F84FDD); + LIB_FUNCTION("iXsHViCTZls", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_897B07562093665B); + LIB_FUNCTION("isr1XxY2gIc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_8ACAF55F16368087); + LIB_FUNCTION("iuilWJsw1OA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_8AE8A5589B30D4E0); + LIB_FUNCTION("iumXkJgxszE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_8AE997909831B331); + LIB_FUNCTION("iy1kC+DQ+5k", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_8B2D640BE0D0FB99); + LIB_FUNCTION("iz2atGaNrss", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_8B3D9AB4668DAECB); + LIB_FUNCTION("i176qqzgtGw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_8B5EFAAAACE0B46C); + LIB_FUNCTION("jCeUP0CpiNs", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_8C27943F40A988DB); + LIB_FUNCTION("jFQJbHX18tA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_8C54096C75F5F2D0); + LIB_FUNCTION("jXZjoKUWiBQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_8D7663A0A5168814); + LIB_FUNCTION("jmGPUJmU+tc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_8E618F509994FAD7); + LIB_FUNCTION("jxnmzAZOK5g", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_8F19E6CC064E2B98); + LIB_FUNCTION("j2qK6u6SL-U", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_8F6A8AEAEE922FF5); + LIB_FUNCTION("kBDhrY67+8o", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_9010E1AD8EBBFBCA); + LIB_FUNCTION("kKlVoOcAGuk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_90A955A0E7001AE9); + LIB_FUNCTION("kPnWBn-uzAU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_90F9D6067FEECC05); + LIB_FUNCTION("k0jz0ZVGodo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_9348F3D19546A1DA); + LIB_FUNCTION("k9PAEdsZOIo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_93D3C011DB19388A); + LIB_FUNCTION("lW56T9n4kQM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_956E7A4FD9F89103); + LIB_FUNCTION("lfaZ4ELD5A8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_95F699E042C3E40F); + LIB_FUNCTION("lod7OaoOhzU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_96877B39AA0E8735); + LIB_FUNCTION("ls4HxJ7SNOo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_96CE07C49ED234EA); + LIB_FUNCTION("l2uxeCNbVoE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_976BB178235B5681); + LIB_FUNCTION("l4wLJeWIxNY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_978C0B25E588C4D6); + LIB_FUNCTION("mLomEr7yONY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_98BA2612BEF238D6); + LIB_FUNCTION("mVvdSTGvkTc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_995BDD4931AF9137); + LIB_FUNCTION("mWbjmpJrclA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_9966E39A926B7250); + LIB_FUNCTION("mcIwbxiWNGQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_99C2306F18963464); + LIB_FUNCTION("mcksYTt3a6c", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_99C92C613B776BA7); + LIB_FUNCTION("mk5Lk4zIrTk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_9A4E4B938CC8AD39); + LIB_FUNCTION("myP3tLf3IIE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_9B23F7B4B7F72081); + LIB_FUNCTION("nA6u6ucFqNs", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_9C0EAEEAE705A8DB); + LIB_FUNCTION("nUesWVRd6eg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_9D47AC59545DE9E8); + LIB_FUNCTION("oTBS2LGyrPo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_A13052D8B1B2ACFA); + LIB_FUNCTION("oapD46ePb2I", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_A1AA43E3A78F6F62); + LIB_FUNCTION("oeSM31Rknck", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_A1E48CDF54649DC9); + LIB_FUNCTION("oufe5bCvXRQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_A2E7DEE5B0AF5D14); + LIB_FUNCTION("ovXH-Z-xE-U", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_A2F5C7FD9FF113F5); + LIB_FUNCTION("o2KW4iadRrw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_A36296E2269D46BC); + LIB_FUNCTION("o+4qe58NiK8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_A3EE2A7B9F0D88AF); + LIB_FUNCTION("pEcfn34L+oI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_A4471F9F7E0BFA82); + LIB_FUNCTION("pEm7pSHqNOE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_A449BBA521EA34E1); + LIB_FUNCTION("pI5mbDNOcmw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_A48E666C334E726C); + LIB_FUNCTION("pJt0SbTd5pw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_A49B7449B4DDE69C); + LIB_FUNCTION("pXSEURJcnqQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_A5748451125C9EA4); + LIB_FUNCTION("ppCijWSMwXY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_A690A28D648CC176); + LIB_FUNCTION("pqht4bHLsdk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_A6A86DE1B1CBB1D9); + LIB_FUNCTION("qPK7e4FXQKE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_A8F2BB7B815740A1); + LIB_FUNCTION("qT9kwGpvc5c", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_A93F64C06A6F7397); + LIB_FUNCTION("qzWSX8l9aqM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_AB35925FC97D6AA3); + LIB_FUNCTION("rAFKosmR+ik", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_AC014AA2C991FA29); + LIB_FUNCTION("rAbhCQFASus", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_AC06E10901404AEB); + LIB_FUNCTION("rHXGiBNSNQU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_AC75C68813523505); + LIB_FUNCTION("rUQbxJcILD4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_AD441BC497082C3E); + LIB_FUNCTION("rU8l8CHTVMM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_AD4F25F021D354C3); + LIB_FUNCTION("rfoEqFVBpP4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_ADFA04A85541A4FE); + LIB_FUNCTION("rpYQprUheiM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_AE9610A6B5217A23); + LIB_FUNCTION("ryAZI4JvClg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_AF201923826F0A58); + LIB_FUNCTION("r8AhtDico-o", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_AFC021B4389CA3FA); + LIB_FUNCTION("sBXpmaM3PY8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_B015E999A3373D8F); + LIB_FUNCTION("sDhLhhB-xlI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_B0384B86107FC652); + LIB_FUNCTION("sMYwZTsxZWM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_B0C630653B316563); + LIB_FUNCTION("sQDczYjVxz0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_B100DCCD88D5C73D); + LIB_FUNCTION("sRo-6l5NnqQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_B11A3FEA5E4D9EA4); + LIB_FUNCTION("suf43BmcC5M", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_B2E7F8DC199C0B93); + LIB_FUNCTION("s6thopb23cg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_B3AB61A296F6DDC8); + LIB_FUNCTION("s-MvauYZ7II", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_B3F32F6AE619EC82); + LIB_FUNCTION("tCJ6shO-jPU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_B4227AB213BF8CF5); + LIB_FUNCTION("tGUr9CtgQ2A", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_B4652BF42B604360); + LIB_FUNCTION("tTbB8Tv+l8s", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_B536C1F13BFE97CB); + LIB_FUNCTION("tkXMJkGEvIk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_B645CC264184BC89); + LIB_FUNCTION("tn4XsVgsb70", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_B67E17B1582C6FBD); + LIB_FUNCTION("ttBHxddpWk0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_B6D047C5D7695A4D); + LIB_FUNCTION("t17Y4epi78c", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_B75ED8E1EA62EFC7); + LIB_FUNCTION("t6mpRNvX4QA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_B7A9A944DBD7E100); + LIB_FUNCTION("t8TnW+lPMfM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_B7C4E75BE94F31F3); + LIB_FUNCTION("uIix+SxGQSE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_B888B1F92C464121); + LIB_FUNCTION("uN7CJWSqBXs", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_B8DEC22564AA057B); + LIB_FUNCTION("ubrdHLu65Pg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_B9BADD1CBBBAE4F8); + LIB_FUNCTION("uqn3FpyF5Z8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_BAA9F7169C85E59F); + LIB_FUNCTION("uu5cOJCNYts", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_BAEE5C38908D62DB); + LIB_FUNCTION("vMhV6yUYP4Q", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_BCC855EB25183F84); + LIB_FUNCTION("vQH2NwKcc2Q", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_BD01F637029C7364); + LIB_FUNCTION("vdKfWscHflM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_BDD29F5AC7077E53); + LIB_FUNCTION("vtg90z7K1Q0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_BED83DD33ECAD50D); + LIB_FUNCTION("vufV0Jir9yg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_BEE7D5D098ABF728); + LIB_FUNCTION("wNsVzPWa5iw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_C0DB15CCF59AE62C); + LIB_FUNCTION("wcIp-uD9YPo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_C1C229FEE0FD60FA); + LIB_FUNCTION("wii5rWgpjpg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_C228B9AD68298E98); + LIB_FUNCTION("wphSXO9vsoM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_C298525CEF6FB283); + LIB_FUNCTION("w1Dwk1H21rU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_C350F09351F6D6B5); + LIB_FUNCTION("w3QugPpYAxk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_C3742E80FA580319); + LIB_FUNCTION("w8mFPV1NRdQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_C3C9853D5D4D45D4); + LIB_FUNCTION("w-Xa1Pufw0A", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_C3F5DAD4FB9FC340); + LIB_FUNCTION("xF+w5MzprtY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_C45FB0E4CCE9AED6); + LIB_FUNCTION("xJecuUi348c", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_C4979CB948B7E3C7); + LIB_FUNCTION("xJsluhbPC4w", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_C49B25BA16CF0B8C); + LIB_FUNCTION("xVE0XZYxIB4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_C551345D9631201E); + LIB_FUNCTION("xXopRCE2gpg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_C57A294421368298); + LIB_FUNCTION("xdyRytch1ig", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_C5DC91CAD721D628); + LIB_FUNCTION("xt7O5YkTU1c", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_C6DECEE589135357); + LIB_FUNCTION("yB+LINZ6x40", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_C81F8B20D67AC78D); + LIB_FUNCTION("yCD6VvrIe+o", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_C820FA56FAC87BEA); + LIB_FUNCTION("yHjqkRTF5JA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_C878EA9114C5E490); + LIB_FUNCTION("yKgT6-9HdQk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_C8A813EBFF477509); + LIB_FUNCTION("yWamY9WjVII", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_C966A663D5A35482); + LIB_FUNCTION("yXxMZ-02dNM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_C97C4C67FD3674D3); + LIB_FUNCTION("yZBVDxWEiwc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_C990550F15848B07); + LIB_FUNCTION("yllzeo7Bu74", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_CA59737A8EC1BBBE); + LIB_FUNCTION("ysX96PgNe2U", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_CAC5FDE8F80D7B65); + LIB_FUNCTION("yxNbMNBjm4M", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_CB135B30D0639B83); + LIB_FUNCTION("y4oaqmH2TDo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_CB8A1AAA61F64C3A); + LIB_FUNCTION("y55nRnJYB1c", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_CB9E674672580757); + LIB_FUNCTION("zCudJerqqx0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_CC2B9D25EAEAAB1D); + LIB_FUNCTION("zRslK77fW1M", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_CD1B252BBEDF5B53); + LIB_FUNCTION("zwA76Qy+Gic", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_CF003BE90CBE1A27); + LIB_FUNCTION("zwCONIhKweI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_CF008E34884AC1E2); + LIB_FUNCTION("0Lj0s6NoerI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_D0B8F4B3A3687AB2); + LIB_FUNCTION("0O4ZuOkfYPU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_D0EE19B8E91F60F5); + LIB_FUNCTION("0SuSlL0OD1Y", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_D12B9294BD0E0F56); + LIB_FUNCTION("0cyGJtj6Mos", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_D1CC8626D8FA328B); + LIB_FUNCTION("0vorueuLY6w", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_D2FA2BB9EB8B63AC); + LIB_FUNCTION("0yGXiAz5POs", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_D32197880CF93CEB); + LIB_FUNCTION("0yb1wmzIG44", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_D326F5C26CC81B8E); + LIB_FUNCTION("1PoGuVoyG3o", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_D4FA06B95A321B7A); + LIB_FUNCTION("1So3qQHgSyE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_D52A37A901E04B21); + LIB_FUNCTION("1VBN-DmatAA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_D5504DFC399AB400); + LIB_FUNCTION("1WEFyyf49dw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_D56105CB27F8F5DC); + LIB_FUNCTION("1WirGSNeyxk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_D568AB19235ECB19); + LIB_FUNCTION("1t979mOf5hE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_D6DF7BF6639FE611); + LIB_FUNCTION("2GCKkDEZ10Y", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_D8608A903119D746); + LIB_FUNCTION("2ej8cH1ZkU0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_D9E8FC707D59914D); + LIB_FUNCTION("2fB55i3uWyk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_D9F079E62DEE5B29); + LIB_FUNCTION("2hfOTyl0hTY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_DA17CE4F29748536); + LIB_FUNCTION("2kC579f2EYU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_DA40B9EFD7F61185); + LIB_FUNCTION("2msnT+vCZmo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_DA6B274FEBC2666A); + LIB_FUNCTION("2tAVNch6Ufw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_DAD01535C87A51FC); + LIB_FUNCTION("20UR1EhRDsQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_DB4511D448510EC4); + LIB_FUNCTION("247x--xmJpw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_DB8EF1FFFC66269C); + LIB_FUNCTION("27UI+hudqPc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_DBB508FA1B9DA8F7); + LIB_FUNCTION("3FnJuHC3KaI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_DC59C9B870B729A2); + LIB_FUNCTION("3Gae1sv2dRw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_DC669ED6CBF6751C); + LIB_FUNCTION("3LiihJpByZE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_DCB8A2849A41C991); + LIB_FUNCTION("3Y+ZFtfwOvc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_DD8F9916D7F03AF7); + LIB_FUNCTION("3cM-L05IDCo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_DDC33F2F4E480C2A); + LIB_FUNCTION("3gtCC96LItc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_DE0B420BDE8B22D7); + LIB_FUNCTION("4MC8KYmP43A", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_E0C0BC29898FE370); + LIB_FUNCTION("4M2JPkb7Vbo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_E0CD893E46FB55BA); + LIB_FUNCTION("4lUwFkt-ZZ8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_E25530164B7F659F); + LIB_FUNCTION("42gvQ-33bFg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_E3682F43FDF76C58); + LIB_FUNCTION("44F34ceKgPo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_E38177E1C78A80FA); + LIB_FUNCTION("48p0z-ll3wo", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_E3CA74CFF965DF0A); + LIB_FUNCTION("5FuxkbSbLtk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_E45BB191B49B2ED9); + LIB_FUNCTION("5GW51rYObX0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_E465B9D6B60E6D7D); + LIB_FUNCTION("5NgodsKWw4o", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_E4D82876C296C38A); + LIB_FUNCTION("5N21NQ+ltTg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_E4DDB5350FA5B538); + LIB_FUNCTION("5Uv-b7crx74", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_E54BFF6FB72BC7BE); + LIB_FUNCTION("5ZKpMgMCC7s", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_E592A93203020BBB); + LIB_FUNCTION("5aRK9tfUiv0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_E5A44AF6D7D48AFD); + LIB_FUNCTION("5jmpfPn-FDA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_E639A97CF9FF1430); + LIB_FUNCTION("5qwBeeSKiSc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_E6AC0179E48A8927); + LIB_FUNCTION("51FZZoJ3XYM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_E751596682775D83); + LIB_FUNCTION("54ix5S74JwI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_E788B1E52EF82702); + LIB_FUNCTION("6U8XYT9cnTE", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_E94F17613F5C9D31); + LIB_FUNCTION("6VkBExKNVeA", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_E9590113128D55E0); + LIB_FUNCTION("6eCw3RJWCxY", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_E9E0B0DD12560B16); + LIB_FUNCTION("6vXI7OZMewU", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_EAF5C8ECE64C7B05); + LIB_FUNCTION("65i-XELUp+s", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_EB98BF5C42D4A7EB); + LIB_FUNCTION("66vEqsQ6Row", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_EBABC4AAC43A468C); + LIB_FUNCTION("6-AAhfCCzIs", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_EBF00085F082CC8B); + LIB_FUNCTION("7LZZ7gWNBq8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_ECB659EE058D06AF); + LIB_FUNCTION("7PCWq3UUh64", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_ECF096AB751487AE); + LIB_FUNCTION("7lonFwHbM8A", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_EE5A271701DB33C0); + LIB_FUNCTION("72TLahYlJI4", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_EF64CB6A1625248E); + LIB_FUNCTION("72yKNXx+2GM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_EF6C8A357C7ED863); + LIB_FUNCTION("8A-pT35pmZQ", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_F00FE94F7E699994); + LIB_FUNCTION("8aUdujAykDg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_F1A51DBA30329038); + LIB_FUNCTION("8hbnZqkP3BI", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_F216E766A90FDC12); + LIB_FUNCTION("8qEFhKvl2Cw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_F2A10584ABE5D82C); + LIB_FUNCTION("8tmdOV5UIaM", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_F2D99D395E5421A3); + LIB_FUNCTION("84AB5Si6E3E", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_F38001E528BA1371); + LIB_FUNCTION("857JyPp2h7M", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_F39EC9C8FA7687B3); + LIB_FUNCTION("86--3NYyd1w", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_F3AFFFDCD632775C); + LIB_FUNCTION("87jf8zdIv9M", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_F3B8DFF33748BFD3); + LIB_FUNCTION("9eR-lVD3oUc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_F5E47F9550F7A147); + LIB_FUNCTION("9uk3FNGpOc8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_F6E93714D1A939CF); + LIB_FUNCTION("9v0ZrUjk7wk", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_F6FD19AD48E4EF09); + LIB_FUNCTION("90Tr-GIPfL8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_F744EBFC620F7CBF); + LIB_FUNCTION("925FJay6zH8", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_F76E4525ACBACC7F); + LIB_FUNCTION("95V6SIgvQss", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_F7957A48882F42CB); + LIB_FUNCTION("96gLB4CbqDg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_F7A80B07809BA838); + LIB_FUNCTION("+FccbMW2tZ0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_F8571C6CC5B6B59D); + LIB_FUNCTION("+Xh8+oc4Nvs", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_F9787CFA873836FB); + LIB_FUNCTION("+nifbTTTg-g", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_FA789F6D34D383F8); + LIB_FUNCTION("+rpXQIOsHmw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_FABA574083AC1E6C); + LIB_FUNCTION("-AT9u642j7c", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_FC04FDBBAE368FB7); + LIB_FUNCTION("-S2vvy5A7uc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_FD2DAFBF2E40EEE7); + LIB_FUNCTION("-VXubTX5UK0", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_FD55EE6D35F950AD); + LIB_FUNCTION("-lXuMgmNDVg", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_FE55EE32098D0D58); + LIB_FUNCTION("-nmEECLh2hw", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_FE79841022E1DA1C); + LIB_FUNCTION("--Sj4nn7RKc", "libSceNpCommon", 1, "libSceNpCommon", 1, 1, Func_FFF4A3E279FB44A7); +}; + +} // namespace Libraries::NpCommon \ No newline at end of file diff --git a/src/core/libraries/np_common/np_common.h b/src/core/libraries/np_common/np_common.h new file mode 100644 index 000000000..886610ccc --- /dev/null +++ b/src/core/libraries/np_common/np_common.h @@ -0,0 +1,1245 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::NpCommon { + +constexpr int ORBIS_NP_ONLINEID_MAX_LENGTH = 16; + +struct OrbisNpOnlineId { + char data[ORBIS_NP_ONLINEID_MAX_LENGTH]; + s8 term; + s8 dummy[3]; +}; + +struct OrbisNpId { + OrbisNpOnlineId handle; + u8 opt[8]; + u8 reserved[8]; +}; + +int PS4_SYSV_ABI sceNpCmpNpId(OrbisNpId* np_id1, OrbisNpId* np_id2); +int PS4_SYSV_ABI sceNpCmpNpIdInOrder(OrbisNpId* np_id1, OrbisNpId* np_id2, u32* out_result); +int PS4_SYSV_ABI sceNpCmpOnlineId(OrbisNpOnlineId* online_id1, OrbisNpOnlineId* online_id2); +int PS4_SYSV_ABI _sceNpAllocatorExConvertAllocator(); +int PS4_SYSV_ABI _sceNpAllocatorExFree(); +int PS4_SYSV_ABI _sceNpAllocatorExMalloc(); +int PS4_SYSV_ABI _sceNpAllocatorExRealloc(); +int PS4_SYSV_ABI _sceNpAllocatorExStrdup(); +int PS4_SYSV_ABI _sceNpAllocatorExStrndup(); +int PS4_SYSV_ABI _sceNpAllocatorFree(); +int PS4_SYSV_ABI _sceNpAllocatorMalloc(); +int PS4_SYSV_ABI _sceNpAllocatorRealloc(); +int PS4_SYSV_ABI _sceNpAllocatorStrdup(); +int PS4_SYSV_ABI _sceNpAllocatorStrndup(); +int PS4_SYSV_ABI _sceNpFree(); +int PS4_SYSV_ABI _sceNpHeapFree(); +int PS4_SYSV_ABI _sceNpHeapMalloc(); +int PS4_SYSV_ABI _sceNpHeapRealloc(); +int PS4_SYSV_ABI _sceNpHeapStrdup(); +int PS4_SYSV_ABI _sceNpHeapStrndup(); +int PS4_SYSV_ABI _sceNpMalloc(); +int PS4_SYSV_ABI _sceNpRealloc(); +int PS4_SYSV_ABI _ZN3sce2np10Cancelable10IsCanceledEv(); +int PS4_SYSV_ABI _ZN3sce2np10Cancelable10LockCancelEPKciS3_(); +int PS4_SYSV_ABI _ZN3sce2np10Cancelable11CheckCancelEPKciS3_(); +int PS4_SYSV_ABI _ZN3sce2np10Cancelable12UnlockCancelEPKciS3_(); +int PS4_SYSV_ABI _ZN3sce2np10Cancelable13SetCancelableEb(); +int PS4_SYSV_ABI _ZN3sce2np10Cancelable14SetupSubCancelEPS1_PKciS4_(); +int PS4_SYSV_ABI _ZN3sce2np10Cancelable16CleanupSubCancelEPS1_(); +int PS4_SYSV_ABI _ZN3sce2np10Cancelable4InitEv(); +int PS4_SYSV_ABI _ZN3sce2np10Cancelable6CancelEij(); +int PS4_SYSV_ABI _ZN3sce2np10Cancelable7DestroyEv(); +int PS4_SYSV_ABI _ZN3sce2np10CancelableC2Ev(); +int PS4_SYSV_ABI _ZN3sce2np10CancelableD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np10CancelableD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np10CancelableD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np10CancelLock3EndEPKciS3_(); +int PS4_SYSV_ABI _ZN3sce2np10CancelLock5BeginEPNS0_6HandleEPKciS5_(); +int PS4_SYSV_ABI _ZN3sce2np10CancelLockC1Ev(); +int PS4_SYSV_ABI _ZN3sce2np10CancelLockC2Ev(); +int PS4_SYSV_ABI _ZN3sce2np10CancelLockD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np10CancelLockD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np10EventQueue10ClearAbortEt(); +int PS4_SYSV_ABI _ZN3sce2np10EventQueue10TryDequeueEPvm(); +int PS4_SYSV_ABI _ZN3sce2np10EventQueue4ctorEv(); +int PS4_SYSV_ABI _ZN3sce2np10EventQueue4dtorEv(); +int PS4_SYSV_ABI _ZN3sce2np10EventQueue4InitEPKcmm(); +int PS4_SYSV_ABI _ZN3sce2np10EventQueue5AbortEt(); +int PS4_SYSV_ABI _ZN3sce2np10EventQueue7DequeueEPvmj(); +int PS4_SYSV_ABI _ZN3sce2np10EventQueue7DestroyEv(); +int PS4_SYSV_ABI _ZN3sce2np10EventQueue7EnqueueEPKvmj(); +int PS4_SYSV_ABI _ZN3sce2np10EventQueueC2EP16SceNpAllocatorEx(); +int PS4_SYSV_ABI _ZN3sce2np10EventQueueD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np10EventQueueD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np10EventQueueD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np10JsonNumber5ClearEv(); +int PS4_SYSV_ABI _ZN3sce2np10JsonNumber6SetNumEi(); +int PS4_SYSV_ABI _ZN3sce2np10JsonNumber6SetNumEj(); +int PS4_SYSV_ABI _ZN3sce2np10JsonNumber6SetNumEl(); +int PS4_SYSV_ABI _ZN3sce2np10JsonNumber6SetNumEm(); +int PS4_SYSV_ABI _ZN3sce2np10JsonNumber6SetNumEPKc(); +int PS4_SYSV_ABI _ZN3sce2np10JsonObject16DeleteFieldValueEPKc(); +int PS4_SYSV_ABI _ZN3sce2np10JsonObject5ClearEv(); +int PS4_SYSV_ABI _ZN3sce2np10JsonParser4InitEPK7JsonDefPNS1_12EventHandlerE(); +int PS4_SYSV_ABI _ZN3sce2np10JsonParser5ParseEPKcm(); +int PS4_SYSV_ABI _ZN3sce2np10JsonParserC2EP16SceNpAllocatorEx(); +int PS4_SYSV_ABI _ZN3sce2np10JsonParserD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np10JsonParserD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np10JsonParserD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np10JsonString5ClearEv(); +int PS4_SYSV_ABI _ZN3sce2np10JsonString6SetStrEPKc(); +int PS4_SYSV_ABI _ZN3sce2np10MemoryFile4ReadEPNS0_6HandleEPvmPm(); +int PS4_SYSV_ABI _ZN3sce2np10MemoryFile4SyncEv(); +int PS4_SYSV_ABI _ZN3sce2np10MemoryFile5CloseEv(); +int PS4_SYSV_ABI _ZN3sce2np10MemoryFile5WriteEPNS0_6HandleEPKvmPm(); +int PS4_SYSV_ABI _ZN3sce2np10MemoryFile8TruncateEl(); +int PS4_SYSV_ABI _ZN3sce2np10MemoryFileC2EP16SceNpAllocatorEx(); +int PS4_SYSV_ABI _ZN3sce2np10MemoryFileD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np10MemoryFileD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np10MemoryFileD2Ev(); +int PS4_SYSV_ABI +_ZN3sce2np12HttpTemplate19SetAuthInfoCallbackEPFii15SceHttpAuthTypePKcPcS5_iPPhPmPiPvESA_(); +int PS4_SYSV_ABI _ZN3sce2np12HttpTemplate4InitEiPKcib(); +int PS4_SYSV_ABI _ZN3sce2np12HttpTemplate7DestroyEv(); +int PS4_SYSV_ABI _ZN3sce2np12HttpTemplateC1Ev(); +int PS4_SYSV_ABI _ZN3sce2np12HttpTemplateC2Ev(); +int PS4_SYSV_ABI _ZN3sce2np12HttpTemplateD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np12HttpTemplateD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np12HttpTemplateD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np12StreamBufferixEi(); +int PS4_SYSV_ABI _ZN3sce2np12StreamReader4ReadEPNS0_6HandleEPNS0_9StreamCtxEPvmPm(); +int PS4_SYSV_ABI _ZN3sce2np12StreamReader7ReadAllEPNS0_6HandleEPNS0_9StreamCtxEPvmPm(); +int PS4_SYSV_ABI _ZN3sce2np12StreamReader7ReadAllEPNS0_6HandleEPvmPm(); +int PS4_SYSV_ABI _ZN3sce2np12StreamReader8ReadDataEPNS0_6HandleEPNS0_9StreamCtxEPvmPm(); +int PS4_SYSV_ABI _ZN3sce2np12StreamReader8ReadDataEPNS0_6HandleEPvmPm(); +int PS4_SYSV_ABI _ZN3sce2np12StreamReader8SkipDataEPNS0_6HandleElPl(); +int PS4_SYSV_ABI _ZN3sce2np12StreamReader8SkipDataEPNS0_6HandleEPNS0_9StreamCtxElPl(); +int PS4_SYSV_ABI _ZN3sce2np12StreamWriter15WriteFilledDataEPNS0_6HandleEcl(); +int PS4_SYSV_ABI _ZN3sce2np12StreamWriter15WriteFilledDataEPNS0_6HandleEPNS0_9StreamCtxEcl(); +int PS4_SYSV_ABI _ZN3sce2np12StreamWriter5WriteEPNS0_6HandleEPNS0_9StreamCtxEPKvmPm(); +int PS4_SYSV_ABI _ZN3sce2np12StreamWriter9WriteDataEPNS0_6HandleEPKvmPm(); +int PS4_SYSV_ABI _ZN3sce2np12StreamWriter9WriteDataEPNS0_6HandleEPNS0_9StreamCtxEPKvmPm(); +int PS4_SYSV_ABI _ZN3sce2np12WorkerThread10ThreadMainEv(); +int PS4_SYSV_ABI _ZN3sce2np12WorkerThreadC1EPNS0_9WorkQueueE(); +int PS4_SYSV_ABI _ZN3sce2np12WorkerThreadC2EPNS0_9WorkQueueE(); +int PS4_SYSV_ABI _ZN3sce2np12WorkerThreadD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np12WorkerThreadD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np12WorkerThreadD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np13JsonDocParser5ParseEPKcm(); +int PS4_SYSV_ABI _ZN3sce2np13JsonDocParser9GetResultEPPNS0_10JsonObjectE(); +int PS4_SYSV_ABI _ZN3sce2np13JsonDocParser9GetResultEPPNS0_9JsonValueE(); +int PS4_SYSV_ABI _ZN3sce2np13JsonDocParserC2EP16SceNpAllocatorExPK7JsonDef(); +int PS4_SYSV_ABI _ZN3sce2np13JsonDocParserD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np13JsonDocParserD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np13JsonDocParserD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np13NpTitleSecret5ClearEv(); +int PS4_SYSV_ABI _ZN3sce2np13NpTitleSecretC1EPKvm(); +int PS4_SYSV_ABI _ZN3sce2np13NpTitleSecretC1ERK16SceNpTitleSecret(); +int PS4_SYSV_ABI _ZN3sce2np13NpTitleSecretC1ERKS1_(); +int PS4_SYSV_ABI _ZN3sce2np13NpTitleSecretC1Ev(); +int PS4_SYSV_ABI _ZN3sce2np13NpTitleSecretC2EPKvm(); +int PS4_SYSV_ABI _ZN3sce2np13NpTitleSecretC2ERK16SceNpTitleSecret(); +int PS4_SYSV_ABI _ZN3sce2np13NpTitleSecretC2ERKS1_(); +int PS4_SYSV_ABI _ZN3sce2np13NpTitleSecretC2Ev(); +int PS4_SYSV_ABI _ZN3sce2np13NpTitleSecretD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np13NpTitleSecretD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np13NpTitleSecretD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np13RingBufMemory4ctorEv(); +int PS4_SYSV_ABI _ZN3sce2np13RingBufMemory4dtorEv(); +int PS4_SYSV_ABI _ZN3sce2np13RingBufMemory4InitEm(); +int PS4_SYSV_ABI _ZN3sce2np13RingBufMemory6ExpandEm(); +int PS4_SYSV_ABI _ZN3sce2np13RingBufMemory6IsInitEv(); +int PS4_SYSV_ABI _ZN3sce2np13RingBufMemory7DestroyEv(); +int PS4_SYSV_ABI _ZN3sce2np13RingBufMemoryC2EP16SceNpAllocatorEx(); +int PS4_SYSV_ABI _ZN3sce2np13RingBufMemoryD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np13RingBufMemoryD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np13RingBufMemoryD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np14CalloutContext4InitEPKcimm(); +int PS4_SYSV_ABI _ZN3sce2np14CalloutContext4InitEPKNS1_5ParamE(); +int PS4_SYSV_ABI _ZN3sce2np14CalloutContext7DestroyEv(); +int PS4_SYSV_ABI _ZN3sce2np14CalloutContextC1Ev(); +int PS4_SYSV_ABI _ZN3sce2np14CalloutContextC2Ev(); +int PS4_SYSV_ABI _ZN3sce2np14CalloutContextD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np14CalloutContextD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np14CalloutContextD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np14JsonDocBuilder12BuildBufSizeEv(); +int PS4_SYSV_ABI _ZN3sce2np14JsonDocBuilder16EscapeJsonStringEPKcPcmPm(); +int PS4_SYSV_ABI _ZN3sce2np14JsonDocBuilder23EscapeJsonStringBufSizeEPKc(); +int PS4_SYSV_ABI _ZN3sce2np14JsonDocBuilder5BuildEPcmPm(); +int PS4_SYSV_ABI _ZN3sce2np14JsonDocBuilderC1ERKNS0_9JsonValueE(); +int PS4_SYSV_ABI _ZN3sce2np14JsonDocBuilderC2ERKNS0_9JsonValueE(); +int PS4_SYSV_ABI _ZN3sce2np14JsonDocBuilderD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np14JsonDocBuilderD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np14JsonDocBuilderD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np15CancelableScope3EndEiPKciS3_(); +int PS4_SYSV_ABI _ZN3sce2np15CancelableScope5BeginEPNS0_6HandleEPKciS5_(); +int PS4_SYSV_ABI _ZN3sce2np15CancelableScopeC2Ev(); +int PS4_SYSV_ABI _ZN3sce2np15CancelableScopeD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np15CancelableScopeD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np15CancelableScopeD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np16StreamReadBufferC2EP16SceNpAllocatorEx(); +int PS4_SYSV_ABI _ZN3sce2np16StreamReadBufferD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np16StreamReadBufferD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np18HttpConnectionPool13InvalidateAllEv(); +int PS4_SYSV_ABI _ZN3sce2np18HttpConnectionPool4InitEi(); +int PS4_SYSV_ABI _ZN3sce2np18HttpConnectionPool7DestroyEv(); +int PS4_SYSV_ABI _ZN3sce2np18HttpConnectionPoolC1EP16SceNpAllocatorEx(); +int PS4_SYSV_ABI _ZN3sce2np18HttpConnectionPoolC2EP16SceNpAllocatorEx(); +int PS4_SYSV_ABI _ZN3sce2np18HttpConnectionPoolD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np18HttpConnectionPoolD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np18HttpConnectionPoolD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np18MemoryStreamReader4ReadEPNS0_6HandleEPvmPm(); +int PS4_SYSV_ABI _ZN3sce2np18MemoryStreamReaderC1EPKvm(); +int PS4_SYSV_ABI _ZN3sce2np18MemoryStreamReaderC2EPKvm(); +int PS4_SYSV_ABI _ZN3sce2np18MemoryStreamReaderD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np18MemoryStreamReaderD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np18MemoryStreamReaderD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np18MemoryStreamWriter5WriteEPNS0_6HandleEPKvmPm(); +int PS4_SYSV_ABI _ZN3sce2np18MemoryStreamWriterC1EPvm(); +int PS4_SYSV_ABI _ZN3sce2np18MemoryStreamWriterC2EPvm(); +int PS4_SYSV_ABI _ZN3sce2np18MemoryStreamWriterD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np18MemoryStreamWriterD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np18MemoryStreamWriterD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np20BufferedStreamReader4ReadEPNS0_6HandleEPvmPm(); +int PS4_SYSV_ABI _ZN3sce2np20BufferedStreamReader5CloseEv(); +int PS4_SYSV_ABI _ZN3sce2np20BufferedStreamReaderC2EP16SceNpAllocatorEx(); +int PS4_SYSV_ABI _ZN3sce2np20BufferedStreamReaderD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np20BufferedStreamReaderD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np20BufferedStreamReaderD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np3ipc10IpmiClient10DisconnectEv(); +int PS4_SYSV_ABI _ZN3sce2np3ipc10IpmiClient11IsConnectedEv(); +int PS4_SYSV_ABI _ZN3sce2np3ipc10IpmiClient16invokeSyncMethodEjPKvmPvPmm(); +int PS4_SYSV_ABI _ZN3sce2np3ipc10IpmiClient4ctorEv(); +int PS4_SYSV_ABI _ZN3sce2np3ipc10IpmiClient4dtorEv(); +int PS4_SYSV_ABI _ZN3sce2np3ipc10IpmiClient4InitEPKNS2_6ConfigE(); +int PS4_SYSV_ABI _ZN3sce2np3ipc10IpmiClient7ConnectEPKvm(); +int PS4_SYSV_ABI _ZN3sce2np3ipc10IpmiClient7DestroyEv(); +int PS4_SYSV_ABI _ZN3sce2np3ipc10IpmiClientC1Ev(); +int PS4_SYSV_ABI _ZN3sce2np3ipc10IpmiClientC2Ev(); +int PS4_SYSV_ABI _ZN3sce2np3ipc10IpmiClientD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np3ipc10IpmiClientD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np3ipc10IpmiClientD2Ev(); +int PS4_SYSV_ABI +_ZN3sce2np3ipc13ServiceClientC1EPNS1_17ServiceIpmiClientEPKNS1_17ServiceClientInfoE(); +int PS4_SYSV_ABI +_ZN3sce2np3ipc13ServiceClientC2EPNS1_17ServiceIpmiClientEPKNS1_17ServiceClientInfoE(); +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient10DisconnectEv(); +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient10EndRequestEii(); +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient11findServiceEi(); +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient11InitServiceEi(); +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient11TermServiceEi(); +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient11WaitRequestEiij(); +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient12AbortRequestEii(); +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient12BeginRequestEii(); +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient13CreateRequestEPiiPKvm(); +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient13DeleteRequestEii(); +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient13PollEventFlagEijmjPm(); +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient13WaitEventFlagEijmjPmj(); +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient14PollEventQueueEiPvm(); +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient15CancelEventFlagEijm(); +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient15RegisterServiceEPKNS1_17ServiceClientInfoE(); +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient16RegisterServicesEPKNS1_17ServiceClientInfoE(); +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient17invokeInitServiceEi(); +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient17invokeTermServiceEi(); +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient17UnregisterServiceEi(); +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient18EndRequestForAsyncEii(); +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient19WaitRequestForAsyncEiij(); +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient20AbortRequestForAsyncEii(); +int PS4_SYSV_ABI +_ZN3sce2np3ipc17ServiceIpmiClient20BeginRequestForAsyncEiiPN4IPMI6Client12EventNotifeeE(); +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient21CreateRequestForAsyncEPiiPKvm(); +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient21DeleteRequestForAsyncEii(); +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient4ctorEv(); +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient4dtorEv(); +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient4InitEPNS2_6ConfigE(); +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient7ConnectEPKvm(); +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClient7DestroyEv(); +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClientC1Ev(); +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClientC2Ev(); +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClientD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClientD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np3ipc17ServiceIpmiClientD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np4Cond4ctorEv(); +int PS4_SYSV_ABI _ZN3sce2np4Cond4dtorEv(); +int PS4_SYSV_ABI _ZN3sce2np4Cond4InitEPKcPNS0_5MutexE(); +int PS4_SYSV_ABI _ZN3sce2np4Cond4WaitEj(); +int PS4_SYSV_ABI _ZN3sce2np4Cond6SignalEv(); +int PS4_SYSV_ABI _ZN3sce2np4Cond7DestroyEv(); +int PS4_SYSV_ABI _ZN3sce2np4Cond9SignalAllEv(); +int PS4_SYSV_ABI _ZN3sce2np4CondC1Ev(); +int PS4_SYSV_ABI _ZN3sce2np4CondC2Ev(); +int PS4_SYSV_ABI _ZN3sce2np4CondD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np4CondD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np4CondD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np4Path11BuildAppendEPcmcPKcm(); +int PS4_SYSV_ABI _ZN3sce2np4Path12AddDelimiterEPcmc(); +int PS4_SYSV_ABI _ZN3sce2np4Path5ClearEv(); +int PS4_SYSV_ABI _ZN3sce2np4Path6SetStrEPKcm(); +int PS4_SYSV_ABI _ZN3sce2np4PathD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np4PathD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np4PathD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np4Time10AddMinutesEl(); +int PS4_SYSV_ABI _ZN3sce2np4Time10AddSecondsEl(); +int PS4_SYSV_ABI _ZN3sce2np4Time12GetUserClockEPS1_(); +int PS4_SYSV_ABI _ZN3sce2np4Time15AddMicroSecondsEl(); +int PS4_SYSV_ABI _ZN3sce2np4Time15GetNetworkClockEPS1_(); +int PS4_SYSV_ABI _ZN3sce2np4Time20GetDebugNetworkClockEPS1_(); +int PS4_SYSV_ABI _ZN3sce2np4Time7AddDaysEl(); +int PS4_SYSV_ABI _ZN3sce2np4Time8AddHoursEl(); +int PS4_SYSV_ABI _ZN3sce2np4TimeplERK10SceRtcTick(); +int PS4_SYSV_ABI _ZN3sce2np4TimeplERKS1_(); +int PS4_SYSV_ABI _ZN3sce2np5Mutex4ctorEv(); +int PS4_SYSV_ABI _ZN3sce2np5Mutex4dtorEv(); +int PS4_SYSV_ABI _ZN3sce2np5Mutex4InitEPKcj(); +int PS4_SYSV_ABI _ZN3sce2np5Mutex4LockEv(); +int PS4_SYSV_ABI _ZN3sce2np5Mutex6UnlockEv(); +int PS4_SYSV_ABI _ZN3sce2np5Mutex7DestroyEv(); +int PS4_SYSV_ABI _ZN3sce2np5Mutex7TryLockEv(); +int PS4_SYSV_ABI _ZN3sce2np5MutexC1Ev(); +int PS4_SYSV_ABI _ZN3sce2np5MutexC2Ev(); +int PS4_SYSV_ABI _ZN3sce2np5MutexD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np5MutexD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np5MutexD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np5NpEnv8GetNpEnvEPS1_(); +int PS4_SYSV_ABI _ZN3sce2np6Handle10CancelImplEi(); +int PS4_SYSV_ABI _ZN3sce2np6Handle4InitEv(); +int PS4_SYSV_ABI _ZN3sce2np6Handle7DestroyEv(); +int PS4_SYSV_ABI _ZN3sce2np6HandleC1Ev(); +int PS4_SYSV_ABI _ZN3sce2np6HandleC2Ev(); +int PS4_SYSV_ABI _ZN3sce2np6HandleD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np6HandleD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np6HandleD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np6ObjectdaEPv(); +int PS4_SYSV_ABI _ZN3sce2np6ObjectdaEPvR14SceNpAllocator(); +int PS4_SYSV_ABI _ZN3sce2np6ObjectdaEPvR16SceNpAllocatorEx(); +int PS4_SYSV_ABI _ZN3sce2np6ObjectdlEPv(); +int PS4_SYSV_ABI _ZN3sce2np6ObjectdlEPvR14SceNpAllocator(); +int PS4_SYSV_ABI _ZN3sce2np6ObjectdlEPvR16SceNpAllocatorEx(); +int PS4_SYSV_ABI _ZN3sce2np6ObjectnaEmR14SceNpAllocator(); +int PS4_SYSV_ABI _ZN3sce2np6ObjectnaEmR16SceNpAllocatorEx(); +int PS4_SYSV_ABI _ZN3sce2np6ObjectnwEmR14SceNpAllocator(); +int PS4_SYSV_ABI _ZN3sce2np6ObjectnwEmR16SceNpAllocatorEx(); +int PS4_SYSV_ABI _ZN3sce2np6Thread12DoThreadMainEv(); +int PS4_SYSV_ABI _ZN3sce2np6Thread4ctorEv(); +int PS4_SYSV_ABI _ZN3sce2np6Thread4dtorEv(); +int PS4_SYSV_ABI _ZN3sce2np6Thread4InitEPKcimm(); +int PS4_SYSV_ABI _ZN3sce2np6Thread4InitEPKNS1_5ParamE(); +int PS4_SYSV_ABI _ZN3sce2np6Thread4JoinEPi(); +int PS4_SYSV_ABI _ZN3sce2np6Thread5StartEv(); +int PS4_SYSV_ABI _ZN3sce2np6Thread7DestroyEv(); +int PS4_SYSV_ABI _ZN3sce2np6Thread9EntryFuncEPv(); +int PS4_SYSV_ABI _ZN3sce2np6Thread9GetResultEv(); +int PS4_SYSV_ABI _ZN3sce2np6Thread9IsRunningEv(); +int PS4_SYSV_ABI _ZN3sce2np6ThreadC2Ev(); +int PS4_SYSV_ABI _ZN3sce2np6ThreadD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np6ThreadD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np6ThreadD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np7Callout10IsTimedoutEv(); +int PS4_SYSV_ABI _ZN3sce2np7Callout11CalloutFuncEPv(); +int PS4_SYSV_ABI _ZN3sce2np7Callout4StopEv(); +int PS4_SYSV_ABI _ZN3sce2np7Callout5StartEjPNS1_7HandlerE(); +int PS4_SYSV_ABI _ZN3sce2np7Callout5StartEmPNS1_7HandlerE(); +int PS4_SYSV_ABI _ZN3sce2np7Callout9IsStartedEv(); +int PS4_SYSV_ABI _ZN3sce2np7CalloutC1EPNS0_14CalloutContextE(); +int PS4_SYSV_ABI _ZN3sce2np7CalloutC2EPNS0_14CalloutContextE(); +int PS4_SYSV_ABI _ZN3sce2np7CalloutD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np7CalloutD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np7CalloutD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np7HttpUri5BuildEPKS1_PcmPmj(); +int PS4_SYSV_ABI _ZN3sce2np7HttpUri5ParseEPS1_PKc(); +int PS4_SYSV_ABI _ZN3sce2np7HttpUriC1EP16SceNpAllocatorEx(); +int PS4_SYSV_ABI _ZN3sce2np7HttpUriC2EP16SceNpAllocatorEx(); +int PS4_SYSV_ABI _ZN3sce2np7HttpUriD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np7HttpUriD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np7HttpUriD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np7RingBuf14CheckinForReadEm(); +int PS4_SYSV_ABI _ZN3sce2np7RingBuf15CheckinForWriteEm(); +int PS4_SYSV_ABI _ZN3sce2np7RingBuf15CheckoutForReadEPm(); +int PS4_SYSV_ABI _ZN3sce2np7RingBuf16CheckoutForWriteEPm(); +int PS4_SYSV_ABI _ZN3sce2np7RingBuf4ctorEv(); +int PS4_SYSV_ABI _ZN3sce2np7RingBuf4dtorEv(); +int PS4_SYSV_ABI _ZN3sce2np7RingBuf4InitEPvm(); +int PS4_SYSV_ABI _ZN3sce2np7RingBuf4PeekEmPvm(); +int PS4_SYSV_ABI _ZN3sce2np7RingBuf4ReadEPvm(); +int PS4_SYSV_ABI _ZN3sce2np7RingBuf5ClearEv(); +int PS4_SYSV_ABI _ZN3sce2np7RingBuf5WriteEPKvm(); +int PS4_SYSV_ABI _ZN3sce2np7RingBuf7DestroyEv(); +int PS4_SYSV_ABI _ZN3sce2np7RingBufC1Ev(); +int PS4_SYSV_ABI _ZN3sce2np7RingBufC2Ev(); +int PS4_SYSV_ABI _ZN3sce2np7RingBufD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np7RingBufD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np7RingBufD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np8HttpFile4ReadEPNS0_6HandleEPvmPm(); +int PS4_SYSV_ABI _ZN3sce2np8HttpFile5CloseEv(); +int PS4_SYSV_ABI _ZN3sce2np8HttpFileC2EP16SceNpAllocatorEx(); +int PS4_SYSV_ABI _ZN3sce2np8HttpFileD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np8HttpFileD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np8HttpFileD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np8JsonBool5ClearEv(); +int PS4_SYSV_ABI _ZN3sce2np8JsonBool7SetBoolEb(); +int PS4_SYSV_ABI _ZN3sce2np8JsonFile5CloseEv(); +int PS4_SYSV_ABI _ZN3sce2np8JsonFileD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np8JsonFileD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np8JsonFileD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np8JsonNull5ClearEv(); +int PS4_SYSV_ABI _ZN3sce2np8NpCommId5BuildERKS1_Pcm(); +int PS4_SYSV_ABI _ZN3sce2np8NpCommId5ClearEv(); +int PS4_SYSV_ABI _ZN3sce2np8NpCommId5ParseEPS1_PKc(); +int PS4_SYSV_ABI _ZN3sce2np8NpCommId5ParseEPS1_PKcm(); +int PS4_SYSV_ABI _ZN3sce2np8NpCommIdC1ERK20SceNpCommunicationId(); +int PS4_SYSV_ABI _ZN3sce2np8NpCommIdC1ERKS1_(); +int PS4_SYSV_ABI _ZN3sce2np8NpCommIdC1Ev(); +int PS4_SYSV_ABI _ZN3sce2np8NpCommIdC2ERK20SceNpCommunicationId(); +int PS4_SYSV_ABI _ZN3sce2np8NpCommIdC2ERKS1_(); +int PS4_SYSV_ABI _ZN3sce2np8NpCommIdC2Ev(); +int PS4_SYSV_ABI _ZN3sce2np8NpCommIdD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np8NpCommIdD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np8NpCommIdD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np8Selector4InitEPKc(); +int PS4_SYSV_ABI _ZN3sce2np8SelectorD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np8SelectorD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np8SelectorD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np8WorkItem10SetPendingEv(); +int PS4_SYSV_ABI _ZN3sce2np8WorkItem10SetRunningEv(); +int PS4_SYSV_ABI _ZN3sce2np8WorkItem11SetFinishedEi(); +int PS4_SYSV_ABI _ZN3sce2np8WorkItem14FinishCallbackEv(); +int PS4_SYSV_ABI _ZN3sce2np8WorkItem15RemoveFromQueueEv(); +int PS4_SYSV_ABI _ZN3sce2np8WorkItem6CancelEi(); +int PS4_SYSV_ABI _ZN3sce2np8WorkItem9BindQueueEPNS0_9WorkQueueEi(); +int PS4_SYSV_ABI _ZN3sce2np8WorkItemC2EPKc(); +int PS4_SYSV_ABI _ZN3sce2np8WorkItemD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np8WorkItemD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np8WorkItemD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np9EventFlag3SetEm(); +int PS4_SYSV_ABI _ZN3sce2np9EventFlag4ctorEv(); +int PS4_SYSV_ABI _ZN3sce2np9EventFlag4dtorEv(); +int PS4_SYSV_ABI _ZN3sce2np9EventFlag4OpenEPKc(); +int PS4_SYSV_ABI _ZN3sce2np9EventFlag4PollEmjPm(); +int PS4_SYSV_ABI _ZN3sce2np9EventFlag4WaitEmjPmj(); +int PS4_SYSV_ABI _ZN3sce2np9EventFlag5ClearEm(); +int PS4_SYSV_ABI _ZN3sce2np9EventFlag6CancelEm(); +int PS4_SYSV_ABI _ZN3sce2np9EventFlag6CreateEPKcj(); +int PS4_SYSV_ABI _ZN3sce2np9EventFlag7DestroyEv(); +int PS4_SYSV_ABI _ZN3sce2np9EventFlagC1Ev(); +int PS4_SYSV_ABI _ZN3sce2np9EventFlagC2Ev(); +int PS4_SYSV_ABI _ZN3sce2np9EventFlagD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np9EventFlagD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np9EventFlagD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np9HttpTrans10SetTimeoutEPKNS1_12TimeoutParamE(); +int PS4_SYSV_ABI _ZN3sce2np9HttpTrans11SendRequestEPNS0_6HandleEPKvm(); +int PS4_SYSV_ABI _ZN3sce2np9HttpTrans12RecvResponseEPNS0_6HandleEPvmPm(); +int PS4_SYSV_ABI _ZN3sce2np9HttpTrans12SkipResponseEPNS0_6HandleE(); +int PS4_SYSV_ABI _ZN3sce2np9HttpTrans16AddRequestHeaderEPKcS3_(); +int PS4_SYSV_ABI _ZN3sce2np9HttpTrans16SetRequestHeaderEPKcS3_(); +int PS4_SYSV_ABI _ZN3sce2np9HttpTrans21GetResponseStatusCodeEPNS0_6HandleEPi(); +int PS4_SYSV_ABI _ZN3sce2np9HttpTrans21SetRequestContentTypeEPKc(); +int PS4_SYSV_ABI _ZN3sce2np9HttpTrans23SetRequestContentLengthEm(); +int PS4_SYSV_ABI _ZN3sce2np9HttpTrans24GetResponseContentLengthEPNS0_6HandleEPbPm(); +int PS4_SYSV_ABI _ZN3sce2np9HttpTrans4InitERKNS0_12HttpTemplateEPNS0_18HttpConnectionPoolEiPKcm(); +int PS4_SYSV_ABI +_ZN3sce2np9HttpTrans4InitERKNS0_12HttpTemplateEPNS0_18HttpConnectionPoolEiPKcS8_tS8_m(); +int PS4_SYSV_ABI _ZN3sce2np9HttpTrans4ReadEPNS0_6HandleEPvmPm(); +int PS4_SYSV_ABI _ZN3sce2np9HttpTrans5WriteEPNS0_6HandleEPKvmPm(); +int PS4_SYSV_ABI _ZN3sce2np9HttpTrans7DestroyEv(); +int PS4_SYSV_ABI _ZN3sce2np9HttpTransC1EP16SceNpAllocatorEx(); +int PS4_SYSV_ABI _ZN3sce2np9HttpTransC2EP16SceNpAllocatorEx(); +int PS4_SYSV_ABI _ZN3sce2np9HttpTransD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np9HttpTransD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np9HttpTransD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np9JsonArray12AddItemArrayEPPS1_(); +int PS4_SYSV_ABI _ZN3sce2np9JsonArray5ClearEv(); +int PS4_SYSV_ABI _ZN3sce2np9JsonValue12GetItemValueEi(); +int PS4_SYSV_ABI _ZN3sce2np9JsonValue13GetFieldValueEiPPKc(); +int PS4_SYSV_ABI _ZN3sce2np9JsonValue13GetFieldValueEPKc(); +int PS4_SYSV_ABI _ZN3sce2np9JsonValueD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np9JsonValueD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np9JsonValueD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np9LocalFile4ReadEPNS0_6HandleEPvmPm(); +int PS4_SYSV_ABI _ZN3sce2np9LocalFile4SeekEliPl(); +int PS4_SYSV_ABI _ZN3sce2np9LocalFile4SyncEv(); +int PS4_SYSV_ABI _ZN3sce2np9LocalFile5CloseEv(); +int PS4_SYSV_ABI _ZN3sce2np9LocalFile5WriteEPNS0_6HandleEPKvmPm(); +int PS4_SYSV_ABI _ZN3sce2np9LocalFile6RemoveEPKc(); +int PS4_SYSV_ABI _ZN3sce2np9LocalFile8TruncateEl(); +int PS4_SYSV_ABI _ZN3sce2np9LocalFileC1Ev(); +int PS4_SYSV_ABI _ZN3sce2np9LocalFileC2Ev(); +int PS4_SYSV_ABI _ZN3sce2np9LocalFileD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np9LocalFileD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np9LocalFileD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np9NpTitleId5BuildERKS1_Pcm(); +int PS4_SYSV_ABI _ZN3sce2np9NpTitleId5ClearEv(); +int PS4_SYSV_ABI _ZN3sce2np9NpTitleId5ParseEPS1_PKc(); +int PS4_SYSV_ABI _ZN3sce2np9NpTitleId5ParseEPS1_PKcm(); +int PS4_SYSV_ABI _ZN3sce2np9NpTitleIdC1ERK12SceNpTitleId(); +int PS4_SYSV_ABI _ZN3sce2np9NpTitleIdC1ERKS1_(); +int PS4_SYSV_ABI _ZN3sce2np9NpTitleIdC1Ev(); +int PS4_SYSV_ABI _ZN3sce2np9NpTitleIdC2ERK12SceNpTitleId(); +int PS4_SYSV_ABI _ZN3sce2np9NpTitleIdC2ERKS1_(); +int PS4_SYSV_ABI _ZN3sce2np9NpTitleIdC2Ev(); +int PS4_SYSV_ABI _ZN3sce2np9NpTitleIdD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np9NpTitleIdD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np9NpTitleIdD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np9RefObject6AddRefEv(); +int PS4_SYSV_ABI _ZN3sce2np9RefObject7ReleaseEv(); +int PS4_SYSV_ABI _ZN3sce2np9RefObjectC1Ev(); +int PS4_SYSV_ABI _ZN3sce2np9RefObjectC2Ev(); +int PS4_SYSV_ABI _ZN3sce2np9RefObjectD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np9RefObjectD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np9RefObjectD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np9Semaphore4OpenEPKc(); +int PS4_SYSV_ABI _ZN3sce2np9Semaphore4WaitEj(); +int PS4_SYSV_ABI _ZN3sce2np9Semaphore6CreateEiiPKc(); +int PS4_SYSV_ABI _ZN3sce2np9Semaphore6SignalEv(); +int PS4_SYSV_ABI _ZN3sce2np9Semaphore7DestroyEv(); +int PS4_SYSV_ABI _ZN3sce2np9SemaphoreC1Ev(); +int PS4_SYSV_ABI _ZN3sce2np9SemaphoreC2Ev(); +int PS4_SYSV_ABI _ZN3sce2np9SemaphoreD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np9SemaphoreD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np9SemaphoreD2Ev(); +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue11GetItemByIdEi(); +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue15GetFinishedItemENS0_14WorkItemStatusE(); +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue16WorkItemFinishedEPNS0_8WorkItemEi(); +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue17ProcFinishedItemsENS0_14WorkItemStatusE(); +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue18RemoveFinishedItemEPNS0_8WorkItemE(); +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue18WaitForPendingItemEPPNS0_8WorkItemEPb(); +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue4ctorEv(); +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue4dtorEv(); +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue4InitEPKcimm(); +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue4InitEPKNS0_6Thread5ParamE(); +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue4StopEv(); +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue5StartEv(); +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue6CancelEii(); +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue6IsInitEv(); +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue7DestroyEv(); +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue7EnqueueEiPNS0_8WorkItemE(); +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue9CancelAllEi(); +int PS4_SYSV_ABI _ZN3sce2np9WorkQueue9IsRunningEv(); +int PS4_SYSV_ABI _ZN3sce2np9WorkQueueC1Ev(); +int PS4_SYSV_ABI _ZN3sce2np9WorkQueueC2Ev(); +int PS4_SYSV_ABI _ZN3sce2np9WorkQueueD0Ev(); +int PS4_SYSV_ABI _ZN3sce2np9WorkQueueD1Ev(); +int PS4_SYSV_ABI _ZN3sce2np9WorkQueueD2Ev(); +int PS4_SYSV_ABI _ZN3sce2npeqERK10SceRtcTickRKNS0_4TimeE(); +int PS4_SYSV_ABI _ZN3sce2npeqERK12SceNpTitleIdRKNS0_9NpTitleIdE(); +int PS4_SYSV_ABI _ZN3sce2npeqERK16SceNpTitleSecretRKNS0_13NpTitleSecretE(); +int PS4_SYSV_ABI _ZN3sce2npeqERK20SceNpCommunicationIdRKNS0_8NpCommIdE(); +int PS4_SYSV_ABI _ZN3sce2npeqERKNS0_13NpTitleSecretERK16SceNpTitleSecret(); +int PS4_SYSV_ABI _ZN3sce2npeqERKNS0_13NpTitleSecretES3_(); +int PS4_SYSV_ABI _ZN3sce2npeqERKNS0_4TimeERK10SceRtcTick(); +int PS4_SYSV_ABI _ZN3sce2npeqERKNS0_4TimeES3_(); +int PS4_SYSV_ABI _ZN3sce2npeqERKNS0_8NpCommIdERK20SceNpCommunicationId(); +int PS4_SYSV_ABI _ZN3sce2npeqERKNS0_8NpCommIdES3_(); +int PS4_SYSV_ABI _ZN3sce2npeqERKNS0_9NpTitleIdERK12SceNpTitleId(); +int PS4_SYSV_ABI _ZN3sce2npeqERKNS0_9NpTitleIdES3_(); +int PS4_SYSV_ABI _ZN3sce2npgeERK10SceRtcTickRKNS0_4TimeE(); +int PS4_SYSV_ABI _ZN3sce2npgeERKNS0_4TimeERK10SceRtcTick(); +int PS4_SYSV_ABI _ZN3sce2npgeERKNS0_4TimeES3_(); +int PS4_SYSV_ABI _ZN3sce2npgtERK10SceRtcTickRKNS0_4TimeE(); +int PS4_SYSV_ABI _ZN3sce2npgtERKNS0_4TimeERK10SceRtcTick(); +int PS4_SYSV_ABI _ZN3sce2npgtERKNS0_4TimeES3_(); +int PS4_SYSV_ABI _ZN3sce2npleERK10SceRtcTickRKNS0_4TimeE(); +int PS4_SYSV_ABI _ZN3sce2npleERKNS0_4TimeERK10SceRtcTick(); +int PS4_SYSV_ABI _ZN3sce2npleERKNS0_4TimeES3_(); +int PS4_SYSV_ABI _ZN3sce2npltERK10SceRtcTickRKNS0_4TimeE(); +int PS4_SYSV_ABI _ZN3sce2npltERKNS0_4TimeERK10SceRtcTick(); +int PS4_SYSV_ABI _ZN3sce2npltERKNS0_4TimeES3_(); +int PS4_SYSV_ABI _ZN3sce2npneERK10SceRtcTickRKNS0_4TimeE(); +int PS4_SYSV_ABI _ZN3sce2npneERK12SceNpTitleIdRKNS0_9NpTitleIdE(); +int PS4_SYSV_ABI _ZN3sce2npneERK16SceNpTitleSecretRKNS0_13NpTitleSecretE(); +int PS4_SYSV_ABI _ZN3sce2npneERK20SceNpCommunicationIdRKNS0_8NpCommIdE(); +int PS4_SYSV_ABI _ZN3sce2npneERKNS0_13NpTitleSecretERK16SceNpTitleSecret(); +int PS4_SYSV_ABI _ZN3sce2npneERKNS0_13NpTitleSecretES3_(); +int PS4_SYSV_ABI _ZN3sce2npneERKNS0_4TimeERK10SceRtcTick(); +int PS4_SYSV_ABI _ZN3sce2npneERKNS0_4TimeES3_(); +int PS4_SYSV_ABI _ZN3sce2npneERKNS0_8NpCommIdERK20SceNpCommunicationId(); +int PS4_SYSV_ABI _ZN3sce2npneERKNS0_8NpCommIdES3_(); +int PS4_SYSV_ABI _ZN3sce2npneERKNS0_9NpTitleIdERK12SceNpTitleId(); +int PS4_SYSV_ABI _ZN3sce2npneERKNS0_9NpTitleIdES3_(); +int PS4_SYSV_ABI _ZNK3sce2np10Cancelable6IsInitEv(); +int PS4_SYSV_ABI _ZNK3sce2np10EventQueue6IsInitEv(); +int PS4_SYSV_ABI _ZNK3sce2np10EventQueue7IsEmptyEv(); +int PS4_SYSV_ABI _ZNK3sce2np10JsonNumber5CloneEP16SceNpAllocatorEx(); +int PS4_SYSV_ABI _ZNK3sce2np10JsonNumber6GetNumEPcm(); +int PS4_SYSV_ABI _ZNK3sce2np10JsonNumber6GetNumEPi(); +int PS4_SYSV_ABI _ZNK3sce2np10JsonNumber6GetNumEPj(); +int PS4_SYSV_ABI _ZNK3sce2np10JsonNumber6GetNumEPl(); +int PS4_SYSV_ABI _ZNK3sce2np10JsonNumber6GetNumEPm(); +int PS4_SYSV_ABI _ZNK3sce2np10JsonNumber9GetNumStrEv(); +int PS4_SYSV_ABI _ZNK3sce2np10JsonObject5CloneEP16SceNpAllocatorEx(); +int PS4_SYSV_ABI _ZNK3sce2np10JsonString5CloneEP16SceNpAllocatorEx(); +int PS4_SYSV_ABI _ZNK3sce2np10JsonString6GetStrEPcm(); +int PS4_SYSV_ABI _ZNK3sce2np10JsonString6GetStrEv(); +int PS4_SYSV_ABI _ZNK3sce2np10JsonString9GetLengthEv(); +int PS4_SYSV_ABI _ZNK3sce2np12HttpTemplate6IsInitEv(); +int PS4_SYSV_ABI _ZNK3sce2np18HttpConnectionPool6IsInitEv(); +int PS4_SYSV_ABI _ZNK3sce2np3ipc10IpmiClient6IsInitEv(); +int PS4_SYSV_ABI _ZNK3sce2np3ipc17ServiceIpmiClient6IsInitEv(); +int PS4_SYSV_ABI _ZNK3sce2np4Cond6IsInitEv(); +int PS4_SYSV_ABI _ZNK3sce2np4Time18ConvertToPosixTimeEPl(); +int PS4_SYSV_ABI _ZNK3sce2np5Mutex6IsInitEv(); +int PS4_SYSV_ABI _ZNK3sce2np6Handle6IsInitEv(); +int PS4_SYSV_ABI _ZNK3sce2np6Thread6IsInitEv(); +int PS4_SYSV_ABI _ZNK3sce2np7RingBuf11GetDataSizeEv(); +int PS4_SYSV_ABI _ZNK3sce2np7RingBuf11GetFreeSizeEv(); +int PS4_SYSV_ABI _ZNK3sce2np7RingBuf6IsFullEv(); +int PS4_SYSV_ABI _ZNK3sce2np7RingBuf7IsEmptyEv(); +int PS4_SYSV_ABI _ZNK3sce2np8JsonBool5CloneEP16SceNpAllocatorEx(); +int PS4_SYSV_ABI _ZNK3sce2np8JsonBool7GetBoolEv(); +int PS4_SYSV_ABI _ZNK3sce2np8JsonNull5CloneEP16SceNpAllocatorEx(); +int PS4_SYSV_ABI _ZNK3sce2np8NpCommId7IsEmptyEv(); +int PS4_SYSV_ABI _ZNK3sce2np9EventFlag6IsInitEv(); +int PS4_SYSV_ABI _ZNK3sce2np9HttpTrans6IsInitEv(); +int PS4_SYSV_ABI _ZNK3sce2np9JsonArray5CloneEP16SceNpAllocatorEx(); +int PS4_SYSV_ABI _ZNK3sce2np9JsonValue12GetItemValueEi(); +int PS4_SYSV_ABI _ZNK3sce2np9NpTitleId7IsEmptyEv(); +int PS4_SYSV_ABI _ZNK3sce2np9Semaphore6IsInitEv(); +int PS4_SYSV_ABI _ZThn16_N3sce2np10MemoryFile5WriteEPNS0_6HandleEPKvmPm(); +int PS4_SYSV_ABI _ZThn16_N3sce2np10MemoryFileD0Ev(); +int PS4_SYSV_ABI _ZThn16_N3sce2np10MemoryFileD1Ev(); +int PS4_SYSV_ABI _ZThn16_N3sce2np9HttpTrans5WriteEPNS0_6HandleEPKvmPm(); +int PS4_SYSV_ABI _ZThn16_N3sce2np9HttpTransD0Ev(); +int PS4_SYSV_ABI _ZThn16_N3sce2np9HttpTransD1Ev(); +int PS4_SYSV_ABI _ZThn16_N3sce2np9LocalFile5WriteEPNS0_6HandleEPKvmPm(); +int PS4_SYSV_ABI _ZThn16_N3sce2np9LocalFileD0Ev(); +int PS4_SYSV_ABI _ZThn16_N3sce2np9LocalFileD1Ev(); +int PS4_SYSV_ABI _ZThn8_N3sce2np10MemoryFile4ReadEPNS0_6HandleEPvmPm(); +int PS4_SYSV_ABI _ZThn8_N3sce2np10MemoryFileD0Ev(); +int PS4_SYSV_ABI _ZThn8_N3sce2np10MemoryFileD1Ev(); +int PS4_SYSV_ABI _ZThn8_N3sce2np6Handle10CancelImplEi(); +int PS4_SYSV_ABI _ZThn8_N3sce2np6HandleD0Ev(); +int PS4_SYSV_ABI _ZThn8_N3sce2np6HandleD1Ev(); +int PS4_SYSV_ABI _ZThn8_N3sce2np9HttpTrans4ReadEPNS0_6HandleEPvmPm(); +int PS4_SYSV_ABI _ZThn8_N3sce2np9HttpTransD0Ev(); +int PS4_SYSV_ABI _ZThn8_N3sce2np9HttpTransD1Ev(); +int PS4_SYSV_ABI _ZThn8_N3sce2np9LocalFile4ReadEPNS0_6HandleEPvmPm(); +int PS4_SYSV_ABI _ZThn8_N3sce2np9LocalFileD0Ev(); +int PS4_SYSV_ABI _ZThn8_N3sce2np9LocalFileD1Ev(); +int PS4_SYSV_ABI _ZTVN3sce2np10JsonNumberE(); +int PS4_SYSV_ABI _ZTVN3sce2np10JsonObjectE(); +int PS4_SYSV_ABI _ZTVN3sce2np10JsonStringE(); +int PS4_SYSV_ABI _ZTVN3sce2np8JsonBoolE(); +int PS4_SYSV_ABI _ZTVN3sce2np8JsonNullE(); +int PS4_SYSV_ABI _ZTVN3sce2np8SelectorE(); +int PS4_SYSV_ABI _ZTVN3sce2np9JsonArrayE(); +int PS4_SYSV_ABI _ZTVN3sce2np9JsonValueE(); +int PS4_SYSV_ABI sceNpAllocateKernelMemoryNoAlignment(); +int PS4_SYSV_ABI sceNpAllocateKernelMemoryWithAlignment(); +int PS4_SYSV_ABI sceNpArchInit(); +int PS4_SYSV_ABI sceNpArchTerm(); +int PS4_SYSV_ABI sceNpAtomicCas32(); +int PS4_SYSV_ABI sceNpAtomicDec32(); +int PS4_SYSV_ABI sceNpAtomicInc32(); +int PS4_SYSV_ABI sceNpBase64Decoder(); +int PS4_SYSV_ABI sceNpBase64Encoder(); +int PS4_SYSV_ABI sceNpBase64GetDecodeSize(); +int PS4_SYSV_ABI sceNpBase64UrlDecoder(); +int PS4_SYSV_ABI sceNpBase64UrlEncoder(); +int PS4_SYSV_ABI sceNpBase64UrlGetDecodeSize(); +int PS4_SYSV_ABI sceNpCalloutInitCtx(); +int PS4_SYSV_ABI sceNpCalloutStartOnCtx(); +int PS4_SYSV_ABI sceNpCalloutStartOnCtx64(); +int PS4_SYSV_ABI sceNpCalloutStopOnCtx(); +int PS4_SYSV_ABI sceNpCalloutTermCtx(); +int PS4_SYSV_ABI sceNpCancelEventFlag(); +int PS4_SYSV_ABI sceNpClearEventFlag(); +int PS4_SYSV_ABI sceNpCloseEventFlag(); +int PS4_SYSV_ABI sceNpCloseSema(); +int PS4_SYSV_ABI sceNpCondDestroy(); +int PS4_SYSV_ABI sceNpCondInit(); +int PS4_SYSV_ABI sceNpCondSignal(); +int PS4_SYSV_ABI sceNpCondSignalAll(); +int PS4_SYSV_ABI sceNpCondSignalTo(); +int PS4_SYSV_ABI sceNpCondTimedwait(); +int PS4_SYSV_ABI sceNpCondWait(); +int PS4_SYSV_ABI sceNpCreateEventFlag(); +int PS4_SYSV_ABI sceNpCreateSema(); +int PS4_SYSV_ABI sceNpCreateThread(); +int PS4_SYSV_ABI sceNpDbgAssignDebugId(); +int PS4_SYSV_ABI sceNpDbgDumpBinary(); +int PS4_SYSV_ABI sceNpDbgDumpText(); +int PS4_SYSV_ABI sceNpDeleteEventFlag(); +int PS4_SYSV_ABI sceNpDeleteSema(); +int PS4_SYSV_ABI sceNpEventGetCurrentNetworkTick(); +int PS4_SYSV_ABI sceNpFreeKernelMemory(); +int PS4_SYSV_ABI sceNpGetNavSdkVersion(); +int PS4_SYSV_ABI sceNpGetPlatformType(); +int PS4_SYSV_ABI sceNpGetProcessId(); +int PS4_SYSV_ABI sceNpGetRandom(); +int PS4_SYSV_ABI sceNpGetSdkVersion(); +int PS4_SYSV_ABI sceNpGetSdkVersionUInt(); +int PS4_SYSV_ABI sceNpGetSystemClockUsec(); +int PS4_SYSV_ABI sceNpGlobalHeapGetAllocator(); +int PS4_SYSV_ABI sceNpGlobalHeapGetAllocatorEx(); +int PS4_SYSV_ABI sceNpGlobalHeapGetAllocatorExPtr(); +int PS4_SYSV_ABI sceNpGlobalHeapGetAllocatorPtr(); +int PS4_SYSV_ABI sceNpHeapDestroy(); +int PS4_SYSV_ABI sceNpHeapGetAllocator(); +int PS4_SYSV_ABI sceNpHeapGetStat(); +int PS4_SYSV_ABI sceNpHeapInit(); +int PS4_SYSV_ABI sceNpHeapShowStat(); +int PS4_SYSV_ABI sceNpHexToInt(); +int PS4_SYSV_ABI sceNpInt32ToStr(); +int PS4_SYSV_ABI sceNpInt64ToStr(); +int PS4_SYSV_ABI sceNpIntGetPlatformType(); +int PS4_SYSV_ABI sceNpIntIsOnlineIdString(); +int PS4_SYSV_ABI sceNpIntIsValidOnlineId(); +int PS4_SYSV_ABI sceNpIntSetPlatformType(); +int PS4_SYSV_ABI sceNpIntToHex(); +int PS4_SYSV_ABI sceNpIpc2ClientInit(); +int PS4_SYSV_ABI sceNpIpc2ClientTerm(); +int PS4_SYSV_ABI sceNpJoinThread(); +int PS4_SYSV_ABI sceNpJsonParse(); +int PS4_SYSV_ABI sceNpJsonParseBuf(); +int PS4_SYSV_ABI sceNpJsonParseBufInit(); +int PS4_SYSV_ABI sceNpJsonParseEx(); +int PS4_SYSV_ABI sceNpJsonParseExInit(); +int PS4_SYSV_ABI sceNpJsonParseInit(); +int PS4_SYSV_ABI sceNpLwCondDestroy(); +int PS4_SYSV_ABI sceNpLwCondInit(); +int PS4_SYSV_ABI sceNpLwCondSignal(); +int PS4_SYSV_ABI sceNpLwCondSignalAll(); +int PS4_SYSV_ABI sceNpLwCondSignalTo(); +int PS4_SYSV_ABI sceNpLwCondWait(); +int PS4_SYSV_ABI sceNpLwMutexDestroy(); +int PS4_SYSV_ABI sceNpLwMutexInit(); +int PS4_SYSV_ABI sceNpLwMutexLock(); +int PS4_SYSV_ABI sceNpLwMutexTryLock(); +int PS4_SYSV_ABI sceNpLwMutexUnlock(); +int PS4_SYSV_ABI sceNpMemoryHeapDestroy(); +int PS4_SYSV_ABI sceNpMemoryHeapGetAllocator(); +int PS4_SYSV_ABI sceNpMemoryHeapGetAllocatorEx(); +int PS4_SYSV_ABI sceNpMemoryHeapInit(); +int PS4_SYSV_ABI sceNpMutexDestroy(); +int PS4_SYSV_ABI sceNpMutexInit(); +int PS4_SYSV_ABI sceNpMutexLock(); +int PS4_SYSV_ABI sceNpMutexTryLock(); +int PS4_SYSV_ABI sceNpMutexUnlock(); +int PS4_SYSV_ABI sceNpOpenEventFlag(); +int PS4_SYSV_ABI sceNpOpenSema(); +int PS4_SYSV_ABI sceNpPanic(); +int PS4_SYSV_ABI sceNpPollEventFlag(); +int PS4_SYSV_ABI sceNpPollSema(); +int PS4_SYSV_ABI sceNpRtcConvertToPosixTime(); +int PS4_SYSV_ABI sceNpRtcFormatRFC3339(); +int PS4_SYSV_ABI sceNpRtcParseRFC3339(); +int PS4_SYSV_ABI sceNpServerErrorJsonGetErrorCode(); +int PS4_SYSV_ABI sceNpServerErrorJsonMultiGetErrorCode(); +int PS4_SYSV_ABI sceNpServerErrorJsonParse(); +int PS4_SYSV_ABI sceNpServerErrorJsonParseInit(); +int PS4_SYSV_ABI sceNpServerErrorJsonParseMultiInit(); +int PS4_SYSV_ABI sceNpSetEventFlag(); +int PS4_SYSV_ABI sceNpSetPlatformType(); +int PS4_SYSV_ABI sceNpSignalSema(); +int PS4_SYSV_ABI sceNpStrBuildHex(); +int PS4_SYSV_ABI sceNpStrcpyToBuf(); +int PS4_SYSV_ABI sceNpStrncpyToBuf(); +int PS4_SYSV_ABI sceNpStrnParseHex(); +int PS4_SYSV_ABI sceNpStrParseHex(); +int PS4_SYSV_ABI sceNpStrToInt32(); +int PS4_SYSV_ABI sceNpStrToInt64(); +int PS4_SYSV_ABI sceNpStrToUInt32(); +int PS4_SYSV_ABI sceNpStrToUInt64(); +int PS4_SYSV_ABI sceNpThreadGetId(); +int PS4_SYSV_ABI sceNpUInt32ToStr(); +int PS4_SYSV_ABI sceNpUInt64ToStr(); +int PS4_SYSV_ABI sceNpUserGetUserIdList(); +int PS4_SYSV_ABI sceNpUtilBuildTitleId(); +int PS4_SYSV_ABI sceNpUtilCanonicalizeNpIdForPs4(); +int PS4_SYSV_ABI sceNpUtilCanonicalizeNpIdForPsp2(); +int PS4_SYSV_ABI sceNpUtilCmpAccountId(); +int PS4_SYSV_ABI sceNpUtilGetDateSetAuto(); +int PS4_SYSV_ABI sceNpUtilGetDbgCommerce(); +int PS4_SYSV_ABI sceNpUtilGetEnv(); +int PS4_SYSV_ABI sceNpUtilGetFakeDisplayNameMode(); +int PS4_SYSV_ABI sceNpUtilGetFakeRateLimit(); +int PS4_SYSV_ABI sceNpUtilGetIgnoreNpTitleId(); +int PS4_SYSV_ABI sceNpUtilGetNpDebug(); +int PS4_SYSV_ABI sceNpUtilGetNpLanguageCode(); +int PS4_SYSV_ABI sceNpUtilGetNpLanguageCode2(); +int PS4_SYSV_ABI sceNpUtilGetNpLanguageCode2Str(); +int PS4_SYSV_ABI sceNpUtilGetNpLanguageCodeStr(); +int PS4_SYSV_ABI sceNpUtilGetNpTestPatch(); +int PS4_SYSV_ABI sceNpUtilGetNthChar(); +int PS4_SYSV_ABI sceNpUtilGetShareTitleCheck(); +int PS4_SYSV_ABI sceNpUtilGetSystemLanguage(); +int PS4_SYSV_ABI sceNpUtilGetTrcNotify(); +int PS4_SYSV_ABI sceNpUtilGetWebApi2FakeRateLimit(); +int PS4_SYSV_ABI sceNpUtilGetWebApi2FakeRateLimitTarget(); +int PS4_SYSV_ABI sceNpUtilGetWebTraceSetting(); +int PS4_SYSV_ABI sceNpUtilHttpUrlEncode(); +int PS4_SYSV_ABI sceNpUtilJidToNpId(); +int PS4_SYSV_ABI sceNpUtilJsonEscape(); +int PS4_SYSV_ABI sceNpUtilJsonGetOneChar(); +int PS4_SYSV_ABI sceNpUtilJsonUnescape(); +int PS4_SYSV_ABI sceNpUtilNpIdToJid(); +int PS4_SYSV_ABI sceNpUtilNumChars(); +int PS4_SYSV_ABI sceNpUtilParseJid(); +int PS4_SYSV_ABI sceNpUtilParseTitleId(); +int PS4_SYSV_ABI sceNpUtilSerializeJid(); +int PS4_SYSV_ABI sceNpUtilXmlEscape(); +int PS4_SYSV_ABI sceNpUtilXmlGetOneChar(); +int PS4_SYSV_ABI sceNpUtilXmlUnescape(); +int PS4_SYSV_ABI sceNpWaitEventFlag(); +int PS4_SYSV_ABI sceNpWaitSema(); +int PS4_SYSV_ABI sceNpXmlParse(); +int PS4_SYSV_ABI sceNpXmlParseInit(); +int PS4_SYSV_ABI Func_00FD578C2DD966DF(); +int PS4_SYSV_ABI Func_0131A2EA80689F4C(); +int PS4_SYSV_ABI Func_01443C54863BDD20(); +int PS4_SYSV_ABI Func_01BC55BDC5C0ADAD(); +int PS4_SYSV_ABI Func_01D1ECF5750F40E8(); +int PS4_SYSV_ABI Func_020A479A74F5FBAC(); +int PS4_SYSV_ABI Func_024AF5E1D9472AB5(); +int PS4_SYSV_ABI Func_027C5D488713A6B3(); +int PS4_SYSV_ABI Func_02FE9D94C6858355(); +int PS4_SYSV_ABI Func_041F34F1C70D15C1(); +int PS4_SYSV_ABI Func_0530B1D276114248(); +int PS4_SYSV_ABI Func_065DAA14E9C73AD9(); +int PS4_SYSV_ABI Func_06AFF4E5D042BC3E(); +int PS4_SYSV_ABI Func_06EE369299F73997(); +int PS4_SYSV_ABI Func_07C92D9F8D76B617(); +int PS4_SYSV_ABI Func_07E9117498F1E4BF(); +int PS4_SYSV_ABI Func_08F3E0AF3664F275(); +int PS4_SYSV_ABI Func_0A9937C01EF21375(); +int PS4_SYSV_ABI Func_0ACBE6ACCBA3876D(); +int PS4_SYSV_ABI Func_0AE07D3354510CE6(); +int PS4_SYSV_ABI Func_0AEC3C342AE67B7C(); +int PS4_SYSV_ABI Func_0B318420C11E7C23(); +int PS4_SYSV_ABI Func_0BB6C37B03F35D89(); +int PS4_SYSV_ABI Func_0BBE8A9ACDD90FDF(); +int PS4_SYSV_ABI Func_0C7B62905E224E9C(); +int PS4_SYSV_ABI Func_0D35913117241AF9(); +int PS4_SYSV_ABI Func_0D5EE95CEED879A7(); +int PS4_SYSV_ABI Func_0D6FB24B27AB1DA2(); +int PS4_SYSV_ABI Func_0DE8032D534AC41C(); +int PS4_SYSV_ABI Func_0DF4CCA9DCA9E742(); +int PS4_SYSV_ABI Func_0E7449B1D3D98C01(); +int PS4_SYSV_ABI Func_0E77094B7750CB37(); +int PS4_SYSV_ABI Func_0ECAB397B6D50603(); +int PS4_SYSV_ABI Func_0F1DE1D1EADA2948(); +int PS4_SYSV_ABI Func_0F8AFEFA1D26BF1A(); +int PS4_SYSV_ABI Func_11881710562A6BAD(); +int PS4_SYSV_ABI Func_11AFD88BBD0C70DB(); +int PS4_SYSV_ABI Func_11E704A30A4B8877(); +int PS4_SYSV_ABI Func_125014842452F94B(); +int PS4_SYSV_ABI Func_126F0071E11CAC46(); +int PS4_SYSV_ABI Func_12926DCF35994B01(); +int PS4_SYSV_ABI Func_12CC7ABFBF31618F(); +int PS4_SYSV_ABI Func_13C4E51F44592AA2(); +int PS4_SYSV_ABI Func_15330E7C56338254(); +int PS4_SYSV_ABI Func_1566B358CABF2612(); +int PS4_SYSV_ABI Func_1625818F268F45EF(); +int PS4_SYSV_ABI Func_16D32B40D28A9AC2(); +int PS4_SYSV_ABI Func_183F4483BDBD25CD(); +int PS4_SYSV_ABI Func_1887E9E95AF62F3D(); +int PS4_SYSV_ABI Func_18A3CE95FD893D3A(); +int PS4_SYSV_ABI Func_18B3665E4854E7E9(); +int PS4_SYSV_ABI Func_1923B003948AF47E(); +int PS4_SYSV_ABI Func_19B533DA4C59A532(); +int PS4_SYSV_ABI Func_1BB399772DB68E08(); +int PS4_SYSV_ABI Func_1C0AC612D3A2971B(); +int PS4_SYSV_ABI Func_1C5599B779990A43(); +int PS4_SYSV_ABI Func_1CCBB296B04317BE(); +int PS4_SYSV_ABI Func_1CD045542FB93002(); +int PS4_SYSV_ABI Func_1DECECA673AB77B7(); +int PS4_SYSV_ABI Func_1E03E024E26C1A7F(); +int PS4_SYSV_ABI Func_1F101732BB0D7E21(); +int PS4_SYSV_ABI Func_1F4D153EC3DD47BB(); +int PS4_SYSV_ABI Func_1F7C47F63FAF0CBE(); +int PS4_SYSV_ABI Func_1FBE2EE68C0F31B6(); +int PS4_SYSV_ABI Func_2038C1628914B9C9(); +int PS4_SYSV_ABI Func_203FCB56FDB86A74(); +int PS4_SYSV_ABI Func_20569C107C6CB08C(); +int PS4_SYSV_ABI Func_20AB2D734EDE55F0(); +int PS4_SYSV_ABI Func_22B1281180FB0A5E(); +int PS4_SYSV_ABI Func_22F1AADA66A449AE(); +int PS4_SYSV_ABI Func_238B215EFFDF3D30(); +int PS4_SYSV_ABI Func_24E8EC51D149FA15(); +int PS4_SYSV_ABI Func_25728E78A3962C02(); +int PS4_SYSV_ABI Func_25E649A1C6891C05(); +int PS4_SYSV_ABI Func_264B8A38B577705D(); +int PS4_SYSV_ABI Func_266ED08DC1C82A0E(); +int PS4_SYSV_ABI Func_27BB4DE62AB58BAD(); +int PS4_SYSV_ABI Func_283AA96A196EA2EA(); +int PS4_SYSV_ABI Func_285315A390A85A94(); +int PS4_SYSV_ABI Func_29049DBB1EF3194E(); +int PS4_SYSV_ABI Func_29F7BA9C3732CB47(); +int PS4_SYSV_ABI Func_2A732DF331ACCB37(); +int PS4_SYSV_ABI Func_2AA01660EC75B6FB(); +int PS4_SYSV_ABI Func_2B37CBCE941C1681(); +int PS4_SYSV_ABI Func_2CAA3B64D0544E55(); +int PS4_SYSV_ABI Func_2CCD79617EC10A75(); +int PS4_SYSV_ABI Func_2CD8B69716AC0667(); +int PS4_SYSV_ABI Func_2D74F7C0FF9B5E9C(); +int PS4_SYSV_ABI Func_2DCA5A8080544E95(); +int PS4_SYSV_ABI Func_2E69F2743CE7CE57(); +int PS4_SYSV_ABI Func_2EAF1F3BAFF0527D(); +int PS4_SYSV_ABI Func_31493E55BB4E8F66(); +int PS4_SYSV_ABI Func_317EDCAD00FB5F5E(); +int PS4_SYSV_ABI Func_31E01CFA8A18CDA2(); +int PS4_SYSV_ABI Func_32AFD782A061B526(); +int PS4_SYSV_ABI Func_32B5CDEB093B8189(); +int PS4_SYSV_ABI Func_34155152513C93AE(); +int PS4_SYSV_ABI Func_34E4EFFF8EF6C9FE(); +int PS4_SYSV_ABI Func_3572FA0D5C54563B(); +int PS4_SYSV_ABI Func_367C479B264E0DB9(); +int PS4_SYSV_ABI Func_36884FBC964B29CC(); +int PS4_SYSV_ABI Func_3860081BB7559949(); +int PS4_SYSV_ABI Func_39314F7E674AB132(); +int PS4_SYSV_ABI Func_3A02E780FCC556A5(); +int PS4_SYSV_ABI Func_3A17B885BA4849B6(); +int PS4_SYSV_ABI Func_3A38EACAEA5E23A4(); +int PS4_SYSV_ABI Func_3B34A5E07F0DBC1F(); +int PS4_SYSV_ABI Func_3B4E8FFC00FC7EA4(); +int PS4_SYSV_ABI Func_3BAB18FDA235107A(); +int PS4_SYSV_ABI Func_3BDF9996A0A33F11(); +int PS4_SYSV_ABI Func_3C1952F1A45CC37A(); +int PS4_SYSV_ABI Func_3CA37906CDB05F3B(); +int PS4_SYSV_ABI Func_3CDB2908ACEE3A6F(); +int PS4_SYSV_ABI Func_3D3ED165F2BDCD33(); +int PS4_SYSV_ABI Func_3DA4D7D1575FCDCE(); +int PS4_SYSV_ABI Func_3DDFB612CD0BC769(); +int PS4_SYSV_ABI Func_3E0415E167DEADC7(); +int PS4_SYSV_ABI Func_3E7E9F0F1581C1E6(); +int PS4_SYSV_ABI Func_3ED389DB8280ED65(); +int PS4_SYSV_ABI Func_3F0C7F6C0C35487D(); +int PS4_SYSV_ABI Func_3FDA7200389EF0D2(); +int PS4_SYSV_ABI Func_3FF3C258BA516E58(); +int PS4_SYSV_ABI Func_4029453F628A3C5D(); +int PS4_SYSV_ABI Func_405826DDB4AE538E(); +int PS4_SYSV_ABI Func_405A926759F25865(); +int PS4_SYSV_ABI Func_406608FDEE7AE88A(); +int PS4_SYSV_ABI Func_40DDA5558C17DDCF(); +int PS4_SYSV_ABI Func_419D12E52FF60664(); +int PS4_SYSV_ABI Func_4296E539474BE77F(); +int PS4_SYSV_ABI Func_42F41FC563CC3654(); +int PS4_SYSV_ABI Func_43CCC86F4C93026A(); +int PS4_SYSV_ABI Func_4409F60BDABC65E1(); +int PS4_SYSV_ABI Func_4563C70AEC675382(); +int PS4_SYSV_ABI Func_45E66370219BD05E(); +int PS4_SYSV_ABI Func_466A54F072785696(); +int PS4_SYSV_ABI Func_46CD2536976F209A(); +int PS4_SYSV_ABI Func_4863717BD2FDD157(); +int PS4_SYSV_ABI Func_4902EBD19A263149(); +int PS4_SYSV_ABI Func_4904F7FE8D83F40C(); +int PS4_SYSV_ABI Func_4A5E13F784ABFCE7(); +int PS4_SYSV_ABI Func_4B65EEB135C12781(); +int PS4_SYSV_ABI Func_4C19D49978DA85E2(); +int PS4_SYSV_ABI Func_4DE5D620FF66F136(); +int PS4_SYSV_ABI Func_4E170C12B57A8F9E(); +int PS4_SYSV_ABI Func_4E2F3FA405C3260C(); +int PS4_SYSV_ABI Func_4EA9350577513B4D(); +int PS4_SYSV_ABI Func_4F78EB6FC4B5F21F(); +int PS4_SYSV_ABI Func_50348BE4331117B7(); +int PS4_SYSV_ABI Func_508C7E8CDD281CAA(); +int PS4_SYSV_ABI Func_521C1D2C028F5A7E(); +int PS4_SYSV_ABI Func_522FF24A35E67291(); +int PS4_SYSV_ABI Func_5470FE90C25CDD4C(); +int PS4_SYSV_ABI Func_557F260F9A4ACD18(); +int PS4_SYSV_ABI Func_5586F97209F391EB(); +int PS4_SYSV_ABI Func_55B2C9B7ADA95C3C(); +int PS4_SYSV_ABI Func_55B488A3A540B936(); +int PS4_SYSV_ABI Func_5642DFE82AF43143(); +int PS4_SYSV_ABI Func_574E046F294AE187(); +int PS4_SYSV_ABI Func_578926EBF8AA6CBF(); +int PS4_SYSV_ABI Func_585DA5FC650896BC(); +int PS4_SYSV_ABI Func_58D6EB27349EC276(); +int PS4_SYSV_ABI Func_5906B7317949872D(); +int PS4_SYSV_ABI Func_5910B5614335BE70(); +int PS4_SYSV_ABI Func_593D7DA8911F08C9(); +int PS4_SYSV_ABI Func_59757FE6A93B0D53(); +int PS4_SYSV_ABI Func_598E60F862B1141E(); +int PS4_SYSV_ABI Func_5A45351666680DAF(); +int PS4_SYSV_ABI Func_5AABE9EA702E6A7F(); +int PS4_SYSV_ABI Func_5AEA4AE472355B80(); +int PS4_SYSV_ABI Func_5B20E53CDE598741(); +int PS4_SYSV_ABI Func_5B480B59FAE947E0(); +int PS4_SYSV_ABI Func_5B5EEC23690AB9BD(); +int PS4_SYSV_ABI Func_5C0AC5B0AF3EDAE0(); +int PS4_SYSV_ABI Func_5D2E999BEA0762D4(); +int PS4_SYSV_ABI Func_5D55BBFD45110E16(); +int PS4_SYSV_ABI Func_5DEE15403D2BB5FD(); +int PS4_SYSV_ABI Func_6020C708CA74B130(); +int PS4_SYSV_ABI Func_606E1415503C34D2(); +int PS4_SYSV_ABI Func_612140E8EE9A693E(); +int PS4_SYSV_ABI Func_61F13F551DAF61DF(); +int PS4_SYSV_ABI Func_6206D39131752328(); +int PS4_SYSV_ABI Func_621D4543EF0344DE(); +int PS4_SYSV_ABI Func_6259A9A8E56D0273(); +int PS4_SYSV_ABI Func_625F9C7016346F4E(); +int PS4_SYSV_ABI Func_62EF8DF746CD8C4A(); +int PS4_SYSV_ABI Func_636D2A99FD1E6B2B(); +int PS4_SYSV_ABI Func_68013EDF66FE7425(); +int PS4_SYSV_ABI Func_6971F7067DD639D1(); +int PS4_SYSV_ABI Func_69896ADB3AB410B2(); +int PS4_SYSV_ABI Func_6A1389AA6E561387(); +int PS4_SYSV_ABI Func_6A5560D89F12B2E7(); +int PS4_SYSV_ABI Func_6ABF99CF854ABCF1(); +int PS4_SYSV_ABI Func_6B4FDDC6500D8DCB(); +int PS4_SYSV_ABI Func_6CA11D5B49D1928A(); +int PS4_SYSV_ABI Func_6D6C0FB61E6D0715(); +int PS4_SYSV_ABI Func_6D750745FE1348F5(); +int PS4_SYSV_ABI Func_6E1AF3F9D09914BE(); +int PS4_SYSV_ABI Func_6E53ED4C08B2A521(); +int PS4_SYSV_ABI Func_6EF43ACA1ED6B968(); +int PS4_SYSV_ABI Func_6F6FA09F3E1B6A60(); +int PS4_SYSV_ABI Func_7035C340C7195901(); +int PS4_SYSV_ABI Func_7038E21CB5CF641B(); +int PS4_SYSV_ABI Func_706345DCDA5BA44D(); +int PS4_SYSV_ABI Func_7120714EBF10BF1F(); +int PS4_SYSV_ABI Func_713D28A91BC803DD(); +int PS4_SYSV_ABI Func_7153BD76A53AA012(); +int PS4_SYSV_ABI Func_715C625CC7041B6B(); +int PS4_SYSV_ABI Func_71E467BDB18711D0(); +int PS4_SYSV_ABI Func_720D17965C1F4E3F(); +int PS4_SYSV_ABI Func_734380C9BCF65B9A(); +int PS4_SYSV_ABI Func_73F4C08CCD4BBCCF(); +int PS4_SYSV_ABI Func_74403101B7B29D46(); +int PS4_SYSV_ABI Func_7525B081ACD66FF4(); +int PS4_SYSV_ABI Func_75BF4477C13A05CA(); +int PS4_SYSV_ABI Func_7609793F5987C6F7(); +int PS4_SYSV_ABI Func_7616ED01B04769AA(); +int PS4_SYSV_ABI Func_764F873D91A124D8(); +int PS4_SYSV_ABI Func_7706F1E123059565(); +int PS4_SYSV_ABI Func_77F2D07EB6D806E6(); +int PS4_SYSV_ABI Func_79C3704CDCD59E57(); +int PS4_SYSV_ABI Func_79DA0BBA21351545(); +int PS4_SYSV_ABI Func_79FA2447B5F3F0C4(); +int PS4_SYSV_ABI Func_7A4D6F65FF6195A5(); +int PS4_SYSV_ABI Func_7B3195CD114DECE7(); +int PS4_SYSV_ABI Func_7B3238F2301AD36D(); +int PS4_SYSV_ABI Func_7C77FC70750A3266(); +int PS4_SYSV_ABI Func_7D23A9DC459D6D18(); +int PS4_SYSV_ABI Func_7D5988C748D0A05F(); +int PS4_SYSV_ABI Func_7D9597147A99F4F4(); +int PS4_SYSV_ABI Func_7E2953F407DD8346(); +int PS4_SYSV_ABI Func_7EE34E5099709B32(); +int PS4_SYSV_ABI Func_80470E5511D5CA00(); +int PS4_SYSV_ABI Func_807179701C08F069(); +int PS4_SYSV_ABI Func_8096E81FFAF24E46(); +int PS4_SYSV_ABI Func_80B764F4F1B87042(); +int PS4_SYSV_ABI Func_80BF691438AD008B(); +int PS4_SYSV_ABI Func_80CF6CFC96012442(); +int PS4_SYSV_ABI Func_80EA772F8C0519FD(); +int PS4_SYSV_ABI Func_81D0AFD0084D327A(); +int PS4_SYSV_ABI Func_821EB8A72176FD67(); +int PS4_SYSV_ABI Func_82D2FAB54127273F(); +int PS4_SYSV_ABI Func_836AE669C42A59E9(); +int PS4_SYSV_ABI Func_8559A25BFEC3518C(); +int PS4_SYSV_ABI Func_85C1F66C767A49D2(); +int PS4_SYSV_ABI Func_8689ED1383F87BA7(); +int PS4_SYSV_ABI Func_8796CD9E5355D3A6(); +int PS4_SYSV_ABI Func_87D37EB6DDC19D99(); +int PS4_SYSV_ABI Func_880AA48F70F84FDD(); +int PS4_SYSV_ABI Func_897B07562093665B(); +int PS4_SYSV_ABI Func_8ACAF55F16368087(); +int PS4_SYSV_ABI Func_8AE8A5589B30D4E0(); +int PS4_SYSV_ABI Func_8AE997909831B331(); +int PS4_SYSV_ABI Func_8B2D640BE0D0FB99(); +int PS4_SYSV_ABI Func_8B3D9AB4668DAECB(); +int PS4_SYSV_ABI Func_8B5EFAAAACE0B46C(); +int PS4_SYSV_ABI Func_8C27943F40A988DB(); +int PS4_SYSV_ABI Func_8C54096C75F5F2D0(); +int PS4_SYSV_ABI Func_8D7663A0A5168814(); +int PS4_SYSV_ABI Func_8E618F509994FAD7(); +int PS4_SYSV_ABI Func_8F19E6CC064E2B98(); +int PS4_SYSV_ABI Func_8F6A8AEAEE922FF5(); +int PS4_SYSV_ABI Func_9010E1AD8EBBFBCA(); +int PS4_SYSV_ABI Func_90A955A0E7001AE9(); +int PS4_SYSV_ABI Func_90F9D6067FEECC05(); +int PS4_SYSV_ABI Func_9348F3D19546A1DA(); +int PS4_SYSV_ABI Func_93D3C011DB19388A(); +int PS4_SYSV_ABI Func_956E7A4FD9F89103(); +int PS4_SYSV_ABI Func_95F699E042C3E40F(); +int PS4_SYSV_ABI Func_96877B39AA0E8735(); +int PS4_SYSV_ABI Func_96CE07C49ED234EA(); +int PS4_SYSV_ABI Func_976BB178235B5681(); +int PS4_SYSV_ABI Func_978C0B25E588C4D6(); +int PS4_SYSV_ABI Func_98BA2612BEF238D6(); +int PS4_SYSV_ABI Func_995BDD4931AF9137(); +int PS4_SYSV_ABI Func_9966E39A926B7250(); +int PS4_SYSV_ABI Func_99C2306F18963464(); +int PS4_SYSV_ABI Func_99C92C613B776BA7(); +int PS4_SYSV_ABI Func_9A4E4B938CC8AD39(); +int PS4_SYSV_ABI Func_9B23F7B4B7F72081(); +int PS4_SYSV_ABI Func_9C0EAEEAE705A8DB(); +int PS4_SYSV_ABI Func_9D47AC59545DE9E8(); +int PS4_SYSV_ABI Func_A13052D8B1B2ACFA(); +int PS4_SYSV_ABI Func_A1AA43E3A78F6F62(); +int PS4_SYSV_ABI Func_A1E48CDF54649DC9(); +int PS4_SYSV_ABI Func_A2E7DEE5B0AF5D14(); +int PS4_SYSV_ABI Func_A2F5C7FD9FF113F5(); +int PS4_SYSV_ABI Func_A36296E2269D46BC(); +int PS4_SYSV_ABI Func_A3EE2A7B9F0D88AF(); +int PS4_SYSV_ABI Func_A4471F9F7E0BFA82(); +int PS4_SYSV_ABI Func_A449BBA521EA34E1(); +int PS4_SYSV_ABI Func_A48E666C334E726C(); +int PS4_SYSV_ABI Func_A49B7449B4DDE69C(); +int PS4_SYSV_ABI Func_A5748451125C9EA4(); +int PS4_SYSV_ABI Func_A690A28D648CC176(); +int PS4_SYSV_ABI Func_A6A86DE1B1CBB1D9(); +int PS4_SYSV_ABI Func_A8F2BB7B815740A1(); +int PS4_SYSV_ABI Func_A93F64C06A6F7397(); +int PS4_SYSV_ABI Func_AB35925FC97D6AA3(); +int PS4_SYSV_ABI Func_AC014AA2C991FA29(); +int PS4_SYSV_ABI Func_AC06E10901404AEB(); +int PS4_SYSV_ABI Func_AC75C68813523505(); +int PS4_SYSV_ABI Func_AD441BC497082C3E(); +int PS4_SYSV_ABI Func_AD4F25F021D354C3(); +int PS4_SYSV_ABI Func_ADFA04A85541A4FE(); +int PS4_SYSV_ABI Func_AE9610A6B5217A23(); +int PS4_SYSV_ABI Func_AF201923826F0A58(); +int PS4_SYSV_ABI Func_AFC021B4389CA3FA(); +int PS4_SYSV_ABI Func_B015E999A3373D8F(); +int PS4_SYSV_ABI Func_B0384B86107FC652(); +int PS4_SYSV_ABI Func_B0C630653B316563(); +int PS4_SYSV_ABI Func_B100DCCD88D5C73D(); +int PS4_SYSV_ABI Func_B11A3FEA5E4D9EA4(); +int PS4_SYSV_ABI Func_B2E7F8DC199C0B93(); +int PS4_SYSV_ABI Func_B3AB61A296F6DDC8(); +int PS4_SYSV_ABI Func_B3F32F6AE619EC82(); +int PS4_SYSV_ABI Func_B4227AB213BF8CF5(); +int PS4_SYSV_ABI Func_B4652BF42B604360(); +int PS4_SYSV_ABI Func_B536C1F13BFE97CB(); +int PS4_SYSV_ABI Func_B645CC264184BC89(); +int PS4_SYSV_ABI Func_B67E17B1582C6FBD(); +int PS4_SYSV_ABI Func_B6D047C5D7695A4D(); +int PS4_SYSV_ABI Func_B75ED8E1EA62EFC7(); +int PS4_SYSV_ABI Func_B7A9A944DBD7E100(); +int PS4_SYSV_ABI Func_B7C4E75BE94F31F3(); +int PS4_SYSV_ABI Func_B888B1F92C464121(); +int PS4_SYSV_ABI Func_B8DEC22564AA057B(); +int PS4_SYSV_ABI Func_B9BADD1CBBBAE4F8(); +int PS4_SYSV_ABI Func_BAA9F7169C85E59F(); +int PS4_SYSV_ABI Func_BAEE5C38908D62DB(); +int PS4_SYSV_ABI Func_BCC855EB25183F84(); +int PS4_SYSV_ABI Func_BD01F637029C7364(); +int PS4_SYSV_ABI Func_BDD29F5AC7077E53(); +int PS4_SYSV_ABI Func_BED83DD33ECAD50D(); +int PS4_SYSV_ABI Func_BEE7D5D098ABF728(); +int PS4_SYSV_ABI Func_C0DB15CCF59AE62C(); +int PS4_SYSV_ABI Func_C1C229FEE0FD60FA(); +int PS4_SYSV_ABI Func_C228B9AD68298E98(); +int PS4_SYSV_ABI Func_C298525CEF6FB283(); +int PS4_SYSV_ABI Func_C350F09351F6D6B5(); +int PS4_SYSV_ABI Func_C3742E80FA580319(); +int PS4_SYSV_ABI Func_C3C9853D5D4D45D4(); +int PS4_SYSV_ABI Func_C3F5DAD4FB9FC340(); +int PS4_SYSV_ABI Func_C45FB0E4CCE9AED6(); +int PS4_SYSV_ABI Func_C4979CB948B7E3C7(); +int PS4_SYSV_ABI Func_C49B25BA16CF0B8C(); +int PS4_SYSV_ABI Func_C551345D9631201E(); +int PS4_SYSV_ABI Func_C57A294421368298(); +int PS4_SYSV_ABI Func_C5DC91CAD721D628(); +int PS4_SYSV_ABI Func_C6DECEE589135357(); +int PS4_SYSV_ABI Func_C81F8B20D67AC78D(); +int PS4_SYSV_ABI Func_C820FA56FAC87BEA(); +int PS4_SYSV_ABI Func_C878EA9114C5E490(); +int PS4_SYSV_ABI Func_C8A813EBFF477509(); +int PS4_SYSV_ABI Func_C966A663D5A35482(); +int PS4_SYSV_ABI Func_C97C4C67FD3674D3(); +int PS4_SYSV_ABI Func_C990550F15848B07(); +int PS4_SYSV_ABI Func_CA59737A8EC1BBBE(); +int PS4_SYSV_ABI Func_CAC5FDE8F80D7B65(); +int PS4_SYSV_ABI Func_CB135B30D0639B83(); +int PS4_SYSV_ABI Func_CB8A1AAA61F64C3A(); +int PS4_SYSV_ABI Func_CB9E674672580757(); +int PS4_SYSV_ABI Func_CC2B9D25EAEAAB1D(); +int PS4_SYSV_ABI Func_CD1B252BBEDF5B53(); +int PS4_SYSV_ABI Func_CF003BE90CBE1A27(); +int PS4_SYSV_ABI Func_CF008E34884AC1E2(); +int PS4_SYSV_ABI Func_D0B8F4B3A3687AB2(); +int PS4_SYSV_ABI Func_D0EE19B8E91F60F5(); +int PS4_SYSV_ABI Func_D12B9294BD0E0F56(); +int PS4_SYSV_ABI Func_D1CC8626D8FA328B(); +int PS4_SYSV_ABI Func_D2FA2BB9EB8B63AC(); +int PS4_SYSV_ABI Func_D32197880CF93CEB(); +int PS4_SYSV_ABI Func_D326F5C26CC81B8E(); +int PS4_SYSV_ABI Func_D4FA06B95A321B7A(); +int PS4_SYSV_ABI Func_D52A37A901E04B21(); +int PS4_SYSV_ABI Func_D5504DFC399AB400(); +int PS4_SYSV_ABI Func_D56105CB27F8F5DC(); +int PS4_SYSV_ABI Func_D568AB19235ECB19(); +int PS4_SYSV_ABI Func_D6DF7BF6639FE611(); +int PS4_SYSV_ABI Func_D8608A903119D746(); +int PS4_SYSV_ABI Func_D9E8FC707D59914D(); +int PS4_SYSV_ABI Func_D9F079E62DEE5B29(); +int PS4_SYSV_ABI Func_DA17CE4F29748536(); +int PS4_SYSV_ABI Func_DA40B9EFD7F61185(); +int PS4_SYSV_ABI Func_DA6B274FEBC2666A(); +int PS4_SYSV_ABI Func_DAD01535C87A51FC(); +int PS4_SYSV_ABI Func_DB4511D448510EC4(); +int PS4_SYSV_ABI Func_DB8EF1FFFC66269C(); +int PS4_SYSV_ABI Func_DBB508FA1B9DA8F7(); +int PS4_SYSV_ABI Func_DC59C9B870B729A2(); +int PS4_SYSV_ABI Func_DC669ED6CBF6751C(); +int PS4_SYSV_ABI Func_DCB8A2849A41C991(); +int PS4_SYSV_ABI Func_DD8F9916D7F03AF7(); +int PS4_SYSV_ABI Func_DDC33F2F4E480C2A(); +int PS4_SYSV_ABI Func_DE0B420BDE8B22D7(); +int PS4_SYSV_ABI Func_E0C0BC29898FE370(); +int PS4_SYSV_ABI Func_E0CD893E46FB55BA(); +int PS4_SYSV_ABI Func_E25530164B7F659F(); +int PS4_SYSV_ABI Func_E3682F43FDF76C58(); +int PS4_SYSV_ABI Func_E38177E1C78A80FA(); +int PS4_SYSV_ABI Func_E3CA74CFF965DF0A(); +int PS4_SYSV_ABI Func_E45BB191B49B2ED9(); +int PS4_SYSV_ABI Func_E465B9D6B60E6D7D(); +int PS4_SYSV_ABI Func_E4D82876C296C38A(); +int PS4_SYSV_ABI Func_E4DDB5350FA5B538(); +int PS4_SYSV_ABI Func_E54BFF6FB72BC7BE(); +int PS4_SYSV_ABI Func_E592A93203020BBB(); +int PS4_SYSV_ABI Func_E5A44AF6D7D48AFD(); +int PS4_SYSV_ABI Func_E639A97CF9FF1430(); +int PS4_SYSV_ABI Func_E6AC0179E48A8927(); +int PS4_SYSV_ABI Func_E751596682775D83(); +int PS4_SYSV_ABI Func_E788B1E52EF82702(); +int PS4_SYSV_ABI Func_E94F17613F5C9D31(); +int PS4_SYSV_ABI Func_E9590113128D55E0(); +int PS4_SYSV_ABI Func_E9E0B0DD12560B16(); +int PS4_SYSV_ABI Func_EAF5C8ECE64C7B05(); +int PS4_SYSV_ABI Func_EB98BF5C42D4A7EB(); +int PS4_SYSV_ABI Func_EBABC4AAC43A468C(); +int PS4_SYSV_ABI Func_EBF00085F082CC8B(); +int PS4_SYSV_ABI Func_ECB659EE058D06AF(); +int PS4_SYSV_ABI Func_ECF096AB751487AE(); +int PS4_SYSV_ABI Func_EE5A271701DB33C0(); +int PS4_SYSV_ABI Func_EF64CB6A1625248E(); +int PS4_SYSV_ABI Func_EF6C8A357C7ED863(); +int PS4_SYSV_ABI Func_F00FE94F7E699994(); +int PS4_SYSV_ABI Func_F1A51DBA30329038(); +int PS4_SYSV_ABI Func_F216E766A90FDC12(); +int PS4_SYSV_ABI Func_F2A10584ABE5D82C(); +int PS4_SYSV_ABI Func_F2D99D395E5421A3(); +int PS4_SYSV_ABI Func_F38001E528BA1371(); +int PS4_SYSV_ABI Func_F39EC9C8FA7687B3(); +int PS4_SYSV_ABI Func_F3AFFFDCD632775C(); +int PS4_SYSV_ABI Func_F3B8DFF33748BFD3(); +int PS4_SYSV_ABI Func_F5E47F9550F7A147(); +int PS4_SYSV_ABI Func_F6E93714D1A939CF(); +int PS4_SYSV_ABI Func_F6FD19AD48E4EF09(); +int PS4_SYSV_ABI Func_F744EBFC620F7CBF(); +int PS4_SYSV_ABI Func_F76E4525ACBACC7F(); +int PS4_SYSV_ABI Func_F7957A48882F42CB(); +int PS4_SYSV_ABI Func_F7A80B07809BA838(); +int PS4_SYSV_ABI Func_F8571C6CC5B6B59D(); +int PS4_SYSV_ABI Func_F9787CFA873836FB(); +int PS4_SYSV_ABI Func_FA789F6D34D383F8(); +int PS4_SYSV_ABI Func_FABA574083AC1E6C(); +int PS4_SYSV_ABI Func_FC04FDBBAE368FB7(); +int PS4_SYSV_ABI Func_FD2DAFBF2E40EEE7(); +int PS4_SYSV_ABI Func_FD55EE6D35F950AD(); +int PS4_SYSV_ABI Func_FE55EE32098D0D58(); +int PS4_SYSV_ABI Func_FE79841022E1DA1C(); +int PS4_SYSV_ABI Func_FFF4A3E279FB44A7(); + +void RegisterlibSceNpCommon(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::NpCommon \ No newline at end of file diff --git a/src/core/libraries/np_common/np_common_error.h b/src/core/libraries/np_common/np_common_error.h new file mode 100644 index 000000000..5da6e6a90 --- /dev/null +++ b/src/core/libraries/np_common/np_common_error.h @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +constexpr int ORBIS_NP_ERROR_INVALID_ARGUMENT = 0x80550003; +constexpr int ORBIS_NP_UTIL_ERROR_NOT_MATCH = 0x80550609; \ No newline at end of file diff --git a/src/core/libraries/np_manager/np_manager.cpp b/src/core/libraries/np_manager/np_manager.cpp index 87d752c69..3489e3e41 100644 --- a/src/core/libraries/np_manager/np_manager.cpp +++ b/src/core/libraries/np_manager/np_manager.cpp @@ -1,7 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "common/config.h" #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" From 5810c88c00390026ecc33a4244190843afe19b94 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sat, 11 Jan 2025 12:04:46 -0800 Subject: [PATCH 397/549] hotfix: Fix cube instructions. --- .../frontend/translate/vector_alu.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/shader_recompiler/frontend/translate/vector_alu.cpp b/src/shader_recompiler/frontend/translate/vector_alu.cpp index 375c5f078..9d0cd8b4d 100644 --- a/src/shader_recompiler/frontend/translate/vector_alu.cpp +++ b/src/shader_recompiler/frontend/translate/vector_alu.cpp @@ -1069,9 +1069,9 @@ void Translator::V_CUBEID_F32(const GcnInst& inst) { const auto x_neg_cond{ir.FPLessThan(x, ir.Imm32(0.f))}; const auto y_neg_cond{ir.FPLessThan(y, ir.Imm32(0.f))}; const auto z_neg_cond{ir.FPLessThan(z, ir.Imm32(0.f))}; - const IR::F32 x_face{ir.Select(x_neg_cond, ir.Imm32(5.f), ir.Imm32(4.f))}; + const IR::F32 x_face{ir.Select(x_neg_cond, ir.Imm32(1.f), ir.Imm32(0.f))}; const IR::F32 y_face{ir.Select(y_neg_cond, ir.Imm32(3.f), ir.Imm32(2.f))}; - const IR::F32 z_face{ir.Select(z_neg_cond, ir.Imm32(1.f), ir.Imm32(0.f))}; + const IR::F32 z_face{ir.Select(z_neg_cond, ir.Imm32(5.f), ir.Imm32(4.f))}; result = SelectCubeResult(x, y, z, x_face, y_face, z_face); } @@ -1090,10 +1090,11 @@ void Translator::V_CUBESC_F32(const GcnInst& inst) { } else { const auto x_neg_cond{ir.FPLessThan(x, ir.Imm32(0.f))}; const auto z_neg_cond{ir.FPLessThan(z, ir.Imm32(0.f))}; - const IR::F32 x_sc{ir.Select(x_neg_cond, ir.FPNeg(x), x)}; - const IR::F32 z_sc{ir.Select(z_neg_cond, z, ir.FPNeg(z))}; + const IR::F32 x_sc{ir.Select(x_neg_cond, z, ir.FPNeg(z))}; + const IR::F32 y_sc{x}; + const IR::F32 z_sc{ir.Select(z_neg_cond, ir.FPNeg(x), x)}; - result = SelectCubeResult(x, y, z, x_sc, x, z_sc); + result = SelectCubeResult(x, y, z, x_sc, y_sc, z_sc); } SetDst(inst.dst[0], result); } @@ -1109,10 +1110,10 @@ void Translator::V_CUBETC_F32(const GcnInst& inst) { result = IR::F32{ir.CompositeExtract(coords, 1)}; } else { const auto y_neg_cond{ir.FPLessThan(y, ir.Imm32(0.f))}; - const IR::F32 x_z_sc{ir.FPNeg(y)}; - const IR::F32 y_sc{ir.Select(y_neg_cond, ir.FPNeg(z), z)}; + const IR::F32 x_z_tc{ir.FPNeg(y)}; + const IR::F32 y_tc{ir.Select(y_neg_cond, ir.FPNeg(z), z)}; - result = SelectCubeResult(x, y, z, x_z_sc, y_sc, x_z_sc); + result = SelectCubeResult(x, y, z, x_z_tc, y_tc, x_z_tc); } SetDst(inst.dst[0], result); } @@ -1122,7 +1123,7 @@ void Translator::V_CUBEMA_F32(const GcnInst& inst) { const auto y = GetSrc(inst.src[1]); const auto z = GetSrc(inst.src[2]); - const auto two{ir.Imm32(4.f)}; + const auto two{ir.Imm32(2.f)}; const IR::F32 x_major_axis{ir.FPMul(x, two)}; const IR::F32 y_major_axis{ir.FPMul(y, two)}; const IR::F32 z_major_axis{ir.FPMul(z, two)}; From 82cb298c5c666958598a1673aed63b95f66f2963 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sat, 11 Jan 2025 13:57:49 -0800 Subject: [PATCH 398/549] shader_recompiler: Remove AMD native CubeFaceCoord. (#2129) --- .../backend/spirv/emit_spirv_image.cpp | 8 ----- .../backend/spirv/emit_spirv_instructions.h | 1 - .../frontend/translate/vector_alu.cpp | 32 ++++++------------- src/shader_recompiler/ir/ir_emitter.cpp | 4 --- src/shader_recompiler/ir/ir_emitter.h | 1 - src/shader_recompiler/ir/opcodes.inc | 1 - 6 files changed, 10 insertions(+), 37 deletions(-) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index b5ae507a0..e2a969b61 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp @@ -255,14 +255,6 @@ void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id ctx.OpImageWrite(image, coords, texel, operands.mask, operands.operands); } -Id EmitCubeFaceCoord(EmitContext& ctx, IR::Inst* inst, Id cube_coords) { - if (ctx.profile.supports_native_cube_calc) { - return ctx.OpCubeFaceCoordAMD(ctx.F32[2], cube_coords); - } else { - UNREACHABLE_MSG("SPIR-V Instruction"); - } -} - Id EmitCubeFaceIndex(EmitContext& ctx, IR::Inst* inst, Id cube_coords) { if (ctx.profile.supports_native_cube_calc) { return ctx.OpCubeFaceIndexAMD(ctx.F32[1], cube_coords); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index 37b6f7786..f0bb9fd7e 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h @@ -439,7 +439,6 @@ Id EmitImageAtomicAnd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id EmitImageAtomicOr32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value); Id EmitImageAtomicXor32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value); Id EmitImageAtomicExchange32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value); -Id EmitCubeFaceCoord(EmitContext& ctx, IR::Inst* inst, Id cube_coords); Id EmitCubeFaceIndex(EmitContext& ctx, IR::Inst* inst, Id cube_coords); Id EmitLaneId(EmitContext& ctx); Id EmitWarpId(EmitContext& ctx); diff --git a/src/shader_recompiler/frontend/translate/vector_alu.cpp b/src/shader_recompiler/frontend/translate/vector_alu.cpp index 9d0cd8b4d..fd5877c57 100644 --- a/src/shader_recompiler/frontend/translate/vector_alu.cpp +++ b/src/shader_recompiler/frontend/translate/vector_alu.cpp @@ -1083,19 +1083,13 @@ void Translator::V_CUBESC_F32(const GcnInst& inst) { const auto y = GetSrc(inst.src[1]); const auto z = GetSrc(inst.src[2]); - IR::F32 result; - if (profile.supports_native_cube_calc) { - const auto coords{ir.CubeFaceCoord(ir.CompositeConstruct(x, y, z))}; - result = IR::F32{ir.CompositeExtract(coords, 0)}; - } else { - const auto x_neg_cond{ir.FPLessThan(x, ir.Imm32(0.f))}; - const auto z_neg_cond{ir.FPLessThan(z, ir.Imm32(0.f))}; - const IR::F32 x_sc{ir.Select(x_neg_cond, z, ir.FPNeg(z))}; - const IR::F32 y_sc{x}; - const IR::F32 z_sc{ir.Select(z_neg_cond, ir.FPNeg(x), x)}; + const auto x_neg_cond{ir.FPLessThan(x, ir.Imm32(0.f))}; + const auto z_neg_cond{ir.FPLessThan(z, ir.Imm32(0.f))}; + const IR::F32 x_sc{ir.Select(x_neg_cond, z, ir.FPNeg(z))}; + const IR::F32 y_sc{x}; + const IR::F32 z_sc{ir.Select(z_neg_cond, ir.FPNeg(x), x)}; - result = SelectCubeResult(x, y, z, x_sc, y_sc, z_sc); - } + const auto result{SelectCubeResult(x, y, z, x_sc, y_sc, z_sc)}; SetDst(inst.dst[0], result); } @@ -1104,17 +1098,11 @@ void Translator::V_CUBETC_F32(const GcnInst& inst) { const auto y = GetSrc(inst.src[1]); const auto z = GetSrc(inst.src[2]); - IR::F32 result; - if (profile.supports_native_cube_calc) { - const auto coords{ir.CubeFaceCoord(ir.CompositeConstruct(x, y, z))}; - result = IR::F32{ir.CompositeExtract(coords, 1)}; - } else { - const auto y_neg_cond{ir.FPLessThan(y, ir.Imm32(0.f))}; - const IR::F32 x_z_tc{ir.FPNeg(y)}; - const IR::F32 y_tc{ir.Select(y_neg_cond, ir.FPNeg(z), z)}; + const auto y_neg_cond{ir.FPLessThan(y, ir.Imm32(0.f))}; + const IR::F32 x_z_tc{ir.FPNeg(y)}; + const IR::F32 y_tc{ir.Select(y_neg_cond, ir.FPNeg(z), z)}; - result = SelectCubeResult(x, y, z, x_z_tc, y_tc, x_z_tc); - } + const auto result{SelectCubeResult(x, y, z, x_z_tc, y_tc, x_z_tc)}; SetDst(inst.dst[0], result); } diff --git a/src/shader_recompiler/ir/ir_emitter.cpp b/src/shader_recompiler/ir/ir_emitter.cpp index 8626bdfd1..5ac08e7dc 100644 --- a/src/shader_recompiler/ir/ir_emitter.cpp +++ b/src/shader_recompiler/ir/ir_emitter.cpp @@ -1758,10 +1758,6 @@ void IREmitter::ImageWrite(const Value& handle, const Value& coords, const U32& Inst(Opcode::ImageWrite, Flags{info}, handle, coords, lod, multisampling, color); } -[[nodiscard]] Value IREmitter::CubeFaceCoord(const Value& cube_coords) { - return Inst(Opcode::CubeFaceCoord, cube_coords); -} - [[nodiscard]] F32 IREmitter::CubeFaceIndex(const Value& cube_coords) { return Inst(Opcode::CubeFaceIndex, cube_coords); } diff --git a/src/shader_recompiler/ir/ir_emitter.h b/src/shader_recompiler/ir/ir_emitter.h index 783709775..d1dc44d74 100644 --- a/src/shader_recompiler/ir/ir_emitter.h +++ b/src/shader_recompiler/ir/ir_emitter.h @@ -342,7 +342,6 @@ public: void ImageWrite(const Value& handle, const Value& coords, const U32& lod, const U32& multisampling, const Value& color, TextureInstInfo info); - [[nodiscard]] Value CubeFaceCoord(const Value& cube_coords); [[nodiscard]] F32 CubeFaceIndex(const Value& cube_coords); void EmitVertex(); diff --git a/src/shader_recompiler/ir/opcodes.inc b/src/shader_recompiler/ir/opcodes.inc index 19f45418f..b45151dba 100644 --- a/src/shader_recompiler/ir/opcodes.inc +++ b/src/shader_recompiler/ir/opcodes.inc @@ -375,7 +375,6 @@ OPCODE(ImageAtomicXor32, U32, Opaq OPCODE(ImageAtomicExchange32, U32, Opaque, Opaque, U32, ) // Cube operations - optional, usable if profile.supports_native_cube_calc -OPCODE(CubeFaceCoord, F32x2, F32x3, ) OPCODE(CubeFaceIndex, F32, F32x3, ) // Warp operations From 466e071c97af6ab734bbeb55d73f04e27135fd38 Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Sun, 12 Jan 2025 03:24:12 -0600 Subject: [PATCH 399/549] Add libSceSsl2 stubs (#2132) * Auto-generate libSceSsl2 stubs * Copy over sceSslInit stub * Update CMakeLists.txt * Swap to Lib_Ssl2 log category * Fix compile Since libSceSsl has many functions of the same name, these functions get treated as overloaded functions and break compiling. * Clang --- CMakeLists.txt | 2 + src/common/logging/filter.cpp | 1 + src/common/logging/types.h | 1 + src/core/libraries/libs.cpp | 2 + src/core/libraries/network/ssl2.cpp | 353 ++++++++++++++++++++++++++++ src/core/libraries/network/ssl2.h | 14 ++ 6 files changed, 373 insertions(+) create mode 100644 src/core/libraries/network/ssl2.cpp create mode 100644 src/core/libraries/network/ssl2.h diff --git a/CMakeLists.txt b/CMakeLists.txt index aee01a3a5..84eaefbbb 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -263,6 +263,8 @@ set(NETWORK_LIBS src/core/libraries/network/http.cpp src/core/libraries/network/net.h src/core/libraries/network/ssl.cpp src/core/libraries/network/ssl.h + src/core/libraries/network/ssl2.cpp + src/core/libraries/network/ssl2.h ) set(AVPLAYER_LIB src/core/libraries/avplayer/avplayer_common.cpp diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index b15fb07be..6fe2ec4e4 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -96,6 +96,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Lib, SaveDataDialog) \ SUB(Lib, Http) \ SUB(Lib, Ssl) \ + SUB(Lib, Ssl2) \ SUB(Lib, SysModule) \ SUB(Lib, Move) \ SUB(Lib, NpCommon) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index da4cf65e7..c42cf5665 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -63,6 +63,7 @@ enum class Class : u8 { Lib_SaveData, ///< The LibSceSaveData implementation. Lib_SaveDataDialog, ///< The LibSceSaveDataDialog implementation. Lib_Ssl, ///< The LibSceSsl implementation. + Lib_Ssl2, ///< The LibSceSsl2 implementation. Lib_Http, ///< The LibSceHttp implementation. Lib_SysModule, ///< The LibSceSysModule implementation Lib_NpCommon, ///< The LibSceNpCommon implementation diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index d03edf28e..b651bab44 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -24,6 +24,7 @@ #include "core/libraries/network/net.h" #include "core/libraries/network/netctl.h" #include "core/libraries/network/ssl.h" +#include "core/libraries/network/ssl2.h" #include "core/libraries/np_common/np_common.h" #include "core/libraries/np_manager/np_manager.h" #include "core/libraries/np_score/np_score.h" @@ -70,6 +71,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::SaveData::RegisterlibSceSaveData(sym); Libraries::SaveData::Dialog::RegisterlibSceSaveDataDialog(sym); Libraries::Ssl::RegisterlibSceSsl(sym); + Libraries::Ssl2::RegisterlibSceSsl2(sym); Libraries::SysModule::RegisterlibSceSysmodule(sym); Libraries::Posix::Registerlibsceposix(sym); Libraries::AudioIn::RegisterlibSceAudioIn(sym); diff --git a/src/core/libraries/network/ssl2.cpp b/src/core/libraries/network/ssl2.cpp new file mode 100644 index 000000000..8ca29526e --- /dev/null +++ b/src/core/libraries/network/ssl2.cpp @@ -0,0 +1,353 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" +#include "core/libraries/network/ssl2.h" + +namespace Libraries::Ssl2 { + +int PS4_SYSV_ABI CA_MGMT_extractKeyBlobEx() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI CA_MGMT_extractPublicKeyInfo() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI CA_MGMT_freeKeyBlob() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI CRYPTO_initAsymmetricKey() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI CRYPTO_uninitAsymmetricKey() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI RSA_verifySignature() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslCheckRecvPending() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslClose() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslConnect() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslCreateConnection() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslCreateSslConnection() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslDeleteConnection() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslDeleteSslConnection() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslDisableOption() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslDisableOptionInternal() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslDisableOptionInternalInsecure() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslDisableVerifyOption() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslEnableOption() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslEnableOptionInternal() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslEnableVerifyOption() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslFreeCaCerts() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslFreeCaList() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslFreeSslCertName() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslGetAlpnSelected() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslGetCaCerts() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslGetCaList() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslGetFingerprint() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslGetIssuerName() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslGetMemoryPoolStats() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslGetNameEntryCount() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslGetNameEntryInfo() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslGetNanoSSLModuleId() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslGetNotAfter() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslGetNotBefore() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslGetPeerCert() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslGetPem() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslGetSerialNumber() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslGetSslError() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslGetSubjectName() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslInit(std::size_t poolSize) { + LOG_ERROR(Lib_Ssl2, "(DUMMY) called poolSize = {}", poolSize); + // return a value >1 + static int id = 0; + return ++id; +} + +int PS4_SYSV_ABI sceSslLoadCert() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslLoadRootCACert() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslRead() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslRecv() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslReuseConnection() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslSend() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslSetAlpn() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslSetMinSslVersion() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslSetSslVersion() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslSetVerifyCallback() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslTerm() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslUnloadCert() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSslWrite() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI VLONG_freeVlongQueue() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_22E76E60BC0587D7() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_28F8791A771D39C7() { + LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); + return ORBIS_OK; +} + +void RegisterlibSceSsl2(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("Md+HYkCBZB4", "libSceSsl", 1, "libSceSsl", 2, 1, CA_MGMT_extractKeyBlobEx); + LIB_FUNCTION("9bKYzKP6kYU", "libSceSsl", 1, "libSceSsl", 2, 1, CA_MGMT_extractPublicKeyInfo); + LIB_FUNCTION("ipLIammTj2Q", "libSceSsl", 1, "libSceSsl", 2, 1, CA_MGMT_freeKeyBlob); + LIB_FUNCTION("PRWr3-ytpdg", "libSceSsl", 1, "libSceSsl", 2, 1, CRYPTO_initAsymmetricKey); + LIB_FUNCTION("cW7VCIMCh9A", "libSceSsl", 1, "libSceSsl", 2, 1, CRYPTO_uninitAsymmetricKey); + LIB_FUNCTION("pBwtarKd7eg", "libSceSsl", 1, "libSceSsl", 2, 1, RSA_verifySignature); + LIB_FUNCTION("1VM0h1JrUfA", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslCheckRecvPending); + LIB_FUNCTION("viRXSHZYd0c", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslClose); + LIB_FUNCTION("zXvd6iNyfgc", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslConnect); + LIB_FUNCTION("tuscfitnhEo", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslCreateConnection); + LIB_FUNCTION("P14ATpXc4J8", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslCreateSslConnection); + LIB_FUNCTION("HJ1n138CQ2g", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslDeleteConnection); + LIB_FUNCTION("hwrHV6Pprk4", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslDeleteSslConnection); + LIB_FUNCTION("iLKz4+ukLqk", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslDisableOption); + LIB_FUNCTION("-WqxBRAUVM4", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslDisableOptionInternal); + LIB_FUNCTION("w1+L-27nYas", "libSceSsl", 1, "libSceSsl", 2, 1, + sceSslDisableOptionInternalInsecure); + LIB_FUNCTION("PwsHbErG+e8", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslDisableVerifyOption); + LIB_FUNCTION("m-zPyAsIpco", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslEnableOption); + LIB_FUNCTION("g-zCwUKstEQ", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslEnableOptionInternal); + LIB_FUNCTION("po1X86mgHDU", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslEnableVerifyOption); + LIB_FUNCTION("qIvLs0gYxi0", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslFreeCaCerts); + LIB_FUNCTION("+DzXseDVkeI", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslFreeCaList); + LIB_FUNCTION("RwXD8grHZHM", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslFreeSslCertName); + LIB_FUNCTION("4O7+bRkRUe8", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslGetAlpnSelected); + LIB_FUNCTION("TDfQqO-gMbY", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslGetCaCerts); + LIB_FUNCTION("qOn+wm28wmA", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslGetCaList); + LIB_FUNCTION("brRtwGBu4A8", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslGetFingerprint); + LIB_FUNCTION("7whYpYfHP74", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslGetIssuerName); + LIB_FUNCTION("-PoIzr3PEk0", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslGetMemoryPoolStats); + LIB_FUNCTION("R1ePzopYPYM", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslGetNameEntryCount); + LIB_FUNCTION("7RBSTKGrmDA", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslGetNameEntryInfo); + LIB_FUNCTION("AzUipl-DpIw", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslGetNanoSSLModuleId); + LIB_FUNCTION("xHpt6+2pGYk", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslGetNotAfter); + LIB_FUNCTION("Eo0S65Jy28Q", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslGetNotBefore); + LIB_FUNCTION("-TbZc8pwPNc", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslGetPeerCert); + LIB_FUNCTION("kLB5aGoUJXg", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslGetPem); + LIB_FUNCTION("DOwXL+FQMEY", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslGetSerialNumber); + LIB_FUNCTION("0XcZknp7-Wc", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslGetSslError); + LIB_FUNCTION("dQReuBX9sD8", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslGetSubjectName); + LIB_FUNCTION("hdpVEUDFW3s", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslInit); + LIB_FUNCTION("Ab7+DH+gYyM", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslLoadCert); + LIB_FUNCTION("3-643mGVFJo", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslLoadRootCACert); + LIB_FUNCTION("jltWpVKtetg", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslRead); + LIB_FUNCTION("hi0veU3L2pU", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslRecv); + LIB_FUNCTION("50R2xYaYZwE", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslReuseConnection); + LIB_FUNCTION("p5bM5PPufFY", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslSend); + LIB_FUNCTION("TL86glUrmUw", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslSetAlpn); + LIB_FUNCTION("QWSxBzf6lAg", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslSetMinSslVersion); + LIB_FUNCTION("bKaEtQnoUuQ", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslSetSslVersion); + LIB_FUNCTION("E4a-ahM57QQ", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslSetVerifyCallback); + LIB_FUNCTION("0K1yQ6Lv-Yc", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslTerm); + LIB_FUNCTION("UQ+3Qu7v3cA", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslUnloadCert); + LIB_FUNCTION("iNjkt9Poblw", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslWrite); + LIB_FUNCTION("wcVuyTUr5ys", "libSceSsl", 1, "libSceSsl", 2, 1, VLONG_freeVlongQueue); + LIB_FUNCTION("IuduYLwFh9c", "libSceSsl", 1, "libSceSsl", 2, 1, Func_22E76E60BC0587D7); + LIB_FUNCTION("KPh5GncdOcc", "libSceSsl", 1, "libSceSsl", 2, 1, Func_28F8791A771D39C7); +}; + +} // namespace Libraries::Ssl2 \ No newline at end of file diff --git a/src/core/libraries/network/ssl2.h b/src/core/libraries/network/ssl2.h new file mode 100644 index 000000000..03ee3b86e --- /dev/null +++ b/src/core/libraries/network/ssl2.h @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::Ssl2 { +void RegisterlibSceSsl2(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::Ssl2 \ No newline at end of file From 8a309c30a9fa893ff968c0db2b958d9e11847607 Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Sun, 12 Jan 2025 03:24:49 -0600 Subject: [PATCH 400/549] Check thread param on posix_pthread_rename_np (#2133) --- src/core/libraries/kernel/threads/pthread.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/libraries/kernel/threads/pthread.cpp b/src/core/libraries/kernel/threads/pthread.cpp index e81207a0d..639ed1611 100644 --- a/src/core/libraries/kernel/threads/pthread.cpp +++ b/src/core/libraries/kernel/threads/pthread.cpp @@ -386,6 +386,9 @@ int PS4_SYSV_ABI posix_sched_get_priority_min() { } int PS4_SYSV_ABI posix_pthread_rename_np(PthreadT thread, const char* name) { + if (thread == nullptr) { + return POSIX_EINVAL; + } LOG_INFO(Kernel_Pthread, "name = {}", name); Common::SetThreadName(reinterpret_cast(thread->native_thr.GetHandle()), name); thread->name = name; From 394331f2066f25114d057e8bfc240dd63ebac2cb Mon Sep 17 00:00:00 2001 From: psucien <168137814+psucien@users.noreply.github.com> Date: Sun, 12 Jan 2025 19:25:25 +0100 Subject: [PATCH 401/549] video_core: detiler: display micro 64bpp (#2137) --- src/video_core/amdgpu/resource.h | 3 + src/video_core/host_shaders/CMakeLists.txt | 1 + .../detilers/display_micro_64bpp.comp | 60 +++++++++++++++++++ src/video_core/texture_cache/image_info.cpp | 1 + .../texture_cache/texture_cache.cpp | 6 +- src/video_core/texture_cache/tile_manager.cpp | 21 +++++-- src/video_core/texture_cache/tile_manager.h | 2 + 7 files changed, 86 insertions(+), 8 deletions(-) create mode 100644 src/video_core/host_shaders/detilers/display_micro_64bpp.comp diff --git a/src/video_core/amdgpu/resource.h b/src/video_core/amdgpu/resource.h index 744aacdc5..75b8b2acf 100644 --- a/src/video_core/amdgpu/resource.h +++ b/src/video_core/amdgpu/resource.h @@ -119,6 +119,7 @@ constexpr std::string_view NameOf(ImageType type) { enum class TilingMode : u32 { Depth_MacroTiled = 0u, Display_Linear = 0x8u, + Display_MicroTiled = 0x9u, Display_MacroTiled = 0xAu, Texture_MicroTiled = 0xDu, Texture_MacroTiled = 0xEu, @@ -131,6 +132,8 @@ constexpr std::string_view NameOf(TilingMode type) { return "Depth_MacroTiled"; case TilingMode::Display_Linear: return "Display_Linear"; + case TilingMode::Display_MicroTiled: + return "Display_MicroTiled"; case TilingMode::Display_MacroTiled: return "Display_MacroTiled"; case TilingMode::Texture_MicroTiled: diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index a9c2964ad..e60cca122 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -2,6 +2,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later set(SHADER_FILES + detilers/display_micro_64bpp.comp detilers/macro_32bpp.comp detilers/macro_64bpp.comp detilers/macro_8bpp.comp diff --git a/src/video_core/host_shaders/detilers/display_micro_64bpp.comp b/src/video_core/host_shaders/detilers/display_micro_64bpp.comp new file mode 100644 index 000000000..3e0485682 --- /dev/null +++ b/src/video_core/host_shaders/detilers/display_micro_64bpp.comp @@ -0,0 +1,60 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#version 450 + +layout (local_size_x = 64, local_size_y = 1, local_size_z = 1) in; + +layout(std430, binding = 0) buffer input_buf { + uint in_data[]; +}; +layout(std430, binding = 1) buffer output_buf { + uint out_data[]; +}; + +layout(push_constant) uniform image_info { + uint num_levels; + uint pitch; + uint height; + uint c0; + uint c1; +} info; + +const uint lut_64bpp[16] = { + 0x05040100, 0x0d0c0908, + 0x07060302, 0x0f0e0b0a, + 0x15141110, 0x1d1c1918, + 0x17161312, 0x1f1e1b1a, + 0x25242120, 0x2d2c2928, + 0x27262322, 0x2f2e2b2a, + 0x35343130, 0x3d3c3938, + 0x37363332, 0x3f3e3b3a, +}; + +#define MICRO_TILE_DIM (8) +#define MICRO_TILE_SZ (512) +#define TEXELS_PER_ELEMENT (1) +#define BPP (64) + +void main() { + uint x = gl_GlobalInvocationID.x % info.pitch; + uint y = (gl_GlobalInvocationID.x / info.pitch) % info.height; + uint z = gl_GlobalInvocationID.x / (info.pitch * info.height); + + uint col = bitfieldExtract(x, 0, 3); + uint row = bitfieldExtract(y, 0, 3); + uint idx_dw = lut_64bpp[(col + row * MICRO_TILE_DIM) >> 2u]; + uint byte_ofs = gl_LocalInvocationID.x & 3u; + uint idx = bitfieldExtract(idx_dw >> (8 * byte_ofs), 0, 8); + + uint slice_offs = z * info.c1 * MICRO_TILE_SZ; + uint tile_row = y / MICRO_TILE_DIM; + uint tile_column = x / MICRO_TILE_DIM; + uint tile_offs = ((tile_row * info.c0) + tile_column) * MICRO_TILE_SZ; + uint offs = slice_offs + tile_offs + ((idx * BPP) / 8u); + + uint p0 = in_data[(offs >> 2) + 0]; + uint p1 = in_data[(offs >> 2) + 1]; + out_data[2 * gl_GlobalInvocationID.x + 0] = p0; + out_data[2 * gl_GlobalInvocationID.x + 1] = p1; +} diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index 58c2a8e23..1992f1fb7 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -182,6 +182,7 @@ void ImageInfo::UpdateSize() { case AmdGpu::TilingMode::Texture_Volume: mip_d += (-mip_d) & 3u; [[fallthrough]]; + case AmdGpu::TilingMode::Display_MicroTiled: case AmdGpu::TilingMode::Texture_MicroTiled: { std::tie(mip_info.pitch, mip_info.size) = ImageSizeMicroTiled(mip_w, mip_h, bpp, num_samples); diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index bef083d1a..a281b89c9 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -469,9 +469,6 @@ ImageView& TextureCache::FindDepthTarget(BaseDesc& desc) { } void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_scheduler /*= nullptr*/) { - RENDERER_TRACE; - TRACE_HINT(fmt::format("{:x}:{:x}", image.info.guest_address, image.info.guest_size)); - if (False(image.flags & ImageFlagBits::Dirty)) { return; } @@ -480,6 +477,9 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule return; } + RENDERER_TRACE; + TRACE_HINT(fmt::format("{:x}:{:x}", image.info.guest_address, image.info.guest_size)); + if (True(image.flags & ImageFlagBits::MaybeCpuDirty) && False(image.flags & ImageFlagBits::CpuDirty)) { // The image size should be less than page size to be considered MaybeCpuDirty diff --git a/src/video_core/texture_cache/tile_manager.cpp b/src/video_core/texture_cache/tile_manager.cpp index aba255ce5..ede91d128 100644 --- a/src/video_core/texture_cache/tile_manager.cpp +++ b/src/video_core/texture_cache/tile_manager.cpp @@ -8,6 +8,7 @@ #include "video_core/texture_cache/image_view.h" #include "video_core/texture_cache/tile_manager.h" +#include "video_core/host_shaders/detilers/display_micro_64bpp_comp.h" #include "video_core/host_shaders/detilers/macro_32bpp_comp.h" #include "video_core/host_shaders/detilers/macro_64bpp_comp.h" #include "video_core/host_shaders/detilers/macro_8bpp_comp.h" @@ -53,6 +54,14 @@ const DetilerContext* TileManager::GetDetiler(const ImageInfo& info) const { return nullptr; } break; + case AmdGpu::TilingMode::Display_MicroTiled: + switch (bpp) { + case 64: + return &detilers[DetilerType::Display_Micro64]; + default: + return nullptr; + } + break; default: return nullptr; } @@ -68,10 +77,11 @@ struct DetilerParams { TileManager::TileManager(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler) : instance{instance}, scheduler{scheduler} { static const std::array detiler_shaders{ - HostShaders::MICRO_8BPP_COMP, HostShaders::MICRO_16BPP_COMP, - HostShaders::MICRO_32BPP_COMP, HostShaders::MICRO_64BPP_COMP, - HostShaders::MICRO_128BPP_COMP, HostShaders::MACRO_8BPP_COMP, - HostShaders::MACRO_32BPP_COMP, HostShaders::MACRO_64BPP_COMP, + HostShaders::MICRO_8BPP_COMP, HostShaders::MICRO_16BPP_COMP, + HostShaders::MICRO_32BPP_COMP, HostShaders::MICRO_64BPP_COMP, + HostShaders::MICRO_128BPP_COMP, HostShaders::MACRO_8BPP_COMP, + HostShaders::MACRO_32BPP_COMP, HostShaders::MACRO_64BPP_COMP, + HostShaders::DISPLAY_MICRO_64BPP_COMP, }; boost::container::static_vector bindings{ @@ -258,7 +268,8 @@ std::pair TileManager::TryDetile(vk::Buffer in_buffer, u32 in_o params.num_levels = info.resources.levels; params.pitch0 = info.pitch >> (info.props.is_block ? 2u : 0u); params.height = info.size.height; - if (info.tiling_mode == AmdGpu::TilingMode::Texture_Volume) { + if (info.tiling_mode == AmdGpu::TilingMode::Texture_Volume || + info.tiling_mode == AmdGpu::TilingMode::Display_MicroTiled) { ASSERT(info.resources.levels == 1); const auto tiles_per_row = info.pitch / 8u; const auto tiles_per_slice = tiles_per_row * ((info.size.height + 7u) / 8u); diff --git a/src/video_core/texture_cache/tile_manager.h b/src/video_core/texture_cache/tile_manager.h index 4eae7be9e..adda16b3d 100644 --- a/src/video_core/texture_cache/tile_manager.h +++ b/src/video_core/texture_cache/tile_manager.h @@ -22,6 +22,8 @@ enum DetilerType : u32 { Macro32, Macro64, + Display_Micro64, + Max }; From c6ab149c56f9a528da945d49302b67a50340cb79 Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Sun, 12 Jan 2025 14:27:54 -0600 Subject: [PATCH 402/549] libSceHttp2 Stubs (#2139) * Auto-generate libSceHttp2 * Improved stub for sceHttp2Init Needed for updated versions of Cyberpunk 2077. Parameters are based on fpPS4, while the stub itself is based on similar stubs in our other networking libraries. * Clang I guess the line length calculations in the moduleGenerator are still not perfect? --- CMakeLists.txt | 2 + src/common/logging/filter.cpp | 1 + src/common/logging/types.h | 1 + src/core/libraries/libs.cpp | 2 + src/core/libraries/network/http2.cpp | 360 +++++++++++++++++++++++++++ src/core/libraries/network/http2.h | 72 ++++++ 6 files changed, 438 insertions(+) create mode 100644 src/core/libraries/network/http2.cpp create mode 100644 src/core/libraries/network/http2.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 84eaefbbb..30e6c58c0 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -254,6 +254,8 @@ set(KERNEL_LIB src/core/libraries/kernel/sync/mutex.cpp set(NETWORK_LIBS src/core/libraries/network/http.cpp src/core/libraries/network/http.h + src/core/libraries/network/http2.cpp + src/core/libraries/network/http2.h src/core/libraries/network/net.cpp src/core/libraries/network/netctl.cpp src/core/libraries/network/netctl.h diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index 6fe2ec4e4..376c55ba7 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -95,6 +95,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Lib, SaveData) \ SUB(Lib, SaveDataDialog) \ SUB(Lib, Http) \ + SUB(Lib, Http2) \ SUB(Lib, Ssl) \ SUB(Lib, Ssl2) \ SUB(Lib, SysModule) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index c42cf5665..535a88a6d 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -65,6 +65,7 @@ enum class Class : u8 { Lib_Ssl, ///< The LibSceSsl implementation. Lib_Ssl2, ///< The LibSceSsl2 implementation. Lib_Http, ///< The LibSceHttp implementation. + Lib_Http2, ///< The LibSceHttp2 implementation. Lib_SysModule, ///< The LibSceSysModule implementation Lib_NpCommon, ///< The LibSceNpCommon implementation Lib_NpManager, ///< The LibSceNpManager implementation diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index b651bab44..7427640b6 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -21,6 +21,7 @@ #include "core/libraries/mouse/mouse.h" #include "core/libraries/move/move.h" #include "core/libraries/network/http.h" +#include "core/libraries/network/http2.h" #include "core/libraries/network/net.h" #include "core/libraries/network/netctl.h" #include "core/libraries/network/ssl.h" @@ -66,6 +67,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::MsgDialog::RegisterlibSceMsgDialog(sym); Libraries::AudioOut::RegisterlibSceAudioOut(sym); Libraries::Http::RegisterlibSceHttp(sym); + Libraries::Http2::RegisterlibSceHttp2(sym); Libraries::Net::RegisterlibSceNet(sym); Libraries::NetCtl::RegisterlibSceNetCtl(sym); Libraries::SaveData::RegisterlibSceSaveData(sym); diff --git a/src/core/libraries/network/http2.cpp b/src/core/libraries/network/http2.cpp new file mode 100644 index 000000000..52f73edc6 --- /dev/null +++ b/src/core/libraries/network/http2.cpp @@ -0,0 +1,360 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" +#include "core/libraries/network/http2.h" + +namespace Libraries::Http2 { + +int PS4_SYSV_ABI _Z5dummyv() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2AbortRequest() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2AddCookie() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2AddRequestHeader() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2AuthCacheFlush() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2CookieExport() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2CookieFlush() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2CookieImport() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2CreateCookieBox() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2CreateRequestWithURL() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2CreateTemplate() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2DeleteCookieBox() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2DeleteRequest() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2DeleteTemplate() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2GetAllResponseHeaders() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2GetAuthEnabled() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2GetAutoRedirect() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2GetCookie() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2GetCookieBox() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2GetCookieStats() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2GetMemoryPoolStats() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2GetResponseContentLength() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2GetStatusCode() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2Init(int net_id, int ssl_id, size_t pool_size, int max_requests) { + LOG_ERROR(Lib_Http2, "(DUMMY) called"); + static int id = 0; + return ++id; +} + +int PS4_SYSV_ABI sceHttp2ReadData() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2ReadDataAsync() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2RedirectCacheFlush() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2RemoveRequestHeader() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2SendRequest() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2SendRequestAsync() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2SetAuthEnabled() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2SetAuthInfoCallback() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2SetAutoRedirect() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2SetConnectionWaitTimeOut() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2SetConnectTimeOut() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2SetCookieBox() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2SetCookieMaxNum() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2SetCookieMaxNumPerDomain() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2SetCookieMaxSize() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2SetCookieRecvCallback() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2SetCookieSendCallback() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2SetInflateGZIPEnabled() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2SetMinSslVersion() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2SetPreSendCallback() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2SetRecvTimeOut() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2SetRedirectCallback() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2SetRequestContentLength() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2SetResolveRetry() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2SetResolveTimeOut() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2SetSendTimeOut() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2SetSslCallback() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2SetTimeOut() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2SslDisableOption() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2SslEnableOption() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2Term() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttp2WaitAsync() { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + return ORBIS_OK; +} + +void RegisterlibSceHttp2(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("AS45QoYHjc4", "libSceHttp2", 1, "libSceHttp2", 1, 1, _Z5dummyv); + LIB_FUNCTION("IZ-qjhRqvjk", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2AbortRequest); + LIB_FUNCTION("flPxnowtvWY", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2AddCookie); + LIB_FUNCTION("nrPfOE8TQu0", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2AddRequestHeader); + LIB_FUNCTION("WeuDjj5m4YU", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2AuthCacheFlush); + LIB_FUNCTION("JlFGR4v50Kw", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2CookieExport); + LIB_FUNCTION("5VlQSzXW-SQ", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2CookieFlush); + LIB_FUNCTION("B5ibZI5UlzU", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2CookieImport); + LIB_FUNCTION("N4UfjvWJsMw", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2CreateCookieBox); + LIB_FUNCTION("mmyOCxQMVYQ", "libSceHttp2", 1, "libSceHttp2", 1, 1, + sceHttp2CreateRequestWithURL); + LIB_FUNCTION("+wCt7fCijgk", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2CreateTemplate); + LIB_FUNCTION("O9ync3F-JVI", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2DeleteCookieBox); + LIB_FUNCTION("c8D9qIjo8EY", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2DeleteRequest); + LIB_FUNCTION("pDom5-078DA", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2DeleteTemplate); + LIB_FUNCTION("-rdXUi2XW90", "libSceHttp2", 1, "libSceHttp2", 1, 1, + sceHttp2GetAllResponseHeaders); + LIB_FUNCTION("m-OL13q8AI8", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2GetAuthEnabled); + LIB_FUNCTION("od5QCZhZSfw", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2GetAutoRedirect); + LIB_FUNCTION("GQFGj0rYX+A", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2GetCookie); + LIB_FUNCTION("IX23slKvtQI", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2GetCookieBox); + LIB_FUNCTION("eij7UzkUqK8", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2GetCookieStats); + LIB_FUNCTION("otUQuZa-mv0", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2GetMemoryPoolStats); + LIB_FUNCTION("o0DBQpFE13o", "libSceHttp2", 1, "libSceHttp2", 1, 1, + sceHttp2GetResponseContentLength); + LIB_FUNCTION("9XYJwCf3lEA", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2GetStatusCode); + LIB_FUNCTION("3JCe3lCbQ8A", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2Init); + LIB_FUNCTION("QygCNNmbGss", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2ReadData); + LIB_FUNCTION("bGN-6zbo7ms", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2ReadDataAsync); + LIB_FUNCTION("klwUy2Wg+q8", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2RedirectCacheFlush); + LIB_FUNCTION("jHdP0CS4ZlA", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2RemoveRequestHeader); + LIB_FUNCTION("rbqZig38AT8", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2SendRequest); + LIB_FUNCTION("A+NVAFu4eCg", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2SendRequestAsync); + LIB_FUNCTION("jjFahkBPCYs", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2SetAuthEnabled); + LIB_FUNCTION("Wwj6HbB2mOo", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2SetAuthInfoCallback); + LIB_FUNCTION("b9AvoIaOuHI", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2SetAutoRedirect); + LIB_FUNCTION("n8hMLe31OPA", "libSceHttp2", 1, "libSceHttp2", 1, 1, + sceHttp2SetConnectionWaitTimeOut); + LIB_FUNCTION("-HIO4VT87v8", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2SetConnectTimeOut); + LIB_FUNCTION("jrVHsKCXA0g", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2SetCookieBox); + LIB_FUNCTION("mPKVhQqh2Es", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2SetCookieMaxNum); + LIB_FUNCTION("o7+WXe4WadE", "libSceHttp2", 1, "libSceHttp2", 1, 1, + sceHttp2SetCookieMaxNumPerDomain); + LIB_FUNCTION("6a0N6GPD7RM", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2SetCookieMaxSize); + LIB_FUNCTION("zdtXKn9X7no", "libSceHttp2", 1, "libSceHttp2", 1, 1, + sceHttp2SetCookieRecvCallback); + LIB_FUNCTION("McYmUpQ3-DY", "libSceHttp2", 1, "libSceHttp2", 1, 1, + sceHttp2SetCookieSendCallback); + LIB_FUNCTION("uRosf8GQbHQ", "libSceHttp2", 1, "libSceHttp2", 1, 1, + sceHttp2SetInflateGZIPEnabled); + LIB_FUNCTION("09tk+kIA1Ns", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2SetMinSslVersion); + LIB_FUNCTION("UL4Fviw+IAM", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2SetPreSendCallback); + LIB_FUNCTION("izvHhqgDt44", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2SetRecvTimeOut); + LIB_FUNCTION("BJgi0CH7al4", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2SetRedirectCallback); + LIB_FUNCTION("FSAFOzi0FpM", "libSceHttp2", 1, "libSceHttp2", 1, 1, + sceHttp2SetRequestContentLength); + LIB_FUNCTION("Gcjh+CisAZM", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2SetResolveRetry); + LIB_FUNCTION("ACjtE27aErY", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2SetResolveTimeOut); + LIB_FUNCTION("XPtW45xiLHk", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2SetSendTimeOut); + LIB_FUNCTION("YrWX+DhPHQY", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2SetSslCallback); + LIB_FUNCTION("VYMxTcBqSE0", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2SetTimeOut); + LIB_FUNCTION("B37SruheQ5Y", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2SslDisableOption); + LIB_FUNCTION("EWcwMpbr5F8", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2SslEnableOption); + LIB_FUNCTION("YiBUtz-pGkc", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2Term); + LIB_FUNCTION("MOp-AUhdfi8", "libSceHttp2", 1, "libSceHttp2", 1, 1, sceHttp2WaitAsync); +}; + +} // namespace Libraries::Http2 \ No newline at end of file diff --git a/src/core/libraries/network/http2.h b/src/core/libraries/network/http2.h new file mode 100644 index 000000000..aa1d0c5b4 --- /dev/null +++ b/src/core/libraries/network/http2.h @@ -0,0 +1,72 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::Http2 { + +int PS4_SYSV_ABI _Z5dummyv(); +int PS4_SYSV_ABI sceHttp2AbortRequest(); +int PS4_SYSV_ABI sceHttp2AddCookie(); +int PS4_SYSV_ABI sceHttp2AddRequestHeader(); +int PS4_SYSV_ABI sceHttp2AuthCacheFlush(); +int PS4_SYSV_ABI sceHttp2CookieExport(); +int PS4_SYSV_ABI sceHttp2CookieFlush(); +int PS4_SYSV_ABI sceHttp2CookieImport(); +int PS4_SYSV_ABI sceHttp2CreateCookieBox(); +int PS4_SYSV_ABI sceHttp2CreateRequestWithURL(); +int PS4_SYSV_ABI sceHttp2CreateTemplate(); +int PS4_SYSV_ABI sceHttp2DeleteCookieBox(); +int PS4_SYSV_ABI sceHttp2DeleteRequest(); +int PS4_SYSV_ABI sceHttp2DeleteTemplate(); +int PS4_SYSV_ABI sceHttp2GetAllResponseHeaders(); +int PS4_SYSV_ABI sceHttp2GetAuthEnabled(); +int PS4_SYSV_ABI sceHttp2GetAutoRedirect(); +int PS4_SYSV_ABI sceHttp2GetCookie(); +int PS4_SYSV_ABI sceHttp2GetCookieBox(); +int PS4_SYSV_ABI sceHttp2GetCookieStats(); +int PS4_SYSV_ABI sceHttp2GetMemoryPoolStats(); +int PS4_SYSV_ABI sceHttp2GetResponseContentLength(); +int PS4_SYSV_ABI sceHttp2GetStatusCode(); +int PS4_SYSV_ABI sceHttp2Init(int net_id, int ssl_id, size_t pool_size, int max_requests); +int PS4_SYSV_ABI sceHttp2ReadData(); +int PS4_SYSV_ABI sceHttp2ReadDataAsync(); +int PS4_SYSV_ABI sceHttp2RedirectCacheFlush(); +int PS4_SYSV_ABI sceHttp2RemoveRequestHeader(); +int PS4_SYSV_ABI sceHttp2SendRequest(); +int PS4_SYSV_ABI sceHttp2SendRequestAsync(); +int PS4_SYSV_ABI sceHttp2SetAuthEnabled(); +int PS4_SYSV_ABI sceHttp2SetAuthInfoCallback(); +int PS4_SYSV_ABI sceHttp2SetAutoRedirect(); +int PS4_SYSV_ABI sceHttp2SetConnectionWaitTimeOut(); +int PS4_SYSV_ABI sceHttp2SetConnectTimeOut(); +int PS4_SYSV_ABI sceHttp2SetCookieBox(); +int PS4_SYSV_ABI sceHttp2SetCookieMaxNum(); +int PS4_SYSV_ABI sceHttp2SetCookieMaxNumPerDomain(); +int PS4_SYSV_ABI sceHttp2SetCookieMaxSize(); +int PS4_SYSV_ABI sceHttp2SetCookieRecvCallback(); +int PS4_SYSV_ABI sceHttp2SetCookieSendCallback(); +int PS4_SYSV_ABI sceHttp2SetInflateGZIPEnabled(); +int PS4_SYSV_ABI sceHttp2SetMinSslVersion(); +int PS4_SYSV_ABI sceHttp2SetPreSendCallback(); +int PS4_SYSV_ABI sceHttp2SetRecvTimeOut(); +int PS4_SYSV_ABI sceHttp2SetRedirectCallback(); +int PS4_SYSV_ABI sceHttp2SetRequestContentLength(); +int PS4_SYSV_ABI sceHttp2SetResolveRetry(); +int PS4_SYSV_ABI sceHttp2SetResolveTimeOut(); +int PS4_SYSV_ABI sceHttp2SetSendTimeOut(); +int PS4_SYSV_ABI sceHttp2SetSslCallback(); +int PS4_SYSV_ABI sceHttp2SetTimeOut(); +int PS4_SYSV_ABI sceHttp2SslDisableOption(); +int PS4_SYSV_ABI sceHttp2SslEnableOption(); +int PS4_SYSV_ABI sceHttp2Term(); +int PS4_SYSV_ABI sceHttp2WaitAsync(); + +void RegisterlibSceHttp2(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::Http2 \ No newline at end of file From 4f2f9494b02305a1b5d39e94eda63933972e15d9 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Sun, 12 Jan 2025 21:31:05 +0100 Subject: [PATCH 403/549] GUI: Speed up GUI loading by caching game sizes (#2130) * Add show game size toggle * Fix (#7) * Fix I removed the gameSizeCheckBox from the 'Emulator' group and put it in 'GUI settings' hLayoutTrophy which contains the Trophy information was inside the GUIMusicLayout, so I fixed that too. * TR * Use cached sizes if the feature is enabled --------- Co-authored-by: DanielSvoboda --- src/common/config.cpp | 11 +++ src/common/config.h | 2 + src/qt_gui/game_list_frame.cpp | 2 + src/qt_gui/game_list_utils.h | 35 ++++++++ src/qt_gui/settings_dialog.cpp | 2 + src/qt_gui/settings_dialog.ui | 143 ++++++++++++++++--------------- src/qt_gui/translations/ar.ts | 4 + src/qt_gui/translations/da_DK.ts | 4 + src/qt_gui/translations/de.ts | 4 + src/qt_gui/translations/el.ts | 4 + src/qt_gui/translations/en.ts | 4 + src/qt_gui/translations/es_ES.ts | 4 + src/qt_gui/translations/fa_IR.ts | 4 + src/qt_gui/translations/fi.ts | 4 + src/qt_gui/translations/fr.ts | 4 + src/qt_gui/translations/hu_HU.ts | 4 + src/qt_gui/translations/id.ts | 4 + src/qt_gui/translations/it.ts | 4 + src/qt_gui/translations/ja_JP.ts | 4 + src/qt_gui/translations/ko_KR.ts | 4 + src/qt_gui/translations/lt_LT.ts | 4 + src/qt_gui/translations/nb.ts | 4 + src/qt_gui/translations/nl.ts | 4 + src/qt_gui/translations/pl_PL.ts | 4 + src/qt_gui/translations/pt_BR.ts | 4 + src/qt_gui/translations/ro_RO.ts | 4 + src/qt_gui/translations/ru_RU.ts | 4 + src/qt_gui/translations/sq.ts | 4 + src/qt_gui/translations/sv.ts | 4 + src/qt_gui/translations/tr_TR.ts | 4 + src/qt_gui/translations/uk_UA.ts | 4 + src/qt_gui/translations/vi_VN.ts | 4 + src/qt_gui/translations/zh_CN.ts | 4 + src/qt_gui/translations/zh_TW.ts | 4 + 34 files changed, 239 insertions(+), 68 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index b46ab8d6e..158bfeddf 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -72,6 +72,7 @@ static bool checkCompatibilityOnStartup = false; static std::string trophyKey; // Gui +static bool load_game_size = true; std::vector settings_install_dirs = {}; std::filesystem::path settings_addon_install_dir = {}; u32 main_window_geometry_x = 400; @@ -102,6 +103,14 @@ void setTrophyKey(std::string key) { trophyKey = key; } +bool GetLoadGameSizeEnabled() { + return load_game_size; +} + +void setLoadGameSizeEnabled(bool enable) { + load_game_size = enable; +} + bool isNeoModeConsole() { return isNeo; } @@ -650,6 +659,7 @@ void load(const std::filesystem::path& path) { if (data.contains("GUI")) { const toml::value& gui = data.at("GUI"); + load_game_size = toml::find_or(gui, "loadGameSizeEnabled", true); m_icon_size = toml::find_or(gui, "iconSize", 0); m_icon_size_grid = toml::find_or(gui, "iconSizeGrid", 0); m_slider_pos = toml::find_or(gui, "sliderPos", 0); @@ -755,6 +765,7 @@ void save(const std::filesystem::path& path) { install_dirs.emplace_back(std::string{fmt::UTF(dirString.u8string()).data}); } data["GUI"]["installDirs"] = install_dirs; + data["GUI"]["loadGameSizeEnabled"] = load_game_size; data["GUI"]["addonInstallDir"] = std::string{fmt::UTF(settings_addon_install_dir.u8string()).data}; diff --git a/src/common/config.h b/src/common/config.h index 6e6a5d960..c86e35ebc 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -17,6 +17,8 @@ void saveMainWindow(const std::filesystem::path& path); std::string getTrophyKey(); void setTrophyKey(std::string key); +bool GetLoadGameSizeEnabled(); +void setLoadGameSizeEnabled(bool enable); bool getIsFullscreen(); std::string getFullscreenMode(); bool isNeoModeConsole(); diff --git a/src/qt_gui/game_list_frame.cpp b/src/qt_gui/game_list_frame.cpp index b1cd07216..9753f511b 100644 --- a/src/qt_gui/game_list_frame.cpp +++ b/src/qt_gui/game_list_frame.cpp @@ -106,6 +106,8 @@ void GameListFrame::PlayBackgroundMusic(QTableWidgetItem* item) { void GameListFrame::PopulateGameList() { // Do not show status column if it is not enabled this->setColumnHidden(2, !Config::getCompatibilityEnabled()); + this->setColumnHidden(6, !Config::GetLoadGameSizeEnabled()); + this->setRowCount(m_game_info->m_games.size()); ResizeIcons(icon_size); diff --git a/src/qt_gui/game_list_utils.h b/src/qt_gui/game_list_utils.h index ab9233886..581a8a55f 100644 --- a/src/qt_gui/game_list_utils.h +++ b/src/qt_gui/game_list_utils.h @@ -62,11 +62,46 @@ public: QDir dir(dirPath); QDirIterator it(dir.absolutePath(), QDirIterator::Subdirectories); qint64 total = 0; + + if (!Config::GetLoadGameSizeEnabled()) { + game.size = FormatSize(0).toStdString(); + return; + } + + // Cache path + QFile size_cache_file(Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / + game.serial / "size_cache.txt"); + QFileInfo cacheInfo(size_cache_file); + QFileInfo dirInfo(dirPath); + + // Check if cache file exists and is valid + if (size_cache_file.exists() && cacheInfo.lastModified() >= dirInfo.lastModified()) { + if (size_cache_file.open(QIODevice::ReadOnly | QIODevice::Text)) { + QTextStream in(&size_cache_file); + QString cachedSize = in.readLine(); + size_cache_file.close(); + + if (!cachedSize.isEmpty()) { + game.size = cachedSize.toStdString(); + return; + } + } + } + + // Cache is invalid or does not exist; calculate size while (it.hasNext()) { it.next(); total += it.fileInfo().size(); } + game.size = FormatSize(total).toStdString(); + + // Save new cache + if (size_cache_file.open(QIODevice::WriteOnly | QIODevice::Text)) { + QTextStream out(&size_cache_file); + out << QString::fromStdString(game.size) << "\n"; + size_cache_file.close(); + } } static QString GetRegion(char region) { diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index 3f4970dad..a4b584294 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -315,6 +315,7 @@ void SettingsDialog::LoadValuesFromConfig() { toml::find_or(data, "General", "FullscreenMode", "Borderless"))); ui->separateUpdatesCheckBox->setChecked( toml::find_or(data, "General", "separateUpdateEnabled", false)); + ui->gameSizeCheckBox->setChecked(toml::find_or(data, "GUI", "loadGameSizeEnabled", true)); ui->showSplashCheckBox->setChecked(toml::find_or(data, "General", "showSplash", false)); ui->logTypeComboBox->setCurrentText( QString::fromStdString(toml::find_or(data, "General", "logType", "async"))); @@ -568,6 +569,7 @@ void SettingsDialog::UpdateSettings() { Config::setDumpShaders(ui->dumpShadersCheckBox->isChecked()); Config::setNullGpu(ui->nullGpuCheckBox->isChecked()); Config::setSeparateUpdateEnabled(ui->separateUpdatesCheckBox->isChecked()); + Config::setLoadGameSizeEnabled(ui->gameSizeCheckBox->isChecked()); Config::setShowSplash(ui->showSplashCheckBox->isChecked()); Config::setDebugDump(ui->debugDump->isChecked()); Config::setVkValidation(ui->vkValidationCheckBox->isChecked()); diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index 089158fd3..8d68d1c90 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -68,7 +68,7 @@ 0 0 946 - 536 + 586 @@ -134,7 +134,7 @@ - + Fullscreen Mode @@ -483,6 +483,13 @@ 11 + + + + Show Game Size In List + + + @@ -557,59 +564,59 @@ + + + + + + 6 + + + 0 + + + 50 + - - - 6 - - - 0 - - - 80 - + - - - - - Trophy - - - - - - Disable Trophy Pop-ups - - - - - - - Trophy Key - - - - - - - - 0 - 0 - - - - - 10 - false - - - - - - - - + + + Trophy + + + + + + Disable Trophy Pop-ups + + + + + + + Trophy Key + + + + + + + + 0 + 0 + + + + + 10 + false + + + + + + @@ -637,8 +644,8 @@ 0 0 - 926 - 536 + 946 + 586 @@ -853,13 +860,13 @@ - - - - Enable Motion Controls - - - + + + + Enable Motion Controls + + + @@ -935,8 +942,8 @@ 0 0 - 926 - 536 + 946 + 586 @@ -1186,8 +1193,8 @@ 0 0 - 926 - 536 + 946 + 586 @@ -1259,8 +1266,8 @@ 0 0 - 926 - 536 + 946 + 586 diff --git a/src/qt_gui/translations/ar.ts b/src/qt_gui/translations/ar.ts index 4fc9c2de1..a4dadcb1a 100644 --- a/src/qt_gui/translations/ar.ts +++ b/src/qt_gui/translations/ar.ts @@ -540,6 +540,10 @@ Enable Separate Update Folder Enable Separate Update Folder + + Show Game Size In List + عرض حجم اللعبة في القائمة + Show Splash إظهار شاشة البداية diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts index ef1ae27a3..70b7d3ecc 100644 --- a/src/qt_gui/translations/da_DK.ts +++ b/src/qt_gui/translations/da_DK.ts @@ -540,6 +540,10 @@ Enable Separate Update Folder Enable Separate Update Folder + + Show Game Size In List + Vis vis spilstørrelse i listen + Show Splash Show Splash diff --git a/src/qt_gui/translations/de.ts b/src/qt_gui/translations/de.ts index 2fc6a29fe..7f1de3afd 100644 --- a/src/qt_gui/translations/de.ts +++ b/src/qt_gui/translations/de.ts @@ -540,6 +540,10 @@ Enable Separate Update Folder Enable Separate Update Folder + + Show Game Size In List + Zeigen Sie die Spielgröße in der Liste + Show Splash Startbildschirm anzeigen diff --git a/src/qt_gui/translations/el.ts b/src/qt_gui/translations/el.ts index 8d3885808..84165536e 100644 --- a/src/qt_gui/translations/el.ts +++ b/src/qt_gui/translations/el.ts @@ -540,6 +540,10 @@ Enable Separate Update Folder Enable Separate Update Folder + + Show Game Size In List + Εμφάνιση Μεγέθους Παιχνιδιού στη Λίστα + Show Splash Show Splash diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index 0262ee149..fad185d41 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -540,6 +540,10 @@ Enable Separate Update Folder Enable Separate Update Folder + + Show Game Size In List + Show Game Size In List + Show Splash Show Splash diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index a25ff639e..a97d3d3c8 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -540,6 +540,10 @@ Enable Separate Update Folder Enable Separate Update Folder + + Show Game Size In List + Mostrar Tamaño del Juego en la Lista + Show Splash Mostrar splash diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index 52aa4b17c..697e615fb 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -540,6 +540,10 @@ Enable Separate Update Folder فعال‌سازی پوشه جداگانه برای به‌روزرسانی + + Show Game Size In List + نمایش اندازه بازی در لیست + Show Splash Splash نمایش diff --git a/src/qt_gui/translations/fi.ts b/src/qt_gui/translations/fi.ts index 97fee5dfa..51e85dfbb 100644 --- a/src/qt_gui/translations/fi.ts +++ b/src/qt_gui/translations/fi.ts @@ -540,6 +540,10 @@ Enable Separate Update Folder Ota Käyttöön Erillinen Päivityshakemisto + + Show Game Size In List + Näytä pelin koko luettelossa + Show Splash Näytä Aloitusnäyttö diff --git a/src/qt_gui/translations/fr.ts b/src/qt_gui/translations/fr.ts index d25ad30f4..35f3eb55f 100644 --- a/src/qt_gui/translations/fr.ts +++ b/src/qt_gui/translations/fr.ts @@ -540,6 +540,10 @@ Enable Separate Update Folder Dossier séparé pour les mises à jours + + Show Game Size In List + Afficher la taille du jeu dans la liste + Show Splash Afficher l'image du jeu diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index 6ecc3fc90..a2bd9c1da 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -540,6 +540,10 @@ Enable Separate Update Folder Külön Frissítési Mappa Engedélyezése + + Show Game Size In List + Játékméret megjelenítése a listában + Show Splash Indítóképernyő Mutatása diff --git a/src/qt_gui/translations/id.ts b/src/qt_gui/translations/id.ts index fc5ad4a99..b97914ca2 100644 --- a/src/qt_gui/translations/id.ts +++ b/src/qt_gui/translations/id.ts @@ -540,6 +540,10 @@ Enable Separate Update Folder Enable Separate Update Folder + + Show Game Size In List + Tampilkan Ukuran Game di Daftar + Show Splash Show Splash diff --git a/src/qt_gui/translations/it.ts b/src/qt_gui/translations/it.ts index f7ba3661b..d4ea1c7e6 100644 --- a/src/qt_gui/translations/it.ts +++ b/src/qt_gui/translations/it.ts @@ -540,6 +540,10 @@ Enable Separate Update Folder Abilita Cartella Aggiornamenti Separata + + Show Game Size In List + Mostra la dimensione del gioco nell'elenco + Show Splash Mostra Schermata Iniziale diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index 21c8145ed..359955765 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -540,6 +540,10 @@ Enable Separate Update Folder Enable Separate Update Folder + + Show Game Size In List + ゲームサイズをリストに表示 + Show Splash スプラッシュを表示する diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts index fea8d55bc..9cca0b656 100644 --- a/src/qt_gui/translations/ko_KR.ts +++ b/src/qt_gui/translations/ko_KR.ts @@ -540,6 +540,10 @@ Enable Separate Update Folder Enable Separate Update Folder + + Show Game Size In List + 게임 크기를 목록에 표시 + Show Splash Show Splash diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts index eaf51a975..0594bcbd2 100644 --- a/src/qt_gui/translations/lt_LT.ts +++ b/src/qt_gui/translations/lt_LT.ts @@ -540,6 +540,10 @@ Enable Separate Update Folder Enable Separate Update Folder + + Show Game Size In List + Rodyti žaidimo dydį sąraše + Show Splash Show Splash diff --git a/src/qt_gui/translations/nb.ts b/src/qt_gui/translations/nb.ts index 83dbf7dd8..8ca8246ba 100644 --- a/src/qt_gui/translations/nb.ts +++ b/src/qt_gui/translations/nb.ts @@ -540,6 +540,10 @@ Enable Separate Update Folder Aktiver seperat oppdateringsmappe + + Show Game Size In List + Vis spillstørrelse i listen + Show Splash Vis velkomstbilde diff --git a/src/qt_gui/translations/nl.ts b/src/qt_gui/translations/nl.ts index 3142a17e5..12d644458 100644 --- a/src/qt_gui/translations/nl.ts +++ b/src/qt_gui/translations/nl.ts @@ -540,6 +540,10 @@ Enable Separate Update Folder Enable Separate Update Folder + + Show Game Size In List + Toon grootte van het spel in de lijst + Show Splash Show Splash diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index 378673a30..782db12e2 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -540,6 +540,10 @@ Enable Separate Update Folder Enable Separate Update Folder + + Show Game Size In List + Pokaż rozmiar gry na liście + Show Splash Pokaż ekran powitania diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index 5d9c84769..94bbf028a 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -540,6 +540,10 @@ Enable Separate Update Folder Habilitar pasta de atualização separada + + Show Game Size In List + Mostrar Tamanho do Jogo na Lista + Show Splash Mostrar Splash Inicial diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index 71354fb06..3bd8e38b5 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -540,6 +540,10 @@ Enable Separate Update Folder Enable Separate Update Folder + + Show Game Size In List + Afișează dimensiunea jocului în listă + Show Splash Show Splash diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index 0e803ea42..a38e2fd98 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -540,6 +540,10 @@ Enable Separate Update Folder Отдельная папка обновлений + + Show Game Size In List + Показать размер игры в списке + Show Splash Показывать заставку diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index 7354b4bd9..a83dc9829 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -540,6 +540,10 @@ Enable Separate Update Folder Aktivizo dosjen e ndarë të përditësimit + + Show Game Size In List + Shfaq madhësinë e lojës në listë + Show Splash Shfaq Pamjen e nisjes diff --git a/src/qt_gui/translations/sv.ts b/src/qt_gui/translations/sv.ts index 3a6f060cb..9a244a9df 100644 --- a/src/qt_gui/translations/sv.ts +++ b/src/qt_gui/translations/sv.ts @@ -1032,6 +1032,10 @@ Enable Separate Update Folder Aktivera separat uppdateringsmapp + + Show Game Size In List + Visa spelstorlek i listan + Show Splash Visa startskärm diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index 4596000f2..be50f935a 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -540,6 +540,10 @@ Enable Separate Update Folder Enable Separate Update Folder + + Show Game Size In List + Göster oyun boyutunu listede + Show Splash Başlangıç Ekranını Göster diff --git a/src/qt_gui/translations/uk_UA.ts b/src/qt_gui/translations/uk_UA.ts index 5b260050e..ff4e48e80 100644 --- a/src/qt_gui/translations/uk_UA.ts +++ b/src/qt_gui/translations/uk_UA.ts @@ -540,6 +540,10 @@ Enable Separate Update Folder Увімкнути окрему папку оновлень + + Show Game Size In List + Показати розмір гри в списку + Show Splash Показувати заставку diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index 7fcac6d7e..e546d955c 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -540,6 +540,10 @@ Enable Separate Update Folder Enable Separate Update Folder + + Show Game Size In List + Hiển thị Kích thước Game trong Danh sách + Show Splash Show Splash diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index bb4476b9e..ece5f9490 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -540,6 +540,10 @@ Enable Separate Update Folder 启用单独的更新目录 + + Show Game Size In List + 显示游戏大小在列表中 + Show Splash 显示启动画面 diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index 49d419d8b..11642d52b 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -540,6 +540,10 @@ Enable Separate Update Folder Enable Separate Update Folder + + Show Game Size In List + 顯示遊戲大小在列表中 + Show Splash Show Splash From 4719d32295095e6a084045629f6456c2a1f8b635 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sun, 12 Jan 2025 12:44:42 -0800 Subject: [PATCH 404/549] sdl: Respect text input main thread requirements. (#2138) --- externals/sdl3 | 2 +- src/imgui/renderer/imgui_impl_sdl3.cpp | 41 ++++++++++---------------- src/sdl_window.cpp | 8 +++-- 3 files changed, 23 insertions(+), 28 deletions(-) diff --git a/externals/sdl3 b/externals/sdl3 index 3a1d76d29..22422f774 160000 --- a/externals/sdl3 +++ b/externals/sdl3 @@ -1 +1 @@ -Subproject commit 3a1d76d298db023f6cf37fb08ee766f20a4e12ab +Subproject commit 22422f7748d5128135995ed34c8f8012861c7332 diff --git a/src/imgui/renderer/imgui_impl_sdl3.cpp b/src/imgui/renderer/imgui_impl_sdl3.cpp index 60b440c24..e67bdc775 100644 --- a/src/imgui/renderer/imgui_impl_sdl3.cpp +++ b/src/imgui/renderer/imgui_impl_sdl3.cpp @@ -11,7 +11,6 @@ #include #if defined(__APPLE__) #include -#include #endif #ifdef _WIN32 #ifndef WIN32_LEAN_AND_MEAN @@ -72,33 +71,25 @@ static void PlatformSetImeData(ImGuiContext*, ImGuiViewport* viewport, ImGuiPlat auto window_id = (SDL_WindowID)(intptr_t)viewport->PlatformHandle; SDL_Window* window = SDL_GetWindowFromID(window_id); if ((!data->WantVisible || bd->ime_window != window) && bd->ime_window != nullptr) { - auto stop_input = [&bd] { SDL_StopTextInput(bd->ime_window); }; -#ifdef __APPLE__ - dispatch_sync(dispatch_get_main_queue(), ^{ - stop_input(); - }); -#else - stop_input(); -#endif + SDL_RunOnMainThread( + [](void* userdata) { SDL_StopTextInput(static_cast(userdata)); }, + bd->ime_window, true); bd->ime_window = nullptr; } if (data->WantVisible) { - SDL_Rect r; - r.x = (int)data->InputPos.x; - r.y = (int)data->InputPos.y; - r.w = 1; - r.h = (int)data->InputLineHeight; - const auto start_input = [&window, &r] { - SDL_SetTextInputArea(window, &r, 0); - SDL_StartTextInput(window); - }; -#ifdef __APPLE__ - dispatch_sync(dispatch_get_main_queue(), ^{ - start_input(); - }); -#else - start_input(); -#endif + std::pair usr_data; + usr_data.first = window; + usr_data.second.x = (int)data->InputPos.x; + usr_data.second.y = (int)data->InputPos.y; + usr_data.second.w = 1; + usr_data.second.h = (int)data->InputLineHeight; + SDL_RunOnMainThread( + [](void* userdata) { + auto* params = static_cast*>(userdata); + SDL_SetTextInputArea(params->first, ¶ms->second, 0); + SDL_StartTextInput(params->first); + }, + &usr_data, true); bd->ime_window = window; } } diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index 318b3349b..b0126def2 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -205,7 +205,9 @@ void WindowSDL::InitTimers() { void WindowSDL::RequestKeyboard() { if (keyboard_grab == 0) { - SDL_StartTextInput(window); + SDL_RunOnMainThread( + [](void* userdata) { SDL_StartTextInput(static_cast(userdata)); }, window, + true); } keyboard_grab++; } @@ -214,7 +216,9 @@ void WindowSDL::ReleaseKeyboard() { ASSERT(keyboard_grab > 0); keyboard_grab--; if (keyboard_grab == 0) { - SDL_StopTextInput(window); + SDL_RunOnMainThread( + [](void* userdata) { SDL_StopTextInput(static_cast(userdata)); }, window, + true); } } From d94abffd9a6bba013fe2524a7e085ccfa253ced9 Mon Sep 17 00:00:00 2001 From: jarred wilson <20207921+jardon@users.noreply.github.com> Date: Mon, 13 Jan 2025 04:54:20 -0600 Subject: [PATCH 405/549] Fix: rename yakuza screenshot to correct game (#2141) --- dist/net.shadps4.shadPS4.metainfo.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/net.shadps4.shadPS4.metainfo.xml b/dist/net.shadps4.shadPS4.metainfo.xml index 384cf75e8..d8f51baac 100644 --- a/dist/net.shadps4.shadPS4.metainfo.xml +++ b/dist/net.shadps4.shadPS4.metainfo.xml @@ -26,7 +26,7 @@ https://cdn.jsdelivr.net/gh/shadps4-emu/shadps4@main/documents/Screenshots/3.png - Yakuza Kiwami + Yakuza 0 https://cdn.jsdelivr.net/gh/shadps4-emu/shadps4@main/documents/Screenshots/4.png From 5040be164090a10f79a505d2c709ca426fec69a4 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Tue, 14 Jan 2025 21:48:40 -0800 Subject: [PATCH 406/549] renderer_vulkan: Handle depth-stencil copies through depth render overrides. (#2134) --- src/core/devtools/widget/reg_popup.cpp | 3 +- src/core/devtools/widget/reg_view.cpp | 4 +- src/video_core/amdgpu/liverpool.h | 65 ++++++++++++++++- .../renderer_vulkan/vk_rasterizer.cpp | 73 +++++++++++++++++++ .../renderer_vulkan/vk_rasterizer.h | 1 + src/video_core/texture_cache/image_info.cpp | 7 +- src/video_core/texture_cache/image_info.h | 2 +- src/video_core/texture_cache/texture_cache.h | 4 +- 8 files changed, 146 insertions(+), 13 deletions(-) diff --git a/src/core/devtools/widget/reg_popup.cpp b/src/core/devtools/widget/reg_popup.cpp index fae620901..7bb38df24 100644 --- a/src/core/devtools/widget/reg_popup.cpp +++ b/src/core/devtools/widget/reg_popup.cpp @@ -105,7 +105,8 @@ void RegPopup::DrawDepthBuffer(const DepthBuffer& depth_data) { "DEPTH_SLICE.TILE_MAX", depth_buffer.depth_slice.tile_max, "Pitch()", depth_buffer.Pitch(), "Height()", depth_buffer.Height(), - "Address()", depth_buffer.Address(), + "DepthAddress()", depth_buffer.DepthAddress(), + "StencilAddress()", depth_buffer.StencilAddress(), "NumSamples()", depth_buffer.NumSamples(), "NumBits()", depth_buffer.NumBits(), "GetDepthSliceSize()", depth_buffer.GetDepthSliceSize() diff --git a/src/core/devtools/widget/reg_view.cpp b/src/core/devtools/widget/reg_view.cpp index a1b7937df..fa3c5e3e6 100644 --- a/src/core/devtools/widget/reg_view.cpp +++ b/src/core/devtools/widget/reg_view.cpp @@ -155,7 +155,7 @@ void RegView::DrawGraphicsRegs() { TableNextColumn(); TextUnformatted("Depth buffer"); TableNextColumn(); - if (regs.depth_buffer.Address() == 0 || !regs.depth_control.depth_enable) { + if (regs.depth_buffer.DepthAddress() == 0 || !regs.depth_control.depth_enable) { TextUnformatted("N/A"); } else { const char* text = last_selected_cb == depth_id && default_reg_popup.open ? "x" : "->"; @@ -241,7 +241,7 @@ void RegView::SetData(DebugStateType::RegDump _data, const std::string& base_tit default_reg_popup.open = false; if (last_selected_cb == depth_id) { const auto& has_depth = - regs.depth_buffer.Address() != 0 && regs.depth_control.depth_enable; + regs.depth_buffer.DepthAddress() != 0 && regs.depth_control.depth_enable; if (has_depth) { default_reg_popup.SetData(title, regs.depth_buffer, regs.depth_control); default_reg_popup.open = true; diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index 070253ca9..a29bde4ce 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -429,11 +429,19 @@ struct Liverpool { } depth_slice; bool DepthValid() const { - return Address() != 0 && z_info.format != ZFormat::Invalid; + return DepthAddress() != 0 && z_info.format != ZFormat::Invalid; } bool StencilValid() const { - return Address() != 0 && stencil_info.format != StencilFormat::Invalid; + return StencilAddress() != 0 && stencil_info.format != StencilFormat::Invalid; + } + + bool DepthWriteValid() const { + return DepthWriteAddress() != 0 && z_info.format != ZFormat::Invalid; + } + + bool StencilWriteValid() const { + return StencilWriteAddress() != 0 && stencil_info.format != StencilFormat::Invalid; } u32 Pitch() const { @@ -444,7 +452,7 @@ struct Liverpool { return (depth_size.height_tile_max + 1) << 3; } - u64 Address() const { + u64 DepthAddress() const { return u64(z_read_base) << 8; } @@ -452,6 +460,14 @@ struct Liverpool { return u64(stencil_read_base) << 8; } + u64 DepthWriteAddress() const { + return u64(z_write_base) << 8; + } + + u64 StencilWriteAddress() const { + return u64(stencil_write_base) << 8; + } + u32 NumSamples() const { return 1u << z_info.num_samples; // spec doesn't say it is a log2 } @@ -1008,6 +1024,46 @@ struct Liverpool { } }; + enum class ForceEnable : u32 { + Off = 0, + Enable = 1, + Disable = 2, + }; + + enum class ForceSumm : u32 { + Off = 0, + MinZ = 1, + MaxZ = 2, + Both = 3, + }; + + union DepthRenderOverride { + u32 raw; + BitField<0, 2, ForceEnable> force_hiz_enable; + BitField<2, 2, ForceEnable> force_his_enable0; + BitField<4, 2, ForceEnable> force_his_enable1; + BitField<6, 1, u32> force_shader_z_order; + BitField<7, 1, u32> fast_z_disable; + BitField<8, 1, u32> fast_stencil_disable; + BitField<9, 1, u32> noop_cull_disable; + BitField<10, 1, u32> force_color_kill; + BitField<11, 1, u32> force_z_read; + BitField<12, 1, u32> force_stencil_read; + BitField<13, 2, ForceEnable> force_full_z_range; + BitField<15, 1, u32> force_qc_smask_conflict; + BitField<16, 1, u32> disable_viewport_clamp; + BitField<17, 1, u32> ignore_sc_zrange; + BitField<18, 1, u32> disable_fully_covered; + BitField<19, 2, ForceSumm> force_z_limit_summ; + BitField<21, 5, u32> max_tiles_in_dtt; + BitField<26, 1, u32> disable_tile_rate_tiles; + BitField<27, 1, u32> force_z_dirty; + BitField<28, 1, u32> force_stencil_dirty; + BitField<29, 1, u32> force_z_valid; + BitField<30, 1, u32> force_stencil_valid; + BitField<31, 1, u32> preserve_compression; + }; + union AaConfig { BitField<0, 3, u32> msaa_num_samples; BitField<4, 1, u32> aa_mask_centroid_dtmn; @@ -1209,7 +1265,8 @@ struct Liverpool { DepthRenderControl depth_render_control; INSERT_PADDING_WORDS(1); DepthView depth_view; - INSERT_PADDING_WORDS(2); + DepthRenderOverride depth_render_override; + INSERT_PADDING_WORDS(1); Address depth_htile_data_base; INSERT_PADDING_WORDS(2); float depth_bounds_min; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 920e09131..06cfbedac 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -70,6 +70,26 @@ bool Rasterizer::FilterDraw() { return false; } + const bool cb_disabled = + regs.color_control.mode == AmdGpu::Liverpool::ColorControl::OperationMode::Disable; + const auto depth_copy = + regs.depth_render_override.force_z_dirty && regs.depth_render_override.force_z_valid && + regs.depth_buffer.DepthValid() && regs.depth_buffer.DepthWriteValid() && + regs.depth_buffer.DepthAddress() != regs.depth_buffer.DepthWriteAddress(); + const auto stencil_copy = + regs.depth_render_override.force_stencil_dirty && + regs.depth_render_override.force_stencil_valid && regs.depth_buffer.StencilValid() && + regs.depth_buffer.StencilWriteValid() && + regs.depth_buffer.StencilAddress() != regs.depth_buffer.StencilWriteAddress(); + if (cb_disabled && (depth_copy || stencil_copy)) { + // Games may disable color buffer and enable force depth/stencil dirty and valid to + // do a copy from one depth-stencil surface to another, without a pixel shader. + // We need to detect this case and perform the copy, otherwise it will have no effect. + LOG_TRACE(Render_Vulkan, "Performing depth-stencil override copy"); + DepthStencilCopy(depth_copy, stencil_copy); + return false; + } + return true; } @@ -899,6 +919,59 @@ void Rasterizer::Resolve() { } } +void Rasterizer::DepthStencilCopy(bool is_depth, bool is_stencil) { + auto& regs = liverpool->regs; + + auto read_desc = VideoCore::TextureCache::DepthTargetDesc( + regs.depth_buffer, regs.depth_view, regs.depth_control, + regs.depth_htile_data_base.GetAddress(), liverpool->last_db_extent, false); + auto write_desc = VideoCore::TextureCache::DepthTargetDesc( + regs.depth_buffer, regs.depth_view, regs.depth_control, + regs.depth_htile_data_base.GetAddress(), liverpool->last_db_extent, true); + + auto& read_image = texture_cache.GetImage(texture_cache.FindImage(read_desc)); + auto& write_image = texture_cache.GetImage(texture_cache.FindImage(write_desc)); + + VideoCore::SubresourceRange sub_range; + sub_range.base.layer = liverpool->regs.depth_view.slice_start; + sub_range.extent.layers = liverpool->regs.depth_view.NumSlices() - sub_range.base.layer; + + read_image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, + sub_range); + write_image.Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite, + sub_range); + + auto aspect_mask = vk::ImageAspectFlags(0); + if (is_depth) { + aspect_mask |= vk::ImageAspectFlagBits::eDepth; + } + if (is_stencil) { + aspect_mask |= vk::ImageAspectFlagBits::eStencil; + } + vk::ImageCopy region = { + .srcSubresource = + { + .aspectMask = aspect_mask, + .mipLevel = 0, + .baseArrayLayer = sub_range.base.layer, + .layerCount = sub_range.extent.layers, + }, + .srcOffset = {0, 0, 0}, + .dstSubresource = + { + .aspectMask = aspect_mask, + .mipLevel = 0, + .baseArrayLayer = sub_range.base.layer, + .layerCount = sub_range.extent.layers, + }, + .dstOffset = {0, 0, 0}, + .extent = {write_image.info.size.width, write_image.info.size.height, 1}, + }; + const auto cmdbuf = scheduler.CommandBuffer(); + cmdbuf.copyImage(read_image.image, vk::ImageLayout::eTransferSrcOptimal, write_image.image, + vk::ImageLayout::eTransferDstOptimal, region); +} + void Rasterizer::InlineData(VAddr address, const void* value, u32 num_bytes, bool is_gds) { buffer_cache.InlineData(address, value, num_bytes, is_gds); } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 2905c5ddb..1e4a210bb 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -71,6 +71,7 @@ private: RenderState PrepareRenderState(u32 mrt_mask); void BeginRendering(const GraphicsPipeline& pipeline, RenderState& state); void Resolve(); + void DepthStencilCopy(bool is_depth, bool is_stencil); void EliminateFastClear(); void UpdateDynamicState(const GraphicsPipeline& pipeline); diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index 1992f1fb7..07a0488f3 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -98,7 +98,8 @@ ImageInfo::ImageInfo(const AmdGpu::Liverpool::ColorBuffer& buffer, } ImageInfo::ImageInfo(const AmdGpu::Liverpool::DepthBuffer& buffer, u32 num_slices, - VAddr htile_address, const AmdGpu::Liverpool::CbDbExtent& hint) noexcept { + VAddr htile_address, const AmdGpu::Liverpool::CbDbExtent& hint, + bool write_buffer) noexcept { props.is_tiled = false; pixel_format = LiverpoolToVK::DepthFormat(buffer.z_info.format, buffer.stencil_info.format); type = vk::ImageType::e2D; @@ -111,10 +112,10 @@ ImageInfo::ImageInfo(const AmdGpu::Liverpool::DepthBuffer& buffer, u32 num_slice resources.layers = num_slices; meta_info.htile_addr = buffer.z_info.tile_surface_en ? htile_address : 0; - stencil_addr = buffer.StencilAddress(); + stencil_addr = write_buffer ? buffer.StencilWriteAddress() : buffer.StencilAddress(); stencil_size = pitch * size.height * sizeof(u8); - guest_address = buffer.Address(); + guest_address = write_buffer ? buffer.DepthWriteAddress() : buffer.DepthAddress(); const auto depth_slice_sz = buffer.GetDepthSliceSize(); guest_size = depth_slice_sz * num_slices; mips_layout.emplace_back(depth_slice_sz, pitch, 0); diff --git a/src/video_core/texture_cache/image_info.h b/src/video_core/texture_cache/image_info.h index 123540c1e..dad0e751e 100644 --- a/src/video_core/texture_cache/image_info.h +++ b/src/video_core/texture_cache/image_info.h @@ -19,7 +19,7 @@ struct ImageInfo { ImageInfo(const AmdGpu::Liverpool::ColorBuffer& buffer, const AmdGpu::Liverpool::CbDbExtent& hint = {}) noexcept; ImageInfo(const AmdGpu::Liverpool::DepthBuffer& buffer, u32 num_slices, VAddr htile_address, - const AmdGpu::Liverpool::CbDbExtent& hint = {}) noexcept; + const AmdGpu::Liverpool::CbDbExtent& hint = {}, bool write_buffer = false) noexcept; ImageInfo(const AmdGpu::Image& image, const Shader::ImageResource& desc) noexcept; bool IsTiled() const { diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 69907f000..343a510e6 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -79,9 +79,9 @@ public: DepthTargetDesc(const AmdGpu::Liverpool::DepthBuffer& buffer, const AmdGpu::Liverpool::DepthView& view, const AmdGpu::Liverpool::DepthControl& ctl, VAddr htile_address, - const AmdGpu::Liverpool::CbDbExtent& hint = {}) + const AmdGpu::Liverpool::CbDbExtent& hint = {}, bool write_buffer = false) : BaseDesc{BindingType::DepthTarget, - ImageInfo{buffer, view.NumSlices(), htile_address, hint}, + ImageInfo{buffer, view.NumSlices(), htile_address, hint, write_buffer}, ImageViewInfo{buffer, view, ctl}} {} }; From c10f9b8269761c2c19ac8dfabafc384a77a63dc1 Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Wed, 15 Jan 2025 05:19:41 -0600 Subject: [PATCH 407/549] Add libSceNpWebApi (#2150) Includes a dummy return for sceNpWebApiInitialize, to make it return a positive value. --- CMakeLists.txt | 2 + src/common/logging/filter.cpp | 1 + src/common/logging/types.h | 1 + src/core/libraries/libs.cpp | 2 + src/core/libraries/np_web_api/np_web_api.cpp | 692 +++++++++++++++++++ src/core/libraries/np_web_api/np_web_api.h | 116 ++++ 6 files changed, 814 insertions(+) create mode 100644 src/core/libraries/np_web_api/np_web_api.cpp create mode 100644 src/core/libraries/np_web_api/np_web_api.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 30e6c58c0..be87de119 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -430,6 +430,8 @@ set(NP_LIBS src/core/libraries/np_common/np_common.cpp src/core/libraries/np_trophy/trophy_ui.cpp src/core/libraries/np_trophy/trophy_ui.h src/core/libraries/np_trophy/np_trophy_error.h + src/core/libraries/np_web_api/np_web_api.cpp + src/core/libraries/np_web_api/np_web_api.h ) set(MISC_LIBS src/core/libraries/screenshot/screenshot.cpp diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index 376c55ba7..168d03948 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -104,6 +104,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Lib, NpManager) \ SUB(Lib, NpScore) \ SUB(Lib, NpTrophy) \ + SUB(Lib, NpWebApi) \ SUB(Lib, Screenshot) \ SUB(Lib, LibCInternal) \ SUB(Lib, AppContent) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index 535a88a6d..4ca88e1be 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -71,6 +71,7 @@ enum class Class : u8 { Lib_NpManager, ///< The LibSceNpManager implementation Lib_NpScore, ///< The LibSceNpScore implementation Lib_NpTrophy, ///< The LibSceNpTrophy implementation + Lib_NpWebApi, ///< The LibSceWebApi implementation Lib_Screenshot, ///< The LibSceScreenshot implementation Lib_LibCInternal, ///< The LibCInternal implementation. Lib_AppContent, ///< The LibSceAppContent implementation. diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index 7427640b6..6dc455028 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -30,6 +30,7 @@ #include "core/libraries/np_manager/np_manager.h" #include "core/libraries/np_score/np_score.h" #include "core/libraries/np_trophy/np_trophy.h" +#include "core/libraries/np_web_api/np_web_api.h" #include "core/libraries/pad/pad.h" #include "core/libraries/playgo/playgo.h" #include "core/libraries/playgo/playgo_dialog.h" @@ -81,6 +82,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::NpManager::RegisterlibSceNpManager(sym); Libraries::NpScore::RegisterlibSceNpScore(sym); Libraries::NpTrophy::RegisterlibSceNpTrophy(sym); + Libraries::NpWebApi::RegisterlibSceNpWebApi(sym); Libraries::ScreenShot::RegisterlibSceScreenShot(sym); Libraries::AppContent::RegisterlibSceAppContent(sym); Libraries::PngDec::RegisterlibScePngDec(sym); diff --git a/src/core/libraries/np_web_api/np_web_api.cpp b/src/core/libraries/np_web_api/np_web_api.cpp new file mode 100644 index 000000000..8a7979978 --- /dev/null +++ b/src/core/libraries/np_web_api/np_web_api.cpp @@ -0,0 +1,692 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" +#include "core/libraries/np_web_api/np_web_api.h" + +namespace Libraries::NpWebApi { + +s32 PS4_SYSV_ABI sceNpWebApiCreateContext() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiCreatePushEventFilter() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiCreateServicePushEventFilter() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiDeletePushEventFilter() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiDeleteServicePushEventFilter() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiRegisterExtdPushEventCallback() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiRegisterNotificationCallback() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiRegisterPushEventCallback() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiRegisterServicePushEventCallback() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiUnregisterNotificationCallback() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiUnregisterPushEventCallback() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiUnregisterServicePushEventCallback() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiAbortHandle() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiAbortRequest() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiAddHttpRequestHeader() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiAddMultipartPart() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiCheckTimeout() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiClearAllUnusedConnection() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiClearUnusedConnection() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiCreateContextA() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiCreateExtdPushEventFilter() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiCreateHandle() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiCreateMultipartRequest() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiCreateRequest() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiDeleteContext() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiDeleteExtdPushEventFilter() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiDeleteHandle() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiDeleteRequest() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiGetConnectionStats() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiGetErrorCode() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiGetHttpResponseHeaderValue() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiGetHttpResponseHeaderValueLength() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiGetHttpStatusCode() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiGetMemoryPoolStats() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiInitialize() { + LOG_ERROR(Lib_NpWebApi, "(DUMMY) called"); + static s32 id = 0; + return ++id; +} + +s32 PS4_SYSV_ABI sceNpWebApiInitializeForPresence() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiIntCreateCtxIndExtdPushEventFilter() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiIntCreateRequest() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiIntCreateServicePushEventFilter() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiIntInitialize() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiIntRegisterServicePushEventCallback() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiIntRegisterServicePushEventCallbackA() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiReadData() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiRegisterExtdPushEventCallbackA() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiSendMultipartRequest() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiSendMultipartRequest2() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiSendRequest() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiSendRequest2() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiSetHandleTimeout() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiSetMaxConnection() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiSetMultipartContentType() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiSetRequestTimeout() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiTerminate() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiUnregisterExtdPushEventCallback() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiUtilityParseNpId() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiVshInitialize() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_064C4ED1EDBEB9E8() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_0783955D4E9563DA() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_1A6D77F3FD8323A8() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_1E0693A26FE0F954() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_24A9B5F1D77000CF() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_24AAA6F50E4C2361() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_24D8853D6B47FC79() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_279B3E9C7C4A9DC5() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_28461E29E9F8D697() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_3C29624704FAB9E0() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_3F027804ED2EC11E() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_4066C94E782997CD() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_47C85356815DBE90() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_4FCE8065437E3B87() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_536280BE3DABB521() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_57A0E1BC724219F3() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_5819749C040B6637() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_6198D0C825E86319() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_61F2B9E8AB093743() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_6BC388E6113F0D44() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_7500F0C4F8DC2D16() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_75A03814C7E9039F() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_789D6026C521416E() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_7DED63D06399EFFF() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_7E55A2DCC03D395A() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_7E6C8F9FB86967F4() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_7F04B7D4A7D41E80() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_8E167252DFA5C957() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_95D0046E504E3B09() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_97284BFDA4F18FDF() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_99E32C1F4737EAB4() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_9CFF661EA0BCBF83() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_9EB0E1F467AC3B29() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_A2318FE6FBABFAA3() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_BA07A2E1BF7B3971() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_BD0803EEE0CC29A0() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_BE6F4E5524BB135F() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_C0D490EB481EA4D0() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_C175D392CA6D084A() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_CD0136AF165D2F2F() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_D1C0ADB7B52FEAB5() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_E324765D18EE4D12() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_E789F980D907B653() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_F9A32E8685627436() { + LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); + return ORBIS_OK; +} + +void RegisterlibSceNpWebApi(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("x1Y7yiYSk7c", "libSceNpWebApiCompat", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiCreateContext); + LIB_FUNCTION("y5Ta5JCzQHY", "libSceNpWebApiCompat", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiCreatePushEventFilter); + LIB_FUNCTION("sIFx734+xys", "libSceNpWebApiCompat", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiCreateServicePushEventFilter); + LIB_FUNCTION("zE+R6Rcx3W0", "libSceNpWebApiCompat", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiDeletePushEventFilter); + LIB_FUNCTION("PfQ+f6ws764", "libSceNpWebApiCompat", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiDeleteServicePushEventFilter); + LIB_FUNCTION("vrM02A5Gy1M", "libSceNpWebApiCompat", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiRegisterExtdPushEventCallback); + LIB_FUNCTION("HVgWmGIOKdk", "libSceNpWebApiCompat", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiRegisterNotificationCallback); + LIB_FUNCTION("PfSTDCgNMgc", "libSceNpWebApiCompat", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiRegisterPushEventCallback); + LIB_FUNCTION("kJQJE0uKm5w", "libSceNpWebApiCompat", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiRegisterServicePushEventCallback); + LIB_FUNCTION("wjYEvo4xbcA", "libSceNpWebApiCompat", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiUnregisterNotificationCallback); + LIB_FUNCTION("qK4o2656W4w", "libSceNpWebApiCompat", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiUnregisterPushEventCallback); + LIB_FUNCTION("2edrkr0c-wg", "libSceNpWebApiCompat", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiUnregisterServicePushEventCallback); + LIB_FUNCTION("WKcm4PeyJww", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiAbortHandle); + LIB_FUNCTION("JzhYTP2fG18", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiAbortRequest); + LIB_FUNCTION("joRjtRXTFoc", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiAddHttpRequestHeader); + LIB_FUNCTION("19KgfJXgM+U", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiAddMultipartPart); + LIB_FUNCTION("gVNNyxf-1Sg", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiCheckTimeout); + LIB_FUNCTION("KQIkDGf80PQ", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiClearAllUnusedConnection); + LIB_FUNCTION("f-pgaNSd1zc", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiClearUnusedConnection); + LIB_FUNCTION("x1Y7yiYSk7c", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiCreateContext); + LIB_FUNCTION("zk6c65xoyO0", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiCreateContextA); + LIB_FUNCTION("M2BUB+DNEGE", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiCreateExtdPushEventFilter); + LIB_FUNCTION("79M-JqvvGo0", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiCreateHandle); + LIB_FUNCTION("KBxgeNpoRIQ", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiCreateMultipartRequest); + LIB_FUNCTION("y5Ta5JCzQHY", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiCreatePushEventFilter); + LIB_FUNCTION("rdgs5Z1MyFw", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiCreateRequest); + LIB_FUNCTION("sIFx734+xys", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiCreateServicePushEventFilter); + LIB_FUNCTION("XUjdsSTTZ3U", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiDeleteContext); + LIB_FUNCTION("pfaJtb7SQ80", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiDeleteExtdPushEventFilter); + LIB_FUNCTION("5Mn7TYwpl30", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiDeleteHandle); + LIB_FUNCTION("zE+R6Rcx3W0", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiDeletePushEventFilter); + LIB_FUNCTION("noQgleu+KLE", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiDeleteRequest); + LIB_FUNCTION("PfQ+f6ws764", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiDeleteServicePushEventFilter); + LIB_FUNCTION("UJ8H+7kVQUE", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiGetConnectionStats); + LIB_FUNCTION("2qSZ0DgwTsc", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiGetErrorCode); + LIB_FUNCTION("VwJ5L0Higg0", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiGetHttpResponseHeaderValue); + LIB_FUNCTION("743ZzEBzlV8", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiGetHttpResponseHeaderValueLength); + LIB_FUNCTION("k210oKgP80Y", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiGetHttpStatusCode); + LIB_FUNCTION("3OnubUs02UM", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiGetMemoryPoolStats); + LIB_FUNCTION("G3AnLNdRBjE", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, sceNpWebApiInitialize); + LIB_FUNCTION("FkuwsD64zoQ", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiInitializeForPresence); + LIB_FUNCTION("c1pKoztonB8", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiIntCreateCtxIndExtdPushEventFilter); + LIB_FUNCTION("N2Jbx4tIaQ4", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiIntCreateRequest); + LIB_FUNCTION("TZSep4xB4EY", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiIntCreateServicePushEventFilter); + LIB_FUNCTION("8Vjplhyyc44", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiIntInitialize); + LIB_FUNCTION("VjVukb2EWPc", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiIntRegisterServicePushEventCallback); + LIB_FUNCTION("sfq23ZVHVEw", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiIntRegisterServicePushEventCallbackA); + LIB_FUNCTION("CQtPRSF6Ds8", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, sceNpWebApiReadData); + LIB_FUNCTION("vrM02A5Gy1M", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiRegisterExtdPushEventCallback); + LIB_FUNCTION("jhXKGQJ4egI", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiRegisterExtdPushEventCallbackA); + LIB_FUNCTION("HVgWmGIOKdk", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiRegisterNotificationCallback); + LIB_FUNCTION("PfSTDCgNMgc", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiRegisterPushEventCallback); + LIB_FUNCTION("kJQJE0uKm5w", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiRegisterServicePushEventCallback); + LIB_FUNCTION("KCItz6QkeGs", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiSendMultipartRequest); + LIB_FUNCTION("DsPOTEvSe7M", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiSendMultipartRequest2); + LIB_FUNCTION("kVbL4hL3K7w", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiSendRequest); + LIB_FUNCTION("KjNeZ-29ysQ", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiSendRequest2); + LIB_FUNCTION("6g6q-g1i4XU", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiSetHandleTimeout); + LIB_FUNCTION("gRiilVCvfAI", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiSetMaxConnection); + LIB_FUNCTION("i0dr6grIZyc", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiSetMultipartContentType); + LIB_FUNCTION("qWcbJkBj1Lg", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiSetRequestTimeout); + LIB_FUNCTION("asz3TtIqGF8", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, sceNpWebApiTerminate); + LIB_FUNCTION("PqCY25FMzPs", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiUnregisterExtdPushEventCallback); + LIB_FUNCTION("wjYEvo4xbcA", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiUnregisterNotificationCallback); + LIB_FUNCTION("qK4o2656W4w", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiUnregisterPushEventCallback); + LIB_FUNCTION("2edrkr0c-wg", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiUnregisterServicePushEventCallback); + LIB_FUNCTION("or0e885BlXo", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiUtilityParseNpId); + LIB_FUNCTION("uRsskUhAfnM", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, + sceNpWebApiVshInitialize); + LIB_FUNCTION("BkxO0e2+ueg", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_064C4ED1EDBEB9E8); + LIB_FUNCTION("B4OVXU6VY9o", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_0783955D4E9563DA); + LIB_FUNCTION("Gm138-2DI6g", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_1A6D77F3FD8323A8); + LIB_FUNCTION("HgaTom-g+VQ", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_1E0693A26FE0F954); + LIB_FUNCTION("JKm18ddwAM8", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_24A9B5F1D77000CF); + LIB_FUNCTION("JKqm9Q5MI2E", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_24AAA6F50E4C2361); + LIB_FUNCTION("JNiFPWtH-Hk", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_24D8853D6B47FC79); + LIB_FUNCTION("J5s+nHxKncU", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_279B3E9C7C4A9DC5); + LIB_FUNCTION("KEYeKen41pc", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_28461E29E9F8D697); + LIB_FUNCTION("PCliRwT6ueA", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_3C29624704FAB9E0); + LIB_FUNCTION("PwJ4BO0uwR4", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_3F027804ED2EC11E); + LIB_FUNCTION("QGbJTngpl80", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_4066C94E782997CD); + LIB_FUNCTION("R8hTVoFdvpA", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_47C85356815DBE90); + LIB_FUNCTION("T86AZUN+O4c", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_4FCE8065437E3B87); + LIB_FUNCTION("U2KAvj2rtSE", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_536280BE3DABB521); + LIB_FUNCTION("V6DhvHJCGfM", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_57A0E1BC724219F3); + LIB_FUNCTION("WBl0nAQLZjc", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_5819749C040B6637); + LIB_FUNCTION("YZjQyCXoYxk", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_6198D0C825E86319); + LIB_FUNCTION("YfK56KsJN0M", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_61F2B9E8AB093743); + LIB_FUNCTION("a8OI5hE-DUQ", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_6BC388E6113F0D44); + LIB_FUNCTION("dQDwxPjcLRY", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_7500F0C4F8DC2D16); + LIB_FUNCTION("daA4FMfpA58", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_75A03814C7E9039F); + LIB_FUNCTION("eJ1gJsUhQW4", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_789D6026C521416E); + LIB_FUNCTION("fe1j0GOZ7-8", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_7DED63D06399EFFF); + LIB_FUNCTION("flWi3MA9OVo", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_7E55A2DCC03D395A); + LIB_FUNCTION("fmyPn7hpZ-Q", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_7E6C8F9FB86967F4); + LIB_FUNCTION("fwS31KfUHoA", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_7F04B7D4A7D41E80); + LIB_FUNCTION("jhZyUt+lyVc", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_8E167252DFA5C957); + LIB_FUNCTION("ldAEblBOOwk", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_95D0046E504E3B09); + LIB_FUNCTION("lyhL-aTxj98", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_97284BFDA4F18FDF); + LIB_FUNCTION("meMsH0c36rQ", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_99E32C1F4737EAB4); + LIB_FUNCTION("nP9mHqC8v4M", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_9CFF661EA0BCBF83); + LIB_FUNCTION("nrDh9GesOyk", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_9EB0E1F467AC3B29); + LIB_FUNCTION("ojGP5vur+qM", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_A2318FE6FBABFAA3); + LIB_FUNCTION("ugei4b97OXE", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_BA07A2E1BF7B3971); + LIB_FUNCTION("vQgD7uDMKaA", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_BD0803EEE0CC29A0); + LIB_FUNCTION("vm9OVSS7E18", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_BE6F4E5524BB135F); + LIB_FUNCTION("wNSQ60gepNA", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_C0D490EB481EA4D0); + LIB_FUNCTION("wXXTksptCEo", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_C175D392CA6D084A); + LIB_FUNCTION("zQE2rxZdLy8", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_CD0136AF165D2F2F); + LIB_FUNCTION("0cCtt7Uv6rU", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_D1C0ADB7B52FEAB5); + LIB_FUNCTION("4yR2XRjuTRI", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_E324765D18EE4D12); + LIB_FUNCTION("54n5gNkHtlM", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_E789F980D907B653); + LIB_FUNCTION("+aMuhoVidDY", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, Func_F9A32E8685627436); +}; + +} // namespace Libraries::NpWebApi \ No newline at end of file diff --git a/src/core/libraries/np_web_api/np_web_api.h b/src/core/libraries/np_web_api/np_web_api.h new file mode 100644 index 000000000..cc007394f --- /dev/null +++ b/src/core/libraries/np_web_api/np_web_api.h @@ -0,0 +1,116 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::NpWebApi { + +s32 PS4_SYSV_ABI sceNpWebApiCreateContext(); +s32 PS4_SYSV_ABI sceNpWebApiCreatePushEventFilter(); +s32 PS4_SYSV_ABI sceNpWebApiCreateServicePushEventFilter(); +s32 PS4_SYSV_ABI sceNpWebApiDeletePushEventFilter(); +s32 PS4_SYSV_ABI sceNpWebApiDeleteServicePushEventFilter(); +s32 PS4_SYSV_ABI sceNpWebApiRegisterExtdPushEventCallback(); +s32 PS4_SYSV_ABI sceNpWebApiRegisterNotificationCallback(); +s32 PS4_SYSV_ABI sceNpWebApiRegisterPushEventCallback(); +s32 PS4_SYSV_ABI sceNpWebApiRegisterServicePushEventCallback(); +s32 PS4_SYSV_ABI sceNpWebApiUnregisterNotificationCallback(); +s32 PS4_SYSV_ABI sceNpWebApiUnregisterPushEventCallback(); +s32 PS4_SYSV_ABI sceNpWebApiUnregisterServicePushEventCallback(); +s32 PS4_SYSV_ABI sceNpWebApiAbortHandle(); +s32 PS4_SYSV_ABI sceNpWebApiAbortRequest(); +s32 PS4_SYSV_ABI sceNpWebApiAddHttpRequestHeader(); +s32 PS4_SYSV_ABI sceNpWebApiAddMultipartPart(); +s32 PS4_SYSV_ABI sceNpWebApiCheckTimeout(); +s32 PS4_SYSV_ABI sceNpWebApiClearAllUnusedConnection(); +s32 PS4_SYSV_ABI sceNpWebApiClearUnusedConnection(); +s32 PS4_SYSV_ABI sceNpWebApiCreateContextA(); +s32 PS4_SYSV_ABI sceNpWebApiCreateExtdPushEventFilter(); +s32 PS4_SYSV_ABI sceNpWebApiCreateHandle(); +s32 PS4_SYSV_ABI sceNpWebApiCreateMultipartRequest(); +s32 PS4_SYSV_ABI sceNpWebApiCreateRequest(); +s32 PS4_SYSV_ABI sceNpWebApiDeleteContext(); +s32 PS4_SYSV_ABI sceNpWebApiDeleteExtdPushEventFilter(); +s32 PS4_SYSV_ABI sceNpWebApiDeleteHandle(); +s32 PS4_SYSV_ABI sceNpWebApiDeleteRequest(); +s32 PS4_SYSV_ABI sceNpWebApiGetConnectionStats(); +s32 PS4_SYSV_ABI sceNpWebApiGetErrorCode(); +s32 PS4_SYSV_ABI sceNpWebApiGetHttpResponseHeaderValue(); +s32 PS4_SYSV_ABI sceNpWebApiGetHttpResponseHeaderValueLength(); +s32 PS4_SYSV_ABI sceNpWebApiGetHttpStatusCode(); +s32 PS4_SYSV_ABI sceNpWebApiGetMemoryPoolStats(); +s32 PS4_SYSV_ABI sceNpWebApiInitialize(); +s32 PS4_SYSV_ABI sceNpWebApiInitializeForPresence(); +s32 PS4_SYSV_ABI sceNpWebApiIntCreateCtxIndExtdPushEventFilter(); +s32 PS4_SYSV_ABI sceNpWebApiIntCreateRequest(); +s32 PS4_SYSV_ABI sceNpWebApiIntCreateServicePushEventFilter(); +s32 PS4_SYSV_ABI sceNpWebApiIntInitialize(); +s32 PS4_SYSV_ABI sceNpWebApiIntRegisterServicePushEventCallback(); +s32 PS4_SYSV_ABI sceNpWebApiIntRegisterServicePushEventCallbackA(); +s32 PS4_SYSV_ABI sceNpWebApiReadData(); +s32 PS4_SYSV_ABI sceNpWebApiRegisterExtdPushEventCallbackA(); +s32 PS4_SYSV_ABI sceNpWebApiSendMultipartRequest(); +s32 PS4_SYSV_ABI sceNpWebApiSendMultipartRequest2(); +s32 PS4_SYSV_ABI sceNpWebApiSendRequest(); +s32 PS4_SYSV_ABI sceNpWebApiSendRequest2(); +s32 PS4_SYSV_ABI sceNpWebApiSetHandleTimeout(); +s32 PS4_SYSV_ABI sceNpWebApiSetMaxConnection(); +s32 PS4_SYSV_ABI sceNpWebApiSetMultipartContentType(); +s32 PS4_SYSV_ABI sceNpWebApiSetRequestTimeout(); +s32 PS4_SYSV_ABI sceNpWebApiTerminate(); +s32 PS4_SYSV_ABI sceNpWebApiUnregisterExtdPushEventCallback(); +s32 PS4_SYSV_ABI sceNpWebApiUtilityParseNpId(); +s32 PS4_SYSV_ABI sceNpWebApiVshInitialize(); +s32 PS4_SYSV_ABI Func_064C4ED1EDBEB9E8(); +s32 PS4_SYSV_ABI Func_0783955D4E9563DA(); +s32 PS4_SYSV_ABI Func_1A6D77F3FD8323A8(); +s32 PS4_SYSV_ABI Func_1E0693A26FE0F954(); +s32 PS4_SYSV_ABI Func_24A9B5F1D77000CF(); +s32 PS4_SYSV_ABI Func_24AAA6F50E4C2361(); +s32 PS4_SYSV_ABI Func_24D8853D6B47FC79(); +s32 PS4_SYSV_ABI Func_279B3E9C7C4A9DC5(); +s32 PS4_SYSV_ABI Func_28461E29E9F8D697(); +s32 PS4_SYSV_ABI Func_3C29624704FAB9E0(); +s32 PS4_SYSV_ABI Func_3F027804ED2EC11E(); +s32 PS4_SYSV_ABI Func_4066C94E782997CD(); +s32 PS4_SYSV_ABI Func_47C85356815DBE90(); +s32 PS4_SYSV_ABI Func_4FCE8065437E3B87(); +s32 PS4_SYSV_ABI Func_536280BE3DABB521(); +s32 PS4_SYSV_ABI Func_57A0E1BC724219F3(); +s32 PS4_SYSV_ABI Func_5819749C040B6637(); +s32 PS4_SYSV_ABI Func_6198D0C825E86319(); +s32 PS4_SYSV_ABI Func_61F2B9E8AB093743(); +s32 PS4_SYSV_ABI Func_6BC388E6113F0D44(); +s32 PS4_SYSV_ABI Func_7500F0C4F8DC2D16(); +s32 PS4_SYSV_ABI Func_75A03814C7E9039F(); +s32 PS4_SYSV_ABI Func_789D6026C521416E(); +s32 PS4_SYSV_ABI Func_7DED63D06399EFFF(); +s32 PS4_SYSV_ABI Func_7E55A2DCC03D395A(); +s32 PS4_SYSV_ABI Func_7E6C8F9FB86967F4(); +s32 PS4_SYSV_ABI Func_7F04B7D4A7D41E80(); +s32 PS4_SYSV_ABI Func_8E167252DFA5C957(); +s32 PS4_SYSV_ABI Func_95D0046E504E3B09(); +s32 PS4_SYSV_ABI Func_97284BFDA4F18FDF(); +s32 PS4_SYSV_ABI Func_99E32C1F4737EAB4(); +s32 PS4_SYSV_ABI Func_9CFF661EA0BCBF83(); +s32 PS4_SYSV_ABI Func_9EB0E1F467AC3B29(); +s32 PS4_SYSV_ABI Func_A2318FE6FBABFAA3(); +s32 PS4_SYSV_ABI Func_BA07A2E1BF7B3971(); +s32 PS4_SYSV_ABI Func_BD0803EEE0CC29A0(); +s32 PS4_SYSV_ABI Func_BE6F4E5524BB135F(); +s32 PS4_SYSV_ABI Func_C0D490EB481EA4D0(); +s32 PS4_SYSV_ABI Func_C175D392CA6D084A(); +s32 PS4_SYSV_ABI Func_CD0136AF165D2F2F(); +s32 PS4_SYSV_ABI Func_D1C0ADB7B52FEAB5(); +s32 PS4_SYSV_ABI Func_E324765D18EE4D12(); +s32 PS4_SYSV_ABI Func_E789F980D907B653(); +s32 PS4_SYSV_ABI Func_F9A32E8685627436(); + +void RegisterlibSceNpWebApi(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::NpWebApi \ No newline at end of file From 53d0a309ccd5d63dfacbdf6cf81d408f89ee5753 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Wed, 15 Jan 2025 07:33:15 -0800 Subject: [PATCH 408/549] liverpool_to_vk: Add R32Uint depth promote. (#2145) --- .../renderer_vulkan/liverpool_to_vk.h | 29 ++++++++++++++++++- src/video_core/texture_cache/image_view.cpp | 5 ++-- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.h b/src/video_core/renderer_vulkan/liverpool_to_vk.h index a68280e7d..a9fcd03a9 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.h +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.h @@ -71,8 +71,35 @@ vk::ClearValue ColorBufferClearValue(const AmdGpu::Liverpool::ColorBuffer& color vk::SampleCountFlagBits NumSamples(u32 num_samples, vk::SampleCountFlags supported_flags); +static inline bool IsFormatDepthCompatible(vk::Format fmt) { + switch (fmt) { + // 32-bit float compatible + case vk::Format::eD32Sfloat: + case vk::Format::eR32Sfloat: + case vk::Format::eR32Uint: + // 16-bit unorm compatible + case vk::Format::eD16Unorm: + case vk::Format::eR16Unorm: + return true; + default: + return false; + } +} + +static inline bool IsFormatStencilCompatible(vk::Format fmt) { + switch (fmt) { + // 8-bit uint compatible + case vk::Format::eS8Uint: + case vk::Format::eR8Uint: + case vk::Format::eR8Unorm: + return true; + default: + return false; + } +} + static inline vk::Format PromoteFormatToDepth(vk::Format fmt) { - if (fmt == vk::Format::eR32Sfloat) { + if (fmt == vk::Format::eR32Sfloat || fmt == vk::Format::eR32Uint) { return vk::Format::eD32Sfloat; } else if (fmt == vk::Format::eR16Unorm) { return vk::Format::eD16Unorm; diff --git a/src/video_core/texture_cache/image_view.cpp b/src/video_core/texture_cache/image_view.cpp index d90b78c67..e935c7d2e 100644 --- a/src/video_core/texture_cache/image_view.cpp +++ b/src/video_core/texture_cache/image_view.cpp @@ -82,13 +82,12 @@ ImageView::ImageView(const Vulkan::Instance& instance, const ImageViewInfo& info vk::Format format = info.format; vk::ImageAspectFlags aspect = image.aspect_mask; if (image.aspect_mask & vk::ImageAspectFlagBits::eDepth && - (format == vk::Format::eR32Sfloat || format == vk::Format::eD32Sfloat || - format == vk::Format::eR16Unorm || format == vk::Format::eD16Unorm)) { + Vulkan::LiverpoolToVK::IsFormatDepthCompatible(format)) { format = image.info.pixel_format; aspect = vk::ImageAspectFlagBits::eDepth; } if (image.aspect_mask & vk::ImageAspectFlagBits::eStencil && - (format == vk::Format::eR8Uint || format == vk::Format::eR8Unorm)) { + Vulkan::LiverpoolToVK::IsFormatStencilCompatible(format)) { format = image.info.pixel_format; aspect = vk::ImageAspectFlagBits::eStencil; } From 5a7d45fdfa774e9eb4bc066c44736ad33a54d13f Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Wed, 15 Jan 2025 09:37:20 -0600 Subject: [PATCH 409/549] Missing pthread exports (#2144) --- src/core/libraries/kernel/threads/condvar.cpp | 5 +++++ src/core/libraries/kernel/threads/mutex.cpp | 3 +++ src/core/libraries/kernel/threads/pthread.cpp | 1 + 3 files changed, 9 insertions(+) diff --git a/src/core/libraries/kernel/threads/condvar.cpp b/src/core/libraries/kernel/threads/condvar.cpp index 853526559..0b0545ace 100644 --- a/src/core/libraries/kernel/threads/condvar.cpp +++ b/src/core/libraries/kernel/threads/condvar.cpp @@ -339,6 +339,8 @@ int PS4_SYSV_ABI posix_pthread_condattr_setpshared(PthreadCondAttrT* attr, int p void RegisterCond(Core::Loader::SymbolsResolver* sym) { // Posix LIB_FUNCTION("mKoTx03HRWA", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_condattr_init); + LIB_FUNCTION("dJcuQVn6-Iw", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_condattr_destroy); LIB_FUNCTION("0TyVk4MSLt0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_init); LIB_FUNCTION("2MOy+rUfuhQ", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_signal); LIB_FUNCTION("RXXqi4CtF8w", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_destroy); @@ -347,8 +349,11 @@ void RegisterCond(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("mkx2fVhNMsg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_broadcast); // Posix-Kernel + LIB_FUNCTION("0TyVk4MSLt0", "libkernel", 1, "libkernel", 1, 1, posix_pthread_cond_init); LIB_FUNCTION("Op8TBGY5KHg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_cond_wait); LIB_FUNCTION("mkx2fVhNMsg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_cond_broadcast); + LIB_FUNCTION("mKoTx03HRWA", "libkernel", 1, "libkernel", 1, 1, posix_pthread_condattr_init); + LIB_FUNCTION("dJcuQVn6-Iw", "libkernel", 1, "libkernel", 1, 1, posix_pthread_condattr_destroy); // Orbis LIB_FUNCTION("2Tb92quprl0", "libkernel", 1, "libkernel", 1, 1, ORBIS(scePthreadCondInit)); diff --git a/src/core/libraries/kernel/threads/mutex.cpp b/src/core/libraries/kernel/threads/mutex.cpp index 4f11e32da..956e5ef65 100644 --- a/src/core/libraries/kernel/threads/mutex.cpp +++ b/src/core/libraries/kernel/threads/mutex.cpp @@ -438,8 +438,11 @@ void RegisterMutex(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("K-jXhbt2gn4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_trylock); // Posix-Kernel + LIB_FUNCTION("ttHNfU+qDBU", "libkernel", 1, "libkernel", 1, 1, posix_pthread_mutex_init); LIB_FUNCTION("7H0iTOciTLo", "libkernel", 1, "libkernel", 1, 1, posix_pthread_mutex_lock); LIB_FUNCTION("2Z+PpY6CaJg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_mutex_unlock); + LIB_FUNCTION("dQHWEsJtoE4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_mutexattr_init); + LIB_FUNCTION("mDmgMOGVUqg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_mutexattr_settype); // Orbis LIB_FUNCTION("cmo1RIYva9o", "libkernel", 1, "libkernel", 1, 1, ORBIS(scePthreadMutexInit)); diff --git a/src/core/libraries/kernel/threads/pthread.cpp b/src/core/libraries/kernel/threads/pthread.cpp index 639ed1611..641fbe10d 100644 --- a/src/core/libraries/kernel/threads/pthread.cpp +++ b/src/core/libraries/kernel/threads/pthread.cpp @@ -538,6 +538,7 @@ void RegisterThread(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("6XG4B33N09g", "libScePosix", 1, "libkernel", 1, 1, sched_yield); // Posix-Kernel + LIB_FUNCTION("Z4QosVuAsA0", "libkernel", 1, "libkernel", 1, 1, posix_pthread_once); LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self); LIB_FUNCTION("OxhIB8LB-PQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_create); From 1c3048ccc20e2f50901bffd13296e2c2cf66512b Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Wed, 15 Jan 2025 12:45:02 -0300 Subject: [PATCH 410/549] Fix V_FRACT_F64 (#2156) --- src/shader_recompiler/frontend/translate/vector_alu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shader_recompiler/frontend/translate/vector_alu.cpp b/src/shader_recompiler/frontend/translate/vector_alu.cpp index fd5877c57..b2863f6a8 100644 --- a/src/shader_recompiler/frontend/translate/vector_alu.cpp +++ b/src/shader_recompiler/frontend/translate/vector_alu.cpp @@ -844,7 +844,7 @@ void Translator::V_FREXP_MANT_F64(const GcnInst& inst) { } void Translator::V_FRACT_F64(const GcnInst& inst) { - const IR::F32 src0{GetSrc64(inst.src[0])}; + const IR::F64 src0{GetSrc64(inst.src[0])}; SetDst64(inst.dst[0], ir.FPFract(src0)); } From b3739bea92d9028419b295742c15203b150d5b4d Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Thu, 16 Jan 2025 02:14:34 -0800 Subject: [PATCH 411/549] renderer_vulkan: Simplify debug marker settings. (#2159) * renderer_vulkan: Simplify debug marker settings. * liverpool: Add scope markers for graphics/compute queues. * liverpool: Remove unneeded extra label from command buffer markers. * vk_rasterizer: Add scopes around filtered draw passes. --- src/common/config.cpp | 38 +++++++----- src/common/config.h | 3 +- src/emulator.cpp | 5 +- src/imgui/renderer/imgui_core.cpp | 4 +- src/video_core/amdgpu/liverpool.cpp | 46 +++++++++----- src/video_core/buffer_cache/buffer.cpp | 2 + .../renderer_vulkan/vk_instance.cpp | 11 +--- src/video_core/renderer_vulkan/vk_instance.h | 11 +--- src/video_core/renderer_vulkan/vk_platform.h | 7 +++ .../renderer_vulkan/vk_rasterizer.cpp | 62 ++++++++++++------- .../renderer_vulkan/vk_rasterizer.h | 9 +-- .../renderer_vulkan/vk_resource_pool.cpp | 10 +-- .../renderer_vulkan/vk_swapchain.cpp | 15 ++--- src/video_core/texture_cache/image_view.cpp | 7 +++ 14 files changed, 132 insertions(+), 98 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index 158bfeddf..9c842f8b7 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -61,9 +61,10 @@ static u32 vblankDivider = 1; static bool vkValidation = false; static bool vkValidationSync = false; static bool vkValidationGpu = false; -static bool rdocEnable = false; -static bool vkMarkers = false; static bool vkCrashDiagnostic = false; +static bool vkHostMarkers = false; +static bool vkGuestMarkers = false; +static bool rdocEnable = false; static s16 cursorState = HideCursorState::Idle; static int cursorHideTimeout = 5; // 5 seconds (default) static bool separateupdatefolder = false; @@ -227,10 +228,6 @@ bool isRdocEnabled() { return rdocEnable; } -bool isMarkersEnabled() { - return vkMarkers; -} - u32 vblankDiv() { return vblankDivider; } @@ -247,14 +244,20 @@ bool vkValidationGpuEnabled() { return vkValidationGpu; } -bool vkMarkersEnabled() { - return vkMarkers || vkCrashDiagnostic; // Crash diagnostic forces markers on -} - bool vkCrashDiagnosticEnabled() { return vkCrashDiagnostic; } +bool vkHostMarkersEnabled() { + // Forced on when crash diagnostic enabled. + return vkHostMarkers || vkCrashDiagnostic; +} + +bool vkGuestMarkersEnabled() { + // Forced on when crash diagnostic enabled. + return vkGuestMarkers || vkCrashDiagnostic; +} + bool getSeparateUpdateEnabled() { return separateupdatefolder; } @@ -644,9 +647,10 @@ void load(const std::filesystem::path& path) { vkValidation = toml::find_or(vk, "validation", false); vkValidationSync = toml::find_or(vk, "validation_sync", false); vkValidationGpu = toml::find_or(vk, "validation_gpu", true); - rdocEnable = toml::find_or(vk, "rdocEnable", false); - vkMarkers = toml::find_or(vk, "rdocMarkersEnable", false); vkCrashDiagnostic = toml::find_or(vk, "crashDiagnostic", false); + vkHostMarkers = toml::find_or(vk, "hostMarkers", false); + vkGuestMarkers = toml::find_or(vk, "guestMarkers", false); + rdocEnable = toml::find_or(vk, "rdocEnable", false); } if (data.contains("Debug")) { @@ -752,9 +756,10 @@ void save(const std::filesystem::path& path) { data["Vulkan"]["validation"] = vkValidation; data["Vulkan"]["validation_sync"] = vkValidationSync; data["Vulkan"]["validation_gpu"] = vkValidationGpu; - data["Vulkan"]["rdocEnable"] = rdocEnable; - data["Vulkan"]["rdocMarkersEnable"] = vkMarkers; data["Vulkan"]["crashDiagnostic"] = vkCrashDiagnostic; + data["Vulkan"]["hostMarkers"] = vkHostMarkers; + data["Vulkan"]["guestMarkers"] = vkGuestMarkers; + data["Vulkan"]["rdocEnable"] = rdocEnable; data["Debug"]["DebugDump"] = isDebugDump; data["Debug"]["CollectShader"] = isShaderDebug; @@ -852,9 +857,10 @@ void setDefaultValues() { vkValidation = false; vkValidationSync = false; vkValidationGpu = false; - rdocEnable = false; - vkMarkers = false; vkCrashDiagnostic = false; + vkHostMarkers = false; + vkGuestMarkers = false; + rdocEnable = false; emulator_language = "en"; m_language = 1; gpuId = -1; diff --git a/src/common/config.h b/src/common/config.h index c86e35ebc..f9e4c2815 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -100,8 +100,9 @@ void setRdocEnabled(bool enable); bool vkValidationEnabled(); bool vkValidationSyncEnabled(); bool vkValidationGpuEnabled(); -bool vkMarkersEnabled(); bool vkCrashDiagnosticEnabled(); +bool vkHostMarkersEnabled(); +bool vkGuestMarkersEnabled(); // Gui void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h); diff --git a/src/emulator.cpp b/src/emulator.cpp index dbe693340..61d6d3862 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -66,9 +66,10 @@ Emulator::Emulator() { LOG_INFO(Config, "Vulkan vkValidation: {}", Config::vkValidationEnabled()); LOG_INFO(Config, "Vulkan vkValidationSync: {}", Config::vkValidationSyncEnabled()); LOG_INFO(Config, "Vulkan vkValidationGpu: {}", Config::vkValidationGpuEnabled()); - LOG_INFO(Config, "Vulkan rdocEnable: {}", Config::isRdocEnabled()); - LOG_INFO(Config, "Vulkan rdocMarkersEnable: {}", Config::vkMarkersEnabled()); LOG_INFO(Config, "Vulkan crashDiagnostics: {}", Config::vkCrashDiagnosticEnabled()); + LOG_INFO(Config, "Vulkan hostMarkers: {}", Config::vkHostMarkersEnabled()); + LOG_INFO(Config, "Vulkan guestMarkers: {}", Config::vkGuestMarkersEnabled()); + LOG_INFO(Config, "Vulkan rdocEnable: {}", Config::isRdocEnabled()); // Create stdin/stdout/stderr Common::Singleton::Instance()->CreateStdHandles(); diff --git a/src/imgui/renderer/imgui_core.cpp b/src/imgui/renderer/imgui_core.cpp index 46391faef..335185473 100644 --- a/src/imgui/renderer/imgui_core.cpp +++ b/src/imgui/renderer/imgui_core.cpp @@ -199,7 +199,7 @@ void Render(const vk::CommandBuffer& cmdbuf, ::Vulkan::Frame* frame) { return; } - if (Config::vkMarkersEnabled()) { + if (Config::vkHostMarkersEnabled()) { cmdbuf.beginDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT{ .pLabelName = "ImGui Render", }); @@ -224,7 +224,7 @@ void Render(const vk::CommandBuffer& cmdbuf, ::Vulkan::Frame* frame) { cmdbuf.beginRendering(render_info); Vulkan::RenderDrawData(*draw_data, cmdbuf); cmdbuf.endRendering(); - if (Config::vkMarkersEnabled()) { + if (Config::vkHostMarkersEnabled()) { cmdbuf.endDebugUtilsLabelEXT(); } } diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index 16ed84f74..036a031a7 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -224,6 +224,10 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spanScopeMarkerBegin("gfx"); + } + const auto base_addr = reinterpret_cast(dcb.data()); while (!dcb.empty()) { const auto* header = reinterpret_cast(dcb.data()); @@ -260,7 +264,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(&nop->data_block[1]), marker_sz}; if (rasterizer) { - rasterizer->ScopeMarkerBegin(label); + rasterizer->ScopeMarkerBegin(label, true); } break; } @@ -271,13 +275,13 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span( reinterpret_cast(&nop->data_block[1]) + marker_sz); if (rasterizer) { - rasterizer->ScopedMarkerInsertColor(label, color); + rasterizer->ScopedMarkerInsertColor(label, color, true); } break; } case PM4CmdNop::PayloadType::DebugMarkerPop: { if (rasterizer) { - rasterizer->ScopeMarkerEnd(); + rasterizer->ScopeMarkerEnd(true); } break; } @@ -412,7 +416,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); - rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:DrawIndex2", cmd_address)); + rasterizer->ScopeMarkerBegin(fmt::format("{}:DrawIndex2", cmd_address)); rasterizer->Draw(true); rasterizer->ScopeMarkerEnd(); } @@ -429,8 +433,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); - rasterizer->ScopeMarkerBegin( - fmt::format("dcb:{}:DrawIndexOffset2", cmd_address)); + rasterizer->ScopeMarkerBegin(fmt::format("{}:DrawIndexOffset2", cmd_address)); rasterizer->Draw(true, draw_index_off->index_offset); rasterizer->ScopeMarkerEnd(); } @@ -445,7 +448,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); - rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:DrawIndexAuto", cmd_address)); + rasterizer->ScopeMarkerBegin(fmt::format("{}:DrawIndexAuto", cmd_address)); rasterizer->Draw(false); rasterizer->ScopeMarkerEnd(); } @@ -460,7 +463,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); - rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:DrawIndirect", cmd_address)); + rasterizer->ScopeMarkerBegin(fmt::format("{}:DrawIndirect", cmd_address)); rasterizer->DrawIndirect(false, indirect_args_addr, offset, size, 1, 0); rasterizer->ScopeMarkerEnd(); } @@ -476,8 +479,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); - rasterizer->ScopeMarkerBegin( - fmt::format("dcb:{}:DrawIndexIndirect", cmd_address)); + rasterizer->ScopeMarkerBegin(fmt::format("{}:DrawIndexIndirect", cmd_address)); rasterizer->DrawIndirect(true, indirect_args_addr, offset, size, 1, 0); rasterizer->ScopeMarkerEnd(); } @@ -493,7 +495,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); rasterizer->ScopeMarkerBegin( - fmt::format("dcb:{}:DrawIndexIndirectCountMulti", cmd_address)); + fmt::format("{}:DrawIndexIndirectCountMulti", cmd_address)); rasterizer->DrawIndirect( true, indirect_args_addr, offset, draw_index_indirect->stride, draw_index_indirect->count, draw_index_indirect->countAddr); @@ -514,7 +516,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); - rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:Dispatch", cmd_address)); + rasterizer->ScopeMarkerBegin(fmt::format("{}:DispatchDirect", cmd_address)); rasterizer->DispatchDirect(); rasterizer->ScopeMarkerEnd(); } @@ -532,8 +534,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); - rasterizer->ScopeMarkerBegin( - fmt::format("dcb:{}:DispatchIndirect", cmd_address)); + rasterizer->ScopeMarkerBegin(fmt::format("{}:DispatchIndirect", cmd_address)); rasterizer->DispatchIndirect(indirect_args_addr, offset, size); rasterizer->ScopeMarkerEnd(); } @@ -701,6 +702,10 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spanScopeMarkerEnd(); + } + if (ce_task.handle) { ASSERT_MSG(ce_task.handle.done(), "Partially processed CCB"); ce_task.handle.destroy(); @@ -714,6 +719,10 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, u32 vqid) { FIBER_ENTER(acb_task_name[vqid]); const auto& queue = asc_queues[{vqid}]; + if (rasterizer) { + rasterizer->ScopeMarkerBegin(fmt::format("asc[{}]", vqid)); + } + auto base_addr = reinterpret_cast(acb.data()); while (!acb.empty()) { const auto* header = reinterpret_cast(acb.data()); @@ -811,8 +820,7 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, u32 vqid) { } if (rasterizer && (cs_program.dispatch_initiator & 1)) { const auto cmd_address = reinterpret_cast(header); - rasterizer->ScopeMarkerBegin( - fmt::format("acb[{}]:{}:DispatchIndirect", vqid, cmd_address)); + rasterizer->ScopeMarkerBegin(fmt::format("{}:DispatchDirect", cmd_address)); rasterizer->DispatchDirect(); rasterizer->ScopeMarkerEnd(); } @@ -830,7 +838,7 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, u32 vqid) { } if (rasterizer && (cs_program.dispatch_initiator & 1)) { const auto cmd_address = reinterpret_cast(header); - rasterizer->ScopeMarkerBegin(fmt::format("acb[{}]:{}:Dispatch", vqid, cmd_address)); + rasterizer->ScopeMarkerBegin(fmt::format("{}:DispatchIndirect", cmd_address)); rasterizer->DispatchIndirect(ib_address, 0, size); rasterizer->ScopeMarkerEnd(); } @@ -878,6 +886,10 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, u32 vqid) { } } + if (rasterizer) { + rasterizer->ScopeMarkerEnd(); + } + FIBER_EXIT; } diff --git a/src/video_core/buffer_cache/buffer.cpp b/src/video_core/buffer_cache/buffer.cpp index 5a049c185..a8d1271c6 100644 --- a/src/video_core/buffer_cache/buffer.cpp +++ b/src/video_core/buffer_cache/buffer.cpp @@ -131,6 +131,8 @@ vk::BufferView Buffer::View(u32 offset, u32 size, bool is_written, AmdGpu::DataF vk::to_string(view_result)); scheduler->DeferOperation( [view, device = instance->GetDevice()] { device.destroyBufferView(view); }); + Vulkan::SetObjectName(instance->GetDevice(), view, "BufferView {:#x}:{:#x}", cpu_addr + offset, + size); return view; } diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 6c3e066c6..f5bcb54b6 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -92,15 +92,13 @@ std::string GetReadableVersion(u32 version) { Instance::Instance(bool enable_validation, bool enable_crash_diagnostic) : instance{CreateInstance(Frontend::WindowSystemType::Headless, enable_validation, enable_crash_diagnostic)}, - physical_devices{EnumeratePhysicalDevices(instance)}, - crash_diagnostic{enable_crash_diagnostic} {} + physical_devices{EnumeratePhysicalDevices(instance)} {} Instance::Instance(Frontend::WindowSDL& window, s32 physical_device_index, bool enable_validation /*= false*/, bool enable_crash_diagnostic /*= false*/) : instance{CreateInstance(window.GetWindowInfo().type, enable_validation, enable_crash_diagnostic)}, - physical_devices{EnumeratePhysicalDevices(instance)}, - crash_diagnostic{enable_crash_diagnostic} { + physical_devices{EnumeratePhysicalDevices(instance)} { if (enable_validation) { debug_callback = CreateDebugCallback(*instance); } @@ -562,10 +560,7 @@ void Instance::CollectToolingInfo() { return; } for (const vk::PhysicalDeviceToolProperties& tool : tools) { - const std::string_view name = tool.name; - LOG_INFO(Render_Vulkan, "Attached debugging tool: {}", name); - has_renderdoc = has_renderdoc || name == "RenderDoc"; - has_nsight_graphics = has_nsight_graphics || name == "NVIDIA Nsight Graphics"; + LOG_INFO(Render_Vulkan, "Attached debugging tool: {}", tool.name); } } diff --git a/src/video_core/renderer_vulkan/vk_instance.h b/src/video_core/renderer_vulkan/vk_instance.h index 8928b4267..e0d4d0b4d 100644 --- a/src/video_core/renderer_vulkan/vk_instance.h +++ b/src/video_core/renderer_vulkan/vk_instance.h @@ -79,11 +79,6 @@ public: return profiler_context; } - /// Returns true when a known debugging tool is attached. - bool HasDebuggingToolAttached() const { - return crash_diagnostic || has_renderdoc || has_nsight_graphics; - } - /// Returns true if anisotropic filtering is supported bool IsAnisotropicFilteringSupported() const { return features.samplerAnisotropy; @@ -340,13 +335,9 @@ private: bool legacy_vertex_attributes{}; bool image_load_store_lod{}; bool amd_gcn_shader{}; + bool tooling_info{}; u64 min_imported_host_pointer_alignment{}; u32 subgroup_size{}; - bool tooling_info{}; - bool debug_utils_supported{}; - bool crash_diagnostic{}; - bool has_nsight_graphics{}; - bool has_renderdoc{}; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_platform.h b/src/video_core/renderer_vulkan/vk_platform.h index 6b425b6d8..d05d12997 100644 --- a/src/video_core/renderer_vulkan/vk_platform.h +++ b/src/video_core/renderer_vulkan/vk_platform.h @@ -7,6 +7,7 @@ #include #include +#include "common/config.h" #include "common/logging/log.h" #include "common/types.h" #include "video_core/renderer_vulkan/vk_common.h" @@ -32,6 +33,9 @@ concept VulkanHandleType = vk::isVulkanHandleType::value; template void SetObjectName(vk::Device device, const HandleType& handle, std::string_view debug_name) { + if (!Config::vkHostMarkersEnabled()) { + return; + } const vk::DebugUtilsObjectNameInfoEXT name_info = { .objectType = HandleType::objectType, .objectHandle = reinterpret_cast(static_cast(handle)), @@ -46,6 +50,9 @@ void SetObjectName(vk::Device device, const HandleType& handle, std::string_view template void SetObjectName(vk::Device device, const HandleType& handle, const char* format, const Args&... args) { + if (!Config::vkHostMarkersEnabled()) { + return; + } const std::string debug_name = fmt::vformat(format, fmt::make_format_args(args...)); SetObjectName(device, handle, debug_name); } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 06cfbedac..bac647125 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -58,6 +58,7 @@ bool Rasterizer::FilterDraw() { if (regs.color_control.mode == Liverpool::ColorControl::OperationMode::FmaskDecompress) { // TODO: check for a valid MRT1 to promote the draw to the resolve pass. LOG_TRACE(Render_Vulkan, "FMask decompression pass skipped"); + ScopedMarkerInsert("FmaskDecompress"); return false; } if (regs.color_control.mode == Liverpool::ColorControl::OperationMode::Resolve) { @@ -67,6 +68,7 @@ bool Rasterizer::FilterDraw() { } if (regs.primitive_type == AmdGpu::PrimitiveType::None) { LOG_TRACE(Render_Vulkan, "Primitive type 'None' skipped"); + ScopedMarkerInsert("PrimitiveTypeNone"); return false; } @@ -244,10 +246,13 @@ void Rasterizer::EliminateFastClear() { .layerCount = col_buf.view.slice_max - col_buf.view.slice_start + 1, }; scheduler.EndRendering(); + ScopeMarkerBegin(fmt::format("EliminateFastClear:MRT={:#x}:M={:#x}", col_buf.Address(), + col_buf.CmaskAddress())); image.Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite, {}); scheduler.CommandBuffer().clearColorImage(image.image, image.last_state.layout, LiverpoolToVK::ColorBufferClearValue(col_buf).color, range); + ScopeMarkerEnd(); } void Rasterizer::Draw(bool is_indexed, u32 index_offset) { @@ -842,8 +847,6 @@ void Rasterizer::BeginRendering(const GraphicsPipeline& pipeline, RenderState& s } void Rasterizer::Resolve() { - const auto cmdbuf = scheduler.CommandBuffer(); - // Read from MRT0, average all samples, and write to MRT1, which is one-sample const auto& mrt0_hint = liverpool->last_cb_extent[0]; const auto& mrt1_hint = liverpool->last_cb_extent[1]; @@ -863,9 +866,12 @@ void Rasterizer::Resolve() { mrt1_range.base.layer = liverpool->regs.color_buffers[1].view.slice_start; mrt1_range.extent.layers = liverpool->regs.color_buffers[1].NumSlices() - mrt1_range.base.layer; + ScopeMarkerBegin(fmt::format("Resolve:MRT0={:#x}:MRT1={:#x}", + liverpool->regs.color_buffers[0].Address(), + liverpool->regs.color_buffers[1].Address())); + mrt0_image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, mrt0_range); - mrt1_image.Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite, mrt1_range); @@ -892,8 +898,9 @@ void Rasterizer::Resolve() { .dstOffset = {0, 0, 0}, .extent = {mrt1_image.info.size.width, mrt1_image.info.size.height, 1}, }; - cmdbuf.copyImage(mrt0_image.image, vk::ImageLayout::eTransferSrcOptimal, mrt1_image.image, - vk::ImageLayout::eTransferDstOptimal, region); + scheduler.CommandBuffer().copyImage(mrt0_image.image, vk::ImageLayout::eTransferSrcOptimal, + mrt1_image.image, vk::ImageLayout::eTransferDstOptimal, + region); } else { vk::ImageResolve region = { .srcSubresource = @@ -914,9 +921,12 @@ void Rasterizer::Resolve() { .dstOffset = {0, 0, 0}, .extent = {mrt1_image.info.size.width, mrt1_image.info.size.height, 1}, }; - cmdbuf.resolveImage(mrt0_image.image, vk::ImageLayout::eTransferSrcOptimal, - mrt1_image.image, vk::ImageLayout::eTransferDstOptimal, region); + scheduler.CommandBuffer().resolveImage( + mrt0_image.image, vk::ImageLayout::eTransferSrcOptimal, mrt1_image.image, + vk::ImageLayout::eTransferDstOptimal, region); } + + ScopeMarkerEnd(); } void Rasterizer::DepthStencilCopy(bool is_depth, bool is_stencil) { @@ -936,6 +946,11 @@ void Rasterizer::DepthStencilCopy(bool is_depth, bool is_stencil) { sub_range.base.layer = liverpool->regs.depth_view.slice_start; sub_range.extent.layers = liverpool->regs.depth_view.NumSlices() - sub_range.base.layer; + ScopeMarkerBegin(fmt::format( + "DepthStencilCopy:DR={:#x}:SR={:#x}:DW={:#x}:SW={:#x}", regs.depth_buffer.DepthAddress(), + regs.depth_buffer.StencilAddress(), regs.depth_buffer.DepthWriteAddress(), + regs.depth_buffer.StencilWriteAddress())); + read_image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, sub_range); write_image.Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite, @@ -967,9 +982,11 @@ void Rasterizer::DepthStencilCopy(bool is_depth, bool is_stencil) { .dstOffset = {0, 0, 0}, .extent = {write_image.info.size.width, write_image.info.size.height, 1}, }; - const auto cmdbuf = scheduler.CommandBuffer(); - cmdbuf.copyImage(read_image.image, vk::ImageLayout::eTransferSrcOptimal, write_image.image, - vk::ImageLayout::eTransferDstOptimal, region); + scheduler.CommandBuffer().copyImage(read_image.image, vk::ImageLayout::eTransferSrcOptimal, + write_image.image, vk::ImageLayout::eTransferDstOptimal, + region); + + ScopeMarkerEnd(); } void Rasterizer::InlineData(VAddr address, const void* value, u32 num_bytes, bool is_gds) { @@ -1195,42 +1212,43 @@ void Rasterizer::UpdateViewportScissorState() { cmdbuf.setScissorWithCountEXT(scissors); } -void Rasterizer::ScopeMarkerBegin(const std::string_view& str) { - if (Config::nullGpu() || !Config::vkMarkersEnabled()) { +void Rasterizer::ScopeMarkerBegin(const std::string_view& str, bool from_guest) { + if ((from_guest && !Config::vkGuestMarkersEnabled()) || + (!from_guest && !Config::vkHostMarkersEnabled())) { return; } - const auto cmdbuf = scheduler.CommandBuffer(); cmdbuf.beginDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT{ .pLabelName = str.data(), }); } -void Rasterizer::ScopeMarkerEnd() { - if (Config::nullGpu() || !Config::vkMarkersEnabled()) { +void Rasterizer::ScopeMarkerEnd(bool from_guest) { + if ((from_guest && !Config::vkGuestMarkersEnabled()) || + (!from_guest && !Config::vkHostMarkersEnabled())) { return; } - const auto cmdbuf = scheduler.CommandBuffer(); cmdbuf.endDebugUtilsLabelEXT(); } -void Rasterizer::ScopedMarkerInsert(const std::string_view& str) { - if (Config::nullGpu() || !Config::vkMarkersEnabled()) { +void Rasterizer::ScopedMarkerInsert(const std::string_view& str, bool from_guest) { + if ((from_guest && !Config::vkGuestMarkersEnabled()) || + (!from_guest && !Config::vkHostMarkersEnabled())) { return; } - const auto cmdbuf = scheduler.CommandBuffer(); cmdbuf.insertDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT{ .pLabelName = str.data(), }); } -void Rasterizer::ScopedMarkerInsertColor(const std::string_view& str, const u32 color) { - if (Config::nullGpu() || !Config::vkMarkersEnabled()) { +void Rasterizer::ScopedMarkerInsertColor(const std::string_view& str, const u32 color, + bool from_guest) { + if ((from_guest && !Config::vkGuestMarkersEnabled()) || + (!from_guest && !Config::vkHostMarkersEnabled())) { return; } - const auto cmdbuf = scheduler.CommandBuffer(); cmdbuf.insertDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT{ .pLabelName = str.data(), diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 1e4a210bb..abf58e522 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -47,10 +47,11 @@ public: void DispatchDirect(); void DispatchIndirect(VAddr address, u32 offset, u32 size); - void ScopeMarkerBegin(const std::string_view& str); - void ScopeMarkerEnd(); - void ScopedMarkerInsert(const std::string_view& str); - void ScopedMarkerInsertColor(const std::string_view& str, const u32 color); + void ScopeMarkerBegin(const std::string_view& str, bool from_guest = false); + void ScopeMarkerEnd(bool from_guest = false); + void ScopedMarkerInsert(const std::string_view& str, bool from_guest = false); + void ScopedMarkerInsertColor(const std::string_view& str, const u32 color, + bool from_guest = false); void InlineData(VAddr address, const void* value, u32 num_bytes, bool is_gds); u32 ReadDataFromGds(u32 gsd_offset); diff --git a/src/video_core/renderer_vulkan/vk_resource_pool.cpp b/src/video_core/renderer_vulkan/vk_resource_pool.cpp index dba603e71..5eae32e70 100644 --- a/src/video_core/renderer_vulkan/vk_resource_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_resource_pool.cpp @@ -73,9 +73,7 @@ CommandPool::CommandPool(const Instance& instance, MasterSemaphore* master_semap ASSERT_MSG(pool_result == vk::Result::eSuccess, "Failed to create command pool: {}", vk::to_string(pool_result)); cmd_pool = std::move(pool); - if (instance.HasDebuggingToolAttached()) { - SetObjectName(device, *cmd_pool, "CommandPool"); - } + SetObjectName(device, *cmd_pool, "CommandPool"); } CommandPool::~CommandPool() = default; @@ -94,10 +92,8 @@ void CommandPool::Allocate(std::size_t begin, std::size_t end) { device.allocateCommandBuffers(&buffer_alloc_info, cmd_buffers.data() + begin); ASSERT(result == vk::Result::eSuccess); - if (instance.HasDebuggingToolAttached()) { - for (std::size_t i = begin; i < end; ++i) { - SetObjectName(device, cmd_buffers[i], "CommandPool: Command Buffer {}", i); - } + for (std::size_t i = begin; i < end; ++i) { + SetObjectName(device, cmd_buffers[i], "CommandPool: Command Buffer {}", i); } } diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index 44f4be6dd..8278252ad 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -4,6 +4,7 @@ #include #include #include "common/assert.h" +#include "common/config.h" #include "common/logging/log.h" #include "sdl_window.h" #include "video_core/renderer_vulkan/vk_instance.h" @@ -235,11 +236,9 @@ void Swapchain::RefreshSemaphores() { semaphore = sem; } - if (instance.HasDebuggingToolAttached()) { - for (u32 i = 0; i < image_count; ++i) { - SetObjectName(device, image_acquired[i], "Swapchain Semaphore: image_acquired {}", i); - SetObjectName(device, present_ready[i], "Swapchain Semaphore: present_ready {}", i); - } + for (u32 i = 0; i < image_count; ++i) { + SetObjectName(device, image_acquired[i], "Swapchain Semaphore: image_acquired {}", i); + SetObjectName(device, present_ready[i], "Swapchain Semaphore: present_ready {}", i); } } @@ -251,10 +250,8 @@ void Swapchain::SetupImages() { images = std::move(imgs); image_count = static_cast(images.size()); - if (instance.HasDebuggingToolAttached()) { - for (u32 i = 0; i < image_count; ++i) { - SetObjectName(device, images[i], "Swapchain Image {}", i); - } + for (u32 i = 0; i < image_count; ++i) { + SetObjectName(device, images[i], "Swapchain Image {}", i); } } diff --git a/src/video_core/texture_cache/image_view.cpp b/src/video_core/texture_cache/image_view.cpp index e935c7d2e..6b1349386 100644 --- a/src/video_core/texture_cache/image_view.cpp +++ b/src/video_core/texture_cache/image_view.cpp @@ -110,6 +110,13 @@ ImageView::ImageView(const Vulkan::Instance& instance, const ImageViewInfo& info ASSERT_MSG(view_result == vk::Result::eSuccess, "Failed to create image view: {}", vk::to_string(view_result)); image_view = std::move(view); + + const auto view_aspect = aspect & vk::ImageAspectFlagBits::eDepth ? "Depth" + : aspect & vk::ImageAspectFlagBits::eStencil ? "Stencil" + : "Color"; + Vulkan::SetObjectName(instance.GetDevice(), *image_view, "ImageView {}x{}x{} {:#x}:{:#x} ({})", + image.info.size.width, image.info.size.height, image.info.size.depth, + image.info.guest_address, image.info.guest_size, view_aspect); } ImageView::~ImageView() = default; From da2b58f66edd38b4e3cc851f6b26cc61f34736a9 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Thu, 16 Jan 2025 02:36:41 -0800 Subject: [PATCH 412/549] resource_tracking_pass: Persist image resource atomic designation. (#2158) --- src/shader_recompiler/ir/passes/resource_tracking_pass.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index 10d685ed1..a132cac2c 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -164,6 +164,7 @@ public: return desc.sharp_idx == existing.sharp_idx && desc.is_array == existing.is_array; })}; auto& image = image_resources[index]; + image.is_atomic |= desc.is_atomic; image.is_written |= desc.is_written; return index; } From 34a5f2319cb7e3e201239e25b3562ea0781760c9 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Thu, 16 Jan 2025 03:17:07 -0800 Subject: [PATCH 413/549] network: Remove firing Np callbacks from check stubs. (#2161) --- src/core/libraries/network/netctl.cpp | 4 ++-- src/core/libraries/np_manager/np_manager.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/libraries/network/netctl.cpp b/src/core/libraries/network/netctl.cpp index b167d2789..00d980663 100644 --- a/src/core/libraries/network/netctl.cpp +++ b/src/core/libraries/network/netctl.cpp @@ -93,7 +93,7 @@ int PS4_SYSV_ABI sceNetCtlUnregisterCallbackV6() { } int PS4_SYSV_ABI sceNetCtlCheckCallback() { - netctl.CheckCallback(); + LOG_DEBUG(Lib_NetCtl, "(STUBBED) called"); return ORBIS_OK; } @@ -373,7 +373,7 @@ int PS4_SYSV_ABI Func_D8DCB6973537A3DC() { } int PS4_SYSV_ABI sceNetCtlCheckCallbackForNpToolkit() { - netctl.CheckNpToolkitCallback(); + LOG_DEBUG(Lib_NetCtl, "(STUBBED) called"); return ORBIS_OK; } diff --git a/src/core/libraries/np_manager/np_manager.cpp b/src/core/libraries/np_manager/np_manager.cpp index 3489e3e41..e26c5a830 100644 --- a/src/core/libraries/np_manager/np_manager.cpp +++ b/src/core/libraries/np_manager/np_manager.cpp @@ -2509,7 +2509,7 @@ struct NpStateCallbackForNpToolkit { NpStateCallbackForNpToolkit NpStateCbForNp; int PS4_SYSV_ABI sceNpCheckCallbackForLib() { - Core::ExecuteGuest(NpStateCbForNp.func, 1, OrbisNpState::SignedOut, NpStateCbForNp.userdata); + LOG_DEBUG(Lib_NpManager, "(STUBBED) called"); return ORBIS_OK; } From 4695aaa8306158e94df8292106bdc3889976dd1e Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Thu, 16 Jan 2025 18:27:52 +0200 Subject: [PATCH 414/549] sceKernelAio* implementation (#2160) * draft Aio from https://github.com/GoldHEN/GoldHEN_Plugins_Repository * cleanup and fixes to Aio --- CMakeLists.txt | 2 + src/core/libraries/kernel/aio.cpp | 339 ++++++++++++++++++++++++ src/core/libraries/kernel/aio.h | 43 +++ src/core/libraries/kernel/file_system.h | 3 +- src/core/libraries/kernel/kernel.cpp | 2 + src/core/libraries/kernel/time.h | 1 + 6 files changed, 389 insertions(+), 1 deletion(-) create mode 100644 src/core/libraries/kernel/aio.cpp create mode 100644 src/core/libraries/kernel/aio.h diff --git a/CMakeLists.txt b/CMakeLists.txt index be87de119..30cb033ed 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -250,6 +250,8 @@ set(KERNEL_LIB src/core/libraries/kernel/sync/mutex.cpp src/core/libraries/kernel/time.h src/core/libraries/kernel/orbis_error.h src/core/libraries/kernel/posix_error.h + src/core/libraries/kernel/aio.cpp + src/core/libraries/kernel/aio.h ) set(NETWORK_LIBS src/core/libraries/network/http.cpp diff --git a/src/core/libraries/kernel/aio.cpp b/src/core/libraries/kernel/aio.cpp new file mode 100644 index 000000000..e017010cb --- /dev/null +++ b/src/core/libraries/kernel/aio.cpp @@ -0,0 +1,339 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include + +#include "aio.h" +#include "common/assert.h" +#include "common/debug.h" +#include "common/logging/log.h" +#include "core/libraries/kernel/equeue.h" +#include "core/libraries/kernel/orbis_error.h" +#include "core/libraries/libs.h" +#include "file_system.h" + +namespace Libraries::Kernel { + +#define MAX_QUEUE 512 + +static s32* id_state; +static s32 id_index; + +s32 sceKernelAioInitializeImpl(void* p, s32 size) { + + return 0; +} + +s32 PS4_SYSV_ABI sceKernelAioDeleteRequest(OrbisKernelAioSubmitId id, s32* ret) { + if (ret == nullptr) { + return ORBIS_KERNEL_ERROR_EFAULT; + } + id_state[id] = ORBIS_KERNEL_AIO_STATE_ABORTED; + *ret = 0; + return 0; +} + +s32 PS4_SYSV_ABI sceKernelAioDeleteRequests(OrbisKernelAioSubmitId id[], s32 num, s32 ret[]) { + if (ret == nullptr) { + return ORBIS_KERNEL_ERROR_EFAULT; + } + for (s32 i = 0; i < num; i++) { + id_state[id[i]] = ORBIS_KERNEL_AIO_STATE_ABORTED; + ret[i] = 0; + } + + return 0; +} +s32 PS4_SYSV_ABI sceKernelAioPollRequest(OrbisKernelAioSubmitId id, s32* state) { + if (state == nullptr) { + return ORBIS_KERNEL_ERROR_EFAULT; + } + *state = id_state[id]; + return 0; +} + +s32 PS4_SYSV_ABI sceKernelAioPollRequests(OrbisKernelAioSubmitId id[], s32 num, s32 state[]) { + if (state == nullptr) { + return ORBIS_KERNEL_ERROR_EFAULT; + } + for (s32 i = 0; i < num; i++) { + state[i] = id_state[id[i]]; + } + + return 0; +} + +s32 PS4_SYSV_ABI sceKernelAioCancelRequest(OrbisKernelAioSubmitId id, s32* state) { + if (state == nullptr) { + return ORBIS_KERNEL_ERROR_EFAULT; + } + if (id) { + id_state[id] = ORBIS_KERNEL_AIO_STATE_ABORTED; + *state = ORBIS_KERNEL_AIO_STATE_ABORTED; + } else { + *state = ORBIS_KERNEL_AIO_STATE_PROCESSING; + } + return 0; +} + +s32 PS4_SYSV_ABI sceKernelAioCancelRequests(OrbisKernelAioSubmitId id[], s32 num, s32 state[]) { + if (state == nullptr) { + return ORBIS_KERNEL_ERROR_EFAULT; + } + for (s32 i = 0; i < num; i++) { + if (id[i]) { + id_state[id[i]] = ORBIS_KERNEL_AIO_STATE_ABORTED; + state[i] = ORBIS_KERNEL_AIO_STATE_ABORTED; + } else { + state[i] = ORBIS_KERNEL_AIO_STATE_PROCESSING; + } + } + + return 0; +} + +s32 PS4_SYSV_ABI sceKernelAioWaitRequest(OrbisKernelAioSubmitId id, s32* state, u32* usec) { + if (state == nullptr) { + return ORBIS_KERNEL_ERROR_EFAULT; + } + u32 timer = 0; + + s32 timeout = 0; + + while (id_state[id] == ORBIS_KERNEL_AIO_STATE_PROCESSING) { + sceKernelUsleep(10); + + timer += 10; + if (*usec) { + if (timer > *usec) { + timeout = 1; + break; + } + } + } + + *state = id_state[id]; + + if (timeout) + return ORBIS_KERNEL_ERROR_ETIMEDOUT; + return 0; +} + +s32 PS4_SYSV_ABI sceKernelAioWaitRequests(OrbisKernelAioSubmitId id[], s32 num, s32 state[], + u32 mode, u32* usec) { + if (state == nullptr) { + return ORBIS_KERNEL_ERROR_EFAULT; + } + u32 timer = 0; + s32 timeout = 0; + s32 completion = 0; + + for (s32 i = 0; i < num; i++) { + if (!completion && !timeout) { + while (id_state[id[i]] == ORBIS_KERNEL_AIO_STATE_PROCESSING) { + sceKernelUsleep(10); + timer += 10; + + if (*usec) { + if (timer > *usec) { + timeout = 1; + break; + } + } + } + } + + if (mode == 0x02) { + if (id_state[id[i]] == ORBIS_KERNEL_AIO_STATE_COMPLETED) + completion = 1; + } + + state[i] = id_state[id[i]]; + } + + if (timeout) + return ORBIS_KERNEL_ERROR_ETIMEDOUT; + + return 0; +} + +s32 PS4_SYSV_ABI sceKernelAioSubmitReadCommands(OrbisKernelAioRWRequest req[], s32 size, s32 prio, + OrbisKernelAioSubmitId* id) { + if (req == nullptr) { + return ORBIS_KERNEL_ERROR_EFAULT; + } + if (id == nullptr) { + return ORBIS_KERNEL_ERROR_EFAULT; + } + id_state[id_index] = ORBIS_KERNEL_AIO_STATE_PROCESSING; + + for (s32 i = 0; i < size; i++) { + + s64 ret = sceKernelPread(req[i].fd, req[i].buf, req[i].nbyte, req[i].offset); + + if (ret < 0) { + req[i].result->state = ORBIS_KERNEL_AIO_STATE_ABORTED; + req[i].result->returnValue = ret; + + } else { + req[i].result->state = ORBIS_KERNEL_AIO_STATE_COMPLETED; + req[i].result->returnValue = ret; + } + } + + id_state[id_index] = ORBIS_KERNEL_AIO_STATE_COMPLETED; + + *id = id_index; + + id_index = (id_index + 1) % MAX_QUEUE; + + if (!id_index) + id_index++; + + return 0; +} + +s32 PS4_SYSV_ABI sceKernelAioSubmitReadCommandsMultiple(OrbisKernelAioRWRequest req[], s32 size, + s32 prio, OrbisKernelAioSubmitId id[]) { + if (req == nullptr) { + return ORBIS_KERNEL_ERROR_EFAULT; + } + if (id == nullptr) { + return ORBIS_KERNEL_ERROR_EFAULT; + } + for (s32 i = 0; i < size; i++) { + id_state[id_index] = ORBIS_KERNEL_AIO_STATE_PROCESSING; + + s64 ret = sceKernelPread(req[i].fd, req[i].buf, req[i].nbyte, req[i].offset); + + if (ret < 0) { + req[i].result->state = ORBIS_KERNEL_AIO_STATE_ABORTED; + req[i].result->returnValue = ret; + + id_state[id_index] = ORBIS_KERNEL_AIO_STATE_ABORTED; + + } else { + req[i].result->state = ORBIS_KERNEL_AIO_STATE_COMPLETED; + req[i].result->returnValue = ret; + + id_state[id_index] = ORBIS_KERNEL_AIO_STATE_COMPLETED; + } + + id[i] = id_index; + + id_index = (id_index + 1) % MAX_QUEUE; + + if (!id_index) + id_index++; + } + + return 0; +} + +s32 PS4_SYSV_ABI sceKernelAioSubmitWriteCommands(OrbisKernelAioRWRequest req[], s32 size, s32 prio, + OrbisKernelAioSubmitId* id) { + if (req == nullptr) { + return ORBIS_KERNEL_ERROR_EFAULT; + } + if (id == nullptr) { + return ORBIS_KERNEL_ERROR_EFAULT; + } + for (s32 i = 0; i < size; i++) { + id_state[id_index] = ORBIS_KERNEL_AIO_STATE_PROCESSING; + + s64 ret = sceKernelPwrite(req[i].fd, req[i].buf, req[i].nbyte, req[i].offset); + + if (ret < 0) { + req[i].result->state = ORBIS_KERNEL_AIO_STATE_ABORTED; + req[i].result->returnValue = ret; + + id_state[id_index] = ORBIS_KERNEL_AIO_STATE_ABORTED; + + } else { + req[i].result->state = ORBIS_KERNEL_AIO_STATE_COMPLETED; + req[i].result->returnValue = ret; + + id_state[id_index] = ORBIS_KERNEL_AIO_STATE_COMPLETED; + } + } + + *id = id_index; + + id_index = (id_index + 1) % MAX_QUEUE; + + // skip id_index equals 0 , because sceKernelAioCancelRequest will submit id + // equal to 0 + if (!id_index) + id_index++; + + return 0; +} + +s32 PS4_SYSV_ABI sceKernelAioSubmitWriteCommandsMultiple(OrbisKernelAioRWRequest req[], s32 size, + s32 prio, OrbisKernelAioSubmitId id[]) { + if (req == nullptr) { + return ORBIS_KERNEL_ERROR_EFAULT; + } + if (id == nullptr) { + return ORBIS_KERNEL_ERROR_EFAULT; + } + for (s32 i = 0; i < size; i++) { + id_state[id_index] = ORBIS_KERNEL_AIO_STATE_PROCESSING; + s64 ret = sceKernelPwrite(req[i].fd, req[i].buf, req[i].nbyte, req[i].offset); + + if (ret < 0) { + req[i].result->state = ORBIS_KERNEL_AIO_STATE_ABORTED; + req[i].result->returnValue = ret; + + id_state[id_index] = ORBIS_KERNEL_AIO_STATE_ABORTED; + + } else { + req[i].result->state = ORBIS_KERNEL_AIO_STATE_COMPLETED; + req[i].result->returnValue = ret; + id_state[id_index] = ORBIS_KERNEL_AIO_STATE_COMPLETED; + } + + id[i] = id_index; + id_index = (id_index + 1) % MAX_QUEUE; + + if (!id_index) + id_index++; + } + return 0; +} + +s32 PS4_SYSV_ABI sceKernelAioSetParam() { + LOG_ERROR(Kernel, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceKernelAioInitializeParam() { + LOG_ERROR(Kernel, "(STUBBED) called"); + return ORBIS_OK; +} + +void RegisterAio(Core::Loader::SymbolsResolver* sym) { + id_index = 1; + id_state = (int*)malloc(sizeof(int) * MAX_QUEUE); + memset(id_state, 0, sizeof(sizeof(int) * MAX_QUEUE)); + + LIB_FUNCTION("fR521KIGgb8", "libkernel", 1, "libkernel", 1, 1, sceKernelAioCancelRequest); + LIB_FUNCTION("3Lca1XBrQdY", "libkernel", 1, "libkernel", 1, 1, sceKernelAioCancelRequests); + LIB_FUNCTION("5TgME6AYty4", "libkernel", 1, "libkernel", 1, 1, sceKernelAioDeleteRequest); + LIB_FUNCTION("Ft3EtsZzAoY", "libkernel", 1, "libkernel", 1, 1, sceKernelAioDeleteRequests); + LIB_FUNCTION("vYU8P9Td2Zo", "libkernel", 1, "libkernel", 1, 1, sceKernelAioInitializeImpl); + LIB_FUNCTION("nu4a0-arQis", "libkernel", 1, "libkernel", 1, 1, sceKernelAioInitializeParam); + LIB_FUNCTION("2pOuoWoCxdk", "libkernel", 1, "libkernel", 1, 1, sceKernelAioPollRequest); + LIB_FUNCTION("o7O4z3jwKzo", "libkernel", 1, "libkernel", 1, 1, sceKernelAioPollRequests); + LIB_FUNCTION("9WK-vhNXimw", "libkernel", 1, "libkernel", 1, 1, sceKernelAioSetParam); + LIB_FUNCTION("HgX7+AORI58", "libkernel", 1, "libkernel", 1, 1, sceKernelAioSubmitReadCommands); + LIB_FUNCTION("lXT0m3P-vs4", "libkernel", 1, "libkernel", 1, 1, + sceKernelAioSubmitReadCommandsMultiple); + LIB_FUNCTION("XQ8C8y+de+E", "libkernel", 1, "libkernel", 1, 1, sceKernelAioSubmitWriteCommands); + LIB_FUNCTION("xT3Cpz0yh6Y", "libkernel", 1, "libkernel", 1, 1, + sceKernelAioSubmitWriteCommandsMultiple); + LIB_FUNCTION("KOF-oJbQVvc", "libkernel", 1, "libkernel", 1, 1, sceKernelAioWaitRequest); + LIB_FUNCTION("lgK+oIWkJyA", "libkernel", 1, "libkernel", 1, 1, sceKernelAioWaitRequests); +} + +} // namespace Libraries::Kernel \ No newline at end of file diff --git a/src/core/libraries/kernel/aio.h b/src/core/libraries/kernel/aio.h new file mode 100644 index 000000000..0ad21e938 --- /dev/null +++ b/src/core/libraries/kernel/aio.h @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include + +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::Kernel { + +enum AioState { + ORBIS_KERNEL_AIO_STATE_SUBMITTED = 1, + ORBIS_KERNEL_AIO_STATE_PROCESSING = 2, + ORBIS_KERNEL_AIO_STATE_COMPLETED = 3, + ORBIS_KERNEL_AIO_STATE_ABORTED = 4 +}; + +struct OrbisKernelAioResult { + s64 returnValue; + u32 state; +}; + +typedef s32 OrbisKernelAioSubmitId; + +struct OrbisKernelAioRWRequest { + s64 offset; + s64 nbyte; + void* buf; + OrbisKernelAioResult* result; + s32 fd; +}; + +void RegisterAio(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::Kernel \ No newline at end of file diff --git a/src/core/libraries/kernel/file_system.h b/src/core/libraries/kernel/file_system.h index 6443962ff..1838df2fe 100644 --- a/src/core/libraries/kernel/file_system.h +++ b/src/core/libraries/kernel/file_system.h @@ -67,7 +67,8 @@ constexpr int ORBIS_KERNEL_O_DIRECTORY = 0x00020000; s64 PS4_SYSV_ABI sceKernelWrite(int d, const void* buf, size_t nbytes); s64 PS4_SYSV_ABI sceKernelRead(int d, void* buf, size_t nbytes); - +s64 PS4_SYSV_ABI sceKernelPread(int d, void* buf, size_t nbytes, s64 offset); +s64 PS4_SYSV_ABI sceKernelPwrite(int d, void* buf, size_t nbytes, s64 offset); void RegisterFileSystem(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/kernel.cpp b/src/core/libraries/kernel/kernel.cpp index b05c96fad..a9d04ca38 100644 --- a/src/core/libraries/kernel/kernel.cpp +++ b/src/core/libraries/kernel/kernel.cpp @@ -28,6 +28,7 @@ #include #endif #include +#include "aio.h" namespace Libraries::Kernel { @@ -218,6 +219,7 @@ void RegisterKernel(Core::Loader::SymbolsResolver* sym) { Libraries::Kernel::RegisterEventQueue(sym); Libraries::Kernel::RegisterProcess(sym); Libraries::Kernel::RegisterException(sym); + Libraries::Kernel::RegisterAio(sym); LIB_OBJ("f7uOxY9mM1U", "libkernel", 1, "libkernel", 1, 1, &g_stack_chk_guard); LIB_FUNCTION("PfccT7qURYE", "libkernel", 1, "libkernel", 1, 1, kernel_ioctl); diff --git a/src/core/libraries/kernel/time.h b/src/core/libraries/kernel/time.h index 6aa281aaf..407b6f9ed 100644 --- a/src/core/libraries/kernel/time.h +++ b/src/core/libraries/kernel/time.h @@ -82,6 +82,7 @@ int PS4_SYSV_ABI sceKernelConvertLocaltimeToUtc(time_t param_1, int64_t param_2, int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time, OrbisTimesec* st, u64* dst_sec); +int PS4_SYSV_ABI sceKernelUsleep(u32 microseconds); void RegisterTime(Core::Loader::SymbolsResolver* sym); From 440a693fae49468ea1b46d4afb548e5697f946de Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Thu, 16 Jan 2025 11:22:39 -0600 Subject: [PATCH 415/549] Crash on sceKernelDebugRaiseExceptionOnReleaseMode (#2163) --- src/core/libraries/kernel/threads/exception.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/core/libraries/kernel/threads/exception.cpp b/src/core/libraries/kernel/threads/exception.cpp index cc391e928..5e2f35d69 100644 --- a/src/core/libraries/kernel/threads/exception.cpp +++ b/src/core/libraries/kernel/threads/exception.cpp @@ -153,6 +153,11 @@ int PS4_SYSV_ABI sceKernelDebugRaiseException() { return 0; } +int PS4_SYSV_ABI sceKernelDebugRaiseExceptionOnReleaseMode() { + UNREACHABLE(); + return 0; +} + void RegisterException(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("il03nluKfMk", "libkernel_unity", 1, "libkernel", 1, 1, sceKernelRaiseException); LIB_FUNCTION("WkwEd3N7w0Y", "libkernel_unity", 1, "libkernel", 1, 1, @@ -160,6 +165,8 @@ void RegisterException(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("Qhv5ARAoOEc", "libkernel_unity", 1, "libkernel", 1, 1, sceKernelRemoveExceptionHandler) LIB_FUNCTION("OMDRKKAZ8I4", "libkernel", 1, "libkernel", 1, 1, sceKernelDebugRaiseException); + LIB_FUNCTION("zE-wXIZjLoM", "libkernel", 1, "libkernel", 1, 1, + sceKernelDebugRaiseExceptionOnReleaseMode); } } // namespace Libraries::Kernel From 56a6c95730a89aec7056a12603ae63a01a8b4a8f Mon Sep 17 00:00:00 2001 From: Vinicius Rangel Date: Thu, 16 Jan 2025 16:27:23 -0300 Subject: [PATCH 416/549] Render without rendering (#2152) * presenter: render the game inside a ImGui window * presenter: render the previous frame to keep the render rendering * swapchain: fix swapchain image view format not being converted to unorm * devtools: fix frame graph timing --- src/core/debug_state.h | 9 + src/core/devtools/layer.cpp | 15 +- src/core/devtools/widget/frame_graph.cpp | 6 +- src/core/devtools/widget/frame_graph.h | 3 + src/core/libraries/videoout/driver.cpp | 20 ++- src/core/libraries/videoout/driver.h | 3 +- src/imgui/imgui_config.h | 6 + src/imgui/renderer/imgui_core.cpp | 22 ++- src/imgui/renderer/imgui_core.h | 7 +- src/imgui/renderer/imgui_impl_sdl3.cpp | 23 ++- src/imgui/renderer/imgui_impl_sdl3.h | 2 +- src/imgui/renderer/imgui_impl_vulkan.cpp | 48 +++--- src/imgui/renderer/imgui_impl_vulkan.h | 18 +- src/imgui/renderer/texture_manager.cpp | 5 +- .../renderer_vulkan/vk_instance.cpp | 1 + .../renderer_vulkan/vk_presenter.cpp | 161 +++++++++++++----- src/video_core/renderer_vulkan/vk_presenter.h | 7 +- .../renderer_vulkan/vk_swapchain.cpp | 42 ++++- src/video_core/renderer_vulkan/vk_swapchain.h | 18 +- 19 files changed, 306 insertions(+), 110 deletions(-) diff --git a/src/core/debug_state.h b/src/core/debug_state.h index 6a8e15baa..aab741fd0 100644 --- a/src/core/debug_state.h +++ b/src/core/debug_state.h @@ -131,6 +131,8 @@ class DebugStateImpl { friend class Core::Devtools::Widget::FrameGraph; friend class Core::Devtools::Widget::ShaderList; + bool showing_debug_menu_bar = false; + std::queue debug_message_popup; std::mutex guest_threads_mutex{}; @@ -153,6 +155,9 @@ class DebugStateImpl { std::vector shader_dump_list{}; public: + float Framerate = 1.0f / 60.0f; + float FrameDeltaTime; + void ShowDebugMessage(std::string message) { if (message.empty()) { return; @@ -160,6 +165,10 @@ public: debug_message_popup.push(std::move(message)); } + bool& ShowingDebugMenuBar() { + return showing_debug_menu_bar; + } + void AddCurrentThreadToGuestList(); void RemoveCurrentThreadFromGuestList(); diff --git a/src/core/devtools/layer.cpp b/src/core/devtools/layer.cpp index 776f3377d..11990a56f 100644 --- a/src/core/devtools/layer.cpp +++ b/src/core/devtools/layer.cpp @@ -28,7 +28,6 @@ static bool show_simple_fps = false; static bool visibility_toggled = false; static float fps_scale = 1.0f; -static bool show_advanced_debug = false; static int dump_frame_count = 1; static Widget::FrameGraph frame_graph; @@ -253,8 +252,8 @@ void L::DrawAdvanced() { } void L::DrawSimple() { - const auto io = GetIO(); - Text("%.1f FPS (%.2f ms)", io.Framerate, 1000.0f / io.Framerate); + const float frameRate = DebugState.Framerate; + Text("%d FPS (%.1f ms)", static_cast(std::round(1.0f / frameRate)), frameRate * 1000.0f); } static void LoadSettings(const char* line) { @@ -265,7 +264,7 @@ static void LoadSettings(const char* line) { return; } if (sscanf(line, "show_advanced_debug=%d", &i) == 1) { - show_advanced_debug = i != 0; + DebugState.ShowingDebugMenuBar() = i != 0; return; } if (sscanf(line, "show_frame_graph=%d", &i) == 1) { @@ -310,7 +309,7 @@ void L::SetupSettings() { handler.WriteAllFn = [](ImGuiContext*, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) { buf->appendf("[%s][Data]\n", handler->TypeName); buf->appendf("fps_scale=%f\n", fps_scale); - buf->appendf("show_advanced_debug=%d\n", show_advanced_debug); + buf->appendf("show_advanced_debug=%d\n", DebugState.ShowingDebugMenuBar()); buf->appendf("show_frame_graph=%d\n", frame_graph.is_open); buf->appendf("dump_frame_count=%d\n", dump_frame_count); buf->append("\n"); @@ -336,12 +335,12 @@ void L::Draw() { if (!DebugState.IsGuestThreadsPaused()) { const auto fn = DebugState.flip_frame_count.load(); - frame_graph.AddFrame(fn, io.DeltaTime); + frame_graph.AddFrame(fn, DebugState.FrameDeltaTime); } if (IsKeyPressed(ImGuiKey_F10, false)) { if (io.KeyCtrl) { - show_advanced_debug = !show_advanced_debug; + DebugState.ShowingDebugMenuBar() ^= true; } else { show_simple_fps = !show_simple_fps; } @@ -376,7 +375,7 @@ void L::Draw() { End(); } - if (show_advanced_debug) { + if (DebugState.ShowingDebugMenuBar()) { PushFont(io.Fonts->Fonts[IMGUI_FONT_MONO]); PushID("DevtoolsLayer"); DrawAdvanced(); diff --git a/src/core/devtools/widget/frame_graph.cpp b/src/core/devtools/widget/frame_graph.cpp index 0e170db38..d93de571a 100644 --- a/src/core/devtools/widget/frame_graph.cpp +++ b/src/core/devtools/widget/frame_graph.cpp @@ -83,15 +83,13 @@ void FrameGraph::Draw() { auto isSystemPaused = DebugState.IsGuestThreadsPaused(); - static float deltaTime; - static float frameRate; - if (!isSystemPaused) { - deltaTime = io.DeltaTime * 1000.0f; + deltaTime = DebugState.FrameDeltaTime * 1000.0f; frameRate = 1000.0f / deltaTime; } Text("Frame time: %.3f ms (%.1f FPS)", deltaTime, frameRate); + Text("Presenter time: %.3f ms (%.1f FPS)", io.DeltaTime * 1000.0f, 1.0f / io.DeltaTime); Text("Flip frame: %d Gnm submit frame: %d", DebugState.flip_frame_count.load(), DebugState.gnm_frame_count.load()); diff --git a/src/core/devtools/widget/frame_graph.h b/src/core/devtools/widget/frame_graph.h index 40a68ffa7..aef3c0747 100644 --- a/src/core/devtools/widget/frame_graph.h +++ b/src/core/devtools/widget/frame_graph.h @@ -16,6 +16,9 @@ class FrameGraph { std::array frame_list{}; + float deltaTime{}; + float frameRate{}; + void DrawFrameGraph(); public: diff --git a/src/core/libraries/videoout/driver.cpp b/src/core/libraries/videoout/driver.cpp index f6c25afe3..29948f242 100644 --- a/src/core/libraries/videoout/driver.cpp +++ b/src/core/libraries/videoout/driver.cpp @@ -1,8 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include - #include "common/assert.h" #include "common/config.h" #include "common/debug.h" @@ -207,6 +205,13 @@ void VideoOutDriver::DrawBlankFrame() { presenter->Present(empty_frame); } +void VideoOutDriver::DrawLastFrame() { + const auto frame = presenter->PrepareLastFrame(); + if (frame != nullptr) { + presenter->Present(frame, true); + } +} + bool VideoOutDriver::SubmitFlip(VideoOutPort* port, s32 index, s64 flip_arg, bool is_eop /*= false*/) { { @@ -278,17 +283,24 @@ void VideoOutDriver::PresentThread(std::stop_token token) { return {}; }; - auto delay = std::chrono::microseconds{0}; while (!token.stop_requested()) { timer.Start(); + if (DebugState.IsGuestThreadsPaused()) { + DrawLastFrame(); + timer.End(); + continue; + } + // Check if it's time to take a request. auto& vblank_status = main_port.vblank_status; if (vblank_status.count % (main_port.flip_rate + 1) == 0) { const auto request = receive_request(); if (!request) { - if (!main_port.is_open || DebugState.IsGuestThreadsPaused()) { + if (!main_port.is_open) { DrawBlankFrame(); + } else { + DrawLastFrame(); } } else { Flip(request); diff --git a/src/core/libraries/videoout/driver.h b/src/core/libraries/videoout/driver.h index ec01b621f..ad7c7bec2 100644 --- a/src/core/libraries/videoout/driver.h +++ b/src/core/libraries/videoout/driver.h @@ -102,7 +102,8 @@ private: }; void Flip(const Request& req); - void DrawBlankFrame(); // Used when there is no flip request to keep ImGui up to date + void DrawBlankFrame(); // Video port out not open + void DrawLastFrame(); // Used when there is no flip request void SubmitFlipInternal(VideoOutPort* port, s32 index, s64 flip_arg, bool is_eop = false); void PresentThread(std::stop_token token); diff --git a/src/imgui/imgui_config.h b/src/imgui/imgui_config.h index ccb084d94..7b03a4bab 100644 --- a/src/imgui/imgui_config.h +++ b/src/imgui/imgui_config.h @@ -30,6 +30,12 @@ extern void assert_fail_debug_msg(const char* msg); #define IM_VEC4_CLASS_EXTRA \ constexpr ImVec4(float _v) : x(_v), y(_v), z(_v), w(_v) {} +namespace ImGui { +struct Texture; +} +#define ImTextureID ImTextureID +using ImTextureID = ::ImGui::Texture*; + #ifdef IMGUI_USE_WCHAR32 #error "This project uses 16 bits wchar standard like Orbis" #endif \ No newline at end of file diff --git a/src/imgui/renderer/imgui_core.cpp b/src/imgui/renderer/imgui_core.cpp index 335185473..b63f50340 100644 --- a/src/imgui/renderer/imgui_core.cpp +++ b/src/imgui/renderer/imgui_core.cpp @@ -6,6 +6,7 @@ #include "common/config.h" #include "common/path_util.h" +#include "core/debug_state.h" #include "core/devtools/layer.h" #include "imgui/imgui_layer.h" #include "imgui_core.h" @@ -167,7 +168,7 @@ bool ProcessEvent(SDL_Event* event) { } } -void NewFrame() { +ImGuiID NewFrame(bool is_reusing_frame) { { std::scoped_lock lock{change_layers_mutex}; while (!change_layers.empty()) { @@ -182,17 +183,24 @@ void NewFrame() { } } - Sdl::NewFrame(); + Sdl::NewFrame(is_reusing_frame); ImGui::NewFrame(); - DockSpaceOverViewport(0, GetMainViewport(), ImGuiDockNodeFlags_PassthruCentralNode); + ImGuiWindowFlags flags = ImGuiDockNodeFlags_PassthruCentralNode; + if (!DebugState.ShowingDebugMenuBar()) { + flags |= ImGuiDockNodeFlags_NoTabBar; + } + ImGuiID dockId = DockSpaceOverViewport(0, GetMainViewport(), flags); for (auto* layer : layers) { layer->Draw(); } + + return dockId; } -void Render(const vk::CommandBuffer& cmdbuf, ::Vulkan::Frame* frame) { +void Render(const vk::CommandBuffer& cmdbuf, const vk::ImageView& image_view, + const vk::Extent2D& extent) { ImGui::Render(); ImDrawData* draw_data = GetDrawData(); if (draw_data->CmdListsCount == 0) { @@ -207,16 +215,16 @@ void Render(const vk::CommandBuffer& cmdbuf, ::Vulkan::Frame* frame) { vk::RenderingAttachmentInfo color_attachments[1]{ { - .imageView = frame->image_view, + .imageView = image_view, .imageLayout = vk::ImageLayout::eColorAttachmentOptimal, - .loadOp = vk::AttachmentLoadOp::eLoad, + .loadOp = vk::AttachmentLoadOp::eClear, .storeOp = vk::AttachmentStoreOp::eStore, }, }; vk::RenderingInfo render_info{}; render_info.renderArea = vk::Rect2D{ .offset = {0, 0}, - .extent = {frame->width, frame->height}, + .extent = extent, }; render_info.layerCount = 1; render_info.colorAttachmentCount = 1; diff --git a/src/imgui/renderer/imgui_core.h b/src/imgui/renderer/imgui_core.h index 9ad708f81..7d5279bd6 100644 --- a/src/imgui/renderer/imgui_core.h +++ b/src/imgui/renderer/imgui_core.h @@ -3,6 +3,8 @@ #pragma once +#include + #include "video_core/renderer_vulkan/vk_instance.h" #include "vulkan/vulkan_handles.hpp" @@ -24,8 +26,9 @@ void Shutdown(const vk::Device& device); bool ProcessEvent(SDL_Event* event); -void NewFrame(); +ImGuiID NewFrame(bool is_reusing_frame = false); -void Render(const vk::CommandBuffer& cmdbuf, Vulkan::Frame* frame); +void Render(const vk::CommandBuffer& cmdbuf, const vk::ImageView& image_view, + const vk::Extent2D& extent); } // namespace ImGui::Core diff --git a/src/imgui/renderer/imgui_impl_sdl3.cpp b/src/imgui/renderer/imgui_impl_sdl3.cpp index e67bdc775..ddd532cd0 100644 --- a/src/imgui/renderer/imgui_impl_sdl3.cpp +++ b/src/imgui/renderer/imgui_impl_sdl3.cpp @@ -5,6 +5,7 @@ #include #include "common/config.h" +#include "core/debug_state.h" #include "imgui_impl_sdl3.h" // SDL @@ -26,6 +27,7 @@ struct SdlData { SDL_Window* window{}; SDL_WindowID window_id{}; Uint64 time{}; + Uint64 nonReusedtime{}; const char* clipboard_text_data{}; // IME handling @@ -785,7 +787,7 @@ static void UpdateGamepads() { +thumb_dead_zone, +32767); } -void NewFrame() { +void NewFrame(bool is_reusing_frame) { SdlData* bd = GetBackendData(); IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?"); ImGuiIO& io = ImGui::GetIO(); @@ -798,9 +800,26 @@ void NewFrame() { if (current_time <= bd->time) current_time = bd->time + 1; io.DeltaTime = bd->time > 0 ? (float)((double)(current_time - bd->time) / (double)frequency) - : (float)(1.0f / 60.0f); + : 1.0f / 60.0f; bd->time = current_time; + if (!is_reusing_frame) { + if (current_time <= bd->nonReusedtime) + current_time = bd->nonReusedtime + 1; + float deltaTime = + bd->nonReusedtime > 0 + ? (float)((double)(current_time - bd->nonReusedtime) / (double)frequency) + : 1.0f / 60.0f; + bd->nonReusedtime = current_time; + DebugState.FrameDeltaTime = deltaTime; + float distribution = 0.016f / deltaTime / 10.0f; + if (distribution > 1.0f) { + distribution = 1.0f; + } + DebugState.Framerate = + deltaTime * distribution + DebugState.Framerate * (1.0f - distribution); + } + if (bd->mouse_pending_leave_frame && bd->mouse_pending_leave_frame >= ImGui::GetFrameCount() && bd->mouse_buttons_down == 0) { bd->mouse_window_id = 0; diff --git a/src/imgui/renderer/imgui_impl_sdl3.h b/src/imgui/renderer/imgui_impl_sdl3.h index 59b1a6856..fe626a962 100644 --- a/src/imgui/renderer/imgui_impl_sdl3.h +++ b/src/imgui/renderer/imgui_impl_sdl3.h @@ -14,7 +14,7 @@ namespace ImGui::Sdl { bool Init(SDL_Window* window); void Shutdown(); -void NewFrame(); +void NewFrame(bool is_reusing); bool ProcessEvent(const SDL_Event* event); void OnResize(); diff --git a/src/imgui/renderer/imgui_impl_vulkan.cpp b/src/imgui/renderer/imgui_impl_vulkan.cpp index 7f7ade2a5..bd98fa004 100644 --- a/src/imgui/renderer/imgui_impl_vulkan.cpp +++ b/src/imgui/renderer/imgui_impl_vulkan.cpp @@ -57,11 +57,12 @@ struct VkData { vk::DeviceMemory font_memory{}; vk::Image font_image{}; vk::ImageView font_view{}; - vk::DescriptorSet font_descriptor_set{}; + ImTextureID font_texture{}; vk::CommandBuffer font_command_buffer{}; // Render buffers WindowRenderBuffers render_buffers{}; + bool enabled_blending{true}; VkData(const InitInfo init_info) : init_info(init_info) { render_buffers.count = init_info.image_count; @@ -252,8 +253,8 @@ void UploadTextureData::Destroy() { const InitInfo& v = bd->init_info; CheckVkErr(v.device.waitIdle()); - RemoveTexture(descriptor_set); - descriptor_set = VK_NULL_HANDLE; + RemoveTexture(im_texture); + im_texture = nullptr; v.device.destroyImageView(image_view, v.allocator); image_view = VK_NULL_HANDLE; @@ -264,8 +265,8 @@ void UploadTextureData::Destroy() { } // Register a texture -vk::DescriptorSet AddTexture(vk::ImageView image_view, vk::ImageLayout image_layout, - vk::Sampler sampler) { +ImTextureID AddTexture(vk::ImageView image_view, vk::ImageLayout image_layout, + vk::Sampler sampler) { VkData* bd = GetBackendData(); const InitInfo& v = bd->init_info; @@ -303,7 +304,9 @@ vk::DescriptorSet AddTexture(vk::ImageView image_view, vk::ImageLayout image_lay }; v.device.updateDescriptorSets({write_desc}, {}); } - return descriptor_set; + return new Texture{ + .descriptor_set = descriptor_set, + }; } UploadTextureData UploadTexture(const void* data, vk::Format format, u32 width, u32 height, size_t size) { @@ -370,7 +373,7 @@ UploadTextureData UploadTexture(const void* data, vk::Format format, u32 width, } // Create descriptor set (ImTextureID) - info.descriptor_set = AddTexture(info.image_view, vk::ImageLayout::eShaderReadOnlyOptimal); + info.im_texture = AddTexture(info.image_view, vk::ImageLayout::eShaderReadOnlyOptimal); // Create Upload Buffer { @@ -464,10 +467,12 @@ UploadTextureData UploadTexture(const void* data, vk::Format format, u32 width, return info; } -void RemoveTexture(vk::DescriptorSet descriptor_set) { +void RemoveTexture(ImTextureID texture) { + IM_ASSERT(texture != nullptr); VkData* bd = GetBackendData(); const InitInfo& v = bd->init_info; - v.device.freeDescriptorSets(bd->descriptor_pool, {descriptor_set}); + v.device.freeDescriptorSets(bd->descriptor_pool, {texture->descriptor_set}); + delete texture; } static void CreateOrResizeBuffer(RenderBuffer& rb, size_t new_size, vk::BufferUsageFlagBits usage) { @@ -679,15 +684,11 @@ void RenderDrawData(ImDrawData& draw_data, vk::CommandBuffer command_buffer, command_buffer.setScissor(0, 1, &scissor); // Bind DescriptorSet with font or user texture - vk::DescriptorSet desc_set[1]{(VkDescriptorSet)pcmd->TextureId}; - if (sizeof(ImTextureID) < sizeof(ImU64)) { - // We don't support texture switches if ImTextureID hasn't been redefined to be - // 64-bit. Do a flaky check that other textures haven't been used. - IM_ASSERT(pcmd->TextureId == (ImTextureID)bd->font_descriptor_set); - desc_set[0] = bd->font_descriptor_set; - } + vk::DescriptorSet desc_set[1]{pcmd->TextureId->descriptor_set}; command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, bd->pipeline_layout, 0, {desc_set}, {}); + command_buffer.setColorBlendEnableEXT( + 0, {pcmd->TextureId->disable_blend ? vk::False : vk::True}); // Draw command_buffer.drawIndexed(pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, @@ -709,7 +710,7 @@ static bool CreateFontsTexture() { const InitInfo& v = bd->init_info; // Destroy existing texture (if any) - if (bd->font_view || bd->font_image || bd->font_memory || bd->font_descriptor_set) { + if (bd->font_view || bd->font_image || bd->font_memory || bd->font_texture) { CheckVkErr(v.queue.waitIdle()); DestroyFontsTexture(); } @@ -782,7 +783,7 @@ static bool CreateFontsTexture() { } // Create the Descriptor Set: - bd->font_descriptor_set = AddTexture(bd->font_view, vk::ImageLayout::eShaderReadOnlyOptimal); + bd->font_texture = AddTexture(bd->font_view, vk::ImageLayout::eShaderReadOnlyOptimal); // Create the Upload Buffer: vk::DeviceMemory upload_buffer_memory{}; @@ -874,7 +875,7 @@ static bool CreateFontsTexture() { } // Store our identifier - io.Fonts->SetTexID(bd->font_descriptor_set); + io.Fonts->SetTexID(bd->font_texture); // End command buffer vk::SubmitInfo end_info = {}; @@ -898,9 +899,9 @@ static void DestroyFontsTexture() { VkData* bd = GetBackendData(); const InitInfo& v = bd->init_info; - if (bd->font_descriptor_set) { - RemoveTexture(bd->font_descriptor_set); - bd->font_descriptor_set = VK_NULL_HANDLE; + if (bd->font_texture) { + RemoveTexture(bd->font_texture); + bd->font_texture = nullptr; io.Fonts->SetTexID(nullptr); } @@ -1057,9 +1058,10 @@ static void CreatePipeline(vk::Device device, const vk::AllocationCallbacks* all .pAttachments = color_attachment, }; - vk::DynamicState dynamic_states[2]{ + vk::DynamicState dynamic_states[3]{ vk::DynamicState::eViewport, vk::DynamicState::eScissor, + vk::DynamicState::eColorBlendEnableEXT, }; vk::PipelineDynamicStateCreateInfo dynamic_state{ .dynamicStateCount = (uint32_t)IM_ARRAYSIZE(dynamic_states), diff --git a/src/imgui/renderer/imgui_impl_vulkan.h b/src/imgui/renderer/imgui_impl_vulkan.h index e325e2a8d..05f4489da 100644 --- a/src/imgui/renderer/imgui_impl_vulkan.h +++ b/src/imgui/renderer/imgui_impl_vulkan.h @@ -10,6 +10,13 @@ struct ImDrawData; +namespace ImGui { +struct Texture { + vk::DescriptorSet descriptor_set{nullptr}; + bool disable_blend{false}; +}; +} // namespace ImGui + namespace ImGui::Vulkan { struct InitInfo { @@ -34,29 +41,32 @@ struct InitInfo { struct UploadTextureData { vk::Image image; vk::ImageView image_view; - vk::DescriptorSet descriptor_set; vk::DeviceMemory image_memory; vk::CommandBuffer command_buffer; // Submit to the queue vk::Buffer upload_buffer; vk::DeviceMemory upload_buffer_memory; + ImTextureID im_texture; + void Upload(); void Destroy(); }; -vk::DescriptorSet AddTexture(vk::ImageView image_view, vk::ImageLayout image_layout, - vk::Sampler sampler = VK_NULL_HANDLE); +ImTextureID AddTexture(vk::ImageView image_view, vk::ImageLayout image_layout, + vk::Sampler sampler = VK_NULL_HANDLE); UploadTextureData UploadTexture(const void* data, vk::Format format, u32 width, u32 height, size_t size); -void RemoveTexture(vk::DescriptorSet descriptor_set); +void RemoveTexture(ImTextureID descriptor_set); bool Init(InitInfo info); void Shutdown(); void RenderDrawData(ImDrawData& draw_data, vk::CommandBuffer command_buffer, vk::Pipeline pipeline = VK_NULL_HANDLE); +void SetBlendEnabled(bool enabled); + } // namespace ImGui::Vulkan \ No newline at end of file diff --git a/src/imgui/renderer/texture_manager.cpp b/src/imgui/renderer/texture_manager.cpp index f13c995be..d7516a3a5 100644 --- a/src/imgui/renderer/texture_manager.cpp +++ b/src/imgui/renderer/texture_manager.cpp @@ -4,6 +4,7 @@ #include #include +#include #include "common/assert.h" #include "common/config.h" #include "common/io_file.h" @@ -123,7 +124,7 @@ static std::deque g_upload_list; namespace Core::TextureManager { Inner::~Inner() { - if (upload_data.descriptor_set != nullptr) { + if (upload_data.im_texture != nullptr) { std::unique_lock lk{g_upload_mtx}; g_upload_list.emplace_back(UploadJob{ .data = this->upload_data, @@ -239,7 +240,7 @@ void Submit() { } if (upload.core != nullptr) { upload.core->upload_data.Upload(); - upload.core->texture_id = upload.core->upload_data.descriptor_set; + upload.core->texture_id = upload.core->upload_data.im_texture; if (upload.core->count.fetch_sub(1) == 1) { delete upload.core; } diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index f5bcb54b6..323b30816 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -383,6 +383,7 @@ bool Instance::CreateDevice() { .extendedDynamicState = true, }, vk::PhysicalDeviceExtendedDynamicState3FeaturesEXT{ + .extendedDynamicState3ColorBlendEnable = true, .extendedDynamicState3ColorWriteMask = true, }, vk::PhysicalDeviceDepthClipControlFeaturesEXT{ diff --git a/src/video_core/renderer_vulkan/vk_presenter.cpp b/src/video_core/renderer_vulkan/vk_presenter.cpp index 1679aa691..4d76eb8db 100644 --- a/src/video_core/renderer_vulkan/vk_presenter.cpp +++ b/src/video_core/renderer_vulkan/vk_presenter.cpp @@ -20,6 +20,9 @@ #include +#include +#include "imgui/renderer/imgui_impl_vulkan.h" + namespace Vulkan { bool CanBlitToSwapchain(const vk::PhysicalDevice physical_device, vk::Format format) { @@ -103,17 +106,6 @@ static vk::Rect2D FitImage(s32 frame_width, s32 frame_height, s32 swapchain_widt dst_rect.offset.x, dst_rect.offset.y); } -static vk::Format FormatToUnorm(vk::Format fmt) { - switch (fmt) { - case vk::Format::eR8G8B8A8Srgb: - return vk::Format::eR8G8B8A8Unorm; - case vk::Format::eB8G8R8A8Srgb: - return vk::Format::eB8G8R8A8Unorm; - default: - UNREACHABLE(); - } -} - void Presenter::CreatePostProcessPipeline() { static const std::array pp_shaders{ HostShaders::FS_TRI_VERT, @@ -324,9 +316,6 @@ Presenter::Presenter(Frontend::WindowSDL& window_, AmdGpu::Liverpool* liverpool_ CreatePostProcessPipeline(); - // Setup ImGui - ImGui::Core::Initialize(instance, window, num_images, - FormatToUnorm(swapchain.GetSurfaceFormat().format)); ImGui::Layer::AddLayer(Common::Singleton::Instance()); } @@ -344,6 +333,9 @@ Presenter::~Presenter() { void Presenter::RecreateFrame(Frame* frame, u32 width, u32 height) { const vk::Device device = instance.GetDevice(); + if (frame->imgui_texture) { + ImGui::Vulkan::RemoveTexture(frame->imgui_texture); + } if (frame->image_view) { device.destroyImageView(frame->image_view); } @@ -361,7 +353,7 @@ void Presenter::RecreateFrame(Frame* frame, u32 width, u32 height) { .arrayLayers = 1, .samples = vk::SampleCountFlagBits::e1, .usage = vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferDst | - vk::ImageUsageFlagBits::eTransferSrc, + vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eSampled, }; const VmaAllocationCreateInfo alloc_info = { @@ -403,6 +395,64 @@ void Presenter::RecreateFrame(Frame* frame, u32 width, u32 height) { frame->image_view = view; frame->width = width; frame->height = height; + + frame->imgui_texture = ImGui::Vulkan::AddTexture(view, vk::ImageLayout::eShaderReadOnlyOptimal); + frame->imgui_texture->disable_blend = true; +} + +Frame* Presenter::PrepareLastFrame() { + if (last_submit_frame == nullptr) { + return nullptr; + } + + Frame* frame = last_submit_frame; + + while (true) { + vk::Result result = instance.GetDevice().waitForFences(frame->present_done, false, + std::numeric_limits::max()); + if (result == vk::Result::eSuccess) { + break; + } + if (result == vk::Result::eTimeout) { + continue; + } + ASSERT_MSG(result != vk::Result::eErrorDeviceLost, + "Device lost during waiting for a frame"); + } + + auto& scheduler = flip_scheduler; + scheduler.EndRendering(); + const auto cmdbuf = scheduler.CommandBuffer(); + + const auto frame_subresources = vk::ImageSubresourceRange{ + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }; + + const auto pre_barrier = + vk::ImageMemoryBarrier2{.srcStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput, + .srcAccessMask = vk::AccessFlagBits2::eColorAttachmentRead, + .dstStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput, + .dstAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite, + .oldLayout = vk::ImageLayout::eShaderReadOnlyOptimal, + .newLayout = vk::ImageLayout::eGeneral, + .image = frame->image, + .subresourceRange{frame_subresources}}; + + cmdbuf.pipelineBarrier2(vk::DependencyInfo{ + .imageMemoryBarrierCount = 1, + .pImageMemoryBarriers = &pre_barrier, + }); + + // Flush frame creation commands. + frame->ready_semaphore = scheduler.GetMasterSemaphore()->Handle(); + frame->ready_tick = scheduler.CurrentTick(); + SubmitInfo info{}; + scheduler.Flush(info); + return frame; } bool Presenter::ShowSplash(Frame* frame /*= nullptr*/) { @@ -499,6 +549,14 @@ Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop) // Request a free presentation frame. Frame* frame = GetRenderFrame(); + if (image_id != VideoCore::NULL_IMAGE_ID) { + const auto& image = texture_cache.GetImage(image_id); + const auto extent = image.info.size; + if (frame->width != extent.width || frame->height != extent.height) { + RecreateFrame(frame, extent.width, extent.height); + } + } + // EOP flips are triggered from GPU thread so use the drawing scheduler to record // commands. Otherwise we are dealing with a CPU flip which could have arrived // from any guest thread. Use a separate scheduler for that. @@ -515,8 +573,8 @@ Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop) }; const auto pre_barrier = - vk::ImageMemoryBarrier2{.srcStageMask = vk::PipelineStageFlagBits2::eTransfer, - .srcAccessMask = vk::AccessFlagBits2::eTransferRead, + vk::ImageMemoryBarrier2{.srcStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput, + .srcAccessMask = vk::AccessFlagBits2::eColorAttachmentRead, .dstStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput, .dstAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite, .oldLayout = vk::ImageLayout::eUndefined, @@ -627,17 +685,19 @@ Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop) return frame; } -void Presenter::Present(Frame* frame) { +void Presenter::Present(Frame* frame, bool is_reusing_frame) { // Free the frame for reuse const auto free_frame = [&] { - std::scoped_lock fl{free_mutex}; - free_queue.push(frame); - free_cv.notify_one(); + if (!is_reusing_frame) { + last_submit_frame = frame; + std::scoped_lock fl{free_mutex}; + free_queue.push(frame); + free_cv.notify_one(); + } }; // Recreate the swapchain if the window was resized. - if (window.GetWidth() != swapchain.GetExtent().width || - window.GetHeight() != swapchain.GetExtent().height) { + if (window.GetWidth() != swapchain.GetWidth() || window.GetHeight() != swapchain.GetHeight()) { swapchain.Recreate(window.GetWidth(), window.GetHeight()); } @@ -656,15 +716,14 @@ void Presenter::Present(Frame* frame) { // the frame's present fence and future GetRenderFrame() call will hang waiting for this frame. instance.GetDevice().resetFences(frame->present_done); - ImGui::Core::NewFrame(); + ImGuiID dockId = ImGui::Core::NewFrame(is_reusing_frame); const vk::Image swapchain_image = swapchain.Image(); + const vk::ImageView swapchain_image_view = swapchain.ImageView(); auto& scheduler = present_scheduler; const auto cmdbuf = scheduler.CommandBuffer(); - ImGui::Core::Render(cmdbuf, frame); - { auto* profiler_ctx = instance.GetProfilerContext(); TracyVkNamedZoneC(profiler_ctx, renderer_gpu_zone, cmdbuf, "Host frame", @@ -674,9 +733,9 @@ void Presenter::Present(Frame* frame) { const std::array pre_barriers{ vk::ImageMemoryBarrier{ .srcAccessMask = vk::AccessFlagBits::eNone, - .dstAccessMask = vk::AccessFlagBits::eTransferWrite, + .dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite, .oldLayout = vk::ImageLayout::eUndefined, - .newLayout = vk::ImageLayout::eTransferDstOptimal, + .newLayout = vk::ImageLayout::eColorAttachmentOptimal, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = swapchain_image, @@ -690,9 +749,9 @@ void Presenter::Present(Frame* frame) { }, vk::ImageMemoryBarrier{ .srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite, - .dstAccessMask = vk::AccessFlagBits::eTransferRead, + .dstAccessMask = vk::AccessFlagBits::eColorAttachmentRead, .oldLayout = vk::ImageLayout::eGeneral, - .newLayout = vk::ImageLayout::eTransferSrcOptimal, + .newLayout = vk::ImageLayout::eShaderReadOnlyOptimal, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = frame->image, @@ -705,10 +764,11 @@ void Presenter::Present(Frame* frame) { }, }, }; + const vk::ImageMemoryBarrier post_barrier{ - .srcAccessMask = vk::AccessFlagBits::eTransferWrite, + .srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite, .dstAccessMask = vk::AccessFlagBits::eMemoryRead, - .oldLayout = vk::ImageLayout::eTransferDstOptimal, + .oldLayout = vk::ImageLayout::eColorAttachmentOptimal, .newLayout = vk::ImageLayout::ePresentSrcKHR, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -723,14 +783,29 @@ void Presenter::Present(Frame* frame) { }; cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eColorAttachmentOutput, - vk::PipelineStageFlagBits::eTransfer, + vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::DependencyFlagBits::eByRegion, {}, {}, pre_barriers); - cmdbuf.blitImage( - frame->image, vk::ImageLayout::eTransferSrcOptimal, swapchain_image, - vk::ImageLayout::eTransferDstOptimal, - MakeImageBlitStretch(frame->width, frame->height, extent.width, extent.height), - vk::Filter::eLinear); + { // Draw the game + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{0.0f}); + ImGui::SetNextWindowDockID(dockId, ImGuiCond_Once); + ImGui::Begin("Display##game_display", nullptr, ImGuiWindowFlags_NoNav); + + ImVec2 contentArea = ImGui::GetContentRegionAvail(); + const vk::Rect2D imgRect = + FitImage(frame->width, frame->height, (s32)contentArea.x, (s32)contentArea.y); + ImGui::SetCursorPos(ImGui::GetCursorStartPos() + ImVec2{ + (float)imgRect.offset.x, + (float)imgRect.offset.y, + }); + ImGui::Image(frame->imgui_texture, { + static_cast(imgRect.extent.width), + static_cast(imgRect.extent.height), + }); + ImGui::End(); + ImGui::PopStyleVar(); + } + ImGui::Core::Render(cmdbuf, swapchain_image_view, swapchain.GetExtent()); cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands, vk::PipelineStageFlagBits::eAllCommands, @@ -756,7 +831,9 @@ void Presenter::Present(Frame* frame) { } free_frame(); - DebugState.IncFlipFrameNum(); + if (!is_reusing_frame) { + DebugState.IncFlipFrameNum(); + } } Frame* Presenter::GetRenderFrame() { @@ -790,9 +867,9 @@ Frame* Presenter::GetRenderFrame() { } } - // If the window dimensions changed, recreate this frame - if (frame->width != window.GetWidth() || frame->height != window.GetHeight()) { - RecreateFrame(frame, window.GetWidth(), window.GetHeight()); + // Initialize default frame image + if (frame->width == 0 || frame->height == 0) { + RecreateFrame(frame, 1920, 1080); } return frame; diff --git a/src/video_core/renderer_vulkan/vk_presenter.h b/src/video_core/renderer_vulkan/vk_presenter.h index 4c29af0f0..63cb30834 100644 --- a/src/video_core/renderer_vulkan/vk_presenter.h +++ b/src/video_core/renderer_vulkan/vk_presenter.h @@ -5,6 +5,7 @@ #include +#include "imgui/imgui_config.h" #include "video_core/amdgpu/liverpool.h" #include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_scheduler.h" @@ -30,6 +31,8 @@ struct Frame { vk::Fence present_done; vk::Semaphore ready_semaphore; u64 ready_tick; + + ImTextureID imgui_texture; }; enum SchedulerType { @@ -86,8 +89,9 @@ public: } bool ShowSplash(Frame* frame = nullptr); - void Present(Frame* frame); + void Present(Frame* frame, bool is_reusing_frame = false); void RecreateFrame(Frame* frame, u32 width, u32 height); + Frame* PrepareLastFrame(); void FlushDraw() { SubmitInfo info{}; @@ -121,6 +125,7 @@ private: vk::UniqueCommandPool command_pool; std::vector present_frames; std::queue free_queue; + Frame* last_submit_frame; std::mutex free_mutex; std::condition_variable free_cv; std::condition_variable_any frame_cv; diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index 8278252ad..556cccd32 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -4,8 +4,8 @@ #include #include #include "common/assert.h" -#include "common/config.h" #include "common/logging/log.h" +#include "imgui/renderer/imgui_core.h" #include "sdl_window.h" #include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_swapchain.h" @@ -15,7 +15,9 @@ namespace Vulkan { Swapchain::Swapchain(const Instance& instance_, const Frontend::WindowSDL& window) : instance{instance_}, surface{CreateSurface(instance.GetInstance(), window)} { FindPresentFormat(); - Create(window.GetWidth(), window.GetHeight(), surface); + + Create(window.GetWidth(), window.GetHeight()); + ImGui::Core::Initialize(instance, window, image_count, surface_format.format); } Swapchain::~Swapchain() { @@ -23,10 +25,9 @@ Swapchain::~Swapchain() { instance.GetInstance().destroySurfaceKHR(surface); } -void Swapchain::Create(u32 width_, u32 height_, vk::SurfaceKHR surface_) { +void Swapchain::Create(u32 width_, u32 height_) { width = width_; height = height_; - surface = surface_; needs_recreation = false; Destroy(); @@ -86,7 +87,7 @@ void Swapchain::Create(u32 width_, u32 height_, vk::SurfaceKHR surface_) { void Swapchain::Recreate(u32 width_, u32 height_) { LOG_DEBUG(Render_Vulkan, "Recreate the swapchain: width={} height={}", width_, height_); - Create(width_, height_, surface); + Create(width_, height_); } bool Swapchain::AcquireNextImage() { @@ -209,10 +210,14 @@ void Swapchain::Destroy() { if (swapchain) { device.destroySwapchainKHR(swapchain); } - for (u32 i = 0; i < image_count; i++) { - device.destroySemaphore(image_acquired[i]); - device.destroySemaphore(present_ready[i]); + + for (const auto& sem : image_acquired) { + device.destroySemaphore(sem); } + for (const auto& sem : present_ready) { + device.destroySemaphore(sem); + } + image_acquired.clear(); present_ready.clear(); } @@ -249,9 +254,30 @@ void Swapchain::SetupImages() { vk::to_string(images_result)); images = std::move(imgs); image_count = static_cast(images.size()); + images_view.resize(image_count); + for (u32 i = 0; i < image_count; ++i) { + if (images_view[i]) { + device.destroyImageView(images_view[i]); + } + auto [im_view_result, im_view] = device.createImageView(vk::ImageViewCreateInfo{ + .image = images[i], + .viewType = vk::ImageViewType::e2D, + .format = FormatToUnorm(surface_format.format), + .subresourceRange = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .levelCount = 1, + .layerCount = 1, + }, + }); + ASSERT_MSG(im_view_result == vk::Result::eSuccess, "Failed to create image view: {}", + vk::to_string(im_view_result)); + images_view[i] = im_view; + } for (u32 i = 0; i < image_count; ++i) { SetObjectName(device, images[i], "Swapchain Image {}", i); + SetObjectName(device, images_view[i], "Swapchain ImageView {}", i); } } diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h index 19ae9b2d2..192271f32 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.h +++ b/src/video_core/renderer_vulkan/vk_swapchain.h @@ -17,13 +17,24 @@ namespace Vulkan { class Instance; class Scheduler; +inline vk::Format FormatToUnorm(vk::Format fmt) { + switch (fmt) { + case vk::Format::eR8G8B8A8Srgb: + return vk::Format::eR8G8B8A8Unorm; + case vk::Format::eB8G8R8A8Srgb: + return vk::Format::eB8G8R8A8Unorm; + default: + UNREACHABLE(); + } +} + class Swapchain { public: explicit Swapchain(const Instance& instance, const Frontend::WindowSDL& window); ~Swapchain(); /// Creates (or recreates) the swapchain with a given size. - void Create(u32 width, u32 height, vk::SurfaceKHR surface); + void Create(u32 width, u32 height); /// Recreates the swapchain with a given size and current surface. void Recreate(u32 width, u32 height); @@ -42,6 +53,10 @@ public: return images[image_index]; } + vk::ImageView ImageView() const { + return images_view[image_index]; + } + vk::SurfaceFormatKHR GetSurfaceFormat() const { return surface_format; } @@ -103,6 +118,7 @@ private: vk::SurfaceTransformFlagBitsKHR transform; vk::CompositeAlphaFlagBitsKHR composite_alpha; std::vector images; + std::vector images_view; std::vector image_acquired; std::vector present_ready; u32 width = 0; From 8695383d3597d5b625c9ac4d0ad7c5feebf02cac Mon Sep 17 00:00:00 2001 From: Vinicius Rangel Date: Thu, 16 Jan 2025 21:10:17 -0300 Subject: [PATCH 417/549] keep framerate stable even without vsync (#2165) --- src/common/thread.h | 4 ++++ src/core/debug_state.h | 2 +- src/core/devtools/layer.cpp | 10 +++++----- src/core/libraries/videoout/driver.cpp | 11 +++++++---- src/imgui/renderer/imgui_core.cpp | 6 +++++- src/imgui/renderer/imgui_core.h | 2 ++ src/imgui/renderer/imgui_impl_sdl3.cpp | 20 ++++++++++++++------ 7 files changed, 38 insertions(+), 17 deletions(-) diff --git a/src/common/thread.h b/src/common/thread.h index 175ba9445..92cc0c59d 100644 --- a/src/common/thread.h +++ b/src/common/thread.h @@ -37,6 +37,10 @@ public: void Start(); void End(); + + std::chrono::nanoseconds GetTotalWait() const { + return total_wait; + } }; } // namespace Common diff --git a/src/core/debug_state.h b/src/core/debug_state.h index aab741fd0..a9e6f48b7 100644 --- a/src/core/debug_state.h +++ b/src/core/debug_state.h @@ -165,7 +165,7 @@ public: debug_message_popup.push(std::move(message)); } - bool& ShowingDebugMenuBar() { + bool& IsShowingDebugMenuBar() { return showing_debug_menu_bar; } diff --git a/src/core/devtools/layer.cpp b/src/core/devtools/layer.cpp index 11990a56f..c652849e7 100644 --- a/src/core/devtools/layer.cpp +++ b/src/core/devtools/layer.cpp @@ -253,7 +253,7 @@ void L::DrawAdvanced() { void L::DrawSimple() { const float frameRate = DebugState.Framerate; - Text("%d FPS (%.1f ms)", static_cast(std::round(1.0f / frameRate)), frameRate * 1000.0f); + Text("%d FPS (%.1f ms)", static_cast(std::round(frameRate)), 1000.0f / frameRate); } static void LoadSettings(const char* line) { @@ -264,7 +264,7 @@ static void LoadSettings(const char* line) { return; } if (sscanf(line, "show_advanced_debug=%d", &i) == 1) { - DebugState.ShowingDebugMenuBar() = i != 0; + DebugState.IsShowingDebugMenuBar() = i != 0; return; } if (sscanf(line, "show_frame_graph=%d", &i) == 1) { @@ -309,7 +309,7 @@ void L::SetupSettings() { handler.WriteAllFn = [](ImGuiContext*, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) { buf->appendf("[%s][Data]\n", handler->TypeName); buf->appendf("fps_scale=%f\n", fps_scale); - buf->appendf("show_advanced_debug=%d\n", DebugState.ShowingDebugMenuBar()); + buf->appendf("show_advanced_debug=%d\n", DebugState.IsShowingDebugMenuBar()); buf->appendf("show_frame_graph=%d\n", frame_graph.is_open); buf->appendf("dump_frame_count=%d\n", dump_frame_count); buf->append("\n"); @@ -340,7 +340,7 @@ void L::Draw() { if (IsKeyPressed(ImGuiKey_F10, false)) { if (io.KeyCtrl) { - DebugState.ShowingDebugMenuBar() ^= true; + DebugState.IsShowingDebugMenuBar() ^= true; } else { show_simple_fps = !show_simple_fps; } @@ -375,7 +375,7 @@ void L::Draw() { End(); } - if (DebugState.ShowingDebugMenuBar()) { + if (DebugState.IsShowingDebugMenuBar()) { PushFont(io.Fonts->Fonts[IMGUI_FONT_MONO]); PushID("DevtoolsLayer"); DrawAdvanced(); diff --git a/src/core/libraries/videoout/driver.cpp b/src/core/libraries/videoout/driver.cpp index 29948f242..de5421fd7 100644 --- a/src/core/libraries/videoout/driver.cpp +++ b/src/core/libraries/videoout/driver.cpp @@ -9,6 +9,7 @@ #include "core/libraries/kernel/time.h" #include "core/libraries/videoout/driver.h" #include "core/libraries/videoout/videoout_error.h" +#include "imgui/renderer/imgui_core.h" #include "video_core/renderer_vulkan/vk_presenter.h" extern std::unique_ptr presenter; @@ -297,10 +298,12 @@ void VideoOutDriver::PresentThread(std::stop_token token) { if (vblank_status.count % (main_port.flip_rate + 1) == 0) { const auto request = receive_request(); if (!request) { - if (!main_port.is_open) { - DrawBlankFrame(); - } else { - DrawLastFrame(); + if (timer.GetTotalWait().count() < 0) { // Dont draw too fast + if (!main_port.is_open) { + DrawBlankFrame(); + } else if (ImGui::Core::MustKeepDrawing()) { + DrawLastFrame(); + } } } else { Flip(request); diff --git a/src/imgui/renderer/imgui_core.cpp b/src/imgui/renderer/imgui_core.cpp index b63f50340..f3e4609ba 100644 --- a/src/imgui/renderer/imgui_core.cpp +++ b/src/imgui/renderer/imgui_core.cpp @@ -187,7 +187,7 @@ ImGuiID NewFrame(bool is_reusing_frame) { ImGui::NewFrame(); ImGuiWindowFlags flags = ImGuiDockNodeFlags_PassthruCentralNode; - if (!DebugState.ShowingDebugMenuBar()) { + if (!DebugState.IsShowingDebugMenuBar()) { flags |= ImGuiDockNodeFlags_NoTabBar; } ImGuiID dockId = DockSpaceOverViewport(0, GetMainViewport(), flags); @@ -237,6 +237,10 @@ void Render(const vk::CommandBuffer& cmdbuf, const vk::ImageView& image_view, } } +bool MustKeepDrawing() { + return layers.size() > 1 || DebugState.IsShowingDebugMenuBar(); +} + } // namespace Core void Layer::AddLayer(Layer* layer) { diff --git a/src/imgui/renderer/imgui_core.h b/src/imgui/renderer/imgui_core.h index 7d5279bd6..ffee62cf8 100644 --- a/src/imgui/renderer/imgui_core.h +++ b/src/imgui/renderer/imgui_core.h @@ -31,4 +31,6 @@ ImGuiID NewFrame(bool is_reusing_frame = false); void Render(const vk::CommandBuffer& cmdbuf, const vk::ImageView& image_view, const vk::Extent2D& extent); +bool MustKeepDrawing(); // Force the emulator redraw + } // namespace ImGui::Core diff --git a/src/imgui/renderer/imgui_impl_sdl3.cpp b/src/imgui/renderer/imgui_impl_sdl3.cpp index ddd532cd0..ccd31d03a 100644 --- a/src/imgui/renderer/imgui_impl_sdl3.cpp +++ b/src/imgui/renderer/imgui_impl_sdl3.cpp @@ -46,6 +46,11 @@ struct SdlData { ImVector gamepads{}; GamepadMode gamepad_mode{}; bool want_update_gamepads_list{}; + + // Framerate counting (based on ImGui impl) + std::array framerateSecPerFrame; + int framerateSecPerFrameIdx{}; + float framerateSecPerFrameAcc{}; }; // Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui @@ -812,12 +817,15 @@ void NewFrame(bool is_reusing_frame) { : 1.0f / 60.0f; bd->nonReusedtime = current_time; DebugState.FrameDeltaTime = deltaTime; - float distribution = 0.016f / deltaTime / 10.0f; - if (distribution > 1.0f) { - distribution = 1.0f; - } - DebugState.Framerate = - deltaTime * distribution + DebugState.Framerate * (1.0f - distribution); + + int& frameIdx = bd->framerateSecPerFrameIdx; + float& framerateSec = bd->framerateSecPerFrame[frameIdx]; + float& acc = bd->framerateSecPerFrameAcc; + int count = bd->framerateSecPerFrame.size(); + acc += deltaTime - framerateSec; + framerateSec = deltaTime; + frameIdx = (frameIdx + 1) % count; + DebugState.Framerate = acc > 0.0f ? 1.0f / (acc / (float)count) : FLT_MAX; } if (bd->mouse_pending_leave_frame && bd->mouse_pending_leave_frame >= ImGui::GetFrameCount() && From eb49193309f2b40ffe06e74060939761142d8f24 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Thu, 16 Jan 2025 18:24:29 -0800 Subject: [PATCH 418/549] liverpool: Revert queue scope markers. (#2166) --- src/video_core/amdgpu/liverpool.cpp | 41 +++++++++++------------------ 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index 036a031a7..8355dd1e6 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -224,10 +224,6 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spanScopeMarkerBegin("gfx"); - } - const auto base_addr = reinterpret_cast(dcb.data()); while (!dcb.empty()) { const auto* header = reinterpret_cast(dcb.data()); @@ -416,7 +412,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); - rasterizer->ScopeMarkerBegin(fmt::format("{}:DrawIndex2", cmd_address)); + rasterizer->ScopeMarkerBegin(fmt::format("gfx:{}:DrawIndex2", cmd_address)); rasterizer->Draw(true); rasterizer->ScopeMarkerEnd(); } @@ -433,7 +429,8 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); - rasterizer->ScopeMarkerBegin(fmt::format("{}:DrawIndexOffset2", cmd_address)); + rasterizer->ScopeMarkerBegin( + fmt::format("gfx:{}:DrawIndexOffset2", cmd_address)); rasterizer->Draw(true, draw_index_off->index_offset); rasterizer->ScopeMarkerEnd(); } @@ -448,7 +445,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); - rasterizer->ScopeMarkerBegin(fmt::format("{}:DrawIndexAuto", cmd_address)); + rasterizer->ScopeMarkerBegin(fmt::format("gfx:{}:DrawIndexAuto", cmd_address)); rasterizer->Draw(false); rasterizer->ScopeMarkerEnd(); } @@ -463,7 +460,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); - rasterizer->ScopeMarkerBegin(fmt::format("{}:DrawIndirect", cmd_address)); + rasterizer->ScopeMarkerBegin(fmt::format("gfx:{}:DrawIndirect", cmd_address)); rasterizer->DrawIndirect(false, indirect_args_addr, offset, size, 1, 0); rasterizer->ScopeMarkerEnd(); } @@ -479,7 +476,8 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); - rasterizer->ScopeMarkerBegin(fmt::format("{}:DrawIndexIndirect", cmd_address)); + rasterizer->ScopeMarkerBegin( + fmt::format("gfx:{}:DrawIndexIndirect", cmd_address)); rasterizer->DrawIndirect(true, indirect_args_addr, offset, size, 1, 0); rasterizer->ScopeMarkerEnd(); } @@ -495,7 +493,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); rasterizer->ScopeMarkerBegin( - fmt::format("{}:DrawIndexIndirectCountMulti", cmd_address)); + fmt::format("gfx:{}:DrawIndexIndirectCountMulti", cmd_address)); rasterizer->DrawIndirect( true, indirect_args_addr, offset, draw_index_indirect->stride, draw_index_indirect->count, draw_index_indirect->countAddr); @@ -516,7 +514,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); - rasterizer->ScopeMarkerBegin(fmt::format("{}:DispatchDirect", cmd_address)); + rasterizer->ScopeMarkerBegin(fmt::format("gfx:{}:DispatchDirect", cmd_address)); rasterizer->DispatchDirect(); rasterizer->ScopeMarkerEnd(); } @@ -534,7 +532,8 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); - rasterizer->ScopeMarkerBegin(fmt::format("{}:DispatchIndirect", cmd_address)); + rasterizer->ScopeMarkerBegin( + fmt::format("gfx:{}:DispatchIndirect", cmd_address)); rasterizer->DispatchIndirect(indirect_args_addr, offset, size); rasterizer->ScopeMarkerEnd(); } @@ -702,10 +701,6 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spanScopeMarkerEnd(); - } - if (ce_task.handle) { ASSERT_MSG(ce_task.handle.done(), "Partially processed CCB"); ce_task.handle.destroy(); @@ -719,10 +714,6 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, u32 vqid) { FIBER_ENTER(acb_task_name[vqid]); const auto& queue = asc_queues[{vqid}]; - if (rasterizer) { - rasterizer->ScopeMarkerBegin(fmt::format("asc[{}]", vqid)); - } - auto base_addr = reinterpret_cast(acb.data()); while (!acb.empty()) { const auto* header = reinterpret_cast(acb.data()); @@ -820,7 +811,8 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, u32 vqid) { } if (rasterizer && (cs_program.dispatch_initiator & 1)) { const auto cmd_address = reinterpret_cast(header); - rasterizer->ScopeMarkerBegin(fmt::format("{}:DispatchDirect", cmd_address)); + rasterizer->ScopeMarkerBegin( + fmt::format("asc[{}]:{}:DispatchDirect", vqid, cmd_address)); rasterizer->DispatchDirect(); rasterizer->ScopeMarkerEnd(); } @@ -838,7 +830,8 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, u32 vqid) { } if (rasterizer && (cs_program.dispatch_initiator & 1)) { const auto cmd_address = reinterpret_cast(header); - rasterizer->ScopeMarkerBegin(fmt::format("{}:DispatchIndirect", cmd_address)); + rasterizer->ScopeMarkerBegin( + fmt::format("asc[{}]:{}:DispatchIndirect", vqid, cmd_address)); rasterizer->DispatchIndirect(ib_address, 0, size); rasterizer->ScopeMarkerEnd(); } @@ -886,10 +879,6 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, u32 vqid) { } } - if (rasterizer) { - rasterizer->ScopeMarkerEnd(); - } - FIBER_EXIT; } From 3b474a12f99571a70ef526dfe5aa5c633ef2c6bf Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Thu, 16 Jan 2025 18:40:03 -0800 Subject: [PATCH 419/549] shader_recompiler: Improvements to buffer addressing implementation. (#2123) --- .../frontend/translate/vector_memory.cpp | 96 +++++++++------- .../ir/passes/constant_propagation_pass.cpp | 12 +- .../ir/passes/resource_tracking_pass.cpp | 103 +++++++++++------- src/shader_recompiler/specialization.h | 11 ++ src/video_core/amdgpu/resource.h | 10 ++ 5 files changed, 149 insertions(+), 83 deletions(-) diff --git a/src/shader_recompiler/frontend/translate/vector_memory.cpp b/src/shader_recompiler/frontend/translate/vector_memory.cpp index a5b54dff7..8fa0f3f87 100644 --- a/src/shader_recompiler/frontend/translate/vector_memory.cpp +++ b/src/shader_recompiler/frontend/translate/vector_memory.cpp @@ -164,8 +164,8 @@ void Translator::EmitVectorMemory(const GcnInst& inst) { } void Translator::BUFFER_LOAD(u32 num_dwords, bool is_typed, const GcnInst& inst) { - const auto& mtbuf = inst.control.mtbuf; - const bool is_ring = mtbuf.glc && mtbuf.slc; + const auto& mubuf = inst.control.mubuf; + const bool is_ring = mubuf.glc && mubuf.slc; const IR::VectorReg vaddr{inst.src[0].code}; const IR::ScalarReg sharp{inst.src[2].code * 4}; const IR::Value soffset{GetSrc(inst.src[3])}; @@ -178,22 +178,23 @@ void Translator::BUFFER_LOAD(u32 num_dwords, bool is_typed, const GcnInst& inst) if (is_ring) { return ir.CompositeConstruct(ir.GetVectorReg(vaddr), soffset); } - if (mtbuf.idxen && mtbuf.offen) { + if (mubuf.idxen && mubuf.offen) { return ir.CompositeConstruct(ir.GetVectorReg(vaddr), ir.GetVectorReg(vaddr + 1)); } - if (mtbuf.idxen || mtbuf.offen) { + if (mubuf.idxen || mubuf.offen) { return ir.GetVectorReg(vaddr); } return {}; }(); IR::BufferInstInfo buffer_info{}; - buffer_info.index_enable.Assign(mtbuf.idxen); - buffer_info.offset_enable.Assign(mtbuf.offen); - buffer_info.inst_offset.Assign(mtbuf.offset); - buffer_info.globally_coherent.Assign(mtbuf.glc); - buffer_info.system_coherent.Assign(mtbuf.slc); + buffer_info.index_enable.Assign(mubuf.idxen); + buffer_info.offset_enable.Assign(mubuf.offen); + buffer_info.inst_offset.Assign(mubuf.offset); + buffer_info.globally_coherent.Assign(mubuf.glc); + buffer_info.system_coherent.Assign(mubuf.slc); if (is_typed) { + const auto& mtbuf = inst.control.mtbuf; const auto dmft = static_cast(mtbuf.dfmt); const auto nfmt = static_cast(mtbuf.nfmt); ASSERT(nfmt == AmdGpu::NumberFormat::Float && @@ -220,9 +221,11 @@ void Translator::BUFFER_LOAD_FORMAT(u32 num_dwords, const GcnInst& inst) { const auto& mubuf = inst.control.mubuf; const IR::VectorReg vaddr{inst.src[0].code}; const IR::ScalarReg sharp{inst.src[2].code * 4}; - ASSERT_MSG(!mubuf.offen && mubuf.offset == 0, "Offsets for image buffers are not supported"); const IR::Value address = [&] -> IR::Value { - if (mubuf.idxen) { + if (mubuf.idxen && mubuf.offen) { + return ir.CompositeConstruct(ir.GetVectorReg(vaddr), ir.GetVectorReg(vaddr + 1)); + } + if (mubuf.idxen || mubuf.offen) { return ir.GetVectorReg(vaddr); } return {}; @@ -230,13 +233,17 @@ void Translator::BUFFER_LOAD_FORMAT(u32 num_dwords, const GcnInst& inst) { const IR::Value soffset{GetSrc(inst.src[3])}; ASSERT_MSG(soffset.IsImmediate() && soffset.U32() == 0, "Non immediate offset not supported"); - IR::BufferInstInfo info{}; - info.index_enable.Assign(mubuf.idxen); + IR::BufferInstInfo buffer_info{}; + buffer_info.index_enable.Assign(mubuf.idxen); + buffer_info.offset_enable.Assign(mubuf.offen); + buffer_info.inst_offset.Assign(mubuf.offset); + buffer_info.globally_coherent.Assign(mubuf.glc); + buffer_info.system_coherent.Assign(mubuf.slc); const IR::Value handle = ir.CompositeConstruct(ir.GetScalarReg(sharp), ir.GetScalarReg(sharp + 1), ir.GetScalarReg(sharp + 2), ir.GetScalarReg(sharp + 3)); - const IR::Value value = ir.LoadBufferFormat(handle, address, info); + const IR::Value value = ir.LoadBufferFormat(handle, address, buffer_info); const IR::VectorReg dst_reg{inst.src[1].code}; for (u32 i = 0; i < num_dwords; i++) { ir.SetVectorReg(dst_reg + i, IR::F32{ir.CompositeExtract(value, i)}); @@ -244,8 +251,8 @@ void Translator::BUFFER_LOAD_FORMAT(u32 num_dwords, const GcnInst& inst) { } void Translator::BUFFER_STORE(u32 num_dwords, bool is_typed, const GcnInst& inst) { - const auto& mtbuf = inst.control.mtbuf; - const bool is_ring = mtbuf.glc && mtbuf.slc; + const auto& mubuf = inst.control.mubuf; + const bool is_ring = mubuf.glc && mubuf.slc; const IR::VectorReg vaddr{inst.src[0].code}; const IR::ScalarReg sharp{inst.src[2].code * 4}; const IR::Value soffset{GetSrc(inst.src[3])}; @@ -259,22 +266,23 @@ void Translator::BUFFER_STORE(u32 num_dwords, bool is_typed, const GcnInst& inst if (is_ring) { return ir.CompositeConstruct(ir.GetVectorReg(vaddr), soffset); } - if (mtbuf.idxen && mtbuf.offen) { + if (mubuf.idxen && mubuf.offen) { return ir.CompositeConstruct(ir.GetVectorReg(vaddr), ir.GetVectorReg(vaddr + 1)); } - if (mtbuf.idxen || mtbuf.offen) { + if (mubuf.idxen || mubuf.offen) { return ir.GetVectorReg(vaddr); } return {}; }(); IR::BufferInstInfo buffer_info{}; - buffer_info.index_enable.Assign(mtbuf.idxen); - buffer_info.offset_enable.Assign(mtbuf.offen); - buffer_info.inst_offset.Assign(mtbuf.offset); - buffer_info.globally_coherent.Assign(mtbuf.glc); - buffer_info.system_coherent.Assign(mtbuf.slc); + buffer_info.index_enable.Assign(mubuf.idxen); + buffer_info.offset_enable.Assign(mubuf.offen); + buffer_info.inst_offset.Assign(mubuf.offset); + buffer_info.globally_coherent.Assign(mubuf.glc); + buffer_info.system_coherent.Assign(mubuf.slc); if (is_typed) { + const auto& mtbuf = inst.control.mtbuf; const auto dmft = static_cast(mtbuf.dfmt); const auto nfmt = static_cast(mtbuf.nfmt); ASSERT(nfmt == AmdGpu::NumberFormat::Float && @@ -321,8 +329,12 @@ void Translator::BUFFER_STORE_FORMAT(u32 num_dwords, const GcnInst& inst) { const IR::Value soffset{GetSrc(inst.src[3])}; ASSERT_MSG(soffset.IsImmediate() && soffset.U32() == 0, "Non immediate offset not supported"); - IR::BufferInstInfo info{}; - info.index_enable.Assign(mubuf.idxen); + IR::BufferInstInfo buffer_info{}; + buffer_info.index_enable.Assign(mubuf.idxen); + buffer_info.offset_enable.Assign(mubuf.offen); + buffer_info.inst_offset.Assign(mubuf.offset); + buffer_info.globally_coherent.Assign(mubuf.glc); + buffer_info.system_coherent.Assign(mubuf.slc); const IR::VectorReg src_reg{inst.src[1].code}; @@ -338,7 +350,7 @@ void Translator::BUFFER_STORE_FORMAT(u32 num_dwords, const GcnInst& inst) { const IR::Value handle = ir.CompositeConstruct(ir.GetScalarReg(sharp), ir.GetScalarReg(sharp + 1), ir.GetScalarReg(sharp + 2), ir.GetScalarReg(sharp + 3)); - ir.StoreBufferFormat(handle, address, value, info); + ir.StoreBufferFormat(handle, address, value, buffer_info); } void Translator::BUFFER_ATOMIC(AtomicOp op, const GcnInst& inst) { @@ -358,10 +370,12 @@ void Translator::BUFFER_ATOMIC(AtomicOp op, const GcnInst& inst) { const IR::U32 soffset{GetSrc(inst.src[3])}; ASSERT_MSG(soffset.IsImmediate() && soffset.U32() == 0, "Non immediate offset not supported"); - IR::BufferInstInfo info{}; - info.index_enable.Assign(mubuf.idxen); - info.inst_offset.Assign(mubuf.offset); - info.offset_enable.Assign(mubuf.offen); + IR::BufferInstInfo buffer_info{}; + buffer_info.index_enable.Assign(mubuf.idxen); + buffer_info.offset_enable.Assign(mubuf.offen); + buffer_info.inst_offset.Assign(mubuf.offset); + buffer_info.globally_coherent.Assign(mubuf.glc); + buffer_info.system_coherent.Assign(mubuf.slc); IR::Value vdata_val = ir.GetVectorReg(vdata); const IR::Value handle = @@ -371,27 +385,27 @@ void Translator::BUFFER_ATOMIC(AtomicOp op, const GcnInst& inst) { const IR::Value original_val = [&] { switch (op) { case AtomicOp::Swap: - return ir.BufferAtomicSwap(handle, address, vdata_val, info); + return ir.BufferAtomicSwap(handle, address, vdata_val, buffer_info); case AtomicOp::Add: - return ir.BufferAtomicIAdd(handle, address, vdata_val, info); + return ir.BufferAtomicIAdd(handle, address, vdata_val, buffer_info); case AtomicOp::Smin: - return ir.BufferAtomicIMin(handle, address, vdata_val, true, info); + return ir.BufferAtomicIMin(handle, address, vdata_val, true, buffer_info); case AtomicOp::Umin: - return ir.BufferAtomicIMin(handle, address, vdata_val, false, info); + return ir.BufferAtomicIMin(handle, address, vdata_val, false, buffer_info); case AtomicOp::Smax: - return ir.BufferAtomicIMax(handle, address, vdata_val, true, info); + return ir.BufferAtomicIMax(handle, address, vdata_val, true, buffer_info); case AtomicOp::Umax: - return ir.BufferAtomicIMax(handle, address, vdata_val, false, info); + return ir.BufferAtomicIMax(handle, address, vdata_val, false, buffer_info); case AtomicOp::And: - return ir.BufferAtomicAnd(handle, address, vdata_val, info); + return ir.BufferAtomicAnd(handle, address, vdata_val, buffer_info); case AtomicOp::Or: - return ir.BufferAtomicOr(handle, address, vdata_val, info); + return ir.BufferAtomicOr(handle, address, vdata_val, buffer_info); case AtomicOp::Xor: - return ir.BufferAtomicXor(handle, address, vdata_val, info); + return ir.BufferAtomicXor(handle, address, vdata_val, buffer_info); case AtomicOp::Inc: - return ir.BufferAtomicInc(handle, address, vdata_val, info); + return ir.BufferAtomicInc(handle, address, vdata_val, buffer_info); case AtomicOp::Dec: - return ir.BufferAtomicDec(handle, address, vdata_val, info); + return ir.BufferAtomicDec(handle, address, vdata_val, buffer_info); default: UNREACHABLE(); } diff --git a/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp b/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp index fcf2f7d9f..26d819d8e 100644 --- a/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp +++ b/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp @@ -222,9 +222,15 @@ void FoldMul(IR::Block& block, IR::Inst& inst) { return; } const IR::Value rhs{inst.Arg(1)}; - if (rhs.IsImmediate() && Arg(rhs) == 0) { - inst.ReplaceUsesWithAndRemove(IR::Value(0u)); - return; + if (rhs.IsImmediate()) { + if (Arg(rhs) == 0) { + inst.ReplaceUsesWithAndRemove(IR::Value(0u)); + return; + } + if (Arg(rhs) == 1) { + inst.ReplaceUsesWithAndRemove(inst.Arg(0)); + return; + } } } diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index a132cac2c..d94c5223a 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -484,55 +484,73 @@ void PatchDataRingAccess(IR::Block& block, IR::Inst& inst, Info& info, Descripto inst.SetArg(1, ir.Imm32(binding)); } +IR::U32 CalculateBufferAddress(IR::IREmitter& ir, const IR::Inst& inst, const Info& info, + const AmdGpu::Buffer& buffer, u32 stride) { + const auto inst_info = inst.Flags(); + + // index = (inst_idxen ? vgpr_index : 0) + (const_add_tid_enable ? thread_id[5:0] : 0) + IR::U32 index = ir.Imm32(0U); + if (inst_info.index_enable) { + const IR::U32 vgpr_index{inst_info.offset_enable + ? IR::U32{ir.CompositeExtract(inst.Arg(1), 0)} + : IR::U32{inst.Arg(1)}}; + index = ir.IAdd(index, vgpr_index); + } + if (buffer.add_tid_enable) { + ASSERT_MSG(info.l_stage == LogicalStage::Compute, + "Thread ID buffer addressing is not supported outside of compute."); + const IR::U32 thread_id{ir.LaneId()}; + index = ir.IAdd(index, thread_id); + } + // offset = (inst_offen ? vgpr_offset : 0) + inst_offset + IR::U32 offset = ir.Imm32(inst_info.inst_offset.Value()); + if (inst_info.offset_enable) { + const IR::U32 vgpr_offset = inst_info.index_enable + ? IR::U32{ir.CompositeExtract(inst.Arg(1), 1)} + : IR::U32{inst.Arg(1)}; + offset = ir.IAdd(offset, vgpr_offset); + } + const IR::U32 const_stride = ir.Imm32(stride); + IR::U32 buffer_offset; + if (buffer.swizzle_enable) { + const IR::U32 const_index_stride = ir.Imm32(buffer.GetIndexStride()); + const IR::U32 const_element_size = ir.Imm32(buffer.GetElementSize()); + // index_msb = index / const_index_stride + const IR::U32 index_msb{ir.IDiv(index, const_index_stride)}; + // index_lsb = index % const_index_stride + const IR::U32 index_lsb{ir.IMod(index, const_index_stride)}; + // offset_msb = offset / const_element_size + const IR::U32 offset_msb{ir.IDiv(offset, const_element_size)}; + // offset_lsb = offset % const_element_size + const IR::U32 offset_lsb{ir.IMod(offset, const_element_size)}; + // buffer_offset = + // (index_msb * const_stride + offset_msb * const_element_size) * const_index_stride + // + index_lsb * const_element_size + offset_lsb + const IR::U32 buffer_offset_msb = ir.IMul( + ir.IAdd(ir.IMul(index_msb, const_stride), ir.IMul(offset_msb, const_element_size)), + const_index_stride); + const IR::U32 buffer_offset_lsb = + ir.IAdd(ir.IMul(index_lsb, const_element_size), offset_lsb); + buffer_offset = ir.IAdd(buffer_offset_msb, buffer_offset_lsb); + } else { + // buffer_offset = index * const_stride + offset + buffer_offset = ir.IAdd(ir.IMul(index, const_stride), offset); + } + return buffer_offset; +} + void PatchBufferArgs(IR::Block& block, IR::Inst& inst, Info& info) { const auto handle = inst.Arg(0); const auto buffer_res = info.buffers[handle.U32()]; const auto buffer = buffer_res.GetSharp(info); - ASSERT(!buffer.add_tid_enable); - // Address of constant buffer reads can be calculated at IR emission time. if (inst.GetOpcode() == IR::Opcode::ReadConstBuffer) { return; } IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; - const auto inst_info = inst.Flags(); - - const IR::U32 index_stride = ir.Imm32(buffer.index_stride); - const IR::U32 element_size = ir.Imm32(buffer.element_size); - - // Compute address of the buffer using the stride. - IR::U32 address = ir.Imm32(inst_info.inst_offset.Value()); - if (inst_info.index_enable) { - const IR::U32 index = inst_info.offset_enable ? IR::U32{ir.CompositeExtract(inst.Arg(1), 0)} - : IR::U32{inst.Arg(1)}; - if (buffer.swizzle_enable) { - const IR::U32 stride_index_stride = - ir.Imm32(static_cast(buffer.stride * buffer.index_stride)); - const IR::U32 index_msb = ir.IDiv(index, index_stride); - const IR::U32 index_lsb = ir.IMod(index, index_stride); - address = ir.IAdd(address, ir.IAdd(ir.IMul(index_msb, stride_index_stride), - ir.IMul(index_lsb, element_size))); - } else { - address = ir.IAdd(address, ir.IMul(index, ir.Imm32(buffer.GetStride()))); - } - } - if (inst_info.offset_enable) { - const IR::U32 offset = inst_info.index_enable ? IR::U32{ir.CompositeExtract(inst.Arg(1), 1)} - : IR::U32{inst.Arg(1)}; - if (buffer.swizzle_enable) { - const IR::U32 element_size_index_stride = - ir.Imm32(buffer.element_size * buffer.index_stride); - const IR::U32 offset_msb = ir.IDiv(offset, element_size); - const IR::U32 offset_lsb = ir.IMod(offset, element_size); - address = ir.IAdd(address, - ir.IAdd(ir.IMul(offset_msb, element_size_index_stride), offset_lsb)); - } else { - address = ir.IAdd(address, offset); - } - } - inst.SetArg(1, address); + inst.SetArg(1, CalculateBufferAddress(ir, inst, info, buffer, buffer.stride)); } void PatchTextureBufferArgs(IR::Block& block, IR::Inst& inst, Info& info) { @@ -540,8 +558,15 @@ void PatchTextureBufferArgs(IR::Block& block, IR::Inst& inst, Info& info) { const auto buffer_res = info.texture_buffers[handle.U32()]; const auto buffer = buffer_res.GetSharp(info); - ASSERT(!buffer.swizzle_enable && !buffer.add_tid_enable); + // Only linear addressing with index is supported currently, since we cannot yet + // address with sub-texel granularity. + const auto inst_info = inst.Flags(); + ASSERT_MSG(!buffer.swizzle_enable && !inst_info.offset_enable && inst_info.inst_offset == 0, + "Unsupported texture buffer address mode."); + IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; + // Stride of 1 to get an index into formatted data. See above addressing limitations. + inst.SetArg(1, CalculateBufferAddress(ir, inst, info, buffer, 1U)); if (inst.GetOpcode() == IR::Opcode::StoreBufferFormatF32) { const auto swizzled = ApplySwizzle(ir, inst.Arg(2), buffer.DstSelect()); diff --git a/src/shader_recompiler/specialization.h b/src/shader_recompiler/specialization.h index 18b1df1f9..523e63497 100644 --- a/src/shader_recompiler/specialization.h +++ b/src/shader_recompiler/specialization.h @@ -21,10 +21,16 @@ struct VsAttribSpecialization { struct BufferSpecialization { u16 stride : 14; u16 is_storage : 1; + u16 swizzle_enable : 1; + u8 index_stride : 2 = 0; + u8 element_size : 2 = 0; u32 size = 0; bool operator==(const BufferSpecialization& other) const { return stride == other.stride && is_storage == other.is_storage && + swizzle_enable == other.swizzle_enable && + (!swizzle_enable || + (index_stride == other.index_stride && element_size == other.element_size)) && (size >= other.is_storage || is_storage); } }; @@ -101,6 +107,11 @@ struct StageSpecialization { [](auto& spec, const auto& desc, AmdGpu::Buffer sharp) { spec.stride = sharp.GetStride(); spec.is_storage = desc.IsStorage(sharp); + spec.swizzle_enable = sharp.swizzle_enable; + if (spec.swizzle_enable) { + spec.index_stride = sharp.index_stride; + spec.element_size = sharp.element_size; + } if (!spec.is_storage) { spec.size = sharp.GetSize(); } diff --git a/src/video_core/amdgpu/resource.h b/src/video_core/amdgpu/resource.h index 75b8b2acf..fa8edb3e2 100644 --- a/src/video_core/amdgpu/resource.h +++ b/src/video_core/amdgpu/resource.h @@ -76,6 +76,16 @@ struct Buffer { u32 GetSize() const noexcept { return stride == 0 ? num_records : (stride * num_records); } + + u32 GetIndexStride() const noexcept { + // Index stride is 2 bits, meaning 8, 16, 32, or 64. + return 8 << index_stride; + } + + u32 GetElementSize() const noexcept { + // Element size is 2 bits, meaning 2, 4, 8, or 16. + return 2 << element_size; + } }; static_assert(sizeof(Buffer) == 16); // 128bits From c13b29662e23220ff88811173da13463deb1392e Mon Sep 17 00:00:00 2001 From: baggins183 Date: Fri, 17 Jan 2025 00:14:54 -0800 Subject: [PATCH 420/549] handle control point strides that arent a multiple of 16 (#2172) --- .../backend/spirv/spirv_emit_context.cpp | 12 +++++------ .../ir/passes/hull_shader_transform.cpp | 21 +++++++++---------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 7e86dfb4b..7d492a384 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -395,7 +395,7 @@ void EmitContext::DefineInputs() { DefineVariable(U32[1], spv::BuiltIn::PatchVertices, spv::StorageClass::Input); primitive_id = DefineVariable(U32[1], spv::BuiltIn::PrimitiveId, spv::StorageClass::Input); - const u32 num_attrs = runtime_info.hs_info.ls_stride >> 4; + const u32 num_attrs = Common::AlignUp(runtime_info.hs_info.ls_stride, 16) >> 4; if (num_attrs > 0) { const Id per_vertex_type{TypeArray(F32[4], ConstU32(num_attrs))}; // The input vertex count isn't statically known, so make length 32 (what glslang does) @@ -409,7 +409,7 @@ void EmitContext::DefineInputs() { tess_coord = DefineInput(F32[3], std::nullopt, spv::BuiltIn::TessCoord); primitive_id = DefineVariable(U32[1], spv::BuiltIn::PrimitiveId, spv::StorageClass::Input); - const u32 num_attrs = runtime_info.vs_info.hs_output_cp_stride >> 4; + const u32 num_attrs = Common::AlignUp(runtime_info.vs_info.hs_output_cp_stride, 16) >> 4; if (num_attrs > 0) { const Id per_vertex_type{TypeArray(F32[4], ConstU32(num_attrs))}; // The input vertex count isn't statically known, so make length 32 (what glslang does) @@ -418,7 +418,7 @@ void EmitContext::DefineInputs() { Name(input_attr_array, "in_attrs"); } - u32 patch_base_location = runtime_info.vs_info.hs_output_cp_stride >> 4; + const u32 patch_base_location = num_attrs; for (size_t index = 0; index < 30; ++index) { if (!(info.uses_patches & (1U << index))) { continue; @@ -453,7 +453,7 @@ void EmitContext::DefineOutputs() { DefineVariable(type, spv::BuiltIn::CullDistance, spv::StorageClass::Output); } if (stage == Shader::Stage::Local && runtime_info.ls_info.links_with_tcs) { - const u32 num_attrs = runtime_info.ls_info.ls_stride >> 4; + const u32 num_attrs = Common::AlignUp(runtime_info.ls_info.ls_stride, 16) >> 4; if (num_attrs > 0) { const Id type{TypeArray(F32[4], ConstU32(num_attrs))}; output_attr_array = DefineOutput(type, 0); @@ -488,7 +488,7 @@ void EmitContext::DefineOutputs() { Decorate(output_tess_level_inner, spv::Decoration::Patch); } - const u32 num_attrs = runtime_info.hs_info.hs_output_cp_stride >> 4; + const u32 num_attrs = Common::AlignUp(runtime_info.hs_info.hs_output_cp_stride, 16) >> 4; if (num_attrs > 0) { const Id per_vertex_type{TypeArray(F32[4], ConstU32(num_attrs))}; // The input vertex count isn't statically known, so make length 32 (what glslang does) @@ -498,7 +498,7 @@ void EmitContext::DefineOutputs() { Name(output_attr_array, "out_attrs"); } - u32 patch_base_location = runtime_info.hs_info.hs_output_cp_stride >> 4; + const u32 patch_base_location = num_attrs; for (size_t index = 0; index < 30; ++index) { if (!(info.uses_patches & (1U << index))) { continue; diff --git a/src/shader_recompiler/ir/passes/hull_shader_transform.cpp b/src/shader_recompiler/ir/passes/hull_shader_transform.cpp index 6164fec12..b41e38339 100644 --- a/src/shader_recompiler/ir/passes/hull_shader_transform.cpp +++ b/src/shader_recompiler/ir/passes/hull_shader_transform.cpp @@ -349,11 +349,11 @@ static IR::F32 ReadTessControlPointAttribute(IR::U32 addr, const u32 stride, IR: addr = ir.IAdd(addr, ir.Imm32(off_dw)); } const IR::U32 control_point_index = ir.IDiv(addr, ir.Imm32(stride)); - const IR::U32 addr_for_attrs = TryOptimizeAddressModulo(addr, stride, ir); - const IR::U32 attr_index = - ir.ShiftRightLogical(ir.IMod(addr_for_attrs, ir.Imm32(stride)), ir.Imm32(4u)); + const IR::U32 opt_addr = TryOptimizeAddressModulo(addr, stride, ir); + const IR::U32 offset = ir.IMod(opt_addr, ir.Imm32(stride)); + const IR::U32 attr_index = ir.ShiftRightLogical(offset, ir.Imm32(4u)); const IR::U32 comp_index = - ir.ShiftRightLogical(ir.BitwiseAnd(addr_for_attrs, ir.Imm32(0xFU)), ir.Imm32(2u)); + ir.ShiftRightLogical(ir.BitwiseAnd(offset, ir.Imm32(0xFU)), ir.Imm32(2u)); if (is_output_read_in_tcs) { return ir.ReadTcsGenericOuputAttribute(control_point_index, attr_index, comp_index); } else { @@ -452,13 +452,13 @@ void HullShaderTransform(IR::Program& program, RuntimeInfo& runtime_info) { if (off_dw > 0) { addr = ir.IAdd(addr, ir.Imm32(off_dw)); } - u32 stride = runtime_info.hs_info.hs_output_cp_stride; + const u32 stride = runtime_info.hs_info.hs_output_cp_stride; // Invocation ID array index is implicit, handled by SPIRV backend - const IR::U32 addr_for_attrs = TryOptimizeAddressModulo(addr, stride, ir); - const IR::U32 attr_index = ir.ShiftRightLogical( - ir.IMod(addr_for_attrs, ir.Imm32(stride)), ir.Imm32(4u)); + const IR::U32 opt_addr = TryOptimizeAddressModulo(addr, stride, ir); + const IR::U32 offset = ir.IMod(opt_addr, ir.Imm32(stride)); + const IR::U32 attr_index = ir.ShiftRightLogical(offset, ir.Imm32(4u)); const IR::U32 comp_index = ir.ShiftRightLogical( - ir.BitwiseAnd(addr_for_attrs, ir.Imm32(0xFU)), ir.Imm32(2u)); + ir.BitwiseAnd(offset, ir.Imm32(0xFU)), ir.Imm32(2u)); ir.SetTcsGenericAttribute(data_component, attr_index, comp_index); } else { ASSERT(output_kind == AttributeRegion::PatchConst); @@ -535,8 +535,7 @@ void HullShaderTransform(IR::Program& program, RuntimeInfo& runtime_info) { // ... IR::IREmitter ir{*entry_block, it}; - ASSERT(runtime_info.hs_info.ls_stride % 16 == 0); - u32 num_attributes = runtime_info.hs_info.ls_stride / 16; + u32 num_attributes = Common::AlignUp(runtime_info.hs_info.ls_stride, 16) >> 4; const auto invocation_id = ir.GetAttributeU32(IR::Attribute::InvocationId); for (u32 attr_no = 0; attr_no < num_attributes; attr_no++) { for (u32 comp = 0; comp < 4; comp++) { From 1e5b316ac4f7b4e4a5f9d521812b425dc0f0b94f Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 17 Jan 2025 00:15:43 -0800 Subject: [PATCH 421/549] renderer_vulkan: Add debug markers for presenter. (#2167) --- .../renderer_vulkan/vk_presenter.cpp | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/video_core/renderer_vulkan/vk_presenter.cpp b/src/video_core/renderer_vulkan/vk_presenter.cpp index 4d76eb8db..95c7c4ea1 100644 --- a/src/video_core/renderer_vulkan/vk_presenter.cpp +++ b/src/video_core/renderer_vulkan/vk_presenter.cpp @@ -468,6 +468,12 @@ bool Presenter::ShowSplash(Frame* frame /*= nullptr*/) { draw_scheduler.EndRendering(); const auto cmdbuf = draw_scheduler.CommandBuffer(); + if (Config::vkHostMarkersEnabled()) { + cmdbuf.beginDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT{ + .pLabelName = "ShowSplash", + }); + } + if (!frame) { if (!splash_img.has_value()) { VideoCore::ImageInfo info{}; @@ -535,6 +541,10 @@ bool Presenter::ShowSplash(Frame* frame /*= nullptr*/) { .pImageMemoryBarriers = &post_barrier, }); + if (Config::vkHostMarkersEnabled()) { + cmdbuf.endDebugUtilsLabelEXT(); + } + // Flush frame creation commands. frame->ready_semaphore = draw_scheduler.GetMasterSemaphore()->Handle(); frame->ready_tick = draw_scheduler.CurrentTick(); @@ -563,6 +573,12 @@ Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop) auto& scheduler = is_eop ? draw_scheduler : flip_scheduler; scheduler.EndRendering(); const auto cmdbuf = scheduler.CommandBuffer(); + if (Config::vkHostMarkersEnabled()) { + const auto label = fmt::format("PrepareFrameInternal:{}", image_id.index); + cmdbuf.beginDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT{ + .pLabelName = label.c_str(), + }); + } const auto frame_subresources = vk::ImageSubresourceRange{ .aspectMask = vk::ImageAspectFlagBits::eColor, @@ -677,6 +693,10 @@ Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop) .pImageMemoryBarriers = &post_barrier, }); + if (Config::vkHostMarkersEnabled()) { + cmdbuf.endDebugUtilsLabelEXT(); + } + // Flush frame creation commands. frame->ready_semaphore = scheduler.GetMasterSemaphore()->Handle(); frame->ready_tick = scheduler.CurrentTick(); @@ -724,6 +744,12 @@ void Presenter::Present(Frame* frame, bool is_reusing_frame) { auto& scheduler = present_scheduler; const auto cmdbuf = scheduler.CommandBuffer(); + if (Config::vkHostMarkersEnabled()) { + cmdbuf.beginDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT{ + .pLabelName = "Present", + }); + } + { auto* profiler_ctx = instance.GetProfilerContext(); TracyVkNamedZoneC(profiler_ctx, renderer_gpu_zone, cmdbuf, "Host frame", @@ -816,6 +842,10 @@ void Presenter::Present(Frame* frame, bool is_reusing_frame) { } } + if (Config::vkHostMarkersEnabled()) { + cmdbuf.endDebugUtilsLabelEXT(); + } + // Flush vulkan commands. SubmitInfo info{}; info.AddWait(swapchain.GetImageAcquiredSemaphore()); From 1d3427780a118532d6c74788f4bc7f8fc78d12f4 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 17 Jan 2025 00:16:03 -0800 Subject: [PATCH 422/549] renderer_vulkan: Fix present related validation errors. (#2169) --- src/imgui/renderer/imgui_impl_vulkan.cpp | 5 +---- src/imgui/renderer/imgui_impl_vulkan.h | 1 - src/video_core/renderer_vulkan/vk_instance.cpp | 2 +- src/video_core/renderer_vulkan/vk_presenter.cpp | 10 ++++++++-- src/video_core/renderer_vulkan/vk_swapchain.cpp | 14 +++++++++++++- src/video_core/renderer_vulkan/vk_swapchain.h | 5 +++++ 6 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/imgui/renderer/imgui_impl_vulkan.cpp b/src/imgui/renderer/imgui_impl_vulkan.cpp index bd98fa004..104ce4b52 100644 --- a/src/imgui/renderer/imgui_impl_vulkan.cpp +++ b/src/imgui/renderer/imgui_impl_vulkan.cpp @@ -687,8 +687,6 @@ void RenderDrawData(ImDrawData& draw_data, vk::CommandBuffer command_buffer, vk::DescriptorSet desc_set[1]{pcmd->TextureId->descriptor_set}; command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, bd->pipeline_layout, 0, {desc_set}, {}); - command_buffer.setColorBlendEnableEXT( - 0, {pcmd->TextureId->disable_blend ? vk::False : vk::True}); // Draw command_buffer.drawIndexed(pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, @@ -1058,10 +1056,9 @@ static void CreatePipeline(vk::Device device, const vk::AllocationCallbacks* all .pAttachments = color_attachment, }; - vk::DynamicState dynamic_states[3]{ + vk::DynamicState dynamic_states[2]{ vk::DynamicState::eViewport, vk::DynamicState::eScissor, - vk::DynamicState::eColorBlendEnableEXT, }; vk::PipelineDynamicStateCreateInfo dynamic_state{ .dynamicStateCount = (uint32_t)IM_ARRAYSIZE(dynamic_states), diff --git a/src/imgui/renderer/imgui_impl_vulkan.h b/src/imgui/renderer/imgui_impl_vulkan.h index 05f4489da..9b15dcae6 100644 --- a/src/imgui/renderer/imgui_impl_vulkan.h +++ b/src/imgui/renderer/imgui_impl_vulkan.h @@ -13,7 +13,6 @@ struct ImDrawData; namespace ImGui { struct Texture { vk::DescriptorSet descriptor_set{nullptr}; - bool disable_blend{false}; }; } // namespace ImGui diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 323b30816..1600600b9 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -270,6 +270,7 @@ bool Instance::CreateDevice() { legacy_vertex_attributes = add_extension(VK_EXT_LEGACY_VERTEX_ATTRIBUTES_EXTENSION_NAME); image_load_store_lod = add_extension(VK_AMD_SHADER_IMAGE_LOAD_STORE_LOD_EXTENSION_NAME); amd_gcn_shader = add_extension(VK_AMD_GCN_SHADER_EXTENSION_NAME); + add_extension(VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME); // These extensions are promoted by Vulkan 1.3, but for greater compatibility we use Vulkan 1.2 // with extensions. @@ -383,7 +384,6 @@ bool Instance::CreateDevice() { .extendedDynamicState = true, }, vk::PhysicalDeviceExtendedDynamicState3FeaturesEXT{ - .extendedDynamicState3ColorBlendEnable = true, .extendedDynamicState3ColorWriteMask = true, }, vk::PhysicalDeviceDepthClipControlFeaturesEXT{ diff --git a/src/video_core/renderer_vulkan/vk_presenter.cpp b/src/video_core/renderer_vulkan/vk_presenter.cpp index 95c7c4ea1..0f45574bc 100644 --- a/src/video_core/renderer_vulkan/vk_presenter.cpp +++ b/src/video_core/renderer_vulkan/vk_presenter.cpp @@ -380,7 +380,7 @@ void Presenter::RecreateFrame(Frame* frame, u32 width, u32 height) { const vk::ImageViewCreateInfo view_info = { .image = frame->image, .viewType = vk::ImageViewType::e2D, - .format = FormatToUnorm(format), + .format = swapchain.GetViewFormat(), .subresourceRange{ .aspectMask = vk::ImageAspectFlagBits::eColor, .baseMipLevel = 0, @@ -397,7 +397,6 @@ void Presenter::RecreateFrame(Frame* frame, u32 width, u32 height) { frame->height = height; frame->imgui_texture = ImGui::Vulkan::AddTexture(view, vk::ImageLayout::eShaderReadOnlyOptimal); - frame->imgui_texture->disable_blend = true; } Frame* Presenter::PrepareLastFrame() { @@ -615,6 +614,13 @@ Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop) VideoCore::ImageViewInfo info{}; info.format = image.info.pixel_format; + // Exclude alpha from output frame to avoid blending with UI. + info.mapping = vk::ComponentMapping{ + .r = vk::ComponentSwizzle::eIdentity, + .g = vk::ComponentSwizzle::eIdentity, + .b = vk::ComponentSwizzle::eIdentity, + .a = vk::ComponentSwizzle::eOne, + }; if (auto view = image.FindView(info)) { image_info.imageView = *texture_cache.GetImageView(view).image_view; } else { diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index 556cccd32..438fe30ce 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -17,7 +17,7 @@ Swapchain::Swapchain(const Instance& instance_, const Frontend::WindowSDL& windo FindPresentFormat(); Create(window.GetWidth(), window.GetHeight()); - ImGui::Core::Initialize(instance, window, image_count, surface_format.format); + ImGui::Core::Initialize(instance, window, image_count, view_format); } Swapchain::~Swapchain() { @@ -57,7 +57,17 @@ void Swapchain::Create(u32 width_, u32 height_) { const u32 queue_family_indices_count = exclusive ? 1u : 2u; const vk::SharingMode sharing_mode = exclusive ? vk::SharingMode::eExclusive : vk::SharingMode::eConcurrent; + const vk::Format view_formats[2] = { + surface_format.format, + view_format, + }; + const vk::ImageFormatListCreateInfo format_list = { + .viewFormatCount = 2, + .pViewFormats = view_formats, + }; const vk::SwapchainCreateInfoKHR swapchain_info = { + .pNext = &format_list, + .flags = vk::SwapchainCreateFlagBitsKHR::eMutableFormat, .surface = surface, .minImageCount = image_count, .imageFormat = surface_format.format, @@ -149,6 +159,7 @@ void Swapchain::FindPresentFormat() { if (formats[0].format == vk::Format::eUndefined) { surface_format.format = vk::Format::eR8G8B8A8Srgb; surface_format.colorSpace = vk::ColorSpaceKHR::eSrgbNonlinear; + view_format = FormatToUnorm(surface_format.format); return; } @@ -161,6 +172,7 @@ void Swapchain::FindPresentFormat() { surface_format.format = format; surface_format.colorSpace = sformat.colorSpace; + view_format = FormatToUnorm(surface_format.format); return; } diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h index 192271f32..9da75c758 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.h +++ b/src/video_core/renderer_vulkan/vk_swapchain.h @@ -61,6 +61,10 @@ public: return surface_format; } + vk::Format GetViewFormat() const { + return view_format; + } + vk::SwapchainKHR GetHandle() const { return swapchain; } @@ -114,6 +118,7 @@ private: vk::SwapchainKHR swapchain{}; vk::SurfaceKHR surface{}; vk::SurfaceFormatKHR surface_format; + vk::Format view_format; vk::Extent2D extent; vk::SurfaceTransformFlagBitsKHR transform; vk::CompositeAlphaFlagBitsKHR composite_alpha; From 9e5b50c86681053aa05a798663c6eaac6581cd46 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 17 Jan 2025 00:16:15 -0800 Subject: [PATCH 423/549] vk_platform: Clean up unnecessary debug message filters. (#2171) --- src/video_core/renderer_vulkan/vk_platform.cpp | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_platform.cpp b/src/video_core/renderer_vulkan/vk_platform.cpp index 7f0bcb5d2..fdd590e9d 100644 --- a/src/video_core/renderer_vulkan/vk_platform.cpp +++ b/src/video_core/renderer_vulkan/vk_platform.cpp @@ -31,17 +31,6 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL DebugUtilsCallback( VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT type, const VkDebugUtilsMessengerCallbackDataEXT* callback_data, void* user_data) { - switch (static_cast(callback_data->messageIdNumber)) { - case 0x609a13b: // Vertex attribute at location not consumed by shader - case 0xc81ad50e: - case 0xb7c39078: - case 0x32868fde: // vkCreateBufferView(): pCreateInfo->range does not equal VK_WHOLE_SIZE - case 0x1012616b: // `VK_FORMAT_UNDEFINED` does not match fragment shader output type - return VK_FALSE; - default: - break; - } - Common::Log::Level level{}; switch (severity) { case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: From 0cee59cbe6b7ab2471484234e5e7f98c0f6c55e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pl=C3=ADnio=20Larrubia?= Date: Fri, 17 Jan 2025 11:56:52 -0300 Subject: [PATCH 424/549] ci: Don't use the same cache for clang and gcc on linux (#2173) --- .github/workflows/build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c36c026fc..3da7163dd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -390,7 +390,7 @@ jobs: - name: Cache CMake Configuration uses: actions/cache@v4 env: - cache-name: ${{ runner.os }}-sdl-cache-cmake-configuration + cache-name: ${{ runner.os }}-sdl-gcc-cache-cmake-configuration with: path: | ${{github.workspace}}/build @@ -401,7 +401,7 @@ jobs: - name: Cache CMake Build uses: hendrikmuhs/ccache-action@v1.2.14 env: - cache-name: ${{ runner.os }}-sdl-cache-cmake-build + cache-name: ${{ runner.os }}-sdl-gcc-cache-cmake-build with: append-timestamp: false key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }} @@ -426,7 +426,7 @@ jobs: - name: Cache CMake Configuration uses: actions/cache@v4 env: - cache-name: ${{ runner.os }}-qt-cache-cmake-configuration + cache-name: ${{ runner.os }}-qt-gcc-cache-cmake-configuration with: path: | ${{github.workspace}}/build @@ -437,7 +437,7 @@ jobs: - name: Cache CMake Build uses: hendrikmuhs/ccache-action@v1.2.14 env: - cache-name: ${{ runner.os }}-qt-cache-cmake-build + cache-name: ${{ runner.os }}-qt-gcc-cache-cmake-build with: append-timestamp: false key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }} From 4e8c178aecc2d968095a8cc3d88f4290b29b0ed3 Mon Sep 17 00:00:00 2001 From: Vinicius Rangel Date: Fri, 17 Jan 2025 16:11:37 -0300 Subject: [PATCH 425/549] imgui: central node auto-hide tab (#2174) --- src/imgui/renderer/imgui_core.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/imgui/renderer/imgui_core.cpp b/src/imgui/renderer/imgui_core.cpp index f3e4609ba..26253822c 100644 --- a/src/imgui/renderer/imgui_core.cpp +++ b/src/imgui/renderer/imgui_core.cpp @@ -186,7 +186,8 @@ ImGuiID NewFrame(bool is_reusing_frame) { Sdl::NewFrame(is_reusing_frame); ImGui::NewFrame(); - ImGuiWindowFlags flags = ImGuiDockNodeFlags_PassthruCentralNode; + ImGuiWindowFlags flags = + ImGuiDockNodeFlags_PassthruCentralNode | ImGuiDockNodeFlags_AutoHideTabBar; if (!DebugState.IsShowingDebugMenuBar()) { flags |= ImGuiDockNodeFlags_NoTabBar; } From 99a04357d139461e741c2003733e74af3ede6747 Mon Sep 17 00:00:00 2001 From: polybiusproxy <47796739+polybiusproxy@users.noreply.github.com> Date: Fri, 17 Jan 2025 21:51:33 +0100 Subject: [PATCH 426/549] don't compile cs with higher shared memory than supported (#2175) --- src/shader_recompiler/backend/spirv/spirv_emit_context.cpp | 4 ++++ src/shader_recompiler/runtime_info.h | 1 + src/video_core/renderer_vulkan/vk_instance.h | 5 +++++ src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | 1 + 4 files changed, 11 insertions(+) diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 7d492a384..295bef75d 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -847,6 +847,10 @@ void EmitContext::DefineSharedMemory() { if (shared_memory_size == 0) { shared_memory_size = DefaultSharedMemSize; } + + const u32 max_shared_memory_size = runtime_info.cs_info.max_shared_memory_size; + ASSERT(shared_memory_size <= max_shared_memory_size); + const u32 num_elements{Common::DivCeil(shared_memory_size, 4U)}; const Id type{TypeArray(U32[1], ConstU32(num_elements))}; shared_memory_u32_type = TypePointer(spv::StorageClass::Workgroup, type); diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index cf49b0879..8a5e0a1a6 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -198,6 +198,7 @@ struct FragmentRuntimeInfo { struct ComputeRuntimeInfo { u32 shared_memory_size; + u32 max_shared_memory_size; std::array workgroup_size; std::array tgid_enable; diff --git a/src/video_core/renderer_vulkan/vk_instance.h b/src/video_core/renderer_vulkan/vk_instance.h index e0d4d0b4d..1b0c276dc 100644 --- a/src/video_core/renderer_vulkan/vk_instance.h +++ b/src/video_core/renderer_vulkan/vk_instance.h @@ -239,6 +239,11 @@ public: return subgroup_size; } + /// Returns the maximum size of compute shared memory. + u32 MaxComputeSharedMemorySize() const { + return properties.limits.maxComputeSharedMemorySize; + } + /// Returns the maximum supported elements in a texel buffer u32 MaxTexelBufferElements() const { return properties.limits.maxTexelBufferElements; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index f93494389..86f6dcc7a 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -183,6 +183,7 @@ const Shader::RuntimeInfo& PipelineCache::BuildRuntimeInfo(Stage stage, LogicalS info.cs_info.tgid_enable = {cs_pgm.IsTgidEnabled(0), cs_pgm.IsTgidEnabled(1), cs_pgm.IsTgidEnabled(2)}; info.cs_info.shared_memory_size = cs_pgm.SharedMemSize(); + info.cs_info.max_shared_memory_size = instance.MaxComputeSharedMemorySize(); break; } default: From e134fc5f1e0492102f75e30aa3cefd512128c59c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A5IGA?= <164882787+Xphalnos@users.noreply.github.com> Date: Sat, 18 Jan 2025 07:09:10 +0100 Subject: [PATCH 427/549] Qt: Open shadPS4 Folder (#2107) * Qt: Open shadPS4 Folder * clang-format * Change order * Update pt_BR.ts --- src/qt_gui/main_window.cpp | 7 +++++++ src/qt_gui/main_window_ui.h | 8 ++++++++ src/qt_gui/translations/ar.ts | 4 ++++ src/qt_gui/translations/da_DK.ts | 4 ++++ src/qt_gui/translations/de.ts | 4 ++++ src/qt_gui/translations/el.ts | 4 ++++ src/qt_gui/translations/en.ts | 4 ++++ src/qt_gui/translations/es_ES.ts | 4 ++++ src/qt_gui/translations/fa_IR.ts | 4 ++++ src/qt_gui/translations/fi.ts | 4 ++++ src/qt_gui/translations/fr.ts | 4 ++++ src/qt_gui/translations/hu_HU.ts | 4 ++++ src/qt_gui/translations/id.ts | 4 ++++ src/qt_gui/translations/it.ts | 4 ++++ src/qt_gui/translations/ja_JP.ts | 4 ++++ src/qt_gui/translations/ko_KR.ts | 4 ++++ src/qt_gui/translations/lt_LT.ts | 4 ++++ src/qt_gui/translations/nb.ts | 4 ++++ src/qt_gui/translations/nl.ts | 4 ++++ src/qt_gui/translations/pl_PL.ts | 4 ++++ src/qt_gui/translations/pt_BR.ts | 6 +++++- src/qt_gui/translations/ro_RO.ts | 4 ++++ src/qt_gui/translations/ru_RU.ts | 4 ++++ src/qt_gui/translations/sq.ts | 4 ++++ src/qt_gui/translations/sv.ts | 4 ++++ src/qt_gui/translations/tr_TR.ts | 4 ++++ src/qt_gui/translations/uk_UA.ts | 4 ++++ src/qt_gui/translations/vi_VN.ts | 4 ++++ src/qt_gui/translations/zh_CN.ts | 4 ++++ src/qt_gui/translations/zh_TW.ts | 4 ++++ 30 files changed, 128 insertions(+), 1 deletion(-) diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index bd3c27809..3ee392613 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -247,6 +247,12 @@ void MainWindow::CreateConnects() { } }); + connect(ui->shadFolderAct, &QAction::triggered, this, [this]() { + QString userPath; + Common::FS::PathToQString(userPath, Common::FS::GetUserPath(Common::FS::PathType::UserDir)); + QDesktopServices::openUrl(QUrl::fromLocalFile(userPath)); + }); + connect(ui->playButton, &QPushButton::clicked, this, &MainWindow::StartGame); connect(m_game_grid_frame.get(), &QTableWidget::cellDoubleClicked, this, &MainWindow::StartGame); @@ -982,6 +988,7 @@ QIcon MainWindow::RecolorIcon(const QIcon& icon, bool isWhite) { void MainWindow::SetUiIcons(bool isWhite) { ui->bootInstallPkgAct->setIcon(RecolorIcon(ui->bootInstallPkgAct->icon(), isWhite)); ui->bootGameAct->setIcon(RecolorIcon(ui->bootGameAct->icon(), isWhite)); + ui->shadFolderAct->setIcon(RecolorIcon(ui->shadFolderAct->icon(), isWhite)); ui->exitAct->setIcon(RecolorIcon(ui->exitAct->icon(), isWhite)); #ifdef ENABLE_UPDATER ui->updaterAct->setIcon(RecolorIcon(ui->updaterAct->icon(), isWhite)); diff --git a/src/qt_gui/main_window_ui.h b/src/qt_gui/main_window_ui.h index 0d5038d7e..7de166121 100644 --- a/src/qt_gui/main_window_ui.h +++ b/src/qt_gui/main_window_ui.h @@ -12,6 +12,7 @@ public: QAction* bootInstallPkgAct; QAction* bootGameAct; QAction* addElfFolderAct; + QAction* shadFolderAct; QAction* exitAct; QAction* showGameListAct; QAction* refreshGameListAct; @@ -89,6 +90,9 @@ public: addElfFolderAct = new QAction(MainWindow); addElfFolderAct->setObjectName("addElfFolderAct"); addElfFolderAct->setIcon(QIcon(":images/folder_icon.png")); + shadFolderAct = new QAction(MainWindow); + shadFolderAct->setObjectName("shadFolderAct"); + shadFolderAct->setIcon(QIcon(":images/folder_icon.png")); exitAct = new QAction(MainWindow); exitAct->setObjectName("exitAct"); exitAct->setIcon(QIcon(":images/exit_icon.png")); @@ -274,7 +278,9 @@ public: menuBar->addAction(menuHelp->menuAction()); menuFile->addAction(bootInstallPkgAct); menuFile->addAction(bootGameAct); + menuFile->addSeparator(); menuFile->addAction(addElfFolderAct); + menuFile->addAction(shadFolderAct); menuFile->addSeparator(); menuFile->addAction(menuRecent->menuAction()); menuFile->addSeparator(); @@ -333,6 +339,8 @@ public: "MainWindow", "Install application from a .pkg file", nullptr)); #endif // QT_CONFIG(tooltip) menuRecent->setTitle(QCoreApplication::translate("MainWindow", "Recent Games", nullptr)); + shadFolderAct->setText( + QCoreApplication::translate("MainWindow", "Open shadPS4 Folder", nullptr)); exitAct->setText(QCoreApplication::translate("MainWindow", "Exit", nullptr)); #if QT_CONFIG(tooltip) exitAct->setToolTip(QCoreApplication::translate("MainWindow", "Exit shadPS4", nullptr)); diff --git a/src/qt_gui/translations/ar.ts b/src/qt_gui/translations/ar.ts index a4dadcb1a..47bd673b2 100644 --- a/src/qt_gui/translations/ar.ts +++ b/src/qt_gui/translations/ar.ts @@ -247,6 +247,10 @@ Recent Games الألعاب الأخيرة + + Open shadPS4 Folder + Open shadPS4 Folder + Exit خروج diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts index 70b7d3ecc..91a98abd4 100644 --- a/src/qt_gui/translations/da_DK.ts +++ b/src/qt_gui/translations/da_DK.ts @@ -247,6 +247,10 @@ Recent Games Recent Games + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Exit diff --git a/src/qt_gui/translations/de.ts b/src/qt_gui/translations/de.ts index 7f1de3afd..b1e1d2664 100644 --- a/src/qt_gui/translations/de.ts +++ b/src/qt_gui/translations/de.ts @@ -247,6 +247,10 @@ Recent Games Zuletzt gespielt + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Beenden diff --git a/src/qt_gui/translations/el.ts b/src/qt_gui/translations/el.ts index 84165536e..ecda0ede0 100644 --- a/src/qt_gui/translations/el.ts +++ b/src/qt_gui/translations/el.ts @@ -247,6 +247,10 @@ Recent Games Recent Games + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Exit diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index fad185d41..9127df7e3 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -247,6 +247,10 @@ Recent Games Recent Games + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Exit diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index a97d3d3c8..a47f7c577 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -247,6 +247,10 @@ Recent Games Juegos recientes + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Salir diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index 697e615fb..976e7614e 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -247,6 +247,10 @@ Recent Games بازی های اخیر + + Open shadPS4 Folder + Open shadPS4 Folder + Exit خروج diff --git a/src/qt_gui/translations/fi.ts b/src/qt_gui/translations/fi.ts index 51e85dfbb..abc091b7e 100644 --- a/src/qt_gui/translations/fi.ts +++ b/src/qt_gui/translations/fi.ts @@ -247,6 +247,10 @@ Recent Games Viimeisimmät Pelit + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Sulje diff --git a/src/qt_gui/translations/fr.ts b/src/qt_gui/translations/fr.ts index 35f3eb55f..d2a1c5307 100644 --- a/src/qt_gui/translations/fr.ts +++ b/src/qt_gui/translations/fr.ts @@ -247,6 +247,10 @@ Recent Games Jeux récents + + Open shadPS4 Folder + Ouvrir le dossier de shadPS4 + Exit Fermer diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index a2bd9c1da..dff6a3a18 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -247,6 +247,10 @@ Recent Games Legutóbbi Játékok + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Kilépés diff --git a/src/qt_gui/translations/id.ts b/src/qt_gui/translations/id.ts index b97914ca2..e6fb8b5aa 100644 --- a/src/qt_gui/translations/id.ts +++ b/src/qt_gui/translations/id.ts @@ -247,6 +247,10 @@ Recent Games Recent Games + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Exit diff --git a/src/qt_gui/translations/it.ts b/src/qt_gui/translations/it.ts index d4ea1c7e6..73dbdc603 100644 --- a/src/qt_gui/translations/it.ts +++ b/src/qt_gui/translations/it.ts @@ -247,6 +247,10 @@ Recent Games Giochi Recenti + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Uscita diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index 359955765..e07d4eb25 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -247,6 +247,10 @@ Recent Games 最近のゲーム + + Open shadPS4 Folder + Open shadPS4 Folder + Exit 終了 diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts index 9cca0b656..560b58340 100644 --- a/src/qt_gui/translations/ko_KR.ts +++ b/src/qt_gui/translations/ko_KR.ts @@ -247,6 +247,10 @@ Recent Games Recent Games + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Exit diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts index 0594bcbd2..e2ec1e5c3 100644 --- a/src/qt_gui/translations/lt_LT.ts +++ b/src/qt_gui/translations/lt_LT.ts @@ -247,6 +247,10 @@ Recent Games Recent Games + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Exit diff --git a/src/qt_gui/translations/nb.ts b/src/qt_gui/translations/nb.ts index 8ca8246ba..b94d29b23 100644 --- a/src/qt_gui/translations/nb.ts +++ b/src/qt_gui/translations/nb.ts @@ -247,6 +247,10 @@ Recent Games Nylige spill + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Avslutt diff --git a/src/qt_gui/translations/nl.ts b/src/qt_gui/translations/nl.ts index 12d644458..add27500f 100644 --- a/src/qt_gui/translations/nl.ts +++ b/src/qt_gui/translations/nl.ts @@ -247,6 +247,10 @@ Recent Games Recent Games + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Exit diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index 782db12e2..3280beea7 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -247,6 +247,10 @@ Recent Games Ostatnie gry + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Wyjdź diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index 94bbf028a..b9d889519 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -247,6 +247,10 @@ Recent Games Jogos Recentes + + Open shadPS4 Folder + Abrir pasta shadPS4 + Exit Sair @@ -1341,4 +1345,4 @@ TB - \ No newline at end of file + diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index 3bd8e38b5..00a9eb179 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -247,6 +247,10 @@ Recent Games Recent Games + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Exit diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index a38e2fd98..4c90450dd 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -247,6 +247,10 @@ Recent Games Недавние игры + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Выход diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index a83dc9829..768db1e75 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -247,6 +247,10 @@ Recent Games Lojërat e fundit + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Dil diff --git a/src/qt_gui/translations/sv.ts b/src/qt_gui/translations/sv.ts index 9a244a9df..3781ba45c 100644 --- a/src/qt_gui/translations/sv.ts +++ b/src/qt_gui/translations/sv.ts @@ -722,6 +722,10 @@ Recent Games Senaste spel + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Avsluta diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index be50f935a..5e8499073 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -247,6 +247,10 @@ Recent Games Son Oyunlar + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Çıkış diff --git a/src/qt_gui/translations/uk_UA.ts b/src/qt_gui/translations/uk_UA.ts index ff4e48e80..a1c7e97e0 100644 --- a/src/qt_gui/translations/uk_UA.ts +++ b/src/qt_gui/translations/uk_UA.ts @@ -247,6 +247,10 @@ Recent Games Нещодавні ігри + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Вихід diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index e546d955c..a579a1983 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -247,6 +247,10 @@ Recent Games Recent Games + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Exit diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index ece5f9490..5450f3dfd 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -247,6 +247,10 @@ Recent Games 最近启动的游戏 + + Open shadPS4 Folder + Open shadPS4 Folder + Exit 退出 diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index 11642d52b..0ce0b4d69 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -247,6 +247,10 @@ Recent Games Recent Games + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Exit From a5440e0e43f1d91fff0b23e0c0ba4103d5e8976e Mon Sep 17 00:00:00 2001 From: Ian Maclachlan <32774670+imaclachlan@users.noreply.github.com> Date: Sat, 18 Jan 2025 06:16:07 +0000 Subject: [PATCH 428/549] Update kernel.cpp (#2125) In kernel.cpp boost io_context.reset() deprecated/discontinued in latest versions. Changed to io_context.restart() as recommended. --- src/core/libraries/kernel/kernel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/libraries/kernel/kernel.cpp b/src/core/libraries/kernel/kernel.cpp index a9d04ca38..2b7735219 100644 --- a/src/core/libraries/kernel/kernel.cpp +++ b/src/core/libraries/kernel/kernel.cpp @@ -60,7 +60,7 @@ static void KernelServiceThread(std::stop_token stoken) { } io_context.run(); - io_context.reset(); + io_context.restart(); asio_requests = 0; } From 7b8177f48e3d5dfa12b2d416fdf291572e56b905 Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Sat, 18 Jan 2025 09:20:38 +0300 Subject: [PATCH 429/549] renderer: handle disabled clipping (#2146) Co-authored-by: IndecisiveTurtle <47210458+raphaelthegreat@users.noreply.github.com> --- .../backend/spirv/emit_spirv_special.cpp | 38 +++++++++++ .../backend/spirv/spirv_emit_context.cpp | 37 ++++++---- src/shader_recompiler/info.h | 8 +++ src/shader_recompiler/profile.h | 2 + src/shader_recompiler/runtime_info.h | 4 +- .../renderer_vulkan/vk_graphics_pipeline.h | 7 +- .../renderer_vulkan/vk_instance.cpp | 6 ++ src/video_core/renderer_vulkan/vk_instance.h | 8 +++ .../renderer_vulkan/vk_pipeline_cache.cpp | 5 ++ .../renderer_vulkan/vk_rasterizer.cpp | 67 ++++++++++++++----- .../renderer_vulkan/vk_rasterizer.h | 2 +- 11 files changed, 149 insertions(+), 35 deletions(-) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp index 4a22ba09f..a0a3ed8ff 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp @@ -24,10 +24,48 @@ void ConvertDepthMode(EmitContext& ctx) { ctx.OpStore(ctx.output_position, vector); } +void ConvertPositionToClipSpace(EmitContext& ctx) { + const Id type{ctx.F32[1]}; + Id position{ctx.OpLoad(ctx.F32[4], ctx.output_position)}; + const Id x{ctx.OpCompositeExtract(type, position, 0u)}; + const Id y{ctx.OpCompositeExtract(type, position, 1u)}; + const Id z{ctx.OpCompositeExtract(type, position, 2u)}; + const Id w{ctx.OpCompositeExtract(type, position, 3u)}; + const Id xoffset_ptr{ctx.OpAccessChain(ctx.TypePointer(spv::StorageClass::PushConstant, type), + ctx.push_data_block, + ctx.ConstU32(PushData::XOffsetIndex))}; + const Id xoffset{ctx.OpLoad(type, xoffset_ptr)}; + const Id yoffset_ptr{ctx.OpAccessChain(ctx.TypePointer(spv::StorageClass::PushConstant, type), + ctx.push_data_block, + ctx.ConstU32(PushData::YOffsetIndex))}; + const Id yoffset{ctx.OpLoad(type, yoffset_ptr)}; + const Id xscale_ptr{ctx.OpAccessChain(ctx.TypePointer(spv::StorageClass::PushConstant, type), + ctx.push_data_block, + ctx.ConstU32(PushData::XScaleIndex))}; + const Id xscale{ctx.OpLoad(type, xscale_ptr)}; + const Id yscale_ptr{ctx.OpAccessChain(ctx.TypePointer(spv::StorageClass::PushConstant, type), + ctx.push_data_block, + ctx.ConstU32(PushData::YScaleIndex))}; + const Id yscale{ctx.OpLoad(type, yscale_ptr)}; + const Id vport_w = + ctx.Constant(type, float(std::min(ctx.profile.max_viewport_width / 2, 8_KB))); + const Id wnd_x = ctx.OpFAdd(type, ctx.OpFMul(type, x, xscale), xoffset); + const Id ndc_x = ctx.OpFSub(type, ctx.OpFDiv(type, wnd_x, vport_w), ctx.Constant(type, 1.f)); + const Id vport_h = + ctx.Constant(type, float(std::min(ctx.profile.max_viewport_height / 2, 8_KB))); + const Id wnd_y = ctx.OpFAdd(type, ctx.OpFMul(type, y, yscale), yoffset); + const Id ndc_y = ctx.OpFSub(type, ctx.OpFDiv(type, wnd_y, vport_h), ctx.Constant(type, 1.f)); + const Id vector{ctx.OpCompositeConstruct(ctx.F32[4], std::array({ndc_x, ndc_y, z, w}))}; + ctx.OpStore(ctx.output_position, vector); +} + void EmitEpilogue(EmitContext& ctx) { if (ctx.stage == Stage::Vertex && ctx.runtime_info.vs_info.emulate_depth_negative_one_to_one) { ConvertDepthMode(ctx); } + if (ctx.stage == Stage::Vertex && ctx.runtime_info.vs_info.clip_disable) { + ConvertPositionToClipSpace(ctx); + } } void EmitDiscard(EmitContext& ctx) { diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 295bef75d..b0bf5aa0a 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -568,25 +568,34 @@ void EmitContext::DefineOutputs() { void EmitContext::DefinePushDataBlock() { // Create push constants block for instance steps rates - const Id struct_type{Name( - TypeStruct(U32[1], U32[1], U32[4], U32[4], U32[4], U32[4], U32[4], U32[4]), "AuxData")}; + const Id struct_type{Name(TypeStruct(U32[1], U32[1], U32[4], U32[4], U32[4], U32[4], U32[4], + U32[4], F32[1], F32[1], F32[1], F32[1]), + "AuxData")}; Decorate(struct_type, spv::Decoration::Block); MemberName(struct_type, 0, "sr0"); MemberName(struct_type, 1, "sr1"); - MemberName(struct_type, 2, "buf_offsets0"); - MemberName(struct_type, 3, "buf_offsets1"); - MemberName(struct_type, 4, "ud_regs0"); - MemberName(struct_type, 5, "ud_regs1"); - MemberName(struct_type, 6, "ud_regs2"); - MemberName(struct_type, 7, "ud_regs3"); + MemberName(struct_type, Shader::PushData::BufOffsetIndex + 0, "buf_offsets0"); + MemberName(struct_type, Shader::PushData::BufOffsetIndex + 1, "buf_offsets1"); + MemberName(struct_type, Shader::PushData::UdRegsIndex + 0, "ud_regs0"); + MemberName(struct_type, Shader::PushData::UdRegsIndex + 1, "ud_regs1"); + MemberName(struct_type, Shader::PushData::UdRegsIndex + 2, "ud_regs2"); + MemberName(struct_type, Shader::PushData::UdRegsIndex + 3, "ud_regs3"); + MemberName(struct_type, Shader::PushData::XOffsetIndex, "xoffset"); + MemberName(struct_type, Shader::PushData::YOffsetIndex, "yoffset"); + MemberName(struct_type, Shader::PushData::XScaleIndex, "xscale"); + MemberName(struct_type, Shader::PushData::YScaleIndex, "yscale"); MemberDecorate(struct_type, 0, spv::Decoration::Offset, 0U); MemberDecorate(struct_type, 1, spv::Decoration::Offset, 4U); - MemberDecorate(struct_type, 2, spv::Decoration::Offset, 8U); - MemberDecorate(struct_type, 3, spv::Decoration::Offset, 24U); - MemberDecorate(struct_type, 4, spv::Decoration::Offset, 40U); - MemberDecorate(struct_type, 5, spv::Decoration::Offset, 56U); - MemberDecorate(struct_type, 6, spv::Decoration::Offset, 72U); - MemberDecorate(struct_type, 7, spv::Decoration::Offset, 88U); + MemberDecorate(struct_type, Shader::PushData::BufOffsetIndex + 0, spv::Decoration::Offset, 8U); + MemberDecorate(struct_type, Shader::PushData::BufOffsetIndex + 1, spv::Decoration::Offset, 24U); + MemberDecorate(struct_type, Shader::PushData::UdRegsIndex + 0, spv::Decoration::Offset, 40U); + MemberDecorate(struct_type, Shader::PushData::UdRegsIndex + 1, spv::Decoration::Offset, 56U); + MemberDecorate(struct_type, Shader::PushData::UdRegsIndex + 2, spv::Decoration::Offset, 72U); + MemberDecorate(struct_type, Shader::PushData::UdRegsIndex + 3, spv::Decoration::Offset, 88U); + MemberDecorate(struct_type, Shader::PushData::XOffsetIndex, spv::Decoration::Offset, 104U); + MemberDecorate(struct_type, Shader::PushData::YOffsetIndex, spv::Decoration::Offset, 108U); + MemberDecorate(struct_type, Shader::PushData::XScaleIndex, spv::Decoration::Offset, 112U); + MemberDecorate(struct_type, Shader::PushData::YScaleIndex, spv::Decoration::Offset, 116U); push_data_block = DefineVar(struct_type, spv::StorageClass::PushConstant); Name(push_data_block, "push_data"); interfaces.push_back(push_data_block); diff --git a/src/shader_recompiler/info.h b/src/shader_recompiler/info.h index aeff346fa..2cde30629 100644 --- a/src/shader_recompiler/info.h +++ b/src/shader_recompiler/info.h @@ -96,11 +96,19 @@ using FMaskResourceList = boost::container::small_vector; struct PushData { static constexpr u32 BufOffsetIndex = 2; static constexpr u32 UdRegsIndex = 4; + static constexpr u32 XOffsetIndex = 8; + static constexpr u32 YOffsetIndex = 9; + static constexpr u32 XScaleIndex = 10; + static constexpr u32 YScaleIndex = 11; u32 step0; u32 step1; std::array buf_offsets; std::array ud_regs; + float xoffset; + float yoffset; + float xscale; + float yscale; void AddOffset(u32 binding, u32 offset) { ASSERT(offset < 256 && binding < buf_offsets.size()); diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index f8878d442..f8b91a283 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h @@ -30,6 +30,8 @@ struct Profile { bool needs_manual_interpolation{}; bool needs_lds_barriers{}; u64 min_ssbo_alignment{}; + u32 max_viewport_width{}; + u32 max_viewport_height{}; }; } // namespace Shader diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index 8a5e0a1a6..2bf5e3f0a 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -84,6 +84,7 @@ struct VertexRuntimeInfo { u32 num_outputs; std::array outputs; bool emulate_depth_negative_one_to_one{}; + bool clip_disable{}; // Domain AmdGpu::TessellationType tess_type; AmdGpu::TessellationTopology tess_topology; @@ -92,7 +93,8 @@ struct VertexRuntimeInfo { bool operator==(const VertexRuntimeInfo& other) const noexcept { return emulate_depth_negative_one_to_one == other.emulate_depth_negative_one_to_one && - tess_type == other.tess_type && tess_topology == other.tess_topology && + clip_disable == other.clip_disable && tess_type == other.tess_type && + tess_topology == other.tess_topology && tess_partitioning == other.tess_partitioning && hs_output_cp_stride == other.hs_output_cp_stride; } diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index f696c1f72..9a20f94c1 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -42,13 +42,14 @@ struct GraphicsPipelineKey { vk::Format stencil_format; struct { + bool clip_disable : 1; bool depth_test_enable : 1; bool depth_write_enable : 1; bool depth_bounds_test_enable : 1; bool depth_bias_enable : 1; bool stencil_test_enable : 1; // Must be named to be zero-initialized. - u8 _unused : 3; + u8 _unused : 2; }; vk::CompareOp depth_compare_op; @@ -94,6 +95,10 @@ public: return key.mrt_mask; } + auto IsClipDisabled() const { + return key.clip_disable; + } + [[nodiscard]] bool IsPrimitiveListTopology() const { return key.prim_type == AmdGpu::PrimitiveType::PointList || key.prim_type == AmdGpu::PrimitiveType::LineList || diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 1600600b9..d9577a612 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -208,6 +208,7 @@ std::string Instance::GetDriverVersionName() { bool Instance::CreateDevice() { const vk::StructureChain feature_chain = physical_device.getFeatures2< vk::PhysicalDeviceFeatures2, vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT, + vk::PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT, vk::PhysicalDeviceExtendedDynamicState2FeaturesEXT, vk::PhysicalDeviceExtendedDynamicState3FeaturesEXT, vk::PhysicalDeviceCustomBorderColorFeaturesEXT, @@ -317,6 +318,9 @@ bool Instance::CreateDevice() { .pQueuePriorities = queue_priorities.data(), }; + const auto topology_list_restart_features = + feature_chain.get(); + const auto vk12_features = feature_chain.get(); vk::StructureChain device_chain = { vk::DeviceCreateInfo{ @@ -406,6 +410,8 @@ bool Instance::CreateDevice() { }, vk::PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT{ .primitiveTopologyListRestart = true, + .primitiveTopologyPatchListRestart = + topology_list_restart_features.primitiveTopologyPatchListRestart, }, vk::PhysicalDeviceFragmentShaderBarycentricFeaturesKHR{ .fragmentShaderBarycentric = true, diff --git a/src/video_core/renderer_vulkan/vk_instance.h b/src/video_core/renderer_vulkan/vk_instance.h index 1b0c276dc..8c4752c3f 100644 --- a/src/video_core/renderer_vulkan/vk_instance.h +++ b/src/video_core/renderer_vulkan/vk_instance.h @@ -279,6 +279,14 @@ public: return min_imported_host_pointer_alignment; } + u32 GetMaxViewportWidth() const { + return properties.limits.maxViewportDimensions[0]; + } + + u32 GetMaxViewportHeight() const { + return properties.limits.maxViewportDimensions[1]; + } + /// Returns the sample count flags supported by framebuffers. vk::SampleCountFlags GetFramebufferSampleCounts() const { return properties.limits.framebufferColorSampleCounts & diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 86f6dcc7a..c6a56745d 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -125,6 +125,7 @@ const Shader::RuntimeInfo& PipelineCache::BuildRuntimeInfo(Stage stage, LogicalS info.vs_info.emulate_depth_negative_one_to_one = !instance.IsDepthClipControlSupported() && regs.clipper_control.clip_space == Liverpool::ClipSpace::MinusWToW; + info.vs_info.clip_disable = graphics_key.clip_disable; if (l_stage == LogicalStage::TessellationEval) { info.vs_info.tess_type = regs.tess_config.type; info.vs_info.tess_topology = regs.tess_config.topology; @@ -210,6 +211,8 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_, instance.GetDriverID() == vk::DriverId::eNvidiaProprietary, .needs_lds_barriers = instance.GetDriverID() == vk::DriverId::eNvidiaProprietary || instance.GetDriverID() == vk::DriverId::eMoltenvk, + .max_viewport_width = instance.GetMaxViewportWidth(), + .max_viewport_height = instance.GetMaxViewportHeight(), }; auto [cache_result, cache] = instance.GetDevice().createPipelineCacheUnique({}); ASSERT_MSG(cache_result == vk::Result::eSuccess, "Failed to create pipeline cache: {}", @@ -262,6 +265,8 @@ bool PipelineCache::RefreshGraphicsKey() { auto& regs = liverpool->regs; auto& key = graphics_key; + key.clip_disable = + regs.clipper_control.clip_disable || regs.primitive_type == AmdGpu::PrimitiveType::RectList; key.depth_test_enable = regs.depth_control.depth_enable; key.depth_write_enable = regs.depth_control.depth_write_enable && !regs.depth_render_control.depth_clear_enable; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index bac647125..07369c620 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -504,6 +504,17 @@ bool Rasterizer::BindResources(const Pipeline* pipeline) { } push_data.step0 = regs.vgt_instance_step_rate_0; push_data.step1 = regs.vgt_instance_step_rate_1; + + // TODO(roamic): add support for multiple viewports and geometry shaders when ViewportIndex + // is encountered and implemented in the recompiler. + if (stage->stage == Shader::Stage::Vertex) { + push_data.xoffset = + regs.viewport_control.xoffset_enable ? regs.viewports[0].xoffset : 0.f; + push_data.xscale = regs.viewport_control.xscale_enable ? regs.viewports[0].xscale : 1.f; + push_data.yoffset = + regs.viewport_control.yoffset_enable ? regs.viewports[0].yoffset : 0.f; + push_data.yscale = regs.viewport_control.yscale_enable ? regs.viewports[0].yscale : 1.f; + } stage->PushUd(binding, push_data); BindBuffers(*stage, binding, push_data, set_writes, buffer_barriers); @@ -1032,7 +1043,7 @@ void Rasterizer::UnmapMemory(VAddr addr, u64 size) { } void Rasterizer::UpdateDynamicState(const GraphicsPipeline& pipeline) { - UpdateViewportScissorState(); + UpdateViewportScissorState(pipeline); auto& regs = liverpool->regs; const auto cmdbuf = scheduler.CommandBuffer(); @@ -1112,7 +1123,7 @@ void Rasterizer::UpdateDynamicState(const GraphicsPipeline& pipeline) { } } -void Rasterizer::UpdateViewportScissorState() { +void Rasterizer::UpdateViewportScissorState(const GraphicsPipeline& pipeline) { const auto& regs = liverpool->regs; const auto combined_scissor_value_tl = [](s16 scr, s16 win, s16 gen, s16 win_offset) { @@ -1151,26 +1162,46 @@ void Rasterizer::UpdateViewportScissorState() { ? 1.0f : 0.0f; + if (regs.polygon_control.enable_window_offset) { + LOG_ERROR(Render_Vulkan, + "PA_SU_SC_MODE_CNTL.VTX_WINDOW_OFFSET_ENABLE support is not yet implemented."); + } + for (u32 i = 0; i < Liverpool::NumViewports; i++) { const auto& vp = regs.viewports[i]; const auto& vp_d = regs.viewport_depths[i]; if (vp.xscale == 0) { continue; } - const auto xoffset = vp_ctl.xoffset_enable ? vp.xoffset : 0.f; - const auto xscale = vp_ctl.xscale_enable ? vp.xscale : 1.f; - const auto yoffset = vp_ctl.yoffset_enable ? vp.yoffset : 0.f; - const auto yscale = vp_ctl.yscale_enable ? vp.yscale : 1.f; - const auto zoffset = vp_ctl.zoffset_enable ? vp.zoffset : 0.f; - const auto zscale = vp_ctl.zscale_enable ? vp.zscale : 1.f; - viewports.push_back({ - .x = xoffset - xscale, - .y = yoffset - yscale, - .width = xscale * 2.0f, - .height = yscale * 2.0f, - .minDepth = zoffset - zscale * reduce_z, - .maxDepth = zscale + zoffset, - }); + + if (pipeline.IsClipDisabled()) { + // In case if clipping is disabled we patch the shader to convert vertex position + // from screen space coordinates to NDC by defining a render space as full hardware + // window range [0..16383, 0..16383] and setting the viewport to its size. + viewports.push_back({ + .x = 0.f, + .y = 0.f, + .width = float(std::min(instance.GetMaxViewportWidth(), 16_KB)), + .height = float(std::min(instance.GetMaxViewportHeight(), 16_KB)), + .minDepth = 0.0, + .maxDepth = 1.0, + }); + } else { + const auto xoffset = vp_ctl.xoffset_enable ? vp.xoffset : 0.f; + const auto xscale = vp_ctl.xscale_enable ? vp.xscale : 1.f; + const auto yoffset = vp_ctl.yoffset_enable ? vp.yoffset : 0.f; + const auto yscale = vp_ctl.yscale_enable ? vp.yscale : 1.f; + const auto zoffset = vp_ctl.zoffset_enable ? vp.zoffset : 0.f; + const auto zscale = vp_ctl.zscale_enable ? vp.zscale : 1.f; + viewports.push_back({ + .x = xoffset - xscale, + .y = yoffset - yscale, + .width = xscale * 2.0f, + .height = yscale * 2.0f, + .minDepth = zoffset - zscale * reduce_z, + .maxDepth = zscale + zoffset, + }); + } auto vp_scsr = scsr; if (regs.mode_control.vport_scissor_enable) { @@ -1192,8 +1223,8 @@ void Rasterizer::UpdateViewportScissorState() { if (viewports.empty()) { // Vulkan requires providing at least one viewport. constexpr vk::Viewport empty_viewport = { - .x = 0.0f, - .y = 0.0f, + .x = -1.0f, + .y = -1.0f, .width = 1.0f, .height = 1.0f, .minDepth = 0.0f, diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index abf58e522..ed6cc7e71 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -76,7 +76,7 @@ private: void EliminateFastClear(); void UpdateDynamicState(const GraphicsPipeline& pipeline); - void UpdateViewportScissorState(); + void UpdateViewportScissorState(const GraphicsPipeline& pipeline); bool FilterDraw(); From 90b04e8cc0227a35aa5b01d6c361b4eaaefaee44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quang=20Ng=C3=B4?= Date: Sat, 18 Jan 2025 13:59:38 +0700 Subject: [PATCH 430/549] input: Don't use old input state in GameController::ReadState() (#2170) --- src/core/libraries/pad/pad.cpp | 33 +++-- src/input/controller.cpp | 146 ++++++++++---------- src/input/controller.h | 36 +++-- src/sdl_window.cpp | 234 +++++++++++++++++++++++++++------ src/sdl_window.h | 22 +++- 5 files changed, 340 insertions(+), 131 deletions(-) diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index 7eb628a90..9a44f91f0 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -11,6 +11,8 @@ namespace Libraries::Pad { +using Input::GameController; + int PS4_SYSV_ABI scePadClose(s32 handle) { LOG_ERROR(Lib_Pad, "(STUBBED) called"); return ORBIS_OK; @@ -290,7 +292,8 @@ int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) { int connected_count = 0; bool connected = false; Input::State states[64]; - auto* controller = Common::Singleton::Instance(); + auto* controller = Common::Singleton::Instance(); + const auto* engine = controller->GetEngine(); int ret_num = controller->ReadStates(states, num, &connected, &connected_count); if (!connected) { @@ -311,9 +314,14 @@ int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) { pData[i].angularVelocity.x = states[i].angularVelocity.x; pData[i].angularVelocity.y = states[i].angularVelocity.y; pData[i].angularVelocity.z = states[i].angularVelocity.z; - Input::GameController::CalculateOrientation(pData[i].acceleration, pData[i].angularVelocity, - 1.0f / controller->accel_poll_rate, - pData[i].orientation); + if (engine) { + const auto accel_poll_rate = engine->GetAccelPollRate(); + if (accel_poll_rate != 0.0f) { + GameController::CalculateOrientation(pData[i].acceleration, + pData[i].angularVelocity, + 1.0f / accel_poll_rate, pData[i].orientation); + } + } pData[i].touchData.touchNum = (states[i].touchpad[0].state ? 1 : 0) + (states[i].touchpad[1].state ? 1 : 0); pData[i].touchData.touch[0].x = states[i].touchpad[0].x; @@ -356,7 +364,8 @@ int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) { if (handle == ORBIS_PAD_ERROR_DEVICE_NO_HANDLE) { return ORBIS_PAD_ERROR_INVALID_HANDLE; } - auto* controller = Common::Singleton::Instance(); + auto* controller = Common::Singleton::Instance(); + const auto* engine = controller->GetEngine(); int connectedCount = 0; bool isConnected = false; Input::State state; @@ -374,9 +383,13 @@ int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) { pData->angularVelocity.x = state.angularVelocity.x; pData->angularVelocity.y = state.angularVelocity.y; pData->angularVelocity.z = state.angularVelocity.z; - Input::GameController::CalculateOrientation(pData->acceleration, pData->angularVelocity, - 1.0f / controller->accel_poll_rate, - pData->orientation); + if (engine) { + const auto accel_poll_rate = engine->GetAccelPollRate(); + if (accel_poll_rate != 0.0f) { + GameController::CalculateOrientation(pData->acceleration, pData->angularVelocity, + 1.0f / accel_poll_rate, pData->orientation); + } + } pData->touchData.touchNum = (state.touchpad[0].state ? 1 : 0) + (state.touchpad[1].state ? 1 : 0); pData->touchData.touch[0].x = state.touchpad[0].x; @@ -468,7 +481,7 @@ int PS4_SYSV_ABI scePadSetLightBar(s32 handle, const OrbisPadLightBarParam* pPar return ORBIS_PAD_ERROR_INVALID_LIGHTBAR_SETTING; } - auto* controller = Common::Singleton::Instance(); + auto* controller = Common::Singleton::Instance(); controller->SetLightBarRGB(pParam->r, pParam->g, pParam->b); return ORBIS_OK; } @@ -536,7 +549,7 @@ int PS4_SYSV_ABI scePadSetVibration(s32 handle, const OrbisPadVibrationParam* pP if (pParam != nullptr) { LOG_DEBUG(Lib_Pad, "scePadSetVibration called handle = {} data = {} , {}", handle, pParam->smallMotor, pParam->largeMotor); - auto* controller = Common::Singleton::Instance(); + auto* controller = Common::Singleton::Instance(); controller->SetVibration(pParam->smallMotor, pParam->largeMotor); return ORBIS_OK; } diff --git a/src/input/controller.cpp b/src/input/controller.cpp index eb43e6adf..71f0b0c09 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -10,6 +10,55 @@ namespace Input { +using Libraries::Pad::OrbisPadButtonDataOffset; + +void State::OnButton(OrbisPadButtonDataOffset button, bool isPressed) { + if (isPressed) { + buttonsState |= button; + } else { + buttonsState &= ~button; + } +} + +void State::OnAxis(Axis axis, int value) { + const auto toggle = [&](const auto button) { + if (value > 0) { + buttonsState |= button; + } else { + buttonsState &= ~button; + } + }; + switch (axis) { + case Axis::TriggerLeft: + toggle(OrbisPadButtonDataOffset::L2); + break; + case Axis::TriggerRight: + toggle(OrbisPadButtonDataOffset::R2); + break; + default: + break; + } + axes[static_cast(axis)] = value; +} + +void State::OnTouchpad(int touchIndex, bool isDown, float x, float y) { + touchpad[touchIndex].state = isDown; + touchpad[touchIndex].x = static_cast(x * 1920); + touchpad[touchIndex].y = static_cast(y * 941); +} + +void State::OnGyro(const float gyro[3]) { + angularVelocity.x = gyro[0]; + angularVelocity.y = gyro[1]; + angularVelocity.z = gyro[2]; +} + +void State::OnAccel(const float accel[3]) { + acceleration.x = accel[0]; + acceleration.y = accel[1]; + acceleration.z = accel[2]; +} + GameController::GameController() { m_states_num = 0; m_last_state = State(); @@ -20,7 +69,7 @@ void GameController::ReadState(State* state, bool* isConnected, int* connectedCo *isConnected = m_connected; *connectedCount = m_connected_count; - *state = GetLastState(); + *state = m_engine && m_connected ? m_engine->ReadState() : GetLastState(); } int GameController::ReadStates(State* states, int states_num, bool* isConnected, @@ -75,45 +124,22 @@ void GameController::AddState(const State& state) { m_states_num++; } -void GameController::CheckButton(int id, Libraries::Pad::OrbisPadButtonDataOffset button, - bool is_pressed) { +void GameController::CheckButton(int id, OrbisPadButtonDataOffset button, bool is_pressed) { std::scoped_lock lock{m_mutex}; auto state = GetLastState(); + state.time = Libraries::Kernel::sceKernelGetProcessTime(); - if (is_pressed) { - state.buttonsState |= button; - } else { - state.buttonsState &= ~button; - } + state.OnButton(button, is_pressed); AddState(state); } void GameController::Axis(int id, Input::Axis axis, int value) { - using Libraries::Pad::OrbisPadButtonDataOffset; - std::scoped_lock lock{m_mutex}; auto state = GetLastState(); state.time = Libraries::Kernel::sceKernelGetProcessTime(); - int axis_id = static_cast(axis); - state.axes[axis_id] = value; - - if (axis == Input::Axis::TriggerLeft) { - if (value > 0) { - state.buttonsState |= OrbisPadButtonDataOffset::L2; - } else { - state.buttonsState &= ~OrbisPadButtonDataOffset::L2; - } - } - - if (axis == Input::Axis::TriggerRight) { - if (value > 0) { - state.buttonsState |= OrbisPadButtonDataOffset::R2; - } else { - state.buttonsState &= ~OrbisPadButtonDataOffset::R2; - } - } + state.OnAxis(axis, value); AddState(state); } @@ -124,9 +150,7 @@ void GameController::Gyro(int id, const float gyro[3]) { state.time = Libraries::Kernel::sceKernelGetProcessTime(); // Update the angular velocity (gyro data) - state.angularVelocity.x = gyro[0]; // X-axis - state.angularVelocity.y = gyro[1]; // Y-axis - state.angularVelocity.z = gyro[2]; // Z-axis + state.OnGyro(gyro); AddState(state); } @@ -136,9 +160,7 @@ void GameController::Acceleration(int id, const float acceleration[3]) { state.time = Libraries::Kernel::sceKernelGetProcessTime(); // Update the acceleration values - state.acceleration.x = acceleration[0]; // X-axis - state.acceleration.y = acceleration[1]; // Y-axis - state.acceleration.z = acceleration[2]; // Z-axis + state.OnAccel(acceleration); AddState(state); } @@ -211,62 +233,48 @@ void GameController::CalculateOrientation(Libraries::Pad::OrbisFVector3& acceler } void GameController::SetLightBarRGB(u8 r, u8 g, u8 b) { - if (m_sdl_gamepad != nullptr) { - SDL_SetGamepadLED(m_sdl_gamepad, r, g, b); + if (!m_engine) { + return; } + std::scoped_lock _{m_mutex}; + m_engine->SetLightBarRGB(r, g, b); } -bool GameController::SetVibration(u8 smallMotor, u8 largeMotor) { - if (m_sdl_gamepad != nullptr) { - return SDL_RumbleGamepad(m_sdl_gamepad, (smallMotor / 255.0f) * 0xFFFF, - (largeMotor / 255.0f) * 0xFFFF, -1); +void GameController::SetVibration(u8 smallMotor, u8 largeMotor) { + if (!m_engine) { + return; } - return true; + std::scoped_lock _{m_mutex}; + m_engine->SetVibration(smallMotor, largeMotor); } void GameController::SetTouchpadState(int touchIndex, bool touchDown, float x, float y) { if (touchIndex < 2) { std::scoped_lock lock{m_mutex}; auto state = GetLastState(); - state.time = Libraries::Kernel::sceKernelGetProcessTime(); - state.touchpad[touchIndex].state = touchDown; - state.touchpad[touchIndex].x = static_cast(x * 1920); - state.touchpad[touchIndex].y = static_cast(y * 941); + state.time = Libraries::Kernel::sceKernelGetProcessTime(); + state.OnTouchpad(touchIndex, touchDown, x, y); AddState(state); } } -void GameController::TryOpenSDLController() { - if (m_sdl_gamepad == nullptr || !SDL_GamepadConnected(m_sdl_gamepad)) { - int gamepad_count; - SDL_JoystickID* gamepads = SDL_GetGamepads(&gamepad_count); - m_sdl_gamepad = gamepad_count > 0 ? SDL_OpenGamepad(gamepads[0]) : nullptr; - if (Config::getIsMotionControlsEnabled()) { - if (SDL_SetGamepadSensorEnabled(m_sdl_gamepad, SDL_SENSOR_GYRO, true)) { - gyro_poll_rate = SDL_GetGamepadSensorDataRate(m_sdl_gamepad, SDL_SENSOR_GYRO); - LOG_INFO(Input, "Gyro initialized, poll rate: {}", gyro_poll_rate); - } else { - LOG_ERROR(Input, "Failed to initialize gyro controls for gamepad"); - } - if (SDL_SetGamepadSensorEnabled(m_sdl_gamepad, SDL_SENSOR_ACCEL, true)) { - accel_poll_rate = SDL_GetGamepadSensorDataRate(m_sdl_gamepad, SDL_SENSOR_ACCEL); - LOG_INFO(Input, "Accel initialized, poll rate: {}", accel_poll_rate); - } else { - LOG_ERROR(Input, "Failed to initialize accel controls for gamepad"); - } - } - - SDL_free(gamepads); - - SetLightBarRGB(0, 0, 255); +void GameController::SetEngine(std::unique_ptr engine) { + std::scoped_lock _{m_mutex}; + m_engine = std::move(engine); + if (m_engine) { + m_engine->Init(); } } +Engine* GameController::GetEngine() { + return m_engine.get(); +} + u32 GameController::Poll() { - std::scoped_lock lock{m_mutex}; if (m_connected) { + std::scoped_lock lock{m_mutex}; auto time = Libraries::Kernel::sceKernelGetProcessTime(); if (m_states_num == 0) { auto diff = (time - m_last_state.time) / 1000; diff --git a/src/input/controller.h b/src/input/controller.h index c6fc02c24..a45e71d77 100644 --- a/src/input/controller.h +++ b/src/input/controller.h @@ -3,12 +3,12 @@ #pragma once +#include +#include #include #include "common/types.h" #include "core/libraries/pad/pad.h" -struct SDL_Gamepad; - namespace Input { enum class Axis { @@ -28,7 +28,14 @@ struct TouchpadEntry { u16 y{}; }; -struct State { +class State { +public: + void OnButton(Libraries::Pad::OrbisPadButtonDataOffset, bool); + void OnAxis(Axis, int); + void OnTouchpad(int touchIndex, bool isDown, float x, float y); + void OnGyro(const float[3]); + void OnAccel(const float[3]); + Libraries::Pad::OrbisPadButtonDataOffset buttonsState{}; u64 time = 0; int axes[static_cast(Axis::AxisMax)] = {128, 128, 128, 128, 0, 0}; @@ -38,9 +45,19 @@ struct State { Libraries::Pad::OrbisFQuaternion orientation = {0.0f, 0.0f, 0.0f, 1.0f}; }; +class Engine { +public: + virtual ~Engine() = default; + virtual void Init() = 0; + virtual void SetLightBarRGB(u8 r, u8 g, u8 b) = 0; + virtual void SetVibration(u8 smallMotor, u8 largeMotor) = 0; + virtual State ReadState() = 0; + virtual float GetAccelPollRate() const = 0; + virtual float GetGyroPollRate() const = 0; +}; + inline int GetAxis(int min, int max, int value) { - int v = (255 * (value - min)) / (max - min); - return (v < 0 ? 0 : (v > 255 ? 255 : v)); + return std::clamp((255 * (value - min)) / (max - min), 0, 255); } constexpr u32 MAX_STATES = 64; @@ -59,13 +76,12 @@ public: void Gyro(int id, const float gyro[3]); void Acceleration(int id, const float acceleration[3]); void SetLightBarRGB(u8 r, u8 g, u8 b); - bool SetVibration(u8 smallMotor, u8 largeMotor); + void SetVibration(u8 smallMotor, u8 largeMotor); void SetTouchpadState(int touchIndex, bool touchDown, float x, float y); - void TryOpenSDLController(); + void SetEngine(std::unique_ptr); + Engine* GetEngine(); u32 Poll(); - float gyro_poll_rate; - float accel_poll_rate; static void CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration, Libraries::Pad::OrbisFVector3& angularVelocity, float deltaTime, @@ -85,7 +101,7 @@ private: std::array m_states; std::array m_private; - SDL_Gamepad* m_sdl_gamepad = nullptr; + std::unique_ptr m_engine = nullptr; }; } // namespace Input diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index b0126def2..d1fe6bbab 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -10,6 +10,7 @@ #include "common/assert.h" #include "common/config.h" +#include "core/libraries/kernel/time.h" #include "core/libraries/pad/pad.h" #include "imgui/renderer/imgui_core.h" #include "input/controller.h" @@ -20,47 +21,200 @@ #include #endif +namespace Input { + +using Libraries::Pad::OrbisPadButtonDataOffset; + +static OrbisPadButtonDataOffset SDLGamepadToOrbisButton(u8 button) { + using OPBDO = OrbisPadButtonDataOffset; + + switch (button) { + case SDL_GAMEPAD_BUTTON_DPAD_DOWN: + return OPBDO::Down; + case SDL_GAMEPAD_BUTTON_DPAD_UP: + return OPBDO::Up; + case SDL_GAMEPAD_BUTTON_DPAD_LEFT: + return OPBDO::Left; + case SDL_GAMEPAD_BUTTON_DPAD_RIGHT: + return OPBDO::Right; + case SDL_GAMEPAD_BUTTON_SOUTH: + return OPBDO::Cross; + case SDL_GAMEPAD_BUTTON_NORTH: + return OPBDO::Triangle; + case SDL_GAMEPAD_BUTTON_WEST: + return OPBDO::Square; + case SDL_GAMEPAD_BUTTON_EAST: + return OPBDO::Circle; + case SDL_GAMEPAD_BUTTON_START: + return OPBDO::Options; + case SDL_GAMEPAD_BUTTON_TOUCHPAD: + return OPBDO::TouchPad; + case SDL_GAMEPAD_BUTTON_BACK: + return OPBDO::TouchPad; + case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER: + return OPBDO::L1; + case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER: + return OPBDO::R1; + case SDL_GAMEPAD_BUTTON_LEFT_STICK: + return OPBDO::L3; + case SDL_GAMEPAD_BUTTON_RIGHT_STICK: + return OPBDO::R3; + default: + return OPBDO::None; + } +} + +static SDL_GamepadAxis InputAxisToSDL(Axis axis) { + switch (axis) { + case Axis::LeftX: + return SDL_GAMEPAD_AXIS_LEFTX; + case Axis::LeftY: + return SDL_GAMEPAD_AXIS_LEFTY; + case Axis::RightX: + return SDL_GAMEPAD_AXIS_RIGHTX; + case Axis::RightY: + return SDL_GAMEPAD_AXIS_RIGHTY; + case Axis::TriggerLeft: + return SDL_GAMEPAD_AXIS_LEFT_TRIGGER; + case Axis::TriggerRight: + return SDL_GAMEPAD_AXIS_RIGHT_TRIGGER; + default: + UNREACHABLE(); + } +} + +SDLInputEngine::~SDLInputEngine() { + if (m_gamepad) { + SDL_CloseGamepad(m_gamepad); + } +} + +void SDLInputEngine::Init() { + if (m_gamepad) { + SDL_CloseGamepad(m_gamepad); + m_gamepad = nullptr; + } + int gamepad_count; + SDL_JoystickID* gamepads = SDL_GetGamepads(&gamepad_count); + if (!gamepads) { + LOG_ERROR(Input, "Cannot get gamepad list: {}", SDL_GetError()); + return; + } + if (gamepad_count == 0) { + LOG_INFO(Input, "No gamepad found!"); + SDL_free(gamepads); + return; + } + LOG_INFO(Input, "Got {} gamepads. Opening the first one.", gamepad_count); + if (!(m_gamepad = SDL_OpenGamepad(gamepads[0]))) { + LOG_ERROR(Input, "Failed to open gamepad 0: {}", SDL_GetError()); + SDL_free(gamepads); + return; + } + if (Config::getIsMotionControlsEnabled()) { + if (SDL_SetGamepadSensorEnabled(m_gamepad, SDL_SENSOR_GYRO, true)) { + m_gyro_poll_rate = SDL_GetGamepadSensorDataRate(m_gamepad, SDL_SENSOR_GYRO); + LOG_INFO(Input, "Gyro initialized, poll rate: {}", m_gyro_poll_rate); + } else { + LOG_ERROR(Input, "Failed to initialize gyro controls for gamepad"); + } + if (SDL_SetGamepadSensorEnabled(m_gamepad, SDL_SENSOR_ACCEL, true)) { + m_accel_poll_rate = SDL_GetGamepadSensorDataRate(m_gamepad, SDL_SENSOR_ACCEL); + LOG_INFO(Input, "Accel initialized, poll rate: {}", m_accel_poll_rate); + } else { + LOG_ERROR(Input, "Failed to initialize accel controls for gamepad"); + }; + } + SDL_free(gamepads); + SetLightBarRGB(0, 0, 255); +} + +void SDLInputEngine::SetLightBarRGB(u8 r, u8 g, u8 b) { + if (m_gamepad) { + SDL_SetGamepadLED(m_gamepad, r, g, b); + } +} + +void SDLInputEngine::SetVibration(u8 smallMotor, u8 largeMotor) { + if (m_gamepad) { + const auto low_freq = (smallMotor / 255.0f) * 0xFFFF; + const auto high_freq = (largeMotor / 255.0f) * 0xFFFF; + SDL_RumbleGamepad(m_gamepad, low_freq, high_freq, -1); + } +} + +State SDLInputEngine::ReadState() { + State state{}; + state.time = Libraries::Kernel::sceKernelGetProcessTime(); + + // Buttons + for (u8 i = 0; i < SDL_GAMEPAD_BUTTON_COUNT; ++i) { + auto orbisButton = SDLGamepadToOrbisButton(i); + if (orbisButton == OrbisPadButtonDataOffset::None) { + continue; + } + state.OnButton(orbisButton, SDL_GetGamepadButton(m_gamepad, (SDL_GamepadButton)i)); + } + + // Axes + for (int i = 0; i < static_cast(Axis::AxisMax); ++i) { + const auto axis = static_cast(i); + const auto value = SDL_GetGamepadAxis(m_gamepad, InputAxisToSDL(axis)); + switch (axis) { + case Axis::TriggerLeft: + case Axis::TriggerRight: + state.OnAxis(axis, GetAxis(0, 0x8000, value)); + break; + default: + state.OnAxis(axis, GetAxis(-0x8000, 0x8000, value)); + break; + } + } + + // Touchpad + if (SDL_GetNumGamepadTouchpads(m_gamepad) > 0) { + for (int finger = 0; finger < 2; ++finger) { + bool down; + float x, y; + if (SDL_GetGamepadTouchpadFinger(m_gamepad, 0, finger, &down, &x, &y, NULL)) { + state.OnTouchpad(finger, down, x, y); + } + } + } + + // Gyro + if (SDL_GamepadHasSensor(m_gamepad, SDL_SENSOR_GYRO)) { + float gyro[3]; + if (SDL_GetGamepadSensorData(m_gamepad, SDL_SENSOR_GYRO, gyro, 3)) { + state.OnGyro(gyro); + } + } + + // Accel + if (SDL_GamepadHasSensor(m_gamepad, SDL_SENSOR_ACCEL)) { + float accel[3]; + if (SDL_GetGamepadSensorData(m_gamepad, SDL_SENSOR_ACCEL, accel, 3)) { + state.OnAccel(accel); + } + } + + return state; +} + +float SDLInputEngine::GetGyroPollRate() const { + return m_gyro_poll_rate; +} + +float SDLInputEngine::GetAccelPollRate() const { + return m_accel_poll_rate; +} + +} // namespace Input + namespace Frontend { using namespace Libraries::Pad; -static OrbisPadButtonDataOffset SDLGamepadToOrbisButton(u8 button) { - switch (button) { - case SDL_GAMEPAD_BUTTON_DPAD_DOWN: - return OrbisPadButtonDataOffset::Down; - case SDL_GAMEPAD_BUTTON_DPAD_UP: - return OrbisPadButtonDataOffset::Up; - case SDL_GAMEPAD_BUTTON_DPAD_LEFT: - return OrbisPadButtonDataOffset::Left; - case SDL_GAMEPAD_BUTTON_DPAD_RIGHT: - return OrbisPadButtonDataOffset::Right; - case SDL_GAMEPAD_BUTTON_SOUTH: - return OrbisPadButtonDataOffset::Cross; - case SDL_GAMEPAD_BUTTON_NORTH: - return OrbisPadButtonDataOffset::Triangle; - case SDL_GAMEPAD_BUTTON_WEST: - return OrbisPadButtonDataOffset::Square; - case SDL_GAMEPAD_BUTTON_EAST: - return OrbisPadButtonDataOffset::Circle; - case SDL_GAMEPAD_BUTTON_START: - return OrbisPadButtonDataOffset::Options; - case SDL_GAMEPAD_BUTTON_TOUCHPAD: - return OrbisPadButtonDataOffset::TouchPad; - case SDL_GAMEPAD_BUTTON_BACK: - return OrbisPadButtonDataOffset::TouchPad; - case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER: - return OrbisPadButtonDataOffset::L1; - case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER: - return OrbisPadButtonDataOffset::R1; - case SDL_GAMEPAD_BUTTON_LEFT_STICK: - return OrbisPadButtonDataOffset::L3; - case SDL_GAMEPAD_BUTTON_RIGHT_STICK: - return OrbisPadButtonDataOffset::R3; - default: - return OrbisPadButtonDataOffset::None; - } -} - static Uint32 SDLCALL PollController(void* userdata, SDL_TimerID timer_id, Uint32 interval) { auto* controller = reinterpret_cast(userdata); return controller->Poll(); @@ -112,7 +266,7 @@ WindowSDL::WindowSDL(s32 width_, s32 height_, Input::GameController* controller_ SDL_SetWindowFullscreen(window, Config::getIsFullscreen()); SDL_InitSubSystem(SDL_INIT_GAMEPAD); - controller->TryOpenSDLController(); + controller->SetEngine(std::make_unique()); #if defined(SDL_PLATFORM_WIN32) window_info.type = WindowSystemType::Windows; @@ -422,7 +576,7 @@ void WindowSDL::OnGamepadEvent(const SDL_Event* event) { switch (event->type) { case SDL_EVENT_GAMEPAD_ADDED: case SDL_EVENT_GAMEPAD_REMOVED: - controller->TryOpenSDLController(); + controller->SetEngine(std::make_unique()); break; case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN: case SDL_EVENT_GAMEPAD_TOUCHPAD_UP: @@ -433,7 +587,7 @@ void WindowSDL::OnGamepadEvent(const SDL_Event* event) { break; case SDL_EVENT_GAMEPAD_BUTTON_DOWN: case SDL_EVENT_GAMEPAD_BUTTON_UP: { - button = SDLGamepadToOrbisButton(event->gbutton.button); + button = Input::SDLGamepadToOrbisButton(event->gbutton.button); if (button == OrbisPadButtonDataOffset::None) { break; } diff --git a/src/sdl_window.h b/src/sdl_window.h index 78d4bbc39..3ab3c3613 100644 --- a/src/sdl_window.h +++ b/src/sdl_window.h @@ -5,14 +5,32 @@ #include #include "common/types.h" +#include "input/controller.h" struct SDL_Window; struct SDL_Gamepad; union SDL_Event; namespace Input { -class GameController; -} + +class SDLInputEngine : public Engine { +public: + ~SDLInputEngine() override; + void Init() override; + void SetLightBarRGB(u8 r, u8 g, u8 b) override; + void SetVibration(u8 smallMotor, u8 largeMotor) override; + float GetGyroPollRate() const override; + float GetAccelPollRate() const override; + State ReadState() override; + +private: + SDL_Gamepad* m_gamepad = nullptr; + + float m_gyro_poll_rate{}; + float m_accel_poll_rate{}; +}; + +} // namespace Input namespace Frontend { From 40385e13e7ca96f19e004a0c21b718d9bf701570 Mon Sep 17 00:00:00 2001 From: tomboylover93 <95257311+tomboylover93@users.noreply.github.com> Date: Fri, 17 Jan 2025 23:08:20 -0800 Subject: [PATCH 431/549] qt: Improve user experience on Steam Deck and window managers (#2103) --- src/qt_gui/cheats_patches.cpp | 6 + src/qt_gui/settings_dialog.cpp | 16 - src/qt_gui/settings_dialog.ui | 629 ++++++++++++++++----------------- 3 files changed, 310 insertions(+), 341 deletions(-) diff --git a/src/qt_gui/cheats_patches.cpp b/src/qt_gui/cheats_patches.cpp index 2fea0b6ea..13157aa3a 100644 --- a/src/qt_gui/cheats_patches.cpp +++ b/src/qt_gui/cheats_patches.cpp @@ -188,8 +188,12 @@ void CheatsPatches::setupUI() { } }); + QPushButton* closeButton = new QPushButton(tr("Close")); + connect(closeButton, &QPushButton::clicked, [this]() { QWidget::close(); }); + controlLayout->addWidget(downloadButton); controlLayout->addWidget(deleteCheatButton); + controlLayout->addWidget(closeButton); cheatsLayout->addLayout(controlLayout); cheatsTab->setLayout(cheatsLayout); @@ -464,6 +468,8 @@ void CheatsPatches::onSaveButtonClicked() { } else { QMessageBox::information(this, tr("Success"), tr("Options saved successfully.")); } + + QWidget::close(); } QCheckBox* CheatsPatches::findCheckBoxByName(const QString& name) { diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index a4b584294..175c8c51d 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -522,22 +522,6 @@ bool SettingsDialog::eventFilter(QObject* obj, QEvent* event) { } else { ui->descriptionText->setText(defaultTextEdit); } - - // if the text exceeds the size of the box, it will increase the size - QRect currentGeometry = this->geometry(); - int newWidth = currentGeometry.width(); - - int documentHeight = ui->descriptionText->document()->size().height(); - int visibleHeight = ui->descriptionText->viewport()->height(); - if (documentHeight > visibleHeight) { - ui->descriptionText->setMaximumSize(16777215, 110); - this->setGeometry(currentGeometry.x(), currentGeometry.y(), newWidth, - currentGeometry.height() + 40); - } else { - ui->descriptionText->setMaximumSize(16777215, 70); - this->setGeometry(currentGeometry.x(), currentGeometry.y(), newWidth, - initialHeight); - } return true; } } diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index 8d68d1c90..c084d4849 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -12,7 +12,7 @@ 0 0 970 - 750 + 820 @@ -68,7 +68,7 @@ 0 0 946 - 586 + 611 @@ -77,43 +77,6 @@ 0 - - - - - - System - - - - - - Console Language - - - - - - - - - - - - Emulator Language - - - - - - - - - - - - - @@ -217,246 +180,6 @@ - - - - 6 - - - QLayout::SizeConstraint::SetDefaultConstraint - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - Update - - - - 10 - - - 1 - - - 11 - - - 11 - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - Update Channel - - - - 7 - - - 11 - - - 11 - - - 11 - - - 11 - - - - - - 0 - 0 - - - - - Release - - - - - Nightly - - - - - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - Check for Updates - - - - - - - - 0 - 0 - - - - - 11 - false - - - - Check for Updates at Startup - - - - - - - - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - Game Compatibility - - - - 10 - - - 1 - - - 11 - - - - - Display Compatibility Data - - - - - - - Update Compatibility Database On Startup - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - Update Compatibility Database - - - - - - - - @@ -627,6 +350,283 @@ + + + + + + System + + + + + + Console Language + + + + + + + + + + + + Emulator Language + + + + + + + + + + + + + + + + + 6 + + + QLayout::SizeConstraint::SetDefaultConstraint + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Update + + + + 10 + + + 1 + + + 11 + + + 190 + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Update Channel + + + + 7 + + + 11 + + + 11 + + + 11 + + + 11 + + + + + + 0 + 0 + + + + + Release + + + + + Nightly + + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Check for Updates + + + + + + + + 0 + 0 + + + + + 11 + false + + + + Check for Updates at Startup + + + + + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Game Compatibility + + + + 10 + + + 1 + + + 11 + + + + + Display Compatibility Data + + + + + + + Update Compatibility Database On Startup + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Update Compatibility Database + + + + + + + + @@ -645,12 +645,12 @@ 0 0 946 - 586 + 605 - + @@ -664,17 +664,14 @@ Cursor - - - 0 - + 11 11 - + true @@ -701,7 +698,7 @@ - + true @@ -836,7 +833,7 @@ true - + 0 0 @@ -872,6 +869,12 @@ true + + + 0 + 0 + + 0 @@ -885,23 +888,6 @@ - - - - - - Qt::Orientation::Horizontal - - - - 40 - 20 - - - - - - @@ -943,7 +929,7 @@ 0 0 946 - 586 + 605 @@ -1124,11 +1110,14 @@ + + true + Advanced - Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop @@ -1194,7 +1183,7 @@ 0 0 946 - 586 + 605 @@ -1233,22 +1222,6 @@ - - - - Qt::Orientation::Horizontal - - - QSizePolicy::Policy::Preferred - - - - 40 - 20 - - - - @@ -1445,10 +1418,16 @@ + + + 0 + 0 + + 16777215 - 70 + 120 From 9a956f5ed00584cbdf70240826c01445ee083f1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quang=20Ng=C3=B4?= Date: Sat, 18 Jan 2025 14:08:45 +0700 Subject: [PATCH 432/549] renderer_vulkan: Clear blank frame (#2095) * renderer_vulkan: Clear blank frame Fix display of garbage images on startup on some drivers. * Remove duplicated attachment declarations * Remove duplicated rendering_info declarations --- .../renderer_vulkan/vk_presenter.cpp | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_presenter.cpp b/src/video_core/renderer_vulkan/vk_presenter.cpp index 0f45574bc..fcdb84676 100644 --- a/src/video_core/renderer_vulkan/vk_presenter.cpp +++ b/src/video_core/renderer_vulkan/vk_presenter.cpp @@ -602,6 +602,23 @@ Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop) .pImageMemoryBarriers = &pre_barrier, }); + const std::array attachments = {vk::RenderingAttachmentInfo{ + .imageView = frame->image_view, + .imageLayout = vk::ImageLayout::eColorAttachmentOptimal, + .loadOp = vk::AttachmentLoadOp::eClear, + .storeOp = vk::AttachmentStoreOp::eStore, + }}; + const vk::RenderingInfo rendering_info{ + .renderArea = + vk::Rect2D{ + .offset = {0, 0}, + .extent = {frame->width, frame->height}, + }, + .layerCount = 1, + .colorAttachmentCount = attachments.size(), + .pColorAttachments = attachments.data(), + }; + if (image_id != VideoCore::NULL_IMAGE_ID) { auto& image = texture_cache.GetImage(image_id); image.Transit(vk::ImageLayout::eShaderReadOnlyOptimal, vk::AccessFlagBits2::eShaderRead, {}, @@ -662,26 +679,13 @@ Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop) cmdbuf.pushConstants(*pp_pipeline_layout, vk::ShaderStageFlagBits::eFragment, 0, sizeof(PostProcessSettings), &pp_settings); - const std::array attachments = {vk::RenderingAttachmentInfo{ - .imageView = frame->image_view, - .imageLayout = vk::ImageLayout::eColorAttachmentOptimal, - .loadOp = vk::AttachmentLoadOp::eClear, - .storeOp = vk::AttachmentStoreOp::eStore, - }}; - - vk::RenderingInfo rendering_info{ - .renderArea = - vk::Rect2D{ - .offset = {0, 0}, - .extent = {frame->width, frame->height}, - }, - .layerCount = 1, - .colorAttachmentCount = attachments.size(), - .pColorAttachments = attachments.data(), - }; cmdbuf.beginRendering(rendering_info); cmdbuf.draw(3, 1, 0, 0); cmdbuf.endRendering(); + } else { + // Fix display of garbage images on startup on some drivers + cmdbuf.beginRendering(rendering_info); + cmdbuf.endRendering(); } const auto post_barrier = From 81ad575b2294224194a365d087b2a6240a0b9161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quang=20Ng=C3=B4?= Date: Sat, 18 Jan 2025 14:47:38 +0700 Subject: [PATCH 433/549] video_core: Use adaptive mutex on Linux (#2105) Fix performance regression with #1973 on SteamDeck --- src/common/adaptive_mutex.h | 27 ++++++++++++++++++++++ src/video_core/buffer_cache/word_manager.h | 7 ++++++ src/video_core/page_manager.h | 7 ++++++ 3 files changed, 41 insertions(+) create mode 100644 src/common/adaptive_mutex.h diff --git a/src/common/adaptive_mutex.h b/src/common/adaptive_mutex.h new file mode 100644 index 000000000..f174f5996 --- /dev/null +++ b/src/common/adaptive_mutex.h @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#ifdef __linux__ +#include +#endif + +namespace Common { + +#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP +class AdaptiveMutex { +public: + void lock() { + pthread_mutex_lock(&mutex); + } + void unlock() { + pthread_mutex_unlock(&mutex); + } + +private: + pthread_mutex_t mutex = PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP; +}; +#endif // PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP + +} // namespace Common diff --git a/src/video_core/buffer_cache/word_manager.h b/src/video_core/buffer_cache/word_manager.h index 7ad33d7a6..5ad724f96 100644 --- a/src/video_core/buffer_cache/word_manager.h +++ b/src/video_core/buffer_cache/word_manager.h @@ -8,6 +8,9 @@ #include #include +#ifdef __linux__ +#include "common/adaptive_mutex.h" +#endif #include "common/spin_lock.h" #include "common/types.h" #include "video_core/page_manager.h" @@ -272,7 +275,11 @@ private: } } +#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP + Common::AdaptiveMutex lock; +#else Common::SpinLock lock; +#endif PageManager* tracker; VAddr cpu_addr = 0; WordsArray cpu; diff --git a/src/video_core/page_manager.h b/src/video_core/page_manager.h index f44307f92..f6bae9641 100644 --- a/src/video_core/page_manager.h +++ b/src/video_core/page_manager.h @@ -5,6 +5,9 @@ #include #include +#ifdef __linux__ +#include "common/adaptive_mutex.h" +#endif #include "common/spin_lock.h" #include "common/types.h" @@ -36,7 +39,11 @@ private: std::unique_ptr impl; Vulkan::Rasterizer* rasterizer; boost::icl::interval_map cached_pages; +#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP + Common::AdaptiveMutex lock; +#else Common::SpinLock lock; +#endif }; } // namespace VideoCore From 12364b197a87ec79cd476eb0e73d1015ef02cac0 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sat, 18 Jan 2025 01:13:16 -0800 Subject: [PATCH 434/549] renderer_vulkan: Remove swapchain image reinterpretation. (#2176) --- .../renderer_vulkan/vk_instance.cpp | 1 - .../renderer_vulkan/vk_presenter.cpp | 2 +- .../renderer_vulkan/vk_swapchain.cpp | 20 ++++--------------- src/video_core/renderer_vulkan/vk_swapchain.h | 15 -------------- 4 files changed, 5 insertions(+), 33 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index d9577a612..15bd573ef 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -271,7 +271,6 @@ bool Instance::CreateDevice() { legacy_vertex_attributes = add_extension(VK_EXT_LEGACY_VERTEX_ATTRIBUTES_EXTENSION_NAME); image_load_store_lod = add_extension(VK_AMD_SHADER_IMAGE_LOAD_STORE_LOD_EXTENSION_NAME); amd_gcn_shader = add_extension(VK_AMD_GCN_SHADER_EXTENSION_NAME); - add_extension(VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME); // These extensions are promoted by Vulkan 1.3, but for greater compatibility we use Vulkan 1.2 // with extensions. diff --git a/src/video_core/renderer_vulkan/vk_presenter.cpp b/src/video_core/renderer_vulkan/vk_presenter.cpp index fcdb84676..ce2e02a43 100644 --- a/src/video_core/renderer_vulkan/vk_presenter.cpp +++ b/src/video_core/renderer_vulkan/vk_presenter.cpp @@ -380,7 +380,7 @@ void Presenter::RecreateFrame(Frame* frame, u32 width, u32 height) { const vk::ImageViewCreateInfo view_info = { .image = frame->image, .viewType = vk::ImageViewType::e2D, - .format = swapchain.GetViewFormat(), + .format = format, .subresourceRange{ .aspectMask = vk::ImageAspectFlagBits::eColor, .baseMipLevel = 0, diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index 438fe30ce..5467a5733 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -17,7 +17,7 @@ Swapchain::Swapchain(const Instance& instance_, const Frontend::WindowSDL& windo FindPresentFormat(); Create(window.GetWidth(), window.GetHeight()); - ImGui::Core::Initialize(instance, window, image_count, view_format); + ImGui::Core::Initialize(instance, window, image_count, surface_format.format); } Swapchain::~Swapchain() { @@ -57,17 +57,7 @@ void Swapchain::Create(u32 width_, u32 height_) { const u32 queue_family_indices_count = exclusive ? 1u : 2u; const vk::SharingMode sharing_mode = exclusive ? vk::SharingMode::eExclusive : vk::SharingMode::eConcurrent; - const vk::Format view_formats[2] = { - surface_format.format, - view_format, - }; - const vk::ImageFormatListCreateInfo format_list = { - .viewFormatCount = 2, - .pViewFormats = view_formats, - }; const vk::SwapchainCreateInfoKHR swapchain_info = { - .pNext = &format_list, - .flags = vk::SwapchainCreateFlagBitsKHR::eMutableFormat, .surface = surface, .minImageCount = image_count, .imageFormat = surface_format.format, @@ -157,22 +147,20 @@ void Swapchain::FindPresentFormat() { // If there is a single undefined surface format, the device doesn't care, so we'll just use // RGBA sRGB. if (formats[0].format == vk::Format::eUndefined) { - surface_format.format = vk::Format::eR8G8B8A8Srgb; + surface_format.format = vk::Format::eR8G8B8A8Unorm; surface_format.colorSpace = vk::ColorSpaceKHR::eSrgbNonlinear; - view_format = FormatToUnorm(surface_format.format); return; } // Try to find a suitable format. for (const vk::SurfaceFormatKHR& sformat : formats) { vk::Format format = sformat.format; - if (format != vk::Format::eR8G8B8A8Srgb && format != vk::Format::eB8G8R8A8Srgb) { + if (format != vk::Format::eR8G8B8A8Unorm && format != vk::Format::eB8G8R8A8Unorm) { continue; } surface_format.format = format; surface_format.colorSpace = sformat.colorSpace; - view_format = FormatToUnorm(surface_format.format); return; } @@ -274,7 +262,7 @@ void Swapchain::SetupImages() { auto [im_view_result, im_view] = device.createImageView(vk::ImageViewCreateInfo{ .image = images[i], .viewType = vk::ImageViewType::e2D, - .format = FormatToUnorm(surface_format.format), + .format = surface_format.format, .subresourceRange = { .aspectMask = vk::ImageAspectFlagBits::eColor, diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h index 9da75c758..f5cf9f0d2 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.h +++ b/src/video_core/renderer_vulkan/vk_swapchain.h @@ -17,17 +17,6 @@ namespace Vulkan { class Instance; class Scheduler; -inline vk::Format FormatToUnorm(vk::Format fmt) { - switch (fmt) { - case vk::Format::eR8G8B8A8Srgb: - return vk::Format::eR8G8B8A8Unorm; - case vk::Format::eB8G8R8A8Srgb: - return vk::Format::eB8G8R8A8Unorm; - default: - UNREACHABLE(); - } -} - class Swapchain { public: explicit Swapchain(const Instance& instance, const Frontend::WindowSDL& window); @@ -61,10 +50,6 @@ public: return surface_format; } - vk::Format GetViewFormat() const { - return view_format; - } - vk::SwapchainKHR GetHandle() const { return swapchain; } From d3615796186b44902599ca2a41c61dd0b73b3eb5 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sat, 18 Jan 2025 01:35:44 -0800 Subject: [PATCH 435/549] texture_cache: Fix image mip overlap. (#2177) --- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 2 -- src/video_core/texture_cache/image_info.cpp | 2 +- src/video_core/texture_cache/image_view.cpp | 9 ++++++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 07369c620..88b510eca 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -802,8 +802,6 @@ void Rasterizer::BeginRendering(const GraphicsPipeline& pipeline, RenderState& s const auto mip = view.info.range.base.level; state.width = std::min(state.width, std::max(image.info.size.width >> mip, 1u)); state.height = std::min(state.height, std::max(image.info.size.height >> mip, 1u)); - ASSERT(old_img.info.size.width == state.width); - ASSERT(old_img.info.size.height == state.height); } auto& image = texture_cache.GetImage(image_id); if (image.binding.force_general) { diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index 07a0488f3..a9ed76960 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -219,7 +219,7 @@ int ImageInfo::IsMipOf(const ImageInfo& info) const { return -1; } - if (IsTilingCompatible(info.tiling_idx, tiling_idx)) { + if (!IsTilingCompatible(info.tiling_idx, tiling_idx)) { return -1; } diff --git a/src/video_core/texture_cache/image_view.cpp b/src/video_core/texture_cache/image_view.cpp index 6b1349386..7befb5259 100644 --- a/src/video_core/texture_cache/image_view.cpp +++ b/src/video_core/texture_cache/image_view.cpp @@ -114,9 +114,12 @@ ImageView::ImageView(const Vulkan::Instance& instance, const ImageViewInfo& info const auto view_aspect = aspect & vk::ImageAspectFlagBits::eDepth ? "Depth" : aspect & vk::ImageAspectFlagBits::eStencil ? "Stencil" : "Color"; - Vulkan::SetObjectName(instance.GetDevice(), *image_view, "ImageView {}x{}x{} {:#x}:{:#x} ({})", - image.info.size.width, image.info.size.height, image.info.size.depth, - image.info.guest_address, image.info.guest_size, view_aspect); + Vulkan::SetObjectName( + instance.GetDevice(), *image_view, "ImageView {}x{}x{} {:#x}:{:#x} {}:{} {}:{} ({})", + image.info.size.width, image.info.size.height, image.info.size.depth, + image.info.guest_address, image.info.guest_size, info.range.base.level, + info.range.base.level + info.range.extent.levels - 1, info.range.base.layer, + info.range.base.layer + info.range.extent.layers - 1, view_aspect); } ImageView::~ImageView() = default; From c80151addedf25f497ad1064f5b3ab36bdcca28d Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sat, 18 Jan 2025 02:29:19 -0800 Subject: [PATCH 436/549] vk_presenter: Fix splash issues. (#2180) --- src/video_core/renderer_vulkan/vk_presenter.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/video_core/renderer_vulkan/vk_presenter.cpp b/src/video_core/renderer_vulkan/vk_presenter.cpp index ce2e02a43..36d64a5d5 100644 --- a/src/video_core/renderer_vulkan/vk_presenter.cpp +++ b/src/video_core/renderer_vulkan/vk_presenter.cpp @@ -476,7 +476,7 @@ bool Presenter::ShowSplash(Frame* frame /*= nullptr*/) { if (!frame) { if (!splash_img.has_value()) { VideoCore::ImageInfo info{}; - info.pixel_format = vk::Format::eR8G8B8A8Srgb; + info.pixel_format = vk::Format::eR8G8B8A8Unorm; info.type = vk::ImageType::e2D; info.size = VideoCore::Extent3D{splash->GetImageInfo().width, splash->GetImageInfo().height, 1}; @@ -487,6 +487,7 @@ bool Presenter::ShowSplash(Frame* frame /*= nullptr*/) { splash->GetImageInfo().width, splash->GetImageInfo().height, 0); splash_img.emplace(instance, present_scheduler, info); + splash_img->flags &= ~VideoCore::GpuDirty; texture_cache.RefreshImage(*splash_img); splash_img->Transit(vk::ImageLayout::eTransferSrcOptimal, From 1ea5f8f09243b958ff64d879116c89dd24a627ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quang=20Ng=C3=B4?= Date: Sat, 18 Jan 2025 17:48:39 +0700 Subject: [PATCH 437/549] input: Unbroke KBM-only input (#2179) --- src/input/controller.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/input/controller.cpp b/src/input/controller.cpp index 71f0b0c09..ae54553f4 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -69,7 +69,7 @@ void GameController::ReadState(State* state, bool* isConnected, int* connectedCo *isConnected = m_connected; *connectedCount = m_connected_count; - *state = m_engine && m_connected ? m_engine->ReadState() : GetLastState(); + *state = GetLastState(); } int GameController::ReadStates(State* states, int states_num, bool* isConnected, From 3b92cd1c1a68f18b3f68afb58860d3c2e828aff2 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Sat, 18 Jan 2025 13:21:08 +0100 Subject: [PATCH 438/549] CLI: Add argument to pass an argument to the game (#2135) --- src/core/linker.cpp | 10 ++++++++-- src/core/linker.h | 4 ++-- src/emulator.cpp | 13 +++++++++++-- src/emulator.h | 2 +- src/main.cpp | 21 ++++++++++++++++++++- src/qt_gui/main.cpp | 20 +++++++++++++++++++- 6 files changed, 61 insertions(+), 9 deletions(-) diff --git a/src/core/linker.cpp b/src/core/linker.cpp index 28d2eea7b..2461edcb2 100644 --- a/src/core/linker.cpp +++ b/src/core/linker.cpp @@ -52,7 +52,7 @@ Linker::Linker() : memory{Memory::Instance()} {} Linker::~Linker() = default; -void Linker::Execute() { +void Linker::Execute(const std::vector args) { if (Config::debugDump()) { DebugDump(); } @@ -101,7 +101,7 @@ void Linker::Execute() { memory->SetupMemoryRegions(fmem_size, use_extended_mem1, use_extended_mem2); - main_thread.Run([this, module](std::stop_token) { + main_thread.Run([this, module, args](std::stop_token) { Common::SetCurrentThreadName("GAME_MainThread"); LoadSharedLibraries(); @@ -109,6 +109,12 @@ void Linker::Execute() { EntryParams params{}; params.argc = 1; params.argv[0] = "eboot.bin"; + if (!args.empty()) { + params.argc = args.size() + 1; + for (int i = 0; i < args.size() && i < 32; i++) { + params.argv[i + 1] = args[i].c_str(); + } + } params.entry_addr = module->GetEntryAddress(); RunMainEntry(¶ms); }); diff --git a/src/core/linker.h b/src/core/linker.h index 7ef13ae56..00da3a08c 100644 --- a/src/core/linker.h +++ b/src/core/linker.h @@ -49,7 +49,7 @@ class Linker; struct EntryParams { int argc; u32 padding; - const char* argv[3]; + const char* argv[33]; VAddr entry_addr; }; @@ -143,7 +143,7 @@ public: void Relocate(Module* module); bool Resolve(const std::string& name, Loader::SymbolType type, Module* module, Loader::SymbolRecord* return_info); - void Execute(); + void Execute(const std::vector args = {}); void DebugDump(); private: diff --git a/src/emulator.cpp b/src/emulator.cpp index 61d6d3862..e77c2b87f 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -99,7 +99,7 @@ Emulator::~Emulator() { Config::saveMainWindow(config_dir / "config.toml"); } -void Emulator::Run(const std::filesystem::path& file) { +void Emulator::Run(const std::filesystem::path& file, const std::vector args) { // Applications expect to be run from /app0 so mount the file's parent path as app0. auto* mnt = Common::Singleton::Instance(); const auto game_folder = file.parent_path(); @@ -152,6 +152,15 @@ void Emulator::Run(const std::filesystem::path& file) { if (const auto raw_attributes = param_sfo->GetInteger("ATTRIBUTE")) { psf_attributes.raw = *raw_attributes; } + if (!args.empty()) { + int argc = std::min(args.size(), 32); + for (int i = 0; i < argc; i++) { + LOG_INFO(Loader, "Game argument {}: {}", i, args[i]); + } + if (args.size() > 32) { + LOG_ERROR(Loader, "Too many game arguments, only passing the first 32"); + } + } } const auto pic1_path = mnt->GetHostPath("/app0/sce_sys/pic1.png"); @@ -239,7 +248,7 @@ void Emulator::Run(const std::filesystem::path& file) { } #endif - linker->Execute(); + linker->Execute(args); window->InitTimers(); while (window->IsOpen()) { diff --git a/src/emulator.h b/src/emulator.h index a08ab43c3..08c2807a1 100644 --- a/src/emulator.h +++ b/src/emulator.h @@ -25,7 +25,7 @@ public: Emulator(); ~Emulator(); - void Run(const std::filesystem::path& file); + void Run(const std::filesystem::path& file, const std::vector args = {}); void UpdatePlayTime(const std::string& serial); private: diff --git a/src/main.cpp b/src/main.cpp index 54772870c..fad3b1f53 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,6 +29,7 @@ int main(int argc, char* argv[]) { bool has_game_argument = false; std::string game_path; + std::vector game_args{}; // Map of argument strings to lambda functions std::unordered_map> arg_map = { @@ -37,6 +38,9 @@ int main(int argc, char* argv[]) { std::cout << "Usage: shadps4 [options] \n" "Options:\n" " -g, --game Specify game path to launch\n" + " -- ... Parameters passed to the game ELF. " + "Needs to be at the end of the line, and everything after \"--\" is a " + "game argument.\n" " -p, --patch Apply specified patch file\n" " -f, --fullscreen Specify window initial fullscreen " "state. Does not overwrite the config file.\n" @@ -126,6 +130,21 @@ int main(int argc, char* argv[]) { // Assume the last argument is the game file if not specified via -g/--game game_path = argv[i]; has_game_argument = true; + } else if (std::string(argv[i]) == "--") { + if (i + 1 == argc) { + std::cerr << "Warning: -- is set, but no game arguments are added!\n"; + break; + } + for (int j = i + 1; j < argc; j++) { + game_args.push_back(argv[j]); + } + break; + } else if (i + 1 < argc && std::string(argv[i + 1]) == "--") { + if (!has_game_argument) { + game_path = argv[i]; + has_game_argument = true; + } + break; } else { std::cerr << "Unknown argument: " << cur_arg << ", see --help for info.\n"; return 1; @@ -166,7 +185,7 @@ int main(int argc, char* argv[]) { // Run the emulator with the resolved eboot path Core::Emulator emulator; - emulator.Run(eboot_path); + emulator.Run(eboot_path, game_args); return 0; } diff --git a/src/qt_gui/main.cpp b/src/qt_gui/main.cpp index 2d524e199..8babadc35 100644 --- a/src/qt_gui/main.cpp +++ b/src/qt_gui/main.cpp @@ -33,6 +33,7 @@ int main(int argc, char* argv[]) { bool has_command_line_argument = argc > 1; bool show_gui = false, has_game_argument = false; std::string game_path; + std::vector game_args{}; // Map of argument strings to lambda functions std::unordered_map> arg_map = { @@ -43,6 +44,9 @@ int main(int argc, char* argv[]) { " No arguments: Opens the GUI.\n" " -g, --game Specify or " " to launch\n" + " -- ... Parameters passed to the game ELF. " + "Needs to be at the end of the line, and everything after \"--\" is a " + "game argument.\n" " -p, --patch Apply specified patch file\n" " -s, --show-gui Show the GUI\n" " -f, --fullscreen Specify window initial fullscreen " @@ -131,6 +135,20 @@ int main(int argc, char* argv[]) { // Assume the last argument is the game file if not specified via -g/--game game_path = argv[i]; has_game_argument = true; + } else if (std::string(argv[i]) == "--") { + if (i + 1 == argc) { + std::cerr << "Warning: -- is set, but no game arguments are added!\n"; + break; + } + for (int j = i + 1; j < argc; j++) { + game_args.push_back(argv[j]); + } + break; + } else if (i + 1 < argc && std::string(argv[i + 1]) == "--") { + if (!has_game_argument) { + game_path = argv[i]; + has_game_argument = true; + } } else { std::cerr << "Unknown argument: " << cur_arg << ", see --help for info.\n"; return 1; @@ -181,7 +199,7 @@ int main(int argc, char* argv[]) { // Run the emulator with the resolved game path Core::Emulator emulator; - emulator.Run(game_file_path.string()); + emulator.Run(game_file_path.string(), game_args); if (!show_gui) { return 0; // Exit after running the emulator without showing the GUI } From 388548ba470aa68c5fff4e5737e03031e5783f8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A5IGA?= <164882787+Xphalnos@users.noreply.github.com> Date: Sat, 18 Jan 2025 14:02:02 +0100 Subject: [PATCH 439/549] pad: Configurable DeadZone (#2030) --- src/common/config.cpp | 14 ++++++++++++++ src/common/config.h | 2 ++ src/core/libraries/pad/pad.cpp | 8 ++++---- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index 9c842f8b7..ed9af5a72 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -45,6 +45,8 @@ static std::string logFilter; static std::string logType = "async"; static std::string userName = "shadPS4"; static std::string updateChannel; +static u16 deadZoneLeft = 2.0; +static u16 deadZoneRight = 2.0; static std::string backButtonBehavior = "left"; static bool useSpecialPad = false; static int specialPadClass = 1; @@ -140,6 +142,14 @@ bool getEnableDiscordRPC() { return enableDiscordRPC; } +u16 leftDeadZone() { + return deadZoneLeft; +} + +u16 rightDeadZone() { + return deadZoneRight; +} + s16 getCursorState() { return cursorState; } @@ -620,6 +630,8 @@ void load(const std::filesystem::path& path) { if (data.contains("Input")) { const toml::value& input = data.at("Input"); + deadZoneLeft = toml::find_or(input, "deadZoneLeft", 2.0); + deadZoneRight = toml::find_or(input, "deadZoneRight", 2.0); cursorState = toml::find_or(input, "cursorState", HideCursorState::Idle); cursorHideTimeout = toml::find_or(input, "cursorHideTimeout", 5); backButtonBehavior = toml::find_or(input, "backButtonBehavior", "left"); @@ -739,6 +751,8 @@ void save(const std::filesystem::path& path) { data["General"]["separateUpdateEnabled"] = separateupdatefolder; data["General"]["compatibilityEnabled"] = compatibilityData; data["General"]["checkCompatibilityOnStartup"] = checkCompatibilityOnStartup; + data["Input"]["deadZoneLeft"] = deadZoneLeft; + data["Input"]["deadZoneRight"] = deadZoneRight; data["Input"]["cursorState"] = cursorState; data["Input"]["cursorHideTimeout"] = cursorHideTimeout; data["Input"]["backButtonBehavior"] = backButtonBehavior; diff --git a/src/common/config.h b/src/common/config.h index f9e4c2815..cb56f99c7 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -35,6 +35,8 @@ std::string getLogType(); std::string getUserName(); std::string getUpdateChannel(); +u16 leftDeadZone(); +u16 rightDeadZone(); s16 getCursorState(); int getCursorHideTimeout(); std::string getBackButtonBehavior(); diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index 9a44f91f0..f2b81fbe0 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -95,8 +95,8 @@ int PS4_SYSV_ABI scePadGetControllerInformation(s32 handle, OrbisPadControllerIn pInfo->touchPadInfo.pixelDensity = 1; pInfo->touchPadInfo.resolution.x = 1920; pInfo->touchPadInfo.resolution.y = 950; - pInfo->stickInfo.deadZoneLeft = 2; - pInfo->stickInfo.deadZoneRight = 2; + pInfo->stickInfo.deadZoneLeft = Config::leftDeadZone(); + pInfo->stickInfo.deadZoneRight = Config::rightDeadZone(); pInfo->connectionType = ORBIS_PAD_PORT_TYPE_STANDARD; pInfo->connectedCount = 1; pInfo->connected = false; @@ -106,8 +106,8 @@ int PS4_SYSV_ABI scePadGetControllerInformation(s32 handle, OrbisPadControllerIn pInfo->touchPadInfo.pixelDensity = 1; pInfo->touchPadInfo.resolution.x = 1920; pInfo->touchPadInfo.resolution.y = 950; - pInfo->stickInfo.deadZoneLeft = 2; - pInfo->stickInfo.deadZoneRight = 2; + pInfo->stickInfo.deadZoneLeft = Config::leftDeadZone(); + pInfo->stickInfo.deadZoneRight = Config::rightDeadZone(); pInfo->connectionType = ORBIS_PAD_PORT_TYPE_STANDARD; pInfo->connectedCount = 1; pInfo->connected = true; From 269ce126149700ba340d7014a04faa215ddf64f9 Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Sat, 18 Jan 2025 16:54:06 +0300 Subject: [PATCH 440/549] fix build on arch --- src/video_core/renderer_vulkan/vk_instance.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 15bd573ef..5efdf4127 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -566,7 +566,8 @@ void Instance::CollectToolingInfo() { return; } for (const vk::PhysicalDeviceToolProperties& tool : tools) { - LOG_INFO(Render_Vulkan, "Attached debugging tool: {}", tool.name); + const std::string_view name = tool.name; + LOG_INFO(Render_Vulkan, "Attached debugging tool: {}", name); } } From 746f2e091d3aa1c56cf56e0a16c858d01eaf31cd Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sun, 19 Jan 2025 03:06:31 -0800 Subject: [PATCH 441/549] tile: Account for thickness in micro tiled size calculation. (#2185) --- src/video_core/texture_cache/image_info.cpp | 10 ++++++---- src/video_core/texture_cache/tile.h | 10 +++++----- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index a9ed76960..8068aae2f 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -166,6 +166,7 @@ void ImageInfo::UpdateSize() { mip_w = std::max(mip_w, 1u); mip_h = std::max(mip_h, 1u); auto mip_d = std::max(size.depth >> mip, 1u); + auto thickness = 1; if (props.is_pow2) { mip_w = std::bit_ceil(mip_w); @@ -181,12 +182,13 @@ void ImageInfo::UpdateSize() { break; } case AmdGpu::TilingMode::Texture_Volume: - mip_d += (-mip_d) & 3u; + thickness = 4; + mip_d += (-mip_d) & (thickness - 1); [[fallthrough]]; case AmdGpu::TilingMode::Display_MicroTiled: case AmdGpu::TilingMode::Texture_MicroTiled: { std::tie(mip_info.pitch, mip_info.size) = - ImageSizeMicroTiled(mip_w, mip_h, bpp, num_samples); + ImageSizeMicroTiled(mip_w, mip_h, bpp, thickness, num_samples); mip_info.height = std::max(mip_h, 8u); if (props.is_block) { mip_info.pitch = std::max(mip_info.pitch * 4, 32u); @@ -198,8 +200,8 @@ void ImageInfo::UpdateSize() { case AmdGpu::TilingMode::Texture_MacroTiled: case AmdGpu::TilingMode::Depth_MacroTiled: { ASSERT(!props.is_block); - std::tie(mip_info.pitch, mip_info.size) = - ImageSizeMacroTiled(mip_w, mip_h, bpp, num_samples, tiling_idx, mip, alt_tile); + std::tie(mip_info.pitch, mip_info.size) = ImageSizeMacroTiled( + mip_w, mip_h, thickness, bpp, num_samples, tiling_idx, mip, alt_tile); break; } default: { diff --git a/src/video_core/texture_cache/tile.h b/src/video_core/texture_cache/tile.h index 532bf3d88..c111e6aca 100644 --- a/src/video_core/texture_cache/tile.h +++ b/src/video_core/texture_cache/tile.h @@ -308,20 +308,20 @@ constexpr std::pair ImageSizeLinearAligned(u32 pitch, u32 height, u return {pitch_aligned, (log_sz * bpp + 7) / 8}; } -constexpr std::pair ImageSizeMicroTiled(u32 pitch, u32 height, u32 bpp, +constexpr std::pair ImageSizeMicroTiled(u32 pitch, u32 height, u32 thickness, u32 bpp, u32 num_samples) { const auto& [pitch_align, height_align] = micro_tile_extent; auto pitch_aligned = (pitch + pitch_align - 1) & ~(pitch_align - 1); const auto height_aligned = (height + height_align - 1) & ~(height_align - 1); - size_t log_sz = (pitch_aligned * height_aligned * bpp * num_samples + 7) / 8; + size_t log_sz = (pitch_aligned * height_aligned * bpp * num_samples * thickness + 7) / 8; while (log_sz % 256) { - pitch_aligned += 8; + pitch_aligned += pitch_align; log_sz = (pitch_aligned * height_aligned * bpp * num_samples + 7) / 8; } return {pitch_aligned, log_sz}; } -constexpr std::pair ImageSizeMacroTiled(u32 pitch, u32 height, u32 bpp, +constexpr std::pair ImageSizeMacroTiled(u32 pitch, u32 height, u32 thickness, u32 bpp, u32 num_samples, u32 tiling_idx, u32 mip_n, bool alt) { const auto& [pitch_align, height_align] = @@ -335,7 +335,7 @@ constexpr std::pair ImageSizeMacroTiled(u32 pitch, u32 height, u32 } if (downgrade_to_micro) { - return ImageSizeMicroTiled(pitch, height, bpp, num_samples); + return ImageSizeMicroTiled(pitch, height, thickness, bpp, num_samples); } const auto pitch_aligned = (pitch + pitch_align - 1) & ~(pitch_align - 1); From ec0dfb32b532fe2ddc28b3a1e65343e71cfcd92f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quang=20Ng=C3=B4?= Date: Sun, 19 Jan 2025 19:03:15 +0700 Subject: [PATCH 442/549] Some ImGui tweaks for the game window (#2183) * Remove window border * Remove window rounding * Set background color to black --- src/video_core/renderer_vulkan/vk_presenter.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/video_core/renderer_vulkan/vk_presenter.cpp b/src/video_core/renderer_vulkan/vk_presenter.cpp index 36d64a5d5..35ab4318a 100644 --- a/src/video_core/renderer_vulkan/vk_presenter.cpp +++ b/src/video_core/renderer_vulkan/vk_presenter.cpp @@ -825,6 +825,9 @@ void Presenter::Present(Frame* frame, bool is_reusing_frame) { { // Draw the game ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{0.0f}); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); ImGui::SetNextWindowDockID(dockId, ImGuiCond_Once); ImGui::Begin("Display##game_display", nullptr, ImGuiWindowFlags_NoNav); @@ -840,7 +843,8 @@ void Presenter::Present(Frame* frame, bool is_reusing_frame) { static_cast(imgRect.extent.height), }); ImGui::End(); - ImGui::PopStyleVar(); + ImGui::PopStyleVar(3); + ImGui::PopStyleColor(); } ImGui::Core::Render(cmdbuf, swapchain_image_view, swapchain.GetExtent()); From a7d45231b77633361862ef82cfafeb4d2a4669ac Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Sun, 19 Jan 2025 15:44:57 +0200 Subject: [PATCH 443/549] Filesystem devices (#2184) * added dummy devices * More WIP * added urandom,srandom,random,console,deci_tty6 devices * small fix * macOS fix --- CMakeLists.txt | 10 ++++ src/core/devices/deci_tty6.cpp | 66 +++++++++++++++++++++ src/core/devices/deci_tty6.h | 33 +++++++++++ src/core/devices/dev_console.cpp | 67 +++++++++++++++++++++ src/core/devices/dev_console.h | 33 +++++++++++ src/core/devices/random.cpp | 68 ++++++++++++++++++++++ src/core/devices/random.h | 33 +++++++++++ src/core/devices/srandom.cpp | 69 ++++++++++++++++++++++ src/core/devices/srandom.h | 33 +++++++++++ src/core/devices/urandom.cpp | 71 +++++++++++++++++++++++ src/core/devices/urandom.h | 33 +++++++++++ src/core/libraries/kernel/file_system.cpp | 33 ++++------- 12 files changed, 527 insertions(+), 22 deletions(-) create mode 100644 src/core/devices/deci_tty6.cpp create mode 100644 src/core/devices/deci_tty6.h create mode 100644 src/core/devices/dev_console.cpp create mode 100644 src/core/devices/dev_console.h create mode 100644 src/core/devices/random.cpp create mode 100644 src/core/devices/random.h create mode 100644 src/core/devices/srandom.cpp create mode 100644 src/core/devices/srandom.h create mode 100644 src/core/devices/urandom.cpp create mode 100644 src/core/devices/urandom.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 30cb033ed..e5c16bd1b 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -557,6 +557,16 @@ set(CORE src/core/aerolib/stubs.cpp src/core/devices/logger.cpp src/core/devices/logger.h src/core/devices/nop_device.h + src/core/devices/dev_console.cpp + src/core/devices/dev_console.h + src/core/devices/deci_tty6.cpp + src/core/devices/deci_tty6.h + src/core/devices/random.cpp + src/core/devices/random.h + src/core/devices/urandom.cpp + src/core/devices/urandom.h + src/core/devices/srandom.cpp + src/core/devices/srandom.h src/core/file_format/pfs.h src/core/file_format/pkg.cpp src/core/file_format/pkg.h diff --git a/src/core/devices/deci_tty6.cpp b/src/core/devices/deci_tty6.cpp new file mode 100644 index 000000000..20423de61 --- /dev/null +++ b/src/core/devices/deci_tty6.cpp @@ -0,0 +1,66 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "deci_tty6.h" + +namespace Core::Devices { +std::shared_ptr DeciTty6Device::Create(u32 handle, const char*, int, u16) { + return std::shared_ptr( + reinterpret_cast(new DeciTty6Device(handle))); +} +int DeciTty6Device::ioctl(u64 cmd, Common::VaCtx* args) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} +s64 DeciTty6Device::write(const void* buf, size_t nbytes) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} +size_t DeciTty6Device::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} +size_t DeciTty6Device::readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} +s64 DeciTty6Device::preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} +s64 DeciTty6Device::lseek(s64 offset, int whence) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} + +s64 DeciTty6Device::read(void* buf, size_t nbytes) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} + +int DeciTty6Device::fstat(Libraries::Kernel::OrbisKernelStat* sb) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} + +s32 DeciTty6Device::fsync() { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} + +int DeciTty6Device::ftruncate(s64 length) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} + +int DeciTty6Device::getdents(void* buf, u32 nbytes, s64* basep) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} + +s64 DeciTty6Device::pwrite(const void* buf, size_t nbytes, u64 offset) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} +} // namespace Core::Devices \ No newline at end of file diff --git a/src/core/devices/deci_tty6.h b/src/core/devices/deci_tty6.h new file mode 100644 index 000000000..71cbfba6b --- /dev/null +++ b/src/core/devices/deci_tty6.h @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once +#include +#include "base_device.h" + +namespace Core::Devices { + +class DeciTty6Device final : BaseDevice { + u32 handle; + +public: + static std::shared_ptr Create(u32 handle, const char*, int, u16); + explicit DeciTty6Device(u32 handle) : handle(handle) {} + + ~DeciTty6Device() override = default; + + int ioctl(u64 cmd, Common::VaCtx* args) override; + s64 write(const void* buf, size_t nbytes) override; + size_t readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override; + size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override; + s64 preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) override; + s64 lseek(s64 offset, int whence) override; + s64 read(void* buf, size_t nbytes) override; + int fstat(Libraries::Kernel::OrbisKernelStat* sb) override; + s32 fsync() override; + int ftruncate(s64 length) override; + int getdents(void* buf, u32 nbytes, s64* basep) override; + s64 pwrite(const void* buf, size_t nbytes, u64 offset) override; +}; + +} // namespace Core::Devices \ No newline at end of file diff --git a/src/core/devices/dev_console.cpp b/src/core/devices/dev_console.cpp new file mode 100644 index 000000000..0ddcfd040 --- /dev/null +++ b/src/core/devices/dev_console.cpp @@ -0,0 +1,67 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "dev_console.h" + +namespace Core::Devices { +std::shared_ptr ConsoleDevice::Create(u32 handle, const char*, int, u16) { + return std::shared_ptr( + reinterpret_cast(new ConsoleDevice(handle))); +} +int ConsoleDevice::ioctl(u64 cmd, Common::VaCtx* args) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} +s64 ConsoleDevice::write(const void* buf, size_t nbytes) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} +size_t ConsoleDevice::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} +size_t ConsoleDevice::readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} +s64 ConsoleDevice::preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} +s64 ConsoleDevice::lseek(s64 offset, int whence) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} + +s64 ConsoleDevice::read(void* buf, size_t nbytes) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} + +int ConsoleDevice::fstat(Libraries::Kernel::OrbisKernelStat* sb) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} + +s32 ConsoleDevice::fsync() { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} + +int ConsoleDevice::ftruncate(s64 length) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} + +int ConsoleDevice::getdents(void* buf, u32 nbytes, s64* basep) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} + +s64 ConsoleDevice::pwrite(const void* buf, size_t nbytes, u64 offset) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} + +} // namespace Core::Devices \ No newline at end of file diff --git a/src/core/devices/dev_console.h b/src/core/devices/dev_console.h new file mode 100644 index 000000000..f280200e2 --- /dev/null +++ b/src/core/devices/dev_console.h @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once +#include +#include "base_device.h" + +namespace Core::Devices { + +class ConsoleDevice final : BaseDevice { + u32 handle; + +public: + static std::shared_ptr Create(u32 handle, const char*, int, u16); + explicit ConsoleDevice(u32 handle) : handle(handle) {} + + ~ConsoleDevice() override = default; + + int ioctl(u64 cmd, Common::VaCtx* args) override; + s64 write(const void* buf, size_t nbytes) override; + size_t readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override; + size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override; + s64 preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) override; + s64 lseek(s64 offset, int whence) override; + s64 read(void* buf, size_t nbytes) override; + int fstat(Libraries::Kernel::OrbisKernelStat* sb) override; + s32 fsync() override; + int ftruncate(s64 length) override; + int getdents(void* buf, u32 nbytes, s64* basep) override; + s64 pwrite(const void* buf, size_t nbytes, u64 offset) override; +}; + +} // namespace Core::Devices \ No newline at end of file diff --git a/src/core/devices/random.cpp b/src/core/devices/random.cpp new file mode 100644 index 000000000..705449423 --- /dev/null +++ b/src/core/devices/random.cpp @@ -0,0 +1,68 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include "common/logging/log.h" +#include "random.h" + +namespace Core::Devices { +std::shared_ptr RandomDevice::Create(u32 handle, const char*, int, u16) { + std::srand(std::time(nullptr)); + return std::shared_ptr( + reinterpret_cast(new RandomDevice(handle))); +} +int RandomDevice::ioctl(u64 cmd, Common::VaCtx* args) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} +s64 RandomDevice::write(const void* buf, size_t nbytes) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} +size_t RandomDevice::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} +size_t RandomDevice::readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} +s64 RandomDevice::preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} +s64 RandomDevice::lseek(s64 offset, int whence) { + return 0; +} + +s64 RandomDevice::read(void* buf, size_t nbytes) { + auto rbuf = static_cast(buf); + for (size_t i = 0; i < nbytes; i++) + rbuf[i] = std::rand() & 0xFF; + return nbytes; +} + +int RandomDevice::fstat(Libraries::Kernel::OrbisKernelStat* sb) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} + +s32 RandomDevice::fsync() { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} + +int RandomDevice::ftruncate(s64 length) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} + +int RandomDevice::getdents(void* buf, u32 nbytes, s64* basep) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} + +s64 RandomDevice::pwrite(const void* buf, size_t nbytes, u64 offset) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} +} // namespace Core::Devices \ No newline at end of file diff --git a/src/core/devices/random.h b/src/core/devices/random.h new file mode 100644 index 000000000..3bbed1ca2 --- /dev/null +++ b/src/core/devices/random.h @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once +#include +#include "base_device.h" + +namespace Core::Devices { + +class RandomDevice final : BaseDevice { + u32 handle; + +public: + static std::shared_ptr Create(u32 handle, const char*, int, u16); + explicit RandomDevice(u32 handle) : handle(handle) {} + + ~RandomDevice() override = default; + + int ioctl(u64 cmd, Common::VaCtx* args) override; + s64 write(const void* buf, size_t nbytes) override; + size_t readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override; + size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override; + s64 preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) override; + s64 lseek(s64 offset, int whence) override; + s64 read(void* buf, size_t nbytes) override; + int fstat(Libraries::Kernel::OrbisKernelStat* sb) override; + s32 fsync() override; + int ftruncate(s64 length) override; + int getdents(void* buf, u32 nbytes, s64* basep) override; + s64 pwrite(const void* buf, size_t nbytes, u64 offset) override; +}; + +} // namespace Core::Devices \ No newline at end of file diff --git a/src/core/devices/srandom.cpp b/src/core/devices/srandom.cpp new file mode 100644 index 000000000..ff80adeaf --- /dev/null +++ b/src/core/devices/srandom.cpp @@ -0,0 +1,69 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include "common/logging/log.h" +#include "srandom.h" + +namespace Core::Devices { +std::shared_ptr SRandomDevice::Create(u32 handle, const char*, int, u16) { + std::srand(std::time(nullptr)); + return std::shared_ptr( + reinterpret_cast(new SRandomDevice(handle))); +} +int SRandomDevice::ioctl(u64 cmd, Common::VaCtx* args) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} +s64 SRandomDevice::write(const void* buf, size_t nbytes) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} +size_t SRandomDevice::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} +size_t SRandomDevice::readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} +s64 SRandomDevice::preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} +s64 SRandomDevice::lseek(s64 offset, int whence) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} + +s64 SRandomDevice::read(void* buf, size_t nbytes) { + auto rbuf = static_cast(buf); + for (size_t i = 0; i < nbytes; i++) + rbuf[i] = std::rand(); + return nbytes; +} + +int SRandomDevice::fstat(Libraries::Kernel::OrbisKernelStat* sb) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} + +s32 SRandomDevice::fsync() { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return s32(); +} + +int SRandomDevice::ftruncate(s64 length) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} + +int SRandomDevice::getdents(void* buf, u32 nbytes, s64* basep) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} + +s64 SRandomDevice::pwrite(const void* buf, size_t nbytes, u64 offset) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} +} // namespace Core::Devices \ No newline at end of file diff --git a/src/core/devices/srandom.h b/src/core/devices/srandom.h new file mode 100644 index 000000000..3a3b02571 --- /dev/null +++ b/src/core/devices/srandom.h @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once +#include +#include "base_device.h" + +namespace Core::Devices { + +class SRandomDevice final : BaseDevice { + u32 handle; + +public: + static std::shared_ptr Create(u32 handle, const char*, int, u16); + explicit SRandomDevice(u32 handle) : handle(handle) {} + + ~SRandomDevice() override = default; + + int ioctl(u64 cmd, Common::VaCtx* args) override; + s64 write(const void* buf, size_t nbytes) override; + size_t readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override; + size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override; + s64 preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) override; + s64 lseek(s64 offset, int whence) override; + s64 read(void* buf, size_t nbytes) override; + int fstat(Libraries::Kernel::OrbisKernelStat* sb) override; + s32 fsync() override; + int ftruncate(s64 length) override; + int getdents(void* buf, u32 nbytes, s64* basep) override; + s64 pwrite(const void* buf, size_t nbytes, u64 offset) override; +}; + +} // namespace Core::Devices \ No newline at end of file diff --git a/src/core/devices/urandom.cpp b/src/core/devices/urandom.cpp new file mode 100644 index 000000000..8917caea5 --- /dev/null +++ b/src/core/devices/urandom.cpp @@ -0,0 +1,71 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include "common/logging/log.h" +#include "urandom.h" + +namespace Core::Devices { + +std::shared_ptr URandomDevice::Create(u32 handle, const char*, int, u16) { + std::srand(std::time(nullptr)); + return std::shared_ptr( + reinterpret_cast(new URandomDevice(handle))); +} + +int URandomDevice::ioctl(u64 cmd, Common::VaCtx* args) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} +s64 URandomDevice::write(const void* buf, size_t nbytes) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} +size_t URandomDevice::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} +size_t URandomDevice::readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} +s64 URandomDevice::preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} +s64 URandomDevice::lseek(s64 offset, int whence) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} + +s64 URandomDevice::read(void* buf, size_t nbytes) { + auto rbuf = static_cast(buf); + for (size_t i = 0; i < nbytes; i++) + rbuf[i] = std::rand() & 0xFF; + return nbytes; +} + +int URandomDevice::fstat(Libraries::Kernel::OrbisKernelStat* sb) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} + +s32 URandomDevice::fsync() { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} + +int URandomDevice::ftruncate(s64 length) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} + +int URandomDevice::getdents(void* buf, u32 nbytes, s64* basep) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} + +s64 URandomDevice::pwrite(const void* buf, size_t nbytes, u64 offset) { + LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); + return 0; +} +} // namespace Core::Devices \ No newline at end of file diff --git a/src/core/devices/urandom.h b/src/core/devices/urandom.h new file mode 100644 index 000000000..9370017d5 --- /dev/null +++ b/src/core/devices/urandom.h @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once +#include +#include "base_device.h" + +namespace Core::Devices { + +class URandomDevice final : BaseDevice { + u32 handle; + +public: + static std::shared_ptr Create(u32 handle, const char*, int, u16); + explicit URandomDevice(u32 handle) : handle(handle) {} + + ~URandomDevice() override = default; + + int ioctl(u64 cmd, Common::VaCtx* args) override; + s64 write(const void* buf, size_t nbytes) override; + size_t readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override; + size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override; + s64 preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) override; + s64 lseek(s64 offset, int whence) override; + s64 read(void* buf, size_t nbytes) override; + int fstat(Libraries::Kernel::OrbisKernelStat* sb) override; + s32 fsync() override; + int ftruncate(s64 length) override; + int getdents(void* buf, u32 nbytes, s64* basep) override; + s64 pwrite(const void* buf, size_t nbytes, u64 offset) override; +}; + +} // namespace Core::Devices \ No newline at end of file diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index 2eb5d1621..ce91fe192 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -8,8 +8,13 @@ #include "common/logging/log.h" #include "common/scope_exit.h" #include "common/singleton.h" +#include "core/devices/deci_tty6.h" +#include "core/devices/dev_console.h" #include "core/devices/logger.h" #include "core/devices/nop_device.h" +#include "core/devices/random.h" +#include "core/devices/srandom.h" +#include "core/devices/urandom.h" #include "core/file_sys/fs.h" #include "core/libraries/kernel/file_system.h" #include "core/libraries/kernel/orbis_error.h" @@ -41,6 +46,12 @@ static std::map available_device = { {"/dev/deci_stderr", GET_DEVICE_FD(2)}, {"/dev/null", GET_DEVICE_FD(0)}, // fd0 (stdin) is a nop device + + {"/dev/urandom", &D::URandomDevice::Create }, + {"/dev/random", &D::RandomDevice::Create }, + {"/dev/srandom", &D::SRandomDevice::Create }, + {"/dev/console", &D::ConsoleDevice::Create }, + {"/dev/deci_tty6",&D::DeciTty6Device::Create } // clang-format on }; @@ -67,17 +78,6 @@ int PS4_SYSV_ABI sceKernelOpen(const char* raw_path, int flags, u16 mode) { bool directory = (flags & ORBIS_KERNEL_O_DIRECTORY) != 0; std::string_view path{raw_path}; - - if (path == "/dev/console") { - return 2000; - } - if (path == "/dev/deci_tty6") { - return 2001; - } - if (path == "/dev/urandom") { - return 2003; - } - u32 handle = h->CreateHandle(); auto* file = h->GetFile(handle); @@ -167,9 +167,6 @@ int PS4_SYSV_ABI sceKernelClose(int d) { if (d < 3) { // d probably hold an error code return ORBIS_KERNEL_ERROR_EPERM; } - if (d == 2003) { // dev/urandom case - return ORBIS_OK; - } auto* h = Common::Singleton::Instance(); auto* file = h->GetFile(d); if (file == nullptr) { @@ -337,13 +334,6 @@ s64 PS4_SYSV_ABI posix_lseek(int d, s64 offset, int whence) { } s64 PS4_SYSV_ABI sceKernelRead(int d, void* buf, size_t nbytes) { - if (d == 2003) // dev urandom case - { - auto rbuf = static_cast(buf); - for (size_t i = 0; i < nbytes; i++) - rbuf[i] = std::rand() & 0xFF; - return nbytes; - } auto* h = Common::Singleton::Instance(); auto* file = h->GetFile(d); if (file == nullptr) { @@ -757,7 +747,6 @@ s32 PS4_SYSV_ABI sceKernelRename(const char* from, const char* to) { } void RegisterFileSystem(Core::Loader::SymbolsResolver* sym) { - std::srand(std::time(nullptr)); LIB_FUNCTION("1G3lF1Gg1k8", "libkernel", 1, "libkernel", 1, 1, sceKernelOpen); LIB_FUNCTION("wuCroIGjt2g", "libScePosix", 1, "libkernel", 1, 1, posix_open); LIB_FUNCTION("wuCroIGjt2g", "libkernel", 1, "libkernel", 1, 1, open); From c8bbecda2654e4cb0825cfce396942142ab3d19f Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Sun, 19 Jan 2025 12:45:24 -0300 Subject: [PATCH 444/549] Devtools: Close Button ( X ) (#2187) --- src/core/devtools/layer.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/core/devtools/layer.cpp b/src/core/devtools/layer.cpp index c652849e7..a6d99b49b 100644 --- a/src/core/devtools/layer.cpp +++ b/src/core/devtools/layer.cpp @@ -93,6 +93,12 @@ void L::DrawMenuBar() { } ImGui::EndMenu(); } + + SameLine(ImGui::GetWindowWidth() - 30.0f); + if (Button("X", ImVec2(25, 25))) { + DebugState.IsShowingDebugMenuBar() = false; + } + EndMainMenuBar(); } From 17ac63d23a4abd046854a852642bd093a28108d7 Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Sun, 19 Jan 2025 12:47:40 -0300 Subject: [PATCH 445/549] Fix SurfaceFormat (#2188) --- src/video_core/renderer_vulkan/liverpool_to_vk.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp index 9695e127f..4ac8d5cc8 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp @@ -612,7 +612,7 @@ std::span SurfaceFormats() { vk::Format::eB5G6R5UnormPack16), // 1_5_5_5 CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format1_5_5_5, AmdGpu::NumberFormat::Unorm, - vk::Format::eR5G5B5A1UnormPack16), + vk::Format::eA1B5G5R5UnormPack16), // 5_5_5_1 - Remapped to 1_5_5_5. // 4_4_4_4 CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format4_4_4_4, AmdGpu::NumberFormat::Unorm, From 201f2817ca929d54460bfd5e18d3a23b0cad65c5 Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Sun, 19 Jan 2025 18:55:27 -0300 Subject: [PATCH 446/549] Fix SurfaceFormat Format1_5_5_5 - Format5_5_5_1 (#2191) * Fix SurfaceFormat Format1_5_5_5 - again * Fix Format5_5_5_1 --- src/video_core/amdgpu/types.h | 11 +++++++++-- src/video_core/renderer_vulkan/liverpool_to_vk.cpp | 6 ++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/video_core/amdgpu/types.h b/src/video_core/amdgpu/types.h index 57f97418a..63e184cc5 100644 --- a/src/video_core/amdgpu/types.h +++ b/src/video_core/amdgpu/types.h @@ -283,8 +283,7 @@ inline CompMapping RemapSwizzle(const DataFormat format, const CompMapping swizz result.a = swizzle.a; return result; } - case DataFormat::Format10_10_10_2: - case DataFormat::Format5_5_5_1: { + case DataFormat::Format10_10_10_2: { CompMapping result; result.r = swizzle.a; result.g = swizzle.b; @@ -292,6 +291,14 @@ inline CompMapping RemapSwizzle(const DataFormat format, const CompMapping swizz result.a = swizzle.r; return result; } + case DataFormat::Format1_5_5_5: { + CompMapping result; + result.r = swizzle.b; + result.g = swizzle.g; + result.b = swizzle.r; + result.a = swizzle.a; + return result; + } default: return swizzle; } diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp index 4ac8d5cc8..35585edb7 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp @@ -612,8 +612,10 @@ std::span SurfaceFormats() { vk::Format::eB5G6R5UnormPack16), // 1_5_5_5 CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format1_5_5_5, AmdGpu::NumberFormat::Unorm, - vk::Format::eA1B5G5R5UnormPack16), - // 5_5_5_1 - Remapped to 1_5_5_5. + vk::Format::eA1R5G5B5UnormPack16), + // 5_5_5_1 + CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format5_5_5_1, AmdGpu::NumberFormat::Unorm, + vk::Format::eR5G5B5A1UnormPack16), // 4_4_4_4 CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format4_4_4_4, AmdGpu::NumberFormat::Unorm, vk::Format::eR4G4B4A4UnormPack16), From 80092b6367e1aa230fb6e72fb304a0828061d409 Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Sun, 19 Jan 2025 20:09:10 -0300 Subject: [PATCH 447/549] Fix SurfaceFormat Format4_4_4_4 (#2193) * Fix SurfaceFormat Format4_4_4_4 Pac-Man 256 * add_extension --- src/video_core/renderer_vulkan/liverpool_to_vk.cpp | 2 +- src/video_core/renderer_vulkan/vk_instance.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp index 35585edb7..f2fbc6530 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp @@ -618,7 +618,7 @@ std::span SurfaceFormats() { vk::Format::eR5G5B5A1UnormPack16), // 4_4_4_4 CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format4_4_4_4, AmdGpu::NumberFormat::Unorm, - vk::Format::eR4G4B4A4UnormPack16), + vk::Format::eA4B4G4R4UnormPack16), // 8_24 // 24_8 // X24_8_32 diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 5efdf4127..d183d6b09 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -283,6 +283,7 @@ bool Instance::CreateDevice() { add_extension(VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME); add_extension(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME); add_extension(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); + add_extension(VK_EXT_4444_FORMATS_EXTENSION_NAME); #ifdef __APPLE__ // Required by Vulkan spec if supported. From d14e57f6a8bf327db8a032382fcaefa0b441fd1c Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sun, 19 Jan 2025 18:45:34 -0800 Subject: [PATCH 448/549] hotfix: Move some command buffer references down. Prevents references becoming stale due to stream buffer flushes. --- src/video_core/buffer_cache/buffer_cache.cpp | 2 +- src/video_core/texture_cache/texture_cache.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index 487544a21..11ad0e96f 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -210,7 +210,6 @@ void BufferCache::InlineData(VAddr address, const void* value, u32 num_bytes, bo return; } scheduler.EndRendering(); - const auto cmdbuf = scheduler.CommandBuffer(); const Buffer* buffer = [&] { if (is_gds) { return &gds_buffer; @@ -218,6 +217,7 @@ void BufferCache::InlineData(VAddr address, const void* value, u32 num_bytes, bo const BufferId buffer_id = FindBuffer(address, num_bytes); return &slot_buffers[buffer_id]; }(); + const auto cmdbuf = scheduler.CommandBuffer(); const vk::BufferMemoryBarrier2 pre_barrier = { .srcStageMask = vk::PipelineStageFlagBits2::eAllCommands, .srcAccessMask = vk::AccessFlagBits2::eMemoryRead, diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index a281b89c9..5947db864 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -545,12 +545,12 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule auto* sched_ptr = custom_scheduler ? custom_scheduler : &scheduler; sched_ptr->EndRendering(); - const auto cmdbuf = sched_ptr->CommandBuffer(); const VAddr image_addr = image.info.guest_address; const size_t image_size = image.info.guest_size; const auto [vk_buffer, buf_offset] = buffer_cache.ObtainViewBuffer(image_addr, image_size, is_gpu_dirty); + const auto cmdbuf = sched_ptr->CommandBuffer(); // The obtained buffer may be written by a shader so we need to emit a barrier to prevent RAW // hazard if (auto barrier = vk_buffer->GetBarrier(vk::AccessFlagBits2::eTransferRead, From 4fa501c8d525a6afa3ae36134b26e2dc8007a24e Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Sun, 19 Jan 2025 21:12:42 -0600 Subject: [PATCH 449/549] Additional libSceNpManager functions and cleanup (#2195) * Error return on sceNpGetAccountIdA Confirmed through hardware testing, this returns ORBIS_NP_ERROR_SIGNED_OUT on a signed out console. Parameters are based on fpPS4 code. * Fix compile * Move errors to separate file * Cleanup function headers Swaps user_ids to use our OrbisUserServiceUserId type, and fixes parameter names to align with our coding standards. * Add proper parameter checks * Implement sceNpGetAccountId This function takes an online_id, uses an NpManager function to get the user_id, then uses that user_id to perform the same internal functions as sceNpGetAccountIdA. * Implement sceNpHasSignedUp * Fix sceNpGetAccountId Further hardware testing shows that these always write 0 to account_id when failing. * Update np_manager.cpp --- src/core/libraries/np_manager/np_manager.cpp | 42 ++++++++++++++----- src/core/libraries/np_manager/np_manager.h | 14 +++---- .../libraries/np_manager/np_manager_error.h | 9 ++++ 3 files changed, 47 insertions(+), 18 deletions(-) create mode 100644 src/core/libraries/np_manager/np_manager_error.h diff --git a/src/core/libraries/np_manager/np_manager.cpp b/src/core/libraries/np_manager/np_manager.cpp index e26c5a830..a60dcd86f 100644 --- a/src/core/libraries/np_manager/np_manager.cpp +++ b/src/core/libraries/np_manager/np_manager.cpp @@ -5,6 +5,7 @@ #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "core/libraries/np_manager/np_manager.h" +#include "core/libraries/np_manager/np_manager_error.h" #include "core/tls.h" namespace Libraries::NpManager { @@ -935,14 +936,22 @@ int PS4_SYSV_ABI sceNpGetAccountDateOfBirthA() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNpGetAccountId() { - LOG_ERROR(Lib_NpManager, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNpGetAccountId(OrbisNpOnlineId* online_id, u64* account_id) { + LOG_DEBUG(Lib_NpManager, "called"); + if (online_id == nullptr || account_id == nullptr) { + return ORBIS_NP_ERROR_INVALID_ARGUMENT; + } + *account_id = 0; + return ORBIS_NP_ERROR_SIGNED_OUT; } -int PS4_SYSV_ABI sceNpGetAccountIdA() { - LOG_ERROR(Lib_NpManager, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNpGetAccountIdA(OrbisUserServiceUserId user_id, u64* account_id) { + LOG_DEBUG(Lib_NpManager, "user_id {}", user_id); + if (account_id == nullptr) { + return ORBIS_NP_ERROR_INVALID_ARGUMENT; + } + *account_id = 0; + return ORBIS_NP_ERROR_SIGNED_OUT; } int PS4_SYSV_ABI sceNpGetAccountLanguage() { @@ -972,6 +981,9 @@ int PS4_SYSV_ABI sceNpGetGamePresenceStatusA() { int PS4_SYSV_ABI sceNpGetNpId(OrbisUserServiceUserId user_id, OrbisNpId* np_id) { LOG_DEBUG(Lib_NpManager, "user_id {}", user_id); + if (np_id == nullptr) { + return ORBIS_NP_ERROR_INVALID_ARGUMENT; + } return ORBIS_NP_ERROR_SIGNED_OUT; } @@ -980,8 +992,11 @@ int PS4_SYSV_ABI sceNpGetNpReachabilityState() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNpGetOnlineId(s32 user_id, OrbisNpOnlineId* online_id) { +int PS4_SYSV_ABI sceNpGetOnlineId(OrbisUserServiceUserId user_id, OrbisNpOnlineId* online_id) { LOG_DEBUG(Lib_NpManager, "user_id {}", user_id); + if (online_id == nullptr) { + return ORBIS_NP_ERROR_INVALID_ARGUMENT; + } return ORBIS_NP_ERROR_SIGNED_OUT; } @@ -995,7 +1010,10 @@ int PS4_SYSV_ABI sceNpGetParentalControlInfoA() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNpGetState(s32 userId, OrbisNpState* state) { +int PS4_SYSV_ABI sceNpGetState(OrbisUserServiceUserId user_id, OrbisNpState* state) { + if (state == nullptr) { + return ORBIS_NP_ERROR_INVALID_ARGUMENT; + } *state = OrbisNpState::SignedOut; LOG_DEBUG(Lib_NpManager, "Signed out"); return ORBIS_OK; @@ -1011,8 +1029,12 @@ int PS4_SYSV_ABI sceNpGetUserIdByOnlineId() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNpHasSignedUp() { - LOG_ERROR(Lib_NpManager, "(STUBBED) called"); +int PS4_SYSV_ABI sceNpHasSignedUp(OrbisUserServiceUserId user_id, bool* has_signed_up) { + LOG_DEBUG(Lib_NpManager, "called"); + if (has_signed_up == nullptr) { + return ORBIS_NP_ERROR_INVALID_ARGUMENT; + } + *has_signed_up = false; return ORBIS_OK; } diff --git a/src/core/libraries/np_manager/np_manager.h b/src/core/libraries/np_manager/np_manager.h index 6ba588e5e..02a1a32f6 100644 --- a/src/core/libraries/np_manager/np_manager.h +++ b/src/core/libraries/np_manager/np_manager.h @@ -11,8 +11,6 @@ class SymbolsResolver; namespace Libraries::NpManager { -constexpr int ORBIS_NP_ERROR_SIGNED_OUT = 0x80550006; - enum class OrbisNpState : u32 { Unknown = 0, SignedOut, SignedIn }; using OrbisNpStateCallbackForNpToolkit = PS4_SYSV_ABI void (*)(s32 userId, OrbisNpState state, @@ -220,22 +218,22 @@ int PS4_SYSV_ABI sceNpGetAccountCountry(); int PS4_SYSV_ABI sceNpGetAccountCountryA(); int PS4_SYSV_ABI sceNpGetAccountDateOfBirth(); int PS4_SYSV_ABI sceNpGetAccountDateOfBirthA(); -int PS4_SYSV_ABI sceNpGetAccountId(); -int PS4_SYSV_ABI sceNpGetAccountIdA(); +int PS4_SYSV_ABI sceNpGetAccountId(OrbisNpOnlineId* online_id, u64* account_id); +int PS4_SYSV_ABI sceNpGetAccountIdA(OrbisUserServiceUserId user_id, u64* account_id); int PS4_SYSV_ABI sceNpGetAccountLanguage(); int PS4_SYSV_ABI sceNpGetAccountLanguage2(); int PS4_SYSV_ABI sceNpGetAccountLanguageA(); int PS4_SYSV_ABI sceNpGetGamePresenceStatus(); int PS4_SYSV_ABI sceNpGetGamePresenceStatusA(); -int PS4_SYSV_ABI sceNpGetNpId(OrbisUserServiceUserId userId, OrbisNpId* npId); +int PS4_SYSV_ABI sceNpGetNpId(OrbisUserServiceUserId user_id, OrbisNpId* np_id); int PS4_SYSV_ABI sceNpGetNpReachabilityState(); -int PS4_SYSV_ABI sceNpGetOnlineId(s32 userId, OrbisNpOnlineId* onlineId); +int PS4_SYSV_ABI sceNpGetOnlineId(OrbisUserServiceUserId user_id, OrbisNpOnlineId* online_id); int PS4_SYSV_ABI sceNpGetParentalControlInfo(); int PS4_SYSV_ABI sceNpGetParentalControlInfoA(); -int PS4_SYSV_ABI sceNpGetState(s32 userId, OrbisNpState* state); +int PS4_SYSV_ABI sceNpGetState(OrbisUserServiceUserId user_id, OrbisNpState* state); int PS4_SYSV_ABI sceNpGetUserIdByAccountId(); int PS4_SYSV_ABI sceNpGetUserIdByOnlineId(); -int PS4_SYSV_ABI sceNpHasSignedUp(); +int PS4_SYSV_ABI sceNpHasSignedUp(OrbisUserServiceUserId user_id, bool* has_signed_up); int PS4_SYSV_ABI sceNpIdMapperAbortRequest(); int PS4_SYSV_ABI sceNpIdMapperAccountIdToNpId(); int PS4_SYSV_ABI sceNpIdMapperAccountIdToOnlineId(); diff --git a/src/core/libraries/np_manager/np_manager_error.h b/src/core/libraries/np_manager/np_manager_error.h new file mode 100644 index 000000000..4af0d08ef --- /dev/null +++ b/src/core/libraries/np_manager/np_manager_error.h @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +constexpr int ORBIS_NP_ERROR_INVALID_ARGUMENT = 0x80550003; +constexpr int ORBIS_NP_ERROR_SIGNED_OUT = 0x80550006; \ No newline at end of file From 0f93edb377f50dcb462427cdda0eb9939f37de31 Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Sun, 19 Jan 2025 21:20:51 -0600 Subject: [PATCH 450/549] Implement IMAGE_ATOMIC_SWAP (#2194) We already handle everything for this opcode in our IMAGE_ATOMIC function, so implementing this is fairly simple. Should improve Wipeout 3. --- src/shader_recompiler/frontend/format.cpp | 4 ++-- src/shader_recompiler/frontend/translate/vector_memory.cpp | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/shader_recompiler/frontend/format.cpp b/src/shader_recompiler/frontend/format.cpp index 9677be3e5..2fcac7c10 100644 --- a/src/shader_recompiler/frontend/format.cpp +++ b/src/shader_recompiler/frontend/format.cpp @@ -3420,8 +3420,8 @@ constexpr std::array InstructionFormatMIMG = {{ {InstClass::VectorMemImgUt, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, ScalarType::Uint32}, // 15 = IMAGE_ATOMIC_SWAP - {InstClass::VectorMemImgNoSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Undefined, - ScalarType::Undefined}, + {InstClass::VectorMemImgNoSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, + ScalarType::Uint32}, // 16 = IMAGE_ATOMIC_CMPSWAP {InstClass::VectorMemImgNoSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Undefined, ScalarType::Undefined}, diff --git a/src/shader_recompiler/frontend/translate/vector_memory.cpp b/src/shader_recompiler/frontend/translate/vector_memory.cpp index 8fa0f3f87..685785af1 100644 --- a/src/shader_recompiler/frontend/translate/vector_memory.cpp +++ b/src/shader_recompiler/frontend/translate/vector_memory.cpp @@ -107,6 +107,8 @@ void Translator::EmitVectorMemory(const GcnInst& inst) { return IMAGE_GET_RESINFO(inst); // Image atomic operations + case Opcode::IMAGE_ATOMIC_SWAP: + return IMAGE_ATOMIC(AtomicOp::Swap, inst); case Opcode::IMAGE_ATOMIC_ADD: return IMAGE_ATOMIC(AtomicOp::Add, inst); case Opcode::IMAGE_ATOMIC_SMIN: From e1132db197def956132f08870258a1f8c21e9c99 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sun, 19 Jan 2025 23:33:37 -0800 Subject: [PATCH 451/549] texture_cache: Prevent unregistered images from being tracked. (#2196) --- src/video_core/texture_cache/texture_cache.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index 5947db864..798ecf030 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -643,6 +643,9 @@ void TextureCache::UnregisterImage(ImageId image_id) { void TextureCache::TrackImage(ImageId image_id) { auto& image = slot_images[image_id]; + if (!(image.flags & ImageFlagBits::Registered)) { + return; + } const auto image_begin = image.info.guest_address; const auto image_end = image.info.guest_address + image.info.guest_size; if (image_begin == image.track_addr && image_end == image.track_addr_end) { @@ -666,6 +669,9 @@ void TextureCache::TrackImage(ImageId image_id) { void TextureCache::TrackImageHead(ImageId image_id) { auto& image = slot_images[image_id]; + if (!(image.flags & ImageFlagBits::Registered)) { + return; + } const auto image_begin = image.info.guest_address; if (image_begin == image.track_addr) { return; @@ -678,6 +684,9 @@ void TextureCache::TrackImageHead(ImageId image_id) { void TextureCache::TrackImageTail(ImageId image_id) { auto& image = slot_images[image_id]; + if (!(image.flags & ImageFlagBits::Registered)) { + return; + } const auto image_end = image.info.guest_address + image.info.guest_size; if (image_end == image.track_addr_end) { return; From a3967ccdb43ebe5c8005dd35e72825497abbbb3d Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Mon, 20 Jan 2025 04:48:32 -0800 Subject: [PATCH 452/549] externals: Update vulkan-headers (#2197) --- CMakeLists.txt | 2 +- externals/vulkan-headers | 2 +- src/video_core/renderer_vulkan/vk_platform.cpp | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e5c16bd1b..918a1acb7 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -120,7 +120,7 @@ find_package(SDL3 3.1.2 CONFIG) find_package(stb MODULE) find_package(toml11 4.2.0 CONFIG) find_package(tsl-robin-map 1.3.0 CONFIG) -find_package(VulkanHeaders 1.4.303 CONFIG) +find_package(VulkanHeaders 1.4.305 CONFIG) find_package(VulkanMemoryAllocator 3.1.0 CONFIG) find_package(xbyak 7.07 CONFIG) find_package(xxHash 0.8.2 MODULE) diff --git a/externals/vulkan-headers b/externals/vulkan-headers index 6a74a7d65..a03d2f6d5 160000 --- a/externals/vulkan-headers +++ b/externals/vulkan-headers @@ -1 +1 @@ -Subproject commit 6a74a7d65cafa19e38ec116651436cce6efd5b2e +Subproject commit a03d2f6d5753b365d704d58161825890baad0755 diff --git a/src/video_core/renderer_vulkan/vk_platform.cpp b/src/video_core/renderer_vulkan/vk_platform.cpp index fdd590e9d..07ebfbda6 100644 --- a/src/video_core/renderer_vulkan/vk_platform.cpp +++ b/src/video_core/renderer_vulkan/vk_platform.cpp @@ -28,19 +28,19 @@ static const char* const VALIDATION_LAYER_NAME = "VK_LAYER_KHRONOS_validation"; static const char* const CRASH_DIAGNOSTIC_LAYER_NAME = "VK_LAYER_LUNARG_crash_diagnostic"; static VKAPI_ATTR VkBool32 VKAPI_CALL DebugUtilsCallback( - VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT type, - const VkDebugUtilsMessengerCallbackDataEXT* callback_data, void* user_data) { + vk::DebugUtilsMessageSeverityFlagBitsEXT severity, vk::DebugUtilsMessageTypeFlagsEXT type, + const vk::DebugUtilsMessengerCallbackDataEXT* callback_data, void* user_data) { Common::Log::Level level{}; switch (severity) { - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: + case vk::DebugUtilsMessageSeverityFlagBitsEXT::eError: level = Common::Log::Level::Error; break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: + case vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning: level = Common::Log::Level::Info; break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: + case vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo: + case vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose: level = Common::Log::Level::Debug; break; default: From 95a30b2b3e1aa4e20c3db632955cc67bbded0fb1 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Mon, 20 Jan 2025 13:38:09 -0800 Subject: [PATCH 453/549] texture_cache: Lock when updating image. (#2198) --- src/video_core/texture_cache/texture_cache.cpp | 6 ++---- src/video_core/texture_cache/texture_cache.h | 1 + 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index 798ecf030..04711539c 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -60,15 +60,13 @@ void TextureCache::MarkAsMaybeDirty(ImageId image_id, Image& image) { void TextureCache::InvalidateMemory(VAddr addr, size_t size) { std::scoped_lock lock{mutex}; - const auto end = addr + size; const auto pages_start = PageManager::GetPageAddr(addr); const auto pages_end = PageManager::GetNextPageAddr(addr + size - 1); ForEachImageInRegion(pages_start, pages_end - pages_start, [&](ImageId image_id, Image& image) { const auto image_begin = image.info.guest_address; const auto image_end = image.info.guest_address + image.info.guest_size; - if (image_begin < end && addr < image_end) { - // Start or end of the modified region is in the image, or the image is entirely within - // the modified region, so the image was definitely accessed by this page fault. + if (image.Overlaps(addr, size)) { + // Modified region overlaps image, so the image was definitely accessed by this fault. // Untrack the image, so that the range is unprotected and the guest can write freely. image.flags |= ImageFlagBits::CpuDirty; UntrackImage(image_id); diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 343a510e6..f262768ea 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -118,6 +118,7 @@ public: /// Updates image contents if it was modified by CPU. void UpdateImage(ImageId image_id, Vulkan::Scheduler* custom_scheduler = nullptr) { + std::scoped_lock lock{mutex}; Image& image = slot_images[image_id]; TrackImage(image_id); RefreshImage(image, custom_scheduler); From 3563b88d8c9b72474460233b13e4b28ac93e8bc1 Mon Sep 17 00:00:00 2001 From: polyproxy <47796739+polybiusproxy@users.noreply.github.com> Date: Tue, 21 Jan 2025 19:28:39 +0100 Subject: [PATCH 454/549] hotfix: use logger device on stdin --- CMakeLists.txt | 20 +++++++++---------- src/core/devices/base_device.cpp | 4 ++-- src/core/devices/base_device.h | 4 ++-- .../{dev_console.cpp => console_device.cpp} | 13 +++++++++--- .../{dev_console.h => console_device.h} | 4 ++-- .../{deci_tty6.cpp => deci_tty6_device.cpp} | 14 ++++++++++--- .../{deci_tty6.h => deci_tty6_device.h} | 4 ++-- src/core/devices/ioccom.h | 4 ++-- src/core/devices/logger.cpp | 8 +++++--- src/core/devices/logger.h | 4 ++-- src/core/devices/nop_device.h | 17 +++++++++++++--- .../devices/{random.cpp => random_device.cpp} | 18 +++++++++++++---- .../devices/{random.h => random_device.h} | 4 ++-- .../{srandom.cpp => srandom_device.cpp} | 18 +++++++++++++---- .../devices/{srandom.h => srandom_device.h} | 4 ++-- .../{urandom.cpp => urandom_device.cpp} | 16 +++++++++++---- .../devices/{urandom.h => urandom_device.h} | 4 ++-- src/core/file_sys/fs.cpp | 2 +- src/core/libraries/kernel/file_system.cpp | 17 +++++----------- 19 files changed, 114 insertions(+), 65 deletions(-) rename src/core/devices/{dev_console.cpp => console_device.cpp} (92%) rename src/core/devices/{dev_console.h => console_device.h} (90%) rename src/core/devices/{deci_tty6.cpp => deci_tty6_device.cpp} (92%) rename src/core/devices/{deci_tty6.h => deci_tty6_device.h} (90%) rename src/core/devices/{random.cpp => random_device.cpp} (90%) rename src/core/devices/{random.h => random_device.h} (90%) rename src/core/devices/{srandom.cpp => srandom_device.cpp} (90%) rename src/core/devices/{srandom.h => srandom_device.h} (90%) rename src/core/devices/{urandom.cpp => urandom_device.cpp} (90%) rename src/core/devices/{urandom.h => urandom_device.h} (90%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 918a1acb7..131809c8e 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -557,16 +557,16 @@ set(CORE src/core/aerolib/stubs.cpp src/core/devices/logger.cpp src/core/devices/logger.h src/core/devices/nop_device.h - src/core/devices/dev_console.cpp - src/core/devices/dev_console.h - src/core/devices/deci_tty6.cpp - src/core/devices/deci_tty6.h - src/core/devices/random.cpp - src/core/devices/random.h - src/core/devices/urandom.cpp - src/core/devices/urandom.h - src/core/devices/srandom.cpp - src/core/devices/srandom.h + src/core/devices/console_device.cpp + src/core/devices/console_device.h + src/core/devices/deci_tty6_device.cpp + src/core/devices/deci_tty6_device.h + src/core/devices/random_device.cpp + src/core/devices/random_device.h + src/core/devices/urandom_device.cpp + src/core/devices/urandom_device.h + src/core/devices/srandom_device.cpp + src/core/devices/srandom_device.h src/core/file_format/pfs.h src/core/file_format/pkg.cpp src/core/file_format/pkg.h diff --git a/src/core/devices/base_device.cpp b/src/core/devices/base_device.cpp index 4f91c81c7..fc2a98a29 100644 --- a/src/core/devices/base_device.cpp +++ b/src/core/devices/base_device.cpp @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "base_device.h" diff --git a/src/core/devices/base_device.h b/src/core/devices/base_device.h index 351af82b4..36614b8f4 100644 --- a/src/core/devices/base_device.h +++ b/src/core/devices/base_device.h @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/devices/dev_console.cpp b/src/core/devices/console_device.cpp similarity index 92% rename from src/core/devices/dev_console.cpp rename to src/core/devices/console_device.cpp index 0ddcfd040..f109cadb9 100644 --- a/src/core/devices/dev_console.cpp +++ b/src/core/devices/console_device.cpp @@ -1,34 +1,41 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" -#include "dev_console.h" +#include "console_device.h" namespace Core::Devices { + std::shared_ptr ConsoleDevice::Create(u32 handle, const char*, int, u16) { return std::shared_ptr( reinterpret_cast(new ConsoleDevice(handle))); } + int ConsoleDevice::ioctl(u64 cmd, Common::VaCtx* args) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + s64 ConsoleDevice::write(const void* buf, size_t nbytes) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + size_t ConsoleDevice::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + size_t ConsoleDevice::readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + s64 ConsoleDevice::preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + s64 ConsoleDevice::lseek(s64 offset, int whence) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; diff --git a/src/core/devices/dev_console.h b/src/core/devices/console_device.h similarity index 90% rename from src/core/devices/dev_console.h rename to src/core/devices/console_device.h index f280200e2..d4b590ba0 100644 --- a/src/core/devices/dev_console.h +++ b/src/core/devices/console_device.h @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include diff --git a/src/core/devices/deci_tty6.cpp b/src/core/devices/deci_tty6_device.cpp similarity index 92% rename from src/core/devices/deci_tty6.cpp rename to src/core/devices/deci_tty6_device.cpp index 20423de61..e7a5fd4fc 100644 --- a/src/core/devices/deci_tty6.cpp +++ b/src/core/devices/deci_tty6_device.cpp @@ -1,34 +1,41 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" -#include "deci_tty6.h" +#include "deci_tty6_device.h" namespace Core::Devices { + std::shared_ptr DeciTty6Device::Create(u32 handle, const char*, int, u16) { return std::shared_ptr( reinterpret_cast(new DeciTty6Device(handle))); } + int DeciTty6Device::ioctl(u64 cmd, Common::VaCtx* args) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + s64 DeciTty6Device::write(const void* buf, size_t nbytes) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + size_t DeciTty6Device::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + size_t DeciTty6Device::readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + s64 DeciTty6Device::preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + s64 DeciTty6Device::lseek(s64 offset, int whence) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; @@ -63,4 +70,5 @@ s64 DeciTty6Device::pwrite(const void* buf, size_t nbytes, u64 offset) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + } // namespace Core::Devices \ No newline at end of file diff --git a/src/core/devices/deci_tty6.h b/src/core/devices/deci_tty6_device.h similarity index 90% rename from src/core/devices/deci_tty6.h rename to src/core/devices/deci_tty6_device.h index 71cbfba6b..b8bd48556 100644 --- a/src/core/devices/deci_tty6.h +++ b/src/core/devices/deci_tty6_device.h @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include diff --git a/src/core/devices/ioccom.h b/src/core/devices/ioccom.h index 671ee33d4..2ded90bd8 100644 --- a/src/core/devices/ioccom.h +++ b/src/core/devices/ioccom.h @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/devices/logger.cpp b/src/core/devices/logger.cpp index 6f104509c..8dcb24a3b 100644 --- a/src/core/devices/logger.cpp +++ b/src/core/devices/logger.cpp @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/libraries/kernel/file_system.h" @@ -17,10 +17,12 @@ s64 Logger::write(const void* buf, size_t nbytes) { } size_t Logger::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { + size_t total_written = 0; for (int i = 0; i < iovcnt; i++) { log(static_cast(iov[i].iov_base), iov[i].iov_len); + total_written += iov[i].iov_len; } - return iovcnt; + return total_written; } s64 Logger::pwrite(const void* buf, size_t nbytes, u64 offset) { diff --git a/src/core/devices/logger.h b/src/core/devices/logger.h index bfb07f337..eef17bc4b 100644 --- a/src/core/devices/logger.h +++ b/src/core/devices/logger.h @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/devices/nop_device.h b/src/core/devices/nop_device.h index a75b92f1b..5518b1de1 100644 --- a/src/core/devices/nop_device.h +++ b/src/core/devices/nop_device.h @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include "base_device.h" @@ -17,36 +17,47 @@ public: int ioctl(u64 cmd, Common::VaCtx* args) override { return 0; } + s64 write(const void* buf, size_t nbytes) override { return 0; } + size_t readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override { return 0; } + size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override { - return 0; + return ORBIS_KERNEL_ERROR_EBADF; } + s64 preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) override { return 0; } + s64 lseek(s64 offset, int whence) override { return 0; } + s64 read(void* buf, size_t nbytes) override { return 0; } + int fstat(Libraries::Kernel::OrbisKernelStat* sb) override { return 0; } + s32 fsync() override { return 0; } + int ftruncate(s64 length) override { return 0; } + int getdents(void* buf, u32 nbytes, s64* basep) override { return 0; } + s64 pwrite(const void* buf, size_t nbytes, u64 offset) override { return 0; } diff --git a/src/core/devices/random.cpp b/src/core/devices/random_device.cpp similarity index 90% rename from src/core/devices/random.cpp rename to src/core/devices/random_device.cpp index 705449423..50934e3b8 100644 --- a/src/core/devices/random.cpp +++ b/src/core/devices/random_device.cpp @@ -1,43 +1,52 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + #include #include "common/logging/log.h" -#include "random.h" +#include "random_device.h" namespace Core::Devices { + std::shared_ptr RandomDevice::Create(u32 handle, const char*, int, u16) { std::srand(std::time(nullptr)); return std::shared_ptr( reinterpret_cast(new RandomDevice(handle))); } + int RandomDevice::ioctl(u64 cmd, Common::VaCtx* args) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + s64 RandomDevice::write(const void* buf, size_t nbytes) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + size_t RandomDevice::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + size_t RandomDevice::readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + s64 RandomDevice::preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + s64 RandomDevice::lseek(s64 offset, int whence) { return 0; } s64 RandomDevice::read(void* buf, size_t nbytes) { auto rbuf = static_cast(buf); - for (size_t i = 0; i < nbytes; i++) + for (size_t i = 0; i < nbytes; i++) { rbuf[i] = std::rand() & 0xFF; + } return nbytes; } @@ -65,4 +74,5 @@ s64 RandomDevice::pwrite(const void* buf, size_t nbytes, u64 offset) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + } // namespace Core::Devices \ No newline at end of file diff --git a/src/core/devices/random.h b/src/core/devices/random_device.h similarity index 90% rename from src/core/devices/random.h rename to src/core/devices/random_device.h index 3bbed1ca2..a5c8e9845 100644 --- a/src/core/devices/random.h +++ b/src/core/devices/random_device.h @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include diff --git a/src/core/devices/srandom.cpp b/src/core/devices/srandom_device.cpp similarity index 90% rename from src/core/devices/srandom.cpp rename to src/core/devices/srandom_device.cpp index ff80adeaf..ab78ddbe2 100644 --- a/src/core/devices/srandom.cpp +++ b/src/core/devices/srandom_device.cpp @@ -1,35 +1,43 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + #include #include "common/logging/log.h" -#include "srandom.h" +#include "srandom_device.h" namespace Core::Devices { + std::shared_ptr SRandomDevice::Create(u32 handle, const char*, int, u16) { std::srand(std::time(nullptr)); return std::shared_ptr( reinterpret_cast(new SRandomDevice(handle))); } + int SRandomDevice::ioctl(u64 cmd, Common::VaCtx* args) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + s64 SRandomDevice::write(const void* buf, size_t nbytes) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + size_t SRandomDevice::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + size_t SRandomDevice::readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + s64 SRandomDevice::preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + s64 SRandomDevice::lseek(s64 offset, int whence) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; @@ -37,8 +45,9 @@ s64 SRandomDevice::lseek(s64 offset, int whence) { s64 SRandomDevice::read(void* buf, size_t nbytes) { auto rbuf = static_cast(buf); - for (size_t i = 0; i < nbytes; i++) + for (size_t i = 0; i < nbytes; i++) { rbuf[i] = std::rand(); + } return nbytes; } @@ -66,4 +75,5 @@ s64 SRandomDevice::pwrite(const void* buf, size_t nbytes, u64 offset) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + } // namespace Core::Devices \ No newline at end of file diff --git a/src/core/devices/srandom.h b/src/core/devices/srandom_device.h similarity index 90% rename from src/core/devices/srandom.h rename to src/core/devices/srandom_device.h index 3a3b02571..cd32f7289 100644 --- a/src/core/devices/srandom.h +++ b/src/core/devices/srandom_device.h @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include diff --git a/src/core/devices/urandom.cpp b/src/core/devices/urandom_device.cpp similarity index 90% rename from src/core/devices/urandom.cpp rename to src/core/devices/urandom_device.cpp index 8917caea5..c001aab83 100644 --- a/src/core/devices/urandom.cpp +++ b/src/core/devices/urandom_device.cpp @@ -1,8 +1,9 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + #include #include "common/logging/log.h" -#include "urandom.h" +#include "urandom_device.h" namespace Core::Devices { @@ -16,22 +17,27 @@ int URandomDevice::ioctl(u64 cmd, Common::VaCtx* args) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + s64 URandomDevice::write(const void* buf, size_t nbytes) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + size_t URandomDevice::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + size_t URandomDevice::readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + s64 URandomDevice::preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + s64 URandomDevice::lseek(s64 offset, int whence) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; @@ -39,8 +45,9 @@ s64 URandomDevice::lseek(s64 offset, int whence) { s64 URandomDevice::read(void* buf, size_t nbytes) { auto rbuf = static_cast(buf); - for (size_t i = 0; i < nbytes; i++) + for (size_t i = 0; i < nbytes; i++) { rbuf[i] = std::rand() & 0xFF; + } return nbytes; } @@ -68,4 +75,5 @@ s64 URandomDevice::pwrite(const void* buf, size_t nbytes, u64 offset) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + } // namespace Core::Devices \ No newline at end of file diff --git a/src/core/devices/urandom.h b/src/core/devices/urandom_device.h similarity index 90% rename from src/core/devices/urandom.h rename to src/core/devices/urandom_device.h index 9370017d5..b8a854cc0 100644 --- a/src/core/devices/urandom.h +++ b/src/core/devices/urandom_device.h @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include diff --git a/src/core/file_sys/fs.cpp b/src/core/file_sys/fs.cpp index 7d456780b..8a191e666 100644 --- a/src/core/file_sys/fs.cpp +++ b/src/core/file_sys/fs.cpp @@ -233,7 +233,7 @@ void HandleTable::CreateStdHandles() { std::shared_ptr{reinterpret_cast(device)}; }; // order matters - setup("/dev/stdin", new Devices::NopDevice(0)); // stdin + setup("/dev/stdin", new Devices::Logger("stdin", false)); // stdin setup("/dev/stdout", new Devices::Logger("stdout", false)); // stdout setup("/dev/stderr", new Devices::Logger("stderr", true)); // stderr } diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index ce91fe192..b0f7fdafe 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -8,13 +8,13 @@ #include "common/logging/log.h" #include "common/scope_exit.h" #include "common/singleton.h" -#include "core/devices/deci_tty6.h" -#include "core/devices/dev_console.h" +#include "core/devices/deci_tty6_device.h" +#include "core/devices/console_device.h" #include "core/devices/logger.h" #include "core/devices/nop_device.h" -#include "core/devices/random.h" -#include "core/devices/srandom.h" -#include "core/devices/urandom.h" +#include "core/devices/random_device.h" +#include "core/devices/srandom_device.h" +#include "core/devices/urandom_device.h" #include "core/file_sys/fs.h" #include "core/libraries/kernel/file_system.h" #include "core/libraries/kernel/orbis_error.h" @@ -270,13 +270,6 @@ size_t PS4_SYSV_ABI _readv(int d, const SceKernelIovec* iov, int iovcnt) { } size_t PS4_SYSV_ABI _writev(int fd, const SceKernelIovec* iov, int iovcn) { - if (fd == 1) { - size_t total_written = 0; - for (int i = 0; i < iovcn; i++) { - total_written += ::fwrite(iov[i].iov_base, 1, iov[i].iov_len, stdout); - } - return total_written; - } auto* h = Common::Singleton::Instance(); auto* file = h->GetFile(fd); if (file == nullptr) { From 84a341dce570c4a070d07bc023a757652247fb6f Mon Sep 17 00:00:00 2001 From: polyproxy <47796739+polybiusproxy@users.noreply.github.com> Date: Tue, 21 Jan 2025 19:30:34 +0100 Subject: [PATCH 455/549] remove BADF return --- src/core/devices/nop_device.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/devices/nop_device.h b/src/core/devices/nop_device.h index 5518b1de1..da9a3fc82 100644 --- a/src/core/devices/nop_device.h +++ b/src/core/devices/nop_device.h @@ -27,7 +27,7 @@ public: } size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override { - return ORBIS_KERNEL_ERROR_EBADF; + return 0; } s64 preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) override { From 41b39428335025e65f9e707ed8d5a9a1b09ba942 Mon Sep 17 00:00:00 2001 From: polyproxy <47796739+polybiusproxy@users.noreply.github.com> Date: Tue, 21 Jan 2025 19:34:05 +0100 Subject: [PATCH 456/549] clang-format --- src/core/file_sys/fs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/file_sys/fs.cpp b/src/core/file_sys/fs.cpp index 8a191e666..ec940503f 100644 --- a/src/core/file_sys/fs.cpp +++ b/src/core/file_sys/fs.cpp @@ -233,7 +233,7 @@ void HandleTable::CreateStdHandles() { std::shared_ptr{reinterpret_cast(device)}; }; // order matters - setup("/dev/stdin", new Devices::Logger("stdin", false)); // stdin + setup("/dev/stdin", new Devices::Logger("stdin", false)); // stdin setup("/dev/stdout", new Devices::Logger("stdout", false)); // stdout setup("/dev/stderr", new Devices::Logger("stderr", true)); // stderr } From 5c62a00134523c4d0b2f06e9b6c28c596631e748 Mon Sep 17 00:00:00 2001 From: polyproxy <47796739+polybiusproxy@users.noreply.github.com> Date: Tue, 21 Jan 2025 21:14:05 +0100 Subject: [PATCH 457/549] `clang-format` (again) --- src/core/libraries/kernel/file_system.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index b0f7fdafe..0150c11f5 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -8,8 +8,8 @@ #include "common/logging/log.h" #include "common/scope_exit.h" #include "common/singleton.h" -#include "core/devices/deci_tty6_device.h" #include "core/devices/console_device.h" +#include "core/devices/deci_tty6_device.h" #include "core/devices/logger.h" #include "core/devices/nop_device.h" #include "core/devices/random_device.h" From 2a4798cfa60f430840855a88339c1f4b364df9a4 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Wed, 22 Jan 2025 00:40:00 -0800 Subject: [PATCH 458/549] tile: Fix some tile thickness calculation errors. (#2203) * tile: Fix some tile thickness calculation errors. * tile: Do not pad mip height to tile height. --- src/video_core/texture_cache/image_info.cpp | 13 ++++++------- src/video_core/texture_cache/tile.h | 4 ++-- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index 8068aae2f..dd89be8aa 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -178,7 +178,6 @@ void ImageInfo::UpdateSize() { case AmdGpu::TilingMode::Display_Linear: { std::tie(mip_info.pitch, mip_info.size) = ImageSizeLinearAligned(mip_w, mip_h, bpp, num_samples); - mip_info.height = mip_h; break; } case AmdGpu::TilingMode::Texture_Volume: @@ -188,12 +187,7 @@ void ImageInfo::UpdateSize() { case AmdGpu::TilingMode::Display_MicroTiled: case AmdGpu::TilingMode::Texture_MicroTiled: { std::tie(mip_info.pitch, mip_info.size) = - ImageSizeMicroTiled(mip_w, mip_h, bpp, thickness, num_samples); - mip_info.height = std::max(mip_h, 8u); - if (props.is_block) { - mip_info.pitch = std::max(mip_info.pitch * 4, 32u); - mip_info.height = std::max(mip_info.height * 4, 32u); - } + ImageSizeMicroTiled(mip_w, mip_h, thickness, bpp, num_samples); break; } case AmdGpu::TilingMode::Display_MacroTiled: @@ -208,6 +202,11 @@ void ImageInfo::UpdateSize() { UNREACHABLE(); } } + mip_info.height = mip_h; + if (props.is_block) { + mip_info.pitch = std::max(mip_info.pitch * 4, 32u); + mip_info.height = std::max(mip_info.height * 4, 32u); + } mip_info.size *= mip_d; mip_info.offset = guest_size; mips_layout.emplace_back(mip_info); diff --git a/src/video_core/texture_cache/tile.h b/src/video_core/texture_cache/tile.h index c111e6aca..54938b801 100644 --- a/src/video_core/texture_cache/tile.h +++ b/src/video_core/texture_cache/tile.h @@ -313,8 +313,8 @@ constexpr std::pair ImageSizeMicroTiled(u32 pitch, u32 height, u32 const auto& [pitch_align, height_align] = micro_tile_extent; auto pitch_aligned = (pitch + pitch_align - 1) & ~(pitch_align - 1); const auto height_aligned = (height + height_align - 1) & ~(height_align - 1); - size_t log_sz = (pitch_aligned * height_aligned * bpp * num_samples * thickness + 7) / 8; - while (log_sz % 256) { + size_t log_sz = (pitch_aligned * height_aligned * bpp * num_samples + 7) / 8; + while ((log_sz * thickness) % 256) { pitch_aligned += pitch_align; log_sz = (pitch_aligned * height_aligned * bpp * num_samples + 7) / 8; } From 78ae9613c5f0e40f30b5a5cea83fa368530f1c24 Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Wed, 22 Jan 2025 04:07:43 -0600 Subject: [PATCH 459/549] Fix for address_space initialization on Windows (#2202) Should fix some `Region coalescing failed: Attempt to access invalid address.` crashes. Co-authored-by: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> --- src/core/address_space.cpp | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/core/address_space.cpp b/src/core/address_space.cpp index 2b7331cbd..e9fb8cfbc 100644 --- a/src/core/address_space.cpp +++ b/src/core/address_space.cpp @@ -67,28 +67,25 @@ struct AddressSpace::Impl { static constexpr size_t ReductionOnFail = 1_GB; static constexpr size_t MaxReductions = 10; - size_t reduction = 0; size_t virtual_size = SystemManagedSize + SystemReservedSize + UserSize; for (u32 i = 0; i < MaxReductions; i++) { - virtual_base = static_cast(VirtualAlloc2(process, NULL, virtual_size - reduction, + virtual_base = static_cast(VirtualAlloc2(process, NULL, virtual_size, MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, PAGE_NOACCESS, ¶m, 1)); if (virtual_base) { break; } - reduction += ReductionOnFail; + virtual_size -= ReductionOnFail; } ASSERT_MSG(virtual_base, "Unable to reserve virtual address space: {}", Common::GetLastErrorMsg()); - // Take the reduction off of the system managed area, and leave the others unchanged. - reduction = size_t(virtual_base - SYSTEM_MANAGED_MIN); - system_managed_base = virtual_base; - system_managed_size = SystemManagedSize - reduction; system_reserved_base = reinterpret_cast(SYSTEM_RESERVED_MIN); system_reserved_size = SystemReservedSize; + system_managed_base = virtual_base; + system_managed_size = system_reserved_base - virtual_base; user_base = reinterpret_cast(USER_MIN); - user_size = UserSize; + user_size = virtual_base + virtual_size - user_base; LOG_INFO(Kernel_Vmm, "System managed virtual memory region: {} - {}", fmt::ptr(system_managed_base), @@ -101,10 +98,8 @@ struct AddressSpace::Impl { // Initializer placeholder tracker const uintptr_t system_managed_addr = reinterpret_cast(system_managed_base); - const uintptr_t system_reserved_addr = reinterpret_cast(system_reserved_base); - const uintptr_t user_addr = reinterpret_cast(user_base); regions.emplace(system_managed_addr, - MemoryRegion{system_managed_addr, virtual_size - reduction, false}); + MemoryRegion{system_managed_addr, virtual_size, false}); // Allocate backing file that represents the total physical memory. backing_handle = From adbff4056fe5a98026c2239ecd9511cd1be90e0f Mon Sep 17 00:00:00 2001 From: f3d209 <45915273+rootexpm@users.noreply.github.com> Date: Wed, 22 Jan 2025 10:10:35 +0000 Subject: [PATCH 460/549] Added ability to change save data path (#2199) * added ability to change save data path * get default save data path from fs * add copyright * formatting --- src/common/config.cpp | 15 +++ src/common/config.h | 2 + .../libraries/save_data/save_instance.cpp | 6 +- src/qt_gui/settings_dialog.cpp | 33 ++++- src/qt_gui/settings_dialog.ui | 121 +++++++++++------- src/qt_gui/translations/en.ts | 8 ++ 6 files changed, 132 insertions(+), 53 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index ed9af5a72..6e9db50ff 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -78,6 +78,7 @@ static std::string trophyKey; static bool load_game_size = true; std::vector settings_install_dirs = {}; std::filesystem::path settings_addon_install_dir = {}; +std::filesystem::path save_data_path = {}; u32 main_window_geometry_x = 400; u32 main_window_geometry_y = 400; u32 main_window_geometry_w = 1280; @@ -110,6 +111,13 @@ bool GetLoadGameSizeEnabled() { return load_game_size; } +std::filesystem::path GetSaveDataPath() { + if (save_data_path.empty()) { + return Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir); + } + return save_data_path; +} + void setLoadGameSizeEnabled(bool enable) { load_game_size = enable; } @@ -502,6 +510,10 @@ void setGameInstallDirs(const std::vector& settings_insta settings_install_dirs = settings_install_dirs_config; } +void setSaveDataPath(const std::filesystem::path& path) { + save_data_path = path; +} + u32 getMainWindowGeometryX() { return main_window_geometry_x; } @@ -690,6 +702,8 @@ void load(const std::filesystem::path& path) { addGameInstallDir(std::filesystem::path{dir}); } + save_data_path = toml::find_fs_path_or(gui, "saveDataPath", {}); + settings_addon_install_dir = toml::find_fs_path_or(gui, "addonInstallDir", {}); main_window_geometry_x = toml::find_or(gui, "geometry_x", 0); main_window_geometry_y = toml::find_or(gui, "geometry_y", 0); @@ -784,6 +798,7 @@ void save(const std::filesystem::path& path) { install_dirs.emplace_back(std::string{fmt::UTF(dirString.u8string()).data}); } data["GUI"]["installDirs"] = install_dirs; + data["GUI"]["saveDataPath"] = std::string{fmt::UTF(save_data_path.u8string()).data}; data["GUI"]["loadGameSizeEnabled"] = load_game_size; data["GUI"]["addonInstallDir"] = diff --git a/src/common/config.h b/src/common/config.h index cb56f99c7..564947f1e 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -18,6 +18,7 @@ void saveMainWindow(const std::filesystem::path& path); std::string getTrophyKey(); void setTrophyKey(std::string key); bool GetLoadGameSizeEnabled(); +std::filesystem::path GetSaveDataPath(); void setLoadGameSizeEnabled(bool enable); bool getIsFullscreen(); std::string getFullscreenMode(); @@ -82,6 +83,7 @@ void setUserName(const std::string& type); void setUpdateChannel(const std::string& type); void setSeparateUpdateEnabled(bool use); void setGameInstallDirs(const std::vector& settings_install_dirs_config); +void setSaveDataPath(const std::filesystem::path& path); void setCompatibilityEnabled(bool use); void setCheckCompatibilityOnStartup(bool use); diff --git a/src/core/libraries/save_data/save_instance.cpp b/src/core/libraries/save_data/save_instance.cpp index 99daf83cc..c2b7dca3c 100644 --- a/src/core/libraries/save_data/save_instance.cpp +++ b/src/core/libraries/save_data/save_instance.cpp @@ -47,15 +47,13 @@ namespace Libraries::SaveData { std::filesystem::path SaveInstance::MakeTitleSavePath(OrbisUserServiceUserId user_id, std::string_view game_serial) { - return Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir) / std::to_string(user_id) / - game_serial; + return Config::GetSaveDataPath() / std::to_string(user_id) / game_serial; } std::filesystem::path SaveInstance::MakeDirSavePath(OrbisUserServiceUserId user_id, std::string_view game_serial, std::string_view dir_name) { - return Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir) / std::to_string(user_id) / - game_serial / dir_name; + return Config::GetSaveDataPath() / std::to_string(user_id) / game_serial / dir_name; } uint64_t SaveInstance::GetMaxBlockFromSFO(const PSF& psf) { diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index 175c8c51d..5695d6232 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -202,6 +202,21 @@ SettingsDialog::SettingsDialog(std::span physical_devices, delete selected_item; } }); + + connect(ui->browseButton, &QPushButton::clicked, this, [this]() { + const auto save_data_path = Config::GetSaveDataPath(); + QString initial_path; + Common::FS::PathToQString(initial_path, save_data_path); + + QString save_data_path_string = + QFileDialog::getExistingDirectory(this, tr("Directory to save data"), initial_path); + + auto file_path = Common::FS::PathFromQString(save_data_path_string); + if (!file_path.empty()) { + Config::setSaveDataPath(file_path); + ui->currentSaveDataPath->setText(save_data_path_string); + } + }); } // DEBUG TAB @@ -256,6 +271,10 @@ SettingsDialog::SettingsDialog(std::span physical_devices, ui->addFolderButton->installEventFilter(this); ui->removeFolderButton->installEventFilter(this); + ui->saveDataGroupBox->installEventFilter(this); + ui->currentSaveDataPath->installEventFilter(this); + ui->browseButton->installEventFilter(this); + // Debug ui->debugDump->installEventFilter(this); ui->vkValidationCheckBox->installEventFilter(this); @@ -286,6 +305,11 @@ void SettingsDialog::LoadValuesFromConfig() { const QVector languageIndexes = {21, 23, 14, 6, 18, 1, 12, 22, 2, 4, 25, 24, 29, 5, 0, 9, 15, 16, 17, 7, 26, 8, 11, 20, 3, 13, 27, 10, 19, 30, 28}; + const auto save_data_path = Config::GetSaveDataPath(); + QString save_data_path_string; + Common::FS::PathToQString(save_data_path_string, save_data_path); + ui->currentSaveDataPath->setText(save_data_path_string); + ui->consoleLanguageComboBox->setCurrentIndex( std::distance(languageIndexes.begin(), std::find(languageIndexes.begin(), languageIndexes.end(), @@ -497,6 +521,13 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) { text = tr("removeFolderButton"); } + // Save Data + if (elementName == "saveDataGroupBox" || elementName == "currentSaveDataPath") { + text = tr("saveDataBox"); + } else if (elementName == "browseButton") { + text = tr("browseButton"); + } + // Debug if (elementName == "debugDump") { text = tr("debugDump"); @@ -603,4 +634,4 @@ void SettingsDialog::ResetInstallFolders() { } Config::setGameInstallDirs(settings_install_dirs_config); } -} +} \ No newline at end of file diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index c084d4849..d72a56973 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -53,7 +53,7 @@ - 0 + 3 @@ -67,8 +67,8 @@ 0 0 - 946 - 611 + 771 + 606 @@ -644,8 +644,8 @@ 0 0 - 946 - 605 + 455 + 252 @@ -928,8 +928,8 @@ 0 0 - 946 - 605 + 579 + 194 @@ -1178,51 +1178,76 @@ Paths - - - 0 - 0 - 946 - 605 - - - + - - - - - Game Folders - - - - - - 0 - - + + + Game Folders + + + + + - - Add... - + + Add... + - - + + - - Remove - + + Remove + - - - - - - + + + + + Qt::Horizontal + + + + 40 + 20 + + + + - - - + + + + + + + + + + + Save Data Path + + + + + + + + true + + + + + + + Browse + + + + + + + @@ -1239,8 +1264,8 @@ 0 0 - 946 - 586 + 510 + 269 diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index 9127df7e3..dc72d97c9 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -912,6 +912,14 @@ rdocCheckBox Enable RenderDoc Debugging:\nIf enabled, the emulator will provide compatibility with Renderdoc to allow capture and analysis of the currently rendered frame. + + saveDataBox + Save Data Path:\nThe folder where game save data will be saved. + + + browseButton + Browse:\nBrowse for a folder to set as the save data path. + CheatsPatches From 2968cf5a99a1be2fe7f3f28dbc65fe16f488c8af Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Wed, 22 Jan 2025 09:06:27 -0600 Subject: [PATCH 461/549] sceKernelVirtualQuery Fixes (#2204) --- src/core/memory.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 619941000..f90d4e6aa 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -529,8 +529,8 @@ int MemoryManager::VirtualQuery(VAddr addr, int flags, info->is_flexible.Assign(vma.type == VMAType::Flexible); info->is_direct.Assign(vma.type == VMAType::Direct); info->is_stack.Assign(vma.type == VMAType::Stack); - info->is_pooled.Assign(vma.type == VMAType::PoolReserved); - info->is_committed.Assign(vma.type == VMAType::Pooled); + info->is_pooled.Assign(vma.type == VMAType::PoolReserved || vma.type == VMAType::Pooled); + info->is_committed.Assign(vma.IsMapped()); vma.name.copy(info->name.data(), std::min(info->name.size(), vma.name.size())); if (vma.type == VMAType::Direct) { const auto dmem_it = FindDmemArea(vma.phys_base); From b3bce086b30c148f25817f9df06abaedad244453 Mon Sep 17 00:00:00 2001 From: Vinicius Rangel Date: Wed, 22 Jan 2025 12:08:49 -0300 Subject: [PATCH 462/549] devtools: fix ReleaseKeyboard assert being triggered if many shader editor windows exist (#2205) --- src/core/devtools/widget/shader_list.cpp | 65 +++++++++++++++--------- src/core/devtools/widget/shader_list.h | 9 ++-- 2 files changed, 47 insertions(+), 27 deletions(-) diff --git a/src/core/devtools/widget/shader_list.cpp b/src/core/devtools/widget/shader_list.cpp index 97d01896d..0285db5a5 100644 --- a/src/core/devtools/widget/shader_list.cpp +++ b/src/core/devtools/widget/shader_list.cpp @@ -24,16 +24,33 @@ using namespace ImGui; namespace Core::Devtools::Widget { -ShaderList::Selection::Selection(int index) : index(index) { - isa_editor.SetPalette(TextEditor::GetDarkPalette()); - isa_editor.SetReadOnly(true); - glsl_editor.SetPalette(TextEditor::GetDarkPalette()); - glsl_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::GLSL()); +ShaderList::Selection::Selection(int index) + : index(index), isa_editor(std::make_unique()), + glsl_editor(std::make_unique()) { + isa_editor->SetPalette(TextEditor::GetDarkPalette()); + isa_editor->SetReadOnly(true); + glsl_editor->SetPalette(TextEditor::GetDarkPalette()); + glsl_editor->SetLanguageDefinition(TextEditor::LanguageDefinition::GLSL()); presenter->GetWindow().RequestKeyboard(); } ShaderList::Selection::~Selection() { - presenter->GetWindow().ReleaseKeyboard(); + if (index >= 0) { + presenter->GetWindow().ReleaseKeyboard(); + } +} + +ShaderList::Selection::Selection(Selection&& other) noexcept + : index{other.index}, isa_editor{std::move(other.isa_editor)}, + glsl_editor{std::move(other.glsl_editor)}, open{other.open}, showing_bin{other.showing_bin}, + patch_path{std::move(other.patch_path)}, patch_bin_path{std::move(other.patch_bin_path)} { + other.index = -1; +} + +ShaderList::Selection& ShaderList::Selection::operator=(Selection other) { + using std::swap; + swap(*this, other); + return *this; } void ShaderList::Selection::ReloadShader(DebugStateType::ShaderDump& value) { @@ -72,13 +89,13 @@ bool ShaderList::Selection::DrawShader(DebugStateType::ShaderDump& value) { value.is_patched = !value.patch_spv.empty(); if (!value.is_patched) { // No patch - isa_editor.SetText(value.cache_isa_disasm); - glsl_editor.SetText(value.cache_spv_disasm); + isa_editor->SetText(value.cache_isa_disasm); + glsl_editor->SetText(value.cache_spv_disasm); } else { - isa_editor.SetText(value.cache_patch_disasm); - isa_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::SPIRV()); - glsl_editor.SetText(value.patch_source); - glsl_editor.SetReadOnly(false); + isa_editor->SetText(value.cache_patch_disasm); + isa_editor->SetLanguageDefinition(TextEditor::LanguageDefinition::SPIRV()); + glsl_editor->SetText(value.patch_source); + glsl_editor->SetReadOnly(false); } } @@ -97,18 +114,18 @@ bool ShaderList::Selection::DrawShader(DebugStateType::ShaderDump& value) { if (value.patch_source.empty()) { value.patch_source = value.cache_spv_disasm; } - isa_editor.SetText(value.cache_patch_disasm); - isa_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::SPIRV()); - glsl_editor.SetText(value.patch_source); - glsl_editor.SetReadOnly(false); + isa_editor->SetText(value.cache_patch_disasm); + isa_editor->SetLanguageDefinition(TextEditor::LanguageDefinition::SPIRV()); + glsl_editor->SetText(value.patch_source); + glsl_editor->SetReadOnly(false); if (!value.patch_spv.empty()) { ReloadShader(value); } } else { - isa_editor.SetText(value.cache_isa_disasm); - isa_editor.SetLanguageDefinition(TextEditor::LanguageDefinition()); - glsl_editor.SetText(value.cache_spv_disasm); - glsl_editor.SetReadOnly(true); + isa_editor->SetText(value.cache_isa_disasm); + isa_editor->SetLanguageDefinition(TextEditor::LanguageDefinition()); + glsl_editor->SetText(value.cache_spv_disasm); + glsl_editor->SetReadOnly(true); ReloadShader(value); } } @@ -154,7 +171,7 @@ bool ShaderList::Selection::DrawShader(DebugStateType::ShaderDump& value) { compile = true; } if (save) { - value.patch_source = glsl_editor.GetText(); + value.patch_source = glsl_editor->GetText(); std::ofstream file{patch_path, std::ios::binary | std::ios::trunc}; file << value.patch_source; std::string msg = "Patch saved to "; @@ -192,7 +209,7 @@ bool ShaderList::Selection::DrawShader(DebugStateType::ShaderDump& value) { DebugState.ShowDebugMessage("Decompilation failed (Compile was ok):\n" + res); } else { - isa_editor.SetText(value.cache_patch_disasm); + isa_editor->SetText(value.cache_patch_disasm); ReloadShader(value); } } @@ -201,9 +218,9 @@ bool ShaderList::Selection::DrawShader(DebugStateType::ShaderDump& value) { } if (showing_bin) { - isa_editor.Render(value.is_patched ? "SPIRV" : "ISA", GetContentRegionAvail()); + isa_editor->Render(value.is_patched ? "SPIRV" : "ISA", GetContentRegionAvail()); } else { - glsl_editor.Render("GLSL", GetContentRegionAvail()); + glsl_editor->Render("GLSL", GetContentRegionAvail()); } End(); diff --git a/src/core/devtools/widget/shader_list.h b/src/core/devtools/widget/shader_list.h index fbb8d2070..c882b0964 100644 --- a/src/core/devtools/widget/shader_list.h +++ b/src/core/devtools/widget/shader_list.h @@ -14,14 +14,17 @@ class ShaderList { struct Selection { explicit Selection(int index); ~Selection(); + Selection(const Selection& other) = delete; + Selection(Selection&& other) noexcept; + Selection& operator=(Selection other); void ReloadShader(DebugStateType::ShaderDump& value); bool DrawShader(DebugStateType::ShaderDump& value); - int index; - TextEditor isa_editor{}; - TextEditor glsl_editor{}; + int index{-1}; + std::unique_ptr isa_editor{}; + std::unique_ptr glsl_editor{}; bool open = true; bool showing_bin = false; From e584444aa38a6f1191f622ef1b8db7617c0549c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8A=E9=A4=85=E3=81=AECreeeper?= <56744841+creeper-0910@users.noreply.github.com> Date: Thu, 23 Jan 2025 06:52:27 +0900 Subject: [PATCH 463/549] Update Japanese translation (#2209) * Update Japanese translation * Update Japanese translation * Update Japanese translation --- src/qt_gui/translations/ja_JP.ts | 164 +++++++++++++++---------------- 1 file changed, 82 insertions(+), 82 deletions(-) diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index e07d4eb25..bb9488a6b 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -19,7 +19,7 @@ This software should not be used to play games you have not legally obtained. - このソフトウェアは、合法的に入手していないゲームをプレイするために使用するものではありません。 + 非正規、非合法のゲームをプレイするためにこのソフトウェアを使用しないでください。 @@ -33,7 +33,7 @@ GameInfoClass Loading game list, please wait :3 - ゲームリストを読み込み中です。お待ちください :3 + ゲームリストを読み込み中です。しばらくお待ちください :3 Cancel @@ -52,7 +52,7 @@ Select which directory you want to install to. - Select which directory you want to install to. + インストール先のディレクトリを選択してください。 @@ -75,7 +75,7 @@ The value for location to install games is not valid. - ゲームをインストールする場所が無効です。 + ゲームのインストール場所が無効です。 @@ -130,35 +130,35 @@ Delete... - Delete... + 削除... Delete Game - Delete Game + ゲームを削除 Delete Update - Delete Update + アップデートを削除 Delete DLC - Delete DLC + DLCを削除 Compatibility... - Compatibility... + 互換性... Update database - Update database + データベースを更新 View report - View report + レポートを表示 Submit a report - Submit a report + レポートを送信 Shortcut creation @@ -182,23 +182,23 @@ Game - Game + ゲーム requiresEnableSeparateUpdateFolder_MSG - This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + この機能を利用するには、 'アップデートフォルダの分離を有効化' を有効化する必要があります。 This game has no update to delete! - This game has no update to delete! + このゲームにはアップデートがないため削除することができません! Update - Update + アップデート This game has no DLC to delete! - This game has no DLC to delete! + このゲームにはDLCがないため削除することができません! DLC @@ -206,11 +206,11 @@ Delete %1 - Delete %1 + %1 を削除 Are you sure you want to delete %1's %2 directory? - Are you sure you want to delete %1's %2 directory? + %1 の %2 ディレクトリを本当に削除しますか? @@ -241,15 +241,15 @@ Install application from a .pkg file - .pkgファイルからアプリケーションをインストールする + .pkgファイルからアプリケーションをインストール Recent Games - 最近のゲーム + 最近プレイしたゲーム Open shadPS4 Folder - Open shadPS4 Folder + shadPS4フォルダを開く Exit @@ -273,7 +273,7 @@ Tiny - 極小 + 最小 Small @@ -297,7 +297,7 @@ Elf Viewer - Elfビュワー + Elfビューアー Game Install Directory @@ -397,11 +397,11 @@ You have downloaded cheats for all the games you have installed. - インストールしたすべてのゲームのチートをダウンロードしました。 + インストールされているすべてのゲームのチートをダウンロードしました。 Patches Downloaded Successfully! - パッチが正常にダウンロードされました! + パッチが正常にダウンロードされました! All Patches available for all games have been downloaded. @@ -425,11 +425,11 @@ Only one file can be selected! - 1つのファイルしか選択できません! + 1つのファイルしか選択できません! PKG Extraction - PKG抽出 + PKGの抽出 Patch detected! @@ -473,7 +473,7 @@ PKG is a patch, please install the game first! - PKGはパッチです。ゲームを先にインストールしてください! + PKGはパッチです。ゲームを先にインストールしてください! PKG ERROR @@ -526,11 +526,11 @@ Console Language - コンソール言語 + コンソールの言語 Emulator Language - エミュレーター言語 + エミュレーターの言語 Emulator @@ -542,7 +542,7 @@ Enable Separate Update Folder - Enable Separate Update Folder + アップデートフォルダの分離を有効化 Show Game Size In List @@ -550,7 +550,7 @@ Show Splash - スプラッシュを表示する + スプラッシュ画面を表示する Is PS4 Pro @@ -566,11 +566,11 @@ Trophy Key - Trophy Key + トロフィーキー Trophy - Trophy + トロフィー Logger @@ -602,7 +602,7 @@ Hide Cursor Idle Timeout - カーソル非アクティブタイムアウト + カーソルを隠すまでの非アクティブ期間 s @@ -706,7 +706,7 @@ Disable Trophy Pop-ups - Disable Trophy Pop-ups + トロフィーのポップアップを無効化 Play title music @@ -714,19 +714,19 @@ Update Compatibility Database On Startup - Update Compatibility Database On Startup + 起動時に互換性データベースを更新する Game Compatibility - Game Compatibility + ゲームの互換性 Display Compatibility Data - Display Compatibility Data + 互換性に関するデータを表示 Update Compatibility Database - Update Compatibility Database + 互換性データベースを更新 Volume @@ -734,7 +734,7 @@ Audio Backend - Audio Backend + オーディオ バックエンド Save @@ -754,15 +754,15 @@ Point your mouse at an option to display its description. - オプションにマウスをポイントすると、その説明が表示されます。 + 設定項目にマウスをホバーすると、説明が表示されます。 consoleLanguageGroupBox - コンソール言語:\nPS4ゲームが使用する言語を設定します。\nこれはゲームがサポートする言語に設定することをお勧めしますが、地域によって異なる場合があります。 + コンソールの言語:\nPS4ゲームが使用する言語を設定します。\nゲームでサポートされている言語に設定することをお勧めしますが、地域によって異なる場合があります。 emulatorLanguageGroupBox - エミュレーター言語:\nエミュレーターのユーザーインターフェースの言語を設定します。 + エミュレーターの言語:\nエミュレーターのユーザーインターフェースの言語を設定します。 fullscreenCheckBox @@ -770,7 +770,7 @@ separateUpdatesCheckBox - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + Enable Separate Update Folder:\nゲームのアップデートを別のフォルダにインストールすることで、管理が容易になります。 showSplashCheckBox @@ -778,7 +778,7 @@ ps4proCheckBox - PS4 Proです:\nエミュレーターがPS4 PROとして動作するようにし、これをサポートするゲームで特別な機能を有効にする場合があります。 + PS4 Pro モード:\nエミュレーターがPS4 PROとして動作するようになり、PS4 PROをサポートする一部のゲームで特別な機能が有効化される場合があります。 discordRPCCheckbox @@ -790,7 +790,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. + トロフィーキー:\nトロフィーの復号に使用されるキーです。脱獄済みのコンソールから取得することができます。\n16進数のみを受け入れます。 logTypeGroupBox @@ -798,27 +798,27 @@ logFilter - ログフィルター:\n特定の情報のみを印刷するようにログをフィルタリングします。\n例: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" レベル: Trace, Debug, Info, Warning, Error, Critical - この順序で、特定のレベルはリスト内のすべての前のレベルをサイレンスし、その後のすべてのレベルをログに記録します。 + ログフィルター:\n特定の情報のみを印刷するようにログをフィルタリングします。\n例: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" \nレベル: Trace, Debug, Info, Warning, Error, Critical - レベルはこの並び通りに処理され、指定されたレベルより前のレベル ログを抑制し、それ以外のすべてのレベルをログに記録します。 updaterGroupBox - 更新:\nRelease: 非常に古いかもしれないが、より信頼性が高くテスト済みの公式バージョンを毎月リリースします。\nNightly: 最新の機能と修正がすべて含まれていますが、バグが含まれている可能性があり、安定性は低いです。 + 更新:\nRelease: 最新の機能を利用できない可能性がありますが、より信頼性が高くテストされた公式バージョンが毎月リリースされます。\nNightly: 最新の機能と修正がすべて含まれていますが、バグが含まれている可能性があり、安定性は低いです。 GUIgroupBox - タイトルミュージックを再生:\nゲームがそれをサポートしている場合、GUIでゲームを選択したときに特別な音楽を再生することを有効にします。 + タイトルミュージックを再生:\nゲームでサポートされている場合に、GUIでゲームを選択したときに特別な音楽を再生する機能を有効にします。 disableTrophycheckBox - Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + トロフィーのポップアップを無効化:\nゲーム内でのトロフィー通知を無効化します。 トロフィーの進行状況は、トロフィービューアーを使用して確認できます。(メインウィンドウでゲームを右クリック) hideCursorGroupBox - カーソルを隠す:\nカーソルが消えるタイミングを選択してください:\n決して: いつでもマウスが見えます。\nアイドル: アイダルの後に消えるまでの時間を設定します。\n常に: マウスは決して見えません。 + カーソルを隠す:\nカーソルが消えるタイミングを選択してください:\n無効: 常にカーソルが表示されます。\n非アクティブ時: カーソルの非アクティブ期間が指定した時間を超えた場合にカーソルを隠します。\n常に: カーソルは常に隠れた状態になります。 idleTimeoutGroupBox - アイドル後にマウスが消えるまでの時間を設定します。 + カーソルが非アクティブになってから隠すまでの時間を設定します。 backButtonBehaviorGroupBox @@ -826,23 +826,23 @@ enableCompatibilityCheckBox - Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + 互換性に関するデータを表示:\nゲームの互換性に関する情報を表として表示します。常に最新情報を取得したい場合、"起動時に互換性データベースを更新する" を有効化してください。 checkCompatibilityOnStartupCheckBox - Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + 起動時に互換性データベースを更新する:\nshadPS4の起動時に自動で互換性データベースを更新します。 updateCompatibilityButton - Update Compatibility Database:\nImmediately update the compatibility database. + 互換性データベースを更新する:\n今すぐ互換性データベースを更新します。 Never - 決して + 無効 Idle - アイドル + 非アクティブ時 Always @@ -850,11 +850,11 @@ Touchpad Left - タッチパッド左 + 左タッチパッド Touchpad Right - タッチパッド右 + 右タッチパッド Touchpad Center @@ -866,15 +866,15 @@ graphicsAdapterGroupBox - グラフィックデバイス:\n複数のGPUシステムで、ドロップダウンリストからエミュレーターで使用するGPUを選択するか、\n「自動選択」を選択して自動的に決定します。 + グラフィックデバイス:\nシステムに複数のGPUが搭載されている場合、ドロップダウンリストからエミュレーターで使用するGPUを選択するか、\n「自動選択」を選択して自動的に決定します。 resolutionLayout - 幅/高さ:\n起動時にエミュレーターウィンドウのサイズを設定します。ゲーム中にサイズ変更できます。\nこれはゲーム内の解像度とは異なります。 + 幅/高さ:\n起動時にエミュレーターウィンドウのサイズを設定します。ゲーム中でもサイズを変更することができます。\nこれはゲーム内の解像度とは異なります。 heightDivider - Vblankディバイダー:\nエミュレーターが更新されるフレームレートにこの数を掛けます。これを変更すると、ゲームの速度が上がったり、想定外の変更がある場合、ゲームの重要な機能が壊れる可能性があります! + Vblankディバイダー:\nエミュレーターが更新されるフレームレートにこの数を掛けます。これを変更すると、ゲームの速度が上がったり、想定外の変更がある場合、ゲームの重要な機能が壊れる可能性があります! dumpShadersCheckBox @@ -917,11 +917,11 @@ CheatsPatches Cheats / Patches for - Cheats / Patches for + のチート/パッチ defaultTextEdit_MSG - チート/パッチは実験的です。\n使用には注意してください。\n\nリポジトリを選択し、ダウンロードボタンをクリックしてチートを個別にダウンロードします。\n「Patches」タブでは、すべてのパッチを一度にダウンロードし、使用したいものを選択して選択を保存できます。\n\nチート/パッチを開発していないため、\n問題があればチートの作者に報告してください。\n\n新しいチートを作成しましたか?\nhttps://github.com/shadps4-emu/ps4_cheats を訪問してください。 + チート/パッチは実験的です。\n使用には注意してください。\n\nリポジトリを選択し、ダウンロードボタンをクリックしてチートを個別にダウンロードします。\n「Patches」タブでは、すべてのパッチを一度にダウンロードし、使用したいものを選択して選択を保存できます。\n\nチート/パッチは開発を行っていないため、\n問題があればチートの作者に報告してください。\n\n新しいチートを作成しましたか?\nhttps://github.com/shadps4-emu/ps4_cheats を訪問してください。 No Image Available @@ -997,7 +997,7 @@ Unable to open files.json for reading. - files.jsonを読み込み用に開けません。 + files.jsonを読み取りのために開く事が出来ませんでした。 No patch file found for the current serial. @@ -1005,11 +1005,11 @@ Unable to open the file for reading. - ファイルを読み込み用に開けません。 + ファイルを読み取りのために開く事が出来ませんでした。 Unable to open the file for writing. - ファイルを記録用に開けません。 + ファイルをを書き込みのために開く事が出来ませんでした。 Failed to parse XML: @@ -1029,7 +1029,7 @@ The selected source is invalid. - 選択したソースは無効です。 + 選択されたソースは無効です。 File Exists @@ -1113,7 +1113,7 @@ Failed to open files.json for writing - files.jsonを記録用に開けません + files.jsonを読み取りのために開く事が出来ませんでした。 Author: @@ -1125,7 +1125,7 @@ Failed to open files.json for reading. - files.jsonを読み込み用に開けません。 + files.jsonを読み取りのために開く事が出来ませんでした。 Name: @@ -1180,43 +1180,43 @@ Never Played - Never Played + 未プレイ h - h + 時間 m - m + s - s + Compatibility is untested - Compatibility is untested + 互換性は未検証です Game does not initialize properly / crashes the emulator - Game does not initialize properly / crashes the emulator + ゲームが正常に初期化されない/エミュレーターがクラッシュする Game boots, but only displays a blank screen - Game boots, but only displays a blank screen + ゲームは起動しますが、空のスクリーンが表示されます Game displays an image but does not go past the menu - Game displays an image but does not go past the menu + 正常にゲーム画面が表示されますが、メニューから先に進むことができません Game has game-breaking glitches or unplayable performance - Game has game-breaking glitches or unplayable performance + ゲームを壊すような不具合や、プレイが不可能なほどのパフォーマンスの問題があります Game can be completed with playable performance and no major glitches - Game can be completed with playable performance and no major glitches + パフォーマンスに問題はなく、大きな不具合なしでゲームをプレイすることができます From c015ac134462b3c425d69953e27da0e6c8ca9498 Mon Sep 17 00:00:00 2001 From: tomboylover93 <95257311+tomboylover93@users.noreply.github.com> Date: Wed, 22 Jan 2025 13:53:27 -0800 Subject: [PATCH 464/549] Update Linux building documentation (#2211) * wip: redo build instructions guide for Linux * Add Linux screenshots directory to REUSE.toml * change links for Visual Studio Code images * change instructions for building from terminal reference: https://github.com/shadps4-emu/shadPS4/pull/2211#issuecomment-2608134455 --- REUSE.toml | 1 + documents/Screenshots/Linux/1.png | Bin 0 -> 11447 bytes documents/Screenshots/Linux/2.png | Bin 0 -> 20159 bytes documents/Screenshots/Linux/3.png | Bin 0 -> 25540 bytes documents/Screenshots/Linux/4.png | Bin 0 -> 16845 bytes documents/Screenshots/Linux/5.png | Bin 0 -> 26893 bytes documents/building-linux.md | 103 +++++++++++++++++++++++++----- 7 files changed, 89 insertions(+), 15 deletions(-) create mode 100644 documents/Screenshots/Linux/1.png create mode 100644 documents/Screenshots/Linux/2.png create mode 100644 documents/Screenshots/Linux/3.png create mode 100644 documents/Screenshots/Linux/4.png create mode 100644 documents/Screenshots/Linux/5.png diff --git a/REUSE.toml b/REUSE.toml index 55d76673d..a62974bcd 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -14,6 +14,7 @@ path = [ "documents/changelog.md", "documents/Quickstart/2.png", "documents/Screenshots/*", + "documents/Screenshots/Linux/*", "externals/MoltenVK/MoltenVK_icd.json", "scripts/ps4_names.txt", "src/images/about_icon.png", diff --git a/documents/Screenshots/Linux/1.png b/documents/Screenshots/Linux/1.png new file mode 100644 index 0000000000000000000000000000000000000000..3dd5ef92b39960dcf98cf22d47f3895d84884581 GIT binary patch literal 11447 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 OgJdKW#s7Xb^#4D*Qdz11 literal 0 HcmV?d00001 diff --git a/documents/Screenshots/Linux/2.png b/documents/Screenshots/Linux/2.png new file mode 100644 index 0000000000000000000000000000000000000000..fc5ff3b534773394c892a9d026a4d5e553ed2cfa GIT binary patch literal 20159 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

IFt#Ds2NY9n;YzAXc!7BTm6;cMp= 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 HFG2qUd8Vx* literal 0 HcmV?d00001 diff --git a/documents/Screenshots/Linux/4.png b/documents/Screenshots/Linux/4.png new file mode 100644 index 0000000000000000000000000000000000000000..6e8a3b6c1e9a39d06bfc9ec0364db3b1f0b7a5bc GIT binary patch literal 16845 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 r -## Build shadPS4 for Linux +## Build shadPS4 for Linux -### Install the necessary tools to build shadPS4: +First and foremost, Clang 18 is the **recommended compiler** as it is used for official builds and CI. If you build with GCC, you might encounter issues — please report any you find. Additionally, if you choose to use GCC, please build shadPS4 with Clang at least once before creating an `[APP BUG]` issue or submitting a pull request. + +## Preparatory steps + +### Installing dependencies #### Debian & Ubuntu + ``` sudo apt install build-essential clang git cmake libasound2-dev libpulse-dev libopenal-dev libssl-dev zlib1g-dev libedit-dev libudev-dev libevdev-dev libsdl2-dev libjack-dev libsndio-dev qt6-base-dev qt6-tools-dev qt6-multimedia-dev libvulkan-dev vulkan-validationlayers ``` #### Fedora + ``` sudo dnf install clang git cmake libatomic alsa-lib-devel pipewire-jack-audio-connection-kit-devel openal-devel openssl-devel libevdev-devel libudev-devel libXext-devel qt6-qtbase-devel qt6-qtbase-private-devel qt6-qtmultimedia-devel qt6-qtsvg-devel qt6-qttools-devel vulkan-devel vulkan-validation-layers ``` #### Arch Linux + ``` sudo pacman -S base-devel clang git cmake sndio jack2 openal qt6-base qt6-declarative qt6-multimedia sdl2 vulkan-validation-layers ``` +**Note**: The `shadps4-git` AUR package is not maintained by any of the developers, and it uses GCC as the compiler as opposed to Clang. Use at your own discretion. #### OpenSUSE + ``` sudo zypper install clang git cmake libasound2 libpulse-devel libsndio7 libjack-devel openal-soft-devel libopenssl-devel zlib-devel libedit-devel systemd-devel libevdev-devel qt6-base-devel qt6-multimedia-devel qt6-svg-devel qt6-linguist-devel qt6-gui-private-devel vulkan-devel vulkan-validationlayers ``` -### Cloning and compiling: -Clone the repository recursively: +#### Other Linux distributions + +You can try one of two methods: + +- Search the packages by name and install them with your package manager, or +- Install [distrobox](https://distrobox.it/), create a container using any of the distributions cited above as a base, for Arch Linux you'd do: + +``` +distrobox create --name archlinux --init --image archlinux:latest +``` + +and install the dependencies on that container as cited above. +This option is **highly recommended** for NixOS and distributions with immutable/atomic filesystems (example: Fedora Kinoite, SteamOS). +### Cloning + ``` git clone --recursive https://github.com/shadps4-emu/shadPS4.git cd shadPS4 ``` -Generate the build directory in the shadPS4 directory. To disable the QT GUI, remove the ```-DENABLE_QT_GUI=ON``` flag: +## Building + +There are 3 options you can choose from. Option 1 is **highly recommended**. + +#### Option 1: Terminal-only + +1. Generate the build directory in the shadPS4 directory. -**Note**: Clang is the compiler used for official builds and CI. If you build with GCC, you might encounter issues—please report any you find. If you choose to use GCC, we recommend building with Clang at least once before submitting a pull request. ``` cmake -S . -B build/ -DENABLE_QT_GUI=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ ``` -Enter the directory: +To disable the Qt GUI, remove the `-DENABLE_QT_GUI=ON` flag. To change the build type (for debugging), add `-DCMAKE_BUILD_TYPE=Debug`. + +2. Use CMake to build the project: + ``` -cd build/ +cmake --build ./build --parallel$(nproc) ``` -Use make to build the project: +If your computer freezes during this step, this could be caused by excessive system resource usage. In that case, remove `--parallel$(nproc)`. + +Now run the emulator. If Qt was enabled at configure time: + ``` -cmake --build . --parallel$(nproc) +./build/shadps4 ``` -Now run the emulator. If QT is enabled: -``` -./shadps4 -``` Otherwise, specify the path to your PKG's boot file: + ``` -./shadps4 /"PATH"/"TO"/"GAME"/"FOLDER"/eboot.bin +./build/shadps4 /"PATH"/"TO"/"GAME"/"FOLDER"/eboot.bin ``` + +You can also specify the Game ID as an argument for which game to boot, as long as the folder containing the games is specified in config.toml (example: Bloodborne (US) is CUSA00900). +#### Option 2: Configuring with cmake-gui + +`cmake-gui` should be installed by default alongside `cmake`, if not search for the package in your package manager and install it. + +Open `cmake-gui` and specify the source code and build directories. If you cloned the source code to your Home directory, it would be `/home/user/shadPS4` and `/home/user/shadPS4/build`. + +Click on Configure, select "Unix Makefiles", select "Specify native compilers", click Next and choose `clang` and `clang++` as the C and CXX compilers. Usually they are located in `/bin/clang` and `/bin/clang++`. Click on Finish and let it configure the project. + +Now every option should be displayed in red. Change anything you want, then click on Generate to make the changes permanent, then open a terminal window and do steps 2 and 3 of Option 1. + +#### Option 3: Visual Studio Code + +This option is pretty convoluted and should only be used if you have VSCode as your default IDE, or just prefer building and debugging projects through it. This also assumes that you're using an Arch Linux environment, as the naming for some options might differ from other distros. + +[Download Visual Studio Code for your platform](https://code.visualstudio.com/download), or use [Code - OSS](https://github.com/microsoft/vscode) if you'd like. Code - OSS is available on most Linux distributions' package repositories (on Arch Linux it is simply named `code`). + +Once set up, go to Extensions and install "CMake Tools": + +![image](https://raw.githubusercontent.com/shadps4-emu/shadPS4/refs/heads/main/documents/Screenshots/Linux/3.png) + +You can also install other CMake and Clang related extensions if you'd like, but this one is what enables you to configure and build CMake projects directly within VSCode. + +Go to Settings, filter by `@ext:ms-vscode.cmake-tools configure` and disable this option: + +![image](https://raw.githubusercontent.com/shadps4-emu/shadPS4/refs/heads/main/documents/Screenshots/Linux/1.png) + +If you wish to build with the Qt GUI, add `-DENABLE_QT_GUI=ON` to the configure arguments: + +![image](https://raw.githubusercontent.com/shadps4-emu/shadPS4/refs/heads/main/documents/Screenshots/Linux/2.png) + +On the CMake tab, change the options as you wish, but make sure that it looks similar to or exactly like this: + +![image](https://raw.githubusercontent.com/shadps4-emu/shadPS4/refs/heads/main/documents/Screenshots/Linux/4.png) + +When hovering over Project Status > Configure, there should be an icon titled "Configure". Click on it and let it configure the project, then do the same for Project Status > Build. + +If you want to debug it, change the build type under Project Status > Configure to Debug (it should be the default) and compile it, then click on the icon in Project Status > Debug. If you simply want to launch the shadPS4 executable from within VSCode, click on the icon in Project Status > Launch. + +Don't forget to change the launch target for both options to the shadPS4 executable inside shadPS4/build: + +![image](https://raw.githubusercontent.com/shadps4-emu/shadPS4/refs/heads/main/documents/Screenshots/Linux/5.png) \ No newline at end of file From d7c2cb17f3e1b339d78c5eb1bcb44e0305a85e89 Mon Sep 17 00:00:00 2001 From: panzone91 <150828896+panzone91@users.noreply.github.com> Date: Wed, 22 Jan 2025 21:53:54 +0000 Subject: [PATCH 465/549] update extension vector capacity (#2210) --- src/video_core/renderer_vulkan/vk_instance.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index d183d6b09..a722b5322 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -232,7 +232,7 @@ bool Instance::CreateDevice() { return false; } - boost::container::static_vector enabled_extensions; + boost::container::static_vector enabled_extensions; const auto add_extension = [&](std::string_view extension) -> bool { const auto result = std::find_if(available_extensions.begin(), available_extensions.end(), From 1fcfb07421909ab779ee1a884b6a5ce0fb0fd4e3 Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Wed, 22 Jan 2025 19:21:52 -0300 Subject: [PATCH 466/549] GUI: Settings improvements (#2213) --- src/common/config.cpp | 13 +- src/common/config.h | 2 + src/qt_gui/settings_dialog.cpp | 23 +- src/qt_gui/settings_dialog.ui | 1336 ++++++++++++++++++------------ src/qt_gui/translations/ar.ts | 18 +- src/qt_gui/translations/da_DK.ts | 18 +- src/qt_gui/translations/de.ts | 18 +- src/qt_gui/translations/el.ts | 18 +- src/qt_gui/translations/en.ts | 18 +- src/qt_gui/translations/es_ES.ts | 18 +- src/qt_gui/translations/fa_IR.ts | 18 +- src/qt_gui/translations/fi.ts | 18 +- src/qt_gui/translations/fr.ts | 18 +- src/qt_gui/translations/hu_HU.ts | 18 +- src/qt_gui/translations/id.ts | 18 +- src/qt_gui/translations/it.ts | 18 +- src/qt_gui/translations/ja_JP.ts | 18 +- src/qt_gui/translations/ko_KR.ts | 18 +- src/qt_gui/translations/lt_LT.ts | 18 +- src/qt_gui/translations/nb.ts | 18 +- src/qt_gui/translations/nl.ts | 18 +- src/qt_gui/translations/pl_PL.ts | 18 +- src/qt_gui/translations/pt_BR.ts | 24 +- src/qt_gui/translations/ro_RO.ts | 18 +- src/qt_gui/translations/ru_RU.ts | 18 +- src/qt_gui/translations/sq.ts | 18 +- src/qt_gui/translations/sv.ts | 18 +- src/qt_gui/translations/tr_TR.ts | 18 +- src/qt_gui/translations/uk_UA.ts | 18 +- src/qt_gui/translations/vi_VN.ts | 18 +- src/qt_gui/translations/zh_CN.ts | 18 +- src/qt_gui/translations/zh_TW.ts | 18 +- 32 files changed, 1339 insertions(+), 545 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index 6e9db50ff..a57b6d35c 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -45,6 +45,7 @@ static std::string logFilter; static std::string logType = "async"; static std::string userName = "shadPS4"; static std::string updateChannel; +static std::string chooseHomeTab; static u16 deadZoneLeft = 2.0; static u16 deadZoneRight = 2.0; static std::string backButtonBehavior = "left"; @@ -194,6 +195,10 @@ std::string getUpdateChannel() { return updateChannel; } +std::string getChooseHomeTab() { + return chooseHomeTab; +} + std::string getBackButtonBehavior() { return backButtonBehavior; } @@ -399,6 +404,9 @@ void setUserName(const std::string& type) { void setUpdateChannel(const std::string& type) { updateChannel = type; } +void setChooseHomeTab(const std::string& type) { + chooseHomeTab = type; +} void setBackButtonBehavior(const std::string& type) { backButtonBehavior = type; @@ -637,6 +645,7 @@ void load(const std::filesystem::path& path) { compatibilityData = toml::find_or(general, "compatibilityEnabled", false); checkCompatibilityOnStartup = toml::find_or(general, "checkCompatibilityOnStartup", false); + chooseHomeTab = toml::find_or(general, "chooseHomeTab", "Release"); } if (data.contains("Input")) { @@ -760,6 +769,7 @@ void save(const std::filesystem::path& path) { data["General"]["logType"] = logType; data["General"]["userName"] = userName; data["General"]["updateChannel"] = updateChannel; + data["General"]["chooseHomeTab"] = chooseHomeTab; data["General"]["showSplash"] = isShowSplash; data["General"]["autoUpdate"] = isAutoUpdate; data["General"]["separateUpdateEnabled"] = separateupdatefolder; @@ -871,6 +881,7 @@ void setDefaultValues() { } else { updateChannel = "Nightly"; } + chooseHomeTab = "General"; cursorState = HideCursorState::Idle; cursorHideTimeout = 5; backButtonBehavior = "left"; @@ -898,4 +909,4 @@ void setDefaultValues() { checkCompatibilityOnStartup = false; } -} // namespace Config +} // namespace Config \ No newline at end of file diff --git a/src/common/config.h b/src/common/config.h index 564947f1e..15937606e 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -35,6 +35,7 @@ std::string getLogFilter(); std::string getLogType(); std::string getUserName(); std::string getUpdateChannel(); +std::string getChooseHomeTab(); u16 leftDeadZone(); u16 rightDeadZone(); @@ -81,6 +82,7 @@ void setLanguage(u32 language); void setNeoMode(bool enable); void setUserName(const std::string& type); void setUpdateChannel(const std::string& type); +void setChooseHomeTab(const std::string& type); void setSeparateUpdateEnabled(bool use); void setGameInstallDirs(const std::vector& settings_install_dirs_config); void setSaveDataPath(const std::filesystem::path& path); diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index 5695d6232..b41fde745 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -65,6 +65,7 @@ SettingsDialog::SettingsDialog(std::span physical_devices, : QDialog(parent), ui(new Ui::SettingsDialog) { ui->setupUi(this); ui->tabWidgetSettings->setUsesScrollButtons(false); + initialHeight = this->height(); const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); @@ -150,7 +151,6 @@ SettingsDialog::SettingsDialog(std::span physical_devices, }); #else ui->updaterGroupBox->setVisible(false); - ui->GUIgroupBox->setMaximumSize(265, 16777215); #endif connect(ui->updateCompatibilityButton, &QPushButton::clicked, this, [this, parent, m_compat_info]() { @@ -169,6 +169,11 @@ SettingsDialog::SettingsDialog(std::span physical_devices, }); } + // Gui TAB + { + connect(ui->chooseHomeTabComboBox, &QComboBox::currentTextChanged, this, + [](const QString& hometab) { Config::setChooseHomeTab(hometab.toStdString()); }); + } // Input TAB { connect(ui->hideCursorComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, @@ -246,7 +251,7 @@ SettingsDialog::SettingsDialog(std::span physical_devices, #ifdef ENABLE_UPDATER ui->updaterGroupBox->installEventFilter(this); #endif - ui->GUIgroupBox->installEventFilter(this); + ui->GUIMusicGroupBox->installEventFilter(this); ui->disableTrophycheckBox->installEventFilter(this); ui->enableCompatibilityCheckBox->installEventFilter(this); ui->checkCompatibilityOnStartupCheckBox->installEventFilter(this); @@ -373,6 +378,15 @@ void SettingsDialog::LoadValuesFromConfig() { ui->updateComboBox->setCurrentText(QString::fromStdString(updateChannel)); #endif + std::string chooseHomeTab = toml::find_or(data, "General", "chooseHomeTab", ""); + ui->chooseHomeTabComboBox->setCurrentText(QString::fromStdString(chooseHomeTab)); + QStringList tabNames = {tr("General"), tr("Gui"), tr("Graphics"), tr("User"), + tr("Input"), tr("Paths"), tr("Debug")}; + QString chooseHomeTabQString = QString::fromStdString(chooseHomeTab); + int indexTab = tabNames.indexOf(chooseHomeTabQString); + indexTab = (indexTab == -1) ? 0 : indexTab; + ui->tabWidgetSettings->setCurrentIndex(indexTab); + QString backButtonBehavior = QString::fromStdString( toml::find_or(data, "Input", "backButtonBehavior", "left")); int index = ui->backButtonBehaviorComboBox->findData(backButtonBehavior); @@ -476,8 +490,8 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) { } else if (elementName == "updaterGroupBox") { text = tr("updaterGroupBox"); #endif - } else if (elementName == "GUIgroupBox") { - text = tr("GUIgroupBox"); + } else if (elementName == "GUIMusicGroupBox") { + text = tr("GUIMusicGroupBox"); } else if (elementName == "disableTrophycheckBox") { text = tr("disableTrophycheckBox"); } else if (elementName == "enableCompatibilityCheckBox") { @@ -592,6 +606,7 @@ void SettingsDialog::UpdateSettings() { Config::setRdocEnabled(ui->rdocCheckBox->isChecked()); Config::setAutoUpdate(ui->updateCheckBox->isChecked()); Config::setUpdateChannel(ui->updateComboBox->currentText().toStdString()); + Config::setChooseHomeTab(ui->chooseHomeTabComboBox->currentText().toStdString()); Config::setCompatibilityEnabled(ui->enableCompatibilityCheckBox->isChecked()); Config::setCheckCompatibilityOnStartup(ui->checkCompatibilityOnStartupCheckBox->isChecked()); diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index d72a56973..5da6a8198 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -12,11 +12,11 @@ 0 0 970 - 820 + 600 - + 0 0 @@ -52,8 +52,14 @@ 0 + + + 0 + 0 + + - 3 + 0 @@ -67,8 +73,8 @@ 0 0 - 771 - 606 + 946 + 386 @@ -77,14 +83,56 @@ 0 - + + 6 + + + + 0 + + + 0 + + + 0 + + + + 0 + 0 + + Emulator - + + Qt::AlignmentFlag::AlignBottom|Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft + + + false + + + false + + + + 6 + + + 9 + + + 9 + + + 9 + + + 9 + @@ -106,7 +154,7 @@ - + 0 0 @@ -149,215 +197,40 @@ - - - - 6 - - - 0 - - - - - - - Username - - - - - - - - - - - - - - + + + + 6 + + + 0 + - + - + 0 0 - + 0 0 - - GUI Settings - - - - 1 - - - 11 - - - - - Show Game Size In List - - - - - - - - 0 - 0 - - - - Play title music - - - - - - - 1 - - - 0 - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - Volume - - - - - - - Set the volume of the background music. - - - 100 - - - 10 - - - 20 - - - 50 - - - Qt::Orientation::Horizontal - - - false - - - false - - - QSlider::TickPosition::NoTicks - - - 10 - - - - - - - - - 6 - - - 0 - - - 50 - - - - - - - Trophy - - - - - - Disable Trophy Pop-ups - - - - - - - Trophy Key - - - - - - - - 0 - 0 - - - - - 10 - false - - - - - - - - - - - - - - - - - - - - System + + 70 + @@ -387,6 +260,45 @@ + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + @@ -432,16 +344,16 @@ - 10 + 6 - 1 + 9 - 11 + 9 - 190 + 80 @@ -508,7 +420,7 @@ - + 0 0 @@ -554,8 +466,255 @@ - + + + + + + + + true + + + Gui + + + + + 0 + 0 + 946 + 386 + + + + + + + 0 + + + 0 + + + + + 0 + + + + + + 0 + 0 + + + + + 0 + 0 + + + + GUI Settings + + + + 9 + + + 9 + + + + + Default tab when opening settings + + + + + + + 0 + 0 + + + + + General + + + + + Gui + + + + + Graphics + + + + + User + + + + + Input + + + + + Paths + + + + + Debug + + + + + + + + + + + Show Game Size In List + + + + + + + + 0 + 0 + + + + + 11 + false + true + + + + false + + + + + + false + + + + + 10 + 80 + 416 + 29 + + + + + 0 + 0 + + + + Set the volume of the background music. + + + 100 + + + 10 + + + 20 + + + 50 + + + Qt::Orientation::Horizontal + + + false + + + false + + + QSlider::TickPosition::NoTicks + + + 10 + + + + + + 10 + 22 + 416 + 26 + + + + + 0 + 0 + + + + Play title music + + + + + + 10 + 54 + 416 + 20 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Volume + + + + + + + + + + + + 6 + + + 210 + @@ -570,6 +729,12 @@ 0 + + + 0 + 0 + + Game Compatibility @@ -578,10 +743,10 @@ 10 - 1 + 9 - 11 + 9 @@ -632,6 +797,394 @@ + + + true + + + Graphics + + + + + 0 + 0 + 946 + 386 + + + + + + + + + + + Graphics Device + + + + + + + + + Enable NULL GPU + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + 6 + + + 0 + + + + + + + Width + + + + + + true + + + QAbstractSpinBox::CorrectionMode::CorrectToNearestValue + + + false + + + 0 + + + 9999 + + + 1280 + + + + + + + + + + Height + + + + + + true + + + true + + + QAbstractSpinBox::CorrectionMode::CorrectToNearestValue + + + false + + + 0 + + + 9999 + + + 720 + + + + + + + + + + Vblank Divider + + + + + + true + + + true + + + QAbstractSpinBox::CorrectionMode::CorrectToNearestValue + + + false + + + 1 + + + 9999 + + + 1 + + + + + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + 12 + + + 12 + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + true + + + User + + + + + 0 + 0 + 946 + 386 + + + + + + + 0 + + + 0 + + + + + + + Username + + + + + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 40 + + + + + + + + + + 6 + + + 0 + + + 50 + + + + + + + Trophy + + + + + + Disable Trophy Pop-ups + + + + + + + Trophy Key + + + + + + + + 0 + 0 + + + + + 10 + false + + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::Orientation::Vertical + + + QSizePolicy::Policy::MinimumExpanding + + + + 0 + 0 + + + + + + + true @@ -644,8 +1197,8 @@ 0 0 - 455 - 252 + 946 + 386 @@ -916,260 +1469,6 @@ - - - true - - - Graphics - - - - - 0 - 0 - 579 - 194 - - - - - - - - - - - Graphics Device - - - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - - - - - 6 - - - 0 - - - - - - - Width - - - - - - true - - - QAbstractSpinBox::CorrectionMode::CorrectToNearestValue - - - false - - - 0 - - - 9999 - - - 1280 - - - - - - - - - - Height - - - - - - true - - - true - - - QAbstractSpinBox::CorrectionMode::CorrectToNearestValue - - - false - - - 0 - - - 9999 - - - 720 - - - - - - - - - - - - - - 6 - - - 0 - - - - - - - Vblank Divider - - - - - - true - - - true - - - QAbstractSpinBox::CorrectionMode::CorrectToNearestValue - - - false - - - 1 - - - 9999 - - - 1 - - - - - - - - - - - - - - - - 12 - - - 12 - - - - - true - - - Advanced - - - Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop - - - - - - Enable Shaders Dumping - - - - - - - Enable NULL GPU - - - - - - - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - - - - - - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - - - - true @@ -1178,6 +1477,14 @@ Paths + + + 0 + 0 + 946 + 386 + + @@ -1186,34 +1493,34 @@ - - - - - Add... - - - - - - - Remove - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - + + + + + Add... + + + + + + + Remove + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + @@ -1225,27 +1532,27 @@ - Save Data Path + Save Data Path - - - - - - true - - - - - - - Browse - - - - - + + + + + + true + + + + + + + Browse + + + + + @@ -1264,13 +1571,13 @@ 0 0 - 510 - 269 + 946 + 386 - + 0 @@ -1291,6 +1598,13 @@ Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop + + + + Enable Shaders Dumping + + + diff --git a/src/qt_gui/translations/ar.ts b/src/qt_gui/translations/ar.ts index 47bd673b2..f7b93028a 100644 --- a/src/qt_gui/translations/ar.ts +++ b/src/qt_gui/translations/ar.ts @@ -540,10 +540,18 @@ Enable Fullscreen تمكين ملء الشاشة + + Fullscreen Mode + وضع ملء الشاشة + Enable Separate Update Folder Enable Separate Update Folder + + Default tab when opening settings + علامة التبويب الافتراضية عند فتح الإعدادات + Show Game Size In List عرض حجم اللعبة في القائمة @@ -620,6 +628,14 @@ Graphics الرسومات + + Gui + واجهة + + + User + مستخدم + Graphics Device جهاز الرسومات @@ -805,7 +821,7 @@ تحديث: Release: إصدارات رسمية تصدر شهريًا، قد تكون قديمة بعض الشيء، لكنها أكثر استقرارًا واختبارًا. Nightly: إصدارات تطوير تحتوي على أحدث الميزات والإصلاحات، لكنها قد تحتوي على أخطاء وأقل استقرارًا. - GUIgroupBox + GUIMusicGroupBox تشغيل موسيقى العنوان:\nإذا كانت اللعبة تدعم ذلك، قم بتمكين تشغيل موسيقى خاصة عند اختيار اللعبة في واجهة المستخدم. diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts index 91a98abd4..61d2f68bf 100644 --- a/src/qt_gui/translations/da_DK.ts +++ b/src/qt_gui/translations/da_DK.ts @@ -540,10 +540,18 @@ Enable Fullscreen Enable Fullscreen + + Fullscreen Mode + Fuldskærmstilstand + Enable Separate Update Folder Enable Separate Update Folder + + Default tab when opening settings + Standardfaneblad ved åbning af indstillinger + Show Game Size In List Vis vis spilstørrelse i listen @@ -620,6 +628,14 @@ Graphics Graphics + + Gui + Interface + + + User + Bruger + Graphics Device Graphics Device @@ -805,7 +821,7 @@ Opdatering:\nRelease: Officielle builds, der frigives månedligt, som kan være meget ældre, men mere stabile og testet.\nNightly: Udviklerbuilds med de nyeste funktioner og rettelser, men som kan indeholde fejl og være mindre stabile. - GUIgroupBox + GUIMusicGroupBox Titelsmusikafspilning:\nHvis spillet understøtter det, aktiver speciel musik, når spillet vælges i brugergrænsefladen. diff --git a/src/qt_gui/translations/de.ts b/src/qt_gui/translations/de.ts index b1e1d2664..9d99a7048 100644 --- a/src/qt_gui/translations/de.ts +++ b/src/qt_gui/translations/de.ts @@ -540,10 +540,18 @@ Enable Fullscreen Vollbild aktivieren + + Fullscreen Mode + Vollbildmodus + Enable Separate Update Folder Enable Separate Update Folder + + Default tab when opening settings + Standardregisterkarte beim Öffnen der Einstellungen + Show Game Size In List Zeigen Sie die Spielgröße in der Liste @@ -620,6 +628,14 @@ Graphics Grafik + + Gui + Benutzeroberfläche + + + User + Benutzer + Graphics Device Grafikgerät @@ -805,7 +821,7 @@ Update:\nRelease: Offizielle Builds, die monatlich veröffentlicht werden, können viel älter sein, aber stabiler und getestet.\nNightly: Entwickler-Builds, die die neuesten Funktionen und Fehlerbehebungen enthalten, aber Fehler enthalten und weniger stabil sein können. - GUIgroupBox + GUIMusicGroupBox Wiedergabe der Titelmusik:\nWenn das Spiel dies unterstützt, wird beim Auswählen des Spiels in der Benutzeroberfläche spezielle Musik abgespielt. diff --git a/src/qt_gui/translations/el.ts b/src/qt_gui/translations/el.ts index ecda0ede0..66c3c07cd 100644 --- a/src/qt_gui/translations/el.ts +++ b/src/qt_gui/translations/el.ts @@ -540,10 +540,18 @@ Enable Fullscreen Enable Fullscreen + + Fullscreen Mode + Λειτουργία Πλήρους Οθόνης + Enable Separate Update Folder Enable Separate Update Folder + + Default tab when opening settings + Προεπιλεγμένη καρτέλα κατά την ανοίγμα των ρυθμίσεων + Show Game Size In List Εμφάνιση Μεγέθους Παιχνιδιού στη Λίστα @@ -620,6 +628,14 @@ Graphics Graphics + + Gui + Διεπαφή + + + User + Χρήστης + Graphics Device Graphics Device @@ -805,7 +821,7 @@ Ενημερώσεις:\nRelease: Επίσημες εκδόσεις που κυκλοφορούν μηνιαίως, είναι παλαιότερες αλλά πιο σταθερές και δοκιμασμένες.\nNightly: Εκδόσεις προγραμματιστών με νέες δυνατότητες και διορθώσεις, αλλά μπορεί να περιέχουν σφάλματα και να είναι λιγότερο σταθερές. - GUIgroupBox + GUIMusicGroupBox Αναπαραγωγή Μουσικής Τίτλων:\nΕάν το παιχνίδι το υποστηρίζει, ενεργοποιεί ειδική μουσική κατά την επιλογή του παιχνιδιού από τη διεπαφή χρήστη. diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index dc72d97c9..8b6c3c69f 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -540,10 +540,18 @@ Enable Fullscreen Enable Fullscreen + + Fullscreen Mode + Fullscreen Mode + Enable Separate Update Folder Enable Separate Update Folder + + Default tab when opening settings + Default tab when opening settings + Show Game Size In List Show Game Size In List @@ -620,6 +628,14 @@ Graphics Graphics + + Gui + Gui + + + User + User + Graphics Device Graphics Device @@ -805,7 +821,7 @@ Update:\nRelease: Official versions released every month that may be very outdated, but are more reliable and tested.\nNightly: Development versions that have all the latest features and fixes, but may contain bugs and are less stable. - GUIgroupBox + GUIMusicGroupBox Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI. diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index a47f7c577..8f7a09b5d 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -540,10 +540,18 @@ Enable Fullscreen Habilitar pantalla completa + + Fullscreen Mode + Modo de Pantalla Completa + Enable Separate Update Folder Enable Separate Update Folder + + Default tab when opening settings + Pestaña predeterminada al abrir la configuración + Show Game Size In List Mostrar Tamaño del Juego en la Lista @@ -620,6 +628,14 @@ Graphics Gráficos + + Gui + Interfaz + + + User + Usuario + Graphics Device Dispositivo gráfico @@ -805,7 +821,7 @@ Actualización:\nRelease: Versiones oficiales lanzadas cada mes que pueden estar muy desactualizadas, pero son más confiables y están probadas.\nNightly: Versiones de desarrollo que tienen todas las últimas funciones y correcciones, pero pueden contener errores y son menos estables. - GUIgroupBox + GUIMusicGroupBox Reproducir Música del Título:\nSi un juego lo admite, habilita la reproducción de música especial al seleccionar el juego en la interfaz gráfica. diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index 976e7614e..043d782c2 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -540,10 +540,18 @@ Enable Fullscreen تمام صفحه + + Fullscreen Mode + حالت تمام صفحه + Enable Separate Update Folder فعال‌سازی پوشه جداگانه برای به‌روزرسانی + + Default tab when opening settings + زبان پیش‌فرض هنگام باز کردن تنظیمات + Show Game Size In List نمایش اندازه بازی در لیست @@ -620,6 +628,14 @@ Graphics گرافیک + + Gui + رابط کاربری + + + User + کاربر + Graphics Device کارت گرافیک مورداستفاده @@ -805,7 +821,7 @@ به‌روزرسانی:\nانتشار: نسخه‌های رسمی که هر ماه منتشر می‌شوند و ممکن است بسیار قدیمی باشند، اما پایدارتر و تست‌ شده‌تر هستند.\nشبانه: نسخه‌های توسعه‌ای که شامل جدیدترین ویژگی‌ها و اصلاحات هستند، اما ممکن است دارای اشکال باشند و کمتر پایدار باشند. - GUIgroupBox + GUIMusicGroupBox پخش موسیقی عنوان:\nIدر صورتی که بازی از آن پشتیبانی کند، پخش موسیقی ویژه هنگام انتخاب بازی در رابط کاربری را فعال می‌کند. diff --git a/src/qt_gui/translations/fi.ts b/src/qt_gui/translations/fi.ts index abc091b7e..86f6a6d3c 100644 --- a/src/qt_gui/translations/fi.ts +++ b/src/qt_gui/translations/fi.ts @@ -540,10 +540,18 @@ Enable Fullscreen Ota Käyttöön Koko Ruudun Tila + + Fullscreen Mode + Koko näytön tila + Enable Separate Update Folder Ota Käyttöön Erillinen Päivityshakemisto + + Default tab when opening settings + Oletusvälilehti avattaessa asetuksia + Show Game Size In List Näytä pelin koko luettelossa @@ -620,6 +628,14 @@ Graphics Grafiikka + + Gui + Rajapinta + + + User + Käyttäjä + Graphics Device Näytönohjain @@ -805,7 +821,7 @@ Päivitys:\nRelease: Viralliset versiot, jotka julkaistaan kuukausittain ja saattavat olla hyvin vanhoja, mutta ovat luotettavampia ja testatumpia.\nNightly: Kehitysversiot, joissa on kaikki uusimmat ominaisuudet ja korjaukset, mutta ne saattavat sisältää virheitä ja ovat vähemmän vakaita. - GUIgroupBox + GUIMusicGroupBox Soita Otsikkomusiikkia:\nJos peli tukee sitä, ota käyttöön erityisen musiikin soittaminen pelin valinnan yhteydessä käyttöliittymässä. diff --git a/src/qt_gui/translations/fr.ts b/src/qt_gui/translations/fr.ts index d2a1c5307..601ece8b4 100644 --- a/src/qt_gui/translations/fr.ts +++ b/src/qt_gui/translations/fr.ts @@ -540,10 +540,18 @@ Enable Fullscreen Plein écran + + Fullscreen Mode + Mode Plein Écran + Enable Separate Update Folder Dossier séparé pour les mises à jours + + Default tab when opening settings + Onglet par défaut lors de l'ouverture des paramètres + Show Game Size In List Afficher la taille du jeu dans la liste @@ -620,6 +628,14 @@ Graphics Graphismes + + Gui + Interface + + + User + Utilisateur + Graphics Device Carte graphique @@ -805,7 +821,7 @@ Mise à jour:\nRelease: versions officielles publiées chaque mois qui peuvent être très anciennes, mais plus fiables et testées.\nNightly: versions de développement avec toutes les dernières fonctionnalités et correctifs, mais pouvant avoir des bogues et être moins stables. - GUIgroupBox + GUIMusicGroupBox Jouer de la musique de titre:\nSi le jeu le prend en charge, cela active la musique spéciale lorsque vous sélectionnez le jeu dans l'interface utilisateur. diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index dff6a3a18..fe71e8120 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -540,10 +540,18 @@ Enable Fullscreen Teljes Képernyő Engedélyezése + + Fullscreen Mode + Teljes képernyős mód + Enable Separate Update Folder Külön Frissítési Mappa Engedélyezése + + Default tab when opening settings + Alapértelmezett fül a beállítások megnyitásakor + Show Game Size In List Játékméret megjelenítése a listában @@ -620,6 +628,14 @@ Graphics Grafika + + Gui + Felület + + + User + Felhasználó + Graphics Device Grafikai Eszköz @@ -805,7 +821,7 @@ Frissítés:\nRelease: Hivatalos verziók, amelyeket havonta adnak ki, és amelyek nagyon elavultak lehetnek, de megbízhatóbbak és teszteltek.\nNightly: Fejlesztési verziók, amelyek az összes legújabb funkciót és javítást tartalmazzák, de hibákat tartalmazhatnak és kevésbé stabilak. - GUIgroupBox + GUIMusicGroupBox Játék címzene lejátszása:\nHa a játék támogatja, engedélyezze egy speciális zene lejátszását, amikor a játékot kiválasztja a GUI-ban. diff --git a/src/qt_gui/translations/id.ts b/src/qt_gui/translations/id.ts index e6fb8b5aa..acfde43a7 100644 --- a/src/qt_gui/translations/id.ts +++ b/src/qt_gui/translations/id.ts @@ -540,10 +540,18 @@ Enable Fullscreen Enable Fullscreen + + Fullscreen Mode + Mode Layar Penuh + Enable Separate Update Folder Enable Separate Update Folder + + Default tab when opening settings + Tab default saat membuka pengaturan + Show Game Size In List Tampilkan Ukuran Game di Daftar @@ -620,6 +628,14 @@ Graphics Graphics + + Gui + Antarmuka + + + User + Pengguna + Graphics Device Graphics Device @@ -805,7 +821,7 @@ Pembaruan:\nRelease: Versi resmi yang dirilis setiap bulan yang mungkin sangat ketinggalan zaman, tetapi lebih dapat diandalkan dan teruji.\nNightly: Versi pengembangan yang memiliki semua fitur dan perbaikan terbaru, tetapi mungkin mengandung bug dan kurang stabil. - GUIgroupBox + GUIMusicGroupBox Putar Musik Judul Permainan:\nJika permainan mendukungnya, aktifkan pemutaran musik khusus saat memilih permainan di GUI. diff --git a/src/qt_gui/translations/it.ts b/src/qt_gui/translations/it.ts index 73dbdc603..93c6233a6 100644 --- a/src/qt_gui/translations/it.ts +++ b/src/qt_gui/translations/it.ts @@ -540,10 +540,18 @@ Enable Fullscreen Abilita Schermo Intero + + Fullscreen Mode + Modalità Schermo Intero + Enable Separate Update Folder Abilita Cartella Aggiornamenti Separata + + Default tab when opening settings + Scheda predefinita all'apertura delle impostazioni + Show Game Size In List Mostra la dimensione del gioco nell'elenco @@ -620,6 +628,14 @@ Graphics Grafica + + Gui + Interfaccia + + + User + Utente + Graphics Device Scheda Grafica @@ -805,7 +821,7 @@ Aggiornamento:\nRelease: Versioni ufficiali rilasciate ogni mese che potrebbero essere molto datate, ma sono più affidabili e testate.\nNightly: Versioni di sviluppo che hanno tutte le ultime funzionalità e correzioni, ma potrebbero contenere bug e sono meno stabili. - GUIgroupBox + GUIMusicGroupBox Riproduci Musica del Titolo:\nSe un gioco lo supporta, attiva la riproduzione di musica speciale quando selezioni il gioco nell'interfaccia grafica. diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index bb9488a6b..921af52bb 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -540,10 +540,18 @@ Enable Fullscreen フルスクリーンを有効にする + + Fullscreen Mode + 全画面モード + Enable Separate Update Folder アップデートフォルダの分離を有効化 + + Default tab when opening settings + 設定を開くときのデフォルトタブ + Show Game Size In List ゲームサイズをリストに表示 @@ -620,6 +628,14 @@ Graphics グラフィックス + + Gui + インターフェース + + + User + ユーザー + Graphics Device グラフィックスデバイス @@ -805,7 +821,7 @@ 更新:\nRelease: 最新の機能を利用できない可能性がありますが、より信頼性が高くテストされた公式バージョンが毎月リリースされます。\nNightly: 最新の機能と修正がすべて含まれていますが、バグが含まれている可能性があり、安定性は低いです。 - GUIgroupBox + GUIMusicGroupBox タイトルミュージックを再生:\nゲームでサポートされている場合に、GUIでゲームを選択したときに特別な音楽を再生する機能を有効にします。 diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts index 560b58340..2491959a6 100644 --- a/src/qt_gui/translations/ko_KR.ts +++ b/src/qt_gui/translations/ko_KR.ts @@ -540,10 +540,18 @@ Enable Fullscreen Enable Fullscreen + + Fullscreen Mode + 전체 화면 모드 + Enable Separate Update Folder Enable Separate Update Folder + + Default tab when opening settings + 설정 열기 시 기본 탭 + Show Game Size In List 게임 크기를 목록에 표시 @@ -620,6 +628,14 @@ Graphics Graphics + + Gui + 인터페이스 + + + User + 사용자 + Graphics Device Graphics Device @@ -805,7 +821,7 @@ Update:\nRelease: Official versions released every month that may be very outdated, but are more reliable and tested.\nNightly: Development versions that have all the latest features and fixes, but may contain bugs and are less stable. - GUIgroupBox + GUIMusicGroupBox Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI. diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts index e2ec1e5c3..23a52e948 100644 --- a/src/qt_gui/translations/lt_LT.ts +++ b/src/qt_gui/translations/lt_LT.ts @@ -540,10 +540,18 @@ Enable Fullscreen Enable Fullscreen + + Fullscreen Mode + Viso ekranas + Enable Separate Update Folder Enable Separate Update Folder + + Default tab when opening settings + Numatytoji kortelė atidarius nustatymus + Show Game Size In List Rodyti žaidimo dydį sąraše @@ -620,6 +628,14 @@ Graphics Graphics + + Gui + Interfeisa + + + User + Naudotojas + Graphics Device Graphics Device @@ -805,7 +821,7 @@ Atnaujinti:\nRelease: Oficialios versijos, išleidžiamos kiekvieną mėnesį, kurios gali būti labai pasenusios, tačiau yra patikimos ir išbandytos.\nNightly: Vystymo versijos, kuriose yra visos naujausios funkcijos ir taisymai, tačiau gali turėti klaidų ir būti mažiau stabilios. - GUIgroupBox + GUIMusicGroupBox Groti antraščių muziką:\nJei žaidimas tai palaiko, įjungia specialios muzikos grojimą, kai pasirinkite žaidimą GUI. diff --git a/src/qt_gui/translations/nb.ts b/src/qt_gui/translations/nb.ts index b94d29b23..d6d4090df 100644 --- a/src/qt_gui/translations/nb.ts +++ b/src/qt_gui/translations/nb.ts @@ -540,10 +540,18 @@ Enable Fullscreen Aktiver fullskjerm + + Fullscreen Mode + Fullskjermmodus + Enable Separate Update Folder Aktiver seperat oppdateringsmappe + + Default tab when opening settings + Standardfanen når innstillingene åpnes + Show Game Size In List Vis spillstørrelse i listen @@ -620,6 +628,14 @@ Graphics Grafikk + + Gui + Grensesnitt + + + User + Bruker + Graphics Device Grafikkenhet @@ -805,7 +821,7 @@ Oppdatering:\nRelease: Offisielle versjoner utgitt hver måned som kan være veldig utdaterte, men er mer pålitelige og testet.\nNightly: Utviklingsversjoner som har alle de nyeste funksjonene og feilrettingene, men som kan inneholde feil og er mindre stabile. - GUIgroupBox + GUIMusicGroupBox Spille tittelmusikk:\nHvis et spill støtter det, så aktiveres det spesiell musikk når du velger spillet i menyen. diff --git a/src/qt_gui/translations/nl.ts b/src/qt_gui/translations/nl.ts index add27500f..1dabda8b4 100644 --- a/src/qt_gui/translations/nl.ts +++ b/src/qt_gui/translations/nl.ts @@ -540,10 +540,18 @@ Enable Fullscreen Enable Fullscreen + + Fullscreen Mode + Volledig schermmodus + Enable Separate Update Folder Enable Separate Update Folder + + Default tab when opening settings + Standaardtabblad bij het openen van instellingen + Show Game Size In List Toon grootte van het spel in de lijst @@ -620,6 +628,14 @@ Graphics Graphics + + Gui + Interface + + + User + Gebruiker + Graphics Device Graphics Device @@ -805,7 +821,7 @@ Updateren:\nRelease: Officiële versies die elke maand worden uitgebracht, die zeer verouderd kunnen zijn, maar betrouwbaar en getest zijn.\nNightly: Ontwikkelingsversies die alle nieuwste functies en bugfixes bevatten, maar mogelijk bugs bevatten en minder stabiel zijn. - GUIgroupBox + GUIMusicGroupBox Speel titelsong:\nAls een game dit ondersteunt, wordt speciale muziek afgespeeld wanneer je het spel in de GUI selecteert. diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index 3280beea7..528e88337 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -540,10 +540,18 @@ Enable Fullscreen Włącz pełny ekran + + Fullscreen Mode + Tryb Pełnoekranowy + Enable Separate Update Folder Enable Separate Update Folder + + Default tab when opening settings + Domyślna zakładka podczas otwierania ustawień + Show Game Size In List Pokaż rozmiar gry na liście @@ -620,6 +628,14 @@ Graphics Grafika + + Gui + Interfejs + + + User + Użytkownik + Graphics Device Karta graficzna @@ -805,7 +821,7 @@ Aktualizator:\nRelease: Oficjalne wersje wydawane co miesiąc, które mogą być bardzo przestarzałe, ale są niezawodne i przetestowane.\nNightly: Wersje rozwojowe, które zawierają wszystkie najnowsze funkcje i poprawki błędów, ale mogą mieć błędy i być mniej stabilne. - GUIgroupBox + GUIMusicGroupBox Odtwórz muzykę tytułową:\nJeśli gra to obsługuje, aktywuje odtwarzanie specjalnej muzyki podczas wybierania gry w GUI. diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index b9d889519..fde1d3b63 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -540,10 +540,18 @@ Enable Fullscreen Ativar Tela Cheia + + Fullscreen Mode + Modo de Tela Cheia + Enable Separate Update Folder Habilitar pasta de atualização separada + + Default tab when opening settings + Aba padrão ao abrir as configurações + Show Game Size In List Mostrar Tamanho do Jogo na Lista @@ -620,6 +628,14 @@ Graphics Gráficos + + Gui + Interface + + + User + Usuário + Graphics Device Placa de Vídeo @@ -766,7 +782,7 @@ fullscreenCheckBox - Ativar Tela Cheia:\nMove automaticamente a janela do jogo para o modo tela cheia.\nIsso pode ser alterado pressionando a tecla F11. + Ativar Tela Cheia:\nAltera a janela do jogo para o modo tela cheia.\nIsso pode ser alterado pressionando a tecla F11. separateUpdatesCheckBox @@ -805,7 +821,11 @@ Atualizações:\nRelease: Versões oficiais que são lançadas todo mês e podem ser bastante antigas, mas são mais confiáveis e testadas.\nNightly: Versões de desenvolvimento que têm todos os novos recursos e correções, mas podem ter bugs e ser instáveis. - GUIgroupBox + chooseHomeTabGroupBox + do menu. + + + GUIMusicGroupBox Reproduzir música de abertura:\nSe o jogo suportar, ativa a reprodução de uma música especial ao selecionar o jogo na interface do menu. diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index 00a9eb179..9791a7682 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -540,10 +540,18 @@ Enable Fullscreen Enable Fullscreen + + Fullscreen Mode + Mod Ecran Complet + Enable Separate Update Folder Enable Separate Update Folder + + Default tab when opening settings + Tab-ul implicit la deschiderea setărilor + Show Game Size In List Afișează dimensiunea jocului în listă @@ -620,6 +628,14 @@ Graphics Graphics + + Gui + Interfață + + + User + Utilizator + Graphics Device Graphics Device @@ -805,7 +821,7 @@ Actualizare:\nRelease: Versiuni oficiale lansate în fiecare lună, care pot fi foarte învechite, dar sunt mai fiabile și testate.\nNightly: Versiuni de dezvoltare care conțin toate cele mai recente funcții și corecții, dar pot conține erori și sunt mai puțin stabile. - GUIgroupBox + GUIMusicGroupBox Redă muzica titlului:\nDacă un joc o suportă, activează redarea muzicii speciale când selectezi jocul în GUI. diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index 4c90450dd..6423f5ba4 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -540,10 +540,18 @@ Enable Fullscreen Полноэкранный режим + + Fullscreen Mode + Режим Полного Экран + Enable Separate Update Folder Отдельная папка обновлений + + Default tab when opening settings + Вкладка по умолчанию при открытии настроек + Show Game Size In List Показать размер игры в списке @@ -620,6 +628,14 @@ Graphics Графика + + Gui + Интерфейс + + + User + Пользователь + Graphics Device Графическое устройство @@ -805,7 +821,7 @@ Обновление:\nRelease: Официальные версии, которые выпускаются каждый месяц и могут быть очень старыми, но они более надежные и проверенные.\nNightly: Версии разработки, которые содержат все последние функции и исправления, но могут содержать ошибки и менее стабильны. - GUIgroupBox + GUIMusicGroupBox Играть заглавную музыку:\nВключает воспроизведение специальной музыки при выборе игры в списке, если она это поддерживает. diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index 768db1e75..2f88b4390 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -540,10 +540,18 @@ Enable Fullscreen Aktivizo Ekranin e plotë + + Fullscreen Mode + Modaliteti i Plotë + Enable Separate Update Folder Aktivizo dosjen e ndarë të përditësimit + + Default tab when opening settings + Skeda e parazgjedhur kur hapni cilësimet + Show Game Size In List Shfaq madhësinë e lojës në listë @@ -620,6 +628,14 @@ Graphics Grafika + + Gui + Ndërfaqe + + + User + Përdorues + Graphics Device Pajisja e Grafikës @@ -805,7 +821,7 @@ Përditësimi:\nRelease: Versionet zyrtare të lëshuara çdo muaj që mund të jenë shumë të vjetra, por janë më të besueshme dhe të provuara.\nNightly: Versionet e zhvillimit që kanë të gjitha veçoritë dhe rregullimet më të fundit, por mund të përmbajnë gabime dhe janë më pak të qëndrueshme. - GUIgroupBox + GUIMusicGroupBox Luaj muzikën e titullit:\nNëse një lojë e mbështet, aktivizohet luajtja e muzikës të veçantë kur të zgjidhësh lojën në GUI. diff --git a/src/qt_gui/translations/sv.ts b/src/qt_gui/translations/sv.ts index 3781ba45c..e17944f12 100644 --- a/src/qt_gui/translations/sv.ts +++ b/src/qt_gui/translations/sv.ts @@ -1032,10 +1032,18 @@ Enable Fullscreen Aktivera helskärm + + Fullscreen Mode + Helskärmsläge + Enable Separate Update Folder Aktivera separat uppdateringsmapp + + Default tab when opening settings + Standardflik när inställningar öppnas + Show Game Size In List Visa spelstorlek i listan @@ -1108,6 +1116,14 @@ Graphics Grafik + + Gui + Gränssnitt + + + User + Användare + Graphics Device Grafikenhet @@ -1285,7 +1301,7 @@ updaterGroupBox - GUIgroupBox + GUIMusicGroupBox Spela upp titelmusik:\nOm ett spel har stöd för det kan speciell musik spelas upp från spelet i gränssnittet diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index 5e8499073..6436278de 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -540,10 +540,18 @@ Enable Fullscreen Tam Ekranı Etkinleştir + + Fullscreen Mode + Tam Ekran Modu + Enable Separate Update Folder Enable Separate Update Folder + + Default tab when opening settings + Ayarlar açıldığında varsayılan sekme + Show Game Size In List Göster oyun boyutunu listede @@ -620,6 +628,14 @@ Graphics Grafikler + + Gui + Arayüz + + + User + Kullanıcı + Graphics Device Grafik Cihazı @@ -805,7 +821,7 @@ Güncelleme:\nRelease: Her ay yayınlanan resmi sürümler; çok eski olabilirler, ancak daha güvenilirdir ve test edilmiştir.\nNightly: Tüm en son özellikler ve düzeltmeler ile birlikte geliştirme sürümleri; hatalar içerebilir ve daha az kararlıdırlar. - GUIgroupBox + GUIMusicGroupBox Başlık Müziklerini Çal:\nEğer bir oyun bunu destekliyorsa, GUI'de oyunu seçtiğinizde özel müziklerin çalmasını etkinleştirir. diff --git a/src/qt_gui/translations/uk_UA.ts b/src/qt_gui/translations/uk_UA.ts index a1c7e97e0..720ad5b99 100644 --- a/src/qt_gui/translations/uk_UA.ts +++ b/src/qt_gui/translations/uk_UA.ts @@ -540,10 +540,18 @@ Enable Fullscreen Увімкнути повноекранний режим + + Fullscreen Mode + Режим Повноекранний + Enable Separate Update Folder Увімкнути окрему папку оновлень + + Default tab when opening settings + Вкладка за замовчуванням при відкритті налаштувань + Show Game Size In List Показати розмір гри в списку @@ -620,6 +628,14 @@ Graphics Графіка + + Gui + Інтерфейс + + + User + Користувач + Graphics Device Графічний пристрій @@ -805,7 +821,7 @@ Оновлення:\nRelease: Офіційні версії, які випускаються щомісяця і можуть бути дуже старими, але вони більш надійні та перевірені.\nNightly: Версії для розробників, які мають усі найновіші функції та виправлення, але можуть містити помилки та є менш стабільними. - GUIgroupBox + GUIMusicGroupBox Грати заголовну музику:\nВмикає відтворення спеціальної музики під час вибору гри в списку, якщо вона це підтримує. diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index a579a1983..a81630fad 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -540,10 +540,18 @@ Enable Fullscreen Enable Fullscreen + + Fullscreen Mode + Chế độ Toàn màn hình + Enable Separate Update Folder Enable Separate Update Folder + + Default tab when opening settings + Tab mặc định khi mở cài đặt + Show Game Size In List Hiển thị Kích thước Game trong Danh sách @@ -620,6 +628,14 @@ Graphics Graphics + + Gui + Giao diện + + + User + Người dùng + Graphics Device Graphics Device @@ -805,7 +821,7 @@ Cập nhật:\nRelease: Các phiên bản chính thức được phát hành hàng tháng; có thể khá cũ nhưng đáng tin cậy hơn và đã được thử nghiệm.\nNightly: Các phiên bản phát triển có tất cả các tính năng và sửa lỗi mới nhất; có thể có lỗi và ít ổn định hơn. - GUIgroupBox + GUIMusicGroupBox Phát nhạc tiêu đề trò chơi:\nNếu một trò chơi hỗ trợ điều này, hãy kích hoạt phát nhạc đặc biệt khi bạn chọn trò chơi trong GUI. diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index 5450f3dfd..b06b0a286 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -540,10 +540,18 @@ Enable Fullscreen 启用全屏 + + Fullscreen Mode + 全屏模式 + Enable Separate Update Folder 启用单独的更新目录 + + Default tab when opening settings + 打开设置时的默认选项卡 + Show Game Size In List 显示游戏大小在列表中 @@ -620,6 +628,14 @@ Graphics 图像 + + Gui + 界面 + + + User + 用户 + Graphics Device 图形设备 @@ -805,7 +821,7 @@ 更新:\nRelease:每月发布的官方版本可能非常过时,但更可靠且经过测试。\nNightly:包含所有最新功能和修复的开发版本,但可能包含错误且稳定性较低。 - GUIgroupBox + GUIMusicGroupBox 播放标题音乐:\n如果游戏支持,在图形界面选择游戏时播放特殊音乐。 diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index 0ce0b4d69..9f6758797 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -540,10 +540,18 @@ Enable Fullscreen Enable Fullscreen + + Fullscreen Mode + 全螢幕模式 + Enable Separate Update Folder Enable Separate Update Folder + + Default tab when opening settings + 打開設置時的默認選項卡 + Show Game Size In List 顯示遊戲大小在列表中 @@ -620,6 +628,14 @@ Graphics Graphics + + Gui + 介面 + + + User + 使用者 + Graphics Device Graphics Device @@ -805,7 +821,7 @@ 更新:\nRelease: 每月發布的官方版本,可能非常舊,但更可靠且經過測試。\nNightly: 開發版本,擁有所有最新的功能和修復,但可能包含錯誤,穩定性較差。 - GUIgroupBox + GUIMusicGroupBox 播放標題音樂:\n如果遊戲支持,啟用在GUI中選擇遊戲時播放特殊音樂。 From 81e7e4b21646ad40edaae06f9b1e58eaea139b3c Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Thu, 23 Jan 2025 00:52:22 +0200 Subject: [PATCH 467/549] Update building-linux.md --- documents/building-linux.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documents/building-linux.md b/documents/building-linux.md index 2672c70f4..28b8c6056 100644 --- a/documents/building-linux.md +++ b/documents/building-linux.md @@ -99,7 +99,7 @@ Open `cmake-gui` and specify the source code and build directories. If you clone Click on Configure, select "Unix Makefiles", select "Specify native compilers", click Next and choose `clang` and `clang++` as the C and CXX compilers. Usually they are located in `/bin/clang` and `/bin/clang++`. Click on Finish and let it configure the project. -Now every option should be displayed in red. Change anything you want, then click on Generate to make the changes permanent, then open a terminal window and do steps 2 and 3 of Option 1. +Now every option should be displayed in red. Change anything you want, then click on Generate to make the changes permanent, then open a terminal window and do step 2 of Option 1. #### Option 3: Visual Studio Code @@ -131,4 +131,4 @@ If you want to debug it, change the build type under Project Status > Configure Don't forget to change the launch target for both options to the shadPS4 executable inside shadPS4/build: -![image](https://raw.githubusercontent.com/shadps4-emu/shadPS4/refs/heads/main/documents/Screenshots/Linux/5.png) \ No newline at end of file +![image](https://raw.githubusercontent.com/shadps4-emu/shadPS4/refs/heads/main/documents/Screenshots/Linux/5.png) From cc2e13873f9be4e2f95e4da7a71e7333502fa183 Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Wed, 22 Jan 2025 22:21:41 -0300 Subject: [PATCH 468/549] Fix showing debug menu bar / Devtools (#2214) * Fix showing debug menu bar / Devtools * Fix --- src/core/debug_state.cpp | 2 ++ src/core/debug_state.h | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/debug_state.cpp b/src/core/debug_state.cpp index 6508a9875..23ebcbb9b 100644 --- a/src/core/debug_state.cpp +++ b/src/core/debug_state.cpp @@ -17,6 +17,8 @@ using namespace DebugStateType; DebugStateImpl& DebugState = *Common::Singleton::Instance(); +bool DebugStateType::showing_debug_menu_bar = false; + static ThreadID ThisThreadID() { #ifdef _WIN32 return GetCurrentThreadId(); diff --git a/src/core/debug_state.h b/src/core/debug_state.h index a9e6f48b7..217efd1a9 100644 --- a/src/core/debug_state.h +++ b/src/core/debug_state.h @@ -35,6 +35,8 @@ class ShaderList; namespace DebugStateType { +extern bool showing_debug_menu_bar; + enum class QueueType { dcb = 0, ccb = 1, @@ -131,8 +133,6 @@ class DebugStateImpl { friend class Core::Devtools::Widget::FrameGraph; friend class Core::Devtools::Widget::ShaderList; - bool showing_debug_menu_bar = false; - std::queue debug_message_popup; std::mutex guest_threads_mutex{}; From cef92fbcaad34e0caa90bc5afbce5e5b30c43636 Mon Sep 17 00:00:00 2001 From: DemoJameson Date: Thu, 23 Jan 2025 19:28:44 +0800 Subject: [PATCH 469/549] Update Simplified Chinese translation (#2216) * Update Simplified Chinese translation * Fix incorrect translation * Fix new line --- src/qt_gui/translations/zh_CN.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index b06b0a286..16ae03f94 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -552,7 +552,7 @@ Default tab when opening settings 打开设置时的默认选项卡 - + Show Game Size In List 显示游戏大小在列表中 @@ -830,7 +830,7 @@ hideCursorGroupBox - 隐藏光标:\n选择光标何时消失:\n从不: 始终显示光标。\闲置: 光标在闲置若干秒后消失。\n始终: 始终显示光标。 + 隐藏光标:\n选择光标何时消失:\n从不: 从不隐藏光标。\n闲置:光标在闲置若干秒后消失。\n始终:始终隐藏光标。 idleTimeoutGroupBox @@ -928,6 +928,14 @@ rdocCheckBox 启用 RenderDoc 调试:\n启用后模拟器将提供与 Renderdoc 的兼容性,允许在渲染过程中捕获和分析当前渲染的帧。 + + saveDataBox + 存档数据路径:\n保存游戏存档数据的目录。 + + + browseButton + 浏览:\n选择一个目录保存游戏存档数据。 + CheatsPatches From fb738bc24739704abcc87d87f25dd6a241c9719d Mon Sep 17 00:00:00 2001 From: Angelo Rosa Date: Thu, 23 Jan 2025 05:54:42 -0800 Subject: [PATCH 470/549] Optimize workflows by caching `apt install` (#2212) * optimize apt caches * change on push syntax * removing comments and check time improvements on following pushes * add recursive submodule fetch * Revert "add recursive submodule fetch" This reverts commit b059bda3b88aa7d81e4bee3348c9104536b15fd2. * restore old push on everyhing rule * fix libfuse2 uncaching package * moving qt deps outside from caching directives --- .github/workflows/build.yml | 48 ++++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3da7163dd..a49ffefe9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,20 +24,27 @@ jobs: runs-on: ubuntu-24.04 continue-on-error: true steps: + - uses: actions/checkout@v4 with: fetch-depth: 0 + - name: Install run: | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - sudo add-apt-repository 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main' - sudo apt update - sudo apt install clang-format-18 + + - uses: awalsh128/cache-apt-pkgs-action@latest + with: + packages: clang-format-18 + version: 1.0 + - name: Build env: COMMIT_RANGE: ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} run: ./.ci/clang-format.sh - + + get-info: runs-on: ubuntu-24.04 outputs: @@ -282,7 +289,13 @@ jobs: submodules: recursive - name: Install dependencies - run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev + run: | + sudo apt install -y libfuse2 + + - uses: awalsh128/cache-apt-pkgs-action@latest + with: + packages: libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev clang build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev + version: 1.0 - name: Cache CMake Configuration uses: actions/cache@v4 @@ -338,7 +351,13 @@ jobs: submodules: recursive - name: Install dependencies - run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev libudev-dev + run: | + sudo apt install -y libfuse2 qt6-base-dev qt6-tools-dev qt6-multimedia-dev + + - uses: awalsh128/cache-apt-pkgs-action@latest + with: + packages: libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev clang build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev + version: 1.0 - name: Cache CMake Configuration uses: actions/cache@v4 @@ -385,7 +404,13 @@ jobs: submodules: recursive - name: Install dependencies - run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 gcc-14 build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev + run: | + sudo apt install -y libfuse2 + + - uses: awalsh128/cache-apt-pkgs-action@latest + with: + packages: libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev gcc-14 build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev + version: 1.0 - name: Cache CMake Configuration uses: actions/cache@v4 @@ -421,7 +446,13 @@ jobs: submodules: recursive - name: Install dependencies - run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 gcc-14 build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev libudev-dev + run: | + sudo apt install -y libfuse2 qt6-base-dev qt6-tools-dev qt6-multimedia-dev + + - uses: awalsh128/cache-apt-pkgs-action@latest + with: + packages: libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev gcc-14 build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev + version: 1.0 - name: Cache CMake Configuration uses: actions/cache@v4 @@ -443,7 +474,8 @@ jobs: key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }} - name: Configure CMake - run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + run: | + cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc) From 2e6c9b8f9866f48bd39dc35b25310e77e3fa89f6 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Thu, 23 Jan 2025 22:58:45 +0200 Subject: [PATCH 471/549] Revert "Optimize workflows by caching `apt install` (#2212)" (#2220) This reverts commit fb738bc24739704abcc87d87f25dd6a241c9719d. --- .github/workflows/build.yml | 48 +++++++------------------------------ 1 file changed, 8 insertions(+), 40 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a49ffefe9..3da7163dd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,27 +24,20 @@ jobs: runs-on: ubuntu-24.04 continue-on-error: true steps: - - uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Install run: | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - sudo add-apt-repository 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main' - - - uses: awalsh128/cache-apt-pkgs-action@latest - with: - packages: clang-format-18 - version: 1.0 - + sudo apt update + sudo apt install clang-format-18 - name: Build env: COMMIT_RANGE: ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} run: ./.ci/clang-format.sh - - + get-info: runs-on: ubuntu-24.04 outputs: @@ -289,13 +282,7 @@ jobs: submodules: recursive - name: Install dependencies - run: | - sudo apt install -y libfuse2 - - - uses: awalsh128/cache-apt-pkgs-action@latest - with: - packages: libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev clang build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev - version: 1.0 + run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev - name: Cache CMake Configuration uses: actions/cache@v4 @@ -351,13 +338,7 @@ jobs: submodules: recursive - name: Install dependencies - run: | - sudo apt install -y libfuse2 qt6-base-dev qt6-tools-dev qt6-multimedia-dev - - - uses: awalsh128/cache-apt-pkgs-action@latest - with: - packages: libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev clang build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev - version: 1.0 + run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev libudev-dev - name: Cache CMake Configuration uses: actions/cache@v4 @@ -404,13 +385,7 @@ jobs: submodules: recursive - name: Install dependencies - run: | - sudo apt install -y libfuse2 - - - uses: awalsh128/cache-apt-pkgs-action@latest - with: - packages: libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev gcc-14 build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev - version: 1.0 + run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 gcc-14 build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev - name: Cache CMake Configuration uses: actions/cache@v4 @@ -446,13 +421,7 @@ jobs: submodules: recursive - name: Install dependencies - run: | - sudo apt install -y libfuse2 qt6-base-dev qt6-tools-dev qt6-multimedia-dev - - - uses: awalsh128/cache-apt-pkgs-action@latest - with: - packages: libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev gcc-14 build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev - version: 1.0 + run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 gcc-14 build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev libudev-dev - name: Cache CMake Configuration uses: actions/cache@v4 @@ -474,8 +443,7 @@ jobs: key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }} - name: Configure CMake - run: | - cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc) From cdca420a2e8579832c7ebffb7ec2ed8f9e74030c Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Thu, 23 Jan 2025 23:27:37 +0200 Subject: [PATCH 472/549] Update building-linux.md --- documents/building-linux.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/documents/building-linux.md b/documents/building-linux.md index 28b8c6056..eedcd6664 100644 --- a/documents/building-linux.md +++ b/documents/building-linux.md @@ -29,8 +29,7 @@ sudo dnf install clang git cmake libatomic alsa-lib-devel pipewire-jack-audio-co sudo pacman -S base-devel clang git cmake sndio jack2 openal qt6-base qt6-declarative qt6-multimedia sdl2 vulkan-validation-layers ``` -**Note**: The `shadps4-git` AUR package is not maintained by any of the developers, and it uses GCC as the compiler as opposed to Clang. Use at your own discretion. -#### OpenSUSE +**Note**: The `shadps4-git` AUR package is not maintained by any of the developers, and it uses the default compiler, which is often set to GCC. Use at your own discretion.#### OpenSUSE ``` sudo zypper install clang git cmake libasound2 libpulse-devel libsndio7 libjack-devel openal-soft-devel libopenssl-devel zlib-devel libedit-devel systemd-devel libevdev-devel qt6-base-devel qt6-multimedia-devel qt6-svg-devel qt6-linguist-devel qt6-gui-private-devel vulkan-devel vulkan-validationlayers From 0ebe817a28afc14511578d183f8ef2afce3905aa Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Thu, 23 Jan 2025 23:46:15 +0200 Subject: [PATCH 473/549] Update building-linux.md --- documents/building-linux.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documents/building-linux.md b/documents/building-linux.md index eedcd6664..2342e9afc 100644 --- a/documents/building-linux.md +++ b/documents/building-linux.md @@ -29,7 +29,7 @@ sudo dnf install clang git cmake libatomic alsa-lib-devel pipewire-jack-audio-co sudo pacman -S base-devel clang git cmake sndio jack2 openal qt6-base qt6-declarative qt6-multimedia sdl2 vulkan-validation-layers ``` -**Note**: The `shadps4-git` AUR package is not maintained by any of the developers, and it uses the default compiler, which is often set to GCC. Use at your own discretion.#### OpenSUSE +**Note** : The `shadps4-git` AUR package is not maintained by any of the developers, and it uses the default compiler, which is often set to GCC. Use at your own discretion.#### OpenSUSE ``` sudo zypper install clang git cmake libasound2 libpulse-devel libsndio7 libjack-devel openal-soft-devel libopenssl-devel zlib-devel libedit-devel systemd-devel libevdev-devel qt6-base-devel qt6-multimedia-devel qt6-svg-devel qt6-linguist-devel qt6-gui-private-devel vulkan-devel vulkan-validationlayers From cc4ddd28c3f3cfe978a0be75131a99ca4e86851b Mon Sep 17 00:00:00 2001 From: Log3rinioo <80830849+Log3rinioo@users.noreply.github.com> Date: Thu, 23 Jan 2025 22:56:06 +0100 Subject: [PATCH 474/549] Add missing Polish translations and fix typos (#2222) --- src/qt_gui/translations/pl_PL.ts | 86 ++++++++++++++++---------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index 528e88337..85eb63bfb 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -52,7 +52,7 @@ Select which directory you want to install to. - Select which directory you want to install to. + Wybierz katalog, do którego chcesz zainstalować. @@ -130,35 +130,35 @@ Delete... - Delete... + Usuń... Delete Game - Delete Game + Usuń Grę Delete Update - Delete Update + Usuń Aktualizację Delete DLC - Delete DLC + Usuń DLC Compatibility... - Compatibility... + kompatybilność... Update database - Update database + Zaktualizuj bazę danych View report - View report + Wyświetl zgłoszenie Submit a report - Submit a report + Wyślij zgłoszenie Shortcut creation @@ -182,23 +182,23 @@ Game - Game + Gra requiresEnableSeparateUpdateFolder_MSG - This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + Ta funkcja wymaga do działania opcji „Włącz oddzielny folder aktualizacji”. Jeśli chcesz korzystać z tej funkcji, włącz ją. This game has no update to delete! - This game has no update to delete! + Ta gra nie ma aktualizacji do usunięcia! Update - Update + Aktualizacja This game has no DLC to delete! - This game has no DLC to delete! + Ta gra nie ma DLC do usunięcia! DLC @@ -206,11 +206,11 @@ Delete %1 - Delete %1 + Usuń %1 Are you sure you want to delete %1's %2 directory? - Are you sure you want to delete %1's %2 directory? + Czy na pewno chcesz usunąć katalog %1 z %2? @@ -249,7 +249,7 @@ Open shadPS4 Folder - Open shadPS4 Folder + Otwórz folder shadPS4 Exit @@ -546,7 +546,7 @@ Enable Separate Update Folder - Enable Separate Update Folder + Włącz oddzielny folder aktualizacji Default tab when opening settings @@ -574,11 +574,11 @@ Trophy Key - Trophy Key + Klucz trofeów Trophy - Trophy + Trofeum Logger @@ -722,7 +722,7 @@ Disable Trophy Pop-ups - Disable Trophy Pop-ups + Wyłącz wyskakujące okienka trofeów Play title music @@ -730,19 +730,19 @@ Update Compatibility Database On Startup - Update Compatibility Database On Startup + Aktualizuj bazę danych zgodności podczas uruchamiania Game Compatibility - Game Compatibility + Kompatybilność gier Display Compatibility Data - Display Compatibility Data + Wyświetl dane zgodności Update Compatibility Database - Update Compatibility Database + Aktualizuj bazę danych zgodności Volume @@ -750,7 +750,7 @@ Audio Backend - Audio Backend + Zaplecze audio Save @@ -786,7 +786,7 @@ separateUpdatesCheckBox - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + Włącz oddzielny folder aktualizacji:\nUmożliwia instalowanie aktualizacji gier w oddzielnym folderze w celu łatwego zarządzania. showSplashCheckBox @@ -798,7 +798,7 @@ discordRPCCheckbox - Włącz Discord Rich Presence:\nWyświetla ikonę emuladora i odpowiednie informacje na twoim profilu Discord. + Włącz Discord Rich Presence:\nWyświetla ikonę emulatora i odpowiednie informacje na twoim profilu Discord. userName @@ -806,7 +806,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. + Klucz trofeów:\nKlucz używany do odszyfrowywania trofeów. Musi być uzyskany z konsoli po jailbreaku. Musi zawierać tylko znaki w kodzie szesnastkowym. logTypeGroupBox @@ -826,7 +826,7 @@ disableTrophycheckBox - Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + Wyłącz wyskakujące okienka trofeów:\nWyłącz powiadomienia o trofeach w grze. Postępy w zdobywaniu trofeów można nadal śledzić za pomocą przeglądarki trofeów (kliknij prawym przyciskiem myszy grę w oknie głównym). hideCursorGroupBox @@ -842,15 +842,15 @@ enableCompatibilityCheckBox - Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + Wyświetl dane zgodności:\nWyświetla informacje o kompatybilności gry w widoku tabeli. Włącz opcję „Aktualizuj zgodność przy uruchomieniu”, aby uzyskać aktualne informacje. checkCompatibilityOnStartupCheckBox - Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + Aktualizuj zgodność przy uruchomieniu:\nAutomatycznie aktualizuj bazę danych kompatybilności podczas uruchamiania shadPS4. updateCompatibilityButton - Update Compatibility Database:\nImmediately update the compatibility database. + Zaktualizuj bazę danych zgodności:\nNatychmiast zaktualizuj bazę danych zgodności. Never @@ -933,7 +933,7 @@ CheatsPatches Cheats / Patches for - Cheats / Patches for + Kody / Łatki dla defaultTextEdit_MSG @@ -1145,7 +1145,7 @@ Failed to parse JSON: - Nie udało się przeanlizować JSON: + Nie udało się przeanalizować JSON: Can't apply cheats before the game is started @@ -1168,7 +1168,7 @@ Compatibility - Compatibility + Zgodność Region @@ -1196,7 +1196,7 @@ Never Played - Never Played + Nigdy nie grane h @@ -1212,27 +1212,27 @@ Compatibility is untested - Compatibility is untested + Kompatybilność nie została przetestowana Game does not initialize properly / crashes the emulator - Game does not initialize properly / crashes the emulator + Gra nie inicjuje się poprawnie / zawiesza się emulator Game boots, but only displays a blank screen - Game boots, but only displays a blank screen + Gra uruchamia się, ale wyświetla tylko pusty ekran Game displays an image but does not go past the menu - Game displays an image but does not go past the menu + Gra wyświetla obraz, ale nie przechodzi do menu Game has game-breaking glitches or unplayable performance - Game has game-breaking glitches or unplayable performance + Gra ma usterki przerywające rozgrywkę lub niegrywalną wydajność Game can be completed with playable performance and no major glitches - Game can be completed with playable performance and no major glitches + Grę można ukończyć z grywalną wydajnością i bez większych usterek From e652369f22fc7cbdd456ce338dd1dae0974c69a4 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Thu, 23 Jan 2025 23:58:43 +0200 Subject: [PATCH 475/549] sdl3 update (#2221) --- externals/MoltenVK/MoltenVK | 2 +- externals/sdl3 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/externals/MoltenVK/MoltenVK b/externals/MoltenVK/MoltenVK index 9f0b616d9..2473ce6f0 160000 --- a/externals/MoltenVK/MoltenVK +++ b/externals/MoltenVK/MoltenVK @@ -1 +1 @@ -Subproject commit 9f0b616d9e2c39464d2a859b79dbc655c4a30e7e +Subproject commit 2473ce6f0ab7d5d8a49aa91b2e37f3447a939f18 diff --git a/externals/sdl3 b/externals/sdl3 index 22422f774..a336b62d8 160000 --- a/externals/sdl3 +++ b/externals/sdl3 @@ -1 +1 @@ -Subproject commit 22422f7748d5128135995ed34c8f8012861c7332 +Subproject commit a336b62d8b0b97b09214e053203e442e2b6e2be5 From a8a779c79b48f020a1432274114f1ddaff0b1dec Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Fri, 24 Jan 2025 05:11:48 -0300 Subject: [PATCH 476/549] Fix AutoUpdate Changelog (#2224) --- src/qt_gui/check_update.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/qt_gui/check_update.cpp b/src/qt_gui/check_update.cpp index e3e019144..0c1cce5da 100644 --- a/src/qt_gui/check_update.cpp +++ b/src/qt_gui/check_update.cpp @@ -146,14 +146,14 @@ void CheckUpdate::CheckForUpdates(const bool showMessage) { } QString currentRev = (updateChannel == "Nightly") - ? QString::fromStdString(Common::g_scm_rev).left(7) + ? QString::fromStdString(Common::g_scm_rev) : "v." + QString::fromStdString(Common::VERSION); QString currentDate = Common::g_scm_date; QDateTime dateTime = QDateTime::fromString(latestDate, Qt::ISODate); latestDate = dateTime.isValid() ? dateTime.toString("yyyy-MM-dd HH:mm:ss") : "Unknown date"; - if (latestRev == currentRev) { + if (latestRev == currentRev.left(7)) { if (showMessage) { QMessageBox::information(this, tr("Auto Updater"), tr("Your version is already up to date!")); @@ -190,7 +190,7 @@ void CheckUpdate::setupUI(const QString& downloadUrl, const QString& latestDate, QString("


" + tr("Update Channel") + ":
" + updateChannel + "
" + tr("Current Version") + ": %1 (%2)
" + tr("Latest Version") + ": %3 (%4)

" + tr("Do you want to update?") + "

") - .arg(currentRev, currentDate, latestRev, latestDate); + .arg(currentRev.left(7), currentDate, latestRev, latestDate); QLabel* updateLabel = new QLabel(updateText, this); layout->addWidget(updateLabel); From 91444a05453ba1468b29c575d844ae173cc7e738 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 24 Jan 2025 00:21:32 -0800 Subject: [PATCH 477/549] liverpool: Fix tiled check for color buffer. (#2227) --- src/video_core/amdgpu/liverpool.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index a29bde4ce..ec9c92ef1 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -904,7 +904,7 @@ struct Liverpool { } bool IsTiled() const { - return !info.linear_general; + return GetTilingMode() != TilingMode::Display_Linear; } [[nodiscard]] DataFormat GetDataFmt() const { From 74710116f6bdd805c39ffe7fff4be017df3ac910 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 24 Jan 2025 00:21:56 -0800 Subject: [PATCH 478/549] renderer_vulkan: Remove dead code. (#2228) --- CMakeLists.txt | 2 - .../vk_descriptor_update_queue.cpp | 108 ------------------ .../vk_descriptor_update_queue.h | 51 --------- 3 files changed, 161 deletions(-) delete mode 100644 src/video_core/renderer_vulkan/vk_descriptor_update_queue.cpp delete mode 100644 src/video_core/renderer_vulkan/vk_descriptor_update_queue.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 131809c8e..3db142be7 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -751,8 +751,6 @@ set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp src/video_core/renderer_vulkan/vk_common.h src/video_core/renderer_vulkan/vk_compute_pipeline.cpp src/video_core/renderer_vulkan/vk_compute_pipeline.h - src/video_core/renderer_vulkan/vk_descriptor_update_queue.cpp - src/video_core/renderer_vulkan/vk_descriptor_update_queue.h src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp src/video_core/renderer_vulkan/vk_graphics_pipeline.h src/video_core/renderer_vulkan/vk_instance.cpp diff --git a/src/video_core/renderer_vulkan/vk_descriptor_update_queue.cpp b/src/video_core/renderer_vulkan/vk_descriptor_update_queue.cpp deleted file mode 100644 index 7699bea9d..000000000 --- a/src/video_core/renderer_vulkan/vk_descriptor_update_queue.cpp +++ /dev/null @@ -1,108 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "video_core/renderer_vulkan/vk_descriptor_update_queue.h" -#include "video_core/renderer_vulkan/vk_instance.h" - -namespace Vulkan { - -DescriptorUpdateQueue::DescriptorUpdateQueue(const Instance& instance, u32 descriptor_write_max_) - : device{instance.GetDevice()}, descriptor_write_max{descriptor_write_max_} { - descriptor_infos = std::make_unique(descriptor_write_max); - descriptor_writes = std::make_unique(descriptor_write_max); -} - -void DescriptorUpdateQueue::Flush() { - if (descriptor_write_end == 0) { - return; - } - device.updateDescriptorSets({std::span(descriptor_writes.get(), descriptor_write_end)}, {}); - descriptor_write_end = 0; -} - -void DescriptorUpdateQueue::AddStorageImage(vk::DescriptorSet target, u8 binding, - vk::ImageView image_view, - vk::ImageLayout image_layout) { - if (descriptor_write_end >= descriptor_write_max) [[unlikely]] { - Flush(); - } - - auto& image_info = descriptor_infos[descriptor_write_end].image_info; - image_info.sampler = VK_NULL_HANDLE; - image_info.imageView = image_view; - image_info.imageLayout = image_layout; - - descriptor_writes[descriptor_write_end++] = vk::WriteDescriptorSet{ - .dstSet = target, - .dstBinding = binding, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = vk::DescriptorType::eStorageImage, - .pImageInfo = &image_info, - }; -} - -void DescriptorUpdateQueue::AddImageSampler(vk::DescriptorSet target, u8 binding, u8 array_index, - vk::ImageView image_view, vk::Sampler sampler, - vk::ImageLayout image_layout) { - if (descriptor_write_end >= descriptor_write_max) [[unlikely]] { - Flush(); - } - - auto& image_info = descriptor_infos[descriptor_write_end].image_info; - image_info.sampler = sampler; - image_info.imageView = image_view; - image_info.imageLayout = image_layout; - - descriptor_writes[descriptor_write_end++] = vk::WriteDescriptorSet{ - .dstSet = target, - .dstBinding = binding, - .dstArrayElement = array_index, - .descriptorCount = 1, - .descriptorType = - sampler ? vk::DescriptorType::eCombinedImageSampler : vk::DescriptorType::eSampledImage, - .pImageInfo = &image_info, - }; -} - -void DescriptorUpdateQueue::AddBuffer(vk::DescriptorSet target, u8 binding, vk::Buffer buffer, - vk::DeviceSize offset, vk::DeviceSize size, - vk::DescriptorType type) { - if (descriptor_write_end >= descriptor_write_max) [[unlikely]] { - Flush(); - } - - auto& buffer_info = descriptor_infos[descriptor_write_end].buffer_info; - buffer_info.buffer = buffer; - buffer_info.offset = offset; - buffer_info.range = size; - - descriptor_writes[descriptor_write_end++] = vk::WriteDescriptorSet{ - .dstSet = target, - .dstBinding = binding, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = type, - .pBufferInfo = &buffer_info, - }; -} - -void DescriptorUpdateQueue::AddTexelBuffer(vk::DescriptorSet target, u8 binding, - vk::BufferView buffer_view) { - if (descriptor_write_end >= descriptor_write_max) [[unlikely]] { - Flush(); - } - - auto& buffer_info = descriptor_infos[descriptor_write_end].buffer_view; - buffer_info = buffer_view; - descriptor_writes[descriptor_write_end++] = vk::WriteDescriptorSet{ - .dstSet = target, - .dstBinding = binding, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = vk::DescriptorType::eUniformTexelBuffer, - .pTexelBufferView = &buffer_info, - }; -} - -} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_descriptor_update_queue.h b/src/video_core/renderer_vulkan/vk_descriptor_update_queue.h deleted file mode 100644 index 9e864db6e..000000000 --- a/src/video_core/renderer_vulkan/vk_descriptor_update_queue.h +++ /dev/null @@ -1,51 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include - -#include "common/types.h" -#include "video_core/renderer_vulkan/vk_common.h" - -namespace Vulkan { - -class Instance; - -struct DescriptorInfoUnion { - DescriptorInfoUnion() {} - - union { - vk::DescriptorImageInfo image_info; - vk::DescriptorBufferInfo buffer_info; - vk::BufferView buffer_view; - }; -}; - -class DescriptorUpdateQueue { -public: - explicit DescriptorUpdateQueue(const Instance& instance, u32 descriptor_write_max = 2048); - ~DescriptorUpdateQueue() = default; - - void Flush(); - - void AddStorageImage(vk::DescriptorSet target, u8 binding, vk::ImageView image_view, - vk::ImageLayout image_layout = vk::ImageLayout::eGeneral); - - void AddImageSampler(vk::DescriptorSet target, u8 binding, u8 array_index, - vk::ImageView image_view, vk::Sampler sampler, - vk::ImageLayout imageLayout = vk::ImageLayout::eGeneral); - - void AddBuffer(vk::DescriptorSet target, u8 binding, vk::Buffer buffer, vk::DeviceSize offset, - vk::DeviceSize size = VK_WHOLE_SIZE, - vk::DescriptorType type = vk::DescriptorType::eUniformBufferDynamic); - - void AddTexelBuffer(vk::DescriptorSet target, u8 binding, vk::BufferView buffer_view); - -private: - const vk::Device device; - const u32 descriptor_write_max; - std::unique_ptr descriptor_infos; - std::unique_ptr descriptor_writes; - u32 descriptor_write_end = 0; -}; - -} // namespace Vulkan From d1b9a5adcceb0c61e03293db9840ca6844442398 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 24 Jan 2025 00:23:18 -0800 Subject: [PATCH 479/549] texture_cache: Do not overwrite overlap hit with a miss. (#2217) --- src/video_core/texture_cache/texture_cache.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index 04711539c..e995b10b2 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -345,8 +345,13 @@ ImageId TextureCache::FindImage(BaseDesc& desc, FindFlags flags) { view_slice = -1; const auto& merged_info = image_id ? slot_images[image_id].info : info; - std::tie(image_id, view_mip, view_slice) = + auto [overlap_image_id, overlap_view_mip, overlap_view_slice] = ResolveOverlap(merged_info, desc.type, cache_id, image_id); + if (overlap_image_id) { + image_id = overlap_image_id; + view_mip = overlap_view_mip; + view_slice = overlap_view_slice; + } } } From 481f420a892e02ac30bfe3e4966368900c69ead2 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Fri, 24 Jan 2025 10:28:14 +0200 Subject: [PATCH 480/549] Update building-linux.md --- documents/building-linux.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/documents/building-linux.md b/documents/building-linux.md index 2342e9afc..4aa66aac6 100644 --- a/documents/building-linux.md +++ b/documents/building-linux.md @@ -29,7 +29,9 @@ sudo dnf install clang git cmake libatomic alsa-lib-devel pipewire-jack-audio-co sudo pacman -S base-devel clang git cmake sndio jack2 openal qt6-base qt6-declarative qt6-multimedia sdl2 vulkan-validation-layers ``` -**Note** : The `shadps4-git` AUR package is not maintained by any of the developers, and it uses the default compiler, which is often set to GCC. Use at your own discretion.#### OpenSUSE +**Note** : The `shadps4-git` AUR package is not maintained by any of the developers, and it uses the default compiler, which is often set to GCC. Use at your own discretion. + +#### OpenSUSE ``` sudo zypper install clang git cmake libasound2 libpulse-devel libsndio7 libjack-devel openal-soft-devel libopenssl-devel zlib-devel libedit-devel systemd-devel libevdev-devel qt6-base-devel qt6-multimedia-devel qt6-svg-devel qt6-linguist-devel qt6-gui-private-devel vulkan-devel vulkan-validationlayers @@ -48,6 +50,7 @@ distrobox create --name archlinux --init --image archlinux:latest and install the dependencies on that container as cited above. This option is **highly recommended** for NixOS and distributions with immutable/atomic filesystems (example: Fedora Kinoite, SteamOS). + ### Cloning ``` From 0f69697acb7504e57abe2b15238b2c92414a0d8f Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 24 Jan 2025 00:48:39 -0800 Subject: [PATCH 481/549] documents: Update CPU requirements. (#2229) --- documents/Quickstart/Quickstart.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/documents/Quickstart/Quickstart.md b/documents/Quickstart/Quickstart.md index b2931e51e..2f2751887 100644 --- a/documents/Quickstart/Quickstart.md +++ b/documents/Quickstart/Quickstart.md @@ -22,7 +22,10 @@ SPDX-License-Identifier: GPL-2.0-or-later - A processor with at least 4 cores and 6 threads - Above 2.5 GHz frequency -- required support AVX2 extension or Rosetta 2 on ARM +- A CPU supporting the following instruction sets: MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, F16C, CLMUL, AES, BMI1, MOVBE, XSAVE, ABM + - **Intel**: Haswell generation or newer + - **AMD**: Jaguar generation or newer + - **Apple**: Rosetta 2 on macOS 15 or newer ### GPU From 9dcf40e261c6775a997b92d26a4bd8113821c6ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?= Date: Fri, 24 Jan 2025 11:07:36 +0000 Subject: [PATCH 482/549] Handle more 64bit shifts in Translator (#1825) --- .../frontend/translate/scalar_alu.cpp | 22 ++++++++++++++++++- .../frontend/translate/translate.h | 2 ++ .../frontend/translate/vector_alu.cpp | 4 ++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/shader_recompiler/frontend/translate/scalar_alu.cpp b/src/shader_recompiler/frontend/translate/scalar_alu.cpp index 7f34126f5..b1b260fde 100644 --- a/src/shader_recompiler/frontend/translate/scalar_alu.cpp +++ b/src/shader_recompiler/frontend/translate/scalar_alu.cpp @@ -72,10 +72,14 @@ void Translator::EmitScalarAlu(const GcnInst& inst) { return S_OR_B64(NegateMode::Result, true, inst); case Opcode::S_LSHL_B32: return S_LSHL_B32(inst); + case Opcode::S_LSHL_B64: + return S_LSHL_B64(inst); case Opcode::S_LSHR_B32: return S_LSHR_B32(inst); case Opcode::S_ASHR_I32: return S_ASHR_I32(inst); + case Opcode::S_ASHR_I64: + return S_ASHR_I64(inst); case Opcode::S_BFM_B32: return S_BFM_B32(inst); case Opcode::S_MUL_I32: @@ -420,6 +424,14 @@ void Translator::S_LSHL_B32(const GcnInst& inst) { ir.SetScc(ir.INotEqual(result, ir.Imm32(0))); } +void Translator::S_LSHL_B64(const GcnInst& inst) { + const IR::U64 src0{GetSrc64(inst.src[0])}; + const IR::U64 src1{GetSrc64(inst.src[1])}; + const IR::U64 result = ir.ShiftLeftLogical(src0, ir.BitwiseAnd(src1, ir.Imm64(u64(0x3F)))); + SetDst64(inst.dst[0], result); + ir.SetScc(ir.INotEqual(result, ir.Imm64(u64(0)))); +} + void Translator::S_LSHR_B32(const GcnInst& inst) { const IR::U32 src0{GetSrc(inst.src[0])}; const IR::U32 src1{GetSrc(inst.src[1])}; @@ -431,11 +443,19 @@ void Translator::S_LSHR_B32(const GcnInst& inst) { void Translator::S_ASHR_I32(const GcnInst& inst) { const IR::U32 src0{GetSrc(inst.src[0])}; const IR::U32 src1{GetSrc(inst.src[1])}; - const IR::U32 result{ir.ShiftRightArithmetic(src0, src1)}; + const IR::U32 result{ir.ShiftRightArithmetic(src0, ir.BitwiseAnd(src1, ir.Imm32(0x1F)))}; SetDst(inst.dst[0], result); ir.SetScc(ir.INotEqual(result, ir.Imm32(0))); } +void Translator::S_ASHR_I64(const GcnInst& inst) { + const IR::U64 src0{GetSrc64(inst.src[0])}; + const IR::U64 src1{GetSrc64(inst.src[1])}; + const IR::U64 result{ir.ShiftRightArithmetic(src0, ir.BitwiseAnd(src1, ir.Imm64(u64(0x3F))))}; + SetDst64(inst.dst[0], result); + ir.SetScc(ir.INotEqual(result, ir.Imm64(u64(0)))); +} + void Translator::S_BFM_B32(const GcnInst& inst) { const IR::U32 src0{ir.BitwiseAnd(GetSrc(inst.src[0]), ir.Imm32(0x1F))}; const IR::U32 src1{ir.BitwiseAnd(GetSrc(inst.src[1]), ir.Imm32(0x1F))}; diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index bef61f997..496455b50 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -90,8 +90,10 @@ public: void S_OR_B64(NegateMode negate, bool is_xor, const GcnInst& inst); void S_XOR_B32(const GcnInst& inst); void S_LSHL_B32(const GcnInst& inst); + void S_LSHL_B64(const GcnInst& inst); void S_LSHR_B32(const GcnInst& inst); void S_ASHR_I32(const GcnInst& inst); + void S_ASHR_I64(const GcnInst& inst); void S_BFM_B32(const GcnInst& inst); void S_MUL_I32(const GcnInst& inst); void S_BFE(const GcnInst& inst, bool is_signed); diff --git a/src/shader_recompiler/frontend/translate/vector_alu.cpp b/src/shader_recompiler/frontend/translate/vector_alu.cpp index b2863f6a8..ac72293e4 100644 --- a/src/shader_recompiler/frontend/translate/vector_alu.cpp +++ b/src/shader_recompiler/frontend/translate/vector_alu.cpp @@ -1273,6 +1273,10 @@ void Translator::V_LSHL_B64(const GcnInst& inst) { ir.SetVectorReg(dst_reg + 1, ir.Imm32(static_cast(result >> 32))); return; } + + const IR::U64 result = ir.ShiftLeftLogical(src0, ir.BitwiseAnd(src1, ir.Imm64(u64(0x3F)))); + SetDst64(inst.dst[0], result); + return; } UNREACHABLE_MSG("Unimplemented V_LSHL_B64 arguments"); } From 4d12de8149cce521916f7752a0830430170144db Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 24 Jan 2025 03:11:13 -0800 Subject: [PATCH 483/549] hotfix: 64-bit shift fixups --- .../backend/spirv/emit_spirv_instructions.h | 3 ++- .../backend/spirv/emit_spirv_integer.cpp | 6 ++++- .../frontend/translate/vector_alu.cpp | 23 +------------------ src/shader_recompiler/ir/ir_emitter.cpp | 14 +++++++++-- src/shader_recompiler/ir/ir_emitter.h | 2 +- src/shader_recompiler/ir/opcodes.inc | 3 ++- .../ir/passes/constant_propagation_pass.cpp | 5 +++- 7 files changed, 27 insertions(+), 29 deletions(-) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index f0bb9fd7e..4833dc9d0 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h @@ -348,7 +348,8 @@ Id EmitSLessThanEqual(EmitContext& ctx, Id lhs, Id rhs); Id EmitULessThanEqual(EmitContext& ctx, Id lhs, Id rhs); Id EmitSGreaterThan(EmitContext& ctx, Id lhs, Id rhs); Id EmitUGreaterThan(EmitContext& ctx, Id lhs, Id rhs); -Id EmitINotEqual(EmitContext& ctx, Id lhs, Id rhs); +Id EmitINotEqual32(EmitContext& ctx, Id lhs, Id rhs); +Id EmitINotEqual64(EmitContext& ctx, Id lhs, Id rhs); Id EmitSGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs); Id EmitUGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs); Id EmitLogicalOr(EmitContext& ctx, Id a, Id b); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp index 70411ecec..e2d702389 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp @@ -324,7 +324,11 @@ Id EmitUGreaterThan(EmitContext& ctx, Id lhs, Id rhs) { return ctx.OpUGreaterThan(ctx.U1[1], lhs, rhs); } -Id EmitINotEqual(EmitContext& ctx, Id lhs, Id rhs) { +Id EmitINotEqual32(EmitContext& ctx, Id lhs, Id rhs) { + return ctx.OpINotEqual(ctx.U1[1], lhs, rhs); +} + +Id EmitINotEqual64(EmitContext& ctx, Id lhs, Id rhs) { return ctx.OpINotEqual(ctx.U1[1], lhs, rhs); } diff --git a/src/shader_recompiler/frontend/translate/vector_alu.cpp b/src/shader_recompiler/frontend/translate/vector_alu.cpp index ac72293e4..42dbcc513 100644 --- a/src/shader_recompiler/frontend/translate/vector_alu.cpp +++ b/src/shader_recompiler/frontend/translate/vector_alu.cpp @@ -1257,28 +1257,7 @@ void Translator::V_CVT_PK_U8_F32(const GcnInst& inst) { void Translator::V_LSHL_B64(const GcnInst& inst) { const IR::U64 src0{GetSrc64(inst.src[0])}; const IR::U64 src1{GetSrc64(inst.src[1])}; - const IR::VectorReg dst_reg{inst.dst[0].code}; - if (src0.IsImmediate()) { - if (src0.U64() == -1) { - // If src0 is a fixed -1, the result will always be -1. - ir.SetVectorReg(dst_reg, ir.Imm32(0xFFFFFFFF)); - ir.SetVectorReg(dst_reg + 1, ir.Imm32(0xFFFFFFFF)); - return; - } - if (src1.IsImmediate()) { - // If both src0 and src1 are immediates, we can calculate the result now. - // Note that according to the manual, only bits 4:0 are used from src1. - const u64 result = src0.U64() << (src1.U64() & 0x1F); - ir.SetVectorReg(dst_reg, ir.Imm32(static_cast(result))); - ir.SetVectorReg(dst_reg + 1, ir.Imm32(static_cast(result >> 32))); - return; - } - - const IR::U64 result = ir.ShiftLeftLogical(src0, ir.BitwiseAnd(src1, ir.Imm64(u64(0x3F)))); - SetDst64(inst.dst[0], result); - return; - } - UNREACHABLE_MSG("Unimplemented V_LSHL_B64 arguments"); + SetDst64(inst.dst[0], ir.ShiftLeftLogical(src0, ir.BitwiseAnd(src1, ir.Imm64(u64(0x3F))))); } void Translator::V_MUL_F64(const GcnInst& inst) { diff --git a/src/shader_recompiler/ir/ir_emitter.cpp b/src/shader_recompiler/ir/ir_emitter.cpp index 5ac08e7dc..f0558665b 100644 --- a/src/shader_recompiler/ir/ir_emitter.cpp +++ b/src/shader_recompiler/ir/ir_emitter.cpp @@ -1461,8 +1461,18 @@ U1 IREmitter::IGreaterThan(const U32& lhs, const U32& rhs, bool is_signed) { return Inst(is_signed ? Opcode::SGreaterThan : Opcode::UGreaterThan, lhs, rhs); } -U1 IREmitter::INotEqual(const U32& lhs, const U32& rhs) { - return Inst(Opcode::INotEqual, lhs, rhs); +U1 IREmitter::INotEqual(const U32U64& lhs, const U32U64& rhs) { + if (lhs.Type() != rhs.Type()) { + UNREACHABLE_MSG("Mismatching types {} and {}", lhs.Type(), rhs.Type()); + } + switch (lhs.Type()) { + case Type::U32: + return Inst(Opcode::INotEqual32, lhs, rhs); + case Type::U64: + return Inst(Opcode::INotEqual64, lhs, rhs); + default: + ThrowInvalidType(lhs.Type()); + } } U1 IREmitter::IGreaterThanEqual(const U32& lhs, const U32& rhs, bool is_signed) { diff --git a/src/shader_recompiler/ir/ir_emitter.h b/src/shader_recompiler/ir/ir_emitter.h index d1dc44d74..5dd49ce7a 100644 --- a/src/shader_recompiler/ir/ir_emitter.h +++ b/src/shader_recompiler/ir/ir_emitter.h @@ -258,7 +258,7 @@ public: [[nodiscard]] U1 IEqual(const U32U64& lhs, const U32U64& rhs); [[nodiscard]] U1 ILessThanEqual(const U32& lhs, const U32& rhs, bool is_signed); [[nodiscard]] U1 IGreaterThan(const U32& lhs, const U32& rhs, bool is_signed); - [[nodiscard]] U1 INotEqual(const U32& lhs, const U32& rhs); + [[nodiscard]] U1 INotEqual(const U32U64& lhs, const U32U64& rhs); [[nodiscard]] U1 IGreaterThanEqual(const U32& lhs, const U32& rhs, bool is_signed); [[nodiscard]] U1 LogicalOr(const U1& a, const U1& b); diff --git a/src/shader_recompiler/ir/opcodes.inc b/src/shader_recompiler/ir/opcodes.inc index b45151dba..63a4e1e62 100644 --- a/src/shader_recompiler/ir/opcodes.inc +++ b/src/shader_recompiler/ir/opcodes.inc @@ -321,7 +321,8 @@ OPCODE(SLessThanEqual, U1, U32, OPCODE(ULessThanEqual, U1, U32, U32, ) OPCODE(SGreaterThan, U1, U32, U32, ) OPCODE(UGreaterThan, U1, U32, U32, ) -OPCODE(INotEqual, U1, U32, U32, ) +OPCODE(INotEqual32, U1, U32, U32, ) +OPCODE(INotEqual64, U1, U64, U64, ) OPCODE(SGreaterThanEqual, U1, U32, U32, ) OPCODE(UGreaterThanEqual, U1, U32, U32, ) diff --git a/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp b/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp index 26d819d8e..12a1b56e9 100644 --- a/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp +++ b/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp @@ -403,9 +403,12 @@ void ConstantPropagation(IR::Block& block, IR::Inst& inst) { case IR::Opcode::IEqual64: FoldWhenAllImmediates(inst, [](u64 a, u64 b) { return a == b; }); return; - case IR::Opcode::INotEqual: + case IR::Opcode::INotEqual32: FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a != b; }); return; + case IR::Opcode::INotEqual64: + FoldWhenAllImmediates(inst, [](u64 a, u64 b) { return a != b; }); + return; case IR::Opcode::BitwiseAnd32: FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a & b; }); return; From 4f426b723f2f015af022e2f9c48813448c264d7b Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Fri, 24 Jan 2025 14:30:55 +0100 Subject: [PATCH 484/549] Rebase of "Handle munmap over multiple VMAs" (#2233) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Unmap memory in chunks if spanning over multiple VMAs * clang * Merge fixups * Minor code style changes * Update function declarations --------- Co-authored-by: Marcin Mikołajczyk --- src/core/memory.cpp | 49 +++++++++++++++++++++++++++++---------------- src/core/memory.h | 4 +++- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/src/core/memory.cpp b/src/core/memory.cpp index f90d4e6aa..a8dd72acc 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -389,32 +389,29 @@ s32 MemoryManager::UnmapMemory(VAddr virtual_addr, size_t size) { return UnmapMemoryImpl(virtual_addr, size); } -s32 MemoryManager::UnmapMemoryImpl(VAddr virtual_addr, size_t size) { - const auto it = FindVMA(virtual_addr); - const auto& vma_base = it->second; - ASSERT_MSG(vma_base.Contains(virtual_addr, size), - "Existing mapping does not contain requested unmap range"); - - const auto type = vma_base.type; - if (type == VMAType::Free) { - return ORBIS_OK; - } - +u64 MemoryManager::UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma_base, u64 size) { const auto vma_base_addr = vma_base.base; const auto vma_base_size = vma_base.size; + const auto type = vma_base.type; const auto phys_base = vma_base.phys_base; const bool is_exec = vma_base.is_exec; const auto start_in_vma = virtual_addr - vma_base_addr; + const auto adjusted_size = + vma_base_size - start_in_vma < size ? vma_base_size - start_in_vma : size; const bool has_backing = type == VMAType::Direct || type == VMAType::File; + + if (type == VMAType::Free) { + return adjusted_size; + } if (type == VMAType::Direct || type == VMAType::Pooled) { - rasterizer->UnmapMemory(virtual_addr, size); + rasterizer->UnmapMemory(virtual_addr, adjusted_size); } if (type == VMAType::Flexible) { - flexible_usage -= size; + flexible_usage -= adjusted_size; } // Mark region as free and attempt to coalesce it with neighbours. - const auto new_it = CarveVMA(virtual_addr, size); + const auto new_it = CarveVMA(virtual_addr, adjusted_size); auto& vma = new_it->second; vma.type = VMAType::Free; vma.prot = MemoryProt::NoAccess; @@ -423,13 +420,25 @@ s32 MemoryManager::UnmapMemoryImpl(VAddr virtual_addr, size_t size) { vma.name = ""; MergeAdjacent(vma_map, new_it); bool readonly_file = vma.prot == MemoryProt::CpuRead && type == VMAType::File; - if (type != VMAType::Reserved && type != VMAType::PoolReserved) { // Unmap the memory region. - impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + size, phys_base, - is_exec, has_backing, readonly_file); + impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + adjusted_size, + phys_base, is_exec, has_backing, readonly_file); TRACK_FREE(virtual_addr, "VMEM"); } + return adjusted_size; +} + +s32 MemoryManager::UnmapMemoryImpl(VAddr virtual_addr, u64 size) { + u64 unmapped_bytes = 0; + do { + auto it = FindVMA(virtual_addr + unmapped_bytes); + auto& vma_base = it->second; + auto unmapped = + UnmapBytesFromEntry(virtual_addr + unmapped_bytes, vma_base, size - unmapped_bytes); + ASSERT_MSG(unmapped > 0, "Failed to unmap memory, progress is impossible"); + unmapped_bytes += unmapped; + } while (unmapped_bytes < size); return ORBIS_OK; } @@ -651,6 +660,12 @@ MemoryManager::VMAHandle MemoryManager::CarveVMA(VAddr virtual_addr, size_t size const VAddr start_in_vma = virtual_addr - vma.base; const VAddr end_in_vma = start_in_vma + size; + + if (start_in_vma == 0 && size == vma.size) { + // if requsting the whole VMA, return it + return vma_handle; + } + ASSERT_MSG(end_in_vma <= vma.size, "Mapping cannot fit inside free region"); if (end_in_vma != vma.size) { diff --git a/src/core/memory.h b/src/core/memory.h index 615ecc3eb..59e48b248 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -252,7 +252,9 @@ private: DMemHandle Split(DMemHandle dmem_handle, size_t offset_in_area); - s32 UnmapMemoryImpl(VAddr virtual_addr, size_t size); + u64 UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma_base, u64 size); + + s32 UnmapMemoryImpl(VAddr virtual_addr, u64 size); private: AddressSpace impl; From a4eba8e827c062b5e6874255ff810346943d0ea2 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Fri, 24 Jan 2025 19:22:06 +0200 Subject: [PATCH 485/549] stubbed webbrowserdialog,npparty (#2234) * stubbed webbrowserdialog,npparty * added poly's suggestions --- CMakeLists.txt | 4 + src/common/logging/filter.cpp | 2 + src/common/logging/types.h | 2 + src/core/libraries/libs.cpp | 4 + src/core/libraries/np_party/np_party.cpp | 195 ++++++++++++++++++ src/core/libraries/np_party/np_party.h | 44 ++++ .../web_browser_dialog/webbrowserdialog.cpp | 112 ++++++++++ .../web_browser_dialog/webbrowserdialog.h | 30 +++ 8 files changed, 393 insertions(+) create mode 100644 src/core/libraries/np_party/np_party.cpp create mode 100644 src/core/libraries/np_party/np_party.h create mode 100644 src/core/libraries/web_browser_dialog/webbrowserdialog.cpp create mode 100644 src/core/libraries/web_browser_dialog/webbrowserdialog.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3db142be7..4c9dad307 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -344,6 +344,8 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp src/core/libraries/razor_cpu/razor_cpu.h src/core/libraries/mouse/mouse.cpp src/core/libraries/mouse/mouse.h + src/core/libraries/web_browser_dialog/webbrowserdialog.cpp + src/core/libraries/web_browser_dialog/webbrowserdialog.h ) set(VIDEOOUT_LIB src/core/libraries/videoout/buffer.h @@ -434,6 +436,8 @@ set(NP_LIBS src/core/libraries/np_common/np_common.cpp src/core/libraries/np_trophy/np_trophy_error.h src/core/libraries/np_web_api/np_web_api.cpp src/core/libraries/np_web_api/np_web_api.h + src/core/libraries/np_party/np_party.cpp + src/core/libraries/np_party/np_party.h ) set(MISC_LIBS src/core/libraries/screenshot/screenshot.cpp diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index 168d03948..f1d3a9499 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -131,6 +131,8 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Lib, Videodec) \ SUB(Lib, RazorCpu) \ SUB(Lib, Mouse) \ + SUB(Lib, WebBrowserDialog) \ + SUB(Lib, NpParty) \ CLS(Frontend) \ CLS(Render) \ SUB(Render, Vulkan) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index 4ca88e1be..d5530312c 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -98,6 +98,8 @@ enum class Class : u8 { Lib_Videodec, ///< The LibSceVideodec implementation. Lib_RazorCpu, ///< The LibRazorCpu implementation. Lib_Mouse, ///< The LibSceMouse implementation + Lib_WebBrowserDialog, ///< The LibSceWebBrowserDialog implementation + Lib_NpParty, ///< The LibSceNpParty implementation Frontend, ///< Emulator UI Render, ///< Video Core Render_Vulkan, ///< Vulkan backend diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index 6dc455028..e09de1cee 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -28,6 +28,7 @@ #include "core/libraries/network/ssl2.h" #include "core/libraries/np_common/np_common.h" #include "core/libraries/np_manager/np_manager.h" +#include "core/libraries/np_party/np_party.h" #include "core/libraries/np_score/np_score.h" #include "core/libraries/np_trophy/np_trophy.h" #include "core/libraries/np_web_api/np_web_api.h" @@ -52,6 +53,7 @@ #include "core/libraries/videodec/videodec.h" #include "core/libraries/videodec/videodec2.h" #include "core/libraries/videoout/video_out.h" +#include "core/libraries/web_browser_dialog/webbrowserdialog.h" #include "fiber/fiber.h" #include "jpeg/jpegenc.h" @@ -107,6 +109,8 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::Fiber::RegisterlibSceFiber(sym); Libraries::JpegEnc::RegisterlibSceJpegEnc(sym); Libraries::Mouse::RegisterlibSceMouse(sym); + Libraries::WebBrowserDialog::RegisterlibSceWebBrowserDialog(sym); + Libraries::NpParty::RegisterlibSceNpParty(sym); } } // namespace Libraries diff --git a/src/core/libraries/np_party/np_party.cpp b/src/core/libraries/np_party/np_party.cpp new file mode 100644 index 000000000..8a66ccb22 --- /dev/null +++ b/src/core/libraries/np_party/np_party.cpp @@ -0,0 +1,195 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" +#include "core/libraries/np_party/np_party.h" + +namespace Libraries::NpParty { + +s32 PS4_SYSV_ABI sceNpPartyCheckCallback() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyCreate() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyCreateA() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyGetId() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyGetMemberInfo() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyGetMemberInfoA() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyGetMembers() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyGetMembersA() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyGetMemberSessionInfo() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyGetMemberVoiceInfo() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyGetState() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyGetStateAsUser() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyGetStateAsUserA() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyGetVoiceChatPriority() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyInitialize() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyJoin() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyLeave() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyRegisterHandler() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyRegisterHandlerA() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyRegisterPrivateHandler() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartySendBinaryMessage() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartySetVoiceChatPriority() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyShowInvitationList() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyShowInvitationListA() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyTerminate() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyUnregisterPrivateHandler() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +void RegisterlibSceNpParty(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("3e4k2mzLkmc", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyCheckCallback); + LIB_FUNCTION("nOZRy-slBoA", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyCreate); + LIB_FUNCTION("XQSUbbnpPBA", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyCreateA); + LIB_FUNCTION("DRA3ay-1DFQ", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyGetId); + LIB_FUNCTION("F1P+-wpxQow", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyGetMemberInfo); + LIB_FUNCTION("v2RYVGrJDkM", "libSceNpParty", 1, "libSceNpParty", 1, 1, + sceNpPartyGetMemberInfoA); + LIB_FUNCTION("T2UOKf00ZN0", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyGetMembers); + LIB_FUNCTION("TaNw7W25QJw", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyGetMembersA); + LIB_FUNCTION("4gOMfNYzllw", "libSceNpParty", 1, "libSceNpParty", 1, 1, + sceNpPartyGetMemberSessionInfo); + LIB_FUNCTION("EKi1jx59SP4", "libSceNpParty", 1, "libSceNpParty", 1, 1, + sceNpPartyGetMemberVoiceInfo); + LIB_FUNCTION("aEzKdJzATZ0", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyGetState); + LIB_FUNCTION("o7grRhiGHYI", "libSceNpParty", 1, "libSceNpParty", 1, 1, + sceNpPartyGetStateAsUser); + LIB_FUNCTION("EjyAI+QNgFw", "libSceNpParty", 1, "libSceNpParty", 1, 1, + sceNpPartyGetStateAsUserA); + LIB_FUNCTION("-lc6XZnQXvM", "libSceNpParty", 1, "libSceNpParty", 1, 1, + sceNpPartyGetVoiceChatPriority); + LIB_FUNCTION("lhYCTQmBkds", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyInitialize); + LIB_FUNCTION("RXNCDw2GDEg", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyJoin); + LIB_FUNCTION("J8jAi-tfJHc", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyLeave); + LIB_FUNCTION("kA88gbv71ao", "libSceNpParty", 1, "libSceNpParty", 1, 1, + sceNpPartyRegisterHandler); + LIB_FUNCTION("+v4fVHMwFWc", "libSceNpParty", 1, "libSceNpParty", 1, 1, + sceNpPartyRegisterHandlerA); + LIB_FUNCTION("zo4G5WWYpKg", "libSceNpParty", 1, "libSceNpParty", 1, 1, + sceNpPartyRegisterPrivateHandler); + LIB_FUNCTION("U6VdUe-PNAY", "libSceNpParty", 1, "libSceNpParty", 1, 1, + sceNpPartySendBinaryMessage); + LIB_FUNCTION("nazKyHygHhY", "libSceNpParty", 1, "libSceNpParty", 1, 1, + sceNpPartySetVoiceChatPriority); + LIB_FUNCTION("-MFiL7hEnPE", "libSceNpParty", 1, "libSceNpParty", 1, 1, + sceNpPartyShowInvitationList); + LIB_FUNCTION("yARHEYLajs0", "libSceNpParty", 1, "libSceNpParty", 1, 1, + sceNpPartyShowInvitationListA); + LIB_FUNCTION("oLYkibiHqRA", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyTerminate); + LIB_FUNCTION("zQ7gIvt11Pc", "libSceNpParty", 1, "libSceNpParty", 1, 1, + sceNpPartyUnregisterPrivateHandler); + LIB_FUNCTION("nOZRy-slBoA", "libSceNpPartyCompat", 1, "libSceNpParty", 1, 1, sceNpPartyCreate); + LIB_FUNCTION("F1P+-wpxQow", "libSceNpPartyCompat", 1, "libSceNpParty", 1, 1, + sceNpPartyGetMemberInfo); + LIB_FUNCTION("T2UOKf00ZN0", "libSceNpPartyCompat", 1, "libSceNpParty", 1, 1, + sceNpPartyGetMembers); + LIB_FUNCTION("o7grRhiGHYI", "libSceNpPartyCompat", 1, "libSceNpParty", 1, 1, + sceNpPartyGetStateAsUser); + LIB_FUNCTION("kA88gbv71ao", "libSceNpPartyCompat", 1, "libSceNpParty", 1, 1, + sceNpPartyRegisterHandler); + LIB_FUNCTION("-MFiL7hEnPE", "libSceNpPartyCompat", 1, "libSceNpParty", 1, 1, + sceNpPartyShowInvitationList); +}; + +} // namespace Libraries::NpParty \ No newline at end of file diff --git a/src/core/libraries/np_party/np_party.h b/src/core/libraries/np_party/np_party.h new file mode 100644 index 000000000..d5f20e4f8 --- /dev/null +++ b/src/core/libraries/np_party/np_party.h @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::NpParty { + +s32 PS4_SYSV_ABI sceNpPartyCheckCallback(); +s32 PS4_SYSV_ABI sceNpPartyCreate(); +s32 PS4_SYSV_ABI sceNpPartyCreateA(); +s32 PS4_SYSV_ABI sceNpPartyGetId(); +s32 PS4_SYSV_ABI sceNpPartyGetMemberInfo(); +s32 PS4_SYSV_ABI sceNpPartyGetMemberInfoA(); +s32 PS4_SYSV_ABI sceNpPartyGetMembers(); +s32 PS4_SYSV_ABI sceNpPartyGetMembersA(); +s32 PS4_SYSV_ABI sceNpPartyGetMemberSessionInfo(); +s32 PS4_SYSV_ABI sceNpPartyGetMemberVoiceInfo(); +s32 PS4_SYSV_ABI sceNpPartyGetState(); +s32 PS4_SYSV_ABI sceNpPartyGetStateAsUser(); +s32 PS4_SYSV_ABI sceNpPartyGetStateAsUserA(); +s32 PS4_SYSV_ABI sceNpPartyGetVoiceChatPriority(); +s32 PS4_SYSV_ABI sceNpPartyInitialize(); +s32 PS4_SYSV_ABI sceNpPartyJoin(); +s32 PS4_SYSV_ABI sceNpPartyLeave(); +s32 PS4_SYSV_ABI sceNpPartyRegisterHandler(); +s32 PS4_SYSV_ABI sceNpPartyRegisterHandlerA(); +s32 PS4_SYSV_ABI sceNpPartyRegisterPrivateHandler(); +s32 PS4_SYSV_ABI sceNpPartySendBinaryMessage(); +s32 PS4_SYSV_ABI sceNpPartySetVoiceChatPriority(); +s32 PS4_SYSV_ABI sceNpPartyShowInvitationList(); +s32 PS4_SYSV_ABI sceNpPartyShowInvitationListA(); +s32 PS4_SYSV_ABI sceNpPartyTerminate(); +s32 PS4_SYSV_ABI sceNpPartyUnregisterPrivateHandler(); +s32 PS4_SYSV_ABI module_start(); +s32 PS4_SYSV_ABI module_stop(); + +void RegisterlibSceNpParty(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::NpParty \ No newline at end of file diff --git a/src/core/libraries/web_browser_dialog/webbrowserdialog.cpp b/src/core/libraries/web_browser_dialog/webbrowserdialog.cpp new file mode 100644 index 000000000..ee434f96a --- /dev/null +++ b/src/core/libraries/web_browser_dialog/webbrowserdialog.cpp @@ -0,0 +1,112 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" +#include "core/libraries/web_browser_dialog/webbrowserdialog.h" + +namespace Libraries::WebBrowserDialog { + +s32 PS4_SYSV_ABI sceWebBrowserDialogClose() { + LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceWebBrowserDialogGetEvent() { + LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceWebBrowserDialogGetResult() { + LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceWebBrowserDialogGetStatus() { + LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceWebBrowserDialogInitialize() { + LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceWebBrowserDialogNavigate() { + LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceWebBrowserDialogOpen() { + LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceWebBrowserDialogOpenForPredeterminedContent() { + LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceWebBrowserDialogResetCookie() { + LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceWebBrowserDialogSetCookie() { + LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceWebBrowserDialogSetZoom() { + LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceWebBrowserDialogTerminate() { + LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceWebBrowserDialogUpdateStatus() { + LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_F2BE042771625F8C() { + LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called"); + return ORBIS_OK; +} + +void RegisterlibSceWebBrowserDialog(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("PSK+Eik919Q", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1, + sceWebBrowserDialogClose); + LIB_FUNCTION("Wit4LjeoeX4", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1, + sceWebBrowserDialogGetEvent); + LIB_FUNCTION("vCaW0fgVQmc", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1, + sceWebBrowserDialogGetResult); + LIB_FUNCTION("CFTG6a8TjOU", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1, + sceWebBrowserDialogGetStatus); + LIB_FUNCTION("jqb7HntFQFc", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1, + sceWebBrowserDialogInitialize); + LIB_FUNCTION("uYELOMVnmNQ", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1, + sceWebBrowserDialogNavigate); + LIB_FUNCTION("FraP7debcdg", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1, + sceWebBrowserDialogOpen); + LIB_FUNCTION("O7dIZQrwVFY", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1, + sceWebBrowserDialogOpenForPredeterminedContent); + LIB_FUNCTION("Cya+jvTtPqg", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1, + sceWebBrowserDialogResetCookie); + LIB_FUNCTION("TZnDVkP91Rg", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1, + sceWebBrowserDialogSetCookie); + LIB_FUNCTION("RLhKBOoNyXY", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1, + sceWebBrowserDialogSetZoom); + LIB_FUNCTION("ocHtyBwHfys", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1, + sceWebBrowserDialogTerminate); + LIB_FUNCTION("h1dR-t5ISgg", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1, + sceWebBrowserDialogUpdateStatus); + LIB_FUNCTION("8r4EJ3FiX4w", "libSceWebBrowserDialogLimited", 1, "libSceWebBrowserDialog", 1, 1, + Func_F2BE042771625F8C); +}; + +} // namespace Libraries::WebBrowserDialog \ No newline at end of file diff --git a/src/core/libraries/web_browser_dialog/webbrowserdialog.h b/src/core/libraries/web_browser_dialog/webbrowserdialog.h new file mode 100644 index 000000000..aa118fe45 --- /dev/null +++ b/src/core/libraries/web_browser_dialog/webbrowserdialog.h @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::WebBrowserDialog { + +s32 PS4_SYSV_ABI sceWebBrowserDialogClose(); +s32 PS4_SYSV_ABI sceWebBrowserDialogGetEvent(); +s32 PS4_SYSV_ABI sceWebBrowserDialogGetResult(); +s32 PS4_SYSV_ABI sceWebBrowserDialogGetStatus(); +s32 PS4_SYSV_ABI sceWebBrowserDialogInitialize(); +s32 PS4_SYSV_ABI sceWebBrowserDialogNavigate(); +s32 PS4_SYSV_ABI sceWebBrowserDialogOpen(); +s32 PS4_SYSV_ABI sceWebBrowserDialogOpenForPredeterminedContent(); +s32 PS4_SYSV_ABI sceWebBrowserDialogResetCookie(); +s32 PS4_SYSV_ABI sceWebBrowserDialogSetCookie(); +s32 PS4_SYSV_ABI sceWebBrowserDialogSetZoom(); +s32 PS4_SYSV_ABI sceWebBrowserDialogTerminate(); +s32 PS4_SYSV_ABI sceWebBrowserDialogUpdateStatus(); +s32 PS4_SYSV_ABI Func_F2BE042771625F8C(); + +void RegisterlibSceWebBrowserDialog(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::WebBrowserDialog \ No newline at end of file From b3c573f7989ffe3cc0fe15ce69f620bcaab9c61b Mon Sep 17 00:00:00 2001 From: poly <47796739+polybiusproxy@users.noreply.github.com> Date: Fri, 24 Jan 2025 19:37:34 +0100 Subject: [PATCH 486/549] libraries/fiber: print fiber ctx size on stack overflow --- src/core/libraries/fiber/fiber.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/libraries/fiber/fiber.cpp b/src/core/libraries/fiber/fiber.cpp index b77b5b5b6..345f0834d 100644 --- a/src/core/libraries/fiber/fiber.cpp +++ b/src/core/libraries/fiber/fiber.cpp @@ -37,8 +37,9 @@ extern "C" void PS4_SYSV_ABI _sceFiberForceQuit(u64 ret) { void PS4_SYSV_ABI _sceFiberCheckStackOverflow(OrbisFiberContext* ctx) { u64* stack_base = reinterpret_cast(ctx->current_fiber->addr_context); + u64 stack_size = ctx->current_fiber->size_context; if (stack_base && *stack_base != kFiberStackSignature) { - UNREACHABLE_MSG("Stack overflow detected in fiber."); + UNREACHABLE_MSG("Stack overflow detected in fiber with size = 0x{:x}", stack_size); } } From 56f4b8a2b860fa14421b0e6884532b441ea34a12 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 24 Jan 2025 10:41:58 -0800 Subject: [PATCH 487/549] shader_recompiler: Implement shader export formats. (#2226) --- externals/sirit | 2 +- .../spirv/emit_spirv_bitwise_conversion.cpp | 44 +++++ .../backend/spirv/emit_spirv_instructions.h | 8 + .../frontend/translate/export.cpp | 152 ++++++++++++++---- .../frontend/translate/translate.h | 9 ++ .../frontend/translate/vector_alu.cpp | 33 ++-- src/shader_recompiler/ir/ir_emitter.cpp | 32 ++++ src/shader_recompiler/ir/ir_emitter.h | 8 + src/shader_recompiler/ir/opcodes.inc | 8 + .../ir/passes/constant_propagation_pass.cpp | 16 ++ src/shader_recompiler/runtime_info.h | 1 + src/video_core/amdgpu/liverpool.h | 4 + .../renderer_vulkan/vk_graphics_pipeline.h | 5 +- .../renderer_vulkan/vk_pipeline_cache.cpp | 21 ++- 14 files changed, 286 insertions(+), 57 deletions(-) diff --git a/externals/sirit b/externals/sirit index 26ad5a9d0..d6f3c0d99 160000 --- a/externals/sirit +++ b/externals/sirit @@ -1 +1 @@ -Subproject commit 26ad5a9d0fe13260b0d7d6c64419d01a196b2e32 +Subproject commit d6f3c0d99862ab2ff8f95e9ac221560f1f97e29a diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp index 02ac74e19..539c6cb81 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp @@ -58,4 +58,48 @@ Id EmitUnpackHalf2x16(EmitContext& ctx, Id value) { return ctx.OpUnpackHalf2x16(ctx.F32[2], value); } +Id EmitPackUnorm2x16(EmitContext& ctx, Id value) { + return ctx.OpPackUnorm2x16(ctx.U32[1], value); +} + +Id EmitUnpackUnorm2x16(EmitContext& ctx, Id value) { + return ctx.OpUnpackUnorm2x16(ctx.F32[2], value); +} + +Id EmitPackSnorm2x16(EmitContext& ctx, Id value) { + return ctx.OpPackSnorm2x16(ctx.U32[1], value); +} + +Id EmitUnpackSnorm2x16(EmitContext& ctx, Id value) { + return ctx.OpUnpackSnorm2x16(ctx.F32[2], value); +} + +Id EmitPackUint2x16(EmitContext& ctx, Id value) { + // No SPIR-V instruction for this, do it manually. + const auto x{ctx.OpCompositeExtract(ctx.U32[1], value, 0)}; + const auto y{ctx.OpCompositeExtract(ctx.U32[1], value, 1)}; + return ctx.OpBitFieldInsert(ctx.U32[1], x, y, ctx.ConstU32(16U), ctx.ConstU32(16U)); +} + +Id EmitUnpackUint2x16(EmitContext& ctx, Id value) { + // No SPIR-V instruction for this, do it manually. + const auto x{ctx.OpBitFieldUExtract(ctx.U32[1], value, ctx.ConstU32(0U), ctx.ConstU32(16U))}; + const auto y{ctx.OpBitFieldUExtract(ctx.U32[1], value, ctx.ConstU32(16U), ctx.ConstU32(16U))}; + return ctx.OpCompositeConstruct(ctx.U32[2], x, y); +} + +Id EmitPackSint2x16(EmitContext& ctx, Id value) { + // No SPIR-V instruction for this, do it manually. + const auto x{ctx.OpCompositeExtract(ctx.U32[1], value, 0)}; + const auto y{ctx.OpCompositeExtract(ctx.U32[1], value, 1)}; + return ctx.OpBitFieldInsert(ctx.U32[1], x, y, ctx.ConstU32(16U), ctx.ConstU32(16U)); +} + +Id EmitUnpackSint2x16(EmitContext& ctx, Id value) { + // No SPIR-V instruction for this, do it manually. + const auto x{ctx.OpBitFieldSExtract(ctx.U32[1], value, ctx.ConstU32(0U), ctx.ConstU32(16U))}; + const auto y{ctx.OpBitFieldSExtract(ctx.U32[1], value, ctx.ConstU32(16U), ctx.ConstU32(16U))}; + return ctx.OpCompositeConstruct(ctx.U32[2], x, y); +} + } // namespace Shader::Backend::SPIRV diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index 4833dc9d0..842b13207 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h @@ -197,6 +197,14 @@ Id EmitPackFloat2x16(EmitContext& ctx, Id value); Id EmitUnpackFloat2x16(EmitContext& ctx, Id value); Id EmitPackHalf2x16(EmitContext& ctx, Id value); Id EmitUnpackHalf2x16(EmitContext& ctx, Id value); +Id EmitPackUnorm2x16(EmitContext& ctx, Id value); +Id EmitUnpackUnorm2x16(EmitContext& ctx, Id value); +Id EmitPackSnorm2x16(EmitContext& ctx, Id value); +Id EmitUnpackSnorm2x16(EmitContext& ctx, Id value); +Id EmitPackUint2x16(EmitContext& ctx, Id value); +Id EmitUnpackUint2x16(EmitContext& ctx, Id value); +Id EmitPackSint2x16(EmitContext& ctx, Id value); +Id EmitUnpackSint2x16(EmitContext& ctx, Id value); Id EmitFPAbs16(EmitContext& ctx, Id value); Id EmitFPAbs32(EmitContext& ctx, Id value); Id EmitFPAbs64(EmitContext& ctx, Id value); diff --git a/src/shader_recompiler/frontend/translate/export.cpp b/src/shader_recompiler/frontend/translate/export.cpp index 38ff9ae14..84c2ee658 100644 --- a/src/shader_recompiler/frontend/translate/export.cpp +++ b/src/shader_recompiler/frontend/translate/export.cpp @@ -7,6 +7,125 @@ namespace Shader::Gcn { +u32 SwizzleMrtComponent(const FragmentRuntimeInfo::PsColorBuffer& color_buffer, u32 comp) { + const auto [r, g, b, a] = color_buffer.swizzle; + const std::array swizzle_array = {r, g, b, a}; + const auto swizzled_comp_type = static_cast(swizzle_array[comp]); + constexpr auto min_comp_type = static_cast(AmdGpu::CompSwizzle::Red); + return swizzled_comp_type >= min_comp_type ? swizzled_comp_type - min_comp_type : comp; +} + +void Translator::ExportMrtValue(IR::Attribute attribute, u32 comp, const IR::F32& value, + const FragmentRuntimeInfo::PsColorBuffer& color_buffer) { + const auto converted = ApplyWriteNumberConversion(ir, value, color_buffer.num_conversion); + ir.SetAttribute(attribute, converted, comp); +} + +void Translator::ExportMrtCompressed(IR::Attribute attribute, u32 idx, const IR::U32& value) { + const u32 color_buffer_idx = + static_cast(attribute) - static_cast(IR::Attribute::RenderTarget0); + const auto color_buffer = runtime_info.fs_info.color_buffers[color_buffer_idx]; + + IR::Value unpacked_value; + bool is_integer = false; + switch (color_buffer.export_format) { + case AmdGpu::Liverpool::ShaderExportFormat::Zero: + // No export + return; + case AmdGpu::Liverpool::ShaderExportFormat::ABGR_FP16: + unpacked_value = ir.UnpackHalf2x16(value); + break; + case AmdGpu::Liverpool::ShaderExportFormat::ABGR_UNORM16: + unpacked_value = ir.UnpackUnorm2x16(value); + break; + case AmdGpu::Liverpool::ShaderExportFormat::ABGR_SNORM16: + unpacked_value = ir.UnpackSnorm2x16(value); + break; + case AmdGpu::Liverpool::ShaderExportFormat::ABGR_UINT16: + unpacked_value = ir.UnpackUint2x16(value); + is_integer = true; + break; + case AmdGpu::Liverpool::ShaderExportFormat::ABGR_SINT16: + unpacked_value = ir.UnpackSint2x16(value); + is_integer = true; + break; + default: + UNREACHABLE_MSG("Unimplemented compressed MRT export format {}", + static_cast(color_buffer.export_format)); + break; + } + + const auto r = ir.CompositeExtract(unpacked_value, 0); + const auto g = ir.CompositeExtract(unpacked_value, 1); + const IR::F32 float_r = is_integer ? ir.BitCast(IR::U32{r}) : IR::F32{r}; + const IR::F32 float_g = is_integer ? ir.BitCast(IR::U32{g}) : IR::F32{g}; + + const auto swizzled_r = SwizzleMrtComponent(color_buffer, idx * 2); + const auto swizzled_g = SwizzleMrtComponent(color_buffer, idx * 2 + 1); + + ExportMrtValue(attribute, swizzled_r, float_r, color_buffer); + ExportMrtValue(attribute, swizzled_g, float_g, color_buffer); +} + +void Translator::ExportMrtUncompressed(IR::Attribute attribute, u32 comp, const IR::F32& value) { + const u32 color_buffer_idx = + static_cast(attribute) - static_cast(IR::Attribute::RenderTarget0); + const auto color_buffer = runtime_info.fs_info.color_buffers[color_buffer_idx]; + const auto swizzled_comp = SwizzleMrtComponent(color_buffer, comp); + + switch (color_buffer.export_format) { + case AmdGpu::Liverpool::ShaderExportFormat::Zero: + // No export + return; + case AmdGpu::Liverpool::ShaderExportFormat::R_32: + // Red only + if (swizzled_comp != 0) { + return; + } + break; + case AmdGpu::Liverpool::ShaderExportFormat::GR_32: + // Red and Green only + if (swizzled_comp != 0 && swizzled_comp != 1) { + return; + } + break; + case AmdGpu::Liverpool::ShaderExportFormat::AR_32: + // Red and Alpha only + if (swizzled_comp != 0 && swizzled_comp != 3) { + return; + } + break; + case AmdGpu::Liverpool::ShaderExportFormat::ABGR_32: + // All components + break; + default: + UNREACHABLE_MSG("Unimplemented uncompressed MRT export format {}", + static_cast(color_buffer.export_format)); + break; + } + ExportMrtValue(attribute, swizzled_comp, value, color_buffer); +} + +void Translator::ExportCompressed(IR::Attribute attribute, u32 idx, const IR::U32& value) { + if (IsMrt(attribute)) { + ExportMrtCompressed(attribute, idx, value); + return; + } + const IR::Value unpacked_value = ir.UnpackHalf2x16(value); + const IR::F32 r = IR::F32{ir.CompositeExtract(unpacked_value, 0)}; + const IR::F32 g = IR::F32{ir.CompositeExtract(unpacked_value, 1)}; + ir.SetAttribute(attribute, r, idx * 2); + ir.SetAttribute(attribute, g, idx * 2 + 1); +} + +void Translator::ExportUncompressed(IR::Attribute attribute, u32 comp, const IR::F32& value) { + if (IsMrt(attribute)) { + ExportMrtUncompressed(attribute, comp, value); + return; + } + ir.SetAttribute(attribute, value, comp); +} + void Translator::EmitExport(const GcnInst& inst) { if (ir.block->has_multiple_predecessors && info.stage == Stage::Fragment) { ir.Discard(ir.LogicalNot(ir.GetExec())); @@ -26,41 +145,15 @@ void Translator::EmitExport(const GcnInst& inst) { IR::VectorReg(inst.src[3].code), }; - const auto set_attribute = [&](u32 comp, IR::F32 value) { - if (!IR::IsMrt(attrib)) { - ir.SetAttribute(attrib, value, comp); - return; - } - const u32 index = u32(attrib) - u32(IR::Attribute::RenderTarget0); - const auto col_buf = runtime_info.fs_info.color_buffers[index]; - const auto converted = IR::ApplyWriteNumberConversion(ir, value, col_buf.num_conversion); - const auto [r, g, b, a] = col_buf.swizzle; - const std::array swizzle_array = {r, g, b, a}; - const auto swizzled_comp = swizzle_array[comp]; - if (u32(swizzled_comp) < u32(AmdGpu::CompSwizzle::Red)) { - ir.SetAttribute(attrib, converted, comp); - return; - } - ir.SetAttribute(attrib, converted, u32(swizzled_comp) - u32(AmdGpu::CompSwizzle::Red)); - }; - - const auto unpack = [&](u32 idx) { - const IR::Value value = ir.UnpackHalf2x16(ir.GetVectorReg(vsrc[idx])); - const IR::F32 r = IR::F32{ir.CompositeExtract(value, 0)}; - const IR::F32 g = IR::F32{ir.CompositeExtract(value, 1)}; - set_attribute(idx * 2, r); - set_attribute(idx * 2 + 1, g); - }; - // Components are float16 packed into a VGPR if (exp.compr) { // Export R, G if (exp.en & 1) { - unpack(0); + ExportCompressed(attrib, 0, ir.GetVectorReg(vsrc[0])); } // Export B, A if ((exp.en >> 2) & 1) { - unpack(1); + ExportCompressed(attrib, 1, ir.GetVectorReg(vsrc[1])); } } else { // Components are float32 into separate VGPRS @@ -69,8 +162,7 @@ void Translator::EmitExport(const GcnInst& inst) { if ((mask & 1) == 0) { continue; } - const IR::F32 comp = ir.GetVectorReg(vsrc[i]); - set_attribute(i, comp); + ExportUncompressed(attrib, i, ir.GetVectorReg(vsrc[i])); } } if (IR::IsMrt(attrib)) { diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index 496455b50..287885854 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -170,6 +170,7 @@ public: void V_SUBBREV_U32(const GcnInst& inst); void V_LDEXP_F32(const GcnInst& inst); void V_CVT_PKNORM_U16_F32(const GcnInst& inst); + void V_CVT_PKNORM_I16_F32(const GcnInst& inst); void V_CVT_PKRTZ_F16_F32(const GcnInst& inst); // VOP1 @@ -244,6 +245,7 @@ public: void V_SAD(const GcnInst& inst); void V_SAD_U32(const GcnInst& inst); void V_CVT_PK_U16_U32(const GcnInst& inst); + void V_CVT_PK_I16_I32(const GcnInst& inst); void V_CVT_PK_U8_F32(const GcnInst& inst); void V_LSHL_B64(const GcnInst& inst); void V_MUL_F64(const GcnInst& inst); @@ -306,6 +308,13 @@ private: IR::F32 SelectCubeResult(const IR::F32& x, const IR::F32& y, const IR::F32& z, const IR::F32& x_res, const IR::F32& y_res, const IR::F32& z_res); + void ExportMrtValue(IR::Attribute attribute, u32 comp, const IR::F32& value, + const FragmentRuntimeInfo::PsColorBuffer& color_buffer); + void ExportMrtCompressed(IR::Attribute attribute, u32 idx, const IR::U32& value); + void ExportMrtUncompressed(IR::Attribute attribute, u32 comp, const IR::F32& value); + void ExportCompressed(IR::Attribute attribute, u32 idx, const IR::U32& value); + void ExportUncompressed(IR::Attribute attribute, u32 comp, const IR::F32& value); + void LogMissingOpcode(const GcnInst& inst); private: diff --git a/src/shader_recompiler/frontend/translate/vector_alu.cpp b/src/shader_recompiler/frontend/translate/vector_alu.cpp index 42dbcc513..f73618dbe 100644 --- a/src/shader_recompiler/frontend/translate/vector_alu.cpp +++ b/src/shader_recompiler/frontend/translate/vector_alu.cpp @@ -96,6 +96,8 @@ void Translator::EmitVectorAlu(const GcnInst& inst) { return V_LDEXP_F32(inst); case Opcode::V_CVT_PKNORM_U16_F32: return V_CVT_PKNORM_U16_F32(inst); + case Opcode::V_CVT_PKNORM_I16_F32: + return V_CVT_PKNORM_I16_F32(inst); case Opcode::V_CVT_PKRTZ_F16_F32: return V_CVT_PKRTZ_F16_F32(inst); @@ -376,6 +378,8 @@ void Translator::EmitVectorAlu(const GcnInst& inst) { return V_SAD_U32(inst); case Opcode::V_CVT_PK_U16_U32: return V_CVT_PK_U16_U32(inst); + case Opcode::V_CVT_PK_I16_I32: + return V_CVT_PK_I16_I32(inst); case Opcode::V_CVT_PK_U8_F32: return V_CVT_PK_U8_F32(inst); case Opcode::V_LSHL_B64: @@ -645,12 +649,15 @@ void Translator::V_LDEXP_F32(const GcnInst& inst) { } void Translator::V_CVT_PKNORM_U16_F32(const GcnInst& inst) { - const IR::F32 src0{GetSrc(inst.src[0])}; - const IR::F32 src1{GetSrc(inst.src[1])}; - const IR::U32 dst0 = ir.ConvertFToU(32, ir.FPMul(src0, ir.Imm32(65535.f))); - const IR::U32 dst1 = ir.ConvertFToU(32, ir.FPMul(src1, ir.Imm32(65535.f))); - const IR::VectorReg dst_reg{inst.dst[0].code}; - ir.SetVectorReg(dst_reg, ir.BitFieldInsert(dst0, dst1, ir.Imm32(16), ir.Imm32(16))); + const IR::Value vec_f32 = + ir.CompositeConstruct(GetSrc(inst.src[0]), GetSrc(inst.src[1])); + SetDst(inst.dst[0], ir.PackUnorm2x16(vec_f32)); +} + +void Translator::V_CVT_PKNORM_I16_F32(const GcnInst& inst) { + const IR::Value vec_f32 = + ir.CompositeConstruct(GetSrc(inst.src[0]), GetSrc(inst.src[1])); + SetDst(inst.dst[0], ir.PackSnorm2x16(vec_f32)); } void Translator::V_CVT_PKRTZ_F16_F32(const GcnInst& inst) { @@ -1237,11 +1244,15 @@ void Translator::V_SAD_U32(const GcnInst& inst) { } void Translator::V_CVT_PK_U16_U32(const GcnInst& inst) { - const IR::U32 src0{GetSrc(inst.src[0])}; - const IR::U32 src1{GetSrc(inst.src[1])}; - const IR::U32 lo = ir.IMin(src0, ir.Imm32(0xFFFF), false); - const IR::U32 hi = ir.IMin(src1, ir.Imm32(0xFFFF), false); - SetDst(inst.dst[0], ir.BitFieldInsert(lo, hi, ir.Imm32(16), ir.Imm32(16))); + const IR::Value vec_u32 = + ir.CompositeConstruct(GetSrc(inst.src[0]), GetSrc(inst.src[1])); + SetDst(inst.dst[0], ir.PackUint2x16(vec_u32)); +} + +void Translator::V_CVT_PK_I16_I32(const GcnInst& inst) { + const IR::Value vec_u32 = + ir.CompositeConstruct(GetSrc(inst.src[0]), GetSrc(inst.src[1])); + SetDst(inst.dst[0], ir.PackSint2x16(vec_u32)); } void Translator::V_CVT_PK_U8_F32(const GcnInst& inst) { diff --git a/src/shader_recompiler/ir/ir_emitter.cpp b/src/shader_recompiler/ir/ir_emitter.cpp index f0558665b..ecbe1f838 100644 --- a/src/shader_recompiler/ir/ir_emitter.cpp +++ b/src/shader_recompiler/ir/ir_emitter.cpp @@ -795,6 +795,38 @@ Value IREmitter::UnpackHalf2x16(const U32& value) { return Inst(Opcode::UnpackHalf2x16, value); } +U32 IREmitter::PackUnorm2x16(const Value& vector) { + return Inst(Opcode::PackUnorm2x16, vector); +} + +Value IREmitter::UnpackUnorm2x16(const U32& value) { + return Inst(Opcode::UnpackUnorm2x16, value); +} + +U32 IREmitter::PackSnorm2x16(const Value& vector) { + return Inst(Opcode::PackSnorm2x16, vector); +} + +Value IREmitter::UnpackSnorm2x16(const U32& value) { + return Inst(Opcode::UnpackSnorm2x16, value); +} + +U32 IREmitter::PackUint2x16(const Value& value) { + return Inst(Opcode::PackUint2x16, value); +} + +Value IREmitter::UnpackUint2x16(const U32& value) { + return Inst(Opcode::UnpackUint2x16, value); +} + +U32 IREmitter::PackSint2x16(const Value& value) { + return Inst(Opcode::PackSint2x16, value); +} + +Value IREmitter::UnpackSint2x16(const U32& value) { + return Inst(Opcode::UnpackSint2x16, value); +} + F32F64 IREmitter::FPMul(const F32F64& a, const F32F64& b) { if (a.Type() != b.Type()) { UNREACHABLE_MSG("Mismatching types {} and {}", a.Type(), b.Type()); diff --git a/src/shader_recompiler/ir/ir_emitter.h b/src/shader_recompiler/ir/ir_emitter.h index 5dd49ce7a..97b94187a 100644 --- a/src/shader_recompiler/ir/ir_emitter.h +++ b/src/shader_recompiler/ir/ir_emitter.h @@ -175,6 +175,14 @@ public: [[nodiscard]] U32 PackHalf2x16(const Value& vector); [[nodiscard]] Value UnpackHalf2x16(const U32& value); + [[nodiscard]] U32 PackUnorm2x16(const Value& vector); + [[nodiscard]] Value UnpackUnorm2x16(const U32& value); + [[nodiscard]] U32 PackSnorm2x16(const Value& vector); + [[nodiscard]] Value UnpackSnorm2x16(const U32& value); + [[nodiscard]] U32 PackUint2x16(const Value& value); + [[nodiscard]] Value UnpackUint2x16(const U32& value); + [[nodiscard]] U32 PackSint2x16(const Value& value); + [[nodiscard]] Value UnpackSint2x16(const U32& value); [[nodiscard]] F32F64 FPAdd(const F32F64& a, const F32F64& b); [[nodiscard]] F32F64 FPSub(const F32F64& a, const F32F64& b); diff --git a/src/shader_recompiler/ir/opcodes.inc b/src/shader_recompiler/ir/opcodes.inc index 63a4e1e62..6750be5a6 100644 --- a/src/shader_recompiler/ir/opcodes.inc +++ b/src/shader_recompiler/ir/opcodes.inc @@ -187,6 +187,14 @@ OPCODE(PackFloat2x16, U32, F16x OPCODE(UnpackFloat2x16, F16x2, U32, ) OPCODE(PackHalf2x16, U32, F32x2, ) OPCODE(UnpackHalf2x16, F32x2, U32, ) +OPCODE(PackUnorm2x16, U32, F32x2, ) +OPCODE(UnpackUnorm2x16, F32x2, U32, ) +OPCODE(PackSnorm2x16, U32, F32x2, ) +OPCODE(UnpackSnorm2x16, F32x2, U32, ) +OPCODE(PackUint2x16, U32, U32x2, ) +OPCODE(UnpackUint2x16, U32x2, U32, ) +OPCODE(PackSint2x16, U32, U32x2, ) +OPCODE(UnpackSint2x16, U32x2, U32, ) // Floating-point operations OPCODE(FPAbs32, F32, F32, ) diff --git a/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp b/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp index 12a1b56e9..c72b9e835 100644 --- a/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp +++ b/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp @@ -348,6 +348,22 @@ void ConstantPropagation(IR::Block& block, IR::Inst& inst) { return FoldInverseFunc(inst, IR::Opcode::UnpackFloat2x16); case IR::Opcode::UnpackFloat2x16: return FoldInverseFunc(inst, IR::Opcode::PackFloat2x16); + case IR::Opcode::PackUnorm2x16: + return FoldInverseFunc(inst, IR::Opcode::UnpackUnorm2x16); + case IR::Opcode::UnpackUnorm2x16: + return FoldInverseFunc(inst, IR::Opcode::PackUnorm2x16); + case IR::Opcode::PackSnorm2x16: + return FoldInverseFunc(inst, IR::Opcode::UnpackSnorm2x16); + case IR::Opcode::UnpackSnorm2x16: + return FoldInverseFunc(inst, IR::Opcode::PackSnorm2x16); + case IR::Opcode::PackUint2x16: + return FoldInverseFunc(inst, IR::Opcode::UnpackUint2x16); + case IR::Opcode::UnpackUint2x16: + return FoldInverseFunc(inst, IR::Opcode::PackUint2x16); + case IR::Opcode::PackSint2x16: + return FoldInverseFunc(inst, IR::Opcode::UnpackSint2x16); + case IR::Opcode::UnpackSint2x16: + return FoldInverseFunc(inst, IR::Opcode::PackSint2x16); case IR::Opcode::SelectU1: case IR::Opcode::SelectU8: case IR::Opcode::SelectU16: diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index 2bf5e3f0a..103c1faa8 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -184,6 +184,7 @@ struct FragmentRuntimeInfo { AmdGpu::NumberFormat num_format; AmdGpu::NumberConversion num_conversion; AmdGpu::CompMapping swizzle; + AmdGpu::Liverpool::ShaderExportFormat export_format; auto operator<=>(const PsColorBuffer&) const noexcept = default; }; diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index ec9c92ef1..67821b0f2 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -266,6 +266,10 @@ struct Liverpool { BitField<20, 4, ShaderExportFormat> col5; BitField<24, 4, ShaderExportFormat> col6; BitField<28, 4, ShaderExportFormat> col7; + + [[nodiscard]] ShaderExportFormat GetFormat(const u32 buf_idx) const { + return static_cast((raw >> (buf_idx * 4)) & 0xfu); + } }; union VsOutputControl { diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index 9a20f94c1..8c5cb1f3b 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -35,9 +35,8 @@ struct GraphicsPipelineKey { std::array stage_hashes; u32 num_color_attachments; std::array color_formats; - std::array color_num_formats; - std::array color_num_conversions; - std::array color_swizzles; + std::array + color_buffers; vk::Format depth_format; vk::Format stencil_format; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index c6a56745d..3728a55fb 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -167,11 +167,7 @@ const Shader::RuntimeInfo& PipelineCache::BuildRuntimeInfo(Stage stage, LogicalS }; } for (u32 i = 0; i < Shader::MaxColorBuffers; i++) { - info.fs_info.color_buffers[i] = { - .num_format = graphics_key.color_num_formats[i], - .num_conversion = graphics_key.color_num_conversions[i], - .swizzle = graphics_key.color_swizzles[i], - }; + info.fs_info.color_buffers[i] = graphics_key.color_buffers[i]; } break; } @@ -309,11 +305,9 @@ bool PipelineCache::RefreshGraphicsKey() { // order. We need to do some arrays compaction at this stage key.num_color_attachments = 0; key.color_formats.fill(vk::Format::eUndefined); - key.color_num_formats.fill(AmdGpu::NumberFormat::Unorm); - key.color_num_conversions.fill(AmdGpu::NumberConversion::None); + key.color_buffers.fill({}); key.blend_controls.fill({}); key.write_masks.fill({}); - key.color_swizzles.fill({}); key.vertex_buffer_formats.fill(vk::Format::eUndefined); key.patch_control_points = 0; @@ -338,9 +332,12 @@ bool PipelineCache::RefreshGraphicsKey() { key.color_formats[remapped_cb] = LiverpoolToVK::SurfaceFormat(col_buf.GetDataFmt(), col_buf.GetNumberFmt()); - key.color_num_formats[remapped_cb] = col_buf.GetNumberFmt(); - key.color_num_conversions[remapped_cb] = col_buf.GetNumberConversion(); - key.color_swizzles[remapped_cb] = col_buf.Swizzle(); + key.color_buffers[remapped_cb] = { + .num_format = col_buf.GetNumberFmt(), + .num_conversion = col_buf.GetNumberConversion(), + .swizzle = col_buf.Swizzle(), + .export_format = regs.color_export_format.GetFormat(cb), + }; } fetch_shader = std::nullopt; @@ -456,7 +453,7 @@ bool PipelineCache::RefreshGraphicsKey() { // of the latter we need to change format to undefined, and either way we need to // increment the index for the null attachment binding. key.color_formats[remapped_cb] = vk::Format::eUndefined; - key.color_swizzles[remapped_cb] = {}; + key.color_buffers[remapped_cb] = {}; ++remapped_cb; continue; } From a51c8c17e07063065fe1780d648c3b4808f31d59 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 24 Jan 2025 12:47:04 -0800 Subject: [PATCH 488/549] shader_recompiler: Fix image write swizzles. (#2236) --- .../ir/passes/resource_tracking_pass.cpp | 4 +-- src/video_core/amdgpu/types.h | 31 ++++++++++++++++--- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index d94c5223a..43d125bf0 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -569,7 +569,7 @@ void PatchTextureBufferArgs(IR::Block& block, IR::Inst& inst, Info& info) { inst.SetArg(1, CalculateBufferAddress(ir, inst, info, buffer, 1U)); if (inst.GetOpcode() == IR::Opcode::StoreBufferFormatF32) { - const auto swizzled = ApplySwizzle(ir, inst.Arg(2), buffer.DstSelect()); + const auto swizzled = ApplySwizzle(ir, inst.Arg(2), buffer.DstSelect().Inverse()); const auto converted = ApplyWriteNumberConversionVec4(ir, swizzled, buffer.GetNumberConversion()); inst.SetArg(2, converted); @@ -829,7 +829,7 @@ void PatchImageArgs(IR::Block& block, IR::Inst& inst, Info& info) { auto texel = inst.Arg(4); if (is_storage) { // Storage image requires shader swizzle. - texel = ApplySwizzle(ir, texel, image.DstSelect()); + texel = ApplySwizzle(ir, texel, image.DstSelect().Inverse()); } const auto converted = ApplyWriteNumberConversionVec4(ir, texel, image.GetNumberConversion()); diff --git a/src/video_core/amdgpu/types.h b/src/video_core/amdgpu/types.h index 63e184cc5..b442b2f1e 100644 --- a/src/video_core/amdgpu/types.h +++ b/src/video_core/amdgpu/types.h @@ -200,10 +200,10 @@ enum class NumberConversion : u32 { }; struct CompMapping { - CompSwizzle r : 3; - CompSwizzle g : 3; - CompSwizzle b : 3; - CompSwizzle a : 3; + CompSwizzle r; + CompSwizzle g; + CompSwizzle b; + CompSwizzle a; auto operator<=>(const CompMapping& other) const = default; @@ -217,6 +217,15 @@ struct CompMapping { }; } + [[nodiscard]] CompMapping Inverse() const { + CompMapping result{}; + InverseSingle(result.r, CompSwizzle::Red); + InverseSingle(result.g, CompSwizzle::Green); + InverseSingle(result.b, CompSwizzle::Blue); + InverseSingle(result.a, CompSwizzle::Alpha); + return result; + } + private: template T ApplySingle(const std::array& data, const CompSwizzle swizzle) const { @@ -237,6 +246,20 @@ private: UNREACHABLE(); } } + + void InverseSingle(CompSwizzle& dst, const CompSwizzle target) const { + if (r == target) { + dst = CompSwizzle::Red; + } else if (g == target) { + dst = CompSwizzle::Green; + } else if (b == target) { + dst = CompSwizzle::Blue; + } else if (a == target) { + dst = CompSwizzle::Alpha; + } else { + dst = CompSwizzle::Zero; + } + } }; inline DataFormat RemapDataFormat(const DataFormat format) { From 361532418ce8a252fd873eacff0d0b83504eeffb Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 24 Jan 2025 12:47:24 -0800 Subject: [PATCH 489/549] externals: Update SPIRV-Cross for MoltenVK (#2237) --- externals/MoltenVK/SPIRV-Cross | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externals/MoltenVK/SPIRV-Cross b/externals/MoltenVK/SPIRV-Cross index 6173e24b3..1a7b7ef6d 160000 --- a/externals/MoltenVK/SPIRV-Cross +++ b/externals/MoltenVK/SPIRV-Cross @@ -1 +1 @@ -Subproject commit 6173e24b31f09a0c3217103a130e74c4ddec14a6 +Subproject commit 1a7b7ef6de02cf6767e42b10ddad217c45e90d47 From 7072dfc99fdca87e833bf588587debef3e9b08bd Mon Sep 17 00:00:00 2001 From: hspir404 Date: Fri, 24 Jan 2025 21:56:21 +0000 Subject: [PATCH 490/549] Fix stale heap read in UnmapMemoryImpl (#2232) --- src/core/memory.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/memory.cpp b/src/core/memory.cpp index a8dd72acc..271092eaf 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -418,8 +418,9 @@ u64 MemoryManager::UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma vma.phys_base = 0; vma.disallow_merge = false; vma.name = ""; - MergeAdjacent(vma_map, new_it); - bool readonly_file = vma.prot == MemoryProt::CpuRead && type == VMAType::File; + const auto post_merge_it = MergeAdjacent(vma_map, new_it); + auto& post_merge_vma = post_merge_it->second; + bool readonly_file = post_merge_vma.prot == MemoryProt::CpuRead && type == VMAType::File; if (type != VMAType::Reserved && type != VMAType::PoolReserved) { // Unmap the memory region. impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + adjusted_size, From 73b7d344600a6a7aabb55d44dfe888deb8df312f Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 24 Jan 2025 19:55:27 -0800 Subject: [PATCH 491/549] hotfix: Drop scePadSetLightBar log to debug level. Some games like to spam this a lot, and we already handle it. --- src/core/libraries/pad/pad.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index f2b81fbe0..d940d0cfa 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -473,7 +473,7 @@ int PS4_SYSV_ABI scePadSetForceIntercepted() { int PS4_SYSV_ABI scePadSetLightBar(s32 handle, const OrbisPadLightBarParam* pParam) { if (pParam != nullptr) { - LOG_INFO(Lib_Pad, "scePadSetLightBar called handle = {} rgb = {} {} {}", handle, pParam->r, + LOG_DEBUG(Lib_Pad, "called handle = {} rgb = {} {} {}", handle, pParam->r, pParam->g, pParam->b); if (pParam->r < 0xD && pParam->g < 0xD && pParam->b < 0xD) { From e433f3116dd9a36a353d054e1a371e7ff257f87a Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 24 Jan 2025 19:57:04 -0800 Subject: [PATCH 492/549] hotfix 2: clang format --- src/core/libraries/pad/pad.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index d940d0cfa..18709bcb2 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -473,8 +473,8 @@ int PS4_SYSV_ABI scePadSetForceIntercepted() { int PS4_SYSV_ABI scePadSetLightBar(s32 handle, const OrbisPadLightBarParam* pParam) { if (pParam != nullptr) { - LOG_DEBUG(Lib_Pad, "called handle = {} rgb = {} {} {}", handle, pParam->r, - pParam->g, pParam->b); + LOG_DEBUG(Lib_Pad, "called handle = {} rgb = {} {} {}", handle, pParam->r, pParam->g, + pParam->b); if (pParam->r < 0xD && pParam->g < 0xD && pParam->b < 0xD) { LOG_INFO(Lib_Pad, "Invalid lightbar setting"); From 564dbc7b94b02c70febd901f613ccb567923cb5e Mon Sep 17 00:00:00 2001 From: Ian Carpenter Date: Sat, 25 Jan 2025 04:00:52 -0500 Subject: [PATCH 493/549] system_service: Add simple event queue and push an EntitlementUpdate event to it when app content is initialized (#2238) --- .../libraries/app_content/app_content.cpp | 10 ++++++++ src/core/libraries/system/systemservice.cpp | 23 ++++++++++++++++--- src/core/libraries/system/systemservice.h | 4 ++++ 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/core/libraries/app_content/app_content.cpp b/src/core/libraries/app_content/app_content.cpp index 1d23e7f44..1223022c5 100644 --- a/src/core/libraries/app_content/app_content.cpp +++ b/src/core/libraries/app_content/app_content.cpp @@ -12,6 +12,7 @@ #include "core/file_sys/fs.h" #include "core/libraries/app_content/app_content_error.h" #include "core/libraries/libs.h" +#include "core/libraries/system/systemservice.h" namespace Libraries::AppContent { @@ -262,6 +263,15 @@ int PS4_SYSV_ABI sceAppContentInitialize(const OrbisAppContentInitParam* initPar entitlement_label.copy(info.entitlement_label, sizeof(info.entitlement_label)); } } + + if (addcont_count > 0) { + SystemService::OrbisSystemServiceEvent event{}; + event.event_type = SystemService::OrbisSystemServiceEventType::EntitlementUpdate; + event.service_entitlement_update.user_id = 0; + event.service_entitlement_update.np_service_label = 0; + SystemService::PushSystemServiceEvent(event); + } + return ORBIS_OK; } diff --git a/src/core/libraries/system/systemservice.cpp b/src/core/libraries/system/systemservice.cpp index ebb9f4392..a67b9a2fc 100644 --- a/src/core/libraries/system/systemservice.cpp +++ b/src/core/libraries/system/systemservice.cpp @@ -10,6 +10,8 @@ namespace Libraries::SystemService { bool g_splash_status{true}; +std::queue g_event_queue; +std::mutex g_event_queue_mutex; bool IsSplashVisible() { return Config::showSplash() && g_splash_status; @@ -1772,7 +1774,9 @@ s32 PS4_SYSV_ABI sceSystemServiceGetStatus(OrbisSystemServiceStatus* status) { LOG_ERROR(Lib_SystemService, "OrbisSystemServiceStatus is null"); return ORBIS_SYSTEM_SERVICE_ERROR_PARAMETER; } - status->event_num = 0; + + std::lock_guard lock(g_event_queue_mutex); + status->event_num = static_cast(g_event_queue.size()); status->is_system_ui_overlaid = false; status->is_in_background_execution = false; status->is_cpu_mode7_cpu_normal = true; @@ -1940,11 +1944,19 @@ int PS4_SYSV_ABI sceSystemServiceRaiseExceptionLocalProcess() { } s32 PS4_SYSV_ABI sceSystemServiceReceiveEvent(OrbisSystemServiceEvent* event) { - LOG_ERROR(Lib_SystemService, "(STUBBED) called"); + LOG_TRACE(Lib_SystemService, "called"); if (event == nullptr) { return ORBIS_SYSTEM_SERVICE_ERROR_PARAMETER; } - return ORBIS_SYSTEM_SERVICE_ERROR_NO_EVENT; + + std::lock_guard lock(g_event_queue_mutex); + if (g_event_queue.empty()) { + return ORBIS_SYSTEM_SERVICE_ERROR_NO_EVENT; + } + + *event = g_event_queue.front(); + g_event_queue.pop(); + return ORBIS_OK; } int PS4_SYSV_ABI sceSystemServiceReenableMusicPlayer() { @@ -2412,6 +2424,11 @@ int PS4_SYSV_ABI Func_CB5E885E225F69F0() { return ORBIS_OK; } +void PushSystemServiceEvent(const OrbisSystemServiceEvent& event) { + std::lock_guard lock(g_event_queue_mutex); + g_event_queue.push(event); +} + void RegisterlibSceSystemService(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("alZfRdr2RP8", "libSceAppMessaging", 1, "libSceSystemService", 1, 1, sceAppMessagingClearEventFlag); diff --git a/src/core/libraries/system/systemservice.h b/src/core/libraries/system/systemservice.h index cdd3c15e8..c22ccc88f 100644 --- a/src/core/libraries/system/systemservice.h +++ b/src/core/libraries/system/systemservice.h @@ -4,6 +4,8 @@ // https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain/blob/master/include/orbis/_types/sys_service.h #pragma once +#include +#include #include "common/types.h" namespace Core::Loader { @@ -603,5 +605,7 @@ int PS4_SYSV_ABI sceSystemServiceReenableVoiceRecognition(); int PS4_SYSV_ABI Func_6B1CDB955F0EBD65(); int PS4_SYSV_ABI Func_CB5E885E225F69F0(); +void PushSystemServiceEvent(const OrbisSystemServiceEvent& event); + void RegisterlibSceSystemService(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::SystemService From a5a1253185fc6b3231e30477ebb8dba5375ac936 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sat, 25 Jan 2025 04:12:18 -0800 Subject: [PATCH 494/549] liverpool: Implement PM4 MEM_SEMAPHORE. (#2235) --- src/video_core/amdgpu/liverpool.cpp | 24 ++++++++++++ src/video_core/amdgpu/pm4_cmds.h | 61 +++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index 8355dd1e6..2f40d4136 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -636,6 +636,18 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); + if (mem_semaphore->IsSignaling()) { + mem_semaphore->Signal(); + } else { + while (!mem_semaphore->Signaled()) { + YIELD_GFX(); + } + mem_semaphore->Decrement(); + } + break; + } case PM4ItOpcode::AcquireMem: { // const auto* acquire_mem = reinterpret_cast(header); break; @@ -848,6 +860,18 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, u32 vqid) { } break; } + case PM4ItOpcode::MemSemaphore: { + const auto* mem_semaphore = reinterpret_cast(header); + if (mem_semaphore->IsSignaling()) { + mem_semaphore->Signal(); + } else { + while (!mem_semaphore->Signaled()) { + YIELD_ASC(vqid); + } + mem_semaphore->Decrement(); + } + break; + } case PM4ItOpcode::WaitRegMem: { const auto* wait_reg_mem = reinterpret_cast(header); ASSERT(wait_reg_mem->engine.Value() == PM4CmdWaitRegMem::Engine::Me); diff --git a/src/video_core/amdgpu/pm4_cmds.h b/src/video_core/amdgpu/pm4_cmds.h index 311f4d4d0..e92ba17fa 100644 --- a/src/video_core/amdgpu/pm4_cmds.h +++ b/src/video_core/amdgpu/pm4_cmds.h @@ -884,4 +884,65 @@ struct PM4CmdDrawIndexIndirectMulti { u32 draw_initiator; ///< Draw Initiator Register }; +struct PM4CmdMemSemaphore { + enum class ClientCode : u32 { + CommandProcessor = 0u, + CommandBuffer = 1u, + DataBuffer = 2u, + }; + enum class Select : u32 { + SignalSemaphore = 6u, + WaitSemaphore = 7u, + }; + enum class SignalType : u32 { + Increment = 0u, + Write = 1u, + }; + + PM4Type3Header header; ///< header + union { + u32 dw1; + BitField<3, 29, u32> addr_lo; ///< Semaphore address bits [31:3] + }; + union { + u32 dw2; + BitField<0, 8, u32> addr_hi; ///< Semaphore address bits [39:32] + BitField<16, 1, u32> use_mailbox; ///< Enables waiting until mailbox is written to + BitField<20, 1, SignalType> signal_type; ///< Indicates the type of signal sent + BitField<24, 2, ClientCode> client_code; + BitField<29, 3, Select> sem_sel; ///< Indicates whether to do a signal or wait operation + }; + + template + [[nodiscard]] T Address() const { + return std::bit_cast(u64(addr_lo) << 3 | (u64(addr_hi) << 32)); + } + + [[nodiscard]] bool IsSignaling() const { + return sem_sel == Select::SignalSemaphore; + } + + [[nodiscard]] bool Signaled() const { + return *Address() > 0; + } + + void Decrement() const { + *Address() -= 1; + } + + void Signal() const { + auto* ptr = Address(); + switch (signal_type) { + case SignalType::Increment: + *ptr += 1; + break; + case SignalType::Write: + *ptr = 1; + break; + default: + UNREACHABLE_MSG("Unknown signal type {}", static_cast(signal_type.Value())); + } + } +}; + } // namespace AmdGpu From f1bc3b4f3dc492f4c067693a97e5932c8be38491 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sat, 25 Jan 2025 14:59:18 -0800 Subject: [PATCH 495/549] shader_recompiler: Add another constant propagation pass near the end. (#2231) --- src/shader_recompiler/recompiler.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/shader_recompiler/recompiler.cpp b/src/shader_recompiler/recompiler.cpp index bb027a11e..01518ab8f 100644 --- a/src/shader_recompiler/recompiler.cpp +++ b/src/shader_recompiler/recompiler.cpp @@ -90,6 +90,7 @@ IR::Program TranslateProgram(std::span code, Pools& pools, Info& info Shader::Optimization::ResourceTrackingPass(program); Shader::Optimization::IdentityRemovalPass(program.blocks); Shader::Optimization::DeadCodeEliminationPass(program); + Shader::Optimization::ConstantPropagationPass(program.post_order_blocks); Shader::Optimization::CollectShaderInfoPass(program); Shader::Optimization::SharedMemoryBarrierPass(program, profile); From 3960283a67af70fbaf2a64a1ac97f695acf47a16 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sun, 26 Jan 2025 01:00:22 -0800 Subject: [PATCH 496/549] hotfix: Fix missing embedded PS shader address bits. If the emulator code is above a 40-bit address, the embedded shaders need to use address-hi to work. Embedded VS shader already supplies it, PS shader should as well. --- src/core/libraries/gnmdriver/gnmdriver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index fdc3a1acd..06124167c 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -1366,7 +1366,7 @@ s32 PS4_SYSV_ABI sceGnmSetEmbeddedPsShader(u32* cmdbuf, u32 size, u32 shader_id, // pointer to a stack memory, so the check will likely fail. To workaround it we will // repeat set shader functionality here as it is trivial. cmdbuf = PM4CmdSetData::SetShReg(cmdbuf, 8u, ps_regs[0], - 0u); // SPI_SHADER_PGM_LO_PS/SPI_SHADER_PGM_HI_PS + ps_regs[1]); // SPI_SHADER_PGM_LO_PS/SPI_SHADER_PGM_HI_PS cmdbuf = PM4CmdSetData::SetShReg(cmdbuf, 10u, ps_regs[2], ps_regs[3]); // SPI_SHADER_PGM_RSRC1_PS/SPI_SHADER_PGM_RSRC2_PS cmdbuf = PM4CmdSetData::SetContextReg(cmdbuf, 0x1c4u, ps_regs[4], From 461148c22706ea35c8ae4985f21d695e46be01ff Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sun, 26 Jan 2025 04:32:14 -0800 Subject: [PATCH 497/549] qt: Prevent interacting with empty grid cells. (#2243) --- src/qt_gui/game_grid_frame.cpp | 3 ++- src/qt_gui/gui_context_menus.h | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/qt_gui/game_grid_frame.cpp b/src/qt_gui/game_grid_frame.cpp index 2ebb09e5d..d719ac878 100644 --- a/src/qt_gui/game_grid_frame.cpp +++ b/src/qt_gui/game_grid_frame.cpp @@ -38,17 +38,18 @@ GameGridFrame::GameGridFrame(std::shared_ptr game_info_get, void GameGridFrame::onCurrentCellChanged(int currentRow, int currentColumn, int previousRow, int previousColumn) { - cellClicked = true; crtRow = currentRow; crtColumn = currentColumn; columnCnt = this->columnCount(); auto itemID = (crtRow * columnCnt) + currentColumn; if (itemID > m_game_info->m_games.count() - 1) { + cellClicked = false; validCellSelected = false; BackgroundMusicPlayer::getInstance().stopMusic(); return; } + cellClicked = true; validCellSelected = true; SetGridBackgroundImage(crtRow, crtColumn); auto snd0Path = QString::fromStdString(m_game_info->m_games[itemID].snd0_path.string()); diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h index 0e8675c0c..72affeca7 100644 --- a/src/qt_gui/gui_context_menus.h +++ b/src/qt_gui/gui_context_menus.h @@ -41,8 +41,8 @@ public: itemID = widget->currentRow() * widget->columnCount() + widget->currentColumn(); } - // Do not show the menu if an item is selected - if (itemID == -1) { + // Do not show the menu if no item is selected + if (itemID < 0 || itemID >= m_games.size()) { return; } From 46b5437fdfea7e7b3add555d62540766daa4534d Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sun, 26 Jan 2025 08:01:15 -0800 Subject: [PATCH 498/549] emulator: Use correct game folder mount when opening update eboot directly. (#2244) --- src/emulator.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/emulator.cpp b/src/emulator.cpp index e77c2b87f..97215c06f 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -100,9 +100,21 @@ Emulator::~Emulator() { } void Emulator::Run(const std::filesystem::path& file, const std::vector args) { + const auto eboot_name = file.filename().string(); + auto game_folder = file.parent_path(); + if (const auto game_folder_name = game_folder.filename().string(); + game_folder_name.ends_with("-UPDATE")) { + // If an executable was launched from a separate update directory, + // use the base game directory as the game folder. + const auto base_name = game_folder_name.substr(0, game_folder_name.size() - 7); + const auto base_path = game_folder.parent_path() / base_name; + if (std::filesystem::is_directory(base_path)) { + game_folder = base_path; + } + } + // Applications expect to be run from /app0 so mount the file's parent path as app0. auto* mnt = Common::Singleton::Instance(); - const auto game_folder = file.parent_path(); mnt->Mount(game_folder, "/app0"); // Certain games may use /hostapp as well such as CUSA001100 mnt->Mount(game_folder, "/hostapp"); @@ -223,7 +235,7 @@ void Emulator::Run(const std::filesystem::path& file, const std::vectorGetHLESymbols()); // Load the module with the linker - const auto eboot_path = mnt->GetHostPath("/app0/" + file.filename().string()); + const auto eboot_path = mnt->GetHostPath("/app0/" + eboot_name); linker->LoadModule(eboot_path); // check if we have system modules to load From 7d1631f9f43f3b781a20dd91d7ebc0980b55b8ee Mon Sep 17 00:00:00 2001 From: Ian Carpenter Date: Sun, 26 Jan 2025 18:46:59 -0500 Subject: [PATCH 499/549] memory_patcher: Remove hardcoded repositories when loading patches (#2241) --- src/common/memory_patcher.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/common/memory_patcher.cpp b/src/common/memory_patcher.cpp index 6b79edb9f..f81a0ed83 100644 --- a/src/common/memory_patcher.cpp +++ b/src/common/memory_patcher.cpp @@ -7,6 +7,7 @@ #include #include #ifdef ENABLE_QT_GUI +#include #include #include #include @@ -189,14 +190,16 @@ void OnGameLoaded() { // We use the QT headers for the xml and json parsing, this define is only true on QT builds QString patchDir; Common::FS::PathToQString(patchDir, Common::FS::GetUserPath(Common::FS::PathType::PatchesDir)); - QString repositories[] = {"GoldHEN", "shadPS4"}; + QDir dir(patchDir); + QStringList folders = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); - for (const QString& repository : repositories) { - QString filesJsonPath = patchDir + "/" + repository + "/files.json"; + for (const QString& folder : folders) { + QString filesJsonPath = patchDir + "/" + folder + "/files.json"; QFile jsonFile(filesJsonPath); if (!jsonFile.open(QIODevice::ReadOnly)) { - LOG_ERROR(Loader, "Unable to open files.json for reading."); + LOG_ERROR(Loader, "Unable to open files.json for reading in repository {}", + folder.toStdString()); continue; } @@ -220,11 +223,12 @@ void OnGameLoaded() { } if (selectedFileName.isEmpty()) { - LOG_ERROR(Loader, "No patch file found for the current serial."); + LOG_ERROR(Loader, "No patch file found for the current serial in repository {}", + folder.toStdString()); continue; } - QString filePath = patchDir + "/" + repository + "/" + selectedFileName; + QString filePath = patchDir + "/" + folder + "/" + selectedFileName; QFile file(filePath); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { LOG_ERROR(Loader, "Unable to open the file for reading."); From 6f04ea18e4f3d87e9a89f0fefcc1c8c722a87de6 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sun, 26 Jan 2025 19:28:58 -0800 Subject: [PATCH 500/549] externals: Update MoltenVK (#2249) Fixes broken vertex binding dynamic stride when used with tessellation. --- externals/MoltenVK/MoltenVK | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externals/MoltenVK/MoltenVK b/externals/MoltenVK/MoltenVK index 2473ce6f0..6fa077fb8 160000 --- a/externals/MoltenVK/MoltenVK +++ b/externals/MoltenVK/MoltenVK @@ -1 +1 @@ -Subproject commit 2473ce6f0ab7d5d8a49aa91b2e37f3447a939f18 +Subproject commit 6fa077fb8ed8dac4e4cd66b6b1ebd7b4d955a754 From 191e64bfa11e45992e869e7a9df787d6fbe5038b Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Mon, 27 Jan 2025 11:17:23 +0300 Subject: [PATCH 501/549] renderer: respect zmin/zmax even if clipping is disabled (#2250) --- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 88b510eca..7a0652ed7 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -1172,6 +1172,8 @@ void Rasterizer::UpdateViewportScissorState(const GraphicsPipeline& pipeline) { continue; } + const auto zoffset = vp_ctl.zoffset_enable ? vp.zoffset : 0.f; + const auto zscale = vp_ctl.zscale_enable ? vp.zscale : 1.f; if (pipeline.IsClipDisabled()) { // In case if clipping is disabled we patch the shader to convert vertex position // from screen space coordinates to NDC by defining a render space as full hardware @@ -1181,16 +1183,14 @@ void Rasterizer::UpdateViewportScissorState(const GraphicsPipeline& pipeline) { .y = 0.f, .width = float(std::min(instance.GetMaxViewportWidth(), 16_KB)), .height = float(std::min(instance.GetMaxViewportHeight(), 16_KB)), - .minDepth = 0.0, - .maxDepth = 1.0, + .minDepth = zoffset - zscale * reduce_z, + .maxDepth = zscale + zoffset, }); } else { const auto xoffset = vp_ctl.xoffset_enable ? vp.xoffset : 0.f; const auto xscale = vp_ctl.xscale_enable ? vp.xscale : 1.f; const auto yoffset = vp_ctl.yoffset_enable ? vp.yoffset : 0.f; const auto yscale = vp_ctl.yscale_enable ? vp.yscale : 1.f; - const auto zoffset = vp_ctl.zoffset_enable ? vp.zoffset : 0.f; - const auto zscale = vp_ctl.zscale_enable ? vp.zscale : 1.f; viewports.push_back({ .x = xoffset - xscale, .y = yoffset - yscale, From 665261efc574562aad240466a9eed8b05b6b9a50 Mon Sep 17 00:00:00 2001 From: F1219R <109141852+F1219R@users.noreply.github.com> Date: Mon, 27 Jan 2025 16:39:27 +0100 Subject: [PATCH 502/549] Update sq translation (#2251) --- src/qt_gui/translations/sq.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index 2f88b4390..15c18d2f6 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -249,7 +249,7 @@ Open shadPS4 Folder - Open shadPS4 Folder + Hap dosjen e shadPS4 Exit @@ -542,7 +542,7 @@ Fullscreen Mode - Modaliteti i Plotë + Mënyra me ekran të plotë Enable Separate Update Folder @@ -550,7 +550,7 @@ Default tab when opening settings - Skeda e parazgjedhur kur hapni cilësimet + Skeda e parazgjedhur kur hapen cilësimet Show Game Size In List @@ -594,7 +594,7 @@ Open Log Location - Hap vendndodhjen e regjistrit + Hap vendndodhjen e Ditarit Input @@ -630,11 +630,11 @@ Gui - Ndërfaqe + Ndërfaqja User - Përdorues + Përdoruesi Graphics Device @@ -822,7 +822,7 @@ GUIMusicGroupBox - Luaj muzikën e titullit:\nNëse një lojë e mbështet, aktivizohet luajtja e muzikës të veçantë kur të zgjidhësh lojën në GUI. + Luaj muzikën e titullit:\nNëse një lojë e mbështet, aktivizohet luajtja e muzikës të veçantë kur të zgjidhësh lojën në ndërfaqe. disableTrophycheckBox From 2837d848eddf2c4b2b361626c220aa415f8b241c Mon Sep 17 00:00:00 2001 From: panzone91 <150828896+panzone91@users.noreply.github.com> Date: Mon, 27 Jan 2025 21:40:58 +0100 Subject: [PATCH 503/549] linker: handle relocation for exported modules (#2247) --- src/core/linker.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/core/linker.h b/src/core/linker.h index 00da3a08c..357b39664 100644 --- a/src/core/linker.h +++ b/src/core/linker.h @@ -109,10 +109,13 @@ public: void RelocateAnyImports(Module* m) { Relocate(m); - for (auto& module : m_modules) { - const auto imports = module->GetImportModules(); - if (std::ranges::contains(imports, m->name, &ModuleInfo::name)) { - Relocate(module.get()); + const auto exports = m->GetExportModules(); + for (auto& export_mod : exports) { + for (auto& module : m_modules) { + const auto imports = module->GetImportModules(); + if (std::ranges::contains(imports, export_mod.name, &ModuleInfo::name)) { + Relocate(module.get()); + } } } } From d2127b38deace732f0c41b6cc1881e21f4ed268b Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Tue, 28 Jan 2025 00:12:48 -0800 Subject: [PATCH 504/549] vk_rasterizer: Keep viewport depth offset even without native depth clip control. (#2257) --- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 7a0652ed7..7c77f2519 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -1155,10 +1155,7 @@ void Rasterizer::UpdateViewportScissorState(const GraphicsPipeline& pipeline) { const auto& vp_ctl = regs.viewport_control; const float reduce_z = - instance.IsDepthClipControlSupported() && - regs.clipper_control.clip_space == AmdGpu::Liverpool::ClipSpace::MinusWToW - ? 1.0f - : 0.0f; + regs.clipper_control.clip_space == AmdGpu::Liverpool::ClipSpace::MinusWToW ? 1.0f : 0.0f; if (regs.polygon_control.enable_window_offset) { LOG_ERROR(Render_Vulkan, From 8379922f8ab1209d25b4cbec14303401aa75eec9 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Tue, 28 Jan 2025 02:31:26 -0800 Subject: [PATCH 505/549] hotfix: Reduce requested videodec memory block sizes. This really needs a more accurate implementation, but for the stub lowering the value helps games that run out of memory space if it is too large. --- src/core/libraries/videodec/videodec.cpp | 2 +- src/core/libraries/videodec/videodec2.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/libraries/videodec/videodec.cpp b/src/core/libraries/videodec/videodec.cpp index 6c9a9b8c0..79001e76f 100644 --- a/src/core/libraries/videodec/videodec.cpp +++ b/src/core/libraries/videodec/videodec.cpp @@ -9,7 +9,7 @@ namespace Libraries::Videodec { -static constexpr u64 kMinimumMemorySize = 32_MB; ///> Fake minimum memory size for querying +static constexpr u64 kMinimumMemorySize = 8_MB; ///> Fake minimum memory size for querying int PS4_SYSV_ABI sceVideodecCreateDecoder(const OrbisVideodecConfigInfo* pCfgInfoIn, const OrbisVideodecResourceInfo* pRsrcInfoIn, diff --git a/src/core/libraries/videodec/videodec2.cpp b/src/core/libraries/videodec/videodec2.cpp index 07f7a274b..be0db4ea3 100644 --- a/src/core/libraries/videodec/videodec2.cpp +++ b/src/core/libraries/videodec/videodec2.cpp @@ -10,7 +10,7 @@ namespace Libraries::Vdec2 { -static constexpr u64 kMinimumMemorySize = 32_MB; ///> Fake minimum memory size for querying +static constexpr u64 kMinimumMemorySize = 8_MB; ///> Fake minimum memory size for querying s32 PS4_SYSV_ABI sceVideodec2QueryComputeMemoryInfo(OrbisVideodec2ComputeMemoryInfo* computeMemInfo) { From 4b93b8b57460c83421d0d859e243dd05a864bdc1 Mon Sep 17 00:00:00 2001 From: Martin <67326368+Martini-141@users.noreply.github.com> Date: Tue, 28 Jan 2025 17:47:00 +0100 Subject: [PATCH 506/549] add missing translations and other corrections nb (#2253) --- src/qt_gui/translations/nb.ts | 42 +++++++++++++++++------------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/qt_gui/translations/nb.ts b/src/qt_gui/translations/nb.ts index d6d4090df..0b7d2699a 100644 --- a/src/qt_gui/translations/nb.ts +++ b/src/qt_gui/translations/nb.ts @@ -98,7 +98,7 @@ Open Folder... - Åpne mappen... + Åpne mappe... Open Game Folder @@ -126,7 +126,7 @@ Copy All - Kopier alle + Kopier alt Delete... @@ -146,19 +146,19 @@ Compatibility... - Compatibility... + Kompatibilitet... Update database - Update database + Oppdater database View report - View report + Vis rapport Submit a report - Submit a report + Send inn en rapport Shortcut creation @@ -574,11 +574,11 @@ Trophy Key - Trophy Key + Trofénøkkel Trophy - Trophy + Trofé Logger @@ -658,7 +658,7 @@ Enable Shaders Dumping - Aktiver dumping av skyggelegger + Aktiver skyggeleggerdumping Enable NULL GPU @@ -666,7 +666,7 @@ Paths - Stier + Mapper Game Folders @@ -686,7 +686,7 @@ Enable Debug Dumping - Aktiver dumping av feilretting + Aktiver feilrettingsdumping Enable Vulkan Validation Layers @@ -718,7 +718,7 @@ GUI Settings - GUI-innstillinger + Grensesnitt-innstillinger Disable Trophy Pop-ups @@ -730,7 +730,7 @@ Update Compatibility Database On Startup - Oppdater kompatibilitets-database ved oppstart + Oppdater database ved oppstart Game Compatibility @@ -750,7 +750,7 @@ Audio Backend - Audio Backend + Lydsystem Save @@ -806,7 +806,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. + Trofénøkkel:\nNøkkel brukes til å dekryptere trofeer. Må hentes fra din konsoll (jailbroken).\nMå bare inneholde sekskantede tegn. logTypeGroupBox @@ -846,7 +846,7 @@ checkCompatibilityOnStartupCheckBox - Oppdater kompatibilitets-data ved oppstart:\nOppdaterer kompatibilitets-databasen automatisk når shadPS4 starter. + Oppdater database ved oppstart:\nOppdaterer kompatibilitets-databasen automatisk når shadPS4 starter. updateCompatibilityButton @@ -894,7 +894,7 @@ dumpShadersCheckBox - Aktiver dumping av skyggelegger:\nFor teknisk feilsøking lagrer skyggeleggerne fra spillet i en mappe mens de gjengis. + Aktiver skyggeleggerdumping:\nFor teknisk feilsøking lagrer skyggeleggerne fra spillet i en mappe mens de gjengis. nullGpuCheckBox @@ -914,7 +914,7 @@ debugDump - Aktiver dumping av feilsøking:\nLagrer import- og eksport-symbolene og filoverskriftsinformasjonen til det nåværende kjørende PS4-programmet i en katalog. + Aktiver feilrettingsdumping:\nLagrer import- og eksport-symbolene og filoverskriftsinformasjonen til det nåværende kjørende PS4-programmet i en katalog. vkValidationCheckBox @@ -1188,7 +1188,7 @@ Path - Sti + Adresse Play Time @@ -1232,7 +1232,7 @@ Game can be completed with playable performance and no major glitches - Spillet kan fullføres med spillbar ytelse og ingen store feil + Spillet kan fullføres med spillbar ytelse og uten store feil
@@ -1361,4 +1361,4 @@ TB - \ No newline at end of file + From 2cdd873681dd3b03d0752bda5c84412f06f4f0b8 Mon Sep 17 00:00:00 2001 From: slick-daddy <129640104+slick-daddy@users.noreply.github.com> Date: Tue, 28 Jan 2025 19:47:12 +0300 Subject: [PATCH 507/549] Update tr_TR.ts (#2255) --- src/qt_gui/translations/tr_TR.ts | 54 ++++++++++++++++---------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index 6436278de..a2368bfee 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -546,7 +546,7 @@ Enable Separate Update Folder - Enable Separate Update Folder + Ayrı Güncelleme Klasörünü Etkinleştir Default tab when opening settings @@ -554,7 +554,7 @@ Show Game Size In List - Göster oyun boyutunu listede + Oyun Boyutunu Listede Göster Show Splash @@ -574,11 +574,11 @@ Trophy Key - Trophy Key + Kupa Anahtarı Trophy - Trophy + Kupa Logger @@ -722,7 +722,7 @@ Disable Trophy Pop-ups - Disable Trophy Pop-ups + Kupa Açılır Pencerelerini Devre Dışı Bırak Play title music @@ -730,23 +730,23 @@ Update Compatibility Database On Startup - Update Compatibility Database On Startup + Başlangıçta Uyumluluk Veritabanını Güncelle Game Compatibility - Game Compatibility + Oyun Uyumluluğu Display Compatibility Data - Display Compatibility Data + Uyumluluk Verilerini Göster Update Compatibility Database - Update Compatibility Database + Uyumluluk Veritabanını Güncelle Volume - Ses seviyesi + Ses Seviyesi Audio Backend @@ -1105,11 +1105,11 @@ The game is in version: %1 - Oyun sürümde: %1 + Oyun sürümü: %1 The downloaded patch only works on version: %1 - İndirilen yamanın sadece sürümde çalışıyor: %1 + İndirilen yama sadece şu sürümde çalışıyor: %1 You may need to update your game. @@ -1168,7 +1168,7 @@ Compatibility - Compatibility + Uyumluluk Region @@ -1196,35 +1196,35 @@ Never Played - Never Played + Hiç Oynanmadı h - h + sa m - m + dk s - s + sn Compatibility is untested - Compatibility is untested + Uyumluluk test edilmemiş Game does not initialize properly / crashes the emulator - Game does not initialize properly / crashes the emulator + Oyun düzgün bir şekilde başlatılamıyor / emülatörü çökertiyor Game boots, but only displays a blank screen - Game boots, but only displays a blank screen + Oyun başlatılabiliyor ancak yalnızca boş bir ekran gösteriyor Game displays an image but does not go past the menu - Game displays an image but does not go past the menu + Oyun bir resim gösteriyor ancak menüleri geçemiyor Game has game-breaking glitches or unplayable performance @@ -1232,7 +1232,7 @@ Game can be completed with playable performance and no major glitches - Game can be completed with playable performance and no major glitches + Oyun, oynanabilir performansla tamamlanabilir ve büyük aksaklık yok @@ -1267,7 +1267,7 @@ Your version is already up to date! - Versiyonunuz zaten güncel! + Sürümünüz zaten güncel! Update Available @@ -1279,11 +1279,11 @@ Current Version - Mevcut Versiyon + Mevcut Sürüm Latest Version - Son Versiyon + Son Sürüm Do you want to update? @@ -1335,7 +1335,7 @@ Failed to create the update script file - Güncelleme betiği dosyası oluşturulamadı + Güncelleme komut dosyası oluşturulamadı @@ -1361,4 +1361,4 @@ TB - \ No newline at end of file + From a78f8afe58a995cecdb4eb1f1e6d6e58320444f6 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Tue, 28 Jan 2025 08:48:19 -0800 Subject: [PATCH 508/549] libraries: Implement libSceZlib. (#2256) * libraries: add zlib hle skeleton/stub * libraries: Implement libSceZlib. * zlib: Make variables static. --------- Co-authored-by: Nenkai --- CMakeLists.txt | 6 + src/common/logging/filter.cpp | 1 + src/common/logging/types.h | 1 + src/core/libraries/libs.cpp | 2 + src/core/libraries/zlib/zlib.cpp | 183 +++++++++++++++++++++++++++ src/core/libraries/zlib/zlib_error.h | 18 +++ src/core/libraries/zlib/zlib_sce.h | 21 +++ 7 files changed, 232 insertions(+) create mode 100644 src/core/libraries/zlib/zlib.cpp create mode 100644 src/core/libraries/zlib/zlib_error.h create mode 100644 src/core/libraries/zlib/zlib_sce.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c9dad307..78e3c7997 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -440,6 +440,11 @@ set(NP_LIBS src/core/libraries/np_common/np_common.cpp src/core/libraries/np_party/np_party.h ) +set(ZLIB_LIB src/core/libraries/zlib/zlib.cpp + src/core/libraries/zlib/zlib_sce.h + src/core/libraries/zlib/zlib_error.h +) + set(MISC_LIBS src/core/libraries/screenshot/screenshot.cpp src/core/libraries/screenshot/screenshot.h src/core/libraries/move/move.cpp @@ -612,6 +617,7 @@ set(CORE src/core/aerolib/stubs.cpp ${PLAYGO_LIB} ${RANDOM_LIB} ${USBD_LIB} + ${ZLIB_LIB} ${MISC_LIBS} ${IME_LIB} ${FIBER_LIB} diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index f1d3a9499..dd708c528 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -133,6 +133,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Lib, Mouse) \ SUB(Lib, WebBrowserDialog) \ SUB(Lib, NpParty) \ + SUB(Lib, Zlib) \ CLS(Frontend) \ CLS(Render) \ SUB(Render, Vulkan) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index d5530312c..54f8cdd0b 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -100,6 +100,7 @@ enum class Class : u8 { Lib_Mouse, ///< The LibSceMouse implementation Lib_WebBrowserDialog, ///< The LibSceWebBrowserDialog implementation Lib_NpParty, ///< The LibSceNpParty implementation + Lib_Zlib, ///< The LibSceZlib implementation. Frontend, ///< Emulator UI Render, ///< Video Core Render_Vulkan, ///< Vulkan backend diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index e09de1cee..8cf286d13 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -54,6 +54,7 @@ #include "core/libraries/videodec/videodec2.h" #include "core/libraries/videoout/video_out.h" #include "core/libraries/web_browser_dialog/webbrowserdialog.h" +#include "core/libraries/zlib/zlib_sce.h" #include "fiber/fiber.h" #include "jpeg/jpegenc.h" @@ -111,6 +112,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::Mouse::RegisterlibSceMouse(sym); Libraries::WebBrowserDialog::RegisterlibSceWebBrowserDialog(sym); Libraries::NpParty::RegisterlibSceNpParty(sym); + Libraries::Zlib::RegisterlibSceZlib(sym); } } // namespace Libraries diff --git a/src/core/libraries/zlib/zlib.cpp b/src/core/libraries/zlib/zlib.cpp new file mode 100644 index 000000000..899cb5bf6 --- /dev/null +++ b/src/core/libraries/zlib/zlib.cpp @@ -0,0 +1,183 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include + +#include "common/logging/log.h" +#include "common/thread.h" +#include "core/libraries/kernel/threads.h" +#include "core/libraries/libs.h" +#include "core/libraries/zlib/zlib_error.h" +#include "core/libraries/zlib/zlib_sce.h" + +namespace Libraries::Zlib { + +struct InflateTask { + u64 request_id; + const void* src; + u32 src_length; + void* dst; + u32 dst_length; +}; + +struct InflateResult { + u32 length; + s32 status; +}; + +static Kernel::Thread task_thread; + +static std::mutex mutex; +static std::queue task_queue; +static std::condition_variable_any task_queue_cv; +static std::queue done_queue; +static std::condition_variable_any done_queue_cv; +static std::unordered_map results; +static u64 next_request_id; + +void ZlibTaskThread(const std::stop_token& stop) { + Common::SetCurrentThreadName("shadPS4:ZlibTaskThread"); + + while (!stop.stop_requested()) { + InflateTask task; + { + // Lock and pop from the task queue, unless stop has been requested. + std::unique_lock lock(mutex); + if (!task_queue_cv.wait(lock, stop, [&] { return !task_queue.empty(); })) { + break; + } + task = task_queue.back(); + task_queue.pop(); + } + + uLongf decompressed_length = task.dst_length; + const auto ret = uncompress(static_cast(task.dst), &decompressed_length, + static_cast(task.src), task.src_length); + + { + // Lock, insert the new result, and push the finished request ID to the done queue. + std::unique_lock lock(mutex); + results[task.request_id] = InflateResult{ + .length = static_cast(decompressed_length), + .status = ret == Z_BUF_ERROR ? ORBIS_ZLIB_ERROR_NOSPACE + : ret == Z_OK ? ORBIS_OK + : ORBIS_ZLIB_ERROR_FATAL, + }; + done_queue.push(task.request_id); + } + done_queue_cv.notify_one(); + } +} + +s32 PS4_SYSV_ABI sceZlibInitialize(const void* buffer, u32 length) { + LOG_INFO(Lib_Zlib, "called"); + if (task_thread.Joinable()) { + return ORBIS_ZLIB_ERROR_ALREADY_INITIALIZED; + } + + // Initialize with empty task data + task_queue = std::queue(); + done_queue = std::queue(); + results.clear(); + next_request_id = 1; + + task_thread.Run([](const std::stop_token& stop) { ZlibTaskThread(stop); }); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceZlibInflate(const void* src, u32 src_len, void* dst, u32 dst_len, + u64* request_id) { + LOG_DEBUG(Lib_Zlib, "(STUBBED) called"); + if (!task_thread.Joinable()) { + return ORBIS_ZLIB_ERROR_NOT_INITIALIZED; + } + if (!src || !src_len || !dst || !dst_len || !request_id || dst_len > 64_KB || + dst_len % 2_KB != 0) { + return ORBIS_ZLIB_ERROR_INVALID; + } + + { + std::unique_lock lock(mutex); + *request_id = next_request_id++; + task_queue.emplace(InflateTask{ + .request_id = *request_id, + .src = src, + .src_length = src_len, + .dst = dst, + .dst_length = dst_len, + }); + task_queue_cv.notify_one(); + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceZlibWaitForDone(u64* request_id, const u32* timeout) { + LOG_DEBUG(Lib_Zlib, "(STUBBED) called"); + if (!task_thread.Joinable()) { + return ORBIS_ZLIB_ERROR_NOT_INITIALIZED; + } + if (!request_id) { + return ORBIS_ZLIB_ERROR_INVALID; + } + + { + // Pop from the done queue, unless the timeout is reached. + std::unique_lock lock(mutex); + const auto pred = [] { return !done_queue.empty(); }; + if (timeout) { + if (!done_queue_cv.wait_for(lock, std::chrono::milliseconds(*timeout), pred)) { + return ORBIS_ZLIB_ERROR_TIMEDOUT; + } + } else { + done_queue_cv.wait(lock, pred); + } + *request_id = done_queue.back(); + done_queue.pop(); + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceZlibGetResult(const u64 request_id, u32* dst_length, s32* status) { + LOG_DEBUG(Lib_Zlib, "(STUBBED) called"); + if (!task_thread.Joinable()) { + return ORBIS_ZLIB_ERROR_NOT_INITIALIZED; + } + if (!dst_length || !status) { + return ORBIS_ZLIB_ERROR_INVALID; + } + + { + std::unique_lock lock(mutex); + if (!results.contains(request_id)) { + return ORBIS_ZLIB_ERROR_NOT_FOUND; + } + const auto result = results[request_id]; + *dst_length = result.length; + *status = result.status; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceZlibFinalize() { + LOG_INFO(Lib_Zlib, "called"); + if (!task_thread.Joinable()) { + return ORBIS_ZLIB_ERROR_NOT_INITIALIZED; + } + task_thread.Stop(); + return ORBIS_OK; +} + +void RegisterlibSceZlib(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("m1YErdIXCp4", "libSceZlib", 1, "libSceZlib", 1, 1, sceZlibInitialize); + LIB_FUNCTION("6na+Sa-B83w", "libSceZlib", 1, "libSceZlib", 1, 1, sceZlibFinalize); + LIB_FUNCTION("TLar1HULv1Q", "libSceZlib", 1, "libSceZlib", 1, 1, sceZlibInflate); + LIB_FUNCTION("uB8VlDD4e0s", "libSceZlib", 1, "libSceZlib", 1, 1, sceZlibWaitForDone); + LIB_FUNCTION("2eDcGHC0YaM", "libSceZlib", 1, "libSceZlib", 1, 1, sceZlibGetResult); +}; + +} // namespace Libraries::Zlib diff --git a/src/core/libraries/zlib/zlib_error.h b/src/core/libraries/zlib/zlib_error.h new file mode 100644 index 000000000..59574f0b2 --- /dev/null +++ b/src/core/libraries/zlib/zlib_error.h @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +// Zlib library +constexpr int ORBIS_ZLIB_ERROR_NOT_FOUND = 0x81120002; +constexpr int ORBIS_ZLIB_ERROR_BUSY = 0x8112000B; +constexpr int ORBIS_ZLIB_ERROR_FAULT = 0x8112000E; +constexpr int ORBIS_ZLIB_ERROR_INVALID = 0x81120016; +constexpr int ORBIS_ZLIB_ERROR_NOSPACE = 0x8112001C; +constexpr int ORBIS_ZLIB_ERROR_NOT_SUPPORTED = 0x81120025; +constexpr int ORBIS_ZLIB_ERROR_TIMEDOUT = 0x81120027; +constexpr int ORBIS_ZLIB_ERROR_NOT_INITIALIZED = 0x81120032; +constexpr int ORBIS_ZLIB_ERROR_ALREADY_INITIALIZED = 0x81120033; +constexpr int ORBIS_ZLIB_ERROR_FATAL = 0x811200FF; diff --git a/src/core/libraries/zlib/zlib_sce.h b/src/core/libraries/zlib/zlib_sce.h new file mode 100644 index 000000000..6f8cf9468 --- /dev/null +++ b/src/core/libraries/zlib/zlib_sce.h @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} +namespace Libraries::Zlib { + +s32 PS4_SYSV_ABI sceZlibInitialize(const void* buffer, u32 length); +s32 PS4_SYSV_ABI sceZlibInflate(const void* src, u32 src_len, void* dst, u32 dst_len, + u64* request_id); +s32 PS4_SYSV_ABI sceZlibWaitForDone(u64* request_id, const u32* timeout); +s32 PS4_SYSV_ABI sceZlibGetResult(u64 request_id, u32* dst_length, s32* status); +s32 PS4_SYSV_ABI sceZlibFinalize(); + +void RegisterlibSceZlib(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::Zlib \ No newline at end of file From 0575853be18d8f0504482b4a5a407fb14a23d492 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Tue, 28 Jan 2025 17:56:05 -0800 Subject: [PATCH 509/549] externals: Update MoltenVK. (#2264) Adds support for VK_EXT_depth_clip_control --- externals/MoltenVK/MoltenVK | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externals/MoltenVK/MoltenVK b/externals/MoltenVK/MoltenVK index 6fa077fb8..aba997657 160000 --- a/externals/MoltenVK/MoltenVK +++ b/externals/MoltenVK/MoltenVK @@ -1 +1 @@ -Subproject commit 6fa077fb8ed8dac4e4cd66b6b1ebd7b4d955a754 +Subproject commit aba997657b94d6de1794ebad36ce5634341252c7 From 78a0a755c5f2ff06c340ec0dc3431fc247aaa3e4 Mon Sep 17 00:00:00 2001 From: ElBread3 <92335081+ElBread3@users.noreply.github.com> Date: Tue, 28 Jan 2025 21:14:47 -0600 Subject: [PATCH 510/549] qt_gui: Some game install features and fixes (#2261) * open update folder + delete save folder + bulk install checkbox * delete pkg on install checkbox + use game icon for finish window --- src/qt_gui/gui_context_menus.h | 45 ++++++++++++++++++++++++++++--- src/qt_gui/install_dir_select.cpp | 24 ++++++++++++++--- src/qt_gui/install_dir_select.h | 12 +++++++++ src/qt_gui/main_window.cpp | 26 +++++++++++++++--- src/qt_gui/main_window.h | 4 +++ 5 files changed, 101 insertions(+), 10 deletions(-) diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h index 72affeca7..bdc2aec0c 100644 --- a/src/qt_gui/gui_context_menus.h +++ b/src/qt_gui/gui_context_menus.h @@ -52,10 +52,12 @@ public: // "Open Folder..." submenu QMenu* openFolderMenu = new QMenu(tr("Open Folder..."), widget); QAction* openGameFolder = new QAction(tr("Open Game Folder"), widget); + QAction* openUpdateFolder = new QAction(tr("Open Update Folder"), widget); QAction* openSaveDataFolder = new QAction(tr("Open Save Data Folder"), widget); QAction* openLogFolder = new QAction(tr("Open Log Folder"), widget); openFolderMenu->addAction(openGameFolder); + openFolderMenu->addAction(openUpdateFolder); openFolderMenu->addAction(openSaveDataFolder); openFolderMenu->addAction(openLogFolder); @@ -87,10 +89,12 @@ public: QMenu* deleteMenu = new QMenu(tr("Delete..."), widget); QAction* deleteGame = new QAction(tr("Delete Game"), widget); QAction* deleteUpdate = new QAction(tr("Delete Update"), widget); + QAction* deleteSaveData = new QAction(tr("Delete Save Data"), widget); QAction* deleteDLC = new QAction(tr("Delete DLC"), widget); deleteMenu->addAction(deleteGame); deleteMenu->addAction(deleteUpdate); + deleteMenu->addAction(deleteSaveData); deleteMenu->addAction(deleteDLC); menu.addMenu(deleteMenu); @@ -122,6 +126,18 @@ public: QDesktopServices::openUrl(QUrl::fromLocalFile(folderPath)); } + if (selected == openUpdateFolder) { + QString open_update_path; + Common::FS::PathToQString(open_update_path, m_games[itemID].path); + open_update_path += "-UPDATE"; + if (!std::filesystem::exists(Common::FS::PathFromQString(open_update_path))) { + QMessageBox::critical(nullptr, tr("Error"), + QString(tr("This game has no update folder to open!"))); + } else { + QDesktopServices::openUrl(QUrl::fromLocalFile(open_update_path)); + } + } + if (selected == openSaveDataFolder) { QString userPath; Common::FS::PathToQString(userPath, @@ -143,7 +159,7 @@ public: PSF psf; std::filesystem::path game_folder_path = m_games[itemID].path; std::filesystem::path game_update_path = game_folder_path; - game_update_path += "UPDATE"; + game_update_path += "-UPDATE"; if (std::filesystem::exists(game_update_path)) { game_folder_path = game_update_path; } @@ -238,6 +254,11 @@ public: QString trophyPath, gameTrpPath; Common::FS::PathToQString(trophyPath, m_games[itemID].serial); Common::FS::PathToQString(gameTrpPath, m_games[itemID].path); + auto game_update_path = Common::FS::PathFromQString(gameTrpPath); + game_update_path += "-UPDATE"; + if (std::filesystem::exists(game_update_path)) { + Common::FS::PathToQString(gameTrpPath, game_update_path); + } TrophyViewer* trophyViewer = new TrophyViewer(trophyPath, gameTrpPath); trophyViewer->show(); connect(widget->parent(), &QWidget::destroyed, trophyViewer, @@ -335,14 +356,18 @@ public: clipboard->setText(combinedText); } - if (selected == deleteGame || selected == deleteUpdate || selected == deleteDLC) { + if (selected == deleteGame || selected == deleteUpdate || selected == deleteDLC || + selected == deleteSaveData) { bool error = false; - QString folder_path, game_update_path, dlc_path; + QString folder_path, game_update_path, dlc_path, save_data_path; Common::FS::PathToQString(folder_path, m_games[itemID].path); game_update_path = folder_path + "-UPDATE"; Common::FS::PathToQString( dlc_path, Config::getAddonInstallDir() / Common::FS::PathFromQString(folder_path).parent_path().filename()); + Common::FS::PathToQString(save_data_path, + Common::FS::GetUserPath(Common::FS::PathType::UserDir) / + "savedata/1" / m_games[itemID].serial); QString message_type = tr("Game"); if (selected == deleteUpdate) { @@ -363,6 +388,15 @@ public: folder_path = dlc_path; message_type = tr("DLC"); } + } else if (selected == deleteSaveData) { + if (!std::filesystem::exists(Common::FS::PathFromQString(save_data_path))) { + QMessageBox::critical(nullptr, tr("Error"), + QString(tr("This game has no save data to delete!"))); + error = true; + } else { + folder_path = save_data_path; + message_type = tr("Save Data"); + } } if (!error) { QString gameName = QString::fromStdString(m_games[itemID].name); @@ -374,7 +408,10 @@ public: QMessageBox::Yes | QMessageBox::No); if (reply == QMessageBox::Yes) { dir.removeRecursively(); - widget->removeRow(itemID); + if (selected == deleteGame) { + widget->removeRow(itemID); + m_games.removeAt(itemID); + } } } } diff --git a/src/qt_gui/install_dir_select.cpp b/src/qt_gui/install_dir_select.cpp index e0951b123..e90a10ee6 100644 --- a/src/qt_gui/install_dir_select.cpp +++ b/src/qt_gui/install_dir_select.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include #include #include @@ -15,10 +16,11 @@ #include "install_dir_select.h" InstallDirSelect::InstallDirSelect() : selected_dir() { - selected_dir = Config::getGameInstallDirs().empty() ? "" : Config::getGameInstallDirs().front(); + auto install_dirs = Config::getGameInstallDirs(); + selected_dir = install_dirs.empty() ? "" : install_dirs.front(); - if (!Config::getGameInstallDirs().empty() && Config::getGameInstallDirs().size() == 1) { - reject(); + if (!install_dirs.empty() && install_dirs.size() == 1) { + accept(); } auto layout = new QVBoxLayout(this); @@ -53,6 +55,14 @@ QWidget* InstallDirSelect::SetupInstallDirList() { vlayout->addWidget(m_path_list); + auto checkbox = new QCheckBox(tr("Install All Queued to Selected Folder")); + connect(checkbox, &QCheckBox::toggled, this, &InstallDirSelect::setUseForAllQueued); + vlayout->addWidget(checkbox); + + auto checkbox2 = new QCheckBox(tr("Delete PKG File on Install")); + connect(checkbox2, &QCheckBox::toggled, this, &InstallDirSelect::setDeleteFileOnInstall); + vlayout->addWidget(checkbox2); + group->setLayout(vlayout); return group; } @@ -66,6 +76,14 @@ void InstallDirSelect::setSelectedDirectory(QListWidgetItem* item) { } } +void InstallDirSelect::setUseForAllQueued(bool enabled) { + use_for_all_queued = enabled; +} + +void InstallDirSelect::setDeleteFileOnInstall(bool enabled) { + delete_file_on_install = enabled; +} + QWidget* InstallDirSelect::SetupDialogActions() { auto actions = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); diff --git a/src/qt_gui/install_dir_select.h b/src/qt_gui/install_dir_select.h index e3e81575a..e11cbf381 100644 --- a/src/qt_gui/install_dir_select.h +++ b/src/qt_gui/install_dir_select.h @@ -22,9 +22,21 @@ public: return selected_dir; } + bool useForAllQueued() { + return use_for_all_queued; + } + + bool deleteFileOnInstall() { + return delete_file_on_install; + } + private: QWidget* SetupInstallDirList(); QWidget* SetupDialogActions(); void setSelectedDirectory(QListWidgetItem* item); + void setDeleteFileOnInstall(bool enabled); + void setUseForAllQueued(bool enabled); std::filesystem::path selected_dir; + bool delete_file_on_install = false; + bool use_for_all_queued = false; }; diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 3ee392613..3678b3a82 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -725,9 +725,20 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int return; } auto category = psf.GetString("CATEGORY"); - InstallDirSelect ids; - ids.exec(); - auto game_install_dir = ids.getSelectedDirectory(); + + if (!use_for_all_queued || pkgNum == 1) { + InstallDirSelect ids; + const auto selected = ids.exec(); + if (selected == QDialog::Rejected) { + return; + } + + last_install_dir = ids.getSelectedDirectory(); + delete_file_on_install = ids.deleteFileOnInstall(); + use_for_all_queued = ids.useForAllQueued(); + } + std::filesystem::path game_install_dir = last_install_dir; + auto game_folder_path = game_install_dir / pkg.GetTitleID(); QString pkgType = QString::fromStdString(pkg.GetPkgFlags()); bool use_game_update = pkgType.contains("PATCH") && Config::getSeparateUpdateEnabled(); @@ -879,8 +890,14 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int if (pkgNum == nPkg) { QString path; Common::FS::PathToQString(path, game_install_dir); + QIcon windowIcon( + Common::FS::PathToUTF8String(game_folder_path / "sce_sys/icon0.png") + .c_str()); QMessageBox extractMsgBox(this); extractMsgBox.setWindowTitle(tr("Extraction Finished")); + if (!windowIcon.isNull()) { + extractMsgBox.setWindowIcon(windowIcon); + } extractMsgBox.setText( QString(tr("Game successfully installed at %1")).arg(path)); extractMsgBox.addButton(QMessageBox::Ok); @@ -894,6 +911,9 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int }); extractMsgBox.exec(); } + if (delete_file_on_install) { + std::filesystem::remove(file); + } }); connect(&dialog, &QProgressDialog::canceled, [&]() { futureWatcher.cancel(); }); connect(&futureWatcher, &QFutureWatcher::progressValueChanged, &dialog, diff --git a/src/qt_gui/main_window.h b/src/qt_gui/main_window.h index f4163defa..5ac56e44c 100644 --- a/src/qt_gui/main_window.h +++ b/src/qt_gui/main_window.h @@ -123,4 +123,8 @@ protected: } void resizeEvent(QResizeEvent* event) override; + + std::filesystem::path last_install_dir = ""; + bool delete_file_on_install = false; + bool use_for_all_queued = false; }; From 9bad66b24d4082240d00849a3d342d4d4a7c52d3 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Wed, 29 Jan 2025 01:29:52 -0800 Subject: [PATCH 511/549] hotfix: Raise videodec memory back up to 16MB. Found a game that needs more, still should be low enough compared to before to fix some games. --- src/core/libraries/videodec/videodec.cpp | 2 +- src/core/libraries/videodec/videodec2.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/libraries/videodec/videodec.cpp b/src/core/libraries/videodec/videodec.cpp index 79001e76f..02ea61509 100644 --- a/src/core/libraries/videodec/videodec.cpp +++ b/src/core/libraries/videodec/videodec.cpp @@ -9,7 +9,7 @@ namespace Libraries::Videodec { -static constexpr u64 kMinimumMemorySize = 8_MB; ///> Fake minimum memory size for querying +static constexpr u64 kMinimumMemorySize = 16_MB; ///> Fake minimum memory size for querying int PS4_SYSV_ABI sceVideodecCreateDecoder(const OrbisVideodecConfigInfo* pCfgInfoIn, const OrbisVideodecResourceInfo* pRsrcInfoIn, diff --git a/src/core/libraries/videodec/videodec2.cpp b/src/core/libraries/videodec/videodec2.cpp index be0db4ea3..a7e520b41 100644 --- a/src/core/libraries/videodec/videodec2.cpp +++ b/src/core/libraries/videodec/videodec2.cpp @@ -10,7 +10,7 @@ namespace Libraries::Vdec2 { -static constexpr u64 kMinimumMemorySize = 8_MB; ///> Fake minimum memory size for querying +static constexpr u64 kMinimumMemorySize = 16_MB; ///> Fake minimum memory size for querying s32 PS4_SYSV_ABI sceVideodec2QueryComputeMemoryInfo(OrbisVideodec2ComputeMemoryInfo* computeMemInfo) { From aa847de043e2e04512d5d01b4d9e40adc4706b0a Mon Sep 17 00:00:00 2001 From: isshininu Date: Wed, 29 Jan 2025 13:54:08 +0400 Subject: [PATCH 512/549] Update ru_RU translation (#2267) Several changes in ru_RU translation file. --- src/qt_gui/translations/ru_RU.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index 6423f5ba4..0fddb1e7f 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -126,11 +126,11 @@ Copy All - Копировать все + Копировать всё Delete... - Удаление... + Удалить... Delete Game @@ -158,7 +158,7 @@ Submit a report - Отправить отчет + Отправить отчёт Shortcut creation @@ -297,7 +297,7 @@ Elf Viewer - Elf + Исполняемый файл Game Install Directory @@ -542,7 +542,7 @@ Fullscreen Mode - Режим Полного Экран + Тип полноэкранного режима Enable Separate Update Folder @@ -574,11 +574,11 @@ Trophy Key - Trophy Key + Ключ трофеев Trophy - Trophy + Трофеи Logger @@ -750,7 +750,7 @@ Audio Backend - Звуковая Подсистема + Звуковая подсистема Save @@ -826,7 +826,7 @@ disableTrophycheckBox - Отключить уведомления о трофеях:\nОтключает внутриигровые уведомления о трофеях. Прогресс трофеев по прежнему можно отслеживать в меню Просмотр трофеев (правая кнопка мыши по игре в главном окне). + Отключить уведомления о трофеях:\nОтключает внутриигровые уведомления о трофеях. Прогресс трофеев по прежнему можно отслеживать в меню просмотра трофеев (правая кнопка мыши по игре в главном окне). hideCursorGroupBox @@ -1192,11 +1192,11 @@ Play Time - Времени в игре + Время в игре Never Played - Вы не играли + Нет h @@ -1361,4 +1361,4 @@ ТБ - \ No newline at end of file + From 4bb578f9fb73ce77680e26621717db460a93b0e8 Mon Sep 17 00:00:00 2001 From: Missake212 Date: Wed, 29 Jan 2025 11:45:44 +0100 Subject: [PATCH 513/549] updates french translation (#2262) * updates the french translation * fix space at the end * forgot to translate a line * final changes * final changes 2 --- src/qt_gui/translations/fr.ts | 66 +++++++++++++++++------------------ 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/src/qt_gui/translations/fr.ts b/src/qt_gui/translations/fr.ts index 601ece8b4..0f87b087b 100644 --- a/src/qt_gui/translations/fr.ts +++ b/src/qt_gui/translations/fr.ts @@ -146,19 +146,19 @@ Compatibility... - Compatibility... + Compatibilité... Update database - Update database + Mettre à jour la base de données View report - View report + Voir rapport Submit a report - Submit a report + Soumettre un rapport Shortcut creation @@ -186,7 +186,7 @@ requiresEnableSeparateUpdateFolder_MSG - This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + Cette fonctionnalité nécessite l'option 'Dossier séparé pour les mises à jour' pour fonctionner. Si vous voulez utiliser cette fonctionnalité, veuillez l'activer. This game has no update to delete! @@ -546,7 +546,7 @@ Enable Separate Update Folder - Dossier séparé pour les mises à jours + Dossier séparé pour les mises à jour Default tab when opening settings @@ -574,11 +574,11 @@ Trophy Key - Trophy Key + Clé de trophée Trophy - Trophy + Trophée Logger @@ -586,11 +586,11 @@ Log Type - Type + Type de journal Log Filter - Filtre + Filtre du journal Open Log Location @@ -722,7 +722,7 @@ Disable Trophy Pop-ups - Disable Trophy Pop-ups + Désactiver les notifications de trophées Play title music @@ -730,19 +730,19 @@ Update Compatibility Database On Startup - Update Compatibility Database On Startup + Mettre à jour la base de données de compatibilité au lancement Game Compatibility - Game Compatibility + Compatibilité du jeu Display Compatibility Data - Display Compatibility Data + Afficher les données de compatibilité Update Compatibility Database - Update Compatibility Database + Mettre à jour la base de données de compatibilité Volume @@ -750,7 +750,7 @@ Audio Backend - Audio Backend + Back-end audio Save @@ -786,7 +786,7 @@ separateUpdatesCheckBox - Dossier séparé pour les mises à jours:\nInstalle les mises à jours des jeux dans un dossier séparé pour une gestion plus facile. + Dossier séparé pour les mises à jour:\nInstalle les mises à jours des jeux dans un dossier séparé pour une gestion plus facile. showSplashCheckBox @@ -806,7 +806,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. + Clé de trophées:\nClé utilisée pour décrypter les trophées. Doit être obtenu à partir de votre console jailbreakée.\nDoit contenir des caractères hexadécimaux uniquement. logTypeGroupBox @@ -826,7 +826,7 @@ disableTrophycheckBox - Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + Désactiver les notifications de trophées:\nDésactive les notifications de trophées en jeu. La progression des trophées peut toujours être suivie à l'aide de la Visionneuse de trophées (clique droit sur le jeu sur la fenêtre principale). hideCursorGroupBox @@ -842,15 +842,15 @@ enableCompatibilityCheckBox - Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + Afficher les données de compatibilité:\nAffiche les informations de compatibilité des jeux dans une colonne dédiée. Activez "Mettre à jour la compatibilité au démarrage" pour avoir des informations à jour. checkCompatibilityOnStartupCheckBox - Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + Mettre à jour la compatibilité au démarrage:\nMettre à jour automatiquement la base de données de compatibilité au démarrage de shadPS4. updateCompatibilityButton - Update Compatibility Database:\nImmediately update the compatibility database. + Mettre à jour la compatibilité au démarrage:\nMet à jour immédiatement la base de données de compatibilité. Never @@ -1093,7 +1093,7 @@ DownloadComplete_MSG - Patchs téléchargés avec succès ! Tous les patches disponibles pour tous les jeux ont été téléchargés, il n'est pas nécessaire de les télécharger individuellement pour chaque jeu comme c'est le cas pour les Cheats. Si le correctif n'apparaît pas, il se peut qu'il n'existe pas pour le numéro de série et la version spécifiques du jeu. + Patchs téléchargés avec succès ! Tous les patches disponibles pour tous les jeux ont été téléchargés, il n'est pas nécessaire de les télécharger individuellement pour chaque jeu comme c'est le cas pour les Cheats. Si le correctif n'apparaît pas, il se peut qu'il n'existe pas pour la série et la version spécifiques du jeu. Failed to parse JSON data from HTML. @@ -1113,7 +1113,7 @@ You may need to update your game. - Vous devrez peut-être mettre à jour votre jeu. + Vous devriez peut-être mettre à jour votre jeu. Incompatibility Notice @@ -1137,7 +1137,7 @@ Directory does not exist: - Répertoire n'existe pas: + Le répertoire n'existe pas: Failed to open files.json for reading. @@ -1149,7 +1149,7 @@ Can't apply cheats before the game is started - Impossible d'appliquer les Cheats avant que le jeu ne commence. + Impossible d'appliquer les cheats avant que le jeu ne soit lancé @@ -1168,7 +1168,7 @@ Compatibility - Compatibility + Compatibilité Region @@ -1212,27 +1212,27 @@ Compatibility is untested - Compatibility is untested + La compatibilité n'a pas été testé Game does not initialize properly / crashes the emulator - Game does not initialize properly / crashes the emulator + Le jeu ne se lance pas correctement / crash l'émulateur Game boots, but only displays a blank screen - Game boots, but only displays a blank screen + Le jeu démarre, mais n'affiche qu'un écran noir Game displays an image but does not go past the menu - Game displays an image but does not go past the menu + Le jeu affiche une image mais ne dépasse pas le menu Game has game-breaking glitches or unplayable performance - Game has game-breaking glitches or unplayable performance + Le jeu a des problèmes majeurs ou des performances qui le rendent injouable Game can be completed with playable performance and no major glitches - Game can be completed with playable performance and no major glitches + Le jeu peut être terminé avec des performances acceptables et sans problèmes majeurs From 929e15260d8d7a618c10a5f396b1f9e872970425 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Wed, 29 Jan 2025 18:14:36 -0800 Subject: [PATCH 514/549] shader_recompiler: Fix cube sampling coordinates. (#2266) --- .../ir/passes/resource_tracking_pass.cpp | 25 ++++++++++++++----- src/shader_recompiler/specialization.h | 2 ++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index 43d125bf0..c5f98e5b9 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -583,6 +583,18 @@ void PatchTextureBufferArgs(IR::Block& block, IR::Inst& inst, Info& info) { } } +IR::Value FixCubeCoords(IR::IREmitter& ir, const AmdGpu::Image& image, const IR::Value& x, + const IR::Value& y, const IR::Value& face) { + if (!image.IsCube()) { + return ir.CompositeConstruct(x, y, face); + } + // AMD cube math results in coordinates in the range [1.0, 2.0]. We need + // to convert this to the range [0.0, 1.0] to get correct results. + const auto fixed_x = ir.FPSub(IR::F32{x}, ir.Imm32(1.f)); + const auto fixed_y = ir.FPSub(IR::F32{y}, ir.Imm32(1.f)); + return ir.CompositeConstruct(fixed_x, fixed_y, face); +} + void PatchImageSampleArgs(IR::Block& block, IR::Inst& inst, Info& info, const ImageResource& image_res, const AmdGpu::Image& image) { const auto handle = inst.Arg(0); @@ -643,8 +655,8 @@ void PatchImageSampleArgs(IR::Block& block, IR::Inst& inst, Info& info, case AmdGpu::ImageType::Color1DArray: return read(0); case AmdGpu::ImageType::Color2D: - case AmdGpu::ImageType::Color2DArray: case AmdGpu::ImageType::Color2DMsaa: + case AmdGpu::ImageType::Color2DArray: return ir.CompositeConstruct(read(0), read(8)); case AmdGpu::ImageType::Color3D: return ir.CompositeConstruct(read(0), read(8), read(16)); @@ -665,8 +677,8 @@ void PatchImageSampleArgs(IR::Block& block, IR::Inst& inst, Info& info, addr_reg = addr_reg + 2; return {get_addr_reg(addr_reg - 2), get_addr_reg(addr_reg - 1)}; case AmdGpu::ImageType::Color2D: - case AmdGpu::ImageType::Color2DArray: case AmdGpu::ImageType::Color2DMsaa: + case AmdGpu::ImageType::Color2DArray: // (du/dx, dv/dx), (du/dy, dv/dy) addr_reg = addr_reg + 4; return {ir.CompositeConstruct(get_addr_reg(addr_reg - 4), get_addr_reg(addr_reg - 3)), @@ -711,12 +723,13 @@ void PatchImageSampleArgs(IR::Block& block, IR::Inst& inst, Info& info, case AmdGpu::ImageType::Color2D: // x, y addr_reg = addr_reg + 2; return ir.CompositeConstruct(get_coord(addr_reg - 2, 0), get_coord(addr_reg - 1, 1)); - case AmdGpu::ImageType::Color2DArray: // x, y, slice - [[fallthrough]]; case AmdGpu::ImageType::Color2DMsaa: // x, y, frag + [[fallthrough]]; + case AmdGpu::ImageType::Color2DArray: // x, y, slice addr_reg = addr_reg + 3; - return ir.CompositeConstruct(get_coord(addr_reg - 3, 0), get_coord(addr_reg - 2, 1), - get_addr_reg(addr_reg - 1)); + // Note we can use FixCubeCoords with fallthrough cases since it checks for image type. + return FixCubeCoords(ir, image, get_coord(addr_reg - 3, 0), get_coord(addr_reg - 2, 1), + get_addr_reg(addr_reg - 1)); case AmdGpu::ImageType::Color3D: // x, y, z addr_reg = addr_reg + 3; return ir.CompositeConstruct(get_coord(addr_reg - 3, 0), get_coord(addr_reg - 2, 1), diff --git a/src/shader_recompiler/specialization.h b/src/shader_recompiler/specialization.h index 523e63497..eb0085965 100644 --- a/src/shader_recompiler/specialization.h +++ b/src/shader_recompiler/specialization.h @@ -47,6 +47,7 @@ struct ImageSpecialization { AmdGpu::ImageType type = AmdGpu::ImageType::Color2D; bool is_integer = false; bool is_storage = false; + bool is_cube = false; AmdGpu::CompMapping dst_select{}; AmdGpu::NumberConversion num_conversion{}; @@ -127,6 +128,7 @@ struct StageSpecialization { spec.type = sharp.GetViewType(desc.is_array); spec.is_integer = AmdGpu::IsInteger(sharp.GetNumberFmt()); spec.is_storage = desc.is_written; + spec.is_cube = sharp.IsCube(); if (spec.is_storage) { spec.dst_select = sharp.DstSelect(); } From 0358271b93160a97079376c408b5c270e807826c Mon Sep 17 00:00:00 2001 From: Vinicius Rangel Date: Thu, 30 Jan 2025 04:45:48 -0300 Subject: [PATCH 515/549] Savefixes vii (#2279) * savedata: rewrite save memory functions to handle slot id & better backup management * savedata: auto-restore backup if needed * safe save backup shutdown replaced exit by quick_exit --- src/common/elf_info.h | 1 + src/core/libraries/save_data/save_backup.cpp | 21 +- src/core/libraries/save_data/save_backup.h | 2 + .../libraries/save_data/save_instance.cpp | 53 +-- src/core/libraries/save_data/save_instance.h | 5 +- src/core/libraries/save_data/save_memory.cpp | 365 ++++++++---------- src/core/libraries/save_data/save_memory.h | 36 +- src/core/libraries/save_data/savedata.cpp | 108 ++++-- src/emulator.cpp | 3 +- 9 files changed, 285 insertions(+), 309 deletions(-) diff --git a/src/common/elf_info.h b/src/common/elf_info.h index cb32679bb..d885709cd 100644 --- a/src/common/elf_info.h +++ b/src/common/elf_info.h @@ -80,6 +80,7 @@ public: static constexpr u32 FW_40 = 0x4000000; static constexpr u32 FW_45 = 0x4500000; static constexpr u32 FW_50 = 0x5000000; + static constexpr u32 FW_55 = 0x5500000; static constexpr u32 FW_80 = 0x8000000; static ElfInfo& Instance() { diff --git a/src/core/libraries/save_data/save_backup.cpp b/src/core/libraries/save_data/save_backup.cpp index 5261cdb11..f85845f70 100644 --- a/src/core/libraries/save_data/save_backup.cpp +++ b/src/core/libraries/save_data/save_backup.cpp @@ -121,15 +121,17 @@ static void BackupThreadBody() { std::scoped_lock lk{g_backup_queue_mutex}; g_backup_queue.front().done = true; } - std::this_thread::sleep_for(std::chrono::seconds(5)); // Don't backup too often { std::scoped_lock lk{g_backup_queue_mutex}; g_backup_queue.pop_front(); - g_result_queue.push_back(std::move(req)); - if (g_result_queue.size() > 20) { - g_result_queue.pop_front(); + if (req.origin != OrbisSaveDataEventType::__DO_NOT_SAVE) { + g_result_queue.push_back(std::move(req)); + if (g_result_queue.size() > 20) { + g_result_queue.pop_front(); + } } } + std::this_thread::sleep_for(std::chrono::seconds(5)); // Don't backup too often } g_backup_status = WorkerStatus::NotStarted; } @@ -141,6 +143,15 @@ void StartThread() { LOG_DEBUG(Lib_SaveData, "Starting backup thread"); g_backup_status = WorkerStatus::Waiting; g_backup_thread = std::jthread{BackupThreadBody}; + static std::once_flag flag; + std::call_once(flag, [] { + std::at_quick_exit([] { + StopThread(); + while (GetWorkerStatus() != WorkerStatus::NotStarted) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + }); + }); } void StopThread() { @@ -148,12 +159,12 @@ void StopThread() { return; } LOG_DEBUG(Lib_SaveData, "Stopping backup thread"); + g_backup_status = WorkerStatus::Stopping; { std::scoped_lock lk{g_backup_queue_mutex}; g_backup_queue.emplace_back(BackupRequest{}); } g_backup_thread_semaphore.release(); - g_backup_status = WorkerStatus::Stopping; } bool NewRequest(OrbisUserServiceUserId user_id, std::string_view title_id, diff --git a/src/core/libraries/save_data/save_backup.h b/src/core/libraries/save_data/save_backup.h index e49c69f60..83a263c9b 100644 --- a/src/core/libraries/save_data/save_backup.h +++ b/src/core/libraries/save_data/save_backup.h @@ -25,6 +25,8 @@ enum class OrbisSaveDataEventType : u32 { UMOUNT_BACKUP = 1, BACKUP = 2, SAVE_DATA_MEMORY_SYNC = 3, + + __DO_NOT_SAVE = 1000000, // This value is only for the backup thread }; struct BackupRequest { diff --git a/src/core/libraries/save_data/save_instance.cpp b/src/core/libraries/save_data/save_instance.cpp index c2b7dca3c..26708d2d6 100644 --- a/src/core/libraries/save_data/save_instance.cpp +++ b/src/core/libraries/save_data/save_instance.cpp @@ -10,6 +10,7 @@ #include "common/path_util.h" #include "common/singleton.h" #include "core/file_sys/fs.h" +#include "save_backup.h" #include "save_instance.h" constexpr auto OrbisSaveDataBlocksMin2 = 96; // 3MiB @@ -45,14 +46,13 @@ static const std::unordered_map default_title = { namespace Libraries::SaveData { -std::filesystem::path SaveInstance::MakeTitleSavePath(OrbisUserServiceUserId user_id, - std::string_view game_serial) { +fs::path SaveInstance::MakeTitleSavePath(OrbisUserServiceUserId user_id, + std::string_view game_serial) { return Config::GetSaveDataPath() / std::to_string(user_id) / game_serial; } -std::filesystem::path SaveInstance::MakeDirSavePath(OrbisUserServiceUserId user_id, - std::string_view game_serial, - std::string_view dir_name) { +fs::path SaveInstance::MakeDirSavePath(OrbisUserServiceUserId user_id, std::string_view game_serial, + std::string_view dir_name) { return Config::GetSaveDataPath() / std::to_string(user_id) / game_serial / dir_name; } @@ -65,7 +65,7 @@ uint64_t SaveInstance::GetMaxBlockFromSFO(const PSF& psf) { return *(uint64_t*)value.data(); } -std::filesystem::path SaveInstance::GetParamSFOPath(const std::filesystem::path& dir_path) { +fs::path SaveInstance::GetParamSFOPath(const fs::path& dir_path) { return dir_path / sce_sys / "param.sfo"; } @@ -129,7 +129,6 @@ SaveInstance& SaveInstance::operator=(SaveInstance&& other) noexcept { save_path = std::move(other.save_path); param_sfo_path = std::move(other.param_sfo_path); corrupt_file_path = std::move(other.corrupt_file_path); - corrupt_file = std::move(other.corrupt_file); param_sfo = std::move(other.param_sfo); mount_point = std::move(other.mount_point); max_blocks = other.max_blocks; @@ -142,7 +141,8 @@ SaveInstance& SaveInstance::operator=(SaveInstance&& other) noexcept { return *this; } -void SaveInstance::SetupAndMount(bool read_only, bool copy_icon, bool ignore_corrupt) { +void SaveInstance::SetupAndMount(bool read_only, bool copy_icon, bool ignore_corrupt, + bool dont_restore_backup) { if (mounted) { UNREACHABLE_MSG("Save instance is already mounted"); } @@ -161,25 +161,27 @@ void SaveInstance::SetupAndMount(bool read_only, bool copy_icon, bool ignore_cor } exists = true; } else { + std::optional err; if (!ignore_corrupt && fs::exists(corrupt_file_path)) { - throw std::filesystem::filesystem_error( - "Corrupted save data", corrupt_file_path, - std::make_error_code(std::errc::illegal_byte_sequence)); + err = fs::filesystem_error("Corrupted save data", corrupt_file_path, + std::make_error_code(std::errc::illegal_byte_sequence)); + } else if (!param_sfo.Open(param_sfo_path)) { + err = fs::filesystem_error("Failed to read param.sfo", param_sfo_path, + std::make_error_code(std::errc::illegal_byte_sequence)); } - if (!param_sfo.Open(param_sfo_path)) { - throw std::filesystem::filesystem_error( - "Failed to read param.sfo", param_sfo_path, - std::make_error_code(std::errc::illegal_byte_sequence)); + if (err.has_value()) { + if (dont_restore_backup) { + throw err.value(); + } + if (Backup::Restore(save_path)) { + return SetupAndMount(read_only, copy_icon, ignore_corrupt, true); + } } } if (!ignore_corrupt && !read_only) { - int err = corrupt_file.Open(corrupt_file_path, Common::FS::FileAccessMode::Write); - if (err != 0) { - throw std::filesystem::filesystem_error( - "Failed to open corrupted file", corrupt_file_path, - std::make_error_code(std::errc::illegal_byte_sequence)); - } + Common::FS::IOFile f(corrupt_file_path, Common::FS::FileAccessMode::Write); + f.Close(); } max_blocks = static_cast(GetMaxBlockFromSFO(param_sfo)); @@ -197,12 +199,11 @@ void SaveInstance::Umount() { mounted = false; const bool ok = param_sfo.Encode(param_sfo_path); if (!ok) { - throw std::filesystem::filesystem_error("Failed to write param.sfo", param_sfo_path, - std::make_error_code(std::errc::permission_denied)); + throw fs::filesystem_error("Failed to write param.sfo", param_sfo_path, + std::make_error_code(std::errc::permission_denied)); } param_sfo = PSF(); - corrupt_file.Close(); fs::remove(corrupt_file_path); g_mnt->Unmount(save_path, mount_point); } @@ -216,8 +217,8 @@ void SaveInstance::CreateFiles() { const bool ok = param_sfo.Encode(param_sfo_path); if (!ok) { - throw std::filesystem::filesystem_error("Failed to write param.sfo", param_sfo_path, - std::make_error_code(std::errc::permission_denied)); + throw fs::filesystem_error("Failed to write param.sfo", param_sfo_path, + std::make_error_code(std::errc::permission_denied)); } } diff --git a/src/core/libraries/save_data/save_instance.h b/src/core/libraries/save_data/save_instance.h index 3be5c4595..6e7ac8f66 100644 --- a/src/core/libraries/save_data/save_instance.h +++ b/src/core/libraries/save_data/save_instance.h @@ -42,8 +42,6 @@ class SaveInstance { std::filesystem::path param_sfo_path; std::filesystem::path corrupt_file_path; - Common::FS::IOFile corrupt_file; - PSF param_sfo; std::string mount_point; @@ -80,7 +78,8 @@ public: SaveInstance& operator=(const SaveInstance& other) = delete; SaveInstance& operator=(SaveInstance&& other) noexcept; - void SetupAndMount(bool read_only = false, bool copy_icon = false, bool ignore_corrupt = false); + void SetupAndMount(bool read_only = false, bool copy_icon = false, bool ignore_corrupt = false, + bool dont_restore_backup = false); void Umount(); diff --git a/src/core/libraries/save_data/save_memory.cpp b/src/core/libraries/save_data/save_memory.cpp index 84179bc27..13e122c60 100644 --- a/src/core/libraries/save_data/save_memory.cpp +++ b/src/core/libraries/save_data/save_memory.cpp @@ -6,14 +6,16 @@ #include #include #include +#include #include #include #include #include "common/assert.h" +#include "common/elf_info.h" #include "common/logging/log.h" -#include "common/polyfill_thread.h" +#include "common/path_util.h" #include "common/singleton.h" #include "common/thread.h" #include "core/file_sys/fs.h" @@ -23,265 +25,202 @@ using Common::FS::IOFile; namespace fs = std::filesystem; constexpr std::string_view sce_sys = "sce_sys"; // system folder inside save -constexpr std::string_view DirnameSaveDataMemory = "sce_sdmemory"; +constexpr std::string_view StandardDirnameSaveDataMemory = "sce_sdmemory"; constexpr std::string_view FilenameSaveDataMemory = "memory.dat"; +constexpr std::string_view IconName = "icon0.png"; +constexpr std::string_view CorruptFileName = "corrupted"; namespace Libraries::SaveData::SaveMemory { static Core::FileSys::MntPoints* g_mnt = Common::Singleton::Instance(); -static OrbisUserServiceUserId g_user_id{}; -static std::string g_game_serial{}; -static std::filesystem::path g_save_path{}; -static std::filesystem::path g_param_sfo_path{}; -static PSF g_param_sfo; +struct SlotData { + OrbisUserServiceUserId user_id; + std::string game_serial; + std::filesystem::path folder_path; + PSF sfo; + std::vector memory_cache; +}; -static bool g_save_memory_initialized = false; -static std::mutex g_saving_memory_mutex; -static std::vector g_save_memory; +static std::mutex g_slot_mtx; +static std::unordered_map g_attached_slots; -static std::filesystem::path g_icon_path; -static std::vector g_icon_memory; +void PersistMemory(u32 slot_id, bool lock) { + std::unique_lock lck{g_slot_mtx, std::defer_lock}; + if (lock) { + lck.lock(); + } + auto& data = g_attached_slots[slot_id]; + auto memoryPath = data.folder_path / FilenameSaveDataMemory; + fs::create_directories(memoryPath.parent_path()); -static std::condition_variable g_trigger_save_memory; -static std::atomic_bool g_saving_memory = false; -static std::atomic_bool g_save_event = false; -static std::jthread g_save_memory_thread; - -static std::atomic_bool g_memory_dirty = false; -static std::atomic_bool g_param_dirty = false; -static std::atomic_bool g_icon_dirty = false; - -static void SaveFileSafe(void* buf, size_t count, const std::filesystem::path& path) { - const auto& dir = path.parent_path(); - const auto& name = path.filename(); - const auto tmp_path = dir / (name.string() + ".tmp"); - - IOFile file(tmp_path, Common::FS::FileAccessMode::Write); - file.WriteRaw(buf, count); - file.Close(); - - fs::remove(path); - fs::rename(tmp_path, path); -} - -[[noreturn]] void SaveThreadLoop() { - Common::SetCurrentThreadName("shadPS4:SaveData:SaveDataMemoryThread"); - std::mutex mtx; - while (true) { - { - std::unique_lock lk{mtx}; - g_trigger_save_memory.wait(lk); - } - // Save the memory - g_saving_memory = true; - std::scoped_lock lk{g_saving_memory_mutex}; + int n = 0; + std::string errMsg; + while (n++ < 10) { try { - LOG_DEBUG(Lib_SaveData, "Saving save data memory {}", fmt::UTF(g_save_path.u8string())); - - if (g_memory_dirty) { - g_memory_dirty = false; - SaveFileSafe(g_save_memory.data(), g_save_memory.size(), - g_save_path / FilenameSaveDataMemory); + IOFile f; + int r = f.Open(memoryPath, Common::FS::FileAccessMode::Write); + if (f.IsOpen()) { + f.WriteRaw(data.memory_cache.data(), data.memory_cache.size()); + f.Close(); + return; } - if (g_param_dirty) { - g_param_dirty = false; - static std::vector buf; - g_param_sfo.Encode(buf); - SaveFileSafe(buf.data(), buf.size(), g_param_sfo_path); - } - if (g_icon_dirty) { - g_icon_dirty = false; - SaveFileSafe(g_icon_memory.data(), g_icon_memory.size(), g_icon_path); - } - - if (g_save_event) { - Backup::PushBackupEvent(Backup::BackupRequest{ - .user_id = g_user_id, - .title_id = g_game_serial, - .dir_name = std::string{DirnameSaveDataMemory}, - .origin = Backup::OrbisSaveDataEventType::SAVE_DATA_MEMORY_SYNC, - .save_path = g_save_path, - }); - g_save_event = false; - } - } catch (const fs::filesystem_error& e) { - LOG_ERROR(Lib_SaveData, "Failed to save save data memory: {}", e.what()); - MsgDialog::ShowMsgDialog(MsgDialog::MsgDialogState{ - MsgDialog::MsgDialogState::UserState{ - .type = MsgDialog::ButtonType::OK, - .msg = fmt::format("Failed to save save data memory.\nCode: <{}>\n{}", - e.code().message(), e.what()), - }, - }); + const auto err = std::error_code{r, std::iostream_category()}; + throw std::filesystem::filesystem_error{err.message(), err}; + } catch (const std::filesystem::filesystem_error& e) { + errMsg = std::string{e.what()}; + std::this_thread::sleep_for(std::chrono::seconds(1)); } - g_saving_memory = false; } + const MsgDialog::MsgDialogState dialog{MsgDialog::MsgDialogState::UserState{ + .type = MsgDialog::ButtonType::OK, + .msg = "Failed to persist save memory:\n" + errMsg + "\nat " + + Common::FS::PathToUTF8String(memoryPath), + }}; + MsgDialog::ShowMsgDialog(dialog); } -void SetDirectories(OrbisUserServiceUserId user_id, std::string _game_serial) { - g_user_id = user_id; - g_game_serial = std::move(_game_serial); - g_save_path = SaveInstance::MakeDirSavePath(user_id, g_game_serial, DirnameSaveDataMemory); - g_param_sfo_path = SaveInstance::GetParamSFOPath(g_save_path); - g_param_sfo = PSF(); - g_icon_path = g_save_path / sce_sys / "icon0.png"; +std::string GetSaveDir(u32 slot_id) { + std::string dir(StandardDirnameSaveDataMemory); + if (slot_id > 0) { + dir += std::to_string(slot_id); + } + return dir; } -const std::filesystem::path& GetSavePath() { - return g_save_path; +std::filesystem::path GetSavePath(OrbisUserServiceUserId user_id, u32 slot_id, + std::string_view game_serial) { + std::string dir(StandardDirnameSaveDataMemory); + if (slot_id > 0) { + dir += std::to_string(slot_id); + } + return SaveInstance::MakeDirSavePath(user_id, Common::ElfInfo::Instance().GameSerial(), dir); } -size_t CreateSaveMemory(size_t memory_size) { - size_t existed_size = 0; +size_t SetupSaveMemory(OrbisUserServiceUserId user_id, u32 slot_id, std::string_view game_serial) { + std::lock_guard lck{g_slot_mtx}; - static std::once_flag init_save_thread_flag; - std::call_once(init_save_thread_flag, - [] { g_save_memory_thread = std::jthread{SaveThreadLoop}; }); + const auto save_dir = GetSavePath(user_id, slot_id, game_serial); - g_save_memory.resize(memory_size); - SaveInstance::SetupDefaultParamSFO(g_param_sfo, std::string{DirnameSaveDataMemory}, - g_game_serial); + auto& data = g_attached_slots[slot_id]; + data = SlotData{ + .user_id = user_id, + .game_serial = std::string{game_serial}, + .folder_path = save_dir, + .sfo = {}, + .memory_cache = {}, + }; - g_save_memory_initialized = true; + SaveInstance::SetupDefaultParamSFO(data.sfo, GetSaveDir(slot_id), std::string{game_serial}); - if (!fs::exists(g_param_sfo_path)) { - // Create save memory - fs::create_directories(g_save_path / sce_sys); - - IOFile memory_file{g_save_path / FilenameSaveDataMemory, Common::FS::FileAccessMode::Write}; - bool ok = memory_file.SetSize(memory_size); - if (!ok) { - LOG_ERROR(Lib_SaveData, "Failed to set memory size"); - throw std::filesystem::filesystem_error( - "Failed to set save memory size", g_save_path / FilenameSaveDataMemory, - std::make_error_code(std::errc::no_space_on_device)); - } - memory_file.Close(); - } else { - // Load save memory - - bool ok = g_param_sfo.Open(g_param_sfo_path); - if (!ok) { - LOG_ERROR(Lib_SaveData, "Failed to open SFO at {}", - fmt::UTF(g_param_sfo_path.u8string())); - throw std::filesystem::filesystem_error( - "failed to open SFO", g_param_sfo_path, - std::make_error_code(std::errc::illegal_byte_sequence)); - } - - IOFile memory_file{g_save_path / FilenameSaveDataMemory, Common::FS::FileAccessMode::Read}; - if (!memory_file.IsOpen()) { - LOG_ERROR(Lib_SaveData, "Failed to open save memory"); - throw std::filesystem::filesystem_error( - "failed to open save memory", g_save_path / FilenameSaveDataMemory, - std::make_error_code(std::errc::permission_denied)); - } - size_t save_size = memory_file.GetSize(); - existed_size = save_size; - memory_file.Seek(0); - memory_file.ReadRaw(g_save_memory.data(), std::min(save_size, memory_size)); - memory_file.Close(); + auto param_sfo_path = SaveInstance::GetParamSFOPath(save_dir); + if (!fs::exists(param_sfo_path)) { + return 0; } - return existed_size; + if (!data.sfo.Open(param_sfo_path) || fs::exists(save_dir / CorruptFileName)) { + if (!Backup::Restore(save_dir)) { // Could not restore the backup + return 0; + } + } + + const auto memory = save_dir / FilenameSaveDataMemory; + if (fs::exists(memory)) { + return fs::file_size(memory); + } + + return 0; } -void SetIcon(void* buf, size_t buf_size) { +void SetIcon(u32 slot_id, void* buf, size_t buf_size) { + std::lock_guard lck{g_slot_mtx}; + const auto& data = g_attached_slots[slot_id]; + const auto icon_path = data.folder_path / sce_sys / "icon0.png"; if (buf == nullptr) { const auto& src_icon = g_mnt->GetHostPath("/app0/sce_sys/save_data.png"); - if (fs::exists(src_icon)) { - if (fs::exists(g_icon_path)) { - fs::remove(g_icon_path); - } - fs::copy_file(src_icon, g_icon_path); + if (fs::exists(icon_path)) { + fs::remove(icon_path); } - if (fs::exists(g_icon_path)) { - IOFile file(g_icon_path, Common::FS::FileAccessMode::Read); - size_t size = file.GetSize(); - file.Seek(0); - g_icon_memory.resize(size); - file.ReadRaw(g_icon_memory.data(), size); - file.Close(); + if (fs::exists(src_icon)) { + fs::create_directories(icon_path.parent_path()); + fs::copy_file(src_icon, icon_path); } } else { - g_icon_memory.resize(buf_size); - std::memcpy(g_icon_memory.data(), buf, buf_size); - IOFile file(g_icon_path, Common::FS::FileAccessMode::Write); - file.Seek(0); - file.WriteRaw(g_icon_memory.data(), buf_size); + IOFile file(icon_path, Common::FS::FileAccessMode::Write); + file.WriteRaw(buf, buf_size); file.Close(); } } -void WriteIcon(void* buf, size_t buf_size) { - if (buf_size != g_icon_memory.size()) { - g_icon_memory.resize(buf_size); +bool IsSaveMemoryInitialized(u32 slot_id) { + std::lock_guard lck{g_slot_mtx}; + return g_attached_slots.contains(slot_id); +} + +PSF& GetParamSFO(u32 slot_id) { + std::lock_guard lck{g_slot_mtx}; + auto& data = g_attached_slots[slot_id]; + return data.sfo; +} + +std::vector GetIcon(u32 slot_id) { + std::lock_guard lck{g_slot_mtx}; + auto& data = g_attached_slots[slot_id]; + const auto icon_path = data.folder_path / sce_sys / "icon0.png"; + IOFile f{icon_path, Common::FS::FileAccessMode::Read}; + if (!f.IsOpen()) { + return {}; } - std::memcpy(g_icon_memory.data(), buf, buf_size); - g_icon_dirty = true; + const u64 size = f.GetSize(); + std::vector ret; + ret.resize(size); + f.ReadSpan(std::span{ret}); + return ret; } -bool IsSaveMemoryInitialized() { - return g_save_memory_initialized; -} - -PSF& GetParamSFO() { - return g_param_sfo; -} - -std::span GetIcon() { - return {g_icon_memory}; -} - -void SaveSFO(bool sync) { - if (!sync) { - g_param_dirty = true; - return; - } - const bool ok = g_param_sfo.Encode(g_param_sfo_path); +void SaveSFO(u32 slot_id) { + std::lock_guard lck{g_slot_mtx}; + const auto& data = g_attached_slots[slot_id]; + const auto sfo_path = SaveInstance::GetParamSFOPath(data.folder_path); + fs::create_directories(sfo_path.parent_path()); + const bool ok = data.sfo.Encode(sfo_path); if (!ok) { LOG_ERROR(Lib_SaveData, "Failed to encode param.sfo"); - throw std::filesystem::filesystem_error("Failed to write param.sfo", g_param_sfo_path, + throw std::filesystem::filesystem_error("Failed to write param.sfo", sfo_path, std::make_error_code(std::errc::permission_denied)); } } -bool IsSaving() { - return g_saving_memory; + +void ReadMemory(u32 slot_id, void* buf, size_t buf_size, int64_t offset) { + std::lock_guard lk{g_slot_mtx}; + auto& data = g_attached_slots[slot_id]; + auto& memory = data.memory_cache; + if (memory.empty()) { // Load file + IOFile f{data.folder_path / FilenameSaveDataMemory, Common::FS::FileAccessMode::Read}; + if (f.IsOpen()) { + memory.resize(f.GetSize()); + f.Seek(0); + f.ReadSpan(std::span{memory}); + } + } + s64 read_size = buf_size; + if (read_size + offset > memory.size()) { + read_size = memory.size() - offset; + } + std::memcpy(buf, memory.data() + offset, read_size); } -bool TriggerSaveWithoutEvent() { - if (g_saving_memory) { - return false; +void WriteMemory(u32 slot_id, void* buf, size_t buf_size, int64_t offset) { + std::lock_guard lk{g_slot_mtx}; + auto& data = g_attached_slots[slot_id]; + auto& memory = data.memory_cache; + if (offset + buf_size > memory.size()) { + memory.resize(offset + buf_size); } - g_trigger_save_memory.notify_one(); - return true; -} - -bool TriggerSave() { - if (g_saving_memory) { - return false; - } - g_save_event = true; - g_trigger_save_memory.notify_one(); - return true; -} - -void ReadMemory(void* buf, size_t buf_size, int64_t offset) { - std::scoped_lock lk{g_saving_memory_mutex}; - if (offset + buf_size > g_save_memory.size()) { - UNREACHABLE_MSG("ReadMemory out of bounds"); - } - std::memcpy(buf, g_save_memory.data() + offset, buf_size); -} - -void WriteMemory(void* buf, size_t buf_size, int64_t offset) { - std::scoped_lock lk{g_saving_memory_mutex}; - if (offset + buf_size > g_save_memory.size()) { - g_save_memory.resize(offset + buf_size); - } - std::memcpy(g_save_memory.data() + offset, buf, buf_size); - g_memory_dirty = true; + std::memcpy(memory.data() + offset, buf, buf_size); + PersistMemory(slot_id, false); + Backup::NewRequest(data.user_id, data.game_serial, GetSaveDir(slot_id), + Backup::OrbisSaveDataEventType::__DO_NOT_SAVE); } } // namespace Libraries::SaveData::SaveMemory \ No newline at end of file diff --git a/src/core/libraries/save_data/save_memory.h b/src/core/libraries/save_data/save_memory.h index 04eeaa652..681865634 100644 --- a/src/core/libraries/save_data/save_memory.h +++ b/src/core/libraries/save_data/save_memory.h @@ -3,7 +3,7 @@ #pragma once -#include +#include #include "save_backup.h" class PSF; @@ -14,36 +14,30 @@ using OrbisUserServiceUserId = s32; namespace Libraries::SaveData::SaveMemory { -void SetDirectories(OrbisUserServiceUserId user_id, std::string game_serial); +void PersistMemory(u32 slot_id, bool lock = true); -[[nodiscard]] const std::filesystem::path& GetSavePath(); +[[nodiscard]] std::string GetSaveDir(u32 slot_id); -// returns the size of the existed save memory -size_t CreateSaveMemory(size_t memory_size); +[[nodiscard]] std::filesystem::path GetSavePath(OrbisUserServiceUserId user_id, u32 slot_id, + std::string_view game_serial); -// Initialize the icon. Set buf to null to read the standard icon. -void SetIcon(void* buf, size_t buf_size); +// returns the size of the save memory if exists +size_t SetupSaveMemory(OrbisUserServiceUserId user_id, u32 slot_id, std::string_view game_serial); -// Update the icon -void WriteIcon(void* buf, size_t buf_size); +// Write the icon. Set buf to null to read the standard icon. +void SetIcon(u32 slot_id, void* buf = nullptr, size_t buf_size = 0); -[[nodiscard]] bool IsSaveMemoryInitialized(); +[[nodiscard]] bool IsSaveMemoryInitialized(u32 slot_id); -[[nodiscard]] PSF& GetParamSFO(); +[[nodiscard]] PSF& GetParamSFO(u32 slot_id); -[[nodiscard]] std::span GetIcon(); +[[nodiscard]] std::vector GetIcon(u32 slot_id); // Save now or wait for the background thread to save -void SaveSFO(bool sync = false); +void SaveSFO(u32 slot_id); -[[nodiscard]] bool IsSaving(); +void ReadMemory(u32 slot_id, void* buf, size_t buf_size, int64_t offset); -bool TriggerSaveWithoutEvent(); - -bool TriggerSave(); - -void ReadMemory(void* buf, size_t buf_size, int64_t offset); - -void WriteMemory(void* buf, size_t buf_size, int64_t offset); +void WriteMemory(u32 slot_id, void* buf, size_t buf_size, int64_t offset); } // namespace Libraries::SaveData::SaveMemory \ No newline at end of file diff --git a/src/core/libraries/save_data/savedata.cpp b/src/core/libraries/save_data/savedata.cpp index b573ded1e..e9ad77d69 100644 --- a/src/core/libraries/save_data/savedata.cpp +++ b/src/core/libraries/save_data/savedata.cpp @@ -177,7 +177,8 @@ struct OrbisSaveDataMemoryGet2 { OrbisSaveDataMemoryData* data; OrbisSaveDataParam* param; OrbisSaveDataIcon* icon; - std::array _reserved; + u32 slotId; + std::array _reserved; }; struct OrbisSaveDataMemorySet2 { @@ -186,6 +187,8 @@ struct OrbisSaveDataMemorySet2 { const OrbisSaveDataMemoryData* data; const OrbisSaveDataParam* param; const OrbisSaveDataIcon* icon; + u32 dataNum; + u32 slotId; std::array _reserved; }; @@ -198,7 +201,8 @@ struct OrbisSaveDataMemorySetup2 { const OrbisSaveDataParam* initParam; // +4.5 const OrbisSaveDataIcon* initIcon; - std::array _reserved; + u32 slotId; + std::array _reserved; }; struct OrbisSaveDataMemorySetupResult { @@ -206,9 +210,16 @@ struct OrbisSaveDataMemorySetupResult { std::array _reserved; }; +enum OrbisSaveDataMemorySyncOption : u32 { + NONE = 0, + BLOCKING = 1, +}; + struct OrbisSaveDataMemorySync { OrbisUserServiceUserId userId; - std::array _reserved; + u32 slotId; + OrbisSaveDataMemorySyncOption option; + std::array _reserved; }; struct OrbisSaveDataMount2 { @@ -327,6 +338,7 @@ static void initialize() { g_initialized = true; g_game_serial = ElfInfo::Instance().GameSerial(); g_fw_ver = ElfInfo::Instance().FirmwareVer(); + Backup::StartThread(); } // game_00other | game*other @@ -558,7 +570,6 @@ Error PS4_SYSV_ABI sceSaveDataBackup(const OrbisSaveDataBackup* backup) { } } - Backup::StartThread(); Backup::NewRequest(backup->userId, title, dir_name, OrbisSaveDataEventType::BACKUP); return Error::OK; @@ -1136,22 +1147,27 @@ Error PS4_SYSV_ABI sceSaveDataGetSaveDataMemory2(OrbisSaveDataMemoryGet2* getPar LOG_INFO(Lib_SaveData, "called with invalid parameter"); return Error::PARAMETER; } - if (!SaveMemory::IsSaveMemoryInitialized()) { + + u32 slot_id = 0; + if (g_fw_ver > ElfInfo::FW_50) { + slot_id = getParam->slotId; + } + if (!SaveMemory::IsSaveMemoryInitialized(slot_id)) { LOG_INFO(Lib_SaveData, "called without save memory initialized"); return Error::MEMORY_NOT_READY; } LOG_DEBUG(Lib_SaveData, "called"); auto data = getParam->data; if (data != nullptr) { - SaveMemory::ReadMemory(data->buf, data->bufSize, data->offset); + SaveMemory::ReadMemory(slot_id, data->buf, data->bufSize, data->offset); } auto param = getParam->param; if (param != nullptr) { - param->FromSFO(SaveMemory::GetParamSFO()); + param->FromSFO(SaveMemory::GetParamSFO(slot_id)); } auto icon = getParam->icon; if (icon != nullptr) { - auto icon_mem = SaveMemory::GetIcon(); + auto icon_mem = SaveMemory::GetIcon(slot_id); size_t total = std::min(icon->bufSize, icon_mem.size()); std::memcpy(icon->buf, icon_mem.data(), total); icon->dataSize = total; @@ -1494,36 +1510,37 @@ Error PS4_SYSV_ABI sceSaveDataSetSaveDataMemory2(const OrbisSaveDataMemorySet2* LOG_INFO(Lib_SaveData, "called with invalid parameter"); return Error::PARAMETER; } - if (!SaveMemory::IsSaveMemoryInitialized()) { + u32 slot_id = 0; + u32 data_num = 1; + if (g_fw_ver > ElfInfo::FW_50) { + slot_id = setParam->slotId; + if (setParam->dataNum > 1) { + data_num = setParam->dataNum; + } + } + if (!SaveMemory::IsSaveMemoryInitialized(slot_id)) { LOG_INFO(Lib_SaveData, "called without save memory initialized"); return Error::MEMORY_NOT_READY; } - if (SaveMemory::IsSaving()) { - int count = 0; - while (++count < 100 && SaveMemory::IsSaving()) { // try for more 10 seconds - std::this_thread::sleep_for(chrono::milliseconds(100)); - } - if (SaveMemory::IsSaving()) { - LOG_TRACE(Lib_SaveData, "called while saving"); - return Error::BUSY_FOR_SAVING; - } - } + LOG_DEBUG(Lib_SaveData, "called"); auto data = setParam->data; if (data != nullptr) { - SaveMemory::WriteMemory(data->buf, data->bufSize, data->offset); + for (int i = 0; i < data_num; i++) { + SaveMemory::WriteMemory(slot_id, data[i].buf, data[i].bufSize, data[i].offset); + } } auto param = setParam->param; if (param != nullptr) { - param->ToSFO(SaveMemory::GetParamSFO()); - SaveMemory::SaveSFO(); - } - auto icon = setParam->icon; - if (icon != nullptr) { - SaveMemory::WriteIcon(icon->buf, icon->bufSize); + param->ToSFO(SaveMemory::GetParamSFO(slot_id)); + SaveMemory::SaveSFO(slot_id); + } + + auto icon = setParam->icon; + if (icon != nullptr) { + SaveMemory::SetIcon(slot_id, icon->buf, icon->bufSize); } - SaveMemory::TriggerSaveWithoutEvent(); return Error::OK; } @@ -1563,9 +1580,12 @@ Error PS4_SYSV_ABI sceSaveDataSetupSaveDataMemory2(const OrbisSaveDataMemorySetu } LOG_DEBUG(Lib_SaveData, "called"); - SaveMemory::SetDirectories(setupParam->userId, g_game_serial); + u32 slot_id = 0; + if (g_fw_ver > ElfInfo::FW_50) { + slot_id = setupParam->slotId; + } - const auto& save_path = SaveMemory::GetSavePath(); + const auto& save_path = SaveMemory::GetSavePath(setupParam->userId, slot_id, g_game_serial); for (const auto& instance : g_mount_slots) { if (instance.has_value() && instance->GetSavePath() == save_path) { return Error::BUSY; @@ -1573,21 +1593,21 @@ Error PS4_SYSV_ABI sceSaveDataSetupSaveDataMemory2(const OrbisSaveDataMemorySetu } try { - size_t existed_size = SaveMemory::CreateSaveMemory(setupParam->memorySize); + size_t existed_size = + SaveMemory::SetupSaveMemory(setupParam->userId, slot_id, g_game_serial); if (existed_size == 0) { // Just created if (g_fw_ver >= ElfInfo::FW_45 && setupParam->initParam != nullptr) { - auto& sfo = SaveMemory::GetParamSFO(); + auto& sfo = SaveMemory::GetParamSFO(slot_id); setupParam->initParam->ToSFO(sfo); } - SaveMemory::SaveSFO(); + SaveMemory::SaveSFO(slot_id); auto init_icon = setupParam->initIcon; if (g_fw_ver >= ElfInfo::FW_45 && init_icon != nullptr) { - SaveMemory::SetIcon(init_icon->buf, init_icon->bufSize); + SaveMemory::SetIcon(slot_id, init_icon->buf, init_icon->bufSize); } else { - SaveMemory::SetIcon(nullptr, 0); + SaveMemory::SetIcon(slot_id); } - SaveMemory::TriggerSaveWithoutEvent(); } if (g_fw_ver >= ElfInfo::FW_45 && result != nullptr) { result->existedMemorySize = existed_size; @@ -1631,15 +1651,23 @@ Error PS4_SYSV_ABI sceSaveDataSyncSaveDataMemory(OrbisSaveDataMemorySync* syncPa LOG_INFO(Lib_SaveData, "called with invalid parameter"); return Error::PARAMETER; } - if (!SaveMemory::IsSaveMemoryInitialized()) { + + u32 slot_id = 0; + if (g_fw_ver > ElfInfo::FW_50) { + slot_id = syncParam->slotId; + } + + if (!SaveMemory::IsSaveMemoryInitialized(slot_id)) { LOG_INFO(Lib_SaveData, "called without save memory initialized"); return Error::MEMORY_NOT_READY; } LOG_DEBUG(Lib_SaveData, "called"); - bool ok = SaveMemory::TriggerSave(); - if (!ok) { - return Error::BUSY_FOR_SAVING; - } + + SaveMemory::PersistMemory(slot_id); + const auto& save_path = SaveMemory::GetSaveDir(slot_id); + Backup::NewRequest(syncParam->userId, g_game_serial, save_path, + OrbisSaveDataEventType::SAVE_DATA_MEMORY_SYNC); + return Error::OK; } diff --git a/src/emulator.cpp b/src/emulator.cpp index 97215c06f..d873b3462 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -33,6 +33,7 @@ #include "core/libraries/ngs2/ngs2.h" #include "core/libraries/np_trophy/np_trophy.h" #include "core/libraries/rtc/rtc.h" +#include "core/libraries/save_data/save_backup.h" #include "core/linker.h" #include "core/memory.h" #include "emulator.h" @@ -271,7 +272,7 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector Date: Thu, 30 Jan 2025 00:09:11 -0800 Subject: [PATCH 516/549] sdl_window: Allow alternate face button keys on any system. (#2275) * sdl_window: Allow alternate face button keys on any system. * readme: Fix typo --- README.md | 55 ++++++++++++++++++++++++---------------------- src/sdl_window.cpp | 27 ++++++++--------------- 2 files changed, 38 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index fd40d2d63..89aed29b4 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,9 @@ For more information on how to test, debug and report issues with the emulator o # Keyboard mapping +> [!NOTE] +> Some keyboards may also require you to hold the Fn key to use the F\* keys. Mac users should use the Command key instead of Control, and need to use Command+F11 for full screen to avoid conflicting with system key bindings. + | Button | Function | |-------------|-------------| F10 | FPS Counter @@ -86,32 +89,32 @@ F12 | Trigger RenderDoc Capture > [!NOTE] > Xbox and DualShock controllers work out of the box. -| Controller button | Keyboard equivelant | Mac alternative | -|-------------|-------------|--------------| -LEFT AXIS UP | W | | -LEFT AXIS DOWN | S | | -LEFT AXIS LEFT | A | | -LEFT AXIS RIGHT | D | | -RIGHT AXIS UP | I | | -RIGHT AXIS DOWN | K | | -RIGHT AXIS LEFT | J | | -RIGHT AXIS RIGHT | L | | -TRIANGLE | Numpad 8 | C | -CIRCLE | Numpad 6 | B | -CROSS | Numpad 2 | N | -SQUARE | Numpad 4 | V | -PAD UP | UP | | -PAD DOWN | DOWN | | -PAD LEFT | LEFT | | -PAD RIGHT | RIGHT | | -OPTIONS | RETURN | | -BACK BUTTON / TOUCH PAD | SPACE | | -L1 | Q | | -R1 | U | | -L2 | E | | -R2 | O | | -L3 | X | | -R3 | M | | +| Controller button | Keyboard equivalent | +|-------------|-------------| +LEFT AXIS UP | W | +LEFT AXIS DOWN | S | +LEFT AXIS LEFT | A | +LEFT AXIS RIGHT | D | +RIGHT AXIS UP | I | +RIGHT AXIS DOWN | K | +RIGHT AXIS LEFT | J | +RIGHT AXIS RIGHT | L | +TRIANGLE | Numpad 8 or C | +CIRCLE | Numpad 6 or B | +CROSS | Numpad 2 or N | +SQUARE | Numpad 4 or V | +PAD UP | UP | +PAD DOWN | DOWN | +PAD LEFT | LEFT | +PAD RIGHT | RIGHT | +OPTIONS | RETURN | +BACK BUTTON / TOUCH PAD | SPACE | +L1 | Q | +R1 | U | +L2 | E | +R2 | O | +L3 | X | +R3 | M | # Main team diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index d1fe6bbab..9b7617925 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -382,20 +382,6 @@ void WindowSDL::OnResize() { } void WindowSDL::OnKeyPress(const SDL_Event* event) { -#ifdef __APPLE__ - // Use keys that are more friendly for keyboards without a keypad. - // Once there are key binding options this won't be necessary. - constexpr SDL_Keycode CrossKey = SDLK_N; - constexpr SDL_Keycode CircleKey = SDLK_B; - constexpr SDL_Keycode SquareKey = SDLK_V; - constexpr SDL_Keycode TriangleKey = SDLK_C; -#else - constexpr SDL_Keycode CrossKey = SDLK_KP_2; - constexpr SDL_Keycode CircleKey = SDLK_KP_6; - constexpr SDL_Keycode SquareKey = SDLK_KP_4; - constexpr SDL_Keycode TriangleKey = SDLK_KP_8; -#endif - auto button = OrbisPadButtonDataOffset::None; Input::Axis axis = Input::Axis::AxisMax; int axisvalue = 0; @@ -414,16 +400,21 @@ void WindowSDL::OnKeyPress(const SDL_Event* event) { case SDLK_RIGHT: button = OrbisPadButtonDataOffset::Right; break; - case TriangleKey: + // Provide alternatives for face buttons for users without a numpad. + case SDLK_KP_8: + case SDLK_C: button = OrbisPadButtonDataOffset::Triangle; break; - case CircleKey: + case SDLK_KP_6: + case SDLK_B: button = OrbisPadButtonDataOffset::Circle; break; - case CrossKey: + case SDLK_KP_2: + case SDLK_N: button = OrbisPadButtonDataOffset::Cross; break; - case SquareKey: + case SDLK_KP_4: + case SDLK_V: button = OrbisPadButtonDataOffset::Square; break; case SDLK_RETURN: From d8b411429638b48db375c03682fd1bcc3efdfd11 Mon Sep 17 00:00:00 2001 From: Bhaal42 Date: Thu, 30 Jan 2025 09:25:29 +0100 Subject: [PATCH 517/549] Patch 1 (#2278) * Update fr.ts Corrected some typos * Update fr.ts forgot plural --- src/qt_gui/translations/fr.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/qt_gui/translations/fr.ts b/src/qt_gui/translations/fr.ts index 0f87b087b..250b8f3c3 100644 --- a/src/qt_gui/translations/fr.ts +++ b/src/qt_gui/translations/fr.ts @@ -373,7 +373,7 @@ toolBar - Bare d'outils + Barre d'outils Game List @@ -489,7 +489,7 @@ Game successfully installed at %1 - Jeu installé avec succès à %1 + Jeu installé avec succès dans %1 File doesn't appear to be a valid PKG file @@ -554,7 +554,7 @@ Show Game Size In List - Afficher la taille du jeu dans la liste + Afficher la taille des jeux dans la liste Show Splash @@ -945,7 +945,7 @@ Serial: - Série: + Numéro de série: Version: @@ -1164,7 +1164,7 @@ Serial - Série + Numéro de série Compatibility @@ -1361,4 +1361,4 @@ TB - \ No newline at end of file + From 132a9d7d357727ab7e16e974647890434dfcd0d6 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Thu, 30 Jan 2025 01:38:34 -0800 Subject: [PATCH 518/549] externals: Update MoltenVK (#2280) Small fix for pipeline serialization from last change. --- externals/MoltenVK/MoltenVK | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externals/MoltenVK/MoltenVK b/externals/MoltenVK/MoltenVK index aba997657..0c090001c 160000 --- a/externals/MoltenVK/MoltenVK +++ b/externals/MoltenVK/MoltenVK @@ -1 +1 @@ -Subproject commit aba997657b94d6de1794ebad36ce5634341252c7 +Subproject commit 0c090001cb42997031cfe43914340e2639944972 From 3a163002d712ba9a22e2d0fac09302de6f324c61 Mon Sep 17 00:00:00 2001 From: Missake212 Date: Thu, 30 Jan 2025 13:06:55 +0100 Subject: [PATCH 519/549] Update README.md (#2281) Lot of people seem to think that building is required to use shad, adding a little note in the README to redirect them to tabs where they can simply download it. --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 89aed29b4..30efeb263 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,9 @@ This project began as a fun project. Given our limited free time, it may take so # Building +> [!IMPORTANT] +> If you want to use shadPS4 to play your games, you don't have to follow the build instructions, you can simply download the emulator from either the [**release tab**](https://github.com/shadps4-emu/shadPS4/releases) or the [**action tab**](https://github.com/shadps4-emu/shadPS4/actions). + ## Windows Check the build instructions for [**Windows**](https://github.com/shadps4-emu/shadPS4/blob/main/documents/building-windows.md). From 19bbbf994c541e009fc88ab5366c8e21f27d8a49 Mon Sep 17 00:00:00 2001 From: Zaid Ismail <64991232+zaid-ismail031@users.noreply.github.com> Date: Thu, 30 Jan 2025 14:09:50 +0200 Subject: [PATCH 520/549] Fix game title sorting bug from Issue #2260 (#2284) * Fix alphabetical sorting bug caused by case-sensitive string comparisons in GameListFrame. * Fix bug with incorrect use of std::tolower. * Fix clang-format error. --------- Co-authored-by: Zaid Ismail --- src/qt_gui/game_list_frame.h | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/qt_gui/game_list_frame.h b/src/qt_gui/game_list_frame.h index 8c6fcb1e2..90d021093 100644 --- a/src/qt_gui/game_list_frame.h +++ b/src/qt_gui/game_list_frame.h @@ -3,6 +3,9 @@ #pragma once +#include // std::transform +#include // std::tolower + #include #include #include @@ -65,8 +68,12 @@ public: static bool CompareStringsAscending(GameInfo a, GameInfo b, int columnIndex) { switch (columnIndex) { - case 1: - return a.name < b.name; + case 1: { + std::string name_a = a.name, name_b = b.name; + std::transform(name_a.begin(), name_a.end(), name_a.begin(), ::tolower); + std::transform(name_b.begin(), name_b.end(), name_b.begin(), ::tolower); + return name_a < name_b; + } case 2: return a.compatibility.status < b.compatibility.status; case 3: @@ -90,8 +97,12 @@ public: static bool CompareStringsDescending(GameInfo a, GameInfo b, int columnIndex) { switch (columnIndex) { - case 1: - return a.name > b.name; + case 1: { + std::string name_a = a.name, name_b = b.name; + std::transform(name_a.begin(), name_a.end(), name_a.begin(), ::tolower); + std::transform(name_b.begin(), name_b.end(), name_b.begin(), ::tolower); + return name_a > name_b; + } case 2: return a.compatibility.status > b.compatibility.status; case 3: From c89c7e8fed8b296204a0175e0414b4c867a84a1c Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Thu, 30 Jan 2025 09:17:03 -0800 Subject: [PATCH 521/549] cpu_patches: Always use AVX for certain patches. (#2274) --- src/core/cpu_patches.cpp | 39 ++++++++++++--------------------------- 1 file changed, 12 insertions(+), 27 deletions(-) diff --git a/src/core/cpu_patches.cpp b/src/core/cpu_patches.cpp index b812e5444..57d528a81 100644 --- a/src/core/cpu_patches.cpp +++ b/src/core/cpu_patches.cpp @@ -30,16 +30,6 @@ using namespace Xbyak::util; -#define MAYBE_AVX(OPCODE, ...) \ - [&] { \ - Cpu cpu; \ - if (cpu.has(Cpu::tAVX)) { \ - c.v##OPCODE(__VA_ARGS__); \ - } else { \ - c.OPCODE(__VA_ARGS__); \ - } \ - }() - namespace Core { static Xbyak::Reg ZydisToXbyakRegister(const ZydisRegister reg) { @@ -643,7 +633,7 @@ static void GenerateEXTRQ(const ZydisDecodedOperand* operands, Xbyak::CodeGenera ASSERT_MSG(length + index <= 64, "length + index must be less than or equal to 64."); // Get lower qword from xmm register - MAYBE_AVX(movq, scratch1, xmm_dst); + c.vmovq(scratch1, xmm_dst); if (index != 0) { c.shr(scratch1, index); @@ -656,7 +646,7 @@ static void GenerateEXTRQ(const ZydisDecodedOperand* operands, Xbyak::CodeGenera // Writeback to xmm register, extrq instruction says top 64-bits are undefined so we don't // care to preserve them - MAYBE_AVX(movq, xmm_dst, scratch1); + c.vmovq(xmm_dst, scratch1); c.pop(scratch2); c.pop(scratch1); @@ -690,7 +680,7 @@ static void GenerateEXTRQ(const ZydisDecodedOperand* operands, Xbyak::CodeGenera c.push(mask); // Construct the mask out of the length that resides in bottom 6 bits of source xmm - MAYBE_AVX(movq, scratch1, xmm_src); + c.vmovq(scratch1, xmm_src); c.mov(scratch2, scratch1); c.and_(scratch2, 0x3F); c.jz(length_zero); @@ -711,10 +701,10 @@ static void GenerateEXTRQ(const ZydisDecodedOperand* operands, Xbyak::CodeGenera c.and_(scratch1, 0x3F); c.mov(scratch2, scratch1); // cl now contains the shift amount - MAYBE_AVX(movq, scratch1, xmm_dst); + c.vmovq(scratch1, xmm_dst); c.shr(scratch1, cl); c.and_(scratch1, mask); - MAYBE_AVX(movq, xmm_dst, scratch1); + c.vmovq(xmm_dst, scratch1); c.pop(mask); c.pop(scratch2); @@ -765,8 +755,8 @@ static void GenerateINSERTQ(const ZydisDecodedOperand* operands, Xbyak::CodeGene ASSERT_MSG(length + index <= 64, "length + index must be less than or equal to 64."); - MAYBE_AVX(movq, scratch1, xmm_src); - MAYBE_AVX(movq, scratch2, xmm_dst); + c.vmovq(scratch1, xmm_src); + c.vmovq(scratch2, xmm_dst); c.mov(mask, mask_value); // src &= mask @@ -784,12 +774,7 @@ static void GenerateINSERTQ(const ZydisDecodedOperand* operands, Xbyak::CodeGene c.or_(scratch2, scratch1); // Insert scratch2 into low 64 bits of dst, upper 64 bits are unaffected - Cpu cpu; - if (cpu.has(Cpu::tAVX)) { - c.vpinsrq(xmm_dst, xmm_dst, scratch2, 0); - } else { - c.pinsrq(xmm_dst, scratch2, 0); - } + c.vpinsrq(xmm_dst, xmm_dst, scratch2, 0); c.pop(mask); c.pop(scratch2); @@ -816,7 +801,7 @@ static void GenerateINSERTQ(const ZydisDecodedOperand* operands, Xbyak::CodeGene c.push(mask); // Get upper 64 bits of src and copy it to mask and index - MAYBE_AVX(pextrq, index, xmm_src, 1); + c.vpextrq(index, xmm_src, 1); c.mov(mask, index); // When length is 0, set it to 64 @@ -839,7 +824,7 @@ static void GenerateINSERTQ(const ZydisDecodedOperand* operands, Xbyak::CodeGene c.and_(index, 0x3F); // src &= mask - MAYBE_AVX(movq, scratch1, xmm_src); + c.vmovq(scratch1, xmm_src); c.and_(scratch1, mask); // mask = ~(mask << index) @@ -851,12 +836,12 @@ static void GenerateINSERTQ(const ZydisDecodedOperand* operands, Xbyak::CodeGene c.shl(scratch1, cl); // dst = (dst & mask) | src - MAYBE_AVX(movq, scratch2, xmm_dst); + c.vmovq(scratch2, xmm_dst); c.and_(scratch2, mask); c.or_(scratch2, scratch1); // Upper 64 bits are undefined in insertq - MAYBE_AVX(movq, xmm_dst, scratch2); + c.vmovq(xmm_dst, scratch2); c.pop(mask); c.pop(index); From e805b9752097a5292a8c41ca7a5a6c27934a1b94 Mon Sep 17 00:00:00 2001 From: tomboylover93 <95257311+tomboylover93@users.noreply.github.com> Date: Thu, 30 Jan 2025 14:34:31 -0300 Subject: [PATCH 522/549] Add Vulkan debug options to the Debug tab (#2254) Co-authored-by: DanielSvoboda --- documents/building-linux.md | 2 +- src/common/config.cpp | 24 +- src/common/config.h | 9 +- src/emulator.cpp | 6 +- src/imgui/renderer/imgui_core.cpp | 4 +- src/imgui/renderer/texture_manager.cpp | 2 +- src/qt_gui/settings_dialog.cpp | 31 +- src/qt_gui/settings_dialog.ui | 286 ++++++++++++------ src/qt_gui/translations/ar.ts | 44 +++ src/qt_gui/translations/da_DK.ts | 44 +++ src/qt_gui/translations/de.ts | 44 +++ src/qt_gui/translations/el.ts | 44 +++ src/qt_gui/translations/en.ts | 47 ++- src/qt_gui/translations/es_ES.ts | 44 +++ src/qt_gui/translations/fa_IR.ts | 44 +++ src/qt_gui/translations/fi.ts | 44 +++ src/qt_gui/translations/fr.ts | 44 +++ src/qt_gui/translations/hu_HU.ts | 44 +++ src/qt_gui/translations/id.ts | 44 +++ src/qt_gui/translations/it.ts | 44 +++ src/qt_gui/translations/ja_JP.ts | 44 +++ src/qt_gui/translations/ko_KR.ts | 44 +++ src/qt_gui/translations/lt_LT.ts | 44 +++ src/qt_gui/translations/nb.ts | 44 +++ src/qt_gui/translations/nl.ts | 44 +++ src/qt_gui/translations/pl_PL.ts | 44 +++ src/qt_gui/translations/pt_BR.ts | 82 +++-- src/qt_gui/translations/ro_RO.ts | 44 +++ src/qt_gui/translations/ru_RU.ts | 44 +++ src/qt_gui/translations/sq.ts | 44 +++ src/qt_gui/translations/sv.ts | 44 +++ src/qt_gui/translations/tr_TR.ts | 44 +++ src/qt_gui/translations/uk_UA.ts | 44 +++ src/qt_gui/translations/vi_VN.ts | 44 +++ src/qt_gui/translations/zh_CN.ts | 44 +++ src/qt_gui/translations/zh_TW.ts | 44 +++ src/video_core/renderer_vulkan/vk_platform.h | 4 +- .../renderer_vulkan/vk_presenter.cpp | 14 +- .../renderer_vulkan/vk_rasterizer.cpp | 16 +- 39 files changed, 1518 insertions(+), 153 deletions(-) diff --git a/documents/building-linux.md b/documents/building-linux.md index 4aa66aac6..d9ae2e54c 100644 --- a/documents/building-linux.md +++ b/documents/building-linux.md @@ -29,7 +29,7 @@ sudo dnf install clang git cmake libatomic alsa-lib-devel pipewire-jack-audio-co sudo pacman -S base-devel clang git cmake sndio jack2 openal qt6-base qt6-declarative qt6-multimedia sdl2 vulkan-validation-layers ``` -**Note** : The `shadps4-git` AUR package is not maintained by any of the developers, and it uses the default compiler, which is often set to GCC. Use at your own discretion. +**Note**: The `shadps4-git` AUR package is not maintained by any of the developers, and it uses the default compiler, which is often set to GCC. Use at your own discretion. #### OpenSUSE diff --git a/src/common/config.cpp b/src/common/config.cpp index a57b6d35c..70b062951 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -267,18 +267,28 @@ bool vkValidationGpuEnabled() { return vkValidationGpu; } -bool vkCrashDiagnosticEnabled() { +bool getVkCrashDiagnosticEnabled() { return vkCrashDiagnostic; } -bool vkHostMarkersEnabled() { - // Forced on when crash diagnostic enabled. - return vkHostMarkers || vkCrashDiagnostic; +bool getVkHostMarkersEnabled() { + return vkHostMarkers; } -bool vkGuestMarkersEnabled() { - // Forced on when crash diagnostic enabled. - return vkGuestMarkers || vkCrashDiagnostic; +bool getVkGuestMarkersEnabled() { + return vkGuestMarkers; +} + +void setVkCrashDiagnosticEnabled(bool enable) { + vkCrashDiagnostic = enable; +} + +void setVkHostMarkersEnabled(bool enable) { + vkHostMarkers = enable; +} + +void setVkGuestMarkersEnabled(bool enable) { + vkGuestMarkers = enable; } bool getSeparateUpdateEnabled() { diff --git a/src/common/config.h b/src/common/config.h index 15937606e..356cf77fc 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -106,9 +106,12 @@ void setRdocEnabled(bool enable); bool vkValidationEnabled(); bool vkValidationSyncEnabled(); bool vkValidationGpuEnabled(); -bool vkCrashDiagnosticEnabled(); -bool vkHostMarkersEnabled(); -bool vkGuestMarkersEnabled(); +bool getVkCrashDiagnosticEnabled(); +bool getVkHostMarkersEnabled(); +bool getVkGuestMarkersEnabled(); +void setVkCrashDiagnosticEnabled(bool enable); +void setVkHostMarkersEnabled(bool enable); +void setVkGuestMarkersEnabled(bool enable); // Gui void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h); diff --git a/src/emulator.cpp b/src/emulator.cpp index d873b3462..81c4d814d 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -67,9 +67,9 @@ Emulator::Emulator() { LOG_INFO(Config, "Vulkan vkValidation: {}", Config::vkValidationEnabled()); LOG_INFO(Config, "Vulkan vkValidationSync: {}", Config::vkValidationSyncEnabled()); LOG_INFO(Config, "Vulkan vkValidationGpu: {}", Config::vkValidationGpuEnabled()); - LOG_INFO(Config, "Vulkan crashDiagnostics: {}", Config::vkCrashDiagnosticEnabled()); - LOG_INFO(Config, "Vulkan hostMarkers: {}", Config::vkHostMarkersEnabled()); - LOG_INFO(Config, "Vulkan guestMarkers: {}", Config::vkGuestMarkersEnabled()); + LOG_INFO(Config, "Vulkan crashDiagnostics: {}", Config::getVkCrashDiagnosticEnabled()); + LOG_INFO(Config, "Vulkan hostMarkers: {}", Config::getVkHostMarkersEnabled()); + LOG_INFO(Config, "Vulkan guestMarkers: {}", Config::getVkGuestMarkersEnabled()); LOG_INFO(Config, "Vulkan rdocEnable: {}", Config::isRdocEnabled()); // Create stdin/stdout/stderr diff --git a/src/imgui/renderer/imgui_core.cpp b/src/imgui/renderer/imgui_core.cpp index 26253822c..d9530dd70 100644 --- a/src/imgui/renderer/imgui_core.cpp +++ b/src/imgui/renderer/imgui_core.cpp @@ -208,7 +208,7 @@ void Render(const vk::CommandBuffer& cmdbuf, const vk::ImageView& image_view, return; } - if (Config::vkHostMarkersEnabled()) { + if (Config::getVkHostMarkersEnabled()) { cmdbuf.beginDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT{ .pLabelName = "ImGui Render", }); @@ -233,7 +233,7 @@ void Render(const vk::CommandBuffer& cmdbuf, const vk::ImageView& image_view, cmdbuf.beginRendering(render_info); Vulkan::RenderDrawData(*draw_data, cmdbuf); cmdbuf.endRendering(); - if (Config::vkHostMarkersEnabled()) { + if (Config::getVkHostMarkersEnabled()) { cmdbuf.endDebugUtilsLabelEXT(); } } diff --git a/src/imgui/renderer/texture_manager.cpp b/src/imgui/renderer/texture_manager.cpp index d7516a3a5..e217cd130 100644 --- a/src/imgui/renderer/texture_manager.cpp +++ b/src/imgui/renderer/texture_manager.cpp @@ -152,7 +152,7 @@ void WorkerLoop() { g_job_list.pop_front(); g_job_list_mtx.unlock(); - if (Config::vkCrashDiagnosticEnabled()) { + if (Config::getVkCrashDiagnosticEnabled()) { // FIXME: Crash diagnostic hangs when building the command buffer here continue; } diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index b41fde745..802325126 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -285,6 +285,11 @@ SettingsDialog::SettingsDialog(std::span physical_devices, ui->vkValidationCheckBox->installEventFilter(this); ui->vkSyncValidationCheckBox->installEventFilter(this); ui->rdocCheckBox->installEventFilter(this); + ui->crashDiagnosticsCheckBox->installEventFilter(this); + ui->guestMarkersCheckBox->installEventFilter(this); + ui->hostMarkersCheckBox->installEventFilter(this); + ui->collectShaderCheckBox->installEventFilter(this); + ui->copyGPUBuffersCheckBox->installEventFilter(this); } } @@ -360,6 +365,15 @@ void SettingsDialog::LoadValuesFromConfig() { ui->vkSyncValidationCheckBox->setChecked( toml::find_or(data, "Vulkan", "validation_sync", false)); ui->rdocCheckBox->setChecked(toml::find_or(data, "Vulkan", "rdocEnable", false)); + ui->crashDiagnosticsCheckBox->setChecked( + toml::find_or(data, "Vulkan", "crashDiagnostic", false)); + ui->guestMarkersCheckBox->setChecked( + toml::find_or(data, "Vulkan", "guestMarkers", false)); + ui->hostMarkersCheckBox->setChecked(toml::find_or(data, "Vulkan", "hostMarkers", false)); + ui->copyGPUBuffersCheckBox->setChecked( + toml::find_or(data, "GPU", "copyGPUBuffers", false)); + ui->collectShaderCheckBox->setChecked( + toml::find_or(data, "Debug", "CollectShader", false)); ui->enableCompatibilityCheckBox->setChecked( toml::find_or(data, "General", "compatibilityEnabled", false)); ui->checkCompatibilityOnStartupCheckBox->setChecked( @@ -380,7 +394,7 @@ void SettingsDialog::LoadValuesFromConfig() { std::string chooseHomeTab = toml::find_or(data, "General", "chooseHomeTab", ""); ui->chooseHomeTabComboBox->setCurrentText(QString::fromStdString(chooseHomeTab)); - QStringList tabNames = {tr("General"), tr("Gui"), tr("Graphics"), tr("User"), + QStringList tabNames = {tr("General"), tr("GUI"), tr("Graphics"), tr("User"), tr("Input"), tr("Paths"), tr("Debug")}; QString chooseHomeTabQString = QString::fromStdString(chooseHomeTab); int indexTab = tabNames.indexOf(chooseHomeTabQString); @@ -551,6 +565,16 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) { text = tr("vkSyncValidationCheckBox"); } else if (elementName == "rdocCheckBox") { text = tr("rdocCheckBox"); + } else if (elementName == "crashDiagnosticsCheckBox") { + text = tr("crashDiagnosticsCheckBox"); + } else if (elementName == "guestMarkersCheckBox") { + text = tr("guestMarkersCheckBox"); + } else if (elementName == "hostMarkersCheckBox") { + text = tr("hostMarkersCheckBox"); + } else if (elementName == "copyGPUBuffersCheckBox") { + text = tr("copyGPUBuffersCheckBox"); + } else if (elementName == "collectShaderCheckBox") { + text = tr("collectShaderCheckBox"); } ui->descriptionText->setText(text.replace("\\n", "\n")); @@ -604,6 +628,11 @@ void SettingsDialog::UpdateSettings() { Config::setVkValidation(ui->vkValidationCheckBox->isChecked()); Config::setVkSyncValidation(ui->vkSyncValidationCheckBox->isChecked()); Config::setRdocEnabled(ui->rdocCheckBox->isChecked()); + Config::setVkHostMarkersEnabled(ui->hostMarkersCheckBox->isChecked()); + Config::setVkGuestMarkersEnabled(ui->guestMarkersCheckBox->isChecked()); + Config::setVkCrashDiagnosticEnabled(ui->crashDiagnosticsCheckBox->isChecked()); + Config::setCollectShaderForDebug(ui->collectShaderCheckBox->isChecked()); + Config::setCopyGPUCmdBuffers(ui->copyGPUBuffersCheckBox->isChecked()); Config::setAutoUpdate(ui->updateCheckBox->isChecked()); Config::setUpdateChannel(ui->updateComboBox->currentText().toStdString()); Config::setChooseHomeTab(ui->chooseHomeTabComboBox->currentText().toStdString()); diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index 5da6a8198..4e7440af5 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -12,7 +12,7 @@ 0 0 970 - 600 + 750 @@ -74,7 +74,7 @@ 0 0 946 - 386 + 536 @@ -476,7 +476,7 @@ true - Gui + GUI @@ -484,7 +484,7 @@ 0 0 946 - 386 + 536 @@ -497,14 +497,14 @@ 0 - + 0 - + 0 0 @@ -530,7 +530,7 @@ Default tab when opening settings - + @@ -546,7 +546,7 @@ - Gui + GUI @@ -594,6 +594,18 @@ 0 + + + 0 + 100 + + + + + 16777215 + 16777215 + + 11 @@ -605,62 +617,16 @@ false - + Title Music false - - - - 10 - 80 - 416 - 29 - - - - - 0 - 0 - - - - Set the volume of the background music. - - - 100 - - - 10 - - - 20 - - - 50 - - - Qt::Orientation::Horizontal - - - false - - - false - - - QSlider::TickPosition::NoTicks - - - 10 - - - 10 - 22 + 9 + 30 416 26 @@ -675,45 +641,119 @@ Play title music - + - 10 - 54 - 416 - 20 + 0 + 50 + 431 + 47 - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - Volume - + + + 9 + + + 9 + + + 9 + + + 9 + + + 9 + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Volume + + + + + + + + 0 + 0 + + + + Set the volume of the background music. + + + 100 + + + 10 + + + 20 + + + 50 + + + Qt::Orientation::Horizontal + + + false + + + false + + + QSlider::TickPosition::NoTicks + + + 10 + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + - + 6 - 210 + 0 @@ -790,6 +830,19 @@ + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + +
@@ -810,7 +863,7 @@ 0 0 946 - 386 + 536 @@ -1054,10 +1107,10 @@ 0 0 946 - 386 + 536 - + @@ -1198,7 +1251,7 @@ 0 0 946 - 386 + 536 @@ -1482,7 +1535,7 @@ 0 0 946 - 386 + 536 @@ -1572,10 +1625,10 @@ 0 0 946 - 386 + 536 - + @@ -1585,7 +1638,7 @@ 0 - + @@ -1728,20 +1781,57 @@ - - - Qt::Orientation::Vertical + + + + 0 + 0 + - - QSizePolicy::Policy::MinimumExpanding + + Advanced - - - 0 - 0 - + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop - + + + + + Enable Crash Diagnostics + + + + + + + Collect Shaders + + + + + + + Copy GPU Buffers + + + + + + + Host Debug Markers + + + + + + + Guest Debug Markers + + + + + diff --git a/src/qt_gui/translations/ar.ts b/src/qt_gui/translations/ar.ts index f7b93028a..617753ab8 100644 --- a/src/qt_gui/translations/ar.ts +++ b/src/qt_gui/translations/ar.ts @@ -700,6 +700,26 @@ Enable RenderDoc Debugging RenderDoc تمكين تصحيح أخطاء + + Enable Crash Diagnostics + Enable Crash Diagnostics + + + Collect Shaders + Collect Shaders + + + Copy GPU Buffers + Copy GPU Buffers + + + Host Debug Markers + Host Debug Markers + + + Guest Debug Markers + Guest Debug Markers + Update تحديث @@ -720,6 +740,10 @@ GUI Settings إعدادات الواجهة + + Title Music + Title Music + Disable Trophy Pop-ups Disable Trophy Pop-ups @@ -928,6 +952,26 @@ rdocCheckBox تمكين تصحيح RenderDoc:\nإذا تم التمكين، سيوفر المحاكي توافقًا مع Renderdoc لالتقاط وتحليل الإطار الذي يتم عرضه حاليًا. + + collectShaderCheckBox + Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + + + crashDiagnosticsCheckBox + Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + + + copyGPUBuffersCheckBox + Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + + + hostMarkersCheckBox + Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + + + guestMarkersCheckBox + Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + CheatsPatches diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts index 61d2f68bf..abde6ff72 100644 --- a/src/qt_gui/translations/da_DK.ts +++ b/src/qt_gui/translations/da_DK.ts @@ -700,6 +700,26 @@ Enable RenderDoc Debugging Enable RenderDoc Debugging + + Enable Crash Diagnostics + Enable Crash Diagnostics + + + Collect Shaders + Collect Shaders + + + Copy GPU Buffers + Copy GPU Buffers + + + Host Debug Markers + Host Debug Markers + + + Guest Debug Markers + Guest Debug Markers + Update Opdatering @@ -720,6 +740,10 @@ GUI Settings GUI-Indstillinger + + Title Music + Title Music + Disable Trophy Pop-ups Disable Trophy Pop-ups @@ -928,6 +952,26 @@ rdocCheckBox Aktiver RenderDoc-fejlfinding:\nHvis aktiveret, giver det emulatoren mulighed for kompatibilitet med Renderdoc til at fange og analysere det aktuelle gengivne billede. + + collectShaderCheckBox + Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + + + crashDiagnosticsCheckBox + Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + + + copyGPUBuffersCheckBox + Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + + + hostMarkersCheckBox + Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + + + guestMarkersCheckBox + Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + CheatsPatches diff --git a/src/qt_gui/translations/de.ts b/src/qt_gui/translations/de.ts index 9d99a7048..b8fa22881 100644 --- a/src/qt_gui/translations/de.ts +++ b/src/qt_gui/translations/de.ts @@ -700,6 +700,26 @@ Enable RenderDoc Debugging RenderDoc-Debugging aktivieren + + Enable Crash Diagnostics + Enable Crash Diagnostics + + + Collect Shaders + Collect Shaders + + + Copy GPU Buffers + Copy GPU Buffers + + + Host Debug Markers + Host Debug Markers + + + Guest Debug Markers + Guest Debug Markers + Update Aktualisieren @@ -720,6 +740,10 @@ GUI Settings GUI-Einstellungen + + Title Music + Title Music + Disable Trophy Pop-ups Disable Trophy Pop-ups @@ -928,6 +952,26 @@ rdocCheckBox RenderDoc-Debugging aktivieren:\nWenn aktiviert, bietet der Emulator Kompatibilität mit Renderdoc zur Erfassung und Analyse des aktuell gerenderten Frames. + + collectShaderCheckBox + Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + + + crashDiagnosticsCheckBox + Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + + + copyGPUBuffersCheckBox + Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + + + hostMarkersCheckBox + Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + + + guestMarkersCheckBox + Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + CheatsPatches diff --git a/src/qt_gui/translations/el.ts b/src/qt_gui/translations/el.ts index 66c3c07cd..828b99248 100644 --- a/src/qt_gui/translations/el.ts +++ b/src/qt_gui/translations/el.ts @@ -700,6 +700,26 @@ Enable RenderDoc Debugging Enable RenderDoc Debugging + + Enable Crash Diagnostics + Enable Crash Diagnostics + + + Collect Shaders + Collect Shaders + + + Copy GPU Buffers + Copy GPU Buffers + + + Host Debug Markers + Host Debug Markers + + + Guest Debug Markers + Guest Debug Markers + Update Ενημέρωση @@ -720,6 +740,10 @@ GUI Settings Ρυθμίσεις GUI + + Title Music + Title Music + Disable Trophy Pop-ups Disable Trophy Pop-ups @@ -928,6 +952,26 @@ rdocCheckBox Ενεργοποίηση Καταγραφής RenderDoc:\nΌταν είναι ενεργοποιημένο, ο εξομοιωτής είναι συμβατός με το RenderDoc για τη λήψη και ανάλυση του τρέχοντος καρέ. + + collectShaderCheckBox + Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + + + crashDiagnosticsCheckBox + Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + + + copyGPUBuffersCheckBox + Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + + + hostMarkersCheckBox + Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + + + guestMarkersCheckBox + Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + CheatsPatches diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index 8b6c3c69f..7ad2f15c0 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -700,6 +700,27 @@ Enable RenderDoc Debugging Enable RenderDoc Debugging + + Enable Crash Diagnostics + Enable Crash Diagnostics + + + Collect Shaders + Collect Shaders + + + Copy GPU Buffers + Copy GPU Buffers + + + Host Debug Markers + Host Debug Markers + + + Guest Debug Markers + Guest Debug Markers + + Update Update @@ -720,6 +741,10 @@ GUI Settings GUI Settings + + Title Music + Title Music + Disable Trophy Pop-ups Disable Trophy Pop-ups @@ -928,6 +953,26 @@ rdocCheckBox Enable RenderDoc Debugging:\nIf enabled, the emulator will provide compatibility with Renderdoc to allow capture and analysis of the currently rendered frame. + + collectShaderCheckBox + Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + + + crashDiagnosticsCheckBox + Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + + + copyGPUBuffersCheckBox + Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + + + hostMarkersCheckBox + Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + + + guestMarkersCheckBox + Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + saveDataBox Save Data Path:\nThe folder where game save data will be saved. @@ -1369,4 +1414,4 @@ TB - \ No newline at end of file + diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index 8f7a09b5d..8af34c042 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -700,6 +700,26 @@ Enable RenderDoc Debugging Habilitar depuración de RenderDoc + + Enable Crash Diagnostics + Enable Crash Diagnostics + + + Collect Shaders + Collect Shaders + + + Copy GPU Buffers + Copy GPU Buffers + + + Host Debug Markers + Host Debug Markers + + + Guest Debug Markers + Guest Debug Markers + Update Actualización @@ -720,6 +740,10 @@ GUI Settings Configuraciones de la Interfaz + + Title Music + Title Music + Disable Trophy Pop-ups Disable Trophy Pop-ups @@ -928,6 +952,26 @@ rdocCheckBox Habilitar Depuración de RenderDoc:\nSi se habilita, el emulador proporcionará compatibilidad con Renderdoc para permitir la captura y análisis del fotograma actualmente renderizado. + + collectShaderCheckBox + Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + + + crashDiagnosticsCheckBox + Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + + + copyGPUBuffersCheckBox + Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + + + hostMarkersCheckBox + Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + + + guestMarkersCheckBox + Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + CheatsPatches diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index 043d782c2..16f6533b6 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -700,6 +700,26 @@ Enable RenderDoc Debugging RenderDoc Debugging + + Enable Crash Diagnostics + Enable Crash Diagnostics + + + Collect Shaders + Collect Shaders + + + Copy GPU Buffers + Copy GPU Buffers + + + Host Debug Markers + Host Debug Markers + + + Guest Debug Markers + Guest Debug Markers + Update به‌روزرسانی @@ -720,6 +740,10 @@ GUI Settings تنظیمات رابط کاربری + + Title Music + Title Music + Disable Trophy Pop-ups غیرفعال کردن نمایش جوایز @@ -928,6 +952,26 @@ rdocCheckBox Enable RenderDoc Debugging:\nIf enabled, the emulator will provide compatibility with Renderdoc to allow capture and analysis of the currently rendered frame. + + collectShaderCheckBox + Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + + + crashDiagnosticsCheckBox + Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + + + copyGPUBuffersCheckBox + Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + + + hostMarkersCheckBox + Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + + + guestMarkersCheckBox + Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + CheatsPatches diff --git a/src/qt_gui/translations/fi.ts b/src/qt_gui/translations/fi.ts index 86f6a6d3c..7269b4125 100644 --- a/src/qt_gui/translations/fi.ts +++ b/src/qt_gui/translations/fi.ts @@ -700,6 +700,26 @@ Enable RenderDoc Debugging Ota Käyttöön RenderDoc Virheenkorjaus + + Enable Crash Diagnostics + Enable Crash Diagnostics + + + Collect Shaders + Collect Shaders + + + Copy GPU Buffers + Copy GPU Buffers + + + Host Debug Markers + Host Debug Markers + + + Guest Debug Markers + Guest Debug Markers + Update Päivitys @@ -720,6 +740,10 @@ GUI Settings GUI-asetukset + + Title Music + Title Music + Disable Trophy Pop-ups Poista Trophy Pop-upit Käytöstä @@ -928,6 +952,26 @@ rdocCheckBox Ota Käyttöön RenderDoc Virheenkorjaus:\nJos käytössä, emulaattori tarjoaa Renderdoc-yhteensopivuuden, mikä mahdollistaa renderöidyn kehyksen tallennuksen ja analysoinnin. + + collectShaderCheckBox + Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + + + crashDiagnosticsCheckBox + Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + + + copyGPUBuffersCheckBox + Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + + + hostMarkersCheckBox + Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + + + guestMarkersCheckBox + Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + CheatsPatches diff --git a/src/qt_gui/translations/fr.ts b/src/qt_gui/translations/fr.ts index 250b8f3c3..b93af15a6 100644 --- a/src/qt_gui/translations/fr.ts +++ b/src/qt_gui/translations/fr.ts @@ -700,6 +700,26 @@ Enable RenderDoc Debugging Activer le débogage RenderDoc + + Enable Crash Diagnostics + Enable Crash Diagnostics + + + Collect Shaders + Collect Shaders + + + Copy GPU Buffers + Copy GPU Buffers + + + Host Debug Markers + Host Debug Markers + + + Guest Debug Markers + Guest Debug Markers + Update Mise à jour @@ -720,6 +740,10 @@ GUI Settings Paramètres de l'interface + + Title Music + Title Music + Disable Trophy Pop-ups Désactiver les notifications de trophées @@ -928,6 +952,26 @@ rdocCheckBox Activer le débogage RenderDoc:\nS'il est activé, l'émulateur fournit une compatibilité avec Renderdoc, permettant d'enregistrer et d'analyser la trame rendue actuelle. + + collectShaderCheckBox + Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + + + crashDiagnosticsCheckBox + Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + + + copyGPUBuffersCheckBox + Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + + + hostMarkersCheckBox + Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + + + guestMarkersCheckBox + Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + CheatsPatches diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index fe71e8120..98491aa87 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -700,6 +700,26 @@ Enable RenderDoc Debugging RenderDoc Debugolás Engedélyezése + + Enable Crash Diagnostics + Enable Crash Diagnostics + + + Collect Shaders + Collect Shaders + + + Copy GPU Buffers + Copy GPU Buffers + + + Host Debug Markers + Host Debug Markers + + + Guest Debug Markers + Guest Debug Markers + Update Frissítés @@ -720,6 +740,10 @@ GUI Settings GUI Beállítások + + Title Music + Title Music + Disable Trophy Pop-ups Disable Trophy Pop-ups @@ -928,6 +952,26 @@ rdocCheckBox RenderDoc hibakeresés engedélyezése:\nHa engedélyezve van, az emulátor kompatibilitást biztosít a Renderdoc számára, hogy lehetővé tegye a jelenleg renderelt keret rögzítését és elemzését. + + collectShaderCheckBox + Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + + + crashDiagnosticsCheckBox + Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + + + copyGPUBuffersCheckBox + Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + + + hostMarkersCheckBox + Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + + + guestMarkersCheckBox + Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + CheatsPatches diff --git a/src/qt_gui/translations/id.ts b/src/qt_gui/translations/id.ts index acfde43a7..931244209 100644 --- a/src/qt_gui/translations/id.ts +++ b/src/qt_gui/translations/id.ts @@ -700,6 +700,26 @@ Enable RenderDoc Debugging Enable RenderDoc Debugging + + Enable Crash Diagnostics + Enable Crash Diagnostics + + + Collect Shaders + Collect Shaders + + + Copy GPU Buffers + Copy GPU Buffers + + + Host Debug Markers + Host Debug Markers + + + Guest Debug Markers + Guest Debug Markers + Update Pembaruan @@ -720,6 +740,10 @@ GUI Settings Pengaturan GUI + + Title Music + Title Music + Disable Trophy Pop-ups Disable Trophy Pop-ups @@ -928,6 +952,26 @@ rdocCheckBox Aktifkan Debugging RenderDoc:\nJika diaktifkan, emulator akan menyediakan kompatibilitas dengan Renderdoc untuk memungkinkan pengambilan dan analisis bingkai yang sedang dirender. + + collectShaderCheckBox + Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + + + crashDiagnosticsCheckBox + Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + + + copyGPUBuffersCheckBox + Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + + + hostMarkersCheckBox + Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + + + guestMarkersCheckBox + Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + CheatsPatches diff --git a/src/qt_gui/translations/it.ts b/src/qt_gui/translations/it.ts index 93c6233a6..384272137 100644 --- a/src/qt_gui/translations/it.ts +++ b/src/qt_gui/translations/it.ts @@ -700,6 +700,26 @@ Enable RenderDoc Debugging Abilita RenderDoc Debugging + + Enable Crash Diagnostics + Enable Crash Diagnostics + + + Collect Shaders + Collect Shaders + + + Copy GPU Buffers + Copy GPU Buffers + + + Host Debug Markers + Host Debug Markers + + + Guest Debug Markers + Guest Debug Markers + Update Aggiornamento @@ -720,6 +740,10 @@ GUI Settings Impostazioni GUI + + Title Music + Title Music + Disable Trophy Pop-ups Disabilita Notifica Trofei @@ -928,6 +952,26 @@ rdocCheckBox Abilita Debugging RenderDoc:\nSe abilitato, l'emulatore fornirà compatibilità con Renderdoc per consentire la cattura e l'analisi del frame attualmente reso. + + collectShaderCheckBox + Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + + + crashDiagnosticsCheckBox + Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + + + copyGPUBuffersCheckBox + Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + + + hostMarkersCheckBox + Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + + + guestMarkersCheckBox + Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + CheatsPatches diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index 921af52bb..2aae35987 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -700,6 +700,26 @@ Enable RenderDoc Debugging RenderDocデバッグを有効にする + + Enable Crash Diagnostics + Enable Crash Diagnostics + + + Collect Shaders + Collect Shaders + + + Copy GPU Buffers + Copy GPU Buffers + + + Host Debug Markers + Host Debug Markers + + + Guest Debug Markers + Guest Debug Markers + Update 更新 @@ -720,6 +740,10 @@ GUI Settings GUI設定 + + Title Music + Title Music + Disable Trophy Pop-ups トロフィーのポップアップを無効化 @@ -928,6 +952,26 @@ rdocCheckBox RenderDocデバッグを有効にする:\n有効にすると、エミュレーターはRenderdocとの互換性を提供し、現在レンダリング中のフレームのキャプチャと分析を可能にします。 + + collectShaderCheckBox + Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + + + crashDiagnosticsCheckBox + Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + + + copyGPUBuffersCheckBox + Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + + + hostMarkersCheckBox + Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + + + guestMarkersCheckBox + Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + CheatsPatches diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts index 2491959a6..56e891214 100644 --- a/src/qt_gui/translations/ko_KR.ts +++ b/src/qt_gui/translations/ko_KR.ts @@ -700,6 +700,26 @@ Enable RenderDoc Debugging Enable RenderDoc Debugging + + Enable Crash Diagnostics + Enable Crash Diagnostics + + + Collect Shaders + Collect Shaders + + + Copy GPU Buffers + Copy GPU Buffers + + + Host Debug Markers + Host Debug Markers + + + Guest Debug Markers + Guest Debug Markers + Update Update @@ -720,6 +740,10 @@ GUI Settings GUI Settings + + Title Music + Title Music + Disable Trophy Pop-ups Disable Trophy Pop-ups @@ -928,6 +952,26 @@ rdocCheckBox Enable RenderDoc Debugging:\nIf enabled, the emulator will provide compatibility with Renderdoc to allow capture and analysis of the currently rendered frame. + + collectShaderCheckBox + Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + + + crashDiagnosticsCheckBox + Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + + + copyGPUBuffersCheckBox + Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + + + hostMarkersCheckBox + Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + + + guestMarkersCheckBox + Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + CheatsPatches diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts index 23a52e948..c73a43917 100644 --- a/src/qt_gui/translations/lt_LT.ts +++ b/src/qt_gui/translations/lt_LT.ts @@ -700,6 +700,26 @@ Enable RenderDoc Debugging Enable RenderDoc Debugging + + Enable Crash Diagnostics + Enable Crash Diagnostics + + + Collect Shaders + Collect Shaders + + + Copy GPU Buffers + Copy GPU Buffers + + + Host Debug Markers + Host Debug Markers + + + Guest Debug Markers + Guest Debug Markers + Update Atnaujinimas @@ -720,6 +740,10 @@ GUI Settings GUI Nustatymai + + Title Music + Title Music + Disable Trophy Pop-ups Disable Trophy Pop-ups @@ -928,6 +952,26 @@ rdocCheckBox Įjungti RenderDoc derinimą:\nJei įjungta, emuliatorius suteiks suderinamumą su Renderdoc, kad būtų galima užfiksuoti ir analizuoti šiuo metu renderuojamą kadrą. + + collectShaderCheckBox + Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + + + crashDiagnosticsCheckBox + Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + + + copyGPUBuffersCheckBox + Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + + + hostMarkersCheckBox + Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + + + guestMarkersCheckBox + Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + CheatsPatches diff --git a/src/qt_gui/translations/nb.ts b/src/qt_gui/translations/nb.ts index 0b7d2699a..3e6f5fedd 100644 --- a/src/qt_gui/translations/nb.ts +++ b/src/qt_gui/translations/nb.ts @@ -700,6 +700,26 @@ Enable RenderDoc Debugging Aktiver RenderDoc feilretting + + Enable Crash Diagnostics + Enable Crash Diagnostics + + + Collect Shaders + Collect Shaders + + + Copy GPU Buffers + Copy GPU Buffers + + + Host Debug Markers + Host Debug Markers + + + Guest Debug Markers + Guest Debug Markers + Update Oppdatering @@ -720,6 +740,10 @@ GUI Settings Grensesnitt-innstillinger + + Title Music + Title Music + Disable Trophy Pop-ups Deaktiver trofé hurtigmeny @@ -928,6 +952,26 @@ rdocCheckBox Aktiver RenderDoc feilsøking:\nHvis aktivert, vil etterligneren gi kompatibilitet med Renderdoc for å tillate opptak og analyse av det nåværende gjengitte bildet. + + collectShaderCheckBox + Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + + + crashDiagnosticsCheckBox + Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + + + copyGPUBuffersCheckBox + Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + + + hostMarkersCheckBox + Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + + + guestMarkersCheckBox + Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + CheatsPatches diff --git a/src/qt_gui/translations/nl.ts b/src/qt_gui/translations/nl.ts index 1dabda8b4..95ac19ef3 100644 --- a/src/qt_gui/translations/nl.ts +++ b/src/qt_gui/translations/nl.ts @@ -700,6 +700,26 @@ Enable RenderDoc Debugging Enable RenderDoc Debugging + + Enable Crash Diagnostics + Enable Crash Diagnostics + + + Collect Shaders + Collect Shaders + + + Copy GPU Buffers + Copy GPU Buffers + + + Host Debug Markers + Host Debug Markers + + + Guest Debug Markers + Guest Debug Markers + Update Bijwerken @@ -720,6 +740,10 @@ GUI Settings GUI-Instellingen + + Title Music + Title Music + Disable Trophy Pop-ups Disable Trophy Pop-ups @@ -928,6 +952,26 @@ rdocCheckBox RenderDoc foutopsporing inschakelen:\nAls ingeschakeld, biedt de emulator compatibiliteit met Renderdoc om de momenteel gerenderde frame vast te leggen en te analyseren. + + collectShaderCheckBox + Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + + + crashDiagnosticsCheckBox + Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + + + copyGPUBuffersCheckBox + Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + + + hostMarkersCheckBox + Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + + + guestMarkersCheckBox + Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + CheatsPatches diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index 85eb63bfb..89f165de2 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -700,6 +700,26 @@ Enable RenderDoc Debugging Włącz debugowanie RenderDoc + + Enable Crash Diagnostics + Enable Crash Diagnostics + + + Collect Shaders + Collect Shaders + + + Copy GPU Buffers + Copy GPU Buffers + + + Host Debug Markers + Host Debug Markers + + + Guest Debug Markers + Guest Debug Markers + Update Aktualizacja @@ -720,6 +740,10 @@ GUI Settings Ustawienia Interfejsu + + Title Music + Title Music + Disable Trophy Pop-ups Wyłącz wyskakujące okienka trofeów @@ -928,6 +952,26 @@ rdocCheckBox Włącz debugowanie RenderDoc:\nJeśli włączone, emulator zapewnia kompatybilność z Renderdoc, aby umożliwić nagrywanie i analizowanie aktualnie renderowanej klatki. + + collectShaderCheckBox + Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + + + crashDiagnosticsCheckBox + Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + + + copyGPUBuffersCheckBox + Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + + + hostMarkersCheckBox + Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + + + guestMarkersCheckBox + Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + CheatsPatches diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index fde1d3b63..0bce16dcf 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -538,7 +538,7 @@ Enable Fullscreen - Ativar Tela Cheia + Habilitar Tela Cheia Fullscreen Mode @@ -566,7 +566,7 @@ Enable Discord Rich Presence - Ativar Discord Rich Presence + Habilitar Discord Rich Presence Username @@ -574,7 +574,7 @@ Trophy Key - Trophy Key + Chave de Troféu Trophy @@ -582,7 +582,7 @@ Logger - Registro + Registro-Log Log Type @@ -594,7 +594,7 @@ Open Log Location - Abrir local do log + Abrir local do registro Input @@ -658,11 +658,11 @@ Enable Shaders Dumping - Ativar Dumping de Shaders + Habilitar Dumping de Shaders Enable NULL GPU - Ativar GPU NULA + Habilitar GPU NULA Paths @@ -686,19 +686,39 @@ Enable Debug Dumping - Ativar Depuração de Dumping + Habilitar Depuração de Dumping Enable Vulkan Validation Layers - Ativar Camadas de Validação do Vulkan + Habilitar Camadas de Validação do Vulkan Enable Vulkan Synchronization Validation - Ativar Validação de Sincronização do Vulkan + Habilitar Validação de Sincronização do Vulkan Enable RenderDoc Debugging - Ativar Depuração por RenderDoc + Habilitar Depuração do RenderDoc + + + Enable Crash Diagnostics + Habilitar Diagnóstico de Falhas + + + Collect Shaders + Coletar Shaders + + + Copy GPU Buffers + Copiar Buffers de GPU + + + Host Debug Markers + Marcadores de Depuração do Host + + + Guest Debug Markers + Marcadores de Depuração do Convidado Update @@ -720,6 +740,10 @@ GUI Settings Configurações da Interface + + Title Music + Música no Menu + Disable Trophy Pop-ups Desabilitar Pop-ups dos Troféus @@ -782,7 +806,7 @@ fullscreenCheckBox - Ativar Tela Cheia:\nAltera a janela do jogo para o modo tela cheia.\nIsso pode ser alterado pressionando a tecla F11. + Habilitar Tela Cheia:\nAltera a janela do jogo para o modo tela cheia.\nIsso pode ser alterado pressionando a tecla F11. separateUpdatesCheckBox @@ -798,7 +822,7 @@ discordRPCCheckbox - Ativar Discord Rich Presence:\nExibe o ícone do emulador e informações relevantes no seu perfil do Discord. + Habilitar Discord Rich Presence:\nExibe o ícone do emulador e informações relevantes no seu perfil do Discord. userName @@ -898,11 +922,11 @@ dumpShadersCheckBox - Ativar Dumping de Shaders:\nArmazena os shaders do jogo em uma pasta durante a renderização para fins de depuração técnica. + Habilitar Dumping de Shaders:\nArmazena os shaders do jogo em uma pasta durante a renderização para fins de depuração técnica. nullGpuCheckBox - Ativar GPU NULA:\nDesativa a renderização do jogo para fins de depuração técnica, como se não houvesse nenhuma placa gráfica. + Habilitar GPU NULA:\nDesativa a renderização do jogo para fins de depuração técnica, como se não houvesse nenhuma placa gráfica. gameFoldersBox @@ -918,19 +942,39 @@ debugDump - Ativar Depuração de Dumping:\nArmazena os símbolos de importação e exportação e as informações do cabeçalho do arquivo do programa PS4 atual em um diretório. + Habilitar Depuração de Dumping:\nArmazena os símbolos de importação e exportação e as informações do cabeçalho do arquivo do programa PS4 atual em um diretório. vkValidationCheckBox - Ativar Camadas de Validação do Vulkan:\nAtiva um sistema que valida o estado do renderizador Vulkan e registra informações sobre seu estado interno.\nIsso diminui o desempenho e pode alterar o comportamento da emulação. + Habilitar Camadas de Validação do Vulkan:\nAtiva um sistema que valida o estado do renderizador Vulkan e registra informações sobre seu estado interno.\nIsso diminui o desempenho e pode alterar o comportamento da emulação. vkSyncValidationCheckBox - Ativar Validação de Sincronização do Vulkan:\nAtiva um sistema que valida o agendamento de tarefas de renderização Vulkan.\nIsso diminui o desempenho e pode alterar o comportamento da emulação. + Habilitar Validação de Sincronização do Vulkan:\nAtiva um sistema que valida o agendamento de tarefas de renderização Vulkan.\nIsso diminui o desempenho e pode alterar o comportamento da emulação. rdocCheckBox - Ativar depuração por RenderDoc:\nSe ativado, permite que o emulador tenha compatibilidade com RenderDoc para gravação e análise do quadro renderizado atual. + Habilitar Depuração por RenderDoc:\nSe ativado, permite que o emulador tenha compatibilidade com RenderDoc para gravação e análise do quadro renderizado atual. + + + collectShaderCheckBox + Coletar Shaders:\nVocê precisa habilitar isso para editar shaders com o menu de depuração (Ctrl + F10). + + + crashDiagnosticsCheckBox + Diagnósticos de Falha:\nCria um arquivo .yaml com informações sobre o estado do Vulkan no momento da falha.\nÚtil para depurar erros de 'Device lost'. Se você tiver isso habilitado, você deve habilitar os Marcadores de Depuração de Host e de Convidado.\nNão funciona em GPUs da Intel.\nVocê precisa ter as Camadas de Validação Vulkan habilitadas e o SDK do Vulkan para que isso funcione. + + + copyGPUBuffersCheckBox + Copiar Buffers de GPU:\nContorna condições de corrida envolvendo envios de GPU.\nPode ou não ajudar com travamentos do PM4 tipo 0. + + + hostMarkersCheckBox + Marcadores de Depuração de Host:\nInsere informações do lado do emulador, como marcadores para comandos AMDGPU específicos em torno de comandos Vulkan, além de fornecer nomes de depuração aos recursos.\nSe isso estiver habilitado, você deve habilitar o "Diagnóstico de Falha".\nÚtil para programas como o RenderDoc. + + + guestMarkersCheckBox + Marcadores de Depuração de Convidado:\nInsere quaisquer marcadores de depuração que o próprio jogo adicionou ao buffer de comando.\nSe isso estiver habilitado, você deve habilitar "Diagnóstico de Falha".\nÚtil para programas como o RenderDoc. diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index 9791a7682..f60de9823 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -700,6 +700,26 @@ Enable RenderDoc Debugging Enable RenderDoc Debugging + + Enable Crash Diagnostics + Enable Crash Diagnostics + + + Collect Shaders + Collect Shaders + + + Copy GPU Buffers + Copy GPU Buffers + + + Host Debug Markers + Host Debug Markers + + + Guest Debug Markers + Guest Debug Markers + Update Actualizare @@ -720,6 +740,10 @@ GUI Settings Setări GUI + + Title Music + Title Music + Disable Trophy Pop-ups Disable Trophy Pop-ups @@ -928,6 +952,26 @@ rdocCheckBox Activează depanarea RenderDoc:\nDacă este activat, emulatorul va oferi compatibilitate cu Renderdoc pentru a permite capturarea și analiza cadrului redat în prezent. + + collectShaderCheckBox + Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + + + crashDiagnosticsCheckBox + Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + + + copyGPUBuffersCheckBox + Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + + + hostMarkersCheckBox + Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + + + guestMarkersCheckBox + Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + CheatsPatches diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index 0fddb1e7f..270396a6d 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -700,6 +700,26 @@ Enable RenderDoc Debugging Включить отладку RenderDoc + + Enable Crash Diagnostics + Enable Crash Diagnostics + + + Collect Shaders + Collect Shaders + + + Copy GPU Buffers + Copy GPU Buffers + + + Host Debug Markers + Host Debug Markers + + + Guest Debug Markers + Guest Debug Markers + Update Обновление @@ -720,6 +740,10 @@ GUI Settings Интерфейс + + Title Music + Title Music + Disable Trophy Pop-ups Отключить уведомления о трофеях @@ -928,6 +952,26 @@ rdocCheckBox Включить отладку RenderDoc:\nЕсли включено, эмулятор обеспечит совместимость с Renderdoc, позволяя захватывать и анализировать текущие кадры во время рендеринга. + + collectShaderCheckBox + Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + + + crashDiagnosticsCheckBox + Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + + + copyGPUBuffersCheckBox + Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + + + hostMarkersCheckBox + Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + + + guestMarkersCheckBox + Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + CheatsPatches diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index 15c18d2f6..1d37fa9c3 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -700,6 +700,26 @@ Enable RenderDoc Debugging Aktivizo Korrigjimin RenderDoc + + Enable Crash Diagnostics + Enable Crash Diagnostics + + + Collect Shaders + Collect Shaders + + + Copy GPU Buffers + Copy GPU Buffers + + + Host Debug Markers + Host Debug Markers + + + Guest Debug Markers + Guest Debug Markers + Update Përditëso @@ -720,6 +740,10 @@ GUI Settings Cilësimet e GUI-së + + Title Music + Title Music + Disable Trophy Pop-ups Çaktivizo njoftimet për Trofetë @@ -928,6 +952,26 @@ rdocCheckBox Aktivizo korrigjimin RenderDoc:\nNëse aktivizohet, emulatori do të ofrojë pajtueshmëri me Renderdoc për të lejuar kapjen dhe analizën e pamjes të pasqyruar në moment. + + collectShaderCheckBox + Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + + + crashDiagnosticsCheckBox + Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + + + copyGPUBuffersCheckBox + Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + + + hostMarkersCheckBox + Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + + + guestMarkersCheckBox + Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + CheatsPatches diff --git a/src/qt_gui/translations/sv.ts b/src/qt_gui/translations/sv.ts index e17944f12..c5fbce991 100644 --- a/src/qt_gui/translations/sv.ts +++ b/src/qt_gui/translations/sv.ts @@ -1188,6 +1188,26 @@ Enable RenderDoc Debugging Aktivera RenderDoc-felsökning + + Enable Crash Diagnostics + Enable Crash Diagnostics + + + Collect Shaders + Collect Shaders + + + Copy GPU Buffers + Copy GPU Buffers + + + Host Debug Markers + Host Debug Markers + + + Guest Debug Markers + Guest Debug Markers + Update Uppdatera @@ -1208,6 +1228,10 @@ GUI Settings Gränssnittsinställningar + + Title Music + Title Music + Disable Trophy Pop-ups Inaktivera popup för troféer @@ -1408,6 +1432,26 @@ rdocCheckBox Aktivera RenderDoc-felsökning:\nOm aktiverad kommer emulatorn att tillhandahålla kompatibilitet med Renderdoc för att tillåta fångst och analys för aktuell renderad bildruta + + collectShaderCheckBox + Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + + + crashDiagnosticsCheckBox + Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + + + copyGPUBuffersCheckBox + Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + + + hostMarkersCheckBox + Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + + + guestMarkersCheckBox + Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + Release Release diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index a2368bfee..12794e088 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -700,6 +700,26 @@ Enable RenderDoc Debugging RenderDoc Hata Ayıklamayı Etkinleştir + + Enable Crash Diagnostics + Enable Crash Diagnostics + + + Collect Shaders + Collect Shaders + + + Copy GPU Buffers + Copy GPU Buffers + + + Host Debug Markers + Host Debug Markers + + + Guest Debug Markers + Guest Debug Markers + Update Güncelle @@ -720,6 +740,10 @@ GUI Settings GUI Ayarları + + Title Music + Title Music + Disable Trophy Pop-ups Kupa Açılır Pencerelerini Devre Dışı Bırak @@ -928,6 +952,26 @@ rdocCheckBox RenderDoc Hata Ayıklamayı Etkinleştir:\nEğer etkinleştirilirse, emülatör mevcut render edilmiş çerçeveyi yakalamak ve analiz etmek için Renderdoc ile uyumluluk sunar. + + collectShaderCheckBox + Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + + + crashDiagnosticsCheckBox + Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + + + copyGPUBuffersCheckBox + Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + + + hostMarkersCheckBox + Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + + + guestMarkersCheckBox + Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + CheatsPatches diff --git a/src/qt_gui/translations/uk_UA.ts b/src/qt_gui/translations/uk_UA.ts index 720ad5b99..e80d363d3 100644 --- a/src/qt_gui/translations/uk_UA.ts +++ b/src/qt_gui/translations/uk_UA.ts @@ -700,6 +700,26 @@ Enable RenderDoc Debugging Увімкнути налагодження RenderDoc + + Enable Crash Diagnostics + Enable Crash Diagnostics + + + Collect Shaders + Collect Shaders + + + Copy GPU Buffers + Copy GPU Buffers + + + Host Debug Markers + Host Debug Markers + + + Guest Debug Markers + Guest Debug Markers + Update Оновлення @@ -720,6 +740,10 @@ GUI Settings Інтерфейс + + Title Music + Title Music + Disable Trophy Pop-ups Disable Trophy Pop-ups @@ -928,6 +952,26 @@ rdocCheckBox Увімкнути налагодження RenderDoc:\nЯкщо увімкнено, емулятор забезпечить сумісність із Renderdoc, даючи змогу захоплювати й аналізувати поточні кадри під час рендерингу. + + collectShaderCheckBox + Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + + + crashDiagnosticsCheckBox + Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + + + copyGPUBuffersCheckBox + Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + + + hostMarkersCheckBox + Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + + + guestMarkersCheckBox + Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + CheatsPatches diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index a81630fad..32841af81 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -700,6 +700,26 @@ Enable RenderDoc Debugging Enable RenderDoc Debugging + + Enable Crash Diagnostics + Enable Crash Diagnostics + + + Collect Shaders + Collect Shaders + + + Copy GPU Buffers + Copy GPU Buffers + + + Host Debug Markers + Host Debug Markers + + + Guest Debug Markers + Guest Debug Markers + Update Cập nhật @@ -720,6 +740,10 @@ GUI Settings Cài đặt GUI + + Title Music + Title Music + Disable Trophy Pop-ups Disable Trophy Pop-ups @@ -928,6 +952,26 @@ rdocCheckBox Bật gỡ lỗi RenderDoc:\nNếu được kích hoạt, trình giả lập sẽ cung cấp tính tương thích với Renderdoc để cho phép bắt và phân tích khung hình hiện tại đang được kết xuất. + + collectShaderCheckBox + Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + + + crashDiagnosticsCheckBox + Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + + + copyGPUBuffersCheckBox + Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + + + hostMarkersCheckBox + Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + + + guestMarkersCheckBox + Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + CheatsPatches diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index 16ae03f94..00f4337c0 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -700,6 +700,26 @@ Enable RenderDoc Debugging 启用 RenderDoc 调试 + + Enable Crash Diagnostics + Enable Crash Diagnostics + + + Collect Shaders + Collect Shaders + + + Copy GPU Buffers + Copy GPU Buffers + + + Host Debug Markers + Host Debug Markers + + + Guest Debug Markers + Guest Debug Markers + Update 更新 @@ -720,6 +740,10 @@ GUI Settings 界面设置 + + Title Music + Title Music + Disable Trophy Pop-ups 禁止弹出奖杯 @@ -928,6 +952,26 @@ rdocCheckBox 启用 RenderDoc 调试:\n启用后模拟器将提供与 Renderdoc 的兼容性,允许在渲染过程中捕获和分析当前渲染的帧。 + + collectShaderCheckBox + Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + + + crashDiagnosticsCheckBox + Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + + + copyGPUBuffersCheckBox + Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + + + hostMarkersCheckBox + Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + + + guestMarkersCheckBox + Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + saveDataBox 存档数据路径:\n保存游戏存档数据的目录。 diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index 9f6758797..c18e173e4 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -700,6 +700,26 @@ Enable RenderDoc Debugging Enable RenderDoc Debugging + + Enable Crash Diagnostics + Enable Crash Diagnostics + + + Collect Shaders + Collect Shaders + + + Copy GPU Buffers + Copy GPU Buffers + + + Host Debug Markers + Host Debug Markers + + + Guest Debug Markers + Guest Debug Markers + Update 更新 @@ -720,6 +740,10 @@ GUI Settings 介面設置 + + Title Music + Title Music + Disable Trophy Pop-ups Disable Trophy Pop-ups @@ -928,6 +952,26 @@ rdocCheckBox 啟用RenderDoc調試:\n如果啟用,模擬器將提供與Renderdoc的兼容性,以允許捕獲和分析當前渲染的幀。 + + collectShaderCheckBox + Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + + + crashDiagnosticsCheckBox + Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + + + copyGPUBuffersCheckBox + Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + + + hostMarkersCheckBox + Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + + + guestMarkersCheckBox + Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + CheatsPatches diff --git a/src/video_core/renderer_vulkan/vk_platform.h b/src/video_core/renderer_vulkan/vk_platform.h index d05d12997..4e9587e46 100644 --- a/src/video_core/renderer_vulkan/vk_platform.h +++ b/src/video_core/renderer_vulkan/vk_platform.h @@ -33,7 +33,7 @@ concept VulkanHandleType = vk::isVulkanHandleType::value; template void SetObjectName(vk::Device device, const HandleType& handle, std::string_view debug_name) { - if (!Config::vkHostMarkersEnabled()) { + if (!Config::getVkHostMarkersEnabled()) { return; } const vk::DebugUtilsObjectNameInfoEXT name_info = { @@ -50,7 +50,7 @@ void SetObjectName(vk::Device device, const HandleType& handle, std::string_view template void SetObjectName(vk::Device device, const HandleType& handle, const char* format, const Args&... args) { - if (!Config::vkHostMarkersEnabled()) { + if (!Config::getVkHostMarkersEnabled()) { return; } const std::string debug_name = fmt::vformat(format, fmt::make_format_args(args...)); diff --git a/src/video_core/renderer_vulkan/vk_presenter.cpp b/src/video_core/renderer_vulkan/vk_presenter.cpp index 35ab4318a..c2be1c3e8 100644 --- a/src/video_core/renderer_vulkan/vk_presenter.cpp +++ b/src/video_core/renderer_vulkan/vk_presenter.cpp @@ -294,7 +294,7 @@ void Presenter::CreatePostProcessPipeline() { Presenter::Presenter(Frontend::WindowSDL& window_, AmdGpu::Liverpool* liverpool_) : window{window_}, liverpool{liverpool_}, instance{window, Config::getGpuId(), Config::vkValidationEnabled(), - Config::vkCrashDiagnosticEnabled()}, + Config::getVkCrashDiagnosticEnabled()}, draw_scheduler{instance}, present_scheduler{instance}, flip_scheduler{instance}, swapchain{instance, window}, rasterizer{std::make_unique(instance, draw_scheduler, liverpool)}, @@ -467,7 +467,7 @@ bool Presenter::ShowSplash(Frame* frame /*= nullptr*/) { draw_scheduler.EndRendering(); const auto cmdbuf = draw_scheduler.CommandBuffer(); - if (Config::vkHostMarkersEnabled()) { + if (Config::getVkHostMarkersEnabled()) { cmdbuf.beginDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT{ .pLabelName = "ShowSplash", }); @@ -541,7 +541,7 @@ bool Presenter::ShowSplash(Frame* frame /*= nullptr*/) { .pImageMemoryBarriers = &post_barrier, }); - if (Config::vkHostMarkersEnabled()) { + if (Config::getVkHostMarkersEnabled()) { cmdbuf.endDebugUtilsLabelEXT(); } @@ -573,7 +573,7 @@ Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop) auto& scheduler = is_eop ? draw_scheduler : flip_scheduler; scheduler.EndRendering(); const auto cmdbuf = scheduler.CommandBuffer(); - if (Config::vkHostMarkersEnabled()) { + if (Config::getVkHostMarkersEnabled()) { const auto label = fmt::format("PrepareFrameInternal:{}", image_id.index); cmdbuf.beginDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT{ .pLabelName = label.c_str(), @@ -704,7 +704,7 @@ Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop) .pImageMemoryBarriers = &post_barrier, }); - if (Config::vkHostMarkersEnabled()) { + if (Config::getVkHostMarkersEnabled()) { cmdbuf.endDebugUtilsLabelEXT(); } @@ -755,7 +755,7 @@ void Presenter::Present(Frame* frame, bool is_reusing_frame) { auto& scheduler = present_scheduler; const auto cmdbuf = scheduler.CommandBuffer(); - if (Config::vkHostMarkersEnabled()) { + if (Config::getVkHostMarkersEnabled()) { cmdbuf.beginDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT{ .pLabelName = "Present", }); @@ -857,7 +857,7 @@ void Presenter::Present(Frame* frame, bool is_reusing_frame) { } } - if (Config::vkHostMarkersEnabled()) { + if (Config::getVkHostMarkersEnabled()) { cmdbuf.endDebugUtilsLabelEXT(); } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 7c77f2519..fde87fcfb 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -1239,8 +1239,8 @@ void Rasterizer::UpdateViewportScissorState(const GraphicsPipeline& pipeline) { } void Rasterizer::ScopeMarkerBegin(const std::string_view& str, bool from_guest) { - if ((from_guest && !Config::vkGuestMarkersEnabled()) || - (!from_guest && !Config::vkHostMarkersEnabled())) { + if ((from_guest && !Config::getVkGuestMarkersEnabled()) || + (!from_guest && !Config::getVkHostMarkersEnabled())) { return; } const auto cmdbuf = scheduler.CommandBuffer(); @@ -1250,8 +1250,8 @@ void Rasterizer::ScopeMarkerBegin(const std::string_view& str, bool from_guest) } void Rasterizer::ScopeMarkerEnd(bool from_guest) { - if ((from_guest && !Config::vkGuestMarkersEnabled()) || - (!from_guest && !Config::vkHostMarkersEnabled())) { + if ((from_guest && !Config::getVkGuestMarkersEnabled()) || + (!from_guest && !Config::getVkHostMarkersEnabled())) { return; } const auto cmdbuf = scheduler.CommandBuffer(); @@ -1259,8 +1259,8 @@ void Rasterizer::ScopeMarkerEnd(bool from_guest) { } void Rasterizer::ScopedMarkerInsert(const std::string_view& str, bool from_guest) { - if ((from_guest && !Config::vkGuestMarkersEnabled()) || - (!from_guest && !Config::vkHostMarkersEnabled())) { + if ((from_guest && !Config::getVkGuestMarkersEnabled()) || + (!from_guest && !Config::getVkHostMarkersEnabled())) { return; } const auto cmdbuf = scheduler.CommandBuffer(); @@ -1271,8 +1271,8 @@ void Rasterizer::ScopedMarkerInsert(const std::string_view& str, bool from_guest void Rasterizer::ScopedMarkerInsertColor(const std::string_view& str, const u32 color, bool from_guest) { - if ((from_guest && !Config::vkGuestMarkersEnabled()) || - (!from_guest && !Config::vkHostMarkersEnabled())) { + if ((from_guest && !Config::getVkGuestMarkersEnabled()) || + (!from_guest && !Config::getVkHostMarkersEnabled())) { return; } const auto cmdbuf = scheduler.CommandBuffer(); From ad5bd91a13ec891395efdacfbc00cd78c2a58ea6 Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Thu, 30 Jan 2025 15:34:42 -0300 Subject: [PATCH 523/549] Fix game title sorting (#2286) * Fix game title sorting * fix * fix * fix --- src/qt_gui/game_list_frame.cpp | 9 +++++++-- src/qt_gui/game_list_frame.h | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/qt_gui/game_list_frame.cpp b/src/qt_gui/game_list_frame.cpp index 9753f511b..8255c0daf 100644 --- a/src/qt_gui/game_list_frame.cpp +++ b/src/qt_gui/game_list_frame.cpp @@ -69,7 +69,7 @@ GameListFrame::GameListFrame(std::shared_ptr game_info_get, ListSortedAsc = true; } this->clearContents(); - PopulateGameList(); + PopulateGameList(false); }); connect(this, &QTableWidget::customContextMenuRequested, this, [=, this](const QPoint& pos) { @@ -103,7 +103,7 @@ void GameListFrame::PlayBackgroundMusic(QTableWidgetItem* item) { BackgroundMusicPlayer::getInstance().playMusic(snd0path); } -void GameListFrame::PopulateGameList() { +void GameListFrame::PopulateGameList(bool isInitialPopulation) { // Do not show status column if it is not enabled this->setColumnHidden(2, !Config::getCompatibilityEnabled()); this->setColumnHidden(6, !Config::GetLoadGameSizeEnabled()); @@ -111,6 +111,11 @@ void GameListFrame::PopulateGameList() { this->setRowCount(m_game_info->m_games.size()); ResizeIcons(icon_size); + if (isInitialPopulation) { + SortNameAscending(1); // Column 1 = Name + ResizeIcons(icon_size); + } + for (int i = 0; i < m_game_info->m_games.size(); i++) { SetTableItem(i, 1, QString::fromStdString(m_game_info->m_games[i].name)); SetTableItem(i, 3, QString::fromStdString(m_game_info->m_games[i].serial)); diff --git a/src/qt_gui/game_list_frame.h b/src/qt_gui/game_list_frame.h index 90d021093..7e37c4ea7 100644 --- a/src/qt_gui/game_list_frame.h +++ b/src/qt_gui/game_list_frame.h @@ -46,7 +46,7 @@ private: bool ListSortedAsc = true; public: - void PopulateGameList(); + void PopulateGameList(bool isInitialPopulation = true); void ResizeIcons(int iconSize); QImage backgroundImage; From 52df7f6fe5bbb9a599c3fbc9e9de27a033c65426 Mon Sep 17 00:00:00 2001 From: Missake212 Date: Thu, 30 Jan 2025 21:18:45 +0100 Subject: [PATCH 524/549] add french tl (#2289) --- src/qt_gui/translations/fr.ts | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/qt_gui/translations/fr.ts b/src/qt_gui/translations/fr.ts index b93af15a6..f2ea4fcc7 100644 --- a/src/qt_gui/translations/fr.ts +++ b/src/qt_gui/translations/fr.ts @@ -702,23 +702,23 @@ Enable Crash Diagnostics - Enable Crash Diagnostics + Activer le diagnostic de crash Collect Shaders - Collect Shaders + Collecter les shaders Copy GPU Buffers - Copy GPU Buffers + Copier la mémoire tampon GPU Host Debug Markers - Host Debug Markers + Marqueur de débogage hôte Guest Debug Markers - Guest Debug Markers + Marqueur de débogage invité Update @@ -954,30 +954,30 @@ collectShaderCheckBox - Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + Collecter les Shaders:\nVous devez activer cette option pour modifier les shaders avec le menu de débogage (Ctrl + F10). crashDiagnosticsCheckBox - Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + Diagnostic de crash:\nCrée un fichier .yaml avec des informations sur l'état de Vulkan au moment du crash.\nUtile pour déboguer les erreurs "Device lost". Si cette option est activée, vous devez aussi activer Marqueur de débogage hôte ET invité.\nNe marche pas pour les GPUs Intel.\nVous devez activer le Vulkan Validation Layers ainsi que le Vulkan SDK pour que cela fonctionne. copyGPUBuffersCheckBox - Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + Copier la mémoire tampon GPU:\nContourne les conditions de course impliquant des soumissions GPU.\nPeut aider ou non en cas de crash PM4 type 0. hostMarkersCheckBox - Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + Marqueur de débogage hôte:\nInsère des informations côté émulateur telles que des marqueurs pour des commandes spécifiques AMDGPU autour des commandes Vulkan, ainsi que donner les noms de débogages des ressources.\nSi cette option est activée, vous devriez activer "Diagnostic de crash".\nUtile pour des programmes comme RenderDoc. guestMarkersCheckBox - Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + Marqueur de débogage invité:\nInsère tous les marqueurs de débogage que le jeu a ajouté a la commande mémoire tampon.\nSi cette option est activée, vous devriez activer "Diagnostic de crash".\nUtile pour des programmes comme RenderDoc. CheatsPatches Cheats / Patches for - Cheats/Patchs pour + Cheats / Patchs pour defaultTextEdit_MSG From c77f62a7380aa8a504420fa33b80f8fe38f3350c Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Thu, 30 Jan 2025 21:45:49 +0100 Subject: [PATCH 525/549] Detect and log if the user is using a fork (#2219) * Added fork detection * Fallback to "origin" if branch is not found * Add fork names to window titles * clang --- CMakeLists.txt | 24 ++++++++++++++++++++++++ src/common/scm_rev.cpp.in | 12 ++++++++---- src/common/scm_rev.h | 2 ++ src/emulator.cpp | 12 ++++++++++-- src/qt_gui/main_window.cpp | 11 +++++++++-- 5 files changed, 53 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 78e3c7997..340715e78 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -106,6 +106,30 @@ git_describe(GIT_DESC --always --long --dirty) git_branch_name(GIT_BRANCH) string(TIMESTAMP BUILD_DATE "%Y-%m-%d %H:%M:%S") +# Get current remote name and branch +execute_process( + COMMAND git rev-parse --abbrev-ref --symbolic-full-name @{u} + OUTPUT_VARIABLE GIT_REMOTE_NAME + RESULT_VARIABLE GIT_BRANCH_RESULT + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +# Default to origin if branch is not set +if (GIT_BRANCH_RESULT OR GIT_REMOTE_NAME STREQUAL "") + set(GIT_REMOTE_NAME "origin") +else() + # Extract remote name from full name + string(FIND "${GIT_REMOTE_NAME}" "/" INDEX) + string(SUBSTRING "${GIT_REMOTE_NAME}" 0 "${INDEX}" GIT_REMOTE_NAME) +endif() + +# Get remote link +execute_process( + COMMAND git config --get remote.${GIT_REMOTE_NAME}.url + OUTPUT_VARIABLE GIT_REMOTE_URL + OUTPUT_STRIP_TRAILING_WHITESPACE +) + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/common/scm_rev.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/src/common/scm_rev.cpp" @ONLY) find_package(Boost 1.84.0 CONFIG) diff --git a/src/common/scm_rev.cpp.in b/src/common/scm_rev.cpp.in index 642e6373d..2de04e0be 100644 --- a/src/common/scm_rev.cpp.in +++ b/src/common/scm_rev.cpp.in @@ -6,14 +6,18 @@ #define GIT_REV "@GIT_REV@" #define GIT_BRANCH "@GIT_BRANCH@" #define GIT_DESC "@GIT_DESC@" +#define GIT_REMOTE_NAME "@GIT_REMOTE_NAME@" +#define GIT_REMOTE_URL "@GIT_REMOTE_URL@" #define BUILD_DATE "@BUILD_DATE@" namespace Common { -const char g_scm_rev[] = GIT_REV; -const char g_scm_branch[] = GIT_BRANCH; -const char g_scm_desc[] = GIT_DESC; -const char g_scm_date[] = BUILD_DATE; +const char g_scm_rev[] = GIT_REV; +const char g_scm_branch[] = GIT_BRANCH; +const char g_scm_desc[] = GIT_DESC; +const char g_scm_remote_name[] = GIT_REMOTE_NAME; +const char g_scm_remote_url[] = GIT_REMOTE_URL; +const char g_scm_date[] = BUILD_DATE; } // namespace diff --git a/src/common/scm_rev.h b/src/common/scm_rev.h index 005099d2d..f38efff42 100644 --- a/src/common/scm_rev.h +++ b/src/common/scm_rev.h @@ -8,6 +8,8 @@ namespace Common { extern const char g_scm_rev[]; extern const char g_scm_branch[]; extern const char g_scm_desc[]; +extern const char g_scm_remote_name[]; +extern const char g_scm_remote_url[]; extern const char g_scm_date[]; } // namespace Common diff --git a/src/emulator.cpp b/src/emulator.cpp index 81c4d814d..3ab26d484 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -57,6 +57,7 @@ Emulator::Emulator() { LOG_INFO(Loader, "Revision {}", Common::g_scm_rev); LOG_INFO(Loader, "Branch {}", Common::g_scm_branch); LOG_INFO(Loader, "Description {}", Common::g_scm_desc); + LOG_INFO(Loader, "Remote {}", Common::g_scm_remote_url); LOG_INFO(Config, "General LogType: {}", Config::getLogType()); LOG_INFO(Config, "General isNeo: {}", Config::isNeoModeConsole()); @@ -199,8 +200,15 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector( Config::getScreenWidth(), Config::getScreenHeight(), controller, window_title); diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 3678b3a82..ce7b7b19e 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -57,8 +57,15 @@ bool MainWindow::Init() { if (Common::isRelease) { window_title = fmt::format("shadPS4 v{}", Common::VERSION); } else { - window_title = fmt::format("shadPS4 v{} {} {}", Common::VERSION, Common::g_scm_branch, - Common::g_scm_desc); + std::string remote_url(Common::g_scm_remote_url); + if (remote_url == "https://github.com/shadps4-emu/shadPS4.git") { + window_title = fmt::format("shadPS4 v{} {} {}", Common::VERSION, Common::g_scm_branch, + Common::g_scm_desc); + } else { + std::string remote_host = remote_url.substr(19, remote_url.rfind('/') - 19); + window_title = fmt::format("shadPS4 v{} {}/{} {}", Common::VERSION, remote_host, + Common::g_scm_branch, Common::g_scm_desc); + } } setWindowTitle(QString::fromStdString(window_title)); this->show(); From b5d63a31ccc9eefc98818577e87b418af225005e Mon Sep 17 00:00:00 2001 From: C4ndyF1sh <128715345+C4ndyF1sh@users.noreply.github.com> Date: Thu, 30 Jan 2025 23:05:50 +0100 Subject: [PATCH 526/549] update german ts (#2290) * Update de.ts (p1) * Update de.ts (p2) --- src/qt_gui/translations/de.ts | 110 +++++++++++++++++----------------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/src/qt_gui/translations/de.ts b/src/qt_gui/translations/de.ts index b8fa22881..d1ae21cca 100644 --- a/src/qt_gui/translations/de.ts +++ b/src/qt_gui/translations/de.ts @@ -52,7 +52,7 @@ Select which directory you want to install to. - Select which directory you want to install to. + Wählen Sie das Verzeichnis aus, in das Sie installieren möchten. @@ -130,35 +130,35 @@ Delete... - Delete... + Löschen... Delete Game - Delete Game + Lösche Spiel Delete Update - Delete Update + Lösche Aktualisierung Delete DLC - Delete DLC + Lösche DLC Compatibility... - Compatibility... + Kompatibilität... Update database - Update database + Aktualisiere Datenbank View report - View report + Bericht ansehen Submit a report - Submit a report + Einen Bericht einreichen Shortcut creation @@ -182,23 +182,23 @@ Game - Game + Spiel requiresEnableSeparateUpdateFolder_MSG - This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + Damit diese Funktion funktioniert, ist die Konfigurationsoption „Separaten Update-Ordner aktivieren“ erforderlich. Wenn Sie diese Funktion nutzen möchten, aktivieren Sie sie bitte. This game has no update to delete! - This game has no update to delete! + Dieses Spiel hat keine Aktualisierung zum löschen! Update - Update + Aktualisieren This game has no DLC to delete! - This game has no DLC to delete! + Dieses Spiel hat kein DLC zum aktualisieren! DLC @@ -206,11 +206,11 @@ Delete %1 - Delete %1 + Lösche %1 Are you sure you want to delete %1's %2 directory? - Are you sure you want to delete %1's %2 directory? + Sind Sie sicher dass Sie %1 %2 Ordner löschen wollen? @@ -249,7 +249,7 @@ Open shadPS4 Folder - Open shadPS4 Folder + Öffne shadPS4 Ordner Exit @@ -305,7 +305,7 @@ Download Cheats/Patches - Cheats / Patches herunterladen + Cheats/Patches herunterladen Dump Game List @@ -313,7 +313,7 @@ PKG Viewer - PKG-Ansicht + PKG-Anschauer Search... @@ -329,11 +329,11 @@ Game List Icons - Game List Icons + Spiellisten-Symbole Game List Mode - Spiellisten-Symoble + Spiellisten-Modus Settings @@ -373,7 +373,7 @@ toolBar - toolBar + Werkzeugleiste Game List @@ -461,7 +461,7 @@ Would you like to install DLC: %1? - Willst du den DLC installieren: %1? + Willst du das DLC installieren: %1? DLC already installed: @@ -546,7 +546,7 @@ Enable Separate Update Folder - Enable Separate Update Folder + Separaten Update-Ordner aktivieren Default tab when opening settings @@ -574,11 +574,11 @@ Trophy Key - Trophy Key + Trophäenschlüssel Trophy - Trophy + Trophäe Logger @@ -702,23 +702,23 @@ Enable Crash Diagnostics - Enable Crash Diagnostics + Absturz-Diagnostik aktivieren Collect Shaders - Collect Shaders + Sammle Shader Copy GPU Buffers - Copy GPU Buffers + Kopiere GPU Puffer Host Debug Markers - Host Debug Markers + Host-Debug-Markierungen Guest Debug Markers - Guest Debug Markers + Guest-Debug-Markierungen Update @@ -746,7 +746,7 @@ Disable Trophy Pop-ups - Disable Trophy Pop-ups + Deaktiviere Trophäen Pop-ups Play title music @@ -754,19 +754,19 @@ Update Compatibility Database On Startup - Update Compatibility Database On Startup + Aktualisiere Kompatibilitätsdatenbank beim Start Game Compatibility - Game Compatibility + Spielkompatibilität Display Compatibility Data - Display Compatibility Data + Zeige Kompatibilitätsdaten Update Compatibility Database - Update Compatibility Database + Aktualisiere Kompatibilitätsdatenbank Volume @@ -810,7 +810,7 @@ separateUpdatesCheckBox - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + Separaten Update-Ordner aktivieren:\nErmöglicht die Installation von Spielaktualiserungen in einem separaten Ordner zur einfachen Verwaltung. showSplashCheckBox @@ -866,15 +866,15 @@ enableCompatibilityCheckBox - Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + Kompatibilitätsdaten anzeigen:\nZeigt Spielkompatibilitätsinformationen in Tabellenansicht an. Aktivieren Sie „Aktualisiere Kompatibilitätsdatenbank beim Start“, um aktuelle Informationen zu erhalten. checkCompatibilityOnStartupCheckBox - Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + Kompatibilität beim Start aktualisieren:\nAktualisiert die Kompatibilitätsdatenbank automatisch, wenn shadPS4 startet. updateCompatibilityButton - Update Compatibility Database:\nImmediately update the compatibility database. + Aktualisiere Kompatibilitätsdatenbank:\nAktualisiere sofort die Kompatibilitätsdatenbank. Never @@ -954,23 +954,23 @@ collectShaderCheckBox - Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + Shader sammeln:\nSie müssen diese Option aktivieren, um Shader mit dem Debug-Menü (Strg + F10) bearbeiten zu können. crashDiagnosticsCheckBox - Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + Absturzdiagnose:\nErstellt eine .yaml-Datei mit Informationen über den Vulkan-Status zum Zeitpunkt des Absturzes.\nNützlich zum Debuggen von „Gerät verloren“-Fehlern. Wenn Sie dies aktiviert haben, sollten Sie Host- UND Gast-Debug-Markierungen aktivieren.\nFunktioniert nicht auf Intel-GPUs.\nDamit dies funktioniert, müssen Vulkan Validationsschichten aktiviert und das Vulkan SDK installiert sein. copyGPUBuffersCheckBox - Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + GPU-Puffer kopieren:\nUmgeht Race-Bedingungen mit GPU-Übermittlungen.\nKann bei PM4-Abstürzen vom Typ 0 hilfreich sein oder auch nicht. hostMarkersCheckBox - Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + Host-Debug-Marker:\nFügt emulatorseitige Informationen wie Marker für bestimmte AMDGPU-Befehle rund um Vulkan-Befehle ein und gibt Ressourcen-Debug-Namen an.\nWenn Sie dies aktiviert haben, sollten Sie die Absturzdiagnose aktivieren.\nNützlich für Programme wie RenderDoc. guestMarkersCheckBox - Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + Gast-Debug-Markierer:\nFügt alle Debug-Markierer, die das Spiel selbst hinzugefügt hat, in den Befehlspuffer ein.\nWenn Sie dies aktiviert haben, sollten Sie die Absturzdiagnose aktivieren.\nNützlich für Programme wie RenderDoc. @@ -1165,7 +1165,7 @@ Failed to open file: - Fehler beim Öffnen der Datei: + Öffnung der Datei fehlgeschlagen: XML ERROR: @@ -1212,7 +1212,7 @@ Compatibility - Compatibility + Kompatibilität Region @@ -1240,7 +1240,7 @@ Never Played - Never Played + Niemals gespielt h @@ -1256,27 +1256,27 @@ Compatibility is untested - Compatibility is untested + Kompatibilität wurde noch nicht getested Game does not initialize properly / crashes the emulator - Game does not initialize properly / crashes the emulator + Das Spiel wird nicht richtig initialisiert / stürzt den Emulator ab Game boots, but only displays a blank screen - Game boots, but only displays a blank screen + Spiel startet, aber zeigt nur einen blanken Bildschirm Game displays an image but does not go past the menu - Game displays an image but does not go past the menu + Spiel zeigt ein Bild aber geht nicht über das Menü hinaus Game has game-breaking glitches or unplayable performance - Game has game-breaking glitches or unplayable performance + Spiel hat spiel-brechende Störungen oder unspielbare Leistung Game can be completed with playable performance and no major glitches - Game can be completed with playable performance and no major glitches + Spiel kann mit spielbarer Leistung und keinen großen Störungen abgeschlossen werden @@ -1405,4 +1405,4 @@ TB - \ No newline at end of file + From be74f65864eb46f86e6b247db63fcc3cba5a4aed Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Thu, 30 Jan 2025 23:14:13 +0100 Subject: [PATCH 527/549] Fix ccrash if remote is not set (#2291) --- CMakeLists.txt | 14 ++++++++++---- src/emulator.cpp | 3 ++- src/qt_gui/main_window.cpp | 3 ++- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 340715e78..e10fcfb98 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -106,21 +106,27 @@ git_describe(GIT_DESC --always --long --dirty) git_branch_name(GIT_BRANCH) string(TIMESTAMP BUILD_DATE "%Y-%m-%d %H:%M:%S") -# Get current remote name and branch +# Try to get the upstream remote and branch execute_process( COMMAND git rev-parse --abbrev-ref --symbolic-full-name @{u} OUTPUT_VARIABLE GIT_REMOTE_NAME RESULT_VARIABLE GIT_BRANCH_RESULT + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE ) -# Default to origin if branch is not set +# Default to origin if there's no upstream set or if the command failed if (GIT_BRANCH_RESULT OR GIT_REMOTE_NAME STREQUAL "") set(GIT_REMOTE_NAME "origin") else() - # Extract remote name from full name + # Extract remote name if the output contains a remote/branch format string(FIND "${GIT_REMOTE_NAME}" "/" INDEX) - string(SUBSTRING "${GIT_REMOTE_NAME}" 0 "${INDEX}" GIT_REMOTE_NAME) + if (INDEX GREATER -1) + string(SUBSTRING "${GIT_REMOTE_NAME}" 0 "${INDEX}" GIT_REMOTE_NAME) + else() + # If no remote is present (only a branch name), default to origin + set(GIT_REMOTE_NAME "origin") + endif() endif() # Get remote link diff --git a/src/emulator.cpp b/src/emulator.cpp index 3ab26d484..ba8d8917c 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -201,7 +201,8 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector Date: Thu, 30 Jan 2025 19:41:17 -0300 Subject: [PATCH 528/549] Fix minor issue with 'Emulator' group box (#2292) --- src/qt_gui/settings_dialog.ui | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index 4e7440af5..d15f49efe 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -108,9 +108,6 @@ Emulator - - Qt::AlignmentFlag::AlignBottom|Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft - false From 647694d1b9eb6c34b4567b627ac8752ffeb468c9 Mon Sep 17 00:00:00 2001 From: bigol83 <38129260+bigol83@users.noreply.github.com> Date: Fri, 31 Jan 2025 07:34:42 +0100 Subject: [PATCH 529/549] Update Italian translation (#2293) * Update it.ts Update Italian translation * Update Italian translation * Update Italian translation --- src/qt_gui/translations/it.ts | 50 +++++++++++++++++------------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/qt_gui/translations/it.ts b/src/qt_gui/translations/it.ts index 384272137..106d09de0 100644 --- a/src/qt_gui/translations/it.ts +++ b/src/qt_gui/translations/it.ts @@ -146,19 +146,19 @@ Compatibility... - Compatibility... + Compatibilità... Update database - Update database + Aggiorna database View report - View report + Visualizza rapporto Submit a report - Submit a report + Invia rapporto Shortcut creation @@ -194,7 +194,7 @@ Update - Update + Aggiornamento This game has no DLC to delete! @@ -249,7 +249,7 @@ Open shadPS4 Folder - Open shadPS4 Folder + Apri Cartella shadps4 Exit @@ -574,11 +574,11 @@ Trophy Key - Trophy Key + Chiave Trofei Trophy - Trophy + Trofei Logger @@ -702,23 +702,23 @@ Enable Crash Diagnostics - Enable Crash Diagnostics + Abilita Diagnostica Crash Collect Shaders - Collect Shaders + Raccogli Shaders Copy GPU Buffers - Copy GPU Buffers + Copia Buffer GPU Host Debug Markers - Host Debug Markers + Marcatori di Debug dell'Host Guest Debug Markers - Guest Debug Markers + Marcatori di Debug del Guest Update @@ -742,7 +742,7 @@ Title Music - Title Music + Musica del Titolo Disable Trophy Pop-ups @@ -774,7 +774,7 @@ Audio Backend - Audio Backend + Backend Audio Save @@ -830,7 +830,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. + Chiave Trofei:\nChiave utilizzata per la decrittazione dei trofei. Deve essere estratta dalla vostra console con jailbreak.\nDeve contenere solo caratteri esadecimali. logTypeGroupBox @@ -850,7 +850,7 @@ disableTrophycheckBox - Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + Disabilita Notifica Trofei:\nDisabilita notifiche in gioco dei trofei. Il progresso dei Trofei può ancora essere controllato con il Visualizzatore Trofei (clicca tasto destro sul gioco nella finestra principale). hideCursorGroupBox @@ -954,30 +954,30 @@ collectShaderCheckBox - Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + Raccogli Shader:\nBisogna attivare questa opzione per poter modificare gli shader nel menu di debug (Ctrl + F10). crashDiagnosticsCheckBox - Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + Diagnostica Crash:\nCrea un file .yaml che contiene informazioni riguardo lo stato del renderer Vulkan nel momento in cui si verifica un crash.\nUtile per poter effettuare il debug degli errori di tipo "Device Lost". Se hai questa opzione attiva dovresti abilitare anche Marcatori di Debug Host e Guest.\nNon è funzionante su GPU Intel.\nVulkan Validation Layers deve essere abilitato e bisogna aver installato l'SDK Vulkan per poter utilizzare questa funzione. copyGPUBuffersCheckBox - Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + Copia Buffer GPU:\nCerca di aggirare le race conditions che riguardano gli invii alla GPU.\nPotrebbe aiutare ad evitare crash che riguardano i PM4 di tipo 0. hostMarkersCheckBox - Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + Marcatori di Debug dell'Host:\nInserisce nel log informazioni ottenute dall'emulatore come ad esempio marcatori per comandi specifici AMDGPU quando si hanno comandi Vulkan e associa nomi di debug per le risorse.\nSe hai questa opzione abilitata dovresti abilitare anche Diagnostica Crash.\nUtile per programmi come RenderDoc. guestMarkersCheckBox - Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + Marcatori di Debug del Guest:\nInserisce nel log marcatori di debug che il gioco stesso ha aggiunto al buffer dei comandi.\nSe hai abilitato questa opzione dovresti abilitare anche Diagnostica Crash.\nUtile per programmi come RenderDoc. CheatsPatches Cheats / Patches for - Cheats / Patches for + Cheats / Patch per defaultTextEdit_MSG @@ -1244,7 +1244,7 @@ h - h + o m @@ -1405,4 +1405,4 @@ TB - \ No newline at end of file + From 6e58c6c5131a0a1bd27833136e1b50f6a652adbf Mon Sep 17 00:00:00 2001 From: C4ndyF1sh <128715345+C4ndyF1sh@users.noreply.github.com> Date: Fri, 31 Jan 2025 07:35:06 +0100 Subject: [PATCH 530/549] update german ts (#2294) * Update de.ts (p1) * Update de.ts (p2) * Update de.ts (p3) * Update de.ts (p4) --- src/qt_gui/translations/de.ts | 36 +++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/src/qt_gui/translations/de.ts b/src/qt_gui/translations/de.ts index d1ae21cca..71ee066c1 100644 --- a/src/qt_gui/translations/de.ts +++ b/src/qt_gui/translations/de.ts @@ -105,7 +105,11 @@ Spielordner öffnen - Open Save Data Folder + Open Update Folder + Öffne Update-Ordner + + + Open Save Data Folder Speicherordner öffnen @@ -139,7 +143,11 @@ Delete Update Lösche Aktualisierung - + + + Delete Save Data + Lösche Speicherdaten + Delete DLC Lösche DLC @@ -513,7 +521,11 @@ SettingsDialog - Settings + Save Data Path + Speicherdaten-Pfad + + + Settings Einstellungen @@ -554,7 +566,7 @@ Show Game Size In List - Zeigen Sie die Spielgröße in der Liste + Zeige Spielgröße in der Liste Show Splash @@ -599,6 +611,10 @@ Input Eingabe + + + Enable Motion Controls + Aktiviere Bewegungssteuerung Cursor @@ -714,11 +730,11 @@ Host Debug Markers - Host-Debug-Markierungen + Host-Debug-Markierer Guest Debug Markers - Guest-Debug-Markierungen + Guest-Debug-Markierer Update @@ -742,7 +758,7 @@ Title Music - Title Music + Titelmusik Disable Trophy Pop-ups @@ -830,7 +846,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. + Trophäenschlüssel:\nSchlüssel zum Entschlüsseln von Trophäen. Muss von Ihrer gejailbreakten Konsole abgerufen werden.\nDarf nur Hex-Zeichen enthalten. logTypeGroupBox @@ -850,7 +866,7 @@ disableTrophycheckBox - Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + Trophäen-Popups deaktivieren:\nDeaktivieren Sie Trophäenbenachrichtigungen im Spiel. Der Trophäenfortschritt kann weiterhin mit dem Trophäen-Viewer verfolgt werden (klicken Sie mit der rechten Maustaste auf das Spiel im Hauptfenster).. hideCursorGroupBox @@ -977,7 +993,7 @@ CheatsPatches Cheats / Patches for - Cheats / Patches for + Cheats / Patches für defaultTextEdit_MSG From a55cec1954e62ffe2096aa136749283ffcbe6702 Mon Sep 17 00:00:00 2001 From: Martin <67326368+Martini-141@users.noreply.github.com> Date: Fri, 31 Jan 2025 07:35:31 +0100 Subject: [PATCH 531/549] add advdebug translation nb (#2296) * add advdebug translation nb * vvl is a project so no tr --- src/qt_gui/translations/nb.ts | 50 ++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/src/qt_gui/translations/nb.ts b/src/qt_gui/translations/nb.ts index 3e6f5fedd..bce5791af 100644 --- a/src/qt_gui/translations/nb.ts +++ b/src/qt_gui/translations/nb.ts @@ -629,7 +629,7 @@ Grafikk - Gui + GUI Grensesnitt @@ -680,6 +680,22 @@ Remove Fjern + + Save Data Path + Lagrede datamappe + + + Browse + Endre mappe + + + saveDataBox + Lagrede datamappe:\nListe over data shadPS4 lagrer. + + + browseButton + Endre mappe:\nEndrer hvilken mappe shadPS4 skal lagre data til. + Debug Feilretting @@ -690,7 +706,7 @@ Enable Vulkan Validation Layers - Aktiver Vulkan valideringslag + Aktiver Vulkan Validation Layers Enable Vulkan Synchronization Validation @@ -702,23 +718,23 @@ Enable Crash Diagnostics - Enable Crash Diagnostics + Aktiver krasjdiagnostikk Collect Shaders - Collect Shaders + Lagre skyggeleggere Copy GPU Buffers - Copy GPU Buffers + Kopier GPU-buffere Host Debug Markers - Host Debug Markers + Vertsfeilsøkingsmarkører Guest Debug Markers - Guest Debug Markers + Gjestefeilsøkingsmarkører Update @@ -742,7 +758,7 @@ Title Music - Title Music + Tittelmusikk Disable Trophy Pop-ups @@ -926,7 +942,7 @@ gameFoldersBox - Spillmapper:\nListen over mapper som brukes for å se etter installerte spill. + Spillmapper:\nListe over mapper som brukes for å se etter installerte spill. addFolderButton @@ -942,7 +958,7 @@ vkValidationCheckBox - Aktiver Vulkan valideringslag:\nAktiverer et system som validerer tilstanden til Vulkan-gjengiveren og logger informasjon om dens indre tilstand. Dette vil redusere ytelsen og sannsynligvis endre etterlignerens atferd. + Aktiver Vulkan Validation Layers:\nAktiverer et system som validerer tilstanden til Vulkan-gjengiveren og logger informasjon om dens indre tilstand. Dette vil redusere ytelsen og sannsynligvis endre etterlignerens atferd. vkSyncValidationCheckBox @@ -954,23 +970,23 @@ collectShaderCheckBox - Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + Lagre skyggeleggere:\nDu trenger dette aktivert for å redigere skyggeleggerne med feilsøkingsmenyen (Ctrl + F10). crashDiagnosticsCheckBox - Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + Krasjdiagnostikk:\nOppretter en .yaml-fil med informasjon om Vulkan-tilstanden ved krasj.\nNyttig for feilsøking 'Device lost' feil. Hvis du har dette aktivert, burde du aktivere vert OG gjestefeilsøkingsmarkører.\nFunker ikke med Intel GPU-er.\nDu trenger Vulkan Validation Layers og Vulkan SDK for at dette skal fungere. copyGPUBuffersCheckBox - Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + Kopier GPU-buffere:\nKommer rundt løpsforhold som involverer GPU-innsendinger.\nKan muligens hjelpe med PM4 type 0 krasj. hostMarkersCheckBox - Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + Vertsfeilsøkingsmarkører:\nSetter inn etterligner-side informasjon som markører for spesifikke AMDGPU-kommandoer rundt Vulkan-kommandoer, i tillegg til å gi ressurser feilrettingsnavn.\nHvis du har dette aktivert, burde du aktivere krasjdiagnostikk.\nNyttig for programmer som RenderDoc. guestMarkersCheckBox - Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + Gjestefeilsøkingsmarkører:\nSetter inn eventuelle feilsøkingsmarkører spillet selv har lagt til kommandobufferen.\nHvis du har dette aktivert, burde du aktivere krasjdiagnostikk.\nNyttig for programmer som RenderDoc. @@ -1015,6 +1031,10 @@ Delete File Slett fil + + Close + Lukk + No files selected. Ingen filer valgt. From ec7a5412633f68228dab2dc59b5c79ca4be0b1fb Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Fri, 31 Jan 2025 09:53:35 +0200 Subject: [PATCH 532/549] tagged 0.6.0 release --- src/common/version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/version.h b/src/common/version.h index c903b1db6..0305b929e 100644 --- a/src/common/version.h +++ b/src/common/version.h @@ -8,7 +8,7 @@ namespace Common { -constexpr char VERSION[] = "0.5.1 WIP"; -constexpr bool isRelease = false; +constexpr char VERSION[] = "0.6.0"; +constexpr bool isRelease = true; } // namespace Common From 8057ed408c32ae30197b8ec027d5665fe5524adb Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Fri, 31 Jan 2025 10:33:17 +0200 Subject: [PATCH 533/549] started 0.6.1 WIP --- src/common/version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/version.h b/src/common/version.h index 0305b929e..e7f6cc817 100644 --- a/src/common/version.h +++ b/src/common/version.h @@ -8,7 +8,7 @@ namespace Common { -constexpr char VERSION[] = "0.6.0"; -constexpr bool isRelease = true; +constexpr char VERSION[] = "0.6.1 WIP"; +constexpr bool isRelease = false; } // namespace Common From f3c33b29dd249aaf0c78985ff920762289a838ed Mon Sep 17 00:00:00 2001 From: pdaloxd <31321612+pablodrake@users.noreply.github.com> Date: Fri, 31 Jan 2025 09:50:02 +0100 Subject: [PATCH 534/549] Fix issue #1684 (#2277) * Added recursive game scan and only using game directories * Added recursion depth limit to scan directories * Added recursive search by ID in cli mode * Added recursive search to pkg installing --- src/common/path_util.cpp | 28 +++++++++++++++++++++++++ src/common/path_util.h | 15 +++++++++++++ src/main.cpp | 8 +++---- src/qt_gui/game_info.cpp | 35 ++++++++++++++++++++++++------- src/qt_gui/main.cpp | 8 +++---- src/qt_gui/main_window.cpp | 43 +++++++++++++++++++++++++++++++++----- 6 files changed, 117 insertions(+), 20 deletions(-) diff --git a/src/common/path_util.cpp b/src/common/path_util.cpp index 53eb123dc..a4312fada 100644 --- a/src/common/path_util.cpp +++ b/src/common/path_util.cpp @@ -176,6 +176,34 @@ void SetUserPath(PathType shad_path, const fs::path& new_path) { UserPaths.insert_or_assign(shad_path, new_path); } +std::optional FindGameByID(const fs::path& dir, const std::string& game_id, + int max_depth) { + if (max_depth < 0) { + return std::nullopt; + } + + // Check if this is the game we're looking for + if (dir.filename() == game_id && fs::exists(dir / "sce_sys" / "param.sfo")) { + auto eboot_path = dir / "eboot.bin"; + if (fs::exists(eboot_path)) { + return eboot_path; + } + } + + // Recursively search subdirectories + std::error_code ec; + for (const auto& entry : fs::directory_iterator(dir, ec)) { + if (!entry.is_directory()) { + continue; + } + if (auto found = FindGameByID(entry.path(), game_id, max_depth - 1)) { + return found; + } + } + + return std::nullopt; +} + #ifdef ENABLE_QT_GUI void PathToQString(QString& result, const std::filesystem::path& path) { #ifdef _WIN32 diff --git a/src/common/path_util.h b/src/common/path_util.h index 09b7a3337..7190378d6 100644 --- a/src/common/path_util.h +++ b/src/common/path_util.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #ifdef ENABLE_QT_GUI @@ -115,4 +116,18 @@ void PathToQString(QString& result, const std::filesystem::path& path); [[nodiscard]] std::filesystem::path PathFromQString(const QString& path); #endif +/** + * Recursively searches for a game directory by its ID. + * Limits search depth to prevent excessive filesystem traversal. + * + * @param dir Base directory to start the search from + * @param game_id The game ID to search for + * @param max_depth Maximum directory depth to search + * + * @returns Path to eboot.bin if found, std::nullopt otherwise + */ +[[nodiscard]] std::optional FindGameByID(const std::filesystem::path& dir, + const std::string& game_id, + int max_depth); + } // namespace Common::FS diff --git a/src/main.cpp b/src/main.cpp index fad3b1f53..6b334e446 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -167,12 +167,12 @@ int main(int argc, char* argv[]) { // Check if the provided path is a valid file if (!std::filesystem::exists(eboot_path)) { - // If not a file, treat it as a game ID and search in install directories + // If not a file, treat it as a game ID and search in install directories recursively bool game_found = false; + const int max_depth = 5; for (const auto& install_dir : Config::getGameInstallDirs()) { - const auto candidate_path = install_dir / game_path / "eboot.bin"; - if (std::filesystem::exists(candidate_path)) { - eboot_path = candidate_path; + if (auto found_path = Common::FS::FindGameByID(install_dir, game_path, max_depth)) { + eboot_path = *found_path; game_found = true; break; } diff --git a/src/qt_gui/game_info.cpp b/src/qt_gui/game_info.cpp index e4750fa1d..adbf392ed 100644 --- a/src/qt_gui/game_info.cpp +++ b/src/qt_gui/game_info.cpp @@ -7,6 +7,33 @@ #include "compatibility_info.h" #include "game_info.h" +// Maximum depth to search for games in subdirectories +const int max_recursion_depth = 5; + +void ScanDirectoryRecursively(const QString& dir, QStringList& filePaths, int current_depth = 0) { + // Stop recursion if we've reached the maximum depth + if (current_depth >= max_recursion_depth) { + return; + } + + QDir directory(dir); + QFileInfoList entries = directory.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); + + for (const auto& entry : entries) { + if (entry.fileName().endsWith("-UPDATE")) { + continue; + } + + // Check if this directory contains a PS4 game (has sce_sys/param.sfo) + if (QFile::exists(entry.filePath() + "/sce_sys/param.sfo")) { + filePaths.append(entry.absoluteFilePath()); + } else { + // If not a game directory, recursively scan it with increased depth + ScanDirectoryRecursively(entry.absoluteFilePath(), filePaths, current_depth + 1); + } + } +} + GameInfoClass::GameInfoClass() = default; GameInfoClass::~GameInfoClass() = default; @@ -15,13 +42,7 @@ void GameInfoClass::GetGameInfo(QWidget* parent) { for (const auto& installLoc : Config::getGameInstallDirs()) { QString installDir; Common::FS::PathToQString(installDir, installLoc); - QDir parentFolder(installDir); - QFileInfoList fileList = parentFolder.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); - for (const auto& fileInfo : fileList) { - if (fileInfo.isDir() && !fileInfo.filePath().endsWith("-UPDATE")) { - filePaths.append(fileInfo.absoluteFilePath()); - } - } + ScanDirectoryRecursively(installDir, filePaths, 0); } m_games = QtConcurrent::mapped(filePaths, [&](const QString& path) { diff --git a/src/qt_gui/main.cpp b/src/qt_gui/main.cpp index 8babadc35..052f73f25 100644 --- a/src/qt_gui/main.cpp +++ b/src/qt_gui/main.cpp @@ -181,12 +181,12 @@ int main(int argc, char* argv[]) { // Check if the provided path is a valid file if (!std::filesystem::exists(game_file_path)) { - // If not a file, treat it as a game ID and search in install directories + // If not a file, treat it as a game ID and search in install directories recursively bool game_found = false; + const int max_depth = 5; for (const auto& install_dir : Config::getGameInstallDirs()) { - auto potential_game_path = install_dir / game_path / "eboot.bin"; - if (std::filesystem::exists(potential_game_path)) { - game_file_path = potential_game_path; + if (auto found_path = Common::FS::FindGameByID(install_dir, game_path, max_depth)) { + game_file_path = *found_path; game_found = true; break; } diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 945210776..aa02d1237 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -747,12 +747,42 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int } std::filesystem::path game_install_dir = last_install_dir; - auto game_folder_path = game_install_dir / pkg.GetTitleID(); QString pkgType = QString::fromStdString(pkg.GetPkgFlags()); bool use_game_update = pkgType.contains("PATCH") && Config::getSeparateUpdateEnabled(); - auto game_update_path = use_game_update - ? game_install_dir / (std::string(pkg.GetTitleID()) + "-UPDATE") - : game_folder_path; + + // Default paths + auto game_folder_path = game_install_dir / pkg.GetTitleID(); + auto game_update_path = use_game_update ? game_folder_path.parent_path() / + (std::string{pkg.GetTitleID()} + "-UPDATE") + : game_folder_path; + const int max_depth = 5; + + if (pkgType.contains("PATCH")) { + // For patches, try to find the game recursively + auto found_game = Common::FS::FindGameByID(game_install_dir, + std::string{pkg.GetTitleID()}, max_depth); + if (found_game.has_value()) { + game_folder_path = found_game.value().parent_path(); + game_update_path = use_game_update ? game_folder_path.parent_path() / + (std::string{pkg.GetTitleID()} + "-UPDATE") + : game_folder_path; + } + } else { + // For base games, we check if the game is already installed + auto found_game = Common::FS::FindGameByID(game_install_dir, + std::string{pkg.GetTitleID()}, max_depth); + if (found_game.has_value()) { + game_folder_path = found_game.value().parent_path(); + } + // If the game is not found, we install it in the game install directory + else { + game_folder_path = game_install_dir / pkg.GetTitleID(); + } + game_update_path = use_game_update ? game_folder_path.parent_path() / + (std::string{pkg.GetTitleID()} + "-UPDATE") + : game_folder_path; + } + QString gameDirPath; Common::FS::PathToQString(gameDirPath, game_folder_path); QDir game_dir(gameDirPath); @@ -897,10 +927,13 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int connect(&futureWatcher, &QFutureWatcher::finished, this, [=, this]() { if (pkgNum == nPkg) { QString path; - Common::FS::PathToQString(path, game_install_dir); + + // We want to show the parent path instead of the full path + Common::FS::PathToQString(path, game_folder_path.parent_path()); QIcon windowIcon( Common::FS::PathToUTF8String(game_folder_path / "sce_sys/icon0.png") .c_str()); + QMessageBox extractMsgBox(this); extractMsgBox.setWindowTitle(tr("Extraction Finished")); if (!windowIcon.isNull()) { From 49fc210833e0cb80f87980556256e03e5cd6f57e Mon Sep 17 00:00:00 2001 From: Daniel Nylander Date: Fri, 31 Jan 2025 09:50:40 +0100 Subject: [PATCH 535/549] Updated Swedish translation (#2270) * Adding Swedish translation * Updated Swedish translation with additional strings Updated the Swedish translations using lupdate to found additional strings cd src/qt_gui/treanslations lupdate ../../../../shadPS4/ -tr-function-alias QT_TRANSLATE_NOOP+=TRANSLATE,QT_TRANSLATE_NOOP+=TRANSLATE_SV,QT_TRANSLATE_NOOP+=TRANSLATE_STR,QT_TRANSLATE_NOOP+=TRANSLATE_FS,QT_TRANSLATE_N_NOOP3+=TRANSLATE_FMT,QT_TRANSLATE_NOOP+=TRANSLATE_NOOP,translate+=TRANSLATE_PLURAL_STR,translate+=TRANSLATE_PLURAL_FS -no-obsolete -locations none -source-language en -ts sv.ts * Update sv.ts * Updated Swedish translation * Adding copyright boilerplate --- src/qt_gui/translations/sv.ts | 82 +++++++++++++++++++++++++++++++---- 1 file changed, 73 insertions(+), 9 deletions(-) diff --git a/src/qt_gui/translations/sv.ts b/src/qt_gui/translations/sv.ts index c5fbce991..deefa99d8 100644 --- a/src/qt_gui/translations/sv.ts +++ b/src/qt_gui/translations/sv.ts @@ -1,9 +1,9 @@ - - + AboutDialog About shadPS4 @@ -252,6 +252,10 @@ ERROR FEL + + Close + Stäng + CheckUpdate @@ -676,6 +680,26 @@ Failed to convert icon. Misslyckades med att konvertera ikon. + + Open Update Folder + Öppna uppdateringsmapp + + + Delete Save Data + Ta bort sparat data + + + This game has no update folder to open! + Detta spel har ingen uppdateringsmapp att öppna! + + + This game has no save data to delete! + Detta spel har inget sparat data att ta bort! + + + Save Data + Sparat data + InstallDirSelect @@ -687,6 +711,14 @@ Select which directory you want to install to. Välj vilken katalog som du vill installera till. + + Install All Queued to Selected Folder + Installera alla köade till markerad mapp + + + Delete PKG File on Install + Ta bort PKG-fil efter installation + MainWindow @@ -1040,11 +1072,11 @@ Enable Separate Update Folder Aktivera separat uppdateringsmapp - - Default tab when opening settings - Standardflik när inställningar öppnas - - + + Default tab when opening settings + Standardflik när inställningar öppnas + + Show Game Size In List Visa spelstorlek i listan @@ -1142,7 +1174,7 @@ Advanced - Avancerat + Avancerat Enable Shaders Dumping @@ -1322,7 +1354,7 @@ updaterGroupBox - updaterGroupBox + Uppdatering:\nRelease: Officiella versioner som släpps varje månad som kan vara mycket utdaterade, men är mer pålitliga och testade.\nNightly: Utvecklingsversioner som har de senaste funktionerna och fixarna, men kan innehålla fel och är mindre stabila GUIMusicGroupBox @@ -1476,6 +1508,38 @@ Directory to install games Katalog att installera spel till + + Borderless + Fönster utan kanter + + + True + Sant + + + Enable Motion Controls + Aktivera rörelsekontroller + + + Save Data Path + Sökväg för sparat data + + + Browse + Bläddra + + + Directory to save data + Katalog för sparat data + + + saveDataBox + Sökväg för sparat data:\nSökvägen där spelets sparade data kommer att sparas + + + browseButton + Bläddra:\nBläddra efter en mapp att ställa in som sökväg för sparat data + TrophyViewer From ec0cf25097fdd746d220f3035e25e4dee82df778 Mon Sep 17 00:00:00 2001 From: panzone91 <150828896+panzone91@users.noreply.github.com> Date: Fri, 31 Jan 2025 09:51:03 +0100 Subject: [PATCH 536/549] libkernel: handle special case in path for load module (#2269) * libkernel: handle special case for load module * fix linting --- src/core/libraries/kernel/process.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/core/libraries/kernel/process.cpp b/src/core/libraries/kernel/process.cpp index c21257c50..a904152ff 100644 --- a/src/core/libraries/kernel/process.cpp +++ b/src/core/libraries/kernel/process.cpp @@ -41,8 +41,13 @@ s32 PS4_SYSV_ABI sceKernelLoadStartModule(const char* moduleFileName, size_t arg return ORBIS_KERNEL_ERROR_EINVAL; } + std::string guest_path(moduleFileName); + if (moduleFileName[0] != '/') { + guest_path = "/app0/" + guest_path; + } + auto* mnt = Common::Singleton::Instance(); - const auto path = mnt->GetHostPath(moduleFileName); + const auto path = mnt->GetHostPath(guest_path); // Load PRX module and relocate any modules that import it. auto* linker = Common::Singleton::Instance(); From 8aea0fc7eeccc2f3a8594634e3539c49e2411ac7 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Fri, 31 Jan 2025 12:54:16 +0200 Subject: [PATCH 537/549] Revert "libkernel: handle special case in path for load module (#2269)" (#2298) This reverts commit ec0cf25097fdd746d220f3035e25e4dee82df778. --- src/core/libraries/kernel/process.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/core/libraries/kernel/process.cpp b/src/core/libraries/kernel/process.cpp index a904152ff..c21257c50 100644 --- a/src/core/libraries/kernel/process.cpp +++ b/src/core/libraries/kernel/process.cpp @@ -41,13 +41,8 @@ s32 PS4_SYSV_ABI sceKernelLoadStartModule(const char* moduleFileName, size_t arg return ORBIS_KERNEL_ERROR_EINVAL; } - std::string guest_path(moduleFileName); - if (moduleFileName[0] != '/') { - guest_path = "/app0/" + guest_path; - } - auto* mnt = Common::Singleton::Instance(); - const auto path = mnt->GetHostPath(guest_path); + const auto path = mnt->GetHostPath(moduleFileName); // Load PRX module and relocate any modules that import it. auto* linker = Common::Singleton::Instance(); From eed4de1da9c5c76efd982a6172a4dcf68226c4a1 Mon Sep 17 00:00:00 2001 From: poly <47796739+polybiusproxy@users.noreply.github.com> Date: Fri, 31 Jan 2025 12:52:31 +0100 Subject: [PATCH 538/549] renderer_vulkan: use LDS buffer as SSBO on unsupported shared memory size (#2245) * renderer_vulkan: use LDS buffer as SSBO on unsupported shared memory size * shader_recompiler: add `v_trunc_f64` on inst format table --- .../spirv/emit_spirv_shared_memory.cpp | 74 +++++++++++++++---- .../backend/spirv/spirv_emit_context.cpp | 45 ++++++++--- .../backend/spirv/spirv_emit_context.h | 4 +- src/shader_recompiler/frontend/decode.cpp | 4 +- src/shader_recompiler/frontend/format.cpp | 4 +- src/shader_recompiler/info.h | 2 + src/shader_recompiler/profile.h | 1 + src/shader_recompiler/runtime_info.h | 1 - src/shader_recompiler/specialization.h | 9 +++ src/video_core/buffer_cache/buffer_cache.cpp | 6 +- src/video_core/buffer_cache/buffer_cache.h | 6 ++ .../renderer_vulkan/vk_compute_pipeline.cpp | 8 ++ .../renderer_vulkan/vk_pipeline_cache.cpp | 2 +- .../renderer_vulkan/vk_rasterizer.cpp | 17 ++++- 14 files changed, 147 insertions(+), 36 deletions(-) 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 57ea476f1..6ab213864 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp @@ -9,18 +9,33 @@ namespace Shader::Backend::SPIRV { Id EmitLoadSharedU32(EmitContext& ctx, Id offset) { const Id shift_id{ctx.ConstU32(2U)}; const Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)}; - const Id pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, index)}; - return ctx.OpLoad(ctx.U32[1], pointer); + if (ctx.info.has_emulated_shared_memory) { + const Id pointer = + ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, ctx.u32_zero_value, index); + return ctx.OpLoad(ctx.U32[1], pointer); + } else { + const Id pointer = ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, index); + return ctx.OpLoad(ctx.U32[1], pointer); + } } Id EmitLoadSharedU64(EmitContext& ctx, Id offset) { const Id shift_id{ctx.ConstU32(2U)}; const Id base_index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)}; const Id next_index{ctx.OpIAdd(ctx.U32[1], base_index, ctx.ConstU32(1U))}; - const Id lhs_pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, base_index)}; - const Id rhs_pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, next_index)}; - return ctx.OpCompositeConstruct(ctx.U32[2], ctx.OpLoad(ctx.U32[1], lhs_pointer), - ctx.OpLoad(ctx.U32[1], rhs_pointer)); + if (ctx.info.has_emulated_shared_memory) { + const Id lhs_pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, + ctx.u32_zero_value, base_index)}; + const Id rhs_pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, + ctx.u32_zero_value, next_index)}; + return ctx.OpCompositeConstruct(ctx.U32[2], ctx.OpLoad(ctx.U32[1], lhs_pointer), + ctx.OpLoad(ctx.U32[1], rhs_pointer)); + } else { + const Id lhs_pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, base_index)}; + const Id rhs_pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, next_index)}; + return ctx.OpCompositeConstruct(ctx.U32[2], ctx.OpLoad(ctx.U32[1], lhs_pointer), + ctx.OpLoad(ctx.U32[1], rhs_pointer)); + } } Id EmitLoadSharedU128(EmitContext& ctx, Id offset) { @@ -29,8 +44,14 @@ Id EmitLoadSharedU128(EmitContext& ctx, Id offset) { std::array values{}; for (u32 i = 0; i < 4; ++i) { const Id index{i == 0 ? base_index : ctx.OpIAdd(ctx.U32[1], base_index, ctx.ConstU32(i))}; - const Id pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, index)}; - values[i] = ctx.OpLoad(ctx.U32[1], pointer); + if (ctx.info.has_emulated_shared_memory) { + const Id pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, + ctx.u32_zero_value, index)}; + values[i] = ctx.OpLoad(ctx.U32[1], pointer); + } else { + const Id pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, index)}; + values[i] = ctx.OpLoad(ctx.U32[1], pointer); + } } return ctx.OpCompositeConstruct(ctx.U32[4], values); } @@ -38,18 +59,33 @@ Id EmitLoadSharedU128(EmitContext& ctx, Id offset) { void EmitWriteSharedU32(EmitContext& ctx, Id offset, Id value) { const Id shift{ctx.ConstU32(2U)}; const Id word_offset{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift)}; - const Id pointer = ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, word_offset); - ctx.OpStore(pointer, value); + if (ctx.info.has_emulated_shared_memory) { + const Id pointer = ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, + ctx.u32_zero_value, word_offset); + ctx.OpStore(pointer, value); + } else { + const Id pointer = ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, word_offset); + ctx.OpStore(pointer, value); + } } void EmitWriteSharedU64(EmitContext& ctx, Id offset, Id value) { const Id shift{ctx.ConstU32(2U)}; const Id word_offset{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift)}; const Id next_offset{ctx.OpIAdd(ctx.U32[1], word_offset, ctx.ConstU32(1U))}; - const Id lhs_pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, word_offset)}; - const Id rhs_pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, next_offset)}; - ctx.OpStore(lhs_pointer, ctx.OpCompositeExtract(ctx.U32[1], value, 0U)); - ctx.OpStore(rhs_pointer, ctx.OpCompositeExtract(ctx.U32[1], value, 1U)); + if (ctx.info.has_emulated_shared_memory) { + const Id lhs_pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, + ctx.u32_zero_value, word_offset)}; + const Id rhs_pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, + ctx.u32_zero_value, next_offset)}; + ctx.OpStore(lhs_pointer, ctx.OpCompositeExtract(ctx.U32[1], value, 0U)); + ctx.OpStore(rhs_pointer, ctx.OpCompositeExtract(ctx.U32[1], value, 1U)); + } else { + const Id lhs_pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, word_offset)}; + const Id rhs_pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, next_offset)}; + ctx.OpStore(lhs_pointer, ctx.OpCompositeExtract(ctx.U32[1], value, 0U)); + ctx.OpStore(rhs_pointer, ctx.OpCompositeExtract(ctx.U32[1], value, 1U)); + } } void EmitWriteSharedU128(EmitContext& ctx, Id offset, Id value) { @@ -57,8 +93,14 @@ void EmitWriteSharedU128(EmitContext& ctx, Id offset, Id value) { const Id base_index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift)}; for (u32 i = 0; i < 4; ++i) { const Id index{i == 0 ? base_index : ctx.OpIAdd(ctx.U32[1], base_index, ctx.ConstU32(i))}; - const Id pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, index)}; - ctx.OpStore(pointer, ctx.OpCompositeExtract(ctx.U32[1], value, i)); + if (ctx.info.has_emulated_shared_memory) { + const Id pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, + ctx.u32_zero_value, index)}; + ctx.OpStore(pointer, ctx.OpCompositeExtract(ctx.U32[1], value, i)); + } else { + const Id pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, index)}; + ctx.OpStore(pointer, ctx.OpCompositeExtract(ctx.U32[1], value, i)); + } } } diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index b0bf5aa0a..2a0c28563 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -65,17 +65,17 @@ void Name(EmitContext& ctx, Id object, std::string_view format_str, Args&&... ar } // Anonymous namespace -EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_info_, - const Info& info_, Bindings& binding_) +EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_info_, Info& info_, + Bindings& binding_) : Sirit::Module(profile_.supported_spirv), info{info_}, runtime_info{runtime_info_}, profile{profile_}, stage{info.stage}, l_stage{info.l_stage}, binding{binding_} { AddCapability(spv::Capability::Shader); DefineArithmeticTypes(); DefineInterfaces(); + DefineSharedMemory(); DefineBuffers(); DefineTextureBuffers(); DefineImagesAndSamplers(); - DefineSharedMemory(); } EmitContext::~EmitContext() = default; @@ -852,20 +852,45 @@ void EmitContext::DefineSharedMemory() { if (!info.uses_shared) { return; } + const u32 max_shared_memory_size = profile.max_shared_memory_size; u32 shared_memory_size = runtime_info.cs_info.shared_memory_size; if (shared_memory_size == 0) { shared_memory_size = DefaultSharedMemSize; } - const u32 max_shared_memory_size = runtime_info.cs_info.max_shared_memory_size; - ASSERT(shared_memory_size <= max_shared_memory_size); - const u32 num_elements{Common::DivCeil(shared_memory_size, 4U)}; const Id type{TypeArray(U32[1], ConstU32(num_elements))}; - shared_memory_u32_type = TypePointer(spv::StorageClass::Workgroup, type); - shared_u32 = TypePointer(spv::StorageClass::Workgroup, U32[1]); - shared_memory_u32 = AddGlobalVariable(shared_memory_u32_type, spv::StorageClass::Workgroup); - interfaces.push_back(shared_memory_u32); + + if (shared_memory_size <= max_shared_memory_size) { + shared_memory_u32_type = TypePointer(spv::StorageClass::Workgroup, type); + shared_u32 = TypePointer(spv::StorageClass::Workgroup, U32[1]); + shared_memory_u32 = AddGlobalVariable(shared_memory_u32_type, spv::StorageClass::Workgroup); + Name(shared_memory_u32, "shared_mem"); + interfaces.push_back(shared_memory_u32); + } else { + shared_memory_u32_type = TypePointer(spv::StorageClass::StorageBuffer, type); + shared_u32 = TypePointer(spv::StorageClass::StorageBuffer, U32[1]); + + Decorate(type, spv::Decoration::ArrayStride, 4); + + const Id struct_type{TypeStruct(type)}; + Name(struct_type, "shared_memory_buf"); + Decorate(struct_type, spv::Decoration::Block); + MemberName(struct_type, 0, "data"); + MemberDecorate(struct_type, 0, spv::Decoration::Offset, 0U); + + const Id struct_pointer_type{TypePointer(spv::StorageClass::StorageBuffer, struct_type)}; + const Id ssbo_id{AddGlobalVariable(struct_pointer_type, spv::StorageClass::StorageBuffer)}; + Decorate(ssbo_id, spv::Decoration::Binding, binding.unified++); + Decorate(ssbo_id, spv::Decoration::DescriptorSet, 0U); + Name(ssbo_id, "shared_mem_ssbo"); + + shared_memory_u32 = ssbo_id; + + info.has_emulated_shared_memory = true; + info.shared_memory_size = shared_memory_size; + interfaces.push_back(ssbo_id); + } } } // namespace Shader::Backend::SPIRV diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index f552055c0..ab42ecc5b 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -37,7 +37,7 @@ struct VectorIds { class EmitContext final : public Sirit::Module { public: - explicit EmitContext(const Profile& profile, const RuntimeInfo& runtime_info, const Info& info, + explicit EmitContext(const Profile& profile, const RuntimeInfo& runtime_info, Info& info, Bindings& binding); ~EmitContext(); @@ -132,7 +132,7 @@ public: return ConstantComposite(type, constituents); } - const Info& info; + Info& info; const RuntimeInfo& runtime_info; const Profile& profile; Stage stage; diff --git a/src/shader_recompiler/frontend/decode.cpp b/src/shader_recompiler/frontend/decode.cpp index a5187aebd..20b78e869 100644 --- a/src/shader_recompiler/frontend/decode.cpp +++ b/src/shader_recompiler/frontend/decode.cpp @@ -259,9 +259,9 @@ void GcnDecodeContext::updateInstructionMeta(InstEncoding encoding) { ASSERT_MSG(instFormat.src_type != ScalarType::Undefined && instFormat.dst_type != ScalarType::Undefined, - "Instruction format table incomplete for opcode {} ({}, encoding = {})", + "Instruction format table incomplete for opcode {} ({}, encoding = 0x{:x})", magic_enum::enum_name(m_instruction.opcode), u32(m_instruction.opcode), - magic_enum::enum_name(encoding)); + u32(encoding)); m_instruction.inst_class = instFormat.inst_class; m_instruction.category = instFormat.inst_category; diff --git a/src/shader_recompiler/frontend/format.cpp b/src/shader_recompiler/frontend/format.cpp index 2fcac7c10..76b1cc818 100644 --- a/src/shader_recompiler/frontend/format.cpp +++ b/src/shader_recompiler/frontend/format.cpp @@ -1836,7 +1836,9 @@ constexpr std::array InstructionFormatVOP1 = {{ {InstClass::VectorConv, InstCategory::VectorALU, 1, 1, ScalarType::Float64, ScalarType::Uint32}, // 22 = V_CVT_F64_U32 {InstClass::VectorConv, InstCategory::VectorALU, 1, 1, ScalarType::Uint32, ScalarType::Float64}, - {}, + // 23 = V_TRUNC_F64 + {InstClass::VectorConv, InstCategory::VectorALU, 1, 1, ScalarType::Float64, + ScalarType::Float64}, {}, {}, {}, diff --git a/src/shader_recompiler/info.h b/src/shader_recompiler/info.h index 2cde30629..9469eaad7 100644 --- a/src/shader_recompiler/info.h +++ b/src/shader_recompiler/info.h @@ -207,7 +207,9 @@ struct Info { bool stores_tess_level_outer{}; bool stores_tess_level_inner{}; bool translation_failed{}; // indicates that shader has unsupported instructions + bool has_emulated_shared_memory{}; bool has_readconst{}; + u32 shared_memory_size{}; u8 mrt_mask{0u}; bool has_fetch_shader{false}; u32 fetch_shader_sgpr_base{0u}; diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index f8b91a283..f359a7dcc 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h @@ -32,6 +32,7 @@ struct Profile { u64 min_ssbo_alignment{}; u32 max_viewport_width{}; u32 max_viewport_height{}; + u32 max_shared_memory_size{}; }; } // namespace Shader diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index 103c1faa8..138a707b3 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -201,7 +201,6 @@ struct FragmentRuntimeInfo { struct ComputeRuntimeInfo { u32 shared_memory_size; - u32 max_shared_memory_size; std::array workgroup_size; std::array tgid_enable; diff --git a/src/shader_recompiler/specialization.h b/src/shader_recompiler/specialization.h index eb0085965..2083d11a9 100644 --- a/src/shader_recompiler/specialization.h +++ b/src/shader_recompiler/specialization.h @@ -101,6 +101,9 @@ struct StageSpecialization { }); } u32 binding{}; + if (info->has_emulated_shared_memory) { + binding++; + } if (info->has_readconst) { binding++; } @@ -197,9 +200,15 @@ struct StageSpecialization { } } u32 binding{}; + if (info->has_emulated_shared_memory != other.info->has_emulated_shared_memory) { + return false; + } if (info->has_readconst != other.info->has_readconst) { return false; } + if (info->has_emulated_shared_memory) { + binding++; + } if (info->has_readconst) { binding++; } diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index 11ad0e96f..c779c1c45 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -17,7 +17,7 @@ namespace VideoCore { -static constexpr size_t GdsBufferSize = 64_KB; +static constexpr size_t DataShareBufferSize = 64_KB; static constexpr size_t StagingBufferSize = 1_GB; static constexpr size_t UboStreamBufferSize = 64_MB; @@ -28,9 +28,11 @@ BufferCache::BufferCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& s texture_cache{texture_cache_}, tracker{tracker_}, staging_buffer{instance, scheduler, MemoryUsage::Upload, StagingBufferSize}, stream_buffer{instance, scheduler, MemoryUsage::Stream, UboStreamBufferSize}, - gds_buffer{instance, scheduler, MemoryUsage::Stream, 0, AllFlags, GdsBufferSize}, + gds_buffer{instance, scheduler, MemoryUsage::Stream, 0, AllFlags, DataShareBufferSize}, + lds_buffer{instance, scheduler, MemoryUsage::DeviceLocal, 0, AllFlags, DataShareBufferSize}, memory_tracker{&tracker} { Vulkan::SetObjectName(instance.GetDevice(), gds_buffer.Handle(), "GDS Buffer"); + Vulkan::SetObjectName(instance.GetDevice(), lds_buffer.Handle(), "LDS Buffer"); // Ensure the first slot is used for the null buffer const auto null_id = diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 575ee2c60..088c22c12 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -68,6 +68,11 @@ public: return &gds_buffer; } + /// Returns a pointer to LDS device local buffer. + [[nodiscard]] const Buffer* GetLdsBuffer() const noexcept { + return &lds_buffer; + } + /// Retrieves the buffer with the specified id. [[nodiscard]] Buffer& GetBuffer(BufferId id) { return slot_buffers[id]; @@ -154,6 +159,7 @@ private: StreamBuffer staging_buffer; StreamBuffer stream_buffer; Buffer gds_buffer; + Buffer lds_buffer; std::shared_mutex mutex; Common::SlotVector slot_buffers; RangeSet gpu_modified_ranges; diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp index 23faacfc2..afa598fca 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp @@ -29,6 +29,14 @@ ComputePipeline::ComputePipeline(const Instance& instance_, Scheduler& scheduler u32 binding{}; boost::container::small_vector bindings; + if (info->has_emulated_shared_memory) { + bindings.push_back({ + .binding = binding++, + .descriptorType = vk::DescriptorType::eStorageBuffer, + .descriptorCount = 1, + .stageFlags = vk::ShaderStageFlagBits::eCompute, + }); + } if (info->has_readconst) { bindings.push_back({ .binding = binding++, diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 3728a55fb..629899a33 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -180,7 +180,6 @@ const Shader::RuntimeInfo& PipelineCache::BuildRuntimeInfo(Stage stage, LogicalS info.cs_info.tgid_enable = {cs_pgm.IsTgidEnabled(0), cs_pgm.IsTgidEnabled(1), cs_pgm.IsTgidEnabled(2)}; info.cs_info.shared_memory_size = cs_pgm.SharedMemSize(); - info.cs_info.max_shared_memory_size = instance.MaxComputeSharedMemorySize(); break; } default: @@ -209,6 +208,7 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_, instance.GetDriverID() == vk::DriverId::eMoltenvk, .max_viewport_width = instance.GetMaxViewportWidth(), .max_viewport_height = instance.GetMaxViewportHeight(), + .max_shared_memory_size = instance.MaxComputeSharedMemorySize(), }; auto [cache_result, cache] = instance.GetDevice().createPipelineCacheUnique({}); ASSERT_MSG(cache_result == vk::Result::eSuccess, "Failed to create pipeline cache: {}", diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index fde87fcfb..1c90c6c27 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -175,7 +175,7 @@ RenderState Rasterizer::PrepareRenderState(u32 mrt_mask) { const bool is_depth_clear = regs.depth_render_control.depth_clear_enable || texture_cache.IsMetaCleared(htile_address, slice); const bool is_stencil_clear = regs.depth_render_control.stencil_clear_enable; - ASSERT(desc.view_info.range.extent.layers == 1); + ASSERT(desc.view_info.range.extent.levels == 1); state.width = std::min(state.width, image.info.size.width); state.height = std::min(state.height, image.info.size.height); @@ -554,6 +554,21 @@ void Rasterizer::BindBuffers(const Shader::Info& stage, Shader::Backend::Binding } } + // Bind a SSBO to act as shared memory in case of not being able to use a workgroup buffer + // (e.g. when the compute shared memory is bigger than the GPU's shared memory) + if (stage.has_emulated_shared_memory) { + const auto* lds_buf = buffer_cache.GetLdsBuffer(); + buffer_infos.emplace_back(lds_buf->Handle(), 0, lds_buf->SizeBytes()); + set_writes.push_back({ + .dstSet = VK_NULL_HANDLE, + .dstBinding = binding.unified++, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eStorageBuffer, + .pBufferInfo = &buffer_infos.back(), + }); + } + // Bind the flattened user data buffer as a UBO so it's accessible to the shader if (stage.has_readconst) { const auto [vk_buffer, offset] = buffer_cache.ObtainHostUBO(stage.flattened_ud_buf); From fda8f1afa37210861c3684edc4b51c9003b7b89c Mon Sep 17 00:00:00 2001 From: Sn0wCrack <442287+Sn0wCrack@users.noreply.github.com> Date: Fri, 31 Jan 2025 23:22:57 +1100 Subject: [PATCH 539/549] feat: set desktop file name to get icon on wayland (#2299) --- src/qt_gui/main.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/qt_gui/main.cpp b/src/qt_gui/main.cpp index 052f73f25..36dc226ae 100644 --- a/src/qt_gui/main.cpp +++ b/src/qt_gui/main.cpp @@ -26,6 +26,8 @@ int main(int argc, char* argv[]) { QApplication a(argc, argv); + QApplication::setDesktopFileName("net.shadps4.shadPS4"); + // Load configurations and initialize Qt application const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); Config::load(user_dir / "config.toml"); From f3810cebeacbf85496b2bb153374d5fcfcdfbedf Mon Sep 17 00:00:00 2001 From: jarred wilson <20207921+jardon@users.noreply.github.com> Date: Fri, 31 Jan 2025 06:55:14 -0600 Subject: [PATCH 540/549] add 0.6.0 release to metainfo (#2300) --- dist/net.shadps4.shadPS4.metainfo.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dist/net.shadps4.shadPS4.metainfo.xml b/dist/net.shadps4.shadPS4.metainfo.xml index d8f51baac..c8c9d5c23 100644 --- a/dist/net.shadps4.shadPS4.metainfo.xml +++ b/dist/net.shadps4.shadPS4.metainfo.xml @@ -37,6 +37,9 @@ Game + + https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.6.0 + https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.5.0 From c4bfaa60312c6e224c26eca9a00b7b573253aa42 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Fri, 31 Jan 2025 15:36:14 +0100 Subject: [PATCH 541/549] Added keyboard and mouse input remapping, mouse movement to joystick logic, GUI and more (#1356) * added support for loading keyboard config from file * final minor update before pull request * fix messing up the merge * fix waitEvent to correctly handle mouse inputs * add license * Applied coding style fixes * clang-format fucked up the .ini file * actually fix clang changing ini syntax use relative path for the ini file * remove big commented out code blocks, and fixed platform-dependent code * fix windows hating me * added mouse config option * added toggle for mouse movement input (f7) * fix license and style * add numpad support i accidentally left out * added support for mouse wheel (to buttons only) * if keyboard config doesn't exist, autogenerate it * added keybinds for "walk mode" * Mouse movement input is now off by default * code cleanup and misc fixes * delete config file since it is now autogenerated * F6 = F7 + F9 * added better mouse handling with config options * Added capslock support * fix clang-format * Added support for mod key toggle key * F6 and F7 are removed, F9 captures and enables the mouse * Encapsulated globals and new classes in a new namespace * Added mouse side button support * Added per-game config * relocated input parser to the new namespace * changed parser parameters to make it possible to use it from the gui * added home, end, pgup and pgdown * Resolved merge conflict and refactored code * Updated default keybindings * Changed input handling to be single-threaded * General code cleanup * Start working on new backend * Mouse polling, CMakeLists, and basic framework * Output update handling, and reworked file creating, reading and parsing * Parsing works now * Single key button inputs work now * Axis outputs work now * Wheel works now (for me), l2/r2 handling improvements, and misc bugfixes * Downgraded prints to log_debug, and implemented input hierarchy * Implemented key toggle * Added mouse parameter parsing * clang-format * Fixed clang and added a const keyword for mac * Fix input hierarchy * Fixed joysick halfmodes, and possibly the last update on input hierarchy * clang-format * Rewrote the default config to reflect new changes * clang * Update code style * Updated sorting to accomodate for that one specific edge case * Fix default config and the latest bug with input hiearchy * Fix typo * Temporarily added my GUI * Update cmakelists * Possible fix for Gravity Rush * Update Help text, default config, and clang * Updated README with the new keybind info * okay so maybe the gravity rush fix might have slightly broken the joystick halfmode and key toggle * Fixed mistakenly overwriting the last opened config with the default one if the GUI is opened multiple times in a session * Updated Help descriptions and fixed mouse movement default parameters * Fix crash if the Help dialog was opened a second time If it's closed with the top right close button instead of clicking the Help button again, a required flag wasn't reset, making the next click on Help try to close a nonexistent window and segfault * Added closing the config also closing the Help window, and fixed more segfaults due to mismatched flags * Initial controller support * clang and debug print cleanup * Initial axis-to-button logic * Updated Help text * Added 'Reset to Default' button in GUI * Minor text and description updates + fixed an issue with Help text box rendering * Fix button-to-touchpad logic and l2/r2 handling, as they are both axes and buttons The touchpad's button state was correctly handled, so games that use that were fine, but the touchDown flag was always set to true, so games that use this flag had problems, like Gravity Rush * Fix merge conflict * Clang * Added back back button to touchpad binding * Added touchpad button handling * Added end-of-line comments and fixed some crashes happening with the VS debugger * Apply recent changes from kbm-only * Deadzone + initial directional axis-to-button mapping * Added that one missing space in the README. Are you all happy now? * Fixups from making everything use SDL * Revert directional joystick code and fix a memory leak * Change config directory name again to conform to project standards * Clang * Revert the old deeadzone code and properly add the new one * Clang --- CMakeLists.txt | 8 + README.md | 56 +-- src/common/config.cpp | 121 +++++- src/common/config.h | 5 +- src/core/libraries/pad/pad.cpp | 8 +- src/imgui/renderer/imgui_core.cpp | 2 +- src/input/input_handler.cpp | 676 ++++++++++++++++++++++++++++++ src/input/input_handler.h | 407 ++++++++++++++++++ src/input/input_mouse.cpp | 74 ++++ src/input/input_mouse.h | 18 + src/qt_gui/kbm_config_dialog.cpp | 237 +++++++++++ src/qt_gui/kbm_config_dialog.h | 38 ++ src/qt_gui/kbm_help_dialog.cpp | 112 +++++ src/qt_gui/kbm_help_dialog.h | 169 ++++++++ src/qt_gui/main_window.cpp | 10 + src/sdl_window.cpp | 336 +++++---------- src/sdl_window.h | 5 +- 17 files changed, 1996 insertions(+), 286 deletions(-) create mode 100644 src/input/input_handler.cpp create mode 100644 src/input/input_handler.h create mode 100644 src/input/input_mouse.cpp create mode 100644 src/input/input_mouse.h create mode 100644 src/qt_gui/kbm_config_dialog.cpp create mode 100644 src/qt_gui/kbm_config_dialog.h create mode 100644 src/qt_gui/kbm_help_dialog.cpp create mode 100644 src/qt_gui/kbm_help_dialog.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e10fcfb98..4822658c6 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -854,6 +854,10 @@ set(IMGUI src/imgui/imgui_config.h set(INPUT src/input/controller.cpp src/input/controller.h + src/input/input_handler.cpp + src/input/input_handler.h + src/input/input_mouse.cpp + src/input/input_mouse.h ) set(EMULATOR src/emulator.cpp @@ -903,6 +907,10 @@ set(QT_GUI src/qt_gui/about_dialog.cpp src/qt_gui/trophy_viewer.h src/qt_gui/elf_viewer.cpp src/qt_gui/elf_viewer.h + src/qt_gui/kbm_config_dialog.cpp + src/qt_gui/kbm_config_dialog.h + src/qt_gui/kbm_help_dialog.cpp + src/qt_gui/kbm_help_dialog.h src/qt_gui/main_window_themes.cpp src/qt_gui/main_window_themes.h src/qt_gui/settings_dialog.cpp diff --git a/README.md b/README.md index 30efeb263..0e5ba7e39 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ Check the build instructions for [**macOS**](https://github.com/shadps4-emu/shad For more information on how to test, debug and report issues with the emulator or games, read the [**Debugging documentation**](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Debugging/Debugging.md). -# Keyboard mapping +# Keyboard and Mouse Mappings > [!NOTE] > Some keyboards may also require you to hold the Fn key to use the F\* keys. Mac users should use the Command key instead of Control, and need to use Command+F11 for full screen to avoid conflicting with system key bindings. @@ -92,32 +92,34 @@ F12 | Trigger RenderDoc Capture > [!NOTE] > Xbox and DualShock controllers work out of the box. -| Controller button | Keyboard equivalent | -|-------------|-------------| -LEFT AXIS UP | W | -LEFT AXIS DOWN | S | -LEFT AXIS LEFT | A | -LEFT AXIS RIGHT | D | -RIGHT AXIS UP | I | -RIGHT AXIS DOWN | K | -RIGHT AXIS LEFT | J | -RIGHT AXIS RIGHT | L | -TRIANGLE | Numpad 8 or C | -CIRCLE | Numpad 6 or B | -CROSS | Numpad 2 or N | -SQUARE | Numpad 4 or V | -PAD UP | UP | -PAD DOWN | DOWN | -PAD LEFT | LEFT | -PAD RIGHT | RIGHT | -OPTIONS | RETURN | -BACK BUTTON / TOUCH PAD | SPACE | -L1 | Q | -R1 | U | -L2 | E | -R2 | O | -L3 | X | -R3 | M | +The default controls are inspired by the *Elden Ring* PC controls. Inputs support up to three keys per binding, mouse buttons, mouse movement mapped to joystick input, and more. + +| Action | Default Key(s) | +|-------------|-----------------------------| +| Triangle | F | +| Circle | Space | +| Cross | E | +| Square | R | +| Pad Up | W, LAlt / Mouse Wheel Up | +| Pad Down | S, LAlt / Mouse Wheel Down | +| Pad Left | A, LAlt / Mouse Wheel Left | +| Pad Right | D, LAlt / Mouse Wheel Right | +| L1 | Right Button, LShift | +| R1 | Left Button | +| L2 | Right Button | +| R2 | Left Button, LShift | +| L3 | X | +| R3 | Q / Middle Button | +| Options | Escape | +| Touchpad | G | + +| Joystick | Default Input | +|--------------------|----------------| +| Left Joystick | WASD | +| Right Joystick | Mouse movement | + +Keyboard and mouse inputs can be customized in the settings menu by clicking the Controller button, and further details and help on controls are also found there. Custom bindings are saved per-game. + # Main team diff --git a/src/common/config.cpp b/src/common/config.cpp index 70b062951..e79a52796 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -46,8 +46,6 @@ static std::string logType = "async"; static std::string userName = "shadPS4"; static std::string updateChannel; static std::string chooseHomeTab; -static u16 deadZoneLeft = 2.0; -static u16 deadZoneRight = 2.0; static std::string backButtonBehavior = "left"; static bool useSpecialPad = false; static int specialPadClass = 1; @@ -151,14 +149,6 @@ bool getEnableDiscordRPC() { return enableDiscordRPC; } -u16 leftDeadZone() { - return deadZoneLeft; -} - -u16 rightDeadZone() { - return deadZoneRight; -} - s16 getCursorState() { return cursorState; } @@ -661,8 +651,6 @@ void load(const std::filesystem::path& path) { if (data.contains("Input")) { const toml::value& input = data.at("Input"); - deadZoneLeft = toml::find_or(input, "deadZoneLeft", 2.0); - deadZoneRight = toml::find_or(input, "deadZoneRight", 2.0); cursorState = toml::find_or(input, "cursorState", HideCursorState::Idle); cursorHideTimeout = toml::find_or(input, "cursorHideTimeout", 5); backButtonBehavior = toml::find_or(input, "backButtonBehavior", "left"); @@ -785,8 +773,6 @@ void save(const std::filesystem::path& path) { data["General"]["separateUpdateEnabled"] = separateupdatefolder; data["General"]["compatibilityEnabled"] = compatibilityData; data["General"]["checkCompatibilityOnStartup"] = checkCompatibilityOnStartup; - data["Input"]["deadZoneLeft"] = deadZoneLeft; - data["Input"]["deadZoneRight"] = deadZoneRight; data["Input"]["cursorState"] = cursorState; data["Input"]["cursorHideTimeout"] = cursorHideTimeout; data["Input"]["backButtonBehavior"] = backButtonBehavior; @@ -919,4 +905,109 @@ void setDefaultValues() { checkCompatibilityOnStartup = false; } -} // namespace Config \ No newline at end of file +constexpr std::string_view GetDefaultKeyboardConfig() { + return R"(#Feeling lost? Check out the Help section! + +#Keyboard bindings + +triangle = f +circle = space +cross = e +square = r + +pad_up = w, lalt +pad_up = mousewheelup +pad_down = s, lalt +pad_down = mousewheeldown +pad_left = a, lalt +pad_left = mousewheelleft +pad_right = d, lalt +pad_right = mousewheelright + +l1 = rightbutton, lshift +r1 = leftbutton +l2 = rightbutton +r2 = leftbutton, lshift +l3 = x +r3 = q +r3 = middlebutton + +options = escape +touchpad = g + +key_toggle = i, lalt +mouse_to_joystick = right +mouse_movement_params = 0.5, 1, 0.125 +leftjoystick_halfmode = lctrl + +axis_left_x_minus = a +axis_left_x_plus = d +axis_left_y_minus = w +axis_left_y_plus = s + +#Controller bindings + +triangle = triangle +cross = cross +square = square +circle = circle + +l1 = l1 +l2 = l2 +l3 = l3 +r1 = r1 +r2 = r2 +r3 = r3 + +pad_up = pad_up +pad_down = pad_down +pad_left = pad_left +pad_right = pad_right + +options = options +touchpad = back + +axis_left_x = axis_left_x +axis_left_y = axis_left_y + +axis_right_x = axis_right_x +axis_right_y = axis_right_y +)"; +} +std::filesystem::path GetFoolproofKbmConfigFile(const std::string& game_id) { + // Read configuration file of the game, and if it doesn't exist, generate it from default + // If that doesn't exist either, generate that from getDefaultConfig() and try again + // If even the folder is missing, we start with that. + + const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "input_config"; + const auto config_file = config_dir / (game_id + ".ini"); + const auto default_config_file = config_dir / "default.ini"; + + // Ensure the config directory exists + if (!std::filesystem::exists(config_dir)) { + std::filesystem::create_directories(config_dir); + } + + // Check if the default config exists + if (!std::filesystem::exists(default_config_file)) { + // If the default config is also missing, create it from getDefaultConfig() + const auto default_config = GetDefaultKeyboardConfig(); + std::ofstream default_config_stream(default_config_file); + if (default_config_stream) { + default_config_stream << default_config; + } + } + + // if empty, we only need to execute the function up until this point + if (game_id.empty()) { + return default_config_file; + } + + // If game-specific config doesn't exist, create it from the default config + if (!std::filesystem::exists(config_file)) { + std::filesystem::copy(default_config_file, config_file); + } + return config_file; +} + +} // namespace Config diff --git a/src/common/config.h b/src/common/config.h index 356cf77fc..f726f840c 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -37,8 +37,6 @@ std::string getUserName(); std::string getUpdateChannel(); std::string getChooseHomeTab(); -u16 leftDeadZone(); -u16 rightDeadZone(); s16 getCursorState(); int getCursorHideTimeout(); std::string getBackButtonBehavior(); @@ -152,6 +150,9 @@ std::string getEmulatorLanguage(); void setDefaultValues(); +// todo: name and function location pending +std::filesystem::path GetFoolproofKbmConfigFile(const std::string& game_id = ""); + // settings u32 GetLanguage(); }; // namespace Config diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index 18709bcb2..173b78382 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -95,8 +95,8 @@ int PS4_SYSV_ABI scePadGetControllerInformation(s32 handle, OrbisPadControllerIn pInfo->touchPadInfo.pixelDensity = 1; pInfo->touchPadInfo.resolution.x = 1920; pInfo->touchPadInfo.resolution.y = 950; - pInfo->stickInfo.deadZoneLeft = Config::leftDeadZone(); - pInfo->stickInfo.deadZoneRight = Config::rightDeadZone(); + pInfo->stickInfo.deadZoneLeft = 1; + pInfo->stickInfo.deadZoneRight = 1; pInfo->connectionType = ORBIS_PAD_PORT_TYPE_STANDARD; pInfo->connectedCount = 1; pInfo->connected = false; @@ -106,8 +106,8 @@ int PS4_SYSV_ABI scePadGetControllerInformation(s32 handle, OrbisPadControllerIn pInfo->touchPadInfo.pixelDensity = 1; pInfo->touchPadInfo.resolution.x = 1920; pInfo->touchPadInfo.resolution.y = 950; - pInfo->stickInfo.deadZoneLeft = Config::leftDeadZone(); - pInfo->stickInfo.deadZoneRight = Config::rightDeadZone(); + pInfo->stickInfo.deadZoneLeft = 1; + pInfo->stickInfo.deadZoneRight = 1; pInfo->connectionType = ORBIS_PAD_PORT_TYPE_STANDARD; pInfo->connectedCount = 1; pInfo->connected = true; diff --git a/src/imgui/renderer/imgui_core.cpp b/src/imgui/renderer/imgui_core.cpp index d9530dd70..ab43b281e 100644 --- a/src/imgui/renderer/imgui_core.cpp +++ b/src/imgui/renderer/imgui_core.cpp @@ -148,7 +148,7 @@ bool ProcessEvent(SDL_Event* event) { case SDL_EVENT_MOUSE_BUTTON_DOWN: { const auto& io = GetIO(); return io.WantCaptureMouse && io.Ctx->NavWindow != nullptr && - io.Ctx->NavWindow->ID != dock_id; + (io.Ctx->NavWindow->Flags & ImGuiWindowFlags_NoNav) == 0; } case SDL_EVENT_TEXT_INPUT: case SDL_EVENT_KEY_DOWN: { diff --git a/src/input/input_handler.cpp b/src/input/input_handler.cpp new file mode 100644 index 000000000..a78a54131 --- /dev/null +++ b/src/input/input_handler.cpp @@ -0,0 +1,676 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "input_handler.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "SDL3/SDL_events.h" +#include "SDL3/SDL_timer.h" + +#include "common/config.h" +#include "common/elf_info.h" +#include "common/io_file.h" +#include "common/path_util.h" +#include "common/version.h" +#include "input/controller.h" +#include "input/input_mouse.h" + +namespace Input { +/* +Project structure: +n to m connection between inputs and outputs +Keyup and keydown events update a dynamic list* of u32 'flags' (what is currently in the list is +'pressed') On every event, after flag updates, we check for every input binding -> controller output +pair if all their flags are 'on' If not, disable; if so, enable them. For axes, we gather their data +into a struct cumulatively from all inputs, then after we checked all of those, we update them all +at once. Wheel inputs generate a timer that doesn't turn off their outputs automatically, but push a +userevent to do so. + +What structs are needed? +InputBinding(key1, key2, key3) +ControllerOutput(button, axis) - we only need a const array of these, and one of the attr-s is +always 0 BindingConnection(inputBinding (member), controllerOutput (ref to the array element)) + +Things to always test before pushing like a dumbass: +Button outputs +Axis outputs +Input hierarchy +Multi key inputs +Mouse to joystick +Key toggle +Joystick halfmode + +Don't be an idiot and test only the changed part expecting everything else to not be broken +*/ + +bool leftjoystick_halfmode = false, rightjoystick_halfmode = false; +int leftjoystick_deadzone, rightjoystick_deadzone, lefttrigger_deadzone, righttrigger_deadzone; + +std::list> pressed_keys; +std::list toggled_keys; +static std::vector connections; + +auto output_array = std::array{ + // Important: these have to be the first, or else they will update in the wrong order + ControllerOutput(LEFTJOYSTICK_HALFMODE), + ControllerOutput(RIGHTJOYSTICK_HALFMODE), + ControllerOutput(KEY_TOGGLE), + + // Button mappings + ControllerOutput(SDL_GAMEPAD_BUTTON_NORTH), // Triangle + ControllerOutput(SDL_GAMEPAD_BUTTON_EAST), // Circle + ControllerOutput(SDL_GAMEPAD_BUTTON_SOUTH), // Cross + ControllerOutput(SDL_GAMEPAD_BUTTON_WEST), // Square + ControllerOutput(SDL_GAMEPAD_BUTTON_LEFT_SHOULDER), // L1 + ControllerOutput(SDL_GAMEPAD_BUTTON_LEFT_STICK), // L3 + ControllerOutput(SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER), // R1 + ControllerOutput(SDL_GAMEPAD_BUTTON_RIGHT_STICK), // R3 + ControllerOutput(SDL_GAMEPAD_BUTTON_START), // Options + ControllerOutput(SDL_GAMEPAD_BUTTON_TOUCHPAD), // TouchPad + ControllerOutput(SDL_GAMEPAD_BUTTON_DPAD_UP), // Up + ControllerOutput(SDL_GAMEPAD_BUTTON_DPAD_DOWN), // Down + ControllerOutput(SDL_GAMEPAD_BUTTON_DPAD_LEFT), // Left + ControllerOutput(SDL_GAMEPAD_BUTTON_DPAD_RIGHT), // Right + + // Axis mappings + // ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_LEFTX, false), + // ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_LEFTY, false), + // ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_RIGHTX, false), + // ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_RIGHTY, false), + ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_LEFTX), + ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_LEFTY), + ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_RIGHTX), + ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_RIGHTY), + + ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_LEFT_TRIGGER), + ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER), + + ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_INVALID), +}; + +void ControllerOutput::LinkJoystickAxes() { + // for (int i = 17; i < 23; i += 2) { + // delete output_array[i].new_param; + // output_array[i].new_param = output_array[i + 1].new_param; + // } +} + +static OrbisPadButtonDataOffset SDLGamepadToOrbisButton(u8 button) { + using OPBDO = OrbisPadButtonDataOffset; + + switch (button) { + case SDL_GAMEPAD_BUTTON_DPAD_DOWN: + return OPBDO::Down; + case SDL_GAMEPAD_BUTTON_DPAD_UP: + return OPBDO::Up; + case SDL_GAMEPAD_BUTTON_DPAD_LEFT: + return OPBDO::Left; + case SDL_GAMEPAD_BUTTON_DPAD_RIGHT: + return OPBDO::Right; + case SDL_GAMEPAD_BUTTON_SOUTH: + return OPBDO::Cross; + case SDL_GAMEPAD_BUTTON_NORTH: + return OPBDO::Triangle; + case SDL_GAMEPAD_BUTTON_WEST: + return OPBDO::Square; + case SDL_GAMEPAD_BUTTON_EAST: + return OPBDO::Circle; + case SDL_GAMEPAD_BUTTON_START: + return OPBDO::Options; + case SDL_GAMEPAD_BUTTON_TOUCHPAD: + return OPBDO::TouchPad; + case SDL_GAMEPAD_BUTTON_BACK: + return OPBDO::TouchPad; + case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER: + return OPBDO::L1; + case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER: + return OPBDO::R1; + case SDL_GAMEPAD_BUTTON_LEFT_STICK: + return OPBDO::L3; + case SDL_GAMEPAD_BUTTON_RIGHT_STICK: + return OPBDO::R3; + default: + return OPBDO::None; + } +} + +Axis GetAxisFromSDLAxis(u8 sdl_axis) { + switch (sdl_axis) { + case SDL_GAMEPAD_AXIS_LEFTX: + return Axis::LeftX; + case SDL_GAMEPAD_AXIS_LEFTY: + return Axis::LeftY; + case SDL_GAMEPAD_AXIS_RIGHTX: + return Axis::RightX; + case SDL_GAMEPAD_AXIS_RIGHTY: + return Axis::RightY; + case SDL_GAMEPAD_AXIS_LEFT_TRIGGER: + return Axis::TriggerLeft; + case SDL_GAMEPAD_AXIS_RIGHT_TRIGGER: + return Axis::TriggerRight; + default: + return Axis::AxisMax; + } +} + +// syntax: 'name, name,name' or 'name,name' or 'name' +InputBinding GetBindingFromString(std::string& line) { + std::array keys = {InputID(), InputID(), InputID()}; + + // Check and process tokens + for (const auto token : std::views::split(line, ',')) { // Split by comma + const std::string t(token.begin(), token.end()); + InputID input; + + if (string_to_keyboard_key_map.find(t) != string_to_keyboard_key_map.end()) { + input = InputID(InputType::KeyboardMouse, string_to_keyboard_key_map.at(t)); + } else if (string_to_axis_map.find(t) != string_to_axis_map.end()) { + input = InputID(InputType::Axis, (u32)string_to_axis_map.at(t).axis); + } else if (string_to_cbutton_map.find(t) != string_to_cbutton_map.end()) { + input = InputID(InputType::Controller, string_to_cbutton_map.at(t)); + } else { + // Invalid token found; return default binding + LOG_DEBUG(Input, "Invalid token found: {}", t); + return InputBinding(); + } + + // Assign to the first available slot + for (auto& key : keys) { + if (!key.IsValid()) { + key = input; + break; + } + } + } + LOG_DEBUG(Input, "Parsed line: {}", InputBinding(keys[0], keys[1], keys[2]).ToString()); + return InputBinding(keys[0], keys[1], keys[2]); +} + +void ParseInputConfig(const std::string game_id = "") { + const auto config_file = Config::GetFoolproofKbmConfigFile(game_id); + + if (game_id == "") { + return; + } + + // we reset these here so in case the user fucks up or doesn't include some of these, + // we can fall back to default + connections.clear(); + float mouse_deadzone_offset = 0.5; + float mouse_speed = 1; + float mouse_speed_offset = 0.125; + + leftjoystick_deadzone = 1; + rightjoystick_deadzone = 1; + lefttrigger_deadzone = 1; + righttrigger_deadzone = 1; + + int lineCount = 0; + + std::ifstream file(config_file); + std::string line = ""; + while (std::getline(file, line)) { + lineCount++; + + // Strip the ; and whitespace + line.erase(std::remove_if(line.begin(), line.end(), + [](unsigned char c) { return std::isspace(c); }), + line.end()); + + if (line.empty()) { + continue; + } + // Truncate lines starting at # + std::size_t comment_pos = line.find('#'); + if (comment_pos != std::string::npos) { + line = line.substr(0, comment_pos); + } + // Remove trailing semicolon + if (!line.empty() && line[line.length() - 1] == ';') { + line = line.substr(0, line.length() - 1); + } + if (line.empty()) { + continue; + } + + // Split the line by '=' + std::size_t equal_pos = line.find('='); + if (equal_pos == std::string::npos) { + LOG_WARNING(Input, "Invalid format at line: {}, data: \"{}\", skipping line.", + lineCount, line); + continue; + } + + std::string output_string = line.substr(0, equal_pos); + std::string input_string = line.substr(equal_pos + 1); + std::size_t comma_pos = input_string.find(','); + + if (output_string == "mouse_to_joystick") { + if (input_string == "left") { + SetMouseToJoystick(1); + } else if (input_string == "right") { + SetMouseToJoystick(2); + } else { + LOG_WARNING(Input, "Invalid argument for mouse-to-joystick binding"); + SetMouseToJoystick(0); + } + continue; + } else if (output_string == "key_toggle") { + if (comma_pos != std::string::npos) { + // handle key-to-key toggling (separate list?) + InputBinding toggle_keys = GetBindingFromString(input_string); + if (toggle_keys.KeyCount() != 2) { + LOG_WARNING(Input, + "Syntax error: Please provide exactly 2 keys: " + "first is the toggler, the second is the key to toggle: {}", + line); + continue; + } + ControllerOutput* toggle_out = + &*std::ranges::find(output_array, ControllerOutput(KEY_TOGGLE)); + BindingConnection toggle_connection = BindingConnection( + InputBinding(toggle_keys.keys[0]), toggle_out, 0, toggle_keys.keys[1]); + connections.insert(connections.end(), toggle_connection); + continue; + } + LOG_WARNING(Input, "Invalid format at line: {}, data: \"{}\", skipping line.", + lineCount, line); + continue; + } else if (output_string == "mouse_movement_params") { + std::stringstream ss(input_string); + char comma; // To hold the comma separators between the floats + ss >> mouse_deadzone_offset >> comma >> mouse_speed >> comma >> mouse_speed_offset; + + // Check for invalid input (in case there's an unexpected format) + if (ss.fail()) { + LOG_WARNING(Input, "Failed to parse mouse movement parameters from line: {}", line); + continue; + } + SetMouseParams(mouse_deadzone_offset, mouse_speed, mouse_speed_offset); + continue; + } else if (output_string == "analog_deadzone") { + std::stringstream ss(input_string); + std::string device; + int deadzone; + std::getline(ss, device, ','); + ss >> deadzone; + if (ss.fail()) { + LOG_WARNING(Input, "Failed to parse deadzone config from line: {}", line); + continue; + } else { + LOG_DEBUG(Input, "Parsed deadzone: {} {}", device, deadzone); + } + if (device == "leftjoystick") { + leftjoystick_deadzone = deadzone; + } else if (device == "rightjoystick") { + rightjoystick_deadzone = deadzone; + } else if (device == "l2") { + lefttrigger_deadzone = deadzone; + } else if (device == "r2") { + righttrigger_deadzone = deadzone; + } else { + LOG_WARNING(Input, "Invalid axis name at line: {}, data: \"{}\", skipping line.", + lineCount, line); + } + continue; + } + + // normal cases + InputBinding binding = GetBindingFromString(input_string); + BindingConnection connection(InputID(), nullptr); + auto button_it = string_to_cbutton_map.find(output_string); + auto axis_it = string_to_axis_map.find(output_string); + + if (binding.IsEmpty()) { + LOG_WARNING(Input, "Invalid format at line: {}, data: \"{}\", skipping line.", + lineCount, line); + continue; + } + if (button_it != string_to_cbutton_map.end()) { + connection = BindingConnection( + binding, &*std::ranges::find(output_array, ControllerOutput(button_it->second))); + connections.insert(connections.end(), connection); + + } else if (axis_it != string_to_axis_map.end()) { + int value_to_set = binding.keys[2].type == InputType::Axis ? 0 : axis_it->second.value; + connection = BindingConnection( + binding, + &*std::ranges::find(output_array, ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, + axis_it->second.axis, + axis_it->second.value >= 0)), + value_to_set); + connections.insert(connections.end(), connection); + } else { + LOG_WARNING(Input, "Invalid format at line: {}, data: \"{}\", skipping line.", + lineCount, line); + continue; + } + LOG_DEBUG(Input, "Succesfully parsed line {}", lineCount); + } + file.close(); + std::sort(connections.begin(), connections.end()); + for (auto& c : connections) { + LOG_DEBUG(Input, "Binding: {} : {}", c.output->ToString(), c.binding.ToString()); + } + LOG_DEBUG(Input, "Done parsing the input config!"); +} + +u32 GetMouseWheelEvent(const SDL_Event& event) { + if (event.type != SDL_EVENT_MOUSE_WHEEL && event.type != SDL_EVENT_MOUSE_WHEEL_OFF) { + LOG_WARNING(Input, "Something went wrong with wheel input parsing!"); + return (u32)-1; + } + if (event.wheel.y > 0) { + return SDL_MOUSE_WHEEL_UP; + } else if (event.wheel.y < 0) { + return SDL_MOUSE_WHEEL_DOWN; + } else if (event.wheel.x > 0) { + return SDL_MOUSE_WHEEL_RIGHT; + } else if (event.wheel.x < 0) { + return SDL_MOUSE_WHEEL_LEFT; + } + return (u32)-1; +} + +InputEvent InputBinding::GetInputEventFromSDLEvent(const SDL_Event& e) { + switch (e.type) { + case SDL_EVENT_KEY_DOWN: + case SDL_EVENT_KEY_UP: + return InputEvent(InputType::KeyboardMouse, e.key.key, e.key.down, 0); + case SDL_EVENT_MOUSE_BUTTON_DOWN: + case SDL_EVENT_MOUSE_BUTTON_UP: + return InputEvent(InputType::KeyboardMouse, (u32)e.button.button, e.button.down, 0); + case SDL_EVENT_MOUSE_WHEEL: + case SDL_EVENT_MOUSE_WHEEL_OFF: + return InputEvent(InputType::KeyboardMouse, GetMouseWheelEvent(e), + e.type == SDL_EVENT_MOUSE_WHEEL, 0); + case SDL_EVENT_GAMEPAD_BUTTON_DOWN: + case SDL_EVENT_GAMEPAD_BUTTON_UP: + return InputEvent(InputType::Controller, (u32)e.gbutton.button, e.gbutton.down, 0); + case SDL_EVENT_GAMEPAD_AXIS_MOTION: + return InputEvent(InputType::Axis, (u32)e.gaxis.axis, true, e.gaxis.value / 256); + default: + return InputEvent(); + } +} + +GameController* ControllerOutput::controller = nullptr; +void ControllerOutput::SetControllerOutputController(GameController* c) { + ControllerOutput::controller = c; +} + +void ToggleKeyInList(InputID input) { + if (input.type == InputType::Axis) { + LOG_ERROR(Input, "Toggling analog inputs is not supported!"); + return; + } + auto it = std::find(toggled_keys.begin(), toggled_keys.end(), input); + if (it == toggled_keys.end()) { + toggled_keys.insert(toggled_keys.end(), input); + LOG_DEBUG(Input, "Added {} to toggled keys", input.ToString()); + } else { + toggled_keys.erase(it); + LOG_DEBUG(Input, "Removed {} from toggled keys", input.ToString()); + } +} + +void ControllerOutput::ResetUpdate() { + state_changed = false; + new_button_state = false; + *new_param = 0; // bruh +} +void ControllerOutput::AddUpdate(InputEvent event) { + state_changed = true; + if (button == KEY_TOGGLE) { + if (event.active) { + ToggleKeyInList(event.input); + } + } else if (button != SDL_GAMEPAD_BUTTON_INVALID) { + if (event.input.type == InputType::Axis) { + bool temp = event.axis_value * (positive_axis ? 1 : -1) > 0x40; + new_button_state |= event.active && event.axis_value * (positive_axis ? 1 : -1) > 0x40; + if (temp) { + LOG_DEBUG(Input, "Toggled a button from an axis"); + } + } else { + new_button_state |= event.active; + } + + } else if (axis != SDL_GAMEPAD_AXIS_INVALID) { + switch (axis) { + case SDL_GAMEPAD_AXIS_LEFT_TRIGGER: + case SDL_GAMEPAD_AXIS_RIGHT_TRIGGER: + // if it's a button input, then we know the value to set, so the param is 0. + // if it's an analog input, then the param isn't 0 + *new_param = (event.active ? event.axis_value : 0) + *new_param; + break; + default: + *new_param = (event.active ? event.axis_value : 0) + *new_param; + break; + } + } +} +void ControllerOutput::FinalizeUpdate() { + if (!state_changed) { + // return; + } + + old_button_state = new_button_state; + old_param = *new_param; + float touchpad_x = 0; + if (button != SDL_GAMEPAD_BUTTON_INVALID) { + switch (button) { + case SDL_GAMEPAD_BUTTON_TOUCHPAD: + touchpad_x = Config::getBackButtonBehavior() == "left" ? 0.25f + : Config::getBackButtonBehavior() == "right" ? 0.75f + : 0.5f; + controller->SetTouchpadState(0, new_button_state, touchpad_x, 0.5f); + controller->CheckButton(0, SDLGamepadToOrbisButton(button), new_button_state); + break; + case LEFTJOYSTICK_HALFMODE: + leftjoystick_halfmode = new_button_state; + break; + case RIGHTJOYSTICK_HALFMODE: + rightjoystick_halfmode = new_button_state; + break; + // KEY_TOGGLE isn't handled here anymore, as this function doesn't have the necessary data + // to do it, and it would be inconvenient to force it here, when AddUpdate does the job just + // fine, and a toggle doesn't have to checked against every input that's bound to it, it's + // enough that one is pressed + default: // is a normal key (hopefully) + controller->CheckButton(0, SDLGamepadToOrbisButton(button), new_button_state); + break; + } + } else if (axis != SDL_GAMEPAD_AXIS_INVALID && positive_axis) { + // avoid double-updating axes, but don't skip directional button bindings + float multiplier = 1.0; + int deadzone = 0; + auto ApplyDeadzone = [](s16* value, int deadzone) { + if (std::abs(*value) <= deadzone) { + *value = 0; + } + }; + Axis c_axis = GetAxisFromSDLAxis(axis); + switch (c_axis) { + case Axis::LeftX: + case Axis::LeftY: + ApplyDeadzone(new_param, leftjoystick_deadzone); + multiplier = leftjoystick_halfmode ? 0.5 : 1.0; + break; + case Axis::RightX: + case Axis::RightY: + ApplyDeadzone(new_param, rightjoystick_deadzone); + multiplier = rightjoystick_halfmode ? 0.5 : 1.0; + break; + case Axis::TriggerLeft: + ApplyDeadzone(new_param, lefttrigger_deadzone); + controller->Axis(0, c_axis, GetAxis(0x0, 0x80, *new_param)); + controller->CheckButton(0, OrbisPadButtonDataOffset::L2, *new_param > 0x20); + return; + case Axis::TriggerRight: + ApplyDeadzone(new_param, righttrigger_deadzone); + controller->Axis(0, c_axis, GetAxis(0x0, 0x80, *new_param)); + controller->CheckButton(0, OrbisPadButtonDataOffset::R2, *new_param > 0x20); + return; + default: + break; + } + controller->Axis(0, c_axis, GetAxis(-0x80, 0x80, *new_param * multiplier)); + } +} + +// Updates the list of pressed keys with the given input. +// Returns whether the list was updated or not. +bool UpdatePressedKeys(InputEvent event) { + // Skip invalid inputs + InputID input = event.input; + if (input.sdl_id == (u32)-1) { + return false; + } + if (input.type == InputType::Axis) { + // analog input, it gets added when it first sends an event, + // and from there, it only changes the parameter + auto it = std::lower_bound(pressed_keys.begin(), pressed_keys.end(), input, + [](const std::pair& e, InputID i) { + return std::tie(e.first.input.type, e.first.input.sdl_id) < + std::tie(i.type, i.sdl_id); + }); + if (it == pressed_keys.end() || it->first.input != input) { + pressed_keys.insert(it, {event, false}); + LOG_DEBUG(Input, "Added axis {} to the input list", event.input.sdl_id); + } else { + it->first.axis_value = event.axis_value; + } + return true; + } else if (event.active) { + // Find the correct position for insertion to maintain order + auto it = std::lower_bound(pressed_keys.begin(), pressed_keys.end(), input, + [](const std::pair& e, InputID i) { + return std::tie(e.first.input.type, e.first.input.sdl_id) < + std::tie(i.type, i.sdl_id); + }); + + // Insert only if 'value' is not already in the list + if (it == pressed_keys.end() || it->first.input != input) { + pressed_keys.insert(it, {event, false}); + return true; + } + } else { + // Remove 'value' from the list if it's not pressed + auto it = std::find_if( + pressed_keys.begin(), pressed_keys.end(), + [input](const std::pair& e) { return e.first.input == input; }); + if (it != pressed_keys.end()) { + pressed_keys.erase(it); + return true; + } + } + LOG_DEBUG(Input, "No change was made!"); + return false; +} +// Check if the binding's all keys are currently active. +// It also extracts the analog inputs' parameters, and updates the input hierarchy flags. +InputEvent BindingConnection::ProcessBinding() { + // the last key is always set (if the connection isn't empty), + // and the analog inputs are always the last one due to how they are sorted, + // so this signifies whether or not the input is analog + InputEvent event = InputEvent(binding.keys[0]); + if (pressed_keys.empty()) { + return event; + } + if (event.input.type != InputType::Axis) { + // for button inputs + event.axis_value = axis_param; + } + // it's a bit scuffed, but if the output is a toggle, then we put the key here + if (output->button == KEY_TOGGLE) { + event.input = toggle; + } + + // Extract keys from InputBinding and ignore unused or toggled keys + std::list input_keys = {binding.keys[0], binding.keys[1], binding.keys[2]}; + input_keys.remove(InputID()); + for (auto key = input_keys.begin(); key != input_keys.end();) { + if (std::find(toggled_keys.begin(), toggled_keys.end(), *key) != toggled_keys.end()) { + key = input_keys.erase(key); // Use the returned iterator + } else { + ++key; // Increment only if no erase happened + } + } + if (input_keys.empty()) { + LOG_DEBUG(Input, "No actual inputs to check, returning true"); + event.active = true; + return event; + } + + // Iterator for pressed_keys, starting from the beginning + auto pressed_it = pressed_keys.begin(); + + // Store pointers to flags in pressed_keys that need to be set if all keys are active + std::list flags_to_set; + + // Check if all keys in input_keys are active + for (InputID key : input_keys) { + bool key_found = false; + + while (pressed_it != pressed_keys.end()) { + if (pressed_it->first.input == key && (pressed_it->second == false)) { + key_found = true; + if (output->positive_axis) { + flags_to_set.push_back(&pressed_it->second); + } + if (pressed_it->first.input.type == InputType::Axis) { + event.axis_value = pressed_it->first.axis_value; + } + ++pressed_it; + break; + } + ++pressed_it; + } + if (!key_found) { + return event; + } + } + + for (bool* flag : flags_to_set) { + *flag = true; + } + if (binding.keys[0].type != InputType::Axis) { // the axes spam inputs, making this unreadable + LOG_DEBUG(Input, "Input found: {}", binding.ToString()); + } + event.active = true; + return event; // All keys are active +} + +void ActivateOutputsFromInputs() { + // Reset values and flags + for (auto& it : pressed_keys) { + it.second = false; + } + for (auto& it : output_array) { + it.ResetUpdate(); + } + + // Iterate over all inputs, and update their respecive outputs accordingly + for (auto& it : connections) { + it.output->AddUpdate(it.ProcessBinding()); + } + + // Update all outputs + for (auto& it : output_array) { + it.FinalizeUpdate(); + } +} + +} // namespace Input diff --git a/src/input/input_handler.h b/src/input/input_handler.h new file mode 100644 index 000000000..0178e7937 --- /dev/null +++ b/src/input/input_handler.h @@ -0,0 +1,407 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include + +#include "SDL3/SDL_events.h" +#include "SDL3/SDL_timer.h" + +#include "common/logging/log.h" +#include "common/types.h" +#include "core/libraries/pad/pad.h" +#include "fmt/format.h" +#include "input/controller.h" + +// +1 and +2 is taken +#define SDL_MOUSE_WHEEL_UP SDL_EVENT_MOUSE_WHEEL + 3 +#define SDL_MOUSE_WHEEL_DOWN SDL_EVENT_MOUSE_WHEEL + 4 +#define SDL_MOUSE_WHEEL_LEFT SDL_EVENT_MOUSE_WHEEL + 5 +#define SDL_MOUSE_WHEEL_RIGHT SDL_EVENT_MOUSE_WHEEL + 7 + +// idk who already used what where so I just chose a big number +#define SDL_EVENT_MOUSE_WHEEL_OFF SDL_EVENT_USER + 10 + +#define LEFTJOYSTICK_HALFMODE 0x00010000 +#define RIGHTJOYSTICK_HALFMODE 0x00020000 +#define BACK_BUTTON 0x00040000 + +#define KEY_TOGGLE 0x00200000 + +namespace Input { +using Input::Axis; +using Libraries::Pad::OrbisPadButtonDataOffset; + +struct AxisMapping { + u32 axis; + s16 value; + AxisMapping(SDL_GamepadAxis a, s16 v) : axis(a), value(v) {} +}; + +enum class InputType { Axis, KeyboardMouse, Controller, Count }; +const std::array input_type_names = {"Axis", "KBM", "Controller", "Unknown"}; + +class InputID { +public: + InputType type; + u32 sdl_id; + InputID(InputType d = InputType::Count, u32 i = (u32)-1) : type(d), sdl_id(i) {} + bool operator==(const InputID& o) const { + return type == o.type && sdl_id == o.sdl_id; + } + bool operator!=(const InputID& o) const { + return type != o.type || sdl_id != o.sdl_id; + } + bool operator<=(const InputID& o) const { + return type <= o.type && sdl_id <= o.sdl_id; + } + bool IsValid() const { + return *this != InputID(); + } + std::string ToString() { + return fmt::format("({}: {:x})", input_type_names[(u8)type], sdl_id); + } +}; + +class InputEvent { +public: + InputID input; + bool active; + s8 axis_value; + + InputEvent(InputID i = InputID(), bool a = false, s8 v = 0) + : input(i), active(a), axis_value(v) {} + InputEvent(InputType d, u32 i, bool a = false, s8 v = 0) + : input(d, i), active(a), axis_value(v) {} +}; + +// i strongly suggest you collapse these maps +const std::map string_to_cbutton_map = { + {"triangle", SDL_GAMEPAD_BUTTON_NORTH}, + {"circle", SDL_GAMEPAD_BUTTON_EAST}, + {"cross", SDL_GAMEPAD_BUTTON_SOUTH}, + {"square", SDL_GAMEPAD_BUTTON_WEST}, + {"l1", SDL_GAMEPAD_BUTTON_LEFT_SHOULDER}, + {"r1", SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER}, + {"l3", SDL_GAMEPAD_BUTTON_LEFT_STICK}, + {"r3", SDL_GAMEPAD_BUTTON_RIGHT_STICK}, + {"pad_up", SDL_GAMEPAD_BUTTON_DPAD_UP}, + {"pad_down", SDL_GAMEPAD_BUTTON_DPAD_DOWN}, + {"pad_left", SDL_GAMEPAD_BUTTON_DPAD_LEFT}, + {"pad_right", SDL_GAMEPAD_BUTTON_DPAD_RIGHT}, + {"options", SDL_GAMEPAD_BUTTON_START}, + + // these are outputs only (touchpad can only be bound to itself) + {"touchpad", SDL_GAMEPAD_BUTTON_TOUCHPAD}, + {"leftjoystick_halfmode", LEFTJOYSTICK_HALFMODE}, + {"rightjoystick_halfmode", RIGHTJOYSTICK_HALFMODE}, + + // this is only for input + {"back", SDL_GAMEPAD_BUTTON_BACK}, +}; + +const std::map string_to_axis_map = { + {"axis_left_x_plus", {SDL_GAMEPAD_AXIS_LEFTX, 127}}, + {"axis_left_x_minus", {SDL_GAMEPAD_AXIS_LEFTX, -127}}, + {"axis_left_y_plus", {SDL_GAMEPAD_AXIS_LEFTY, 127}}, + {"axis_left_y_minus", {SDL_GAMEPAD_AXIS_LEFTY, -127}}, + {"axis_right_x_plus", {SDL_GAMEPAD_AXIS_RIGHTX, 127}}, + {"axis_right_x_minus", {SDL_GAMEPAD_AXIS_RIGHTX, -127}}, + {"axis_right_y_plus", {SDL_GAMEPAD_AXIS_RIGHTY, 127}}, + {"axis_right_y_minus", {SDL_GAMEPAD_AXIS_RIGHTY, -127}}, + + {"l2", {SDL_GAMEPAD_AXIS_LEFT_TRIGGER, 127}}, + {"r2", {SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, 127}}, + + // should only use these to bind analog inputs to analog outputs + {"axis_left_x", {SDL_GAMEPAD_AXIS_LEFTX, 127}}, + {"axis_left_y", {SDL_GAMEPAD_AXIS_LEFTY, 127}}, + {"axis_right_x", {SDL_GAMEPAD_AXIS_RIGHTX, 127}}, + {"axis_right_y", {SDL_GAMEPAD_AXIS_RIGHTY, 127}}, +}; +const std::map string_to_keyboard_key_map = { + {"a", SDLK_A}, + {"b", SDLK_B}, + {"c", SDLK_C}, + {"d", SDLK_D}, + {"e", SDLK_E}, + {"f", SDLK_F}, + {"g", SDLK_G}, + {"h", SDLK_H}, + {"i", SDLK_I}, + {"j", SDLK_J}, + {"k", SDLK_K}, + {"l", SDLK_L}, + {"m", SDLK_M}, + {"n", SDLK_N}, + {"o", SDLK_O}, + {"p", SDLK_P}, + {"q", SDLK_Q}, + {"r", SDLK_R}, + {"s", SDLK_S}, + {"t", SDLK_T}, + {"u", SDLK_U}, + {"v", SDLK_V}, + {"w", SDLK_W}, + {"x", SDLK_X}, + {"y", SDLK_Y}, + {"z", SDLK_Z}, + {"0", SDLK_0}, + {"1", SDLK_1}, + {"2", SDLK_2}, + {"3", SDLK_3}, + {"4", SDLK_4}, + {"5", SDLK_5}, + {"6", SDLK_6}, + {"7", SDLK_7}, + {"8", SDLK_8}, + {"9", SDLK_9}, + {"kp0", SDLK_KP_0}, + {"kp1", SDLK_KP_1}, + {"kp2", SDLK_KP_2}, + {"kp3", SDLK_KP_3}, + {"kp4", SDLK_KP_4}, + {"kp5", SDLK_KP_5}, + {"kp6", SDLK_KP_6}, + {"kp7", SDLK_KP_7}, + {"kp8", SDLK_KP_8}, + {"kp9", SDLK_KP_9}, + {"comma", SDLK_COMMA}, + {"period", SDLK_PERIOD}, + {"question", SDLK_QUESTION}, + {"semicolon", SDLK_SEMICOLON}, + {"minus", SDLK_MINUS}, + {"underscore", SDLK_UNDERSCORE}, + {"lparenthesis", SDLK_LEFTPAREN}, + {"rparenthesis", SDLK_RIGHTPAREN}, + {"lbracket", SDLK_LEFTBRACKET}, + {"rbracket", SDLK_RIGHTBRACKET}, + {"lbrace", SDLK_LEFTBRACE}, + {"rbrace", SDLK_RIGHTBRACE}, + {"backslash", SDLK_BACKSLASH}, + {"dash", SDLK_SLASH}, + {"enter", SDLK_RETURN}, + {"space", SDLK_SPACE}, + {"tab", SDLK_TAB}, + {"backspace", SDLK_BACKSPACE}, + {"escape", SDLK_ESCAPE}, + {"left", SDLK_LEFT}, + {"right", SDLK_RIGHT}, + {"up", SDLK_UP}, + {"down", SDLK_DOWN}, + {"lctrl", SDLK_LCTRL}, + {"rctrl", SDLK_RCTRL}, + {"lshift", SDLK_LSHIFT}, + {"rshift", SDLK_RSHIFT}, + {"lalt", SDLK_LALT}, + {"ralt", SDLK_RALT}, + {"lmeta", SDLK_LGUI}, + {"rmeta", SDLK_RGUI}, + {"lwin", SDLK_LGUI}, + {"rwin", SDLK_RGUI}, + {"home", SDLK_HOME}, + {"end", SDLK_END}, + {"pgup", SDLK_PAGEUP}, + {"pgdown", SDLK_PAGEDOWN}, + {"leftbutton", SDL_BUTTON_LEFT}, + {"rightbutton", SDL_BUTTON_RIGHT}, + {"middlebutton", SDL_BUTTON_MIDDLE}, + {"sidebuttonback", SDL_BUTTON_X1}, + {"sidebuttonforward", SDL_BUTTON_X2}, + {"mousewheelup", SDL_MOUSE_WHEEL_UP}, + {"mousewheeldown", SDL_MOUSE_WHEEL_DOWN}, + {"mousewheelleft", SDL_MOUSE_WHEEL_LEFT}, + {"mousewheelright", SDL_MOUSE_WHEEL_RIGHT}, + {"kpperiod", SDLK_KP_PERIOD}, + {"kpcomma", SDLK_KP_COMMA}, + {"kpdivide", SDLK_KP_DIVIDE}, + {"kpmultiply", SDLK_KP_MULTIPLY}, + {"kpminus", SDLK_KP_MINUS}, + {"kpplus", SDLK_KP_PLUS}, + {"kpenter", SDLK_KP_ENTER}, + {"kpequals", SDLK_KP_EQUALS}, + {"capslock", SDLK_CAPSLOCK}, +}; + +void ParseInputConfig(const std::string game_id); + +class InputBinding { +public: + InputID keys[3]; + InputBinding(InputID k1 = InputID(), InputID k2 = InputID(), InputID k3 = InputID()) { + // we format the keys so comparing them will be very fast, because we will only have to + // compare 3 sorted elements, where the only possible duplicate item is 0 + + // duplicate entries get changed to one original, one null + if (k1 == k2 && k1 != InputID()) { + k2 = InputID(); + } + if (k1 == k3 && k1 != InputID()) { + k3 = InputID(); + } + if (k3 == k2 && k2 != InputID()) { + k2 = InputID(); + } + // this sorts them + if (k1 <= k2 && k1 <= k3) { + keys[0] = k1; + if (k2 <= k3) { + keys[1] = k2; + keys[2] = k3; + } else { + keys[1] = k3; + keys[2] = k2; + } + } else if (k2 <= k1 && k2 <= k3) { + keys[0] = k2; + if (k1 <= k3) { + keys[1] = k1; + keys[2] = k3; + } else { + keys[1] = k3; + keys[2] = k1; + } + } else { + keys[0] = k3; + if (k1 <= k2) { + keys[1] = k1; + keys[2] = k2; + } else { + keys[1] = k2; + keys[3] = k1; + } + } + } + // copy ctor + InputBinding(const InputBinding& o) { + keys[0] = o.keys[0]; + keys[1] = o.keys[1]; + keys[2] = o.keys[2]; + } + + inline bool operator==(const InputBinding& o) { + // InputID() signifies an unused slot + return (keys[0] == o.keys[0] || keys[0] == InputID() || o.keys[0] == InputID()) && + (keys[1] == o.keys[1] || keys[1] == InputID() || o.keys[1] == InputID()) && + (keys[2] == o.keys[2] || keys[2] == InputID() || o.keys[2] == InputID()); + // it is already very fast, + // but reverse order makes it check the actual keys first instead of possible 0-s, + // potenially skipping the later expressions of the three-way AND + } + inline int KeyCount() const { + return (keys[0].IsValid() ? 1 : 0) + (keys[1].IsValid() ? 1 : 0) + + (keys[2].IsValid() ? 1 : 0); + } + // Sorts by the amount of non zero keys - left side is 'bigger' here + bool operator<(const InputBinding& other) const { + return KeyCount() > other.KeyCount(); + } + inline bool IsEmpty() { + return !(keys[0].IsValid() || keys[1].IsValid() || keys[2].IsValid()); + } + std::string ToString() { // todo add device type + switch (KeyCount()) { + case 1: + return fmt::format("({})", keys[0].ToString()); + case 2: + return fmt::format("({}, {})", keys[0].ToString(), keys[1].ToString()); + case 3: + return fmt::format("({}, {}, {})", keys[0].ToString(), keys[1].ToString(), + keys[2].ToString()); + default: + return "Empty"; + } + } + + // returns an InputEvent based on the event type (keyboard, mouse buttons/wheel, or controller) + static InputEvent GetInputEventFromSDLEvent(const SDL_Event& e); +}; +class ControllerOutput { + static GameController* controller; + +public: + static void SetControllerOutputController(GameController* c); + static void LinkJoystickAxes(); + + u32 button; + u32 axis; + // these are only used as s8, + // but I added some padding to avoid overflow if it's activated by multiple inputs + // axis_plus and axis_minus pairs share a common new_param, the other outputs have their own + s16 old_param; + s16* new_param; + bool old_button_state, new_button_state, state_changed, positive_axis; + + ControllerOutput(const u32 b, u32 a = SDL_GAMEPAD_AXIS_INVALID, bool p = true) { + button = b; + axis = a; + new_param = new s16(0); + old_param = 0; + positive_axis = p; + } + ControllerOutput(const ControllerOutput& o) : button(o.button), axis(o.axis) { + new_param = new s16(*o.new_param); + } + ~ControllerOutput() { + delete new_param; + } + inline bool operator==(const ControllerOutput& o) const { // fucking consts everywhere + return button == o.button && axis == o.axis; + } + inline bool operator!=(const ControllerOutput& o) const { + return button != o.button || axis != o.axis; + } + std::string ToString() const { + return fmt::format("({}, {}, {})", (s32)button, (int)axis, old_param); + } + inline bool IsButton() const { + return axis == SDL_GAMEPAD_AXIS_INVALID && button != SDL_GAMEPAD_BUTTON_INVALID; + } + inline bool IsAxis() const { + return axis != SDL_GAMEPAD_AXIS_INVALID && button == SDL_GAMEPAD_BUTTON_INVALID; + } + + void ResetUpdate(); + void AddUpdate(InputEvent event); + void FinalizeUpdate(); +}; +class BindingConnection { +public: + InputBinding binding; + ControllerOutput* output; + u32 axis_param; + InputID toggle; + + BindingConnection(InputBinding b, ControllerOutput* out, u32 param = 0, InputID t = InputID()) { + binding = b; + axis_param = param; + output = out; + toggle = t; + } + bool operator<(const BindingConnection& other) const { + // a button is a higher priority than an axis, as buttons can influence axes + // (e.g. joystick_halfmode) + if (output->IsButton() && + (other.output->IsAxis() && (other.output->axis != SDL_GAMEPAD_AXIS_LEFT_TRIGGER && + other.output->axis != SDL_GAMEPAD_AXIS_RIGHT_TRIGGER))) { + return true; + } + if (binding < other.binding) { + return true; + } + return false; + } + InputEvent ProcessBinding(); +}; + +// Updates the list of pressed keys with the given input. +// Returns whether the list was updated or not. +bool UpdatePressedKeys(InputEvent event); + +void ActivateOutputsFromInputs(); + +} // namespace Input diff --git a/src/input/input_mouse.cpp b/src/input/input_mouse.cpp new file mode 100644 index 000000000..11feaeebb --- /dev/null +++ b/src/input/input_mouse.cpp @@ -0,0 +1,74 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include + +#include "common/types.h" +#include "input/controller.h" +#include "input_mouse.h" + +#include "SDL3/SDL.h" + +namespace Input { + +int mouse_joystick_binding = 0; +float mouse_deadzone_offset = 0.5, mouse_speed = 1, mouse_speed_offset = 0.1250; +Uint32 mouse_polling_id = 0; +bool mouse_enabled = false; + +// We had to go through 3 files of indirection just to update a flag +void ToggleMouseEnabled() { + mouse_enabled = !mouse_enabled; +} + +void SetMouseToJoystick(int joystick) { + mouse_joystick_binding = joystick; +} + +void SetMouseParams(float mdo, float ms, float mso) { + mouse_deadzone_offset = mdo; + mouse_speed = ms; + mouse_speed_offset = mso; +} + +Uint32 MousePolling(void* param, Uint32 id, Uint32 interval) { + auto* controller = (GameController*)param; + if (!mouse_enabled) + return interval; + + Axis axis_x, axis_y; + switch (mouse_joystick_binding) { + case 1: + axis_x = Axis::LeftX; + axis_y = Axis::LeftY; + break; + case 2: + axis_x = Axis::RightX; + axis_y = Axis::RightY; + break; + default: + return interval; // no update needed + } + + float d_x = 0, d_y = 0; + SDL_GetRelativeMouseState(&d_x, &d_y); + + float output_speed = + SDL_clamp((sqrt(d_x * d_x + d_y * d_y) + mouse_speed_offset * 128) * mouse_speed, + mouse_deadzone_offset * 128, 128.0); + + float angle = atan2(d_y, d_x); + float a_x = cos(angle) * output_speed, a_y = sin(angle) * output_speed; + + if (d_x != 0 && d_y != 0) { + controller->Axis(0, axis_x, GetAxis(-0x80, 0x80, a_x)); + controller->Axis(0, axis_y, GetAxis(-0x80, 0x80, a_y)); + } else { + controller->Axis(0, axis_x, GetAxis(-0x80, 0x80, 0)); + controller->Axis(0, axis_y, GetAxis(-0x80, 0x80, 0)); + } + + return interval; +} + +} // namespace Input diff --git a/src/input/input_mouse.h b/src/input/input_mouse.h new file mode 100644 index 000000000..da18ee04e --- /dev/null +++ b/src/input/input_mouse.h @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "SDL3/SDL.h" +#include "common/types.h" + +namespace Input { + +void ToggleMouseEnabled(); +void SetMouseToJoystick(int joystick); +void SetMouseParams(float mouse_deadzone_offset, float mouse_speed, float mouse_speed_offset); + +// Polls the mouse for changes, and simulates joystick movement from it. +Uint32 MousePolling(void* param, Uint32 id, Uint32 interval); + +} // namespace Input diff --git a/src/qt_gui/kbm_config_dialog.cpp b/src/qt_gui/kbm_config_dialog.cpp new file mode 100644 index 000000000..af198f79d --- /dev/null +++ b/src/qt_gui/kbm_config_dialog.cpp @@ -0,0 +1,237 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "kbm_config_dialog.h" +#include "kbm_help_dialog.h" + +#include +#include +#include +#include "common/config.h" +#include "common/path_util.h" +#include "game_info.h" +#include "src/sdl_window.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +QString previous_game = "default"; +bool isHelpOpen = false; +HelpDialog* helpDialog; + +EditorDialog::EditorDialog(QWidget* parent) : QDialog(parent) { + + setWindowTitle("Edit Keyboard + Mouse and Controller input bindings"); + resize(600, 400); + + // Create the editor widget + editor = new QPlainTextEdit(this); + editorFont.setPointSize(10); // Set default text size + editor->setFont(editorFont); // Apply font to the editor + + // Create the game selection combo box + gameComboBox = new QComboBox(this); + gameComboBox->addItem("default"); // Add default option + /* + gameComboBox = new QComboBox(this); + layout->addWidget(gameComboBox); // Add the combobox for selecting game configurations + + // Populate the combo box with game configurations + QStringList gameConfigs = GameInfoClass::GetGameInfo(this); + gameComboBox->addItems(gameConfigs); + gameComboBox->setCurrentText("default.ini"); // Set the default selection + */ + // Load all installed games + loadInstalledGames(); + + // Create Save, Cancel, and Help buttons + QPushButton* saveButton = new QPushButton("Save", this); + QPushButton* cancelButton = new QPushButton("Cancel", this); + QPushButton* helpButton = new QPushButton("Help", this); + QPushButton* defaultButton = new QPushButton("Default", this); + + // Layout for the game selection and buttons + QHBoxLayout* topLayout = new QHBoxLayout(); + topLayout->addWidget(gameComboBox); + topLayout->addStretch(); + topLayout->addWidget(saveButton); + topLayout->addWidget(cancelButton); + topLayout->addWidget(defaultButton); + topLayout->addWidget(helpButton); + + // Main layout with editor and buttons + QVBoxLayout* layout = new QVBoxLayout(this); + layout->addLayout(topLayout); + layout->addWidget(editor); + + // Load the default config file content into the editor + loadFile(gameComboBox->currentText()); + + // Connect button and combo box signals + connect(saveButton, &QPushButton::clicked, this, &EditorDialog::onSaveClicked); + connect(cancelButton, &QPushButton::clicked, this, &EditorDialog::onCancelClicked); + connect(helpButton, &QPushButton::clicked, this, &EditorDialog::onHelpClicked); + connect(defaultButton, &QPushButton::clicked, this, &EditorDialog::onResetToDefaultClicked); + connect(gameComboBox, &QComboBox::currentTextChanged, this, + &EditorDialog::onGameSelectionChanged); +} + +void EditorDialog::loadFile(QString game) { + + const auto config_file = Config::GetFoolproofKbmConfigFile(game.toStdString()); + QFile file(config_file); + + if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { + QTextStream in(&file); + editor->setPlainText(in.readAll()); + originalConfig = editor->toPlainText(); + file.close(); + } else { + QMessageBox::warning(this, "Error", "Could not open the file for reading"); + } +} + +void EditorDialog::saveFile(QString game) { + + const auto config_file = Config::GetFoolproofKbmConfigFile(game.toStdString()); + QFile file(config_file); + + if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { + QTextStream out(&file); + out << editor->toPlainText(); + file.close(); + } else { + QMessageBox::warning(this, "Error", "Could not open the file for writing"); + } +} + +// Override the close event to show the save confirmation dialog only if changes were made +void EditorDialog::closeEvent(QCloseEvent* event) { + if (isHelpOpen) { + helpDialog->close(); + isHelpOpen = false; + // at this point I might have to add this flag and the help dialog to the class itself + } + if (hasUnsavedChanges()) { + QMessageBox::StandardButton reply; + reply = QMessageBox::question(this, "Save Changes", "Do you want to save changes?", + QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); + + if (reply == QMessageBox::Yes) { + saveFile(gameComboBox->currentText()); + event->accept(); // Close the dialog + } else if (reply == QMessageBox::No) { + event->accept(); // Close the dialog without saving + } else { + event->ignore(); // Cancel the close event + } + } else { + event->accept(); // No changes, close the dialog without prompting + } +} +void EditorDialog::keyPressEvent(QKeyEvent* event) { + if (event->key() == Qt::Key_Escape) { + if (isHelpOpen) { + helpDialog->close(); + isHelpOpen = false; + } + close(); // Trigger the close action, same as pressing the close button + } else { + QDialog::keyPressEvent(event); // Call the base class implementation for other keys + } +} + +void EditorDialog::onSaveClicked() { + if (isHelpOpen) { + helpDialog->close(); + isHelpOpen = false; + } + saveFile(gameComboBox->currentText()); + reject(); // Close the dialog +} + +void EditorDialog::onCancelClicked() { + if (isHelpOpen) { + helpDialog->close(); + isHelpOpen = false; + } + reject(); // Close the dialog +} + +void EditorDialog::onHelpClicked() { + if (!isHelpOpen) { + helpDialog = new HelpDialog(&isHelpOpen, this); + helpDialog->setWindowTitle("Help"); + helpDialog->setAttribute(Qt::WA_DeleteOnClose); // Clean up on close + // Get the position and size of the Config window + QRect configGeometry = this->geometry(); + int helpX = configGeometry.x() + configGeometry.width() + 10; // 10 pixels offset + int helpY = configGeometry.y(); + // Move the Help dialog to the right side of the Config window + helpDialog->move(helpX, helpY); + helpDialog->show(); + isHelpOpen = true; + } else { + helpDialog->close(); + isHelpOpen = false; + } +} + +void EditorDialog::onResetToDefaultClicked() { + bool default_default = gameComboBox->currentText() == "default"; + QString prompt = + default_default + ? "Do you want to reset your custom default config to the original default config?" + : "Do you want to reset this config to your custom default config?"; + QMessageBox::StandardButton reply = + QMessageBox::question(this, "Reset to Default", prompt, QMessageBox::Yes | QMessageBox::No); + + if (reply == QMessageBox::Yes) { + if (default_default) { + const auto default_file = Config::GetFoolproofKbmConfigFile("default"); + std::filesystem::remove(default_file); + } + const auto config_file = Config::GetFoolproofKbmConfigFile("default"); + QFile file(config_file); + + if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { + QTextStream in(&file); + editor->setPlainText(in.readAll()); + file.close(); + } else { + QMessageBox::warning(this, "Error", "Could not open the file for reading"); + } + // saveFile(gameComboBox->currentText()); + } +} + +bool EditorDialog::hasUnsavedChanges() { + // Compare the current content with the original content to check if there are unsaved changes + return editor->toPlainText() != originalConfig; +} +void EditorDialog::loadInstalledGames() { + previous_game = "default"; + QStringList filePaths; + for (const auto& installLoc : Config::getGameInstallDirs()) { + QString installDir; + Common::FS::PathToQString(installDir, installLoc); + QDir parentFolder(installDir); + QFileInfoList fileList = parentFolder.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); + for (const auto& fileInfo : fileList) { + if (fileInfo.isDir() && !fileInfo.filePath().endsWith("-UPDATE")) { + gameComboBox->addItem(fileInfo.fileName()); // Add game name to combo box + } + } + } +} +void EditorDialog::onGameSelectionChanged(const QString& game) { + saveFile(previous_game); + loadFile(gameComboBox->currentText()); // Reload file based on the selected game + previous_game = gameComboBox->currentText(); +} diff --git a/src/qt_gui/kbm_config_dialog.h b/src/qt_gui/kbm_config_dialog.h new file mode 100644 index 000000000..f436b4a71 --- /dev/null +++ b/src/qt_gui/kbm_config_dialog.h @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include "string" + +class EditorDialog : public QDialog { + Q_OBJECT // Necessary for using Qt's meta-object system (signals/slots) + public : explicit EditorDialog(QWidget* parent = nullptr); // Constructor + +protected: + void closeEvent(QCloseEvent* event) override; // Override close event + void keyPressEvent(QKeyEvent* event) override; + +private: + QPlainTextEdit* editor; // Editor widget for the config file + QFont editorFont; // To handle the text size + QString originalConfig; // Starting config string + std::string gameId; + + QComboBox* gameComboBox; // Combo box for selecting game configurations + + void loadFile(QString game); // Function to load the config file + void saveFile(QString game); // Function to save the config file + void loadInstalledGames(); // Helper to populate gameComboBox + bool hasUnsavedChanges(); // Checks for unsaved changes + +private slots: + void onSaveClicked(); // Save button slot + void onCancelClicked(); // Slot for handling cancel button + void onHelpClicked(); // Slot for handling help button + void onResetToDefaultClicked(); + void onGameSelectionChanged(const QString& game); // Slot for game selection changes +}; diff --git a/src/qt_gui/kbm_help_dialog.cpp b/src/qt_gui/kbm_help_dialog.cpp new file mode 100644 index 000000000..44f75f6f8 --- /dev/null +++ b/src/qt_gui/kbm_help_dialog.cpp @@ -0,0 +1,112 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "kbm_help_dialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +ExpandableSection::ExpandableSection(const QString& title, const QString& content, + QWidget* parent = nullptr) + : QWidget(parent) { + QVBoxLayout* layout = new QVBoxLayout(this); + + // Button to toggle visibility of content + toggleButton = new QPushButton(title); + layout->addWidget(toggleButton); + + // QTextBrowser for content (initially hidden) + contentBrowser = new QTextBrowser(); + contentBrowser->setPlainText(content); + contentBrowser->setVisible(false); + + // Remove scrollbars from QTextBrowser + contentBrowser->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + contentBrowser->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + // Set size policy to allow vertical stretching only + contentBrowser->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); + + // Calculate and set initial height based on content + updateContentHeight(); + + layout->addWidget(contentBrowser); + + // Connect button click to toggle visibility + connect(toggleButton, &QPushButton::clicked, [this]() { + contentBrowser->setVisible(!contentBrowser->isVisible()); + if (contentBrowser->isVisible()) { + updateContentHeight(); // Update height when expanding + } + emit expandedChanged(); // Notify for layout adjustments + }); + + // Connect to update height if content changes + connect(contentBrowser->document(), &QTextDocument::contentsChanged, this, + &ExpandableSection::updateContentHeight); + + // Minimal layout settings for spacing + layout->setSpacing(2); + layout->setContentsMargins(0, 0, 0, 0); +} + +void HelpDialog::closeEvent(QCloseEvent* event) { + *help_open_ptr = false; + close(); +} +void HelpDialog::reject() { + *help_open_ptr = false; + close(); +} + +HelpDialog::HelpDialog(bool* open_flag, QWidget* parent) : QDialog(parent) { + help_open_ptr = open_flag; + // Main layout for the help dialog + QVBoxLayout* mainLayout = new QVBoxLayout(this); + + // Container widget for the scroll area + QWidget* containerWidget = new QWidget; + QVBoxLayout* containerLayout = new QVBoxLayout(containerWidget); + + // Add expandable sections to container layout + auto* quickstartSection = new ExpandableSection("Quickstart", quickstart()); + auto* faqSection = new ExpandableSection("FAQ", faq()); + auto* syntaxSection = new ExpandableSection("Syntax", syntax()); + auto* specialSection = new ExpandableSection("Special Bindings", special()); + auto* bindingsSection = new ExpandableSection("Keybindings", bindings()); + + containerLayout->addWidget(quickstartSection); + containerLayout->addWidget(faqSection); + containerLayout->addWidget(syntaxSection); + containerLayout->addWidget(specialSection); + containerLayout->addWidget(bindingsSection); + containerLayout->addStretch(1); + + // Scroll area wrapping the container + QScrollArea* scrollArea = new QScrollArea; + scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + scrollArea->setWidgetResizable(true); + scrollArea->setWidget(containerWidget); + + // Add the scroll area to the main dialog layout + mainLayout->addWidget(scrollArea); + setLayout(mainLayout); + + // Minimum size for the dialog + setMinimumSize(500, 400); + + // Re-adjust dialog layout when any section expands/collapses + connect(quickstartSection, &ExpandableSection::expandedChanged, this, &HelpDialog::adjustSize); + connect(faqSection, &ExpandableSection::expandedChanged, this, &HelpDialog::adjustSize); + connect(syntaxSection, &ExpandableSection::expandedChanged, this, &HelpDialog::adjustSize); + connect(specialSection, &ExpandableSection::expandedChanged, this, &HelpDialog::adjustSize); + connect(bindingsSection, &ExpandableSection::expandedChanged, this, &HelpDialog::adjustSize); +} \ No newline at end of file diff --git a/src/qt_gui/kbm_help_dialog.h b/src/qt_gui/kbm_help_dialog.h new file mode 100644 index 000000000..8d543b7e7 --- /dev/null +++ b/src/qt_gui/kbm_help_dialog.h @@ -0,0 +1,169 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include +#include +#include + +class ExpandableSection : public QWidget { + Q_OBJECT +public: + explicit ExpandableSection(const QString& title, const QString& content, QWidget* parent); + +signals: + void expandedChanged(); // Signal to indicate layout size change + +private: + QPushButton* toggleButton; + QTextBrowser* contentBrowser; // Changed from QLabel to QTextBrowser + QPropertyAnimation* animation; + int contentHeight; + void updateContentHeight() { + int contentHeight = contentBrowser->document()->size().height(); + contentBrowser->setMinimumHeight(contentHeight + 5); + contentBrowser->setMaximumHeight(contentHeight + 50); + } +}; + +class HelpDialog : public QDialog { + Q_OBJECT +public: + explicit HelpDialog(bool* open_flag = nullptr, QWidget* parent = nullptr); + +protected: + void closeEvent(QCloseEvent* event) override; + void reject() override; + +private: + bool* help_open_ptr; + + QString quickstart() { + return + R"(The keyboard and controller remapping backend, GUI and documentation have been written by kalaposfos + +In this section, you will find information about the project, its features and help on setting up your ideal setup. +To view the config file's syntax, check out the Syntax tab, for keybind names, visit Normal Keybinds and Special Bindings, and if you are here to view emulator-wide keybinds, you can find it in the FAQ section. +This project started out because I didn't like the original unchangeable keybinds, but rather than waiting for someone else to do it, I implemented this myself. From the default keybinds, you can clearly tell this was a project built for Bloodborne, but ovbiously you can make adjustments however you like. +)"; + } + QString faq() { + return + R"(Q: What are the emulator-wide keybinds? +A: -F12: Triggers Renderdoc capture +-F11: Toggles fullscreen +-F10: Toggles FPS counter +-Ctrl F10: Open the debug menu +-F9: Pauses emultor, if the debug menu is open +-F8: Reparses the config file while in-game +-F7: Toggles mouse capture and mouse input + +Q: How do I change between mouse and controller joystick input, and why is it even required? +A: You can switch between them with F7, and it is required, because mouse input is done with polling, which means mouse movement is checked every frame, and if it didn't move, the code manually sets the emulator's virtual controller to 0 (back to the center), even if other input devices would update it. + +Q: What happens if I accidentally make a typo in the config? +A: The code recognises the line as wrong, and skip it, so the rest of the file will get parsed, but that line in question will be treated like a comment line. You can find these lines in the log, if you search for 'input_handler'. + +Q: I want to bind to , but your code doesn't support ! +A: Some keys are intentionally omitted, but if you read the bindings through, and you're sure it is not there and isn't one of the intentionally disabled ones, open an issue on https://github.com/shadps4-emu/shadPS4. +)"; + } + QString syntax() { + return + R"(This is the full list of currently supported mouse, keyboard and controller inputs, and how to use them. +Emulator-reserved keys: F1 through F12 + +Syntax (aka how a line can look like): +#Comment line + = , , ; + = , ; + = ; + +Examples: +#Interact +cross = e; +#Heavy attack (in BB) +r2 = leftbutton, lshift; +#Move forward +axis_left_y_minus = w; + +You can make a comment line by putting # as the first character. +Whitespace doesn't matter, =; is just as valid as = ; +';' at the ends of lines is also optional. +)"; + } + QString bindings() { + return + R"(The following names should be interpreted without the '' around them, and for inputs that have left and right versions, only the left one is shown, but the right can be inferred from that. +Example: 'lshift', 'rshift' + +Keyboard: +Alphabet: 'a', 'b', ..., 'z' +Numbers: '0', '1', ..., '9' +Keypad: 'kp0', kp1', ..., 'kp9', 'kpperiod', 'kpcomma', + 'kpdivide', 'kpmultiply', 'kpdivide', 'kpplus', 'kpminus', 'kpenter' +Punctuation and misc: + 'space', 'comma', 'period', 'question', 'semicolon', 'minus', 'plus', 'lparenthesis', 'lbracket', 'lbrace', 'backslash', 'dash', + 'enter', 'tab', backspace', 'escape' +Arrow keys: 'up', 'down', 'left', 'right' +Modifier keys: + 'lctrl', 'lshift', 'lalt', 'lwin' = 'lmeta' (same input, different names, so if you are not on Windows and don't like calling this the Windows key, there is an alternative) + +Mouse: + 'leftbutton', 'rightbutton', 'middlebutton', 'sidebuttonforward', 'sidebuttonback' + The following wheel inputs cannot be bound to axis input, only button: + 'mousewheelup', 'mousewheeldown', 'mousewheelleft', 'mousewheelright' + +Controller: + The touchpad currently can't be rebound to anything else, but you can bind buttons to it. + If you have a controller that has different names for buttons, it will still work, just look up what are the equivalent names for that controller + The same left-right rule still applies here. + Buttons: + 'triangle', 'circle', 'cross', 'square', 'l1', 'l3', + 'options', touchpad', 'up', 'down', 'left', 'right' + Axes if you bind them to a button input: + 'axis_left_x_plus', 'axis_left_x_minus', 'axis_left_y_plus', 'axis_left_y_minus', + 'axis_right_x_plus', ..., 'axis_right_y_minus', + 'l2' + Axes if you bind them to another axis input: + 'axis_left_x' 'axis_left_y' 'axis_right_x' 'axis_right_y', + 'l2' +)"; + } + QString special() { + return + R"(There are some extra bindings you can put into the config file, that don't correspond to a controller input, but rather something else. +You can find these here, with detailed comments, examples and suggestions for most of them. + +'leftjoystick_halfmode' and 'rightjoystick_halfmode' = ; + These are a pair of input modifiers, that change the way keyboard button bound axes work. By default, those push the joystick to the max in their respective direction, but if their respective joystick_halfmode modifier value is true, they only push it... halfway. With this, you can change from run to walk in games like Bloodborne. + +'mouse_to_joystick' = 'none', 'left' or 'right'; + This binds the mouse movement to either joystick. If it recieves a value that is not 'left' or 'right', it defaults to 'none'. + +'mouse_movement_params' = float, float, float; + (If you don't know what a float is, it is a data type that stores non-whole numbers.) + Default values: 0.5, 1, 0.125 + Let's break each parameter down: + 1st: mouse_deadzone_offset: this value should have a value between 0 and 1 (It gets clamped to that range anyway), with 0 being no offset and 1 being pushing the joystick to the max in the direction the mouse moved. + This controls the minimum distance the joystick gets moved, when moving the mouse. If set to 0, it will emulate raw mouse input, which doesn't work very well due to deadzones preventing input if the movement is not large enough. + 2nd: mouse_speed: It's just a standard multiplier to the mouse input speed. + If you input a negative number, the axis directions get reversed (Keep in mind that the offset can still push it back to positive, if it's big enough) + 3rd: mouse_speed_offset: This also should be in the 0 to 1 range, with 0 being no offset and 1 being offsetting to the max possible value. + This is best explained through an example: Let's set mouse_deadzone to 0.5, and this to 0: This means that if we move the mousevery slowly, it still inputs a half-strength joystick input, and if we increase the speed, it would stay that way until we move faster than half the max speed. If we instead set this to 0.25, we now only need to move the mouse faster than the 0.5-0.25=0.25=quarter of the max speed, to get an increase in joystick speed. If we set it to 0.5, then even moving the mouse at 1 pixel per frame will result in a faster-than-minimum speed. + +'key_toggle' = , ; + This assigns a key to another key, and if pressed, toggles that key's virtual value. If it's on, then it doesn't matter if the key is pressed or not, the input handler will treat it as if it's pressed. + You can make an input toggleable with this, for example: Let's say we want to be able to toggle l1 with t. You can then bind l1 to a key you won't use, like kpenter, then bind t to toggle that, so you will end up with this: + l1 = kpenter; + key_toggle = t, kpenter; +'analog_deadzone' = , ; + value goes from 1 to 127 (no deadzone to max deadzone) + devices: leftjoystick, rightjoystick, l2, r2 +)"; + } +}; \ No newline at end of file diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index aa02d1237..de3557992 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include "about_dialog.h" @@ -21,6 +22,9 @@ #include "install_dir_select.h" #include "main_window.h" #include "settings_dialog.h" + +#include "kbm_config_dialog.h" + #include "video_core/renderer_vulkan/vk_instance.h" #ifdef ENABLE_DISCORD_RPC #include "common/discord_rpc_handler.h" @@ -291,6 +295,12 @@ void MainWindow::CreateConnects() { settingsDialog->exec(); }); + // this is the editor for kbm keybinds + connect(ui->controllerButton, &QPushButton::clicked, this, [this]() { + EditorDialog* editorWindow = new EditorDialog(this); + editorWindow->exec(); // Show the editor window modally + }); + #ifdef ENABLE_UPDATER connect(ui->updaterAct, &QAction::triggered, this, [this]() { auto checkUpdate = new CheckUpdate(true); diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index 9b7617925..6eaad62e5 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -1,24 +1,27 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include -#include -#include -#include -#include -#include - +#include "SDL3/SDL_events.h" +#include "SDL3/SDL_hints.h" +#include "SDL3/SDL_init.h" +#include "SDL3/SDL_properties.h" +#include "SDL3/SDL_timer.h" +#include "SDL3/SDL_video.h" #include "common/assert.h" #include "common/config.h" +#include "common/elf_info.h" +#include "common/version.h" #include "core/libraries/kernel/time.h" #include "core/libraries/pad/pad.h" #include "imgui/renderer/imgui_core.h" #include "input/controller.h" +#include "input/input_handler.h" +#include "input/input_mouse.h" #include "sdl_window.h" #include "video_core/renderdoc.h" #ifdef __APPLE__ -#include +#include "SDL3/SDL_metal.h" #endif namespace Input { @@ -290,6 +293,10 @@ WindowSDL::WindowSDL(s32 width_, s32 height_, Input::GameController* controller_ window_info.type = WindowSystemType::Metal; window_info.render_surface = SDL_Metal_GetLayer(SDL_Metal_CreateView(window)); #endif + // input handler init-s + Input::ControllerOutput::SetControllerOutputController(controller); + Input::ControllerOutput::LinkJoystickAxes(); + Input::ParseInputConfig(std::string(Common::ElfInfo::Instance().GameSerial())); } WindowSDL::~WindowSDL() = default; @@ -317,18 +324,28 @@ void WindowSDL::WaitEvent() { is_shown = event.type == SDL_EVENT_WINDOW_EXPOSED; OnResize(); break; + case SDL_EVENT_MOUSE_BUTTON_DOWN: + case SDL_EVENT_MOUSE_BUTTON_UP: + case SDL_EVENT_MOUSE_WHEEL: + case SDL_EVENT_MOUSE_WHEEL_OFF: case SDL_EVENT_KEY_DOWN: case SDL_EVENT_KEY_UP: - OnKeyPress(&event); + OnKeyboardMouseInput(&event); + break; + case SDL_EVENT_GAMEPAD_ADDED: + case SDL_EVENT_GAMEPAD_REMOVED: + controller->SetEngine(std::make_unique()); + break; + case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN: + case SDL_EVENT_GAMEPAD_TOUCHPAD_UP: + case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION: + controller->SetTouchpadState(event.gtouchpad.finger, + event.type != SDL_EVENT_GAMEPAD_TOUCHPAD_UP, event.gtouchpad.x, + event.gtouchpad.y); break; case SDL_EVENT_GAMEPAD_BUTTON_DOWN: case SDL_EVENT_GAMEPAD_BUTTON_UP: case SDL_EVENT_GAMEPAD_AXIS_MOTION: - case SDL_EVENT_GAMEPAD_ADDED: - case SDL_EVENT_GAMEPAD_REMOVED: - case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN: - case SDL_EVENT_GAMEPAD_TOUCHPAD_UP: - case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION: OnGamepadEvent(&event); break; // i really would have appreciated ANY KIND OF DOCUMENTATION ON THIS @@ -355,6 +372,7 @@ void WindowSDL::WaitEvent() { void WindowSDL::InitTimers() { SDL_AddTimer(100, &PollController, controller); + SDL_AddTimer(33, Input::MousePolling, (void*)controller); } void WindowSDL::RequestKeyboard() { @@ -381,239 +399,87 @@ void WindowSDL::OnResize() { ImGui::Core::OnResize(); } -void WindowSDL::OnKeyPress(const SDL_Event* event) { - auto button = OrbisPadButtonDataOffset::None; - Input::Axis axis = Input::Axis::AxisMax; - int axisvalue = 0; - int ax = 0; - std::string backButtonBehavior = Config::getBackButtonBehavior(); - switch (event->key.key) { - case SDLK_UP: - button = OrbisPadButtonDataOffset::Up; - break; - case SDLK_DOWN: - button = OrbisPadButtonDataOffset::Down; - break; - case SDLK_LEFT: - button = OrbisPadButtonDataOffset::Left; - break; - case SDLK_RIGHT: - button = OrbisPadButtonDataOffset::Right; - break; - // Provide alternatives for face buttons for users without a numpad. - case SDLK_KP_8: - case SDLK_C: - button = OrbisPadButtonDataOffset::Triangle; - break; - case SDLK_KP_6: - case SDLK_B: - button = OrbisPadButtonDataOffset::Circle; - break; - case SDLK_KP_2: - case SDLK_N: - button = OrbisPadButtonDataOffset::Cross; - break; - case SDLK_KP_4: - case SDLK_V: - button = OrbisPadButtonDataOffset::Square; - break; - case SDLK_RETURN: - button = OrbisPadButtonDataOffset::Options; - break; - case SDLK_A: - axis = Input::Axis::LeftX; - if (event->type == SDL_EVENT_KEY_DOWN) { - axisvalue += -127; - } else { - axisvalue = 0; +Uint32 wheelOffCallback(void* og_event, Uint32 timer_id, Uint32 interval) { + SDL_Event off_event = *(SDL_Event*)og_event; + off_event.type = SDL_EVENT_MOUSE_WHEEL_OFF; + SDL_PushEvent(&off_event); + delete (SDL_Event*)og_event; + return 0; +} + +void WindowSDL::OnKeyboardMouseInput(const SDL_Event* event) { + using Libraries::Pad::OrbisPadButtonDataOffset; + + // get the event's id, if it's keyup or keydown + const bool input_down = event->type == SDL_EVENT_KEY_DOWN || + event->type == SDL_EVENT_MOUSE_BUTTON_DOWN || + event->type == SDL_EVENT_MOUSE_WHEEL; + Input::InputEvent input_event = Input::InputBinding::GetInputEventFromSDLEvent(*event); + + // Handle window controls outside of the input maps + if (event->type == SDL_EVENT_KEY_DOWN) { + u32 input_id = input_event.input.sdl_id; + // Reparse kbm inputs + if (input_id == SDLK_F8) { + Input::ParseInputConfig(std::string(Common::ElfInfo::Instance().GameSerial())); + return; } - ax = Input::GetAxis(-0x80, 0x80, axisvalue); - break; - case SDLK_D: - axis = Input::Axis::LeftX; - if (event->type == SDL_EVENT_KEY_DOWN) { - axisvalue += 127; - } else { - axisvalue = 0; + // Toggle mouse capture and movement input + else if (input_id == SDLK_F7) { + Input::ToggleMouseEnabled(); + SDL_SetWindowRelativeMouseMode(this->GetSDLWindow(), + !SDL_GetWindowRelativeMouseMode(this->GetSDLWindow())); + return; } - ax = Input::GetAxis(-0x80, 0x80, axisvalue); - break; - case SDLK_W: - axis = Input::Axis::LeftY; - if (event->type == SDL_EVENT_KEY_DOWN) { - axisvalue += -127; - } else { - axisvalue = 0; + // Toggle fullscreen + else if (input_id == SDLK_F11) { + SDL_WindowFlags flag = SDL_GetWindowFlags(window); + bool is_fullscreen = flag & SDL_WINDOW_FULLSCREEN; + SDL_SetWindowFullscreen(window, !is_fullscreen); + return; } - ax = Input::GetAxis(-0x80, 0x80, axisvalue); - break; - case SDLK_S: - axis = Input::Axis::LeftY; - if (event->type == SDL_EVENT_KEY_DOWN) { - axisvalue += 127; - } else { - axisvalue = 0; - } - ax = Input::GetAxis(-0x80, 0x80, axisvalue); - break; - case SDLK_J: - axis = Input::Axis::RightX; - if (event->type == SDL_EVENT_KEY_DOWN) { - axisvalue += -127; - } else { - axisvalue = 0; - } - ax = Input::GetAxis(-0x80, 0x80, axisvalue); - break; - case SDLK_L: - axis = Input::Axis::RightX; - if (event->type == SDL_EVENT_KEY_DOWN) { - axisvalue += 127; - } else { - axisvalue = 0; - } - ax = Input::GetAxis(-0x80, 0x80, axisvalue); - break; - case SDLK_I: - axis = Input::Axis::RightY; - if (event->type == SDL_EVENT_KEY_DOWN) { - axisvalue += -127; - } else { - axisvalue = 0; - } - ax = Input::GetAxis(-0x80, 0x80, axisvalue); - break; - case SDLK_K: - axis = Input::Axis::RightY; - if (event->type == SDL_EVENT_KEY_DOWN) { - axisvalue += 127; - } else { - axisvalue = 0; - } - ax = Input::GetAxis(-0x80, 0x80, axisvalue); - break; - case SDLK_X: - button = OrbisPadButtonDataOffset::L3; - break; - case SDLK_M: - button = OrbisPadButtonDataOffset::R3; - break; - case SDLK_Q: - button = OrbisPadButtonDataOffset::L1; - break; - case SDLK_U: - button = OrbisPadButtonDataOffset::R1; - break; - case SDLK_E: - button = OrbisPadButtonDataOffset::L2; - axis = Input::Axis::TriggerLeft; - if (event->type == SDL_EVENT_KEY_DOWN) { - axisvalue += 255; - } else { - axisvalue = 0; - } - ax = Input::GetAxis(0, 0x80, axisvalue); - break; - case SDLK_O: - button = OrbisPadButtonDataOffset::R2; - axis = Input::Axis::TriggerRight; - if (event->type == SDL_EVENT_KEY_DOWN) { - axisvalue += 255; - } else { - axisvalue = 0; - } - ax = Input::GetAxis(0, 0x80, axisvalue); - break; - case SDLK_SPACE: - if (backButtonBehavior != "none") { - float x = backButtonBehavior == "left" ? 0.25f - : (backButtonBehavior == "right" ? 0.75f : 0.5f); - // trigger a touchpad event so that the touchpad emulation for back button works - controller->SetTouchpadState(0, true, x, 0.5f); - button = OrbisPadButtonDataOffset::TouchPad; - } else { - button = {}; - } - break; - case SDLK_F11: - if (event->type == SDL_EVENT_KEY_DOWN) { - { - SDL_WindowFlags flag = SDL_GetWindowFlags(window); - bool is_fullscreen = flag & SDL_WINDOW_FULLSCREEN; - SDL_SetWindowFullscreen(window, !is_fullscreen); - } - } - break; - case SDLK_F12: - if (event->type == SDL_EVENT_KEY_DOWN) { - // Trigger rdoc capture + // Trigger rdoc capture + else if (input_id == SDLK_F12) { VideoCore::TriggerCapture(); + return; } - break; - default: - break; } - if (button != OrbisPadButtonDataOffset::None) { - controller->CheckButton(0, button, event->type == SDL_EVENT_KEY_DOWN); + + // if it's a wheel event, make a timer that turns it off after a set time + if (event->type == SDL_EVENT_MOUSE_WHEEL) { + const SDL_Event* copy = new SDL_Event(*event); + SDL_AddTimer(33, wheelOffCallback, (void*)copy); } - if (axis != Input::Axis::AxisMax) { - controller->Axis(0, axis, ax); + + // add/remove it from the list + bool inputs_changed = Input::UpdatePressedKeys(input_event); + + // update bindings + if (inputs_changed) { + Input::ActivateOutputsFromInputs(); } } void WindowSDL::OnGamepadEvent(const SDL_Event* event) { - auto button = OrbisPadButtonDataOffset::None; - Input::Axis axis = Input::Axis::AxisMax; - switch (event->type) { - case SDL_EVENT_GAMEPAD_ADDED: - case SDL_EVENT_GAMEPAD_REMOVED: - controller->SetEngine(std::make_unique()); - break; - case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN: - case SDL_EVENT_GAMEPAD_TOUCHPAD_UP: - case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION: - controller->SetTouchpadState(event->gtouchpad.finger, - event->type != SDL_EVENT_GAMEPAD_TOUCHPAD_UP, - event->gtouchpad.x, event->gtouchpad.y); - break; - case SDL_EVENT_GAMEPAD_BUTTON_DOWN: - case SDL_EVENT_GAMEPAD_BUTTON_UP: { - button = Input::SDLGamepadToOrbisButton(event->gbutton.button); - if (button == OrbisPadButtonDataOffset::None) { - break; - } - if (event->gbutton.button != SDL_GAMEPAD_BUTTON_BACK) { - controller->CheckButton(0, button, event->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN); - break; - } - const auto backButtonBehavior = Config::getBackButtonBehavior(); - if (backButtonBehavior != "none") { - float x = backButtonBehavior == "left" ? 0.25f - : (backButtonBehavior == "right" ? 0.75f : 0.5f); - // trigger a touchpad event so that the touchpad emulation for back button works - controller->SetTouchpadState(0, true, x, 0.5f); - controller->CheckButton(0, button, event->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN); - } - break; - } - case SDL_EVENT_GAMEPAD_AXIS_MOTION: - axis = event->gaxis.axis == SDL_GAMEPAD_AXIS_LEFTX ? Input::Axis::LeftX - : event->gaxis.axis == SDL_GAMEPAD_AXIS_LEFTY ? Input::Axis::LeftY - : event->gaxis.axis == SDL_GAMEPAD_AXIS_RIGHTX ? Input::Axis::RightX - : event->gaxis.axis == SDL_GAMEPAD_AXIS_RIGHTY ? Input::Axis::RightY - : event->gaxis.axis == SDL_GAMEPAD_AXIS_LEFT_TRIGGER ? Input::Axis::TriggerLeft - : event->gaxis.axis == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER ? Input::Axis::TriggerRight - : Input::Axis::AxisMax; - if (axis != Input::Axis::AxisMax) { - if (event->gaxis.axis == SDL_GAMEPAD_AXIS_LEFT_TRIGGER || - event->gaxis.axis == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) { - controller->Axis(0, axis, Input::GetAxis(0, 0x8000, event->gaxis.value)); - } else { - controller->Axis(0, axis, Input::GetAxis(-0x8000, 0x8000, event->gaxis.value)); - } - } - break; + bool input_down = event->type == SDL_EVENT_GAMEPAD_AXIS_MOTION || + event->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN; + Input::InputEvent input_event = Input::InputBinding::GetInputEventFromSDLEvent(*event); + + // the touchpad button shouldn't be rebound to anything else, + // as it would break the entire touchpad handling + // You can still bind other things to it though + if (event->gbutton.button == SDL_GAMEPAD_BUTTON_TOUCHPAD) { + controller->CheckButton(0, OrbisPadButtonDataOffset::TouchPad, input_down); + return; + } + + // add/remove it from the list + bool inputs_changed = Input::UpdatePressedKeys(input_event); + + // update bindings + if (inputs_changed) { + Input::ActivateOutputsFromInputs(); } } diff --git a/src/sdl_window.h b/src/sdl_window.h index 3ab3c3613..9acd2b16b 100644 --- a/src/sdl_window.h +++ b/src/sdl_window.h @@ -3,9 +3,10 @@ #pragma once -#include #include "common/types.h" +#include "core/libraries/pad/pad.h" #include "input/controller.h" +#include "string" struct SDL_Window; struct SDL_Gamepad; @@ -94,7 +95,7 @@ public: private: void OnResize(); - void OnKeyPress(const SDL_Event* event); + void OnKeyboardMouseInput(const SDL_Event* event); void OnGamepadEvent(const SDL_Event* event); private: From 9aa6c5b9514c9c9c3ce3469790613af987725ec5 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Fri, 31 Jan 2025 17:41:11 +0100 Subject: [PATCH 542/549] Remapping: Documentation and defaults update + add option to use a unified config (#2302) * Add a toggle to use unified or per-game configs, and add a deadzone example to the default config * Add a checkbox to toggle config type * clang --- src/common/config.cpp | 16 +++++++++++++++- src/common/config.h | 2 ++ src/input/input_handler.cpp | 7 ++----- src/qt_gui/kbm_config_dialog.cpp | 11 +++++++++++ src/qt_gui/kbm_help_dialog.h | 6 ++++++ 5 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index e79a52796..fee0b4ed3 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -68,6 +68,7 @@ static bool vkGuestMarkers = false; static bool rdocEnable = false; static s16 cursorState = HideCursorState::Idle; static int cursorHideTimeout = 5; // 5 seconds (default) +static bool useUnifiedInputConfig = true; static bool separateupdatefolder = false; static bool compatibilityData = false; static bool checkCompatibilityOnStartup = false; @@ -98,6 +99,14 @@ std::string emulator_language = "en"; // Language u32 m_language = 1; // english +bool GetUseUnifiedInputConfig() { + return useUnifiedInputConfig; +} + +void SetUseUnifiedInputConfig(bool use) { + useUnifiedInputConfig = use; +} + std::string getTrophyKey() { return trophyKey; } @@ -657,6 +666,7 @@ void load(const std::filesystem::path& path) { useSpecialPad = toml::find_or(input, "useSpecialPad", false); specialPadClass = toml::find_or(input, "specialPadClass", 1); isMotionControlsEnabled = toml::find_or(input, "isMotionControlsEnabled", true); + useUnifiedInputConfig = toml::find_or(input, "useUnifiedInputConfig", true); } if (data.contains("GPU")) { @@ -779,6 +789,7 @@ void save(const std::filesystem::path& path) { data["Input"]["useSpecialPad"] = useSpecialPad; data["Input"]["specialPadClass"] = specialPadClass; data["Input"]["isMotionControlsEnabled"] = isMotionControlsEnabled; + data["Input"]["useUnifiedInputConfig"] = useUnifiedInputConfig; data["GPU"]["screenWidth"] = screenWidth; data["GPU"]["screenHeight"] = screenHeight; data["GPU"]["nullGpu"] = isNullGpu; @@ -969,9 +980,12 @@ touchpad = back axis_left_x = axis_left_x axis_left_y = axis_left_y - axis_right_x = axis_right_x axis_right_y = axis_right_y + +# Range of deadzones: 1 (almost none) to 127 (max) +analog_deadzone = leftjoystick, 2 +analog_deadzone = rightjoystick, 2 )"; } std::filesystem::path GetFoolproofKbmConfigFile(const std::string& game_id) { diff --git a/src/common/config.h b/src/common/config.h index f726f840c..77ed69ece 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -43,6 +43,8 @@ std::string getBackButtonBehavior(); bool getUseSpecialPad(); int getSpecialPadClass(); bool getIsMotionControlsEnabled(); +bool GetUseUnifiedInputConfig(); +void SetUseUnifiedInputConfig(bool use); u32 getScreenWidth(); u32 getScreenHeight(); diff --git a/src/input/input_handler.cpp b/src/input/input_handler.cpp index a78a54131..5394e4818 100644 --- a/src/input/input_handler.cpp +++ b/src/input/input_handler.cpp @@ -198,11 +198,8 @@ InputBinding GetBindingFromString(std::string& line) { } void ParseInputConfig(const std::string game_id = "") { - const auto config_file = Config::GetFoolproofKbmConfigFile(game_id); - - if (game_id == "") { - return; - } + std::string config_type = Config::GetUseUnifiedInputConfig() ? "default" : game_id; + const auto config_file = Config::GetFoolproofKbmConfigFile(config_type); // we reset these here so in case the user fucks up or doesn't include some of these, // we can fall back to default diff --git a/src/qt_gui/kbm_config_dialog.cpp b/src/qt_gui/kbm_config_dialog.cpp index af198f79d..74a49034b 100644 --- a/src/qt_gui/kbm_config_dialog.cpp +++ b/src/qt_gui/kbm_config_dialog.cpp @@ -12,6 +12,7 @@ #include "game_info.h" #include "src/sdl_window.h" +#include #include #include #include @@ -50,7 +51,16 @@ EditorDialog::EditorDialog(QWidget* parent) : QDialog(parent) { // Load all installed games loadInstalledGames(); + QCheckBox* unifiedInputCheckBox = new QCheckBox("Use Per-Game configs", this); + unifiedInputCheckBox->setChecked(!Config::GetUseUnifiedInputConfig()); + + // Connect checkbox signal + connect(unifiedInputCheckBox, &QCheckBox::toggled, this, [](bool checked) { + Config::SetUseUnifiedInputConfig(!checked); + Config::save(Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "config.toml"); + }); // Create Save, Cancel, and Help buttons + Config::SetUseUnifiedInputConfig(!Config::GetUseUnifiedInputConfig()); QPushButton* saveButton = new QPushButton("Save", this); QPushButton* cancelButton = new QPushButton("Cancel", this); QPushButton* helpButton = new QPushButton("Help", this); @@ -58,6 +68,7 @@ EditorDialog::EditorDialog(QWidget* parent) : QDialog(parent) { // Layout for the game selection and buttons QHBoxLayout* topLayout = new QHBoxLayout(); + topLayout->addWidget(unifiedInputCheckBox); topLayout->addWidget(gameComboBox); topLayout->addStretch(); topLayout->addWidget(saveButton); diff --git a/src/qt_gui/kbm_help_dialog.h b/src/qt_gui/kbm_help_dialog.h index 8d543b7e7..c482d2b5c 100644 --- a/src/qt_gui/kbm_help_dialog.h +++ b/src/qt_gui/kbm_help_dialog.h @@ -70,6 +70,12 @@ A: The code recognises the line as wrong, and skip it, so the rest of the file w Q: I want to bind to , but your code doesn't support ! A: Some keys are intentionally omitted, but if you read the bindings through, and you're sure it is not there and isn't one of the intentionally disabled ones, open an issue on https://github.com/shadps4-emu/shadPS4. + +Q: What does default.ini do? +A: If you're using per-game configs, it's the base from which all new games generate their config file. If you use the unified config, then this is used for every game directly instead. + +Q: What does the use Per-game Config checkbox do? +A: It controls whether the config is loaded from CUSAXXXXX.ini for a game, or from default.ini. This way, if you only want to manage one set of bindings, you can do so, but if you want to use a different setup for every game, that's possible as well. )"; } QString syntax() { From 259d5ef60b5cd0aa1474f6c3be757804203890a3 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sat, 1 Feb 2025 00:34:41 -0800 Subject: [PATCH 543/549] config: Restore previous keyboard mapping defaults. (#2313) --- README.md | 53 +++++++++++++++++++------------------- src/common/config.cpp | 60 +++++++++++++++++++++---------------------- 2 files changed, 57 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index 0e5ba7e39..97e3ab383 100644 --- a/README.md +++ b/README.md @@ -92,33 +92,34 @@ F12 | Trigger RenderDoc Capture > [!NOTE] > Xbox and DualShock controllers work out of the box. -The default controls are inspired by the *Elden Ring* PC controls. Inputs support up to three keys per binding, mouse buttons, mouse movement mapped to joystick input, and more. +| Controller button | Keyboard equivalent | +|-------------|-------------| +LEFT AXIS UP | W | +LEFT AXIS DOWN | S | +LEFT AXIS LEFT | A | +LEFT AXIS RIGHT | D | +RIGHT AXIS UP | I | +RIGHT AXIS DOWN | K | +RIGHT AXIS LEFT | J | +RIGHT AXIS RIGHT | L | +TRIANGLE | Numpad 8 or C | +CIRCLE | Numpad 6 or B | +CROSS | Numpad 2 or N | +SQUARE | Numpad 4 or V | +PAD UP | UP | +PAD DOWN | DOWN | +PAD LEFT | LEFT | +PAD RIGHT | RIGHT | +OPTIONS | RETURN | +BACK BUTTON / TOUCH PAD | SPACE | +L1 | Q | +R1 | U | +L2 | E | +R2 | O | +L3 | X | +R3 | M | -| Action | Default Key(s) | -|-------------|-----------------------------| -| Triangle | F | -| Circle | Space | -| Cross | E | -| Square | R | -| Pad Up | W, LAlt / Mouse Wheel Up | -| Pad Down | S, LAlt / Mouse Wheel Down | -| Pad Left | A, LAlt / Mouse Wheel Left | -| Pad Right | D, LAlt / Mouse Wheel Right | -| L1 | Right Button, LShift | -| R1 | Left Button | -| L2 | Right Button | -| R2 | Left Button, LShift | -| L3 | X | -| R3 | Q / Middle Button | -| Options | Escape | -| Touchpad | G | - -| Joystick | Default Input | -|--------------------|----------------| -| Left Joystick | WASD | -| Right Joystick | Mouse movement | - -Keyboard and mouse inputs can be customized in the settings menu by clicking the Controller button, and further details and help on controls are also found there. Custom bindings are saved per-game. +Keyboard and mouse inputs can be customized in the settings menu by clicking the Controller button, and further details and help on controls are also found there. Custom bindings are saved per-game. Inputs support up to three keys per binding, mouse buttons, mouse movement mapped to joystick input, and more. # Main team diff --git a/src/common/config.cpp b/src/common/config.cpp index fee0b4ed3..2059da0b3 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -919,44 +919,44 @@ void setDefaultValues() { constexpr std::string_view GetDefaultKeyboardConfig() { return R"(#Feeling lost? Check out the Help section! -#Keyboard bindings +# Keyboard bindings -triangle = f -circle = space -cross = e -square = r +triangle = kp8 +circle = kp6 +cross = kp2 +square = kp4 +# Alternatives for users without a keypad +triangle = c +circle = b +cross = n +square = v -pad_up = w, lalt -pad_up = mousewheelup -pad_down = s, lalt -pad_down = mousewheeldown -pad_left = a, lalt -pad_left = mousewheelleft -pad_right = d, lalt -pad_right = mousewheelright - -l1 = rightbutton, lshift -r1 = leftbutton -l2 = rightbutton -r2 = leftbutton, lshift +l1 = q +r1 = u +l2 = e +r2 = o l3 = x -r3 = q -r3 = middlebutton +r3 = m -options = escape -touchpad = g +options = enter +touchpad = space -key_toggle = i, lalt -mouse_to_joystick = right -mouse_movement_params = 0.5, 1, 0.125 -leftjoystick_halfmode = lctrl +pad_up = up +pad_down = down +pad_left = left +pad_right = right axis_left_x_minus = a axis_left_x_plus = d axis_left_y_minus = w axis_left_y_plus = s -#Controller bindings +axis_right_x_minus = j +axis_right_x_plus = l +axis_right_y_minus = i +axis_right_y_plus = k + +# Controller bindings triangle = triangle cross = cross @@ -970,14 +970,14 @@ r1 = r1 r2 = r2 r3 = r3 +options = options +touchpad = back + pad_up = pad_up pad_down = pad_down pad_left = pad_left pad_right = pad_right -options = options -touchpad = back - axis_left_x = axis_left_x axis_left_y = axis_left_y axis_right_x = axis_right_x From e1550c9091e2389c09634c56befb6c589703d00c Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sat, 1 Feb 2025 00:52:40 -0800 Subject: [PATCH 544/549] audioout: Add error returns when not initialized. (#2309) --- src/core/libraries/audio/audioout.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/core/libraries/audio/audioout.cpp b/src/core/libraries/audio/audioout.cpp index f0ad59c3b..dea8115e9 100644 --- a/src/core/libraries/audio/audioout.cpp +++ b/src/core/libraries/audio/audioout.cpp @@ -89,6 +89,9 @@ int PS4_SYSV_ABI sceAudioOutChangeAppModuleState() { int PS4_SYSV_ABI sceAudioOutClose(s32 handle) { LOG_INFO(Lib_AudioOut, "handle = {}", handle); + if (audio == nullptr) { + return ORBIS_AUDIO_OUT_ERROR_NOT_INIT; + } if (handle < 1 || handle > SCE_AUDIO_OUT_NUM_PORTS) { return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; } @@ -171,6 +174,9 @@ int PS4_SYSV_ABI sceAudioOutGetLastOutputTime() { } int PS4_SYSV_ABI sceAudioOutGetPortState(s32 handle, OrbisAudioOutPortState* state) { + if (audio == nullptr) { + return ORBIS_AUDIO_OUT_ERROR_NOT_INIT; + } if (handle < 1 || handle > SCE_AUDIO_OUT_NUM_PORTS) { return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; } @@ -305,6 +311,10 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id, user_id, magic_enum::enum_name(port_type), index, length, sample_rate, magic_enum::enum_name(param_type.data_format.Value()), magic_enum::enum_name(param_type.attributes.Value())); + if (audio == nullptr) { + LOG_ERROR(Lib_AudioOut, "Audio out not initialized"); + return ORBIS_AUDIO_OUT_ERROR_NOT_INIT; + } if ((port_type < OrbisAudioOutPort::Main || port_type > OrbisAudioOutPort::Padspk) && (port_type != OrbisAudioOutPort::Aux)) { LOG_ERROR(Lib_AudioOut, "Invalid port type"); @@ -368,6 +378,9 @@ int PS4_SYSV_ABI sceAudioOutOpenEx() { } s32 PS4_SYSV_ABI sceAudioOutOutput(s32 handle, void* ptr) { + if (audio == nullptr) { + return ORBIS_AUDIO_OUT_ERROR_NOT_INIT; + } if (handle < 1 || handle > SCE_AUDIO_OUT_NUM_PORTS) { return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; } @@ -489,6 +502,9 @@ int PS4_SYSV_ABI sceAudioOutSetUsbVolume() { } s32 PS4_SYSV_ABI sceAudioOutSetVolume(s32 handle, s32 flag, s32* vol) { + if (audio == nullptr) { + return ORBIS_AUDIO_OUT_ERROR_NOT_INIT; + } if (handle < 1 || handle > SCE_AUDIO_OUT_NUM_PORTS) { return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; } From 84c27eea2ae56807a7b178e31363f6ab1c27a9bb Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sat, 1 Feb 2025 01:54:40 -0800 Subject: [PATCH 545/549] texture_cache: Make sure left-overlapped mips get marked for rebind. (#2268) --- .../texture_cache/texture_cache.cpp | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index e995b10b2..ecac78847 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -240,32 +240,32 @@ std::tuple TextureCache::ResolveOverlap(const ImageInfo& imag return {{}, -1, -1}; } else { // Left overlap, the image from cache is a possible subresource of the image requested - if (!merged_image_id) { - // We need to have a larger, already allocated image to copy this one into - return {{}, -1, -1}; - } - if (auto mip = tex_cache_image.info.IsMipOf(image_info); mip >= 0) { if (tex_cache_image.binding.is_target) { // We have a larger image created and a separate one, representing a subres of it, // bound as render target. In this case we need to rebind render target. tex_cache_image.binding.needs_rebind = 1u; - GetImage(merged_image_id).binding.is_target = 1u; + if (merged_image_id) { + GetImage(merged_image_id).binding.is_target = 1u; + } FreeImage(cache_image_id); return {merged_image_id, -1, -1}; } - tex_cache_image.Transit(vk::ImageLayout::eTransferSrcOptimal, - vk::AccessFlagBits2::eTransferRead, {}); + // We need to have a larger, already allocated image to copy this one into + if (merged_image_id) { + tex_cache_image.Transit(vk::ImageLayout::eTransferSrcOptimal, + vk::AccessFlagBits2::eTransferRead, {}); - const auto num_mips_to_copy = tex_cache_image.info.resources.levels; - ASSERT(num_mips_to_copy == 1); + const auto num_mips_to_copy = tex_cache_image.info.resources.levels; + ASSERT(num_mips_to_copy == 1); - auto& merged_image = slot_images[merged_image_id]; - merged_image.CopyMip(tex_cache_image, mip); + auto& merged_image = slot_images[merged_image_id]; + merged_image.CopyMip(tex_cache_image, mip); - FreeImage(cache_image_id); + FreeImage(cache_image_id); + } } } From 83671ebf763c9e94731b1a0b94626d02a4f1fb27 Mon Sep 17 00:00:00 2001 From: pdaloxd <31321612+pablodrake@users.noreply.github.com> Date: Sat, 1 Feb 2025 10:58:05 +0100 Subject: [PATCH 546/549] Translatable Compatibility Status (#2304) * qt_gui: Made compatibility status translatable * Translations: Added English and Spanish translation for compatibility status --- src/qt_gui/compatibility_info.cpp | 19 +++++++++++++++++++ src/qt_gui/compatibility_info.h | 8 +------- src/qt_gui/game_list_frame.cpp | 2 +- src/qt_gui/translations/en.ts | 27 +++++++++++++++++++++++++++ src/qt_gui/translations/es_ES.ts | 27 +++++++++++++++++++++++++++ 5 files changed, 75 insertions(+), 8 deletions(-) diff --git a/src/qt_gui/compatibility_info.cpp b/src/qt_gui/compatibility_info.cpp index 69fb3e377..884387061 100644 --- a/src/qt_gui/compatibility_info.cpp +++ b/src/qt_gui/compatibility_info.cpp @@ -260,3 +260,22 @@ void CompatibilityInfoClass::ExtractCompatibilityInfo(QByteArray response) { return; } + +const QString CompatibilityInfoClass::GetCompatStatusString(const CompatibilityStatus status) { + switch (status) { + case CompatibilityStatus::Unknown: + return tr("Unknown"); + case CompatibilityStatus::Nothing: + return tr("Nothing"); + case CompatibilityStatus::Boots: + return tr("Boots"); + case CompatibilityStatus::Menus: + return tr("Menus"); + case CompatibilityStatus::Ingame: + return tr("Ingame"); + case CompatibilityStatus::Playable: + return tr("Playable"); + default: + return tr("Unknown"); + } +} \ No newline at end of file diff --git a/src/qt_gui/compatibility_info.h b/src/qt_gui/compatibility_info.h index 0c47c27ff..511c106ce 100644 --- a/src/qt_gui/compatibility_info.h +++ b/src/qt_gui/compatibility_info.h @@ -69,13 +69,6 @@ public: {QStringLiteral("os-windows"), OSType::Win32}, }; - inline static const std::unordered_map CompatStatusToString = { - {CompatibilityStatus::Unknown, QStringLiteral("Unknown")}, - {CompatibilityStatus::Nothing, QStringLiteral("Nothing")}, - {CompatibilityStatus::Boots, QStringLiteral("Boots")}, - {CompatibilityStatus::Menus, QStringLiteral("Menus")}, - {CompatibilityStatus::Ingame, QStringLiteral("Ingame")}, - {CompatibilityStatus::Playable, QStringLiteral("Playable")}}; inline static const std::unordered_map OSTypeToString = { {OSType::Linux, QStringLiteral("os-linux")}, {OSType::macOS, QStringLiteral("os-macOS")}, @@ -87,6 +80,7 @@ public: void UpdateCompatibilityDatabase(QWidget* parent = nullptr, bool forced = false); bool LoadCompatibilityFile(); CompatibilityEntry GetCompatibilityInfo(const std::string& serial); + const QString GetCompatStatusString(const CompatibilityStatus status); void ExtractCompatibilityInfo(QByteArray response); static bool WaitForReply(QNetworkReply* reply); QNetworkReply* FetchPage(int page_num); diff --git a/src/qt_gui/game_list_frame.cpp b/src/qt_gui/game_list_frame.cpp index 8255c0daf..f2d08f578 100644 --- a/src/qt_gui/game_list_frame.cpp +++ b/src/qt_gui/game_list_frame.cpp @@ -289,7 +289,7 @@ void GameListFrame::SetCompatibilityItem(int row, int column, CompatibilityEntry QLabel* dotLabel = new QLabel("", widget); dotLabel->setPixmap(circle_pixmap); - QLabel* label = new QLabel(m_compat_info->CompatStatusToString.at(entry.status), widget); + QLabel* label = new QLabel(m_compat_info->GetCompatStatusString(entry.status), widget); label->setStyleSheet("color: white; font-size: 16px; font-weight: bold;"); diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index 7ad2f15c0..58d6e9aa8 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -1414,4 +1414,31 @@ TB + + CompatibilityInfoClass + + Unknown + Unknown + + + Nothing + Nothing + + + Boots + Boots + + + Menus + Menus + + + Ingame + Ingame + + + Playable + Playable + + diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index 8af34c042..d732e67ea 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -1405,4 +1405,31 @@ TB + + CompatibilityInfoClass + + Unknown + Desconocido + + + Nothing + Nada + + + Boots + Inicia + + + Menus + Menús + + + Ingame + En el juego + + + Playable + Jugable + + \ No newline at end of file From 1d8c607c1554e308a4f5e9fc5dc0ca7bf821ebff Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Sat, 1 Feb 2025 20:44:10 +0300 Subject: [PATCH 547/549] hotfix: stronger conditions for the vtx offset error message --- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 1c90c6c27..7f2db3f8d 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -1172,7 +1172,8 @@ void Rasterizer::UpdateViewportScissorState(const GraphicsPipeline& pipeline) { const float reduce_z = regs.clipper_control.clip_space == AmdGpu::Liverpool::ClipSpace::MinusWToW ? 1.0f : 0.0f; - if (regs.polygon_control.enable_window_offset) { + if (regs.polygon_control.enable_window_offset && + (regs.window_offset.window_x_offset != 0 || regs.window_offset.window_y_offset != 0)) { LOG_ERROR(Render_Vulkan, "PA_SU_SC_MODE_CNTL.VTX_WINDOW_OFFSET_ENABLE support is not yet implemented."); } From 831903799b51c675804c50fc0b7d0cd305dda844 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sun, 2 Feb 2025 14:51:45 -0800 Subject: [PATCH 548/549] shader_recompiler: Insert end of divergence scope at last relevant instruction. (#2325) --- src/shader_recompiler/frontend/control_flow_graph.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/shader_recompiler/frontend/control_flow_graph.cpp b/src/shader_recompiler/frontend/control_flow_graph.cpp index ec5c117f7..12d2a2922 100644 --- a/src/shader_recompiler/frontend/control_flow_graph.cpp +++ b/src/shader_recompiler/frontend/control_flow_graph.cpp @@ -161,6 +161,12 @@ void CFG::EmitDivergenceLabels() { // scope. const auto start = inst_list.begin() + curr_begin + 1; if (!std::ranges::all_of(start, inst_list.begin() + index, IgnoresExecMask)) { + // Determine the last instruction affected by the exec mask, so that any + // trailing instructions not affected can be excluded from the scope. + s32 curr_end = index; + while (IgnoresExecMask(inst_list[curr_end - 1])) { + --curr_end; + } // Add a label to the instruction right after the open scope call. // It is the start of a new basic block. const auto& save_inst = inst_list[curr_begin]; @@ -173,8 +179,8 @@ void CFG::EmitDivergenceLabels() { // * Normal instruction at the end of the block // For the last case we must NOT add a label as that would cause // the instruction to be separated into its own basic block. - if (is_close) { - AddLabel(index_to_pc[index]); + if (curr_end != end_index - 1) { + AddLabel(index_to_pc[curr_end]); } } // Reset scope begin. From 460c266e045766e7d6a2c1e7b54a95124d476a62 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sun, 2 Feb 2025 15:37:08 -0800 Subject: [PATCH 549/549] fix: Restore previous version of divergence PR. --- .../frontend/control_flow_graph.cpp | 65 +++++++++---------- 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/src/shader_recompiler/frontend/control_flow_graph.cpp b/src/shader_recompiler/frontend/control_flow_graph.cpp index 12d2a2922..126cb4eb6 100644 --- a/src/shader_recompiler/frontend/control_flow_graph.cpp +++ b/src/shader_recompiler/frontend/control_flow_graph.cpp @@ -148,47 +148,46 @@ void CFG::EmitDivergenceLabels() { const size_t end_index = GetIndex(end); s32 curr_begin = -1; + s32 last_exec_idx = -1; for (size_t index = GetIndex(start); index < end_index; index++) { const auto& inst = inst_list[index]; - const bool is_close = is_close_scope(inst); - if ((is_close || index == end_index - 1) && curr_begin != -1) { - // If there are no instructions inside scope don't do anything. - if (index - curr_begin == 1) { + if (curr_begin != -1) { + // Keep note of the last instruction that does not ignore exec, so we know where + // to end the divergence block without impacting trailing instructions that do. + if (!IgnoresExecMask(inst)) { + last_exec_idx = index; + } + // Consider a close scope on certain instruction types or at the last instruction + // before the next label. + if (is_close_scope(inst) || index == end_index - 1) { + // Only insert a scope if, since the open-scope instruction, there is at least + // one instruction that does not ignore exec. + if (index - curr_begin > 1 && last_exec_idx != -1) { + // Add a label to the instruction right after the open scope call. + // It is the start of a new basic block. + const auto& save_inst = inst_list[curr_begin]; + AddLabel(index_to_pc[curr_begin] + save_inst.length); + // Add a label to the close scope instruction. + // There are 3 cases where we need to close a scope. + // * Close scope instruction inside the block + // * Close scope instruction at the end of the block (cbranch or endpgm) + // * Normal instruction at the end of the block + // If the instruction we want to close the scope at is at the end of the + // block, we do not need to insert a new label. + if (last_exec_idx != end_index - 1) { + // Add the label after the last instruction affected by exec. + const auto& last_exec_inst = inst_list[last_exec_idx]; + AddLabel(index_to_pc[last_exec_idx] + last_exec_inst.length); + } + } + // Reset scope begin. curr_begin = -1; - continue; } - // If all instructions in the scope ignore exec masking, we shouldn't insert a - // scope. - const auto start = inst_list.begin() + curr_begin + 1; - if (!std::ranges::all_of(start, inst_list.begin() + index, IgnoresExecMask)) { - // Determine the last instruction affected by the exec mask, so that any - // trailing instructions not affected can be excluded from the scope. - s32 curr_end = index; - while (IgnoresExecMask(inst_list[curr_end - 1])) { - --curr_end; - } - // Add a label to the instruction right after the open scope call. - // It is the start of a new basic block. - const auto& save_inst = inst_list[curr_begin]; - const Label label = index_to_pc[curr_begin] + save_inst.length; - AddLabel(label); - // Add a label to the close scope instruction. - // There are 3 cases where we need to close a scope. - // * Close scope instruction inside the block - // * Close scope instruction at the end of the block (cbranch or endpgm) - // * Normal instruction at the end of the block - // For the last case we must NOT add a label as that would cause - // the instruction to be separated into its own basic block. - if (curr_end != end_index - 1) { - AddLabel(index_to_pc[curr_end]); - } - } - // Reset scope begin. - curr_begin = -1; } // Mark a potential start of an exec scope. if (is_open_scope(inst)) { curr_begin = index; + last_exec_idx = -1; } } }