From a49b13fe66603c387e01fa6eb9cbd85b6193b99c Mon Sep 17 00:00:00 2001 From: TheTurtle Date: Thu, 26 Jun 2025 12:14:36 +0300 Subject: [PATCH 01/13] shader_recompiler: Optimize general case of buffer addressing (#3159) * shader_recompiler: Simplify dma types Only U32 is needed for S_LOAD_DWORD * shader_recompiler: Perform address shift on IR level Buffer instructions now expect address in the data unit they work on. Doing the shift on IR level will allow us to optimize some operations away on common case * shader_recompiler: Optimize common buffer access pattern * emit_spirv: Use 32-bit integer ops for fault buffer Not many GPUs have 8-bit bitwise or operations so that would probably require some overhead to emulate from the driver * resource_tracking_pass: Fix texel buffer shift --- .../backend/spirv/emit_spirv.cpp | 2 +- .../backend/spirv/emit_spirv_atomic.cpp | 52 +++--- .../spirv/emit_spirv_context_get_set.cpp | 111 ++++++------- .../backend/spirv/spirv_emit_context.cpp | 155 +++++++++--------- .../backend/spirv/spirv_emit_context.h | 90 ++++------ .../frontend/translate/scalar_alu.cpp | 1 - src/shader_recompiler/info.h | 2 +- .../ir/passes/resource_tracking_pass.cpp | 64 +++++++- .../ir/passes/shader_info_collection_pass.cpp | 15 +- src/shader_recompiler/profile.h | 2 +- .../renderer_vulkan/vk_pipeline_cache.cpp | 1 + .../renderer_vulkan/vk_rasterizer.cpp | 9 +- 12 files changed, 271 insertions(+), 233 deletions(-) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index 02f290140..b5b18eed1 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -300,7 +300,7 @@ void SetupCapabilities(const Info& info, const Profile& profile, EmitContext& ct if (stage == LogicalStage::TessellationControl || stage == LogicalStage::TessellationEval) { ctx.AddCapability(spv::Capability::Tessellation); } - if (info.dma_types != IR::Type::Void) { + if (info.uses_dma) { ctx.AddCapability(spv::Capability::PhysicalStorageBufferAddresses); ctx.AddExtension("SPV_KHR_physical_storage_buffer"); } diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp index 97e455ff8..3c833b87d 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp @@ -7,7 +7,11 @@ #include "shader_recompiler/backend/spirv/spirv_emit_context.h" namespace Shader::Backend::SPIRV { + namespace { +using PointerType = EmitContext::PointerType; +using PointerSize = EmitContext::PointerSize; + std::pair AtomicArgs(EmitContext& ctx) { const Id scope{ctx.ConstU32(static_cast(spv::Scope::Device))}; const Id semantics{ctx.u32_zero_value}; @@ -61,14 +65,13 @@ Id BufferAtomicU32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id return ctx.U32[1]; } }(); - if (Sirit::ValidId(buffer.offset)) { - address = ctx.OpIAdd(ctx.U32[1], address, buffer.offset); + if (const Id offset = buffer.Offset(PointerSize::B32); Sirit::ValidId(offset)) { + address = ctx.OpIAdd(ctx.U32[1], address, offset); } - const Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(2u)); - const auto [id, pointer_type] = buffer[EmitContext::PointerType::U32]; - const Id ptr = ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, index); + const auto [id, pointer_type] = buffer.Alias(PointerType::U32); + const Id ptr = ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, address); const auto [scope, semantics]{AtomicArgs(ctx)}; - return AccessBoundsCheck<32, 1, is_float>(ctx, index, buffer.size_dwords, [&] { + return AccessBoundsCheck<32, 1, is_float>(ctx, address, buffer.Size(PointerSize::B32), [&] { return (ctx.*atomic_func)(type, ptr, scope, semantics, value); }); } @@ -76,14 +79,13 @@ Id BufferAtomicU32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id Id BufferAtomicU32IncDec(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id)) { const auto& buffer = ctx.buffers[handle]; - if (Sirit::ValidId(buffer.offset)) { - address = ctx.OpIAdd(ctx.U32[1], address, buffer.offset); + if (const Id offset = buffer.Offset(PointerSize::B32); Sirit::ValidId(offset)) { + address = ctx.OpIAdd(ctx.U32[1], address, offset); } - const Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(2u)); - const auto [id, pointer_type] = buffer[EmitContext::PointerType::U32]; - const Id ptr = ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, index); + const auto [id, pointer_type] = buffer.Alias(PointerType::U32); + const Id ptr = ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, address); const auto [scope, semantics]{AtomicArgs(ctx)}; - return AccessBoundsCheck<32>(ctx, index, buffer.size_dwords, [&] { + return AccessBoundsCheck<32>(ctx, address, buffer.Size(PointerSize::B32), [&] { return (ctx.*atomic_func)(ctx.U32[1], ptr, scope, semantics); }); } @@ -92,14 +94,13 @@ Id BufferAtomicU32CmpSwap(EmitContext& ctx, IR::Inst* inst, u32 handle, Id addre Id cmp_value, Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id, Id, Id)) { const auto& buffer = ctx.buffers[handle]; - if (Sirit::ValidId(buffer.offset)) { - address = ctx.OpIAdd(ctx.U32[1], address, buffer.offset); + if (const Id offset = buffer.Offset(PointerSize::B32); Sirit::ValidId(offset)) { + address = ctx.OpIAdd(ctx.U32[1], address, offset); } - const Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(2u)); - const auto [id, pointer_type] = buffer[EmitContext::PointerType::U32]; - const Id ptr = ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, index); + const auto [id, pointer_type] = buffer.Alias(PointerType::U32); + const Id ptr = ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, address); const auto [scope, semantics]{AtomicArgs(ctx)}; - return AccessBoundsCheck<32>(ctx, index, buffer.size_dwords, [&] { + return AccessBoundsCheck<32>(ctx, address, buffer.Size(PointerSize::B32), [&] { return (ctx.*atomic_func)(ctx.U32[1], ptr, scope, semantics, semantics, value, cmp_value); }); } @@ -107,14 +108,13 @@ Id BufferAtomicU32CmpSwap(EmitContext& ctx, IR::Inst* inst, u32 handle, Id addre Id BufferAtomicU64(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value, Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) { const auto& buffer = ctx.buffers[handle]; - if (Sirit::ValidId(buffer.offset)) { - address = ctx.OpIAdd(ctx.U32[1], address, buffer.offset); + if (const Id offset = buffer.Offset(PointerSize::B64); Sirit::ValidId(offset)) { + address = ctx.OpIAdd(ctx.U32[1], address, offset); } - const Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(3u)); - const auto [id, pointer_type] = buffer[EmitContext::PointerType::U64]; - const Id ptr = ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, index); + const auto [id, pointer_type] = buffer.Alias(PointerType::U64); + const Id ptr = ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, address); const auto [scope, semantics]{AtomicArgs(ctx)}; - return AccessBoundsCheck<64>(ctx, index, buffer.size_qwords, [&] { + return AccessBoundsCheck<64>(ctx, address, buffer.Size(PointerSize::B64), [&] { return (ctx.*atomic_func)(ctx.U64, ptr, scope, semantics, value); }); } @@ -360,7 +360,7 @@ Id EmitImageAtomicExchange32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id co Id EmitDataAppend(EmitContext& ctx, u32 gds_addr, u32 binding) { const auto& buffer = ctx.buffers[binding]; - const auto [id, pointer_type] = buffer[EmitContext::PointerType::U32]; + const auto [id, pointer_type] = buffer.Alias(PointerType::U32); const Id ptr = ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, ctx.ConstU32(gds_addr)); const auto [scope, semantics]{AtomicArgs(ctx)}; return ctx.OpAtomicIIncrement(ctx.U32[1], ptr, scope, semantics); @@ -368,7 +368,7 @@ Id EmitDataAppend(EmitContext& ctx, u32 gds_addr, u32 binding) { Id EmitDataConsume(EmitContext& ctx, u32 gds_addr, u32 binding) { const auto& buffer = ctx.buffers[binding]; - const auto [id, pointer_type] = buffer[EmitContext::PointerType::U32]; + const auto [id, pointer_type] = buffer.Alias(PointerType::U32); const Id ptr = ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, ctx.ConstU32(gds_addr)); const auto [scope, semantics]{AtomicArgs(ctx)}; return ctx.OpAtomicIDecrement(ctx.U32[1], ptr, scope, 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 ccbe54d0a..564fb3f80 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 @@ -3,6 +3,7 @@ #include "common/assert.h" #include "common/logging/log.h" +#include "shader_recompiler/backend/spirv/emit_spirv_bounds.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" @@ -11,8 +12,6 @@ #include -#include "emit_spirv_bounds.h" - namespace Shader::Backend::SPIRV { namespace { @@ -164,6 +163,7 @@ void EmitGetGotoVariable(EmitContext&) { } using PointerType = EmitContext::PointerType; +using PointerSize = EmitContext::PointerSize; Id EmitReadConst(EmitContext& ctx, IR::Inst* inst, Id addr, Id offset) { const u32 flatbuf_off_dw = inst->Flags(); @@ -179,14 +179,15 @@ Id EmitReadConst(EmitContext& ctx, IR::Inst* inst, Id addr, Id offset) { template Id ReadConstBuffer(EmitContext& ctx, u32 handle, Id index) { const auto& buffer = ctx.buffers[handle]; - index = ctx.OpIAdd(ctx.U32[1], index, buffer.offset_dwords); - const auto [id, pointer_type] = buffer[type]; + if (const Id offset = buffer.Offset(PointerSize::B32); Sirit::ValidId(offset)) { + index = ctx.OpIAdd(ctx.U32[1], index, offset); + } + const auto [id, pointer_type] = buffer.Alias(type); const auto value_type = type == PointerType::U32 ? ctx.U32[1] : ctx.F32[1]; const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, index)}; const Id result{ctx.OpLoad(value_type, ptr)}; - - if (Sirit::ValidId(buffer.size_dwords)) { - const Id in_bounds = ctx.OpULessThan(ctx.U1[1], index, buffer.size_dwords); + if (const Id size = buffer.Size(PointerSize::B32); Sirit::ValidId(size)) { + const Id in_bounds = ctx.OpULessThan(ctx.U1[1], index, size); return ctx.OpSelect(value_type, in_bounds, result, ctx.u32_zero_value); } return result; @@ -419,25 +420,24 @@ void EmitSetPatch(EmitContext& ctx, IR::Patch patch, Id value) { template static Id EmitLoadBufferB32xN(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) { + constexpr bool is_float = alias == PointerType::F32; const auto flags = inst->Flags(); const auto& spv_buffer = ctx.buffers[handle]; - if (Sirit::ValidId(spv_buffer.offset)) { - address = ctx.OpIAdd(ctx.U32[1], address, spv_buffer.offset); + if (const Id offset = spv_buffer.Offset(PointerSize::B32); Sirit::ValidId(offset)) { + address = ctx.OpIAdd(ctx.U32[1], address, offset); } - const Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(2u)); const auto& data_types = alias == PointerType::U32 ? ctx.U32 : ctx.F32; - const auto [id, pointer_type] = spv_buffer[alias]; + const auto [id, pointer_type] = spv_buffer.Alias(alias); boost::container::static_vector ids; for (u32 i = 0; i < N; i++) { - const Id index_i = i == 0 ? index : ctx.OpIAdd(ctx.U32[1], index, ctx.ConstU32(i)); + const Id index_i = i == 0 ? address : ctx.OpIAdd(ctx.U32[1], address, ctx.ConstU32(i)); const Id ptr_i = ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, index_i); const Id result_i = ctx.OpLoad(data_types[1], ptr_i); if (!flags.typed) { // Untyped loads have bounds checking per-component. - ids.push_back(LoadAccessBoundsCheck < 32, 1, - alias == - PointerType::F32 > (ctx, index_i, spv_buffer.size_dwords, result_i)); + ids.push_back(LoadAccessBoundsCheck<32, 1, is_float>( + ctx, index_i, spv_buffer.Size(PointerSize::B32), result_i)); } else { ids.push_back(result_i); } @@ -446,33 +446,32 @@ static Id EmitLoadBufferB32xN(EmitContext& ctx, IR::Inst* inst, u32 handle, Id a const Id result = N == 1 ? ids[0] : ctx.OpCompositeConstruct(data_types[N], ids); if (flags.typed) { // Typed loads have single bounds check for the whole load. - return LoadAccessBoundsCheck < 32, N, - alias == PointerType::F32 > (ctx, index, spv_buffer.size_dwords, result); + return LoadAccessBoundsCheck<32, N, is_float>(ctx, address, + spv_buffer.Size(PointerSize::B32), result); } return result; } Id EmitLoadBufferU8(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) { const auto& spv_buffer = ctx.buffers[handle]; - if (Sirit::ValidId(spv_buffer.offset)) { - address = ctx.OpIAdd(ctx.U32[1], address, spv_buffer.offset); + if (const Id offset = spv_buffer.Offset(PointerSize::B8); Sirit::ValidId(offset)) { + address = ctx.OpIAdd(ctx.U32[1], address, offset); } - const auto [id, pointer_type] = spv_buffer[PointerType::U8]; + const auto [id, pointer_type] = spv_buffer.Alias(PointerType::U8); const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, address)}; const Id result{ctx.OpLoad(ctx.U8, ptr)}; - return LoadAccessBoundsCheck<8>(ctx, address, spv_buffer.size, result); + return LoadAccessBoundsCheck<8>(ctx, address, spv_buffer.Size(PointerSize::B8), result); } Id EmitLoadBufferU16(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) { const auto& spv_buffer = ctx.buffers[handle]; - if (Sirit::ValidId(spv_buffer.offset)) { - address = ctx.OpIAdd(ctx.U32[1], address, spv_buffer.offset); + if (const Id offset = spv_buffer.Offset(PointerSize::B16); Sirit::ValidId(offset)) { + address = ctx.OpIAdd(ctx.U32[1], address, offset); } - const auto [id, pointer_type] = spv_buffer[PointerType::U16]; - const Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(1u)); - const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, index)}; + const auto [id, pointer_type] = spv_buffer.Alias(PointerType::U16); + const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, address)}; const Id result{ctx.OpLoad(ctx.U16, ptr)}; - return LoadAccessBoundsCheck<16>(ctx, index, spv_buffer.size_shorts, result); + return LoadAccessBoundsCheck<16>(ctx, address, spv_buffer.Size(PointerSize::B16), result); } Id EmitLoadBufferU32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) { @@ -493,14 +492,13 @@ Id EmitLoadBufferU32x4(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) Id EmitLoadBufferU64(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) { const auto& spv_buffer = ctx.buffers[handle]; - if (Sirit::ValidId(spv_buffer.offset)) { - address = ctx.OpIAdd(ctx.U32[1], address, spv_buffer.offset); + if (const Id offset = spv_buffer.Offset(PointerSize::B64); Sirit::ValidId(offset)) { + address = ctx.OpIAdd(ctx.U32[1], address, offset); } - const auto [id, pointer_type] = spv_buffer[PointerType::U64]; - const Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(3u)); - const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u64_zero_value, index)}; + const auto [id, pointer_type] = spv_buffer.Alias(PointerType::U64); + const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u64_zero_value, address)}; const Id result{ctx.OpLoad(ctx.U64, ptr)}; - return LoadAccessBoundsCheck<64>(ctx, index, spv_buffer.size_qwords, result); + return LoadAccessBoundsCheck<64>(ctx, address, spv_buffer.Size(PointerSize::B64), result); } Id EmitLoadBufferF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) { @@ -526,18 +524,18 @@ Id EmitLoadBufferFormatF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id addr template static void EmitStoreBufferB32xN(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) { + constexpr bool is_float = alias == PointerType::F32; const auto flags = inst->Flags(); const auto& spv_buffer = ctx.buffers[handle]; - if (Sirit::ValidId(spv_buffer.offset)) { - address = ctx.OpIAdd(ctx.U32[1], address, spv_buffer.offset); + if (const Id offset = spv_buffer.Offset(PointerSize::B32); Sirit::ValidId(offset)) { + address = ctx.OpIAdd(ctx.U32[1], address, offset); } - const Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(2u)); const auto& data_types = alias == PointerType::U32 ? ctx.U32 : ctx.F32; - const auto [id, pointer_type] = spv_buffer[alias]; + const auto [id, pointer_type] = spv_buffer.Alias(alias); auto store = [&] { for (u32 i = 0; i < N; i++) { - const Id index_i = i == 0 ? index : ctx.OpIAdd(ctx.U32[1], index, ctx.ConstU32(i)); + const Id index_i = i == 0 ? address : ctx.OpIAdd(ctx.U32[1], address, ctx.ConstU32(i)); const Id ptr_i = ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, index_i); const Id value_i = N == 1 ? value : ctx.OpCompositeExtract(data_types[1], value, i); auto store_i = [&] { @@ -546,8 +544,8 @@ static void EmitStoreBufferB32xN(EmitContext& ctx, IR::Inst* inst, u32 handle, I }; if (!flags.typed) { // Untyped stores have bounds checking per-component. - AccessBoundsCheck<32, 1, alias == PointerType::F32>( - ctx, index_i, spv_buffer.size_dwords, store_i); + AccessBoundsCheck<32, 1, is_float>(ctx, index_i, spv_buffer.Size(PointerSize::B32), + store_i); } else { store_i(); } @@ -557,8 +555,7 @@ static void EmitStoreBufferB32xN(EmitContext& ctx, IR::Inst* inst, u32 handle, I if (flags.typed) { // Typed stores have single bounds check for the whole store. - AccessBoundsCheck<32, N, alias == PointerType::F32>(ctx, index, spv_buffer.size_dwords, - store); + AccessBoundsCheck<32, N, is_float>(ctx, address, spv_buffer.Size(PointerSize::B32), store); } else { store(); } @@ -566,12 +563,12 @@ static void EmitStoreBufferB32xN(EmitContext& ctx, IR::Inst* inst, u32 handle, I void EmitStoreBufferU8(EmitContext& ctx, IR::Inst*, u32 handle, Id address, Id value) { const auto& spv_buffer = ctx.buffers[handle]; - if (Sirit::ValidId(spv_buffer.offset)) { - address = ctx.OpIAdd(ctx.U32[1], address, spv_buffer.offset); + if (const Id offset = spv_buffer.Offset(PointerSize::B8); Sirit::ValidId(offset)) { + address = ctx.OpIAdd(ctx.U32[1], address, offset); } - const auto [id, pointer_type] = spv_buffer[PointerType::U8]; + const auto [id, pointer_type] = spv_buffer.Alias(PointerType::U8); const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, address)}; - AccessBoundsCheck<8>(ctx, address, spv_buffer.size, [&] { + AccessBoundsCheck<8>(ctx, address, spv_buffer.Size(PointerSize::B8), [&] { ctx.OpStore(ptr, value); return Id{}; }); @@ -579,13 +576,12 @@ void EmitStoreBufferU8(EmitContext& ctx, IR::Inst*, u32 handle, Id address, Id v void EmitStoreBufferU16(EmitContext& ctx, IR::Inst*, u32 handle, Id address, Id value) { const auto& spv_buffer = ctx.buffers[handle]; - if (Sirit::ValidId(spv_buffer.offset)) { - address = ctx.OpIAdd(ctx.U32[1], address, spv_buffer.offset); + if (const Id offset = spv_buffer.Offset(PointerSize::B16); Sirit::ValidId(offset)) { + address = ctx.OpIAdd(ctx.U32[1], address, offset); } - const auto [id, pointer_type] = spv_buffer[PointerType::U16]; - const Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(1u)); - const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, index)}; - AccessBoundsCheck<16>(ctx, index, spv_buffer.size_shorts, [&] { + const auto [id, pointer_type] = spv_buffer.Alias(PointerType::U16); + const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, address)}; + AccessBoundsCheck<16>(ctx, address, spv_buffer.Size(PointerSize::B16), [&] { ctx.OpStore(ptr, value); return Id{}; }); @@ -609,13 +605,12 @@ void EmitStoreBufferU32x4(EmitContext& ctx, IR::Inst* inst, u32 handle, Id addre void EmitStoreBufferU64(EmitContext& ctx, IR::Inst*, u32 handle, Id address, Id value) { const auto& spv_buffer = ctx.buffers[handle]; - if (Sirit::ValidId(spv_buffer.offset)) { - address = ctx.OpIAdd(ctx.U32[1], address, spv_buffer.offset); + if (const Id offset = spv_buffer.Offset(PointerSize::B64); Sirit::ValidId(offset)) { + address = ctx.OpIAdd(ctx.U32[1], address, offset); } - const auto [id, pointer_type] = spv_buffer[PointerType::U64]; - const Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(3u)); - const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u64_zero_value, index)}; - AccessBoundsCheck<64>(ctx, index, spv_buffer.size_qwords, [&] { + const auto [id, pointer_type] = spv_buffer.Alias(PointerType::U64); + const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u64_zero_value, address)}; + AccessBoundsCheck<64>(ctx, address, spv_buffer.Size(PointerSize::B64), [&] { ctx.OpStore(ptr, value); return Id{}; }); diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 567c059ae..524914ad4 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -71,7 +71,7 @@ EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_inf 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_} { - if (info.dma_types != IR::Type::Void) { + if (info.uses_dma) { SetMemoryModel(spv::AddressingModel::PhysicalStorageBuffer64, spv::MemoryModel::GLSL450); } else { SetMemoryModel(spv::AddressingModel::Logical, spv::MemoryModel::GLSL450); @@ -169,34 +169,8 @@ void EmitContext::DefineArithmeticTypes() { if (info.uses_fp64) { frexp_result_f64 = Name(TypeStruct(F64[1], S32[1]), "frexp_result_f64"); } - - if (True(info.dma_types & IR::Type::F64)) { - physical_pointer_types[PointerType::F64] = - TypePointer(spv::StorageClass::PhysicalStorageBuffer, F64[1]); - } - if (True(info.dma_types & IR::Type::U64)) { - physical_pointer_types[PointerType::U64] = - TypePointer(spv::StorageClass::PhysicalStorageBuffer, U64); - } - if (True(info.dma_types & IR::Type::F32)) { - physical_pointer_types[PointerType::F32] = - TypePointer(spv::StorageClass::PhysicalStorageBuffer, F32[1]); - } - if (True(info.dma_types & IR::Type::U32)) { - physical_pointer_types[PointerType::U32] = - TypePointer(spv::StorageClass::PhysicalStorageBuffer, U32[1]); - } - if (True(info.dma_types & IR::Type::F16)) { - physical_pointer_types[PointerType::F16] = - TypePointer(spv::StorageClass::PhysicalStorageBuffer, F16[1]); - } - if (True(info.dma_types & IR::Type::U16)) { - physical_pointer_types[PointerType::U16] = - TypePointer(spv::StorageClass::PhysicalStorageBuffer, U16); - } - if (True(info.dma_types & IR::Type::U8)) { - physical_pointer_types[PointerType::U8] = - TypePointer(spv::StorageClass::PhysicalStorageBuffer, U8); + if (info.uses_dma) { + physical_pointer_type_u32 = TypePointer(spv::StorageClass::PhysicalStorageBuffer, U32[1]); } } @@ -239,7 +213,7 @@ Id EmitContext::GetBufferSize(const u32 sharp_idx) { // Can this be done with memory access? Like we do now with ReadConst const auto& srt_flatbuf = buffers[flatbuf_index]; ASSERT(srt_flatbuf.buffer_type == BufferType::Flatbuf); - const auto [id, pointer_type] = srt_flatbuf[PointerType::U32]; + const auto [id, pointer_type] = srt_flatbuf.Alias(PointerType::U32); const auto rsrc1{ OpLoad(U32[1], OpAccessChain(pointer_type, id, u32_zero_value, ConstU32(sharp_idx + 1)))}; @@ -255,39 +229,70 @@ Id EmitContext::GetBufferSize(const u32 sharp_idx) { } void EmitContext::DefineBufferProperties() { + if (!profile.needs_buffer_offsets && profile.supports_robust_buffer_access) { + return; + } for (u32 i = 0; i < buffers.size(); i++) { - BufferDefinition& buffer = buffers[i]; + auto& buffer = buffers[i]; + const auto& desc = info.buffers[i]; + const u32 binding = buffer.binding; if (buffer.buffer_type != BufferType::Guest) { continue; } - const u32 binding = buffer.binding; - const u32 half = PushData::BufOffsetIndex + (binding >> 4); - const u32 comp = (binding & 0xf) >> 2; - const u32 offset = (binding & 0x3) << 3; - const Id ptr{OpAccessChain(TypePointer(spv::StorageClass::PushConstant, U32[1]), - push_data_block, ConstU32(half), ConstU32(comp))}; - const Id value{OpLoad(U32[1], ptr)}; - buffer.offset = OpBitFieldUExtract(U32[1], value, ConstU32(offset), ConstU32(8U)); - Name(buffer.offset, fmt::format("buf{}_off", binding)); - buffer.offset_dwords = OpShiftRightLogical(U32[1], buffer.offset, ConstU32(2U)); - Name(buffer.offset_dwords, fmt::format("buf{}_dword_off", binding)); - // Only need to load size if performing bounds checks and the buffer is both guest and not - // inline. - if (!profile.supports_robust_buffer_access && buffer.buffer_type == BufferType::Guest) { - const BufferResource& desc = info.buffers[i]; - if (desc.sharp_idx == std::numeric_limits::max()) { - buffer.size = ConstU32(desc.inline_cbuf.GetSize()); - } else { - buffer.size = GetBufferSize(desc.sharp_idx); + // Only load and apply buffer offsets if host GPU alignment is larger than guest. + if (profile.needs_buffer_offsets) { + const u32 half = PushData::BufOffsetIndex + (binding >> 4); + const u32 comp = (binding & 0xf) >> 2; + const u32 offset = (binding & 0x3) << 3; + const Id ptr{OpAccessChain(TypePointer(spv::StorageClass::PushConstant, U32[1]), + push_data_block, ConstU32(half), ConstU32(comp))}; + const Id value{OpLoad(U32[1], ptr)}; + + const Id buf_offset{OpBitFieldUExtract(U32[1], value, ConstU32(offset), ConstU32(8U))}; + Name(buf_offset, fmt::format("buf{}_off", binding)); + buffer.Offset(PointerSize::B8) = buf_offset; + + if (True(desc.used_types & IR::Type::U16)) { + const Id buf_word_offset{OpShiftRightLogical(U32[1], buf_offset, ConstU32(1U))}; + Name(buf_word_offset, fmt::format("buf{}_word_off", binding)); + buffer.Offset(PointerSize::B16) = buf_word_offset; + } + if (True(desc.used_types & IR::Type::U32)) { + const Id buf_dword_offset{OpShiftRightLogical(U32[1], buf_offset, ConstU32(2U))}; + Name(buf_dword_offset, fmt::format("buf{}_dword_off", binding)); + buffer.Offset(PointerSize::B32) = buf_dword_offset; + } + if (True(desc.used_types & IR::Type::U64)) { + const Id buf_qword_offset{OpShiftRightLogical(U32[1], buf_offset, ConstU32(3U))}; + Name(buf_qword_offset, fmt::format("buf{}_qword_off", binding)); + buffer.Offset(PointerSize::B64) = buf_qword_offset; + } + } + + // Only load size if performing bounds checks. + if (!profile.supports_robust_buffer_access) { + const Id buf_size{desc.sharp_idx == std::numeric_limits::max() + ? ConstU32(desc.inline_cbuf.GetSize()) + : GetBufferSize(desc.sharp_idx)}; + Name(buf_size, fmt::format("buf{}_size", binding)); + buffer.Size(PointerSize::B8) = buf_size; + + if (True(desc.used_types & IR::Type::U16)) { + const Id buf_word_size{OpShiftRightLogical(U32[1], buf_size, ConstU32(1U))}; + Name(buf_word_size, fmt::format("buf{}_short_size", binding)); + buffer.Size(PointerSize::B16) = buf_word_size; + } + if (True(desc.used_types & IR::Type::U32)) { + const Id buf_dword_size{OpShiftRightLogical(U32[1], buf_size, ConstU32(2U))}; + Name(buf_dword_size, fmt::format("buf{}_dword_size", binding)); + buffer.Size(PointerSize::B32) = buf_dword_size; + } + if (True(desc.used_types & IR::Type::U64)) { + const Id buf_qword_size{OpShiftRightLogical(U32[1], buf_size, ConstU32(3U))}; + Name(buf_qword_size, fmt::format("buf{}_qword_size", binding)); + buffer.Size(PointerSize::B64) = buf_qword_size; } - Name(buffer.size, fmt::format("buf{}_size", binding)); - buffer.size_shorts = OpShiftRightLogical(U32[1], buffer.size, ConstU32(1U)); - Name(buffer.size_shorts, fmt::format("buf{}_short_size", binding)); - buffer.size_dwords = OpShiftRightLogical(U32[1], buffer.size, ConstU32(2U)); - Name(buffer.size_dwords, fmt::format("buf{}_dword_size", binding)); - buffer.size_qwords = OpShiftRightLogical(U32[1], buffer.size, ConstU32(3U)); - Name(buffer.size_qwords, fmt::format("buf{}_qword_size", binding)); } } } @@ -779,8 +784,7 @@ EmitContext::BufferSpv EmitContext::DefineBuffer(bool is_storage, bool is_writte }; void EmitContext::DefineBuffers() { - if (!profile.supports_robust_buffer_access && - info.readconst_types == Info::ReadConstType::None) { + if (!profile.supports_robust_buffer_access && !info.uses_dma) { // In case Flatbuf has not already been bound by IR and is needed // to query buffer sizes, bind it now. info.buffers.push_back({ @@ -809,23 +813,23 @@ void EmitContext::DefineBuffers() { // Define aliases depending on the shader usage. auto& spv_buffer = buffers.emplace_back(binding.buffer++, desc.buffer_type); if (True(desc.used_types & IR::Type::U64)) { - spv_buffer[PointerType::U64] = + spv_buffer.Alias(PointerType::U64) = DefineBuffer(is_storage, desc.is_written, 3, desc.buffer_type, U64); } if (True(desc.used_types & IR::Type::U32)) { - spv_buffer[PointerType::U32] = + spv_buffer.Alias(PointerType::U32) = DefineBuffer(is_storage, desc.is_written, 2, desc.buffer_type, U32[1]); } if (True(desc.used_types & IR::Type::F32)) { - spv_buffer[PointerType::F32] = + spv_buffer.Alias(PointerType::F32) = DefineBuffer(is_storage, desc.is_written, 2, desc.buffer_type, F32[1]); } if (True(desc.used_types & IR::Type::U16)) { - spv_buffer[PointerType::U16] = + spv_buffer.Alias(PointerType::U16) = DefineBuffer(is_storage, desc.is_written, 1, desc.buffer_type, U16); } if (True(desc.used_types & IR::Type::U8)) { - spv_buffer[PointerType::U8] = + spv_buffer.Alias(PointerType::U8) = DefineBuffer(is_storage, desc.is_written, 0, desc.buffer_type, U8); } ++binding.unified; @@ -1154,7 +1158,7 @@ Id EmitContext::DefineGetBdaPointer() { const auto page{OpShiftRightLogical(U64, address, caching_pagebits)}; const auto page32{OpUConvert(U32[1], page)}; const auto& bda_buffer{buffers[bda_pagetable_index]}; - const auto [bda_buffer_id, bda_pointer_type] = bda_buffer[PointerType::U64]; + const auto [bda_buffer_id, bda_pointer_type] = bda_buffer.Alias(PointerType::U64); const auto bda_ptr{OpAccessChain(bda_pointer_type, bda_buffer_id, u32_zero_value, page32)}; const auto bda{OpLoad(U64, bda_ptr)}; @@ -1166,14 +1170,14 @@ Id EmitContext::DefineGetBdaPointer() { // First time acces, mark as fault AddLabel(fault_label); const auto& fault_buffer{buffers[fault_buffer_index]}; - const auto [fault_buffer_id, fault_pointer_type] = fault_buffer[PointerType::U8]; - const auto page_div8{OpShiftRightLogical(U32[1], page32, ConstU32(3U))}; - const auto page_mod8{OpBitwiseAnd(U32[1], page32, ConstU32(7U))}; - const auto page_mask{OpShiftLeftLogical(U8, u8_one_value, page_mod8)}; + const auto [fault_buffer_id, fault_pointer_type] = fault_buffer.Alias(PointerType::U32); + const auto page_div32{OpShiftRightLogical(U32[1], page32, ConstU32(5U))}; + const auto page_mod32{OpBitwiseAnd(U32[1], page32, ConstU32(31U))}; + const auto page_mask{OpShiftLeftLogical(U32[1], u32_one_value, page_mod32)}; const auto fault_ptr{ - OpAccessChain(fault_pointer_type, fault_buffer_id, u32_zero_value, page_div8)}; - const auto fault_value{OpLoad(U8, fault_ptr)}; - const auto fault_value_masked{OpBitwiseOr(U8, fault_value, page_mask)}; + OpAccessChain(fault_pointer_type, fault_buffer_id, u32_zero_value, page_div32)}; + const auto fault_value{OpLoad(U32[1], fault_ptr)}; + const auto fault_value_masked{OpBitwiseOr(U32[1], fault_value, page_mask)}; OpStore(fault_ptr, fault_value_masked); // Return null pointer @@ -1211,14 +1215,15 @@ Id EmitContext::DefineReadConst(bool dynamic) { const auto offset_bytes{OpShiftLeftLogical(U32[1], offset, ConstU32(2U))}; const auto addr{OpIAdd(U64, base_addr, OpUConvert(U64, offset_bytes))}; - const auto result = EmitMemoryRead(U32[1], addr, [&]() { + const auto result = EmitDwordMemoryRead(addr, [&]() { if (dynamic) { return u32_zero_value; } else { const auto& flatbuf_buffer{buffers[flatbuf_index]}; ASSERT(flatbuf_buffer.binding >= 0 && flatbuf_buffer.buffer_type == BufferType::Flatbuf); - const auto [flatbuf_buffer_id, flatbuf_pointer_type] = flatbuf_buffer[PointerType::U32]; + const auto [flatbuf_buffer_id, flatbuf_pointer_type] = + flatbuf_buffer.Alias(PointerType::U32); const auto ptr{OpAccessChain(flatbuf_pointer_type, flatbuf_buffer_id, u32_zero_value, flatbuf_offset)}; return OpLoad(U32[1], ptr); @@ -1239,7 +1244,7 @@ void EmitContext::DefineFunctions() { uf11_to_f32 = DefineUfloatM5ToFloat32(6, "uf11_to_f32"); uf10_to_f32 = DefineUfloatM5ToFloat32(5, "uf10_to_f32"); } - if (info.dma_types != IR::Type::Void) { + if (info.uses_dma) { get_bda_pointer = DefineGetBdaPointer(); } diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index 1eb7d05c6..f8c6416e8 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -42,17 +42,6 @@ public: Bindings& binding); ~EmitContext(); - enum class PointerType : u32 { - U8, - U16, - F16, - U32, - F32, - U64, - F64, - NumAlias, - }; - Id Def(const IR::Value& value); void DefineBufferProperties(); @@ -155,25 +144,7 @@ public: return last_label; } - PointerType PointerTypeFromType(Id type) { - if (type.value == U8.value) - return PointerType::U8; - if (type.value == U16.value) - return PointerType::U16; - if (type.value == F16[1].value) - return PointerType::F16; - if (type.value == U32[1].value) - return PointerType::U32; - if (type.value == F32[1].value) - return PointerType::F32; - if (type.value == U64.value) - return PointerType::U64; - if (type.value == F64[1].value) - return PointerType::F64; - UNREACHABLE_MSG("Unknown type for pointer"); - } - - Id EmitMemoryRead(Id type, Id address, auto&& fallback) { + Id EmitDwordMemoryRead(Id address, auto&& fallback) { const Id available_label = OpLabel(); const Id fallback_label = OpLabel(); const Id merge_label = OpLabel(); @@ -185,10 +156,8 @@ public: // Available AddLabel(available_label); - const auto pointer_type = PointerTypeFromType(type); - const Id pointer_type_id = physical_pointer_types[pointer_type]; - const Id addr_ptr = OpConvertUToPtr(pointer_type_id, addr); - const Id result = OpLoad(type, addr_ptr, spv::MemoryAccessMask::Aligned, 4u); + const Id addr_ptr = OpConvertUToPtr(physical_pointer_type_u32, addr); + const Id result = OpLoad(U32[1], addr_ptr, spv::MemoryAccessMask::Aligned, 4u); OpBranch(merge_label); // Fallback @@ -199,7 +168,7 @@ public: // Merge AddLabel(merge_label); const Id final_result = - OpPhi(type, fallback_result, fallback_label, result, available_label); + OpPhi(U32[1], fallback_result, fallback_label, result, available_label); return final_result; } @@ -314,6 +283,24 @@ public: bool is_storage = false; }; + enum class PointerType : u32 { + U8, + U16, + U32, + F32, + U64, + F64, + NumAlias, + }; + + enum class PointerSize : u32 { + B8, + B16, + B32, + B64, + NumClass, + }; + struct BufferSpv { Id id; Id pointer_type; @@ -322,32 +309,23 @@ public: struct BufferDefinition { u32 binding; BufferType buffer_type; - Id offset; - Id offset_dwords; - Id size; - Id size_shorts; - Id size_dwords; - Id size_qwords; + std::array offsets; + std::array sizes; std::array aliases; - const BufferSpv& operator[](PointerType alias) const { - return aliases[u32(alias)]; + template + auto& Alias(this Self& self, PointerType alias) { + return self.aliases[u32(alias)]; } - BufferSpv& operator[](PointerType alias) { - return aliases[u32(alias)]; - } - }; - - struct PhysicalPointerTypes { - std::array types; - - const Id& operator[](PointerType type) const { - return types[u32(type)]; + template + auto& Offset(this Self& self, PointerSize size) { + return self.offsets[u32(size)]; } - Id& operator[](PointerType type) { - return types[u32(type)]; + template + auto& Size(this Self& self, PointerSize size) { + return self.sizes[u32(size)]; } }; @@ -356,12 +334,12 @@ public: boost::container::small_vector buffers; boost::container::small_vector images; boost::container::small_vector samplers; - PhysicalPointerTypes physical_pointer_types; std::unordered_map first_to_last_label_map; size_t flatbuf_index{}; size_t bda_pagetable_index{}; size_t fault_buffer_index{}; + Id physical_pointer_type_u32; Id sampler_type{}; Id sampler_pointer_type{}; diff --git a/src/shader_recompiler/frontend/translate/scalar_alu.cpp b/src/shader_recompiler/frontend/translate/scalar_alu.cpp index 7beb594c3..48f977f49 100644 --- a/src/shader_recompiler/frontend/translate/scalar_alu.cpp +++ b/src/shader_recompiler/frontend/translate/scalar_alu.cpp @@ -1,7 +1,6 @@ // 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" diff --git a/src/shader_recompiler/info.h b/src/shader_recompiler/info.h index 6777c4769..b2b03bbbf 100644 --- a/src/shader_recompiler/info.h +++ b/src/shader_recompiler/info.h @@ -238,7 +238,7 @@ struct Info { Dynamic = 1 << 1, }; ReadConstType readconst_types{}; - IR::Type dma_types{IR::Type::Void}; + bool uses_dma{false}; explicit Info(Stage stage_, LogicalStage l_stage_, ShaderParams params) : stage{stage_}, l_stage{l_stage_}, pgm_hash{params.hash}, pgm_base{params.Base()}, diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index 2e9b78f0e..f758d8e7b 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -105,6 +105,49 @@ IR::Type BufferDataType(const IR::Inst& inst, AmdGpu::NumberFormat num_format) { } } +u32 BufferAddressShift(const IR::Inst& inst, AmdGpu::DataFormat data_format) { + switch (inst.GetOpcode()) { + case IR::Opcode::LoadBufferU8: + case IR::Opcode::StoreBufferU8: + return 0; + case IR::Opcode::LoadBufferU16: + case IR::Opcode::StoreBufferU16: + return 1; + case IR::Opcode::LoadBufferU64: + case IR::Opcode::StoreBufferU64: + case IR::Opcode::BufferAtomicIAdd64: + return 3; + case IR::Opcode::LoadBufferFormatF32: + case IR::Opcode::StoreBufferFormatF32: { + switch (data_format) { + case AmdGpu::DataFormat::Format8: + return 0; + case AmdGpu::DataFormat::Format8_8: + case AmdGpu::DataFormat::Format16: + return 1; + case AmdGpu::DataFormat::Format8_8_8_8: + case AmdGpu::DataFormat::Format16_16: + case AmdGpu::DataFormat::Format10_11_11: + case AmdGpu::DataFormat::Format2_10_10_10: + case AmdGpu::DataFormat::Format16_16_16_16: + case AmdGpu::DataFormat::Format32: + case AmdGpu::DataFormat::Format32_32: + case AmdGpu::DataFormat::Format32_32_32: + case AmdGpu::DataFormat::Format32_32_32_32: + return 2; + default: + return 0; + } + break; + } + case IR::Opcode::ReadConstBuffer: + // Provided address is already in dwords + return 0; + default: + return 2; + } +} + bool IsImageAtomicInstruction(const IR::Inst& inst) { switch (inst.GetOpcode()) { case IR::Opcode::ImageAtomicIAdd32: @@ -496,6 +539,22 @@ void PatchDataRingAccess(IR::Block& block, IR::Inst& inst, Info& info, Descripto 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(); + const u32 inst_offset = inst_info.inst_offset.Value(); + const auto is_inst_typed = inst_info.inst_data_fmt != AmdGpu::DataFormat::FormatInvalid; + const auto data_format = is_inst_typed + ? AmdGpu::RemapDataFormat(inst_info.inst_data_fmt.Value()) + : buffer.GetDataFmt(); + const u32 shift = BufferAddressShift(inst, data_format); + const u32 mask = (1 << shift) - 1; + + // If address calculation is of the form "index * const_stride + offset" with offset constant + // and both const_stride and offset are divisible with the element size, apply shift directly. + if (inst_info.index_enable && !inst_info.offset_enable && !buffer.swizzle_enable && + !buffer.add_tid_enable && (stride & mask) == 0 && (inst_offset & mask) == 0) { + // buffer_offset = index * (const_stride >> shift) + (inst_offset >> shift) + const IR::U32 index = IR::U32{inst.Arg(1)}; + return ir.IAdd(ir.IMul(index, ir.Imm32(stride >> shift)), ir.Imm32(inst_offset >> shift)); + } // index = (inst_idxen ? vgpr_index : 0) + (const_add_tid_enable ? thread_id[5:0] : 0) IR::U32 index = ir.Imm32(0U); @@ -512,7 +571,7 @@ IR::U32 CalculateBufferAddress(IR::IREmitter& ir, const IR::Inst& inst, const In index = ir.IAdd(index, thread_id); } // offset = (inst_offen ? vgpr_offset : 0) + inst_offset - IR::U32 offset = ir.Imm32(inst_info.inst_offset.Value()); + IR::U32 offset = ir.Imm32(inst_offset); if (inst_info.offset_enable) { const IR::U32 vgpr_offset = inst_info.index_enable ? IR::U32{ir.CompositeExtract(inst.Arg(1), 1)} @@ -545,6 +604,9 @@ IR::U32 CalculateBufferAddress(IR::IREmitter& ir, const IR::Inst& inst, const In // buffer_offset = index * const_stride + offset buffer_offset = ir.IAdd(ir.IMul(index, const_stride), offset); } + if (shift != 0) { + buffer_offset = ir.ShiftRightLogical(buffer_offset, ir.Imm32(shift)); + } return buffer_offset; } 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 b3b4ac36a..797d8bb4a 100644 --- a/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp +++ b/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp @@ -102,7 +102,7 @@ void Visit(Info& info, const IR::Inst& inst) { info.uses_lane_id = true; break; case IR::Opcode::ReadConst: - if (info.readconst_types == Info::ReadConstType::None) { + if (!info.uses_dma) { info.buffers.push_back({ .used_types = IR::Type::U32, // We can't guarantee that flatbuf will not grow past UBO @@ -116,7 +116,7 @@ void Visit(Info& info, const IR::Inst& inst) { } else { info.readconst_types |= Info::ReadConstType::Dynamic; } - info.dma_types |= IR::Type::U32; + info.uses_dma = true; break; case IR::Opcode::PackUfloat10_11_11: info.uses_pack_10_11_11 = true; @@ -130,21 +130,22 @@ void Visit(Info& info, const IR::Inst& inst) { } void CollectShaderInfoPass(IR::Program& program) { + auto& info = program.info; for (IR::Block* const block : program.post_order_blocks) { for (IR::Inst& inst : block->Instructions()) { - Visit(program.info, inst); + Visit(info, inst); } } - if (program.info.dma_types != IR::Type::Void) { - program.info.buffers.push_back({ + if (info.uses_dma) { + info.buffers.push_back({ .used_types = IR::Type::U64, .inline_cbuf = AmdGpu::Buffer::Placeholder(VideoCore::BufferCache::BDA_PAGETABLE_SIZE), .buffer_type = BufferType::BdaPagetable, .is_written = true, }); - program.info.buffers.push_back({ - .used_types = IR::Type::U8, + info.buffers.push_back({ + .used_types = IR::Type::U32, .inline_cbuf = AmdGpu::Buffer::Placeholder(VideoCore::BufferCache::FAULT_BUFFER_SIZE), .buffer_type = BufferType::FaultBuffer, .is_written = true, diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index bcdf86962..d7eb307b6 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h @@ -35,7 +35,7 @@ struct Profile { bool lower_left_origin_mode{}; bool needs_manual_interpolation{}; bool needs_lds_barriers{}; - u64 min_ssbo_alignment{}; + bool needs_buffer_offsets{}; u64 max_ubo_size{}; u32 max_viewport_width{}; u32 max_viewport_height{}; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 1d8ac4823..831995339 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -225,6 +225,7 @@ 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, + .needs_buffer_offsets = instance.StorageMinAlignment() > 4, // When binding a UBO, we calculate its size considering the offset in the larger buffer // cache underlying resource. In some cases, it may produce sizes exceeding the system // maximum allowed UBO range, so we need to reduce the threshold to prevent issues. diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 9dea5ceea..fbeaaf9dc 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -468,17 +468,12 @@ bool Rasterizer::BindResources(const Pipeline* pipeline) { stage->PushUd(binding, push_data); BindBuffers(*stage, binding, push_data); BindTextures(*stage, binding); - - uses_dma |= stage->dma_types != Shader::IR::Type::Void; + uses_dma |= stage->uses_dma; } - pipeline->BindResources(set_writes, buffer_barriers, push_data); - if (uses_dma && !fault_process_pending) { // We only use fault buffer for DMA right now. { - // TODO: GPU might have written to memory (for example with EVENT_WRITE_EOP) - // we need to account for that and synchronize. Common::RecursiveSharedLock lock{mapped_ranges_mutex}; for (auto& range : mapped_ranges) { buffer_cache.SynchronizeBuffersInRange(range.lower(), @@ -490,6 +485,8 @@ bool Rasterizer::BindResources(const Pipeline* pipeline) { fault_process_pending |= uses_dma; + pipeline->BindResources(set_writes, buffer_barriers, push_data); + return true; } From 9f37ede336f589abfd4ac34f8194ce2e8db30d4e Mon Sep 17 00:00:00 2001 From: Lander Gallastegi Date: Thu, 26 Jun 2025 18:38:53 +0200 Subject: [PATCH 02/13] video_core: Page manager and memory tracker improvenets (#3155) * I don't know what to put here * clang-format * clang-format 2.0 * Deadlock free locking * Por boost range lock implementation * clang-format --- CMakeLists.txt | 1 + src/common/adaptive_mutex.h | 3 + src/common/range_lock.h | 101 ++++++++++++++++++ src/video_core/buffer_cache/memory_tracker.h | 10 +- .../buffer_cache/region_definitions.h | 14 +-- src/video_core/buffer_cache/region_manager.h | 23 ++-- src/video_core/page_manager.cpp | 57 ++++++---- src/video_core/page_manager.h | 9 +- 8 files changed, 176 insertions(+), 42 deletions(-) create mode 100644 src/common/range_lock.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8d50e3bf4..ab846fa9d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -683,6 +683,7 @@ set(COMMON src/common/logging/backend.cpp src/common/path_util.h src/common/object_pool.h src/common/polyfill_thread.h + src/common/range_lock.h src/common/rdtsc.cpp src/common/rdtsc.h src/common/recursive_lock.cpp diff --git a/src/common/adaptive_mutex.h b/src/common/adaptive_mutex.h index f174f5996..2ab385bdb 100644 --- a/src/common/adaptive_mutex.h +++ b/src/common/adaptive_mutex.h @@ -18,6 +18,9 @@ public: void unlock() { pthread_mutex_unlock(&mutex); } + [[nodiscard]] bool try_lock() { + return pthread_mutex_trylock(&mutex) == 0; + } private: pthread_mutex_t mutex = PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP; diff --git a/src/common/range_lock.h b/src/common/range_lock.h new file mode 100644 index 000000000..efe6eb549 --- /dev/null +++ b/src/common/range_lock.h @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +namespace Common { + +// From boost thread locking + +template +struct RangeLockGuard { + Iterator begin; + Iterator end; + + RangeLockGuard(Iterator begin_, Iterator end_) : begin(begin_), end(end_) { + LockRange(begin, end); + } + + void release() { + begin = end; + } + + ~RangeLockGuard() { + for (; begin != end; ++begin) { + begin->unlock(); + } + } +}; + +template +Iterator TryLockRange(Iterator begin, Iterator end) { + using LockType = typename std::iterator_traits::value_type; + + if (begin == end) { + return end; + } + + std::unique_lock guard(*begin, std::try_to_lock); + if (!guard.owns_lock()) { + return begin; + } + + Iterator failed = TryLockRange(++begin, end); + if (failed == end) { + guard.release(); + } + + return failed; +} + +template +void LockRange(Iterator begin, Iterator end) { + using LockType = typename std::iterator_traits::value_type; + + if (begin == end) { + return; + } + + bool start_with_begin = true; + Iterator second = begin; + ++second; + Iterator next = second; + + while (true) { + std::unique_lock begin_lock(*begin, std::defer_lock); + if (start_with_begin) { + begin_lock.lock(); + + const Iterator failed_lock = TryLockRange(next, end); + if (failed_lock == end) { + begin_lock.release(); + return; + } + + start_with_begin = false; + next = failed_lock; + } else { + RangeLockGuard guard(next, end); + + if (begin_lock.try_lock()) { + const Iterator failed_lock = TryLockRange(second, next); + if (failed_lock == next) { + begin_lock.release(); + guard.release(); + return; + } + + start_with_begin = false; + next = failed_lock; + } else { + start_with_begin = true; + next = second; + } + } + } +} + +} // namespace Common \ No newline at end of file diff --git a/src/video_core/buffer_cache/memory_tracker.h b/src/video_core/buffer_cache/memory_tracker.h index 37fafa2d6..3dbffdabd 100644 --- a/src/video_core/buffer_cache/memory_tracker.h +++ b/src/video_core/buffer_cache/memory_tracker.h @@ -16,7 +16,7 @@ namespace VideoCore { class MemoryTracker { public: static constexpr size_t MAX_CPU_PAGE_BITS = 40; - static constexpr size_t NUM_HIGH_PAGES = 1ULL << (MAX_CPU_PAGE_BITS - HIGHER_PAGE_BITS); + static constexpr size_t NUM_HIGH_PAGES = 1ULL << (MAX_CPU_PAGE_BITS - TRACKER_HIGHER_PAGE_BITS); static constexpr size_t MANAGER_POOL_SIZE = 32; public: @@ -90,11 +90,11 @@ private: 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}; + std::size_t page_index{cpu_address >> TRACKER_HIGHER_PAGE_BITS}; + u64 page_offset{cpu_address & TRACKER_HIGHER_PAGE_MASK}; while (remaining_size > 0) { const std::size_t copy_amount{ - std::min(HIGHER_PAGE_SIZE - page_offset, remaining_size)}; + std::min(TRACKER_HIGHER_PAGE_SIZE - page_offset, remaining_size)}; auto* manager{top_tier[page_index]}; if (manager) { if constexpr (BOOL_BREAK) { @@ -123,7 +123,7 @@ private: } void CreateRegion(std::size_t page_index) { - const VAddr base_cpu_addr = page_index << HIGHER_PAGE_BITS; + const VAddr base_cpu_addr = page_index << TRACKER_HIGHER_PAGE_BITS; if (free_managers.empty()) { manager_pool.emplace_back(); auto& last_pool = manager_pool.back(); diff --git a/src/video_core/buffer_cache/region_definitions.h b/src/video_core/buffer_cache/region_definitions.h index 80c6afdc6..f035704d9 100644 --- a/src/video_core/buffer_cache/region_definitions.h +++ b/src/video_core/buffer_cache/region_definitions.h @@ -9,13 +9,13 @@ namespace VideoCore { -constexpr u64 PAGES_PER_WORD = 64; -constexpr u64 BYTES_PER_PAGE = 4_KB; +constexpr u64 TRACKER_PAGE_BITS = 12; // 4K pages +constexpr u64 TRACKER_BYTES_PER_PAGE = 1ULL << TRACKER_PAGE_BITS; -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_PAGES = HIGHER_PAGE_SIZE / BYTES_PER_PAGE; +constexpr u64 TRACKER_HIGHER_PAGE_BITS = 24; // each region is 16MB +constexpr u64 TRACKER_HIGHER_PAGE_SIZE = 1ULL << TRACKER_HIGHER_PAGE_BITS; +constexpr u64 TRACKER_HIGHER_PAGE_MASK = TRACKER_HIGHER_PAGE_SIZE - 1ULL; +constexpr u64 NUM_PAGES_PER_REGION = TRACKER_HIGHER_PAGE_SIZE / TRACKER_BYTES_PER_PAGE; enum class Type { CPU, @@ -23,6 +23,6 @@ enum class Type { Writeable, }; -using RegionBits = Common::BitArray; +using RegionBits = Common::BitArray; } // namespace VideoCore \ No newline at end of file diff --git a/src/video_core/buffer_cache/region_manager.h b/src/video_core/buffer_cache/region_manager.h index 07ffee36b..e8ec21129 100644 --- a/src/video_core/buffer_cache/region_manager.h +++ b/src/video_core/buffer_cache/region_manager.h @@ -83,9 +83,10 @@ public: void ChangeRegionState(u64 dirty_addr, u64 size) noexcept(type == Type::GPU) { RENDERER_TRACE; const size_t offset = dirty_addr - cpu_addr; - const size_t start_page = SanitizeAddress(offset) / BYTES_PER_PAGE; - const size_t end_page = Common::DivCeil(SanitizeAddress(offset + size), BYTES_PER_PAGE); - if (start_page >= NUM_REGION_PAGES || end_page <= start_page) { + const size_t start_page = SanitizeAddress(offset) / TRACKER_BYTES_PER_PAGE; + const size_t end_page = + Common::DivCeil(SanitizeAddress(offset + size), TRACKER_BYTES_PER_PAGE); + if (start_page >= NUM_PAGES_PER_REGION || end_page <= start_page) { return; } std::scoped_lock lk{lock}; @@ -114,9 +115,10 @@ public: void ForEachModifiedRange(VAddr query_cpu_range, s64 size, auto&& func) { RENDERER_TRACE; const size_t offset = query_cpu_range - cpu_addr; - const size_t start_page = SanitizeAddress(offset) / BYTES_PER_PAGE; - const size_t end_page = Common::DivCeil(SanitizeAddress(offset + size), BYTES_PER_PAGE); - if (start_page >= NUM_REGION_PAGES || end_page <= start_page) { + const size_t start_page = SanitizeAddress(offset) / TRACKER_BYTES_PER_PAGE; + const size_t end_page = + Common::DivCeil(SanitizeAddress(offset + size), TRACKER_BYTES_PER_PAGE); + if (start_page >= NUM_PAGES_PER_REGION || end_page <= start_page) { return; } std::scoped_lock lk{lock}; @@ -131,7 +133,7 @@ public: } for (const auto& [start, end] : mask) { - func(cpu_addr + start * BYTES_PER_PAGE, (end - start) * BYTES_PER_PAGE); + func(cpu_addr + start * TRACKER_BYTES_PER_PAGE, (end - start) * TRACKER_BYTES_PER_PAGE); } if constexpr (clear) { @@ -151,9 +153,10 @@ public: template [[nodiscard]] bool IsRegionModified(u64 offset, u64 size) const noexcept { RENDERER_TRACE; - const size_t start_page = SanitizeAddress(offset) / BYTES_PER_PAGE; - const size_t end_page = Common::DivCeil(SanitizeAddress(offset + size), BYTES_PER_PAGE); - if (start_page >= NUM_REGION_PAGES || end_page <= start_page) { + const size_t start_page = SanitizeAddress(offset) / TRACKER_BYTES_PER_PAGE; + const size_t end_page = + Common::DivCeil(SanitizeAddress(offset + size), TRACKER_BYTES_PER_PAGE); + if (start_page >= NUM_PAGES_PER_REGION || end_page <= start_page) { return false; } // std::scoped_lock lk{lock}; // Is this needed? diff --git a/src/video_core/page_manager.cpp b/src/video_core/page_manager.cpp index 145779070..15dbf909c 100644 --- a/src/video_core/page_manager.cpp +++ b/src/video_core/page_manager.cpp @@ -4,6 +4,7 @@ #include #include "common/assert.h" #include "common/debug.h" +#include "common/range_lock.h" #include "common/signal_context.h" #include "core/memory.h" #include "core/signals.h" @@ -59,6 +60,7 @@ struct PageManager::Impl { static constexpr size_t ADDRESS_BITS = 40; static constexpr size_t NUM_ADDRESS_PAGES = 1ULL << (40 - PAGE_BITS); + static constexpr size_t NUM_ADDRESS_LOCKS = NUM_ADDRESS_PAGES / PAGES_PER_LOCK; inline static Vulkan::Rasterizer* rasterizer; #ifdef ENABLE_USERFAULTFD Impl(Vulkan::Rasterizer* rasterizer_) { @@ -191,9 +193,17 @@ struct PageManager::Impl { RENDERER_TRACE; size_t page = addr >> PAGE_BITS; + const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE); + + // Acquire locks for the range of pages + const auto lock_start = locks.begin() + (page / PAGES_PER_LOCK); + const auto lock_end = locks.begin() + Common::DivCeil(page_end, PAGES_PER_LOCK); + Common::RangeLockGuard lk(lock_start, lock_end); + auto perms = cached_pages[page].Perm(); u64 range_begin = 0; u64 range_bytes = 0; + u64 potential_range_bytes = 0; const auto release_pending = [&] { if (range_bytes > 0) { @@ -201,13 +211,11 @@ struct PageManager::Impl { // Perform pending (un)protect action Protect(range_begin << PAGE_BITS, range_bytes, perms); range_bytes = 0; + potential_range_bytes = 0; } }; - std::scoped_lock lk(lock); - // Iterate requested pages - const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE); const u64 aligned_addr = page << PAGE_BITS; const u64 aligned_end = page_end << PAGE_BITS; ASSERT_MSG(rasterizer->IsMapped(aligned_addr, aligned_end - aligned_addr), @@ -225,14 +233,19 @@ struct PageManager::Impl { release_pending(); perms = new_perms; } else if (range_bytes != 0) { - // If the protection did not change, extend the current range - range_bytes += PAGE_SIZE; + // If the protection did not change, extend the potential range + potential_range_bytes += PAGE_SIZE; } // Only start a new range if the page must be (un)protected - if (range_bytes == 0 && ((new_count == 0 && !track) || (new_count == 1 && track))) { - range_begin = page; - range_bytes = PAGE_SIZE; + if ((new_count == 0 && !track) || (new_count == 1 && track)) { + if (range_bytes == 0) { + // Start a new potential range + range_begin = page; + potential_range_bytes = PAGE_SIZE; + } + // Extend current range up to potential range + range_bytes = potential_range_bytes; } } @@ -256,9 +269,12 @@ struct PageManager::Impl { } size_t base_page = (base_addr >> PAGE_BITS); + ASSERT(base_page % PAGES_PER_LOCK == 0); + std::scoped_lock lk(locks[base_page / PAGES_PER_LOCK]); auto perms = cached_pages[base_page + start_range.first].Perm(); u64 range_begin = 0; u64 range_bytes = 0; + u64 potential_range_bytes = 0; const auto release_pending = [&] { if (range_bytes > 0) { @@ -266,11 +282,10 @@ struct PageManager::Impl { // Perform pending (un)protect action Protect((range_begin << PAGE_BITS), range_bytes, perms); range_bytes = 0; + potential_range_bytes = 0; } }; - std::scoped_lock lk(lock); - // Iterate pages for (size_t page = start_range.first; page < end_range.second; ++page) { PageState& state = cached_pages[base_page + page]; @@ -284,8 +299,8 @@ struct PageManager::Impl { release_pending(); perms = new_perms; } else if (range_bytes != 0) { - // If the protection did not change, extend the current range - range_bytes += PAGE_SIZE; + // If the protection did not change, extend the potential range + potential_range_bytes += PAGE_SIZE; } // If the page is not being updated, skip it @@ -293,10 +308,15 @@ struct PageManager::Impl { continue; } - // Only start a new range if the page must be (un)protected - if (range_bytes == 0 && ((new_count == 0 && !track) || (new_count == 1 && track))) { - range_begin = base_page + page; - range_bytes = PAGE_SIZE; + // If the page must be (un)protected + if ((new_count == 0 && !track) || (new_count == 1 && track)) { + if (range_bytes == 0) { + // Start a new potential range + range_begin = base_page + page; + potential_range_bytes = PAGE_SIZE; + } + // Extend current rango up to potential range + range_bytes = potential_range_bytes; } } @@ -306,10 +326,11 @@ struct PageManager::Impl { std::array cached_pages{}; #ifdef __linux__ - Common::AdaptiveMutex lock; + using LockType = Common::AdaptiveMutex; #else - Common::SpinLock lock; + using LockType = Common::SpinLock; #endif + std::array locks{}; }; PageManager::PageManager(Vulkan::Rasterizer* rasterizer_) diff --git a/src/video_core/page_manager.h b/src/video_core/page_manager.h index 157b34984..561087ead 100644 --- a/src/video_core/page_manager.h +++ b/src/video_core/page_manager.h @@ -15,8 +15,13 @@ class Rasterizer; namespace VideoCore { class PageManager { - static constexpr size_t PAGE_BITS = 12; - static constexpr size_t PAGE_SIZE = 1ULL << PAGE_BITS; + // Use the same page size as the tracker. + static constexpr size_t PAGE_BITS = TRACKER_PAGE_BITS; + static constexpr size_t PAGE_SIZE = TRACKER_BYTES_PER_PAGE; + + // Keep the lock granularity the same as region granularity. (since each regions has + // itself a lock) + static constexpr size_t PAGES_PER_LOCK = NUM_PAGES_PER_REGION; public: explicit PageManager(Vulkan::Rasterizer* rasterizer); From 0a58ead5f6bf9b0c98c4a017788903f5920bde9d Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Thu, 26 Jun 2025 12:16:23 -0500 Subject: [PATCH 03/13] Add alternate code paths for handling legacy struct behavior in sceVideodec2GetPictureInfo (#3154) Older games aren't fond of how our sceVideodec2GetPictureInfo implementation outputs AVC picture info after the struct size increase. Adding the old struct, and additional code using it for these games works around this problem. --- src/core/libraries/videodec/videodec2.cpp | 37 ++++++++++--- src/core/libraries/videodec/videodec2_avc.h | 53 +++++++++++++++++++ .../libraries/videodec/videodec2_impl.cpp | 48 ++++++++++++----- src/core/libraries/videodec/videodec2_impl.h | 1 + 4 files changed, 117 insertions(+), 22 deletions(-) diff --git a/src/core/libraries/videodec/videodec2.cpp b/src/core/libraries/videodec/videodec2.cpp index 1c6044fe2..8c91e2bf1 100644 --- a/src/core/libraries/videodec/videodec2.cpp +++ b/src/core/libraries/videodec/videodec2.cpp @@ -171,19 +171,40 @@ s32 PS4_SYSV_ABI sceVideodec2GetPictureInfo(const OrbisVideodec2OutputInfo* outp LOG_ERROR(Lib_Vdec2, "Invalid struct size"); return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; } - if (outputInfo->pictureCount == 0 || gPictureInfos.empty()) { + if (outputInfo->pictureCount == 0) { LOG_ERROR(Lib_Vdec2, "No picture info available"); return ORBIS_OK; } - if (p1stPictureInfoOut) { - OrbisVideodec2AvcPictureInfo* picInfo = - static_cast(p1stPictureInfoOut); - if ((picInfo->thisSize | 16) != sizeof(OrbisVideodec2AvcPictureInfo)) { - LOG_ERROR(Lib_Vdec2, "Invalid struct size"); - return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; + // If the game uses the older Videodec2 structs, we need to accomodate that. + if (outputInfo->thisSize != sizeof(OrbisVideodec2OutputInfo)) { + if (gLegacyPictureInfos.empty()) { + LOG_ERROR(Lib_Vdec2, "No picture info available"); + return ORBIS_OK; + } + if (p1stPictureInfoOut) { + OrbisVideodec2LegacyAvcPictureInfo* picInfo = + static_cast(p1stPictureInfoOut); + if (picInfo->thisSize != sizeof(OrbisVideodec2LegacyAvcPictureInfo)) { + LOG_ERROR(Lib_Vdec2, "Invalid struct size"); + return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; + } + *picInfo = gLegacyPictureInfos.back(); + } + } else { + if (gPictureInfos.empty()) { + LOG_ERROR(Lib_Vdec2, "No picture info available"); + return ORBIS_OK; + } + if (p1stPictureInfoOut) { + OrbisVideodec2AvcPictureInfo* picInfo = + static_cast(p1stPictureInfoOut); + if (picInfo->thisSize != sizeof(OrbisVideodec2AvcPictureInfo)) { + LOG_ERROR(Lib_Vdec2, "Invalid struct size"); + return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; + } + *picInfo = gPictureInfos.back(); } - *picInfo = gPictureInfos.back(); } if (outputInfo->pictureCount > 1) { diff --git a/src/core/libraries/videodec/videodec2_avc.h b/src/core/libraries/videodec/videodec2_avc.h index 1975209cb..725d2335f 100644 --- a/src/core/libraries/videodec/videodec2_avc.h +++ b/src/core/libraries/videodec/videodec2_avc.h @@ -74,4 +74,57 @@ struct OrbisVideodec2AvcPictureInfo { }; static_assert(sizeof(OrbisVideodec2AvcPictureInfo) == 0x78); +// An older version of the OrbisVideodec2AvcPictureInfo struct +// Keeping this is needed for compatiblity with older games. +struct OrbisVideodec2LegacyAvcPictureInfo { + 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; +}; +static_assert(sizeof(OrbisVideodec2LegacyAvcPictureInfo) == 0x68); + } // 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 373809c14..667fb79ac 100644 --- a/src/core/libraries/videodec/videodec2_impl.cpp +++ b/src/core/libraries/videodec/videodec2_impl.cpp @@ -12,6 +12,7 @@ namespace Libraries::Vdec2 { std::vector gPictureInfos; +std::vector gLegacyPictureInfos; static inline void CopyNV12Data(u8* dst, const AVFrame& src) { std::memcpy(dst, src.data[0], src.width * src.height); @@ -117,27 +118,46 @@ s32 VdecDecoder::Decode(const OrbisVideodec2InputData& inputData, outputInfo.isErrorFrame = false; outputInfo.pictureCount = 1; // TODO: 2 pictures for interlaced video - // Only set framePitchInBytes if the game uses the newer struct version. + // For proper compatibility with older games, check the inputted OutputInfo struct size. if (outputInfo.thisSize == sizeof(OrbisVideodec2OutputInfo)) { + // framePitchInBytes only exists in the newer struct. outputInfo.framePitchInBytes = frame->linesize[0]; - } + if (outputInfo.isValid) { + OrbisVideodec2AvcPictureInfo pictureInfo = {}; - if (outputInfo.isValid) { - OrbisVideodec2AvcPictureInfo pictureInfo = {}; + pictureInfo.thisSize = sizeof(OrbisVideodec2AvcPictureInfo); + pictureInfo.isValid = true; - pictureInfo.thisSize = sizeof(OrbisVideodec2AvcPictureInfo); - pictureInfo.isValid = true; + pictureInfo.ptsData = inputData.ptsData; + pictureInfo.dtsData = inputData.dtsData; + pictureInfo.attachedData = inputData.attachedData; - 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; - pictureInfo.frameCropLeftOffset = frame->crop_left; - pictureInfo.frameCropRightOffset = frame->crop_right; - pictureInfo.frameCropTopOffset = frame->crop_top; - pictureInfo.frameCropBottomOffset = frame->crop_bottom; + gPictureInfos.push_back(pictureInfo); + } + } else { + if (outputInfo.isValid) { + // If the game uses the older struct versions, we need to use it too. + OrbisVideodec2LegacyAvcPictureInfo pictureInfo = {}; - gPictureInfos.push_back(pictureInfo); + pictureInfo.thisSize = sizeof(OrbisVideodec2LegacyAvcPictureInfo); + 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; + + gLegacyPictureInfos.push_back(pictureInfo); + } } } diff --git a/src/core/libraries/videodec/videodec2_impl.h b/src/core/libraries/videodec/videodec2_impl.h index c8e8ea253..7ee3339db 100644 --- a/src/core/libraries/videodec/videodec2_impl.h +++ b/src/core/libraries/videodec/videodec2_impl.h @@ -16,6 +16,7 @@ extern "C" { namespace Libraries::Vdec2 { extern std::vector gPictureInfos; +extern std::vector gLegacyPictureInfos; class VdecDecoder { public: From 8e44a7099f152b413f87acf24544e53b92e35daf Mon Sep 17 00:00:00 2001 From: TheTurtle Date: Fri, 27 Jun 2025 04:07:56 +0300 Subject: [PATCH 04/13] bit_array: Remove non const operator~ (#3161) --- src/common/bit_array.h | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/common/bit_array.h b/src/common/bit_array.h index f211bbf95..0ab464390 100644 --- a/src/common/bit_array.h +++ b/src/common/bit_array.h @@ -361,13 +361,6 @@ public: return *this; } - inline constexpr BitArray& operator~() { - for (size_t i = 0; i < WORD_COUNT; ++i) { - data[i] = ~data[i]; - } - return *this; - } - inline constexpr BitArray operator|(const BitArray& other) const { BitArray result = *this; result |= other; @@ -388,7 +381,9 @@ public: inline constexpr BitArray operator~() const { BitArray result = *this; - result = ~result; + for (size_t i = 0; i < WORD_COUNT; ++i) { + result.data[i] = ~result.data[i]; + } return result; } @@ -408,4 +403,4 @@ private: std::array data{}; }; -} // namespace Common \ No newline at end of file +} // namespace Common From 52bd92b97dc3790123349dd4a554adfd78211391 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Fri, 27 Jun 2025 09:36:47 +0300 Subject: [PATCH 05/13] New Crowdin updates (#3152) * New translations en_us.ts (Turkish) * New translations en_us.ts (Portuguese, Brazilian) * New translations en_us.ts (Arabic) * New translations en_us.ts (Persian) * New translations en_us.ts (Catalan) * New translations en_us.ts (Serbian (Latin)) * New translations en_us.ts (Swedish) * New translations en_us.ts (Romanian) * New translations en_us.ts (French) * New translations en_us.ts (Spanish) * New translations en_us.ts (Danish) * New translations en_us.ts (German) * New translations en_us.ts (Greek) * New translations en_us.ts (Finnish) * New translations en_us.ts (Hungarian) * New translations en_us.ts (Italian) * New translations en_us.ts (Japanese) * New translations en_us.ts (Korean) * New translations en_us.ts (Lithuanian) * New translations en_us.ts (Dutch) * New translations en_us.ts (Polish) * New translations en_us.ts (Portuguese) * New translations en_us.ts (Russian) * New translations en_us.ts (Slovenian) * New translations en_us.ts (Albanian) * New translations en_us.ts (Ukrainian) * New translations en_us.ts (Chinese Simplified) * New translations en_us.ts (Chinese Traditional) * New translations en_us.ts (Vietnamese) * New translations en_us.ts (Indonesian) * New translations en_us.ts (Norwegian Bokmal) * New translations en_us.ts (Vietnamese) * New translations en_us.ts (Norwegian Bokmal) * New translations en_us.ts (Swedish) * New translations en_us.ts (Catalan) * New translations en_us.ts (Russian) * New translations en_us.ts (Albanian) * New translations en_us.ts (Chinese Simplified) * New translations en_us.ts (Norwegian Bokmal) * New translations en_us.ts (Turkish) * New translations en_us.ts (Turkish) * New translations en_us.ts (Portuguese, Brazilian) --- src/qt_gui/translations/ar_SA.ts | 12 ++++++++++++ src/qt_gui/translations/ca_ES.ts | 12 ++++++++++++ src/qt_gui/translations/da_DK.ts | 12 ++++++++++++ src/qt_gui/translations/de_DE.ts | 12 ++++++++++++ src/qt_gui/translations/el_GR.ts | 12 ++++++++++++ src/qt_gui/translations/es_ES.ts | 12 ++++++++++++ src/qt_gui/translations/fa_IR.ts | 12 ++++++++++++ src/qt_gui/translations/fi_FI.ts | 12 ++++++++++++ src/qt_gui/translations/fr_FR.ts | 12 ++++++++++++ src/qt_gui/translations/hu_HU.ts | 12 ++++++++++++ src/qt_gui/translations/id_ID.ts | 12 ++++++++++++ src/qt_gui/translations/it_IT.ts | 12 ++++++++++++ src/qt_gui/translations/ja_JP.ts | 12 ++++++++++++ src/qt_gui/translations/ko_KR.ts | 12 ++++++++++++ src/qt_gui/translations/lt_LT.ts | 12 ++++++++++++ src/qt_gui/translations/nb_NO.ts | 14 +++++++++++++- src/qt_gui/translations/nl_NL.ts | 12 ++++++++++++ src/qt_gui/translations/pl_PL.ts | 12 ++++++++++++ src/qt_gui/translations/pt_BR.ts | 12 ++++++++++++ src/qt_gui/translations/pt_PT.ts | 12 ++++++++++++ src/qt_gui/translations/ro_RO.ts | 12 ++++++++++++ src/qt_gui/translations/ru_RU.ts | 12 ++++++++++++ src/qt_gui/translations/sl_SI.ts | 12 ++++++++++++ src/qt_gui/translations/sq_AL.ts | 18 +++++++++++++++--- src/qt_gui/translations/sr_CS.ts | 12 ++++++++++++ src/qt_gui/translations/sv_SE.ts | 12 ++++++++++++ src/qt_gui/translations/tr_TR.ts | 14 +++++++++++++- src/qt_gui/translations/uk_UA.ts | 12 ++++++++++++ src/qt_gui/translations/vi_VN.ts | 14 +++++++++++++- src/qt_gui/translations/zh_CN.ts | 12 ++++++++++++ src/qt_gui/translations/zh_TW.ts | 12 ++++++++++++ 31 files changed, 378 insertions(+), 6 deletions(-) diff --git a/src/qt_gui/translations/ar_SA.ts b/src/qt_gui/translations/ar_SA.ts index a090c8b9b..9e974a365 100644 --- a/src/qt_gui/translations/ar_SA.ts +++ b/src/qt_gui/translations/ar_SA.ts @@ -748,6 +748,10 @@ Last updated آخر تحديث + + Favorite + Favorite + GameListUtils @@ -950,6 +954,14 @@ SFO Viewer for عارض SFO لـ + + Remove from Favorites + Remove from Favorites + + + Add to Favorites + Add to Favorites + HelpDialog diff --git a/src/qt_gui/translations/ca_ES.ts b/src/qt_gui/translations/ca_ES.ts index bb9dc3915..0ab2a1680 100644 --- a/src/qt_gui/translations/ca_ES.ts +++ b/src/qt_gui/translations/ca_ES.ts @@ -748,6 +748,10 @@ Last updated Darrera actualització + + Favorite + Preferit + GameListUtils @@ -950,6 +954,14 @@ SFO Viewer for Visualitzador SFO per + + Remove from Favorites + Esborra dels preferits + + + Add to Favorites + Afegeix a preferits + HelpDialog diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts index 871a05af4..6f32b3f52 100644 --- a/src/qt_gui/translations/da_DK.ts +++ b/src/qt_gui/translations/da_DK.ts @@ -748,6 +748,10 @@ Last updated Sidst opdateret + + Favorite + Favorite + GameListUtils @@ -950,6 +954,14 @@ SFO Viewer for SFO Viewer for + + Remove from Favorites + Remove from Favorites + + + Add to Favorites + Add to Favorites + HelpDialog diff --git a/src/qt_gui/translations/de_DE.ts b/src/qt_gui/translations/de_DE.ts index 771e4d2e4..d34e1db20 100644 --- a/src/qt_gui/translations/de_DE.ts +++ b/src/qt_gui/translations/de_DE.ts @@ -748,6 +748,10 @@ Last updated Zuletzt aktualisiert + + Favorite + Favorite + GameListUtils @@ -950,6 +954,14 @@ SFO Viewer for SFO-Betrachter für + + Remove from Favorites + Remove from Favorites + + + Add to Favorites + Add to Favorites + HelpDialog diff --git a/src/qt_gui/translations/el_GR.ts b/src/qt_gui/translations/el_GR.ts index 6d6a629e2..a7ed888d7 100644 --- a/src/qt_gui/translations/el_GR.ts +++ b/src/qt_gui/translations/el_GR.ts @@ -748,6 +748,10 @@ Last updated Τελευταία ενημέρωση + + Favorite + Favorite + GameListUtils @@ -950,6 +954,14 @@ SFO Viewer for SFO Viewer for + + Remove from Favorites + Remove from Favorites + + + Add to Favorites + Add to Favorites + HelpDialog diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index 6e469c1fa..33ddb69f0 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -748,6 +748,10 @@ Last updated Última actualización + + Favorite + Favorite + GameListUtils @@ -950,6 +954,14 @@ SFO Viewer for Visualizador de SFO para + + Remove from Favorites + Remove from Favorites + + + Add to Favorites + Add to Favorites + HelpDialog diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index c270dd64a..12de2dfe3 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -748,6 +748,10 @@ Last updated آخرین به‌روزرسانی + + Favorite + Favorite + GameListUtils @@ -950,6 +954,14 @@ SFO Viewer for SFO مشاهده + + Remove from Favorites + Remove from Favorites + + + Add to Favorites + Add to Favorites + HelpDialog diff --git a/src/qt_gui/translations/fi_FI.ts b/src/qt_gui/translations/fi_FI.ts index 49b6381e6..192cbd09f 100644 --- a/src/qt_gui/translations/fi_FI.ts +++ b/src/qt_gui/translations/fi_FI.ts @@ -748,6 +748,10 @@ Last updated Viimeksi päivitetty + + Favorite + Favorite + GameListUtils @@ -950,6 +954,14 @@ SFO Viewer for SFO Viewer for + + Remove from Favorites + Remove from Favorites + + + Add to Favorites + Add to Favorites + HelpDialog diff --git a/src/qt_gui/translations/fr_FR.ts b/src/qt_gui/translations/fr_FR.ts index 803063979..17a078c87 100644 --- a/src/qt_gui/translations/fr_FR.ts +++ b/src/qt_gui/translations/fr_FR.ts @@ -748,6 +748,10 @@ Last updated Dernière mise à jour + + Favorite + Favorite + GameListUtils @@ -950,6 +954,14 @@ SFO Viewer for Visionneuse SFO pour + + Remove from Favorites + Remove from Favorites + + + Add to Favorites + Add to Favorites + HelpDialog diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index d6f50a274..37a4d2efb 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -748,6 +748,10 @@ Last updated Utoljára frissítve + + Favorite + Favorite + GameListUtils @@ -950,6 +954,14 @@ SFO Viewer for SFO Viewer for + + Remove from Favorites + Remove from Favorites + + + Add to Favorites + Add to Favorites + HelpDialog diff --git a/src/qt_gui/translations/id_ID.ts b/src/qt_gui/translations/id_ID.ts index d35ec509f..9aeb3934c 100644 --- a/src/qt_gui/translations/id_ID.ts +++ b/src/qt_gui/translations/id_ID.ts @@ -748,6 +748,10 @@ Last updated Terakhir diperbarui + + Favorite + Favorite + GameListUtils @@ -950,6 +954,14 @@ SFO Viewer for SFO Viewer for + + Remove from Favorites + Remove from Favorites + + + Add to Favorites + Add to Favorites + HelpDialog diff --git a/src/qt_gui/translations/it_IT.ts b/src/qt_gui/translations/it_IT.ts index 65334a6f8..d689214d6 100644 --- a/src/qt_gui/translations/it_IT.ts +++ b/src/qt_gui/translations/it_IT.ts @@ -748,6 +748,10 @@ Last updated Ultimo aggiornamento + + Favorite + Favorite + GameListUtils @@ -950,6 +954,14 @@ SFO Viewer for Visualizzatore SFO per + + Remove from Favorites + Remove from Favorites + + + Add to Favorites + Add to Favorites + HelpDialog diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index dd10956f3..3f378d3d0 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -748,6 +748,10 @@ Last updated 最終更新 + + Favorite + Favorite + GameListUtils @@ -950,6 +954,14 @@ SFO Viewer for SFO Viewer for + + Remove from Favorites + Remove from Favorites + + + Add to Favorites + Add to Favorites + HelpDialog diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts index cbe8d00f9..83b27910c 100644 --- a/src/qt_gui/translations/ko_KR.ts +++ b/src/qt_gui/translations/ko_KR.ts @@ -748,6 +748,10 @@ Last updated 마지막 업데이트 + + Favorite + Favorite + GameListUtils @@ -950,6 +954,14 @@ SFO Viewer for SFO Viewer for + + Remove from Favorites + Remove from Favorites + + + Add to Favorites + Add to Favorites + HelpDialog diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts index fda6f595f..8c6496ac6 100644 --- a/src/qt_gui/translations/lt_LT.ts +++ b/src/qt_gui/translations/lt_LT.ts @@ -748,6 +748,10 @@ Last updated Paskutinį kartą atnaujinta + + Favorite + Favorite + GameListUtils @@ -950,6 +954,14 @@ SFO Viewer for SFO Viewer for + + Remove from Favorites + Remove from Favorites + + + Add to Favorites + Add to Favorites + HelpDialog diff --git a/src/qt_gui/translations/nb_NO.ts b/src/qt_gui/translations/nb_NO.ts index 1e022a5b4..53fa461bd 100644 --- a/src/qt_gui/translations/nb_NO.ts +++ b/src/qt_gui/translations/nb_NO.ts @@ -748,6 +748,10 @@ Last updated Sist oppdatert + + Favorite + Favoritter + GameListUtils @@ -950,6 +954,14 @@ SFO Viewer for SFO-viser for + + Remove from Favorites + Fjern fra favoritter + + + Add to Favorites + Legg til i favoritter + HelpDialog @@ -1078,7 +1090,7 @@ note: click Help Button/Special Keybindings for more information - Merk: Trykk på hjelpeknappen for mer informasjon + Merk: Trykk på «Hjelp»-knappen for mer informasjon Face Buttons diff --git a/src/qt_gui/translations/nl_NL.ts b/src/qt_gui/translations/nl_NL.ts index 7dc03bbc5..3fd718cb1 100644 --- a/src/qt_gui/translations/nl_NL.ts +++ b/src/qt_gui/translations/nl_NL.ts @@ -748,6 +748,10 @@ Last updated Laatst bijgewerkt + + Favorite + Favorite + GameListUtils @@ -950,6 +954,14 @@ SFO Viewer for SFO Viewer for + + Remove from Favorites + Remove from Favorites + + + Add to Favorites + Add to Favorites + HelpDialog diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index 38036c07f..d14dabec7 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -748,6 +748,10 @@ Last updated Ostatnia aktualizacja + + Favorite + Favorite + GameListUtils @@ -950,6 +954,14 @@ SFO Viewer for Menedżer plików SFO dla + + Remove from Favorites + Remove from Favorites + + + Add to Favorites + Add to Favorites + HelpDialog diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index acc75790e..3ff06d213 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -748,6 +748,10 @@ Last updated Última atualização + + Favorite + Favorito + GameListUtils @@ -950,6 +954,14 @@ SFO Viewer for Visualizador de SFO para + + Remove from Favorites + Remover dos Favoritos + + + Add to Favorites + Adicionar aos Favoritos + HelpDialog diff --git a/src/qt_gui/translations/pt_PT.ts b/src/qt_gui/translations/pt_PT.ts index fba315859..a8aa10d85 100644 --- a/src/qt_gui/translations/pt_PT.ts +++ b/src/qt_gui/translations/pt_PT.ts @@ -748,6 +748,10 @@ Last updated Última atualização + + Favorite + Favorite + GameListUtils @@ -950,6 +954,14 @@ SFO Viewer for Visualizador SFO para + + Remove from Favorites + Remove from Favorites + + + Add to Favorites + Add to Favorites + HelpDialog diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index 1a626d1a8..a7ca9800d 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -748,6 +748,10 @@ Last updated Ultima actualizare + + Favorite + Favorite + GameListUtils @@ -950,6 +954,14 @@ SFO Viewer for SFO Viewer for + + Remove from Favorites + Remove from Favorites + + + Add to Favorites + Add to Favorites + HelpDialog diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index 7579078e6..844508603 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -748,6 +748,10 @@ Last updated Последнее обновление + + Favorite + Избранное + GameListUtils @@ -950,6 +954,14 @@ SFO Viewer for Просмотр SFO для + + Remove from Favorites + Удалить из избранного + + + Add to Favorites + Добавить в избранное + HelpDialog diff --git a/src/qt_gui/translations/sl_SI.ts b/src/qt_gui/translations/sl_SI.ts index 1a0c5df5b..333b999aa 100644 --- a/src/qt_gui/translations/sl_SI.ts +++ b/src/qt_gui/translations/sl_SI.ts @@ -748,6 +748,10 @@ Last updated Last updated + + Favorite + Favorite + GameListUtils @@ -950,6 +954,14 @@ SFO Viewer for SFO Viewer for + + Remove from Favorites + Remove from Favorites + + + Add to Favorites + Add to Favorites + HelpDialog diff --git a/src/qt_gui/translations/sq_AL.ts b/src/qt_gui/translations/sq_AL.ts index 26daf7419..365e33f5e 100644 --- a/src/qt_gui/translations/sq_AL.ts +++ b/src/qt_gui/translations/sq_AL.ts @@ -748,6 +748,10 @@ Last updated Përditësuar për herë të fundit + + Favorite + Të Preferuarat + GameListUtils @@ -950,6 +954,14 @@ SFO Viewer for Shikuesi SFO për + + Remove from Favorites + Hiq nga të Preferuarat + + + Add to Favorites + Shto në të Preferuarat + HelpDialog @@ -1186,15 +1198,15 @@ Touchpad Left - Touchpad Left + Paneli me Prekje Majtas Touchpad Center - Touchpad Center + Paneli me Prekje në Qendër Touchpad Right - Touchpad Right + Paneli me Prekje Djathtas diff --git a/src/qt_gui/translations/sr_CS.ts b/src/qt_gui/translations/sr_CS.ts index e6527006d..aa9fcd18d 100644 --- a/src/qt_gui/translations/sr_CS.ts +++ b/src/qt_gui/translations/sr_CS.ts @@ -748,6 +748,10 @@ Last updated Last updated + + Favorite + Favorite + GameListUtils @@ -950,6 +954,14 @@ SFO Viewer for SFO Viewer for + + Remove from Favorites + Remove from Favorites + + + Add to Favorites + Add to Favorites + HelpDialog diff --git a/src/qt_gui/translations/sv_SE.ts b/src/qt_gui/translations/sv_SE.ts index c10f0058f..813198c42 100644 --- a/src/qt_gui/translations/sv_SE.ts +++ b/src/qt_gui/translations/sv_SE.ts @@ -748,6 +748,10 @@ Last updated Senast uppdaterad + + Favorite + Favorit + GameListUtils @@ -950,6 +954,14 @@ SFO Viewer for SFO-visare för + + Remove from Favorites + Ta bort från favoriter + + + Add to Favorites + Lägg till i favoriter + HelpDialog diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index 8838b3132..e58542f98 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -748,6 +748,10 @@ Last updated Son güncelleme + + Favorite + Favorite + GameListUtils @@ -950,6 +954,14 @@ SFO Viewer for SFO Görüntüleyici: + + Remove from Favorites + Remove from Favorites + + + Add to Favorites + Add to Favorites + HelpDialog @@ -1180,7 +1192,7 @@ Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: %1 - Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + Aynı tuş birden fazla kez atanamaz. Aşağıdaki tuşlara birden fazla giriş atanmış: %1 diff --git a/src/qt_gui/translations/uk_UA.ts b/src/qt_gui/translations/uk_UA.ts index 070194fc3..85442c4e6 100644 --- a/src/qt_gui/translations/uk_UA.ts +++ b/src/qt_gui/translations/uk_UA.ts @@ -748,6 +748,10 @@ Last updated Останнє оновлення + + Favorite + Favorite + GameListUtils @@ -950,6 +954,14 @@ SFO Viewer for Перегляд SFO + + Remove from Favorites + Remove from Favorites + + + Add to Favorites + Add to Favorites + HelpDialog diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index 961f9272b..544add7e1 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -748,6 +748,10 @@ Last updated Cập nhật lần cuối + + Favorite + Favorite + GameListUtils @@ -924,7 +928,7 @@ No log file found for this game! - ! + Failed to convert icon. @@ -950,6 +954,14 @@ SFO Viewer for SFO Viewer for + + Remove from Favorites + Remove from Favorites + + + Add to Favorites + Add to Favorites + HelpDialog diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index 9533e95ea..2b3f67b1f 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -748,6 +748,10 @@ Last updated 最后更新 + + Favorite + 收藏 + GameListUtils @@ -950,6 +954,14 @@ SFO Viewer for SFO 查看器 - + + Remove from Favorites + 从收藏中移除 + + + Add to Favorites + 添加至收藏 + HelpDialog diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index 2b33053e0..0d334f9ef 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -748,6 +748,10 @@ Last updated 最後更新 + + Favorite + Favorite + GameListUtils @@ -950,6 +954,14 @@ SFO Viewer for SFO 檢視器: + + Remove from Favorites + Remove from Favorites + + + Add to Favorites + Add to Favorites + HelpDialog From 4bfd8b967bc384607b4dbee5d47f06e919b803bf Mon Sep 17 00:00:00 2001 From: nickci2002 <58965309+nickci2002@users.noreply.github.com> Date: Fri, 27 Jun 2025 06:55:12 -0400 Subject: [PATCH 06/13] Changed symbol bindings to their names (#3158) * Changed symbol bindings to their names * Fixed kakposfos' requests on GitHub * Fine, I'll do it myself. --------- Co-authored-by: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> --- src/input/input_handler.h | 78 ++++++++++++------------- src/qt_gui/kbm_gui.cpp | 64 ++++++++++---------- src/qt_gui/kbm_help_dialog.cpp | 103 ++++++++++++++++++++++----------- 3 files changed, 142 insertions(+), 103 deletions(-) diff --git a/src/input/input_handler.h b/src/input/input_handler.h index 745906620..daef22f21 100644 --- a/src/input/input_handler.h +++ b/src/input/input_handler.h @@ -177,38 +177,38 @@ const std::map string_to_keyboard_key_map = { {"9", SDLK_9}, // symbols - {"`", SDLK_GRAVE}, - {"~", SDLK_TILDE}, - {"!", SDLK_EXCLAIM}, - {"@", SDLK_AT}, - {"#", SDLK_HASH}, - {"$", SDLK_DOLLAR}, - {"%", SDLK_PERCENT}, - {"^", SDLK_CARET}, - {"&", SDLK_AMPERSAND}, - {"*", SDLK_ASTERISK}, - {"(", SDLK_LEFTPAREN}, - {")", SDLK_RIGHTPAREN}, - {"-", SDLK_MINUS}, - {"_", SDLK_UNDERSCORE}, - {"=", SDLK_EQUALS}, - {"+", SDLK_PLUS}, - {"[", SDLK_LEFTBRACKET}, - {"]", SDLK_RIGHTBRACKET}, - {"{", SDLK_LEFTBRACE}, - {"}", SDLK_RIGHTBRACE}, - {"\\", SDLK_BACKSLASH}, - {"|", SDLK_PIPE}, - {";", SDLK_SEMICOLON}, - {":", SDLK_COLON}, - {"'", SDLK_APOSTROPHE}, - {"\"", SDLK_DBLAPOSTROPHE}, - {",", SDLK_COMMA}, - {"<", SDLK_LESS}, - {".", SDLK_PERIOD}, - {">", SDLK_GREATER}, - {"/", SDLK_SLASH}, - {"?", SDLK_QUESTION}, + {"grave", SDLK_GRAVE}, + {"tilde", SDLK_TILDE}, + {"exclamation", SDLK_EXCLAIM}, + {"at", SDLK_AT}, + {"hash", SDLK_HASH}, + {"dollar", SDLK_DOLLAR}, + {"percent", SDLK_PERCENT}, + {"caret", SDLK_CARET}, + {"ampersand", SDLK_AMPERSAND}, + {"asterisk", SDLK_ASTERISK}, + {"lparen", SDLK_LEFTPAREN}, + {"rparen", SDLK_RIGHTPAREN}, + {"minus", SDLK_MINUS}, + {"underscore", SDLK_UNDERSCORE}, + {"equals", SDLK_EQUALS}, + {"plus", SDLK_PLUS}, + {"lbracket", SDLK_LEFTBRACKET}, + {"rbracket", SDLK_RIGHTBRACKET}, + {"lbrace", SDLK_LEFTBRACE}, + {"rbrace", SDLK_RIGHTBRACE}, + {"backslash", SDLK_BACKSLASH}, + {"pipe", SDLK_PIPE}, + {"semicolon", SDLK_SEMICOLON}, + {"colon", SDLK_COLON}, + {"apostrophe", SDLK_APOSTROPHE}, + {"quote", SDLK_DBLAPOSTROPHE}, + {"comma", SDLK_COMMA}, + {"less", SDLK_LESS}, + {"period", SDLK_PERIOD}, + {"greater", SDLK_GREATER}, + {"slash", SDLK_SLASH}, + {"question", SDLK_QUESTION}, // special keys {"escape", SDLK_ESCAPE}, @@ -252,13 +252,13 @@ const std::map string_to_keyboard_key_map = { {"kp7", SDLK_KP_7}, {"kp8", SDLK_KP_8}, {"kp9", SDLK_KP_9}, - {"kp.", SDLK_KP_PERIOD}, - {"kp,", SDLK_KP_COMMA}, - {"kp/", SDLK_KP_DIVIDE}, - {"kp*", SDLK_KP_MULTIPLY}, - {"kp-", SDLK_KP_MINUS}, - {"kp+", SDLK_KP_PLUS}, - {"kp=", SDLK_KP_EQUALS}, + {"kpperiod", SDLK_KP_PERIOD}, + {"kpcomma", SDLK_KP_COMMA}, + {"kpslash", SDLK_KP_DIVIDE}, + {"kpasterisk", SDLK_KP_MULTIPLY}, + {"kpminus", SDLK_KP_MINUS}, + {"kpplus", SDLK_KP_PLUS}, + {"kpequals", SDLK_KP_EQUALS}, {"kpenter", SDLK_KP_ENTER}, // mouse diff --git a/src/qt_gui/kbm_gui.cpp b/src/qt_gui/kbm_gui.cpp index 1f7743412..4cc3c16db 100644 --- a/src/qt_gui/kbm_gui.cpp +++ b/src/qt_gui/kbm_gui.cpp @@ -711,92 +711,98 @@ bool KBMSettings::eventFilter(QObject* obj, QEvent* event) { break; // symbols + case Qt::Key_QuoteLeft: + pressedKeys.insert("grave"); + break; + case Qt::Key_AsciiTilde: + pressedKeys.insert("tilde"); + break; case Qt::Key_Exclam: - pressedKeys.insert("!"); + pressedKeys.insert("exclamation"); break; case Qt::Key_At: - pressedKeys.insert("@"); + pressedKeys.insert("at"); break; case Qt::Key_NumberSign: - pressedKeys.insert("#"); + pressedKeys.insert("hash"); break; case Qt::Key_Dollar: - pressedKeys.insert("$"); + pressedKeys.insert("dollar"); break; case Qt::Key_Percent: - pressedKeys.insert("%"); + pressedKeys.insert("percent"); break; case Qt::Key_AsciiCircum: - pressedKeys.insert("^"); + pressedKeys.insert("caret"); break; case Qt::Key_Ampersand: - pressedKeys.insert("&"); + pressedKeys.insert("ampersand"); break; case Qt::Key_Asterisk: - pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kp*", "*")); + pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kpasterisk", "asterisk")); break; case Qt::Key_ParenLeft: - pressedKeys.insert("("); + pressedKeys.insert("lparen"); break; case Qt::Key_ParenRight: - pressedKeys.insert(")"); + pressedKeys.insert("rparen"); break; case Qt::Key_Minus: - pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kp-", "-")); + pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kpminus", "minus")); break; case Qt::Key_Underscore: - pressedKeys.insert("_"); + pressedKeys.insert("underscore"); break; case Qt::Key_Equal: - pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kp=", "=")); + pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kpequals", "equals")); break; case Qt::Key_Plus: - pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kp+", "+")); + pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kpplus", "plus")); break; case Qt::Key_BracketLeft: - pressedKeys.insert("["); + pressedKeys.insert("lbracket"); break; case Qt::Key_BracketRight: - pressedKeys.insert("]"); + pressedKeys.insert("rbracket"); break; case Qt::Key_BraceLeft: - pressedKeys.insert("{"); + pressedKeys.insert("lbrace"); break; case Qt::Key_BraceRight: - pressedKeys.insert("}"); + pressedKeys.insert("rbrace"); break; case Qt::Key_Backslash: - pressedKeys.insert("\\"); + pressedKeys.insert("backslash"); break; case Qt::Key_Bar: - pressedKeys.insert("|"); + pressedKeys.insert("pipe"); break; case Qt::Key_Semicolon: - pressedKeys.insert(";"); + pressedKeys.insert("semicolon"); break; case Qt::Key_Colon: - pressedKeys.insert(":"); + pressedKeys.insert("colon"); break; case Qt::Key_Apostrophe: - pressedKeys.insert("'"); + pressedKeys.insert("apostrophe"); break; case Qt::Key_QuoteDbl: - pressedKeys.insert("\""); + pressedKeys.insert("quote"); break; case Qt::Key_Comma: - pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kp,", ",")); + pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kpcomma", "comma")); break; case Qt::Key_Less: - pressedKeys.insert("<"); + pressedKeys.insert("less"); break; case Qt::Key_Period: - pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kp.", ".")); + pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kpperiod", "period")); break; case Qt::Key_Greater: - pressedKeys.insert(">"); + pressedKeys.insert("greater"); break; case Qt::Key_Slash: - pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kp/", "/")); + pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kpslash", "slash")); break; case Qt::Key_Question: pressedKeys.insert("question"); diff --git a/src/qt_gui/kbm_help_dialog.cpp b/src/qt_gui/kbm_help_dialog.cpp index 1c40c6c4d..b9055222c 100644 --- a/src/qt_gui/kbm_help_dialog.cpp +++ b/src/qt_gui/kbm_help_dialog.cpp @@ -121,35 +121,6 @@ To view the config file's syntax, check out the Syntax tab, for keybind names, v This project began because I disliked the original, unchangeable keybinds. 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 obviously, you can make adjustments however you like.)"; } -QString HelpDialog::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 the emulator if the debug menu is open --F8: Reparses the config file while in-game --F7: Toggles mouse capture and mouse input --F6: Toggles mouse-to-gyro emulation - -Q: How do I switch between mouse and controller joystick input? Why is it even required? -A: Pressing F7 toggles between mouse and controller joystick input. It is required because the program polls the mouse input, which means it checks mouse movement every frame. If it didn't move, the code would manually set the emulator's virtual controller to 0 (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 skips 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. - -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 default.ini 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 HelpDialog::syntax() { return R"( Below is the file format for mouse, keyboard, and controller inputs: @@ -187,13 +158,12 @@ Keyboard: Numbers: '0', '1', ..., '9' Keypad: - 'kp 0', 'kp 1', ..., 'kp 9', - 'kp .', 'kp ,', 'kp /', 'kp *', 'kp -', 'kp +', 'kp =', 'kp enter' + 'kp0', 'kp1', ..., 'kp9', + 'kpperiod', 'kpcomma', 'kpslash', 'kpasterisk', 'kpminus', 'kpplus', 'kpequals', 'kpenter' Symbols: - '`', '~', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '-', '_', '=', '+', '{', '}', '[', ']', '\', '|', - ';', ':', ''', '"', ',', '<', '.', '>', '/', '?' + (See below) Special keys: - 'escape (text editor only)', 'printscreen', 'scrolllock', 'pausebreak', + 'escape' (text editor only), 'printscreen', 'scrolllock', 'pausebreak', 'backspace', 'insert', 'delete', 'home', 'end', 'pgup', 'pgdown', 'tab', 'capslock', 'enter', 'space' Arrow keys: @@ -228,7 +198,38 @@ Controller: 'l2' Invalid Inputs: - 'F1-F12' are reserved for emulator-wide keybinds, and cannot be bound to controller inputs.)"; + 'F1-F12' are reserved for emulator-wide keybinds, and cannot be bound to controller inputs. + +Symbols (expanded): + ` 'grave' + ~ 'tilde' + ! 'exclamation' + @ 'at' + # 'hash' + $ 'dollar' + % 'percent' + ^ 'caret' + & 'ampersand' + * 'asterisk' + ( 'lparen' + - 'minus' + _ 'underscore' + = 'equals' + + 'plus' + [ 'lbracket' + { 'lbrace' + \ 'backslash' + | 'pipe' + ; 'semicolon' + : 'colon' + ' 'apostrophe' + " 'quote' + , 'comma' + < 'less' + . 'period' + > 'greater' + / 'slash' + ? 'question')"; } QString HelpDialog::special() { @@ -267,3 +268,35 @@ You can find these here, with detailed comments, examples, and suggestions for m 'mouse_gyro_roll_mode': Controls whether moving the mouse sideways causes a panning or a rolling motion while mouse-to-gyro emulation is active.)"; } + +QString HelpDialog::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 the emulator if the debug menu is open +-F8: Reparses the config file while in-game +-F7: Toggles mouse capture and mouse input +-F6: Toggles mouse-to-gyro emulation + +Q: How do I switch between mouse and controller joystick input? Why is it even required? +A: Pressing F7 toggles between mouse and controller joystick input. It is required because the program polls the mouse input, which means it checks mouse movement every frame. If it didn't move, the code would manually set the emulator's virtual controller to 0 (to the center), even if other input devices would update it. + +Q: What in the world is a 'grave' key? +A: (`). It represents one of the many symbols you can bind to a key. You can find the various symbols and their names in the Bindings tab. + +Q: What happens if I accidentally make a typo in the config? +A: The code recognises the line as wrong and skips 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. + +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 default.ini 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.)"; +} \ No newline at end of file From 09b584b23fc564c2e492a821b6eeb03b46f84dc5 Mon Sep 17 00:00:00 2001 From: rainmakerv2 <30595646+rainmakerv3@users.noreply.github.com> Date: Fri, 27 Jun 2025 21:49:31 +0800 Subject: [PATCH 07/13] Kbm GUI - minor fixes (#3146) * KBM Gui fixes * remove unneeded activate window calls --- src/qt_gui/kbm_gui.cpp | 150 +++++++++++++++++++++++------------------ src/qt_gui/kbm_gui.h | 4 +- 2 files changed, 87 insertions(+), 67 deletions(-) diff --git a/src/qt_gui/kbm_gui.cpp b/src/qt_gui/kbm_gui.cpp index 4cc3c16db..3f41e1f08 100644 --- a/src/qt_gui/kbm_gui.cpp +++ b/src/qt_gui/kbm_gui.cpp @@ -144,6 +144,8 @@ tr("Do you want to overwrite existing mappings with the mappings from the Common QString SOSString = tr("Speed Offset (def 0.125):") + " " + SOSValue; ui->SpeedOffsetLabel->setText(SOSString); }); + + connect(this, &KBMSettings::PushKBMEvent, this, [this]() { CheckMapping(MappingButton); }); } void KBMSettings::ButtonConnects() { @@ -518,7 +520,6 @@ void KBMSettings::StartTimer(QPushButton*& button) { MappingTimer = 3; EnableMapping = true; MappingCompleted = false; - modifier = ""; mapping = button->text(); DisableMappingButtons(); @@ -873,7 +874,6 @@ bool KBMSettings::eventFilter(QObject* obj, QEvent* event) { } break; case Qt::Key_Meta: - activateWindow(); #ifdef _WIN32 pressedKeys.insert("lwin"); #else @@ -884,7 +884,6 @@ bool KBMSettings::eventFilter(QObject* obj, QEvent* event) { pressedKeys.insert("space"); break; case Qt::Key_Up: - activateWindow(); pressedKeys.insert("up"); break; case Qt::Key_Down: @@ -909,81 +908,100 @@ bool KBMSettings::eventFilter(QObject* obj, QEvent* event) { } return true; } - } - if (event->type() == QEvent::MouseButtonPress) { - QMouseEvent* mouseEvent = static_cast(event); - if (pressedKeys.size() < 3) { - switch (mouseEvent->button()) { - case Qt::LeftButton: - pressedKeys.insert("leftbutton"); - break; - case Qt::RightButton: - pressedKeys.insert("rightbutton"); - break; - case Qt::MiddleButton: - pressedKeys.insert("middlebutton"); - break; - case Qt::XButton1: - pressedKeys.insert("sidebuttonback"); - break; - case Qt::XButton2: - pressedKeys.insert("sidebuttonforward"); - break; + if (event->type() == QEvent::MouseButtonPress) { + QMouseEvent* mouseEvent = static_cast(event); + if (pressedKeys.size() < 3) { + switch (mouseEvent->button()) { + case Qt::LeftButton: + pressedKeys.insert("leftbutton"); + break; + case Qt::RightButton: + pressedKeys.insert("rightbutton"); + break; + case Qt::MiddleButton: + pressedKeys.insert("middlebutton"); + break; + case Qt::XButton1: + pressedKeys.insert("sidebuttonback"); + break; + case Qt::XButton2: + pressedKeys.insert("sidebuttonforward"); + break; - // default case - default: - break; - // bottom text + // default case + default: + break; + // bottom text + } + return true; } - return true; } - } - const QList AxisList = { - ui->LStickUpButton, ui->LStickDownButton, ui->LStickLeftButton, ui->LStickRightButton, - ui->RStickUpButton, ui->LStickDownButton, ui->LStickLeftButton, ui->RStickRightButton}; + const QList AxisList = { + ui->LStickUpButton, ui->LStickDownButton, ui->LStickLeftButton, ui->LStickRightButton, + ui->RStickUpButton, ui->LStickDownButton, ui->LStickLeftButton, ui->RStickRightButton}; - if (event->type() == QEvent::Wheel) { - QWheelEvent* wheelEvent = static_cast(event); - if (pressedKeys.size() < 3) { - if (wheelEvent->angleDelta().y() > 5) { - if (std::find(AxisList.begin(), AxisList.end(), MappingButton) == AxisList.end()) { - pressedKeys.insert("mousewheelup"); - } else { - QMessageBox::information(this, tr("Cannot set mapping"), - tr("Mousewheel cannot be mapped to stick outputs")); + if (event->type() == QEvent::Wheel) { + QWheelEvent* wheelEvent = static_cast(event); + if (pressedKeys.size() < 3) { + if (wheelEvent->angleDelta().y() > 5) { + if (std::find(AxisList.begin(), AxisList.end(), MappingButton) == + AxisList.end()) { + pressedKeys.insert("mousewheelup"); + if (QApplication::keyboardModifiers() == Qt::NoModifier) + emit PushKBMEvent(); + } else { + QMessageBox::information( + this, tr("Cannot set mapping"), + tr("Mousewheel cannot be mapped to stick outputs")); + } + } else if (wheelEvent->angleDelta().y() < -5) { + if (std::find(AxisList.begin(), AxisList.end(), MappingButton) == + AxisList.end()) { + pressedKeys.insert("mousewheeldown"); + if (QApplication::keyboardModifiers() == Qt::NoModifier) + emit PushKBMEvent(); + } else { + QMessageBox::information( + this, tr("Cannot set mapping"), + tr("Mousewheel cannot be mapped to stick outputs")); + } } - } else if (wheelEvent->angleDelta().y() < -5) { - if (std::find(AxisList.begin(), AxisList.end(), MappingButton) == AxisList.end()) { - pressedKeys.insert("mousewheeldown"); - } else { - QMessageBox::information(this, tr("Cannot set mapping"), - tr("Mousewheel cannot be mapped to stick outputs")); - } - } - if (wheelEvent->angleDelta().x() > 5) { - if (std::find(AxisList.begin(), AxisList.end(), MappingButton) == AxisList.end()) { - // QT changes scrolling to horizontal for all widgets with the alt modifier - pressedKeys.insert( - GetModifiedButton(Qt::AltModifier, "mousewheelup", "mousewheelright")); - } else { - QMessageBox::information(this, tr("Cannot set mapping"), - tr("Mousewheel cannot be mapped to stick outputs")); - } - } else if (wheelEvent->angleDelta().x() < -5) { - if (std::find(AxisList.begin(), AxisList.end(), MappingButton) == AxisList.end()) { - pressedKeys.insert( - GetModifiedButton(Qt::AltModifier, "mousewheeldown", "mousewheelleft")); - } else { - QMessageBox::information(this, tr("Cannot set mapping"), - tr("Mousewheel cannot be mapped to stick outputs")); + if (wheelEvent->angleDelta().x() > 5) { + if (std::find(AxisList.begin(), AxisList.end(), MappingButton) == + AxisList.end()) { + // QT changes scrolling to horizontal for all widgets with the alt modifier + pressedKeys.insert( + GetModifiedButton(Qt::AltModifier, "mousewheelup", "mousewheelright")); + if (QApplication::keyboardModifiers() == Qt::NoModifier) + emit PushKBMEvent(); + } else { + QMessageBox::information( + this, tr("Cannot set mapping"), + tr("Mousewheel cannot be mapped to stick outputs")); + } + } else if (wheelEvent->angleDelta().x() < -5) { + if (std::find(AxisList.begin(), AxisList.end(), MappingButton) == + AxisList.end()) { + pressedKeys.insert( + GetModifiedButton(Qt::AltModifier, "mousewheeldown", "mousewheelleft")); + if (QApplication::keyboardModifiers() == Qt::NoModifier) + emit PushKBMEvent(); + } else { + QMessageBox::information( + this, tr("Cannot set mapping"), + tr("Mousewheel cannot be mapped to stick outputs")); + } } } } + + if (event->type() == QEvent::KeyRelease || event->type() == QEvent::MouseButtonRelease) + emit PushKBMEvent(); } return QDialog::eventFilter(obj, event); } -KBMSettings::~KBMSettings() {} \ No newline at end of file +KBMSettings::~KBMSettings() {} diff --git a/src/qt_gui/kbm_gui.h b/src/qt_gui/kbm_gui.h index 09a9166b9..b14b506bd 100644 --- a/src/qt_gui/kbm_gui.h +++ b/src/qt_gui/kbm_gui.h @@ -26,6 +26,9 @@ public: explicit KBMSettings(std::shared_ptr game_info_get, QWidget* parent = nullptr); ~KBMSettings(); +signals: + void PushKBMEvent(); + private Q_SLOTS: void SaveKBMConfig(bool CloseOnSave); void SetDefault(); @@ -50,7 +53,6 @@ private: bool MappingCompleted = false; bool HelpWindowOpen = false; QString mapping; - QString modifier; int MappingTimer; QTimer* timer; QPushButton* MappingButton; From fa32537f402b0ab84c612ff7fdf0c5e73b533016 Mon Sep 17 00:00:00 2001 From: rainmakerv2 <30595646+rainmakerv3@users.noreply.github.com> Date: Fri, 27 Jun 2025 21:55:52 +0800 Subject: [PATCH 08/13] Controller Remapping GUI v2 (#3144) * Remapping GUI V2 - initial commit * Unmap button with escape key * Allow combination inputs * Use separate class for SDL event signals so that i can work with the SDL window event loop * Automatically pause game when GUI open to better manage event queue * Move sd;_gamepad_added event from remap object to GUI object to avoid conflicts with sdl window * Use signals on button/trigger to release to make GUI more responsive * pause game while KBM window is open for consistency * don't check gamepad when game is running to avoid conflicts * Block all other sdl events instead of pausing game, automatic parse inputs after saving * Don't block window restored or window exposed cases * Properly exit event loop thread on exit --- CMakeLists.txt | 2 + src/qt_gui/control_settings.cpp | 907 +++++++++++++++++++++---------- src/qt_gui/control_settings.h | 70 ++- src/qt_gui/control_settings.ui | 676 +++++++++++++---------- src/qt_gui/kbm_gui.cpp | 40 +- src/qt_gui/kbm_gui.h | 6 +- src/qt_gui/main_window.cpp | 9 +- src/qt_gui/main_window.h | 2 + src/qt_gui/sdl_event_wrapper.cpp | 47 ++ src/qt_gui/sdl_event_wrapper.h | 25 + src/sdl_window.cpp | 11 + 11 files changed, 1194 insertions(+), 601 deletions(-) create mode 100644 src/qt_gui/sdl_event_wrapper.cpp create mode 100644 src/qt_gui/sdl_event_wrapper.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ab846fa9d..466933608 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1068,6 +1068,8 @@ set(QT_GUI src/qt_gui/about_dialog.cpp src/qt_gui/gui_settings.h src/qt_gui/settings.cpp src/qt_gui/settings.h + src/qt_gui/sdl_event_wrapper.cpp + src/qt_gui/sdl_event_wrapper.h ${EMULATOR} ${RESOURCE_FILES} ${TRANSLATIONS} diff --git a/src/qt_gui/control_settings.cpp b/src/qt_gui/control_settings.cpp index 4206e45b8..319daecdd 100644 --- a/src/qt_gui/control_settings.cpp +++ b/src/qt_gui/control_settings.cpp @@ -2,21 +2,67 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include #include +#include "common/logging/log.h" #include "common/path_util.h" #include "control_settings.h" +#include "input/input_handler.h" #include "ui_control_settings.h" -ControlSettings::ControlSettings(std::shared_ptr game_info_get, QWidget* parent) - : QDialog(parent), m_game_info(game_info_get), ui(new Ui::ControlSettings) { +ControlSettings::ControlSettings(std::shared_ptr game_info_get, bool isGameRunning, + std::string GameRunningSerial, QWidget* parent) + : QDialog(parent), m_game_info(game_info_get), GameRunning(isGameRunning), + RunningGameSerial(GameRunningSerial), ui(new Ui::ControlSettings) { ui->setupUi(this); - ui->PerGameCheckBox->setChecked(!Config::GetUseUnifiedInputConfig()); + + if (!GameRunning) { + SDL_InitSubSystem(SDL_INIT_GAMEPAD); + SDL_InitSubSystem(SDL_INIT_EVENTS); + CheckGamePad(); + } else { + SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); + } AddBoxItems(); SetUIValuestoMappings(); UpdateLightbarColor(); + installEventFilter(this); + + ButtonsList = {ui->CrossButton, + ui->CircleButton, + ui->TriangleButton, + ui->SquareButton, + ui->L1Button, + ui->R1Button, + ui->L2Button, + ui->R2Button, + ui->L3Button, + ui->R3Button, + ui->OptionsButton, + ui->TouchpadLeftButton, + ui->TouchpadCenterButton, + ui->TouchpadRightButton, + ui->DpadUpButton, + ui->DpadDownButton, + ui->DpadLeftButton, + ui->DpadRightButton}; + + AxisList = {ui->LStickUpButton, ui->LStickDownButton, ui->LStickLeftButton, + ui->LStickRightButton, ui->RStickUpButton, ui->RStickDownButton, + ui->RStickLeftButton, ui->RStickRightButton}; + + for (auto& button : ButtonsList) { + connect(button, &QPushButton::clicked, this, + [this, &button]() { StartTimer(button, true); }); + } + + for (auto& button : AxisList) { + connect(button, &QPushButton::clicked, this, + [this, &button]() { StartTimer(button, false); }); + } connect(ui->buttonBox, &QDialogButtonBox::clicked, this, [this](QAbstractButton* button) { if (button == ui->buttonBox->button(QDialogButtonBox::Save)) { @@ -33,6 +79,8 @@ ControlSettings::ControlSettings(std::shared_ptr game_info_get, Q ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setText(tr("Restore Defaults")); ui->buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel")); + ui->PerGameCheckBox->setChecked(!Config::GetUseUnifiedInputConfig()); + connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QWidget::close); connect(ui->ProfileComboBox, &QComboBox::currentTextChanged, this, [this] { @@ -45,24 +93,6 @@ ControlSettings::ControlSettings(std::shared_ptr game_info_get, Q connect(ui->RightDeadzoneSlider, &QSlider::valueChanged, this, [this](int value) { ui->RightDeadzoneValue->setText(QString::number(value)); }); - connect(ui->LStickUpBox, &QComboBox::currentIndexChanged, this, - [this](int value) { ui->LStickDownBox->setCurrentIndex(value); }); - connect(ui->LStickDownBox, &QComboBox::currentIndexChanged, this, - [this](int value) { ui->LStickUpBox->setCurrentIndex(value); }); - connect(ui->LStickRightBox, &QComboBox::currentIndexChanged, this, - [this](int value) { ui->LStickLeftBox->setCurrentIndex(value); }); - connect(ui->LStickLeftBox, &QComboBox::currentIndexChanged, this, - [this](int value) { ui->LStickRightBox->setCurrentIndex(value); }); - - connect(ui->RStickUpBox, &QComboBox::currentIndexChanged, this, - [this](int value) { ui->RStickDownBox->setCurrentIndex(value); }); - connect(ui->RStickDownBox, &QComboBox::currentIndexChanged, this, - [this](int value) { ui->RStickUpBox->setCurrentIndex(value); }); - connect(ui->RStickRightBox, &QComboBox::currentIndexChanged, this, - [this](int value) { ui->RStickLeftBox->setCurrentIndex(value); }); - connect(ui->RStickLeftBox, &QComboBox::currentIndexChanged, this, - [this](int value) { ui->RStickRightBox->setCurrentIndex(value); }); - connect(ui->RSlider, &QSlider::valueChanged, this, [this](int value) { QString RedValue = QString("%1").arg(value, 3, 10, QChar('0')); QString RValue = tr("R:") + " " + RedValue; @@ -83,30 +113,44 @@ ControlSettings::ControlSettings(std::shared_ptr game_info_get, Q ui->BLabel->setText(BValue); UpdateLightbarColor(); }); + + connect(this, &ControlSettings::PushGamepadEvent, this, + [this]() { CheckMapping(MappingButton); }); + connect(this, &ControlSettings::AxisChanged, this, + [this]() { ConnectAxisInputs(MappingButton); }); + + RemapWrapper = SdlEventWrapper::Wrapper::GetInstance(); + SdlEventWrapper::Wrapper::wrapperActive = true; + QObject::connect(RemapWrapper, &SdlEventWrapper::Wrapper::SDLEvent, this, + &ControlSettings::processSDLEvents); + + if (!GameRunning) { + Polling = QtConcurrent::run(&ControlSettings::pollSDLEvents, this); + } } void ControlSettings::SaveControllerConfig(bool CloseOnSave) { - QList list; - list << ui->RStickUpBox << ui->RStickRightBox << ui->LStickUpBox << ui->LStickRightBox; + QList list; + list << ui->RStickUpButton << ui->RStickRightButton << ui->LStickUpButton + << ui->LStickRightButton; int count_axis_left_x = 0, count_axis_left_y = 0, count_axis_right_x = 0, count_axis_right_y = 0; for (const auto& i : list) { - if (i->currentText() == "axis_left_x") { + if (i->text() == "axis_left_x") { count_axis_left_x = count_axis_left_x + 1; - } else if (i->currentText() == "axis_left_y") { + } else if (i->text() == "axis_left_y") { count_axis_left_y = count_axis_left_y + 1; - } else if (i->currentText() == "axis_right_x") { + } else if (i->text() == "axis_right_x") { count_axis_right_x = count_axis_right_x + 1; - } else if (i->currentText() == "axis_right_y") { + } else if (i->text() == "axis_right_y") { count_axis_right_y = count_axis_right_y + 1; } } if (count_axis_left_x > 1 | count_axis_left_y > 1 | count_axis_right_x > 1 | count_axis_right_y > 1) { - QMessageBox::StandardButton nosave; - nosave = QMessageBox::information(this, tr("Unable to Save"), - tr("Cannot bind axis values more than once")); + QMessageBox::information(this, tr("Unable to Save"), + tr("Cannot bind axis values more than once")); return; } @@ -118,7 +162,7 @@ void ControlSettings::SaveControllerConfig(bool CloseOnSave) { int lineCount = 0; std::string line; - std::vector lines; + std::vector lines, inputs; std::string output_string = "", input_string = ""; std::fstream file(config_file); @@ -141,9 +185,17 @@ void ControlSettings::SaveControllerConfig(bool CloseOnSave) { output_string = line.substr(0, equal_pos - 1); input_string = line.substr(equal_pos + 2); - if (std::find(ControllerInputs.begin(), ControllerInputs.end(), input_string) != - ControllerInputs.end() || - output_string == "analog_deadzone" || output_string == "override_controller_color") { + bool controllerInputdetected = false; + for (std::string input : ControllerInputs) { + // Needed to avoid detecting backspace while detecting back + if (input_string.contains(input) && !input_string.contains("backspace")) { + controllerInputdetected = true; + break; + } + } + + if (controllerInputdetected || output_string == "analog_deadzone" || + output_string == "override_controller_color") { line.erase(); continue; } @@ -152,92 +204,60 @@ void ControlSettings::SaveControllerConfig(bool CloseOnSave) { file.close(); - input_string = "cross"; - output_string = ui->ABox->currentText().toStdString(); - lines.push_back(output_string + " = " + input_string); + // Lambda to reduce repetitive code for mapping buttons to config lines + auto add_mapping = [&](const QString& buttonText, const std::string& output_name) { + input_string = buttonText.toStdString(); + output_string = output_name; + if (input_string != "unmapped") { + lines.push_back(output_string + " = " + input_string); + inputs.push_back(input_string); + } + }; - input_string = "circle"; - output_string = ui->BBox->currentText().toStdString(); - lines.push_back(output_string + " = " + input_string); - - input_string = "square"; - output_string = ui->XBox->currentText().toStdString(); - lines.push_back(output_string + " = " + input_string); - - input_string = "triangle"; - output_string = ui->YBox->currentText().toStdString(); - lines.push_back(output_string + " = " + input_string); + add_mapping(ui->CrossButton->text(), "cross"); + add_mapping(ui->CircleButton->text(), "circle"); + add_mapping(ui->SquareButton->text(), "square"); + add_mapping(ui->TriangleButton->text(), "triangle"); lines.push_back(""); - input_string = "l1"; - output_string = ui->LBBox->currentText().toStdString(); - lines.push_back(output_string + " = " + input_string); - - input_string = "r1"; - output_string = ui->RBBox->currentText().toStdString(); - lines.push_back(output_string + " = " + input_string); - - input_string = "l2"; - output_string = ui->LTBox->currentText().toStdString(); - lines.push_back(output_string + " = " + input_string); - - input_string = "r2"; - output_string = ui->RTBox->currentText().toStdString(); - lines.push_back(output_string + " = " + input_string); - - input_string = "l3"; - output_string = ui->LClickBox->currentText().toStdString(); - lines.push_back(output_string + " = " + input_string); - - input_string = "r3"; - output_string = ui->RClickBox->currentText().toStdString(); - lines.push_back(output_string + " = " + input_string); + add_mapping(ui->L1Button->text(), "l1"); + add_mapping(ui->R1Button->text(), "r1"); + add_mapping(ui->L2Button->text(), "l2"); + add_mapping(ui->R2Button->text(), "r2"); + add_mapping(ui->L3Button->text(), "l3"); + add_mapping(ui->R3Button->text(), "r3"); lines.push_back(""); - input_string = "back"; - output_string = ui->BackBox->currentText().toStdString(); - lines.push_back(output_string + " = " + input_string); - - input_string = "options"; - output_string = ui->StartBox->currentText().toStdString(); - lines.push_back(output_string + " = " + input_string); + add_mapping(ui->TouchpadLeftButton->text(), "touchpad_left"); + add_mapping(ui->TouchpadCenterButton->text(), "touchpad_center"); + add_mapping(ui->TouchpadRightButton->text(), "touchpad_right"); + add_mapping(ui->OptionsButton->text(), "options"); lines.push_back(""); - input_string = "pad_up"; - output_string = ui->DpadUpBox->currentText().toStdString(); - lines.push_back(output_string + " = " + input_string); - - input_string = "pad_down"; - output_string = ui->DpadDownBox->currentText().toStdString(); - lines.push_back(output_string + " = " + input_string); - - input_string = "pad_left"; - output_string = ui->DpadLeftBox->currentText().toStdString(); - lines.push_back(output_string + " = " + input_string); - - input_string = "pad_right"; - output_string = ui->DpadRightBox->currentText().toStdString(); - lines.push_back(output_string + " = " + input_string); + add_mapping(ui->DpadUpButton->text(), "pad_up"); + add_mapping(ui->DpadDownButton->text(), "pad_down"); + add_mapping(ui->DpadLeftButton->text(), "pad_left"); + add_mapping(ui->DpadRightButton->text(), "pad_right"); lines.push_back(""); - input_string = "axis_left_x"; - output_string = ui->LStickRightBox->currentText().toStdString(); + output_string = "axis_left_x"; + input_string = ui->LStickRightButton->text().toStdString(); lines.push_back(output_string + " = " + input_string); - input_string = "axis_left_y"; - output_string = ui->LStickUpBox->currentText().toStdString(); + output_string = "axis_left_y"; + input_string = ui->LStickUpButton->text().toStdString(); lines.push_back(output_string + " = " + input_string); - input_string = "axis_right_x"; - output_string = ui->RStickRightBox->currentText().toStdString(); + output_string = "axis_right_x"; + input_string = ui->RStickRightButton->text().toStdString(); lines.push_back(output_string + " = " + input_string); - input_string = "axis_right_y"; - output_string = ui->RStickUpBox->currentText().toStdString(); + output_string = "axis_right_y"; + input_string = ui->RStickUpButton->text().toStdString(); lines.push_back(output_string + " = " + input_string); lines.push_back(""); @@ -257,6 +277,33 @@ void ControlSettings::SaveControllerConfig(bool CloseOnSave) { lines.push_back("override_controller_color = " + OverrideLB + ", " + LightBarR + ", " + LightBarG + ", " + LightBarB); + // Prevent duplicate inputs that break the input engine + bool duplicateFound = false; + QSet duplicateMappings; + + for (auto it = inputs.begin(); it != inputs.end(); ++it) { + if (std::find(it + 1, inputs.end(), *it) != inputs.end()) { + duplicateFound = true; + duplicateMappings.insert(QString::fromStdString(*it)); + } + } + + if (duplicateFound) { + QStringList duplicatesList; + for (const QString mapping : duplicateMappings) { + for (const auto& button : ButtonsList) { + if (button->text() == mapping) + duplicatesList.append(button->objectName() + " - " + mapping); + } + } + QMessageBox::information( + this, tr("Unable to Save"), + // clang-format off + QString(tr("Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:\n\n%1").arg(duplicatesList.join("\n")))); + // clang-format on + return; + } + std::vector save; bool CurrentLineEmpty = false, LastLineEmpty = false; for (auto const& line : lines) { @@ -278,36 +325,43 @@ void ControlSettings::SaveControllerConfig(bool CloseOnSave) { ui->BSlider->value()); Config::save(Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "config.toml"); + if (GameRunning) { + Config::GetUseUnifiedInputConfig() ? Input::ParseInputConfig("default") + : Input::ParseInputConfig(RunningGameSerial); + } + if (CloseOnSave) QWidget::close(); } void ControlSettings::SetDefault() { - ui->ABox->setCurrentIndex(0); - ui->BBox->setCurrentIndex(1); - ui->XBox->setCurrentIndex(2); - ui->YBox->setCurrentIndex(3); - ui->DpadUpBox->setCurrentIndex(11); - ui->DpadDownBox->setCurrentIndex(12); - ui->DpadLeftBox->setCurrentIndex(13); - ui->DpadRightBox->setCurrentIndex(14); - ui->LClickBox->setCurrentIndex(8); - ui->RClickBox->setCurrentIndex(9); - ui->LBBox->setCurrentIndex(4); - ui->RBBox->setCurrentIndex(5); - ui->LTBox->setCurrentIndex(6); - ui->RTBox->setCurrentIndex(7); - ui->StartBox->setCurrentIndex(10); - ui->BackBox->setCurrentIndex(15); + ui->CrossButton->setText("cross"); + ui->CircleButton->setText("circle"); + ui->SquareButton->setText("square"); + ui->TriangleButton->setText("triangle"); + ui->DpadUpButton->setText("pad_up"); + ui->DpadDownButton->setText("pad_down"); + ui->DpadLeftButton->setText("pad_left"); + ui->DpadRightButton->setText("pad_right"); + ui->L3Button->setText("l3"); + ui->R3Button->setText("r3"); + ui->L1Button->setText("l1"); + ui->R1Button->setText("r1"); + ui->L2Button->setText("l2"); + ui->R2Button->setText("r2"); + ui->OptionsButton->setText("options"); + ui->TouchpadLeftButton->setText("back"); + ui->TouchpadCenterButton->setText("unmapped"); + ui->TouchpadRightButton->setText("unmapped"); - ui->LStickUpBox->setCurrentIndex(1); - ui->LStickDownBox->setCurrentIndex(1); - ui->LStickLeftBox->setCurrentIndex(0); - ui->LStickRightBox->setCurrentIndex(0); - ui->RStickUpBox->setCurrentIndex(3); - ui->RStickDownBox->setCurrentIndex(3); - ui->RStickLeftBox->setCurrentIndex(2); - ui->RStickRightBox->setCurrentIndex(2); + ui->LStickUpButton->setText("axis_left_y"); + ui->LStickDownButton->setText("axis_left_y"); + ui->LStickLeftButton->setText("axis_left_x"); + ui->LStickRightButton->setText("axis_left_x"); + ui->RStickUpButton->setText("axis_right_y"); + ui->RStickDownButton->setText("axis_right_y"); + ui->RStickLeftButton->setText("axis_right_x"); + ui->RStickRightButton->setText("axis_right_x"); ui->LeftDeadzoneSlider->setValue(2); ui->RightDeadzoneSlider->setValue(2); @@ -320,32 +374,6 @@ void ControlSettings::SetDefault() { } void ControlSettings::AddBoxItems() { - ui->DpadUpBox->addItems(ButtonOutputs); - ui->DpadDownBox->addItems(ButtonOutputs); - ui->DpadLeftBox->addItems(ButtonOutputs); - ui->DpadRightBox->addItems(ButtonOutputs); - ui->LBBox->addItems(ButtonOutputs); - ui->RBBox->addItems(ButtonOutputs); - ui->LTBox->addItems(ButtonOutputs); - ui->RTBox->addItems(ButtonOutputs); - ui->RClickBox->addItems(ButtonOutputs); - ui->LClickBox->addItems(ButtonOutputs); - ui->StartBox->addItems(ButtonOutputs); - ui->ABox->addItems(ButtonOutputs); - ui->BBox->addItems(ButtonOutputs); - ui->XBox->addItems(ButtonOutputs); - ui->YBox->addItems(ButtonOutputs); - ui->BackBox->addItems(ButtonOutputs); - - ui->LStickUpBox->addItems(StickOutputs); - ui->LStickDownBox->addItems(StickOutputs); - ui->LStickLeftBox->addItems(StickOutputs); - ui->LStickRightBox->addItems(StickOutputs); - ui->RStickUpBox->addItems(StickOutputs); - ui->RStickDownBox->addItems(StickOutputs); - ui->RStickLeftBox->addItems(StickOutputs); - ui->RStickRightBox->addItems(StickOutputs); - ui->ProfileComboBox->addItem("Common Config"); for (int i = 0; i < m_game_info->m_games.size(); i++) { ui->ProfileComboBox->addItem(QString::fromStdString(m_game_info->m_games[i].serial)); @@ -366,7 +394,8 @@ void ControlSettings::SetUIValuestoMappings() { bool CrossExists = false, CircleExists = false, SquareExists = false, TriangleExists = false, L1Exists = false, L2Exists = false, L3Exists = false, R1Exists = false, R2Exists = false, R3Exists = false, DPadUpExists = false, DPadDownExists = false, DPadLeftExists = false, - DPadRightExists = false, StartExists = false, BackExists = false, LStickXExists = false, + DPadRightExists = false, OptionsExists = false, TouchpadLeftExists = false, + TouchpadCenterExists = false, TouchpadRightExists = false, LStickXExists = false, LStickYExists = false, RStickXExists = false, RStickYExists = false; int lineCount = 0; std::string line = ""; @@ -388,127 +417,144 @@ void ControlSettings::SetUIValuestoMappings() { std::string output_string = line.substr(0, equal_pos); std::string input_string = line.substr(equal_pos + 1); - if (std::find(ControllerInputs.begin(), ControllerInputs.end(), input_string) != - ControllerInputs.end() || - output_string == "analog_deadzone" || output_string == "override_controller_color") { - if (input_string == "cross") { - ui->ABox->setCurrentText(QString::fromStdString(output_string)); + bool controllerInputdetected = false; + for (std::string input : ControllerInputs) { + // Needed to avoid detecting backspace while detecting back + if (input_string.contains(input) && !input_string.contains("backspace")) { + controllerInputdetected = true; + break; + } + } + + if (controllerInputdetected) { + if (output_string == "cross") { + ui->CrossButton->setText(QString::fromStdString(input_string)); CrossExists = true; - } else if (input_string == "circle") { - ui->BBox->setCurrentText(QString::fromStdString(output_string)); + } else if (output_string == "circle") { + ui->CircleButton->setText(QString::fromStdString(input_string)); CircleExists = true; - } else if (input_string == "square") { - ui->XBox->setCurrentText(QString::fromStdString(output_string)); + } else if (output_string == "square") { + ui->SquareButton->setText(QString::fromStdString(input_string)); SquareExists = true; - } else if (input_string == "triangle") { - ui->YBox->setCurrentText(QString::fromStdString(output_string)); + } else if (output_string == "triangle") { + ui->TriangleButton->setText(QString::fromStdString(input_string)); TriangleExists = true; - } else if (input_string == "l1") { - ui->LBBox->setCurrentText(QString::fromStdString(output_string)); + } else if (output_string == "l1") { + ui->L1Button->setText(QString::fromStdString(input_string)); L1Exists = true; - } else if (input_string == "l2") { - ui->LTBox->setCurrentText(QString::fromStdString(output_string)); + } else if (output_string == "l2") { + ui->L2Button->setText(QString::fromStdString(input_string)); L2Exists = true; - } else if (input_string == "r1") { - ui->RBBox->setCurrentText(QString::fromStdString(output_string)); + } else if (output_string == "r1") { + ui->R1Button->setText(QString::fromStdString(input_string)); R1Exists = true; - } else if (input_string == "r2") { - ui->RTBox->setCurrentText(QString::fromStdString(output_string)); + } else if (output_string == "r2") { + ui->R2Button->setText(QString::fromStdString(input_string)); R2Exists = true; - } else if (input_string == "l3") { - ui->LClickBox->setCurrentText(QString::fromStdString(output_string)); + } else if (output_string == "l3") { + ui->L3Button->setText(QString::fromStdString(input_string)); L3Exists = true; - } else if (input_string == "r3") { - ui->RClickBox->setCurrentText(QString::fromStdString(output_string)); + } else if (output_string == "r3") { + ui->R3Button->setText(QString::fromStdString(input_string)); R3Exists = true; - } else if (input_string == "pad_up") { - ui->DpadUpBox->setCurrentText(QString::fromStdString(output_string)); + } else if (output_string == "pad_up") { + ui->DpadUpButton->setText(QString::fromStdString(input_string)); DPadUpExists = true; - } else if (input_string == "pad_down") { - ui->DpadDownBox->setCurrentText(QString::fromStdString(output_string)); + } else if (output_string == "pad_down") { + ui->DpadDownButton->setText(QString::fromStdString(input_string)); DPadDownExists = true; - } else if (input_string == "pad_left") { - ui->DpadLeftBox->setCurrentText(QString::fromStdString(output_string)); + } else if (output_string == "pad_left") { + ui->DpadLeftButton->setText(QString::fromStdString(input_string)); DPadLeftExists = true; - } else if (input_string == "pad_right") { - ui->DpadRightBox->setCurrentText(QString::fromStdString(output_string)); + } else if (output_string == "pad_right") { + ui->DpadRightButton->setText(QString::fromStdString(input_string)); DPadRightExists = true; - } else if (input_string == "options") { - ui->StartBox->setCurrentText(QString::fromStdString(output_string)); - StartExists = true; - } else if (input_string == "back") { - ui->BackBox->setCurrentText(QString::fromStdString(output_string)); - BackExists = true; - } else if (input_string == "axis_left_x") { - ui->LStickRightBox->setCurrentText(QString::fromStdString(output_string)); - ui->LStickLeftBox->setCurrentText(QString::fromStdString(output_string)); + } else if (output_string == "options") { + ui->OptionsButton->setText(QString::fromStdString(input_string)); + OptionsExists = true; + } else if (output_string == "touchpad_left") { + ui->TouchpadLeftButton->setText(QString::fromStdString(input_string)); + TouchpadLeftExists = true; + } else if (output_string == "touchpad_center") { + ui->TouchpadCenterButton->setText(QString::fromStdString(input_string)); + TouchpadCenterExists = true; + } else if (output_string == "touchpad_right") { + ui->TouchpadRightButton->setText(QString::fromStdString(input_string)); + TouchpadRightExists = true; + } else if (output_string == "axis_left_x") { + ui->LStickRightButton->setText(QString::fromStdString(input_string)); + ui->LStickLeftButton->setText(QString::fromStdString(input_string)); LStickXExists = true; - } else if (input_string == "axis_left_y") { - ui->LStickUpBox->setCurrentText(QString::fromStdString(output_string)); - ui->LStickDownBox->setCurrentText(QString::fromStdString(output_string)); + } else if (output_string == "axis_left_y") { + ui->LStickUpButton->setText(QString::fromStdString(input_string)); + ui->LStickDownButton->setText(QString::fromStdString(input_string)); LStickYExists = true; - } else if (input_string == "axis_right_x") { - ui->RStickRightBox->setCurrentText(QString::fromStdString(output_string)); - ui->RStickLeftBox->setCurrentText(QString::fromStdString(output_string)); + } else if (output_string == "axis_right_x") { + ui->RStickRightButton->setText(QString::fromStdString(input_string)); + ui->RStickLeftButton->setText(QString::fromStdString(input_string)); RStickXExists = true; - } else if (input_string == "axis_right_y") { - ui->RStickUpBox->setCurrentText(QString::fromStdString(output_string)); - ui->RStickDownBox->setCurrentText(QString::fromStdString(output_string)); + } else if (output_string == "axis_right_y") { + ui->RStickUpButton->setText(QString::fromStdString(input_string)); + ui->RStickDownButton->setText(QString::fromStdString(input_string)); RStickYExists = true; - } else if (input_string.contains("leftjoystick")) { - std::size_t comma_pos = line.find(','); - if (comma_pos != std::string::npos) { - int deadzonevalue = std::stoi(line.substr(comma_pos + 1)); - ui->LeftDeadzoneSlider->setValue(deadzonevalue); - ui->LeftDeadzoneValue->setText(QString::number(deadzonevalue)); - } else { - ui->LeftDeadzoneSlider->setValue(2); - ui->LeftDeadzoneValue->setText("2"); + } + } + + if (input_string.contains("leftjoystick")) { + std::size_t comma_pos = line.find(','); + if (comma_pos != std::string::npos) { + int deadzonevalue = std::stoi(line.substr(comma_pos + 1)); + ui->LeftDeadzoneSlider->setValue(deadzonevalue); + ui->LeftDeadzoneValue->setText(QString::number(deadzonevalue)); + } else { + ui->LeftDeadzoneSlider->setValue(2); + ui->LeftDeadzoneValue->setText("2"); + } + } + + if (input_string.contains("rightjoystick")) { + std::size_t comma_pos = line.find(','); + if (comma_pos != std::string::npos) { + int deadzonevalue = std::stoi(line.substr(comma_pos + 1)); + ui->RightDeadzoneSlider->setValue(deadzonevalue); + ui->RightDeadzoneValue->setText(QString::number(deadzonevalue)); + } else { + ui->RightDeadzoneSlider->setValue(2); + ui->RightDeadzoneValue->setText("2"); + } + } + + if (output_string == "override_controller_color") { + std::size_t comma_pos = line.find(','); + if (comma_pos != std::string::npos) { + std::string overridestring = line.substr(equal_pos + 1, comma_pos); + bool override = overridestring.contains("true") ? true : false; + ui->LightbarCheckBox->setChecked(override); + + std::string lightbarstring = line.substr(comma_pos + 1); + std::size_t comma_pos2 = lightbarstring.find(','); + if (comma_pos2 != std::string::npos) { + std::string Rstring = lightbarstring.substr(0, comma_pos2); + ui->RSlider->setValue(std::stoi(Rstring)); + QString RedValue = QString("%1").arg(std::stoi(Rstring), 3, 10, QChar('0')); + QString RValue = tr("R:") + " " + RedValue; + ui->RLabel->setText(RValue); } - } else if (input_string.contains("rightjoystick")) { - std::size_t comma_pos = line.find(','); - if (comma_pos != std::string::npos) { - int deadzonevalue = std::stoi(line.substr(comma_pos + 1)); - ui->RightDeadzoneSlider->setValue(deadzonevalue); - ui->RightDeadzoneValue->setText(QString::number(deadzonevalue)); - } else { - ui->RightDeadzoneSlider->setValue(2); - ui->RightDeadzoneValue->setText("2"); - } - } else if (output_string == "override_controller_color") { - std::size_t comma_pos = line.find(','); - if (comma_pos != std::string::npos) { - std::string overridestring = line.substr(equal_pos + 1, comma_pos); - bool override = overridestring.contains("true") ? true : false; - ui->LightbarCheckBox->setChecked(override); - std::string lightbarstring = line.substr(comma_pos + 1); - std::size_t comma_pos2 = lightbarstring.find(','); - if (comma_pos2 != std::string::npos) { - std::string Rstring = lightbarstring.substr(0, comma_pos2); - ui->RSlider->setValue(std::stoi(Rstring)); - QString RedValue = QString("%1").arg(std::stoi(Rstring), 3, 10, QChar('0')); - QString RValue = tr("R:") + " " + RedValue; - ui->RLabel->setText(RValue); - } + std::string GBstring = lightbarstring.substr(comma_pos2 + 1); + std::size_t comma_pos3 = GBstring.find(','); + if (comma_pos3 != std::string::npos) { + std::string Gstring = GBstring.substr(0, comma_pos3); + ui->GSlider->setValue(std::stoi(Gstring)); + QString GreenValue = QString("%1").arg(std::stoi(Gstring), 3, 10, QChar('0')); + QString GValue = tr("G:") + " " + GreenValue; + ui->GLabel->setText(GValue); - std::string GBstring = lightbarstring.substr(comma_pos2 + 1); - std::size_t comma_pos3 = GBstring.find(','); - if (comma_pos3 != std::string::npos) { - std::string Gstring = GBstring.substr(0, comma_pos3); - ui->GSlider->setValue(std::stoi(Gstring)); - QString GreenValue = - QString("%1").arg(std::stoi(Gstring), 3, 10, QChar('0')); - QString GValue = tr("G:") + " " + GreenValue; - ui->GLabel->setText(GValue); - - std::string Bstring = GBstring.substr(comma_pos3 + 1); - ui->BSlider->setValue(std::stoi(Bstring)); - QString BlueValue = - QString("%1").arg(std::stoi(Bstring), 3, 10, QChar('0')); - QString BValue = tr("B:") + " " + BlueValue; - ui->BLabel->setText(BValue); - } + std::string Bstring = GBstring.substr(comma_pos3 + 1); + ui->BSlider->setValue(std::stoi(Bstring)); + QString BlueValue = QString("%1").arg(std::stoi(Bstring), 3, 10, QChar('0')); + QString BValue = tr("B:") + " " + BlueValue; + ui->BLabel->setText(BValue); } } } @@ -517,53 +563,57 @@ void ControlSettings::SetUIValuestoMappings() { // If an entry does not exist in the config file, we assume the user wants it unmapped if (!CrossExists) - ui->ABox->setCurrentText("unmapped"); + ui->CrossButton->setText("unmapped"); if (!CircleExists) - ui->BBox->setCurrentText("unmapped"); + ui->CircleButton->setText("unmapped"); if (!SquareExists) - ui->XBox->setCurrentText("unmapped"); + ui->SquareButton->setText("unmapped"); if (!TriangleExists) - ui->YBox->setCurrentText("unmapped"); + ui->TriangleButton->setText("unmapped"); if (!L1Exists) - ui->LBBox->setCurrentText("unmapped"); + ui->L1Button->setText("unmapped"); if (!L2Exists) - ui->LTBox->setCurrentText("unmapped"); + ui->L2Button->setText("unmapped"); if (!L3Exists) - ui->LClickBox->setCurrentText("unmapped"); + ui->L3Button->setText("unmapped"); if (!R1Exists) - ui->RBBox->setCurrentText("unmapped"); + ui->R1Button->setText("unmapped"); if (!R2Exists) - ui->RTBox->setCurrentText("unmapped"); + ui->R2Button->setText("unmapped"); if (!R3Exists) - ui->RClickBox->setCurrentText("unmapped"); + ui->R3Button->setText("unmapped"); if (!DPadUpExists) - ui->DpadUpBox->setCurrentText("unmapped"); + ui->DpadUpButton->setText("unmapped"); if (!DPadDownExists) - ui->DpadDownBox->setCurrentText("unmapped"); + ui->DpadDownButton->setText("unmapped"); if (!DPadLeftExists) - ui->DpadLeftBox->setCurrentText("unmapped"); + ui->DpadLeftButton->setText("unmapped"); if (!DPadRightExists) - ui->DpadRightBox->setCurrentText("unmapped"); - if (!BackExists) - ui->BackBox->setCurrentText("unmapped"); - if (!StartExists) - ui->StartBox->setCurrentText("unmapped"); + ui->DpadRightButton->setText("unmapped"); + if (!TouchpadLeftExists) + ui->TouchpadLeftButton->setText("unmapped"); + if (!TouchpadCenterExists) + ui->TouchpadCenterButton->setText("unmapped"); + if (!TouchpadRightExists) + ui->TouchpadRightButton->setText("unmapped"); + if (!OptionsExists) + ui->OptionsButton->setText("unmapped"); if (!LStickXExists) { - ui->LStickRightBox->setCurrentText("unmapped"); - ui->LStickLeftBox->setCurrentText("unmapped"); + ui->LStickRightButton->setText("unmapped"); + ui->LStickLeftButton->setText("unmapped"); } if (!LStickYExists) { - ui->LStickUpBox->setCurrentText("unmapped"); - ui->LStickDownBox->setCurrentText("unmapped"); + ui->LStickUpButton->setText("unmapped"); + ui->LStickDownButton->setText("unmapped"); } if (!RStickXExists) { - ui->RStickRightBox->setCurrentText("unmapped"); - ui->RStickLeftBox->setCurrentText("unmapped"); + ui->RStickRightButton->setText("unmapped"); + ui->RStickLeftButton->setText("unmapped"); } if (!RStickYExists) { - ui->RStickUpBox->setCurrentText("unmapped"); - ui->RStickDownBox->setCurrentText("unmapped"); + ui->RStickUpButton->setText("unmapped"); + ui->RStickDownButton->setText("unmapped"); } } @@ -589,4 +639,305 @@ void ControlSettings::UpdateLightbarColor() { ui->LightbarColorFrame->setStyleSheet(colorstring); } +void ControlSettings::CheckGamePad() { + if (GameRunning) + return; + + if (gamepad) { + SDL_CloseGamepad(gamepad); + 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); + gamepad = SDL_OpenGamepad(gamepads[0]); + + if (!gamepad) { + LOG_ERROR(Input, "Failed to open gamepad 0: {}", SDL_GetError()); + SDL_free(gamepads); + return; + } + + SDL_free(gamepads); +} + +void ControlSettings::DisableMappingButtons() { + for (const auto& i : ButtonsList) { + i->setEnabled(false); + } + + for (const auto& i : AxisList) { + i->setEnabled(false); + } +} + +void ControlSettings::EnableMappingButtons() { + for (const auto& i : ButtonsList) { + i->setEnabled(true); + } + + for (const auto& i : AxisList) { + i->setEnabled(true); + } +} + +void ControlSettings::ConnectAxisInputs(QPushButton*& button) { + QString input = button->text(); + if (button == ui->LStickUpButton) { + ui->LStickDownButton->setText(input); + } else if (button == ui->LStickDownButton) { + ui->LStickUpButton->setText(input); + } else if (button == ui->LStickLeftButton) { + ui->LStickRightButton->setText(input); + } else if (button == ui->LStickRightButton) { + ui->LStickLeftButton->setText(input); + } else if (button == ui->RStickUpButton) { + ui->RStickDownButton->setText(input); + } else if (button == ui->RStickDownButton) { + ui->RStickUpButton->setText(input); + } else if (button == ui->RStickLeftButton) { + ui->RStickRightButton->setText(input); + } else if (button == ui->RStickRightButton) { + ui->RStickLeftButton->setText(input); + } +} + +void ControlSettings::StartTimer(QPushButton*& button, bool isButton) { + MappingTimer = 3; + isButton ? EnableButtonMapping = true : EnableAxisMapping = true; + MappingCompleted = false; + mapping = button->text(); + DisableMappingButtons(); + + EnableButtonMapping + ? button->setText(tr("Press a button") + " [" + QString::number(MappingTimer) + "]") + : button->setText(tr("Move analog stick") + " [" + QString::number(MappingTimer) + "]"); + + timer = new QTimer(this); + MappingButton = button; + timer->start(1000); + connect(timer, &QTimer::timeout, this, [this]() { CheckMapping(MappingButton); }); +} + +void ControlSettings::CheckMapping(QPushButton*& button) { + MappingTimer -= 1; + EnableButtonMapping + ? button->setText(tr("Press a button") + " [" + QString::number(MappingTimer) + "]") + : button->setText(tr("Move analog stick") + " [" + QString::number(MappingTimer) + "]"); + + if (pressedButtons.size() > 0) { + QStringList keyStrings; + + for (const QString& buttonAction : pressedButtons) { + keyStrings << buttonAction; + } + + QString combo = keyStrings.join(","); + SetMapping(combo); + MappingButton->setText(combo); + pressedButtons.clear(); + } + + if (MappingCompleted || MappingTimer <= 0) { + button->setText(mapping); + EnableButtonMapping = false; + EnableAxisMapping = false; + L2Pressed = false; + R2Pressed = false; + EnableMappingButtons(); + timer->stop(); + } +} + +void ControlSettings::SetMapping(QString input) { + mapping = input; + MappingCompleted = true; + if (EnableAxisMapping) { + emit PushGamepadEvent(); + emit AxisChanged(); + } +} + +// use QT events instead of SDL to override default event closing the window with escape +bool ControlSettings::eventFilter(QObject* obj, QEvent* event) { + if (event->type() == QEvent::KeyPress && EnableButtonMapping) { + QKeyEvent* keyEvent = static_cast(event); + if (keyEvent->key() == Qt::Key_Escape) { + SetMapping("unmapped"); + return true; + } + } + return QDialog::eventFilter(obj, event); +} + +void ControlSettings::processSDLEvents(int Type, int Input, int Value) { + if (EnableButtonMapping) { + if (Type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) { + switch (Input) { + case SDL_GAMEPAD_BUTTON_SOUTH: + pressedButtons.insert("cross"); + break; + case SDL_GAMEPAD_BUTTON_EAST: + pressedButtons.insert("circle"); + break; + case SDL_GAMEPAD_BUTTON_NORTH: + pressedButtons.insert("triangle"); + break; + case SDL_GAMEPAD_BUTTON_WEST: + pressedButtons.insert("square"); + break; + case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER: + pressedButtons.insert("l1"); + break; + case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER: + pressedButtons.insert("r1"); + break; + case SDL_GAMEPAD_BUTTON_LEFT_STICK: + pressedButtons.insert("l3"); + break; + case SDL_GAMEPAD_BUTTON_RIGHT_STICK: + pressedButtons.insert("r3"); + break; + case SDL_GAMEPAD_BUTTON_DPAD_UP: + pressedButtons.insert("pad_up"); + break; + case SDL_GAMEPAD_BUTTON_DPAD_DOWN: + pressedButtons.insert("pad_down"); + break; + case SDL_GAMEPAD_BUTTON_DPAD_LEFT: + pressedButtons.insert("pad_left"); + break; + case SDL_GAMEPAD_BUTTON_DPAD_RIGHT: + pressedButtons.insert("pad_right"); + break; + case SDL_GAMEPAD_BUTTON_BACK: + pressedButtons.insert("back"); + break; + case SDL_GAMEPAD_BUTTON_LEFT_PADDLE1: + pressedButtons.insert("lpaddle_high"); + break; + case SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1: + pressedButtons.insert("rpaddle_high"); + break; + case SDL_GAMEPAD_BUTTON_LEFT_PADDLE2: + pressedButtons.insert("lpaddle_low"); + break; + case SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2: + pressedButtons.insert("rpaddle_low"); + break; + case SDL_GAMEPAD_BUTTON_START: + pressedButtons.insert("options"); + break; + default: + break; + } + } + + if (Type == SDL_EVENT_GAMEPAD_AXIS_MOTION) { + // SDL trigger axis values range from 0 to 32000, set mapping on half movement + // Set zone for trigger release signal arbitrarily at 5000 + switch (Input) { + case SDL_GAMEPAD_AXIS_LEFT_TRIGGER: + if (Value > 16000) { + pressedButtons.insert("l2"); + L2Pressed = true; + } else if (Value < 5000) { + if (L2Pressed) + emit PushGamepadEvent(); + } + break; + case SDL_GAMEPAD_AXIS_RIGHT_TRIGGER: + if (Value > 16000) { + pressedButtons.insert("r2"); + R2Pressed = true; + } else if (Value < 5000) { + if (R2Pressed) + emit PushGamepadEvent(); + } + break; + default: + break; + } + } + + if (Type == SDL_EVENT_GAMEPAD_BUTTON_UP) + emit PushGamepadEvent(); + + } else if (EnableAxisMapping) { + if (Type == SDL_EVENT_GAMEPAD_AXIS_MOTION) { + // SDL stick axis values range from -32000 to 32000, set mapping on half movement + if (Value > 16000 || Value < -16000) { + switch (Input) { + case SDL_GAMEPAD_AXIS_LEFTX: + SetMapping("axis_left_x"); + break; + case SDL_GAMEPAD_AXIS_LEFTY: + SetMapping("axis_left_y"); + break; + case SDL_GAMEPAD_AXIS_RIGHTX: + SetMapping("axis_right_x"); + break; + case SDL_GAMEPAD_AXIS_RIGHTY: + SetMapping("axis_right_y"); + break; + default: + break; + } + } + } + } +} + +void ControlSettings::pollSDLEvents() { + SDL_Event event; + while (SdlEventWrapper::Wrapper::wrapperActive) { + + if (!SDL_WaitEvent(&event)) { + return; + } + + if (event.type == SDL_EVENT_QUIT) { + return; + } + + if (event.type == SDL_EVENT_GAMEPAD_ADDED) { + CheckGamePad(); + } + + SdlEventWrapper::Wrapper::GetInstance()->Wrapper::ProcessEvent(&event); + } +} + +void ControlSettings::Cleanup() { + SdlEventWrapper::Wrapper::wrapperActive = false; + if (gamepad) + SDL_CloseGamepad(gamepad); + + if (!GameRunning) { + SDL_Event quitLoop{}; + quitLoop.type = SDL_EVENT_QUIT; + SDL_PushEvent(&quitLoop); + Polling.waitForFinished(); + + SDL_QuitSubSystem(SDL_INIT_GAMEPAD); + SDL_QuitSubSystem(SDL_INIT_EVENTS); + SDL_Quit(); + } else { + SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "0"); + } +} + ControlSettings::~ControlSettings() {} diff --git a/src/qt_gui/control_settings.h b/src/qt_gui/control_settings.h index b1fff1dad..76d16b84e 100644 --- a/src/qt_gui/control_settings.h +++ b/src/qt_gui/control_settings.h @@ -2,7 +2,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include +#include #include "game_info.h" +#include "sdl_event_wrapper.h" namespace Ui { class ControlSettings; @@ -11,22 +14,56 @@ class ControlSettings; class ControlSettings : public QDialog { Q_OBJECT public: - explicit ControlSettings(std::shared_ptr game_info_get, - QWidget* parent = nullptr); + explicit ControlSettings(std::shared_ptr game_info_get, bool GameRunning, + std::string GameRunningSerial, QWidget* parent = nullptr); ~ControlSettings(); +signals: + void PushGamepadEvent(); + void AxisChanged(); + private Q_SLOTS: void SaveControllerConfig(bool CloseOnSave); void SetDefault(); void UpdateLightbarColor(); + void CheckMapping(QPushButton*& button); + void StartTimer(QPushButton*& button, bool isButton); + void ConnectAxisInputs(QPushButton*& button); private: std::unique_ptr ui; std::shared_ptr m_game_info; + bool eventFilter(QObject* obj, QEvent* event) override; void AddBoxItems(); void SetUIValuestoMappings(); void GetGameTitle(); + void CheckGamePad(); + void processSDLEvents(int Type, int Input, int Value); + void pollSDLEvents(); + void SetMapping(QString input); + void DisableMappingButtons(); + void EnableMappingButtons(); + void Cleanup(); + + QList ButtonsList; + QList AxisList; + QSet pressedButtons; + + std::string RunningGameSerial; + bool GameRunning; + bool L2Pressed = false; + bool R2Pressed = false; + bool EnableButtonMapping = false; + bool EnableAxisMapping = false; + bool MappingCompleted = false; + QString mapping; + int MappingTimer; + QTimer* timer; + QPushButton* MappingButton; + SDL_Gamepad* gamepad = nullptr; + SdlEventWrapper::Wrapper* RemapWrapper; + QFuture Polling; const std::vector ControllerInputs = { "cross", "circle", "square", "triangle", "l1", @@ -39,29 +76,8 @@ private: "pad_left", "pad_right", "axis_left_x", "axis_left_y", "axis_right_x", "axis_right_y", "back"}; - const QStringList ButtonOutputs = {"cross", - "circle", - "square", - "triangle", - "l1", - "r1", - "l2", - "r2", - "l3", - - "r3", - "options", - "pad_up", - - "pad_down", - - "pad_left", - "pad_right", - "touchpad_left", - "touchpad_center", - "touchpad_right", - "unmapped"}; - - const QStringList StickOutputs = {"axis_left_x", "axis_left_y", "axis_right_x", "axis_right_y", - "unmapped"}; +protected: + void closeEvent(QCloseEvent* event) override { + Cleanup(); + } }; diff --git a/src/qt_gui/control_settings.ui b/src/qt_gui/control_settings.ui index 41fb005c6..2eb4c754c 100644 --- a/src/qt_gui/control_settings.ui +++ b/src/qt_gui/control_settings.ui @@ -11,8 +11,8 @@ 0 0 - 1043 - 792 + 1114 + 794 @@ -33,8 +33,8 @@ 0 0 - 1019 - 732 + 1094 + 744 @@ -42,8 +42,8 @@ 0 0 - 1021 - 731 + 1091 + 741 @@ -110,7 +110,7 @@ - 124 + 152 0 @@ -125,12 +125,9 @@ - - - false - - - QComboBox::SizeAdjustPolicy::AdjustToContents + + + unmapped @@ -161,7 +158,11 @@ 5 - + + + unmapped + + @@ -185,9 +186,9 @@ 5 - - - false + + + unmapped @@ -213,6 +214,12 @@ + + + 152 + 0 + + 124 @@ -224,21 +231,9 @@ - - - true - - - - 0 - 0 - - - - - 0 - 0 - + + + unmapped @@ -378,7 +373,7 @@ - 124 + 152 16777215 @@ -387,9 +382,9 @@ - - - true + + + unmapped @@ -420,9 +415,9 @@ 5 - - - true + + + unmapped @@ -454,9 +449,9 @@ 5 - - - true + + + unmapped @@ -484,7 +479,7 @@ - 124 + 152 0 @@ -499,15 +494,9 @@ - - - true - - - false - - - false + + + unmapped @@ -617,149 +606,190 @@ 0 - - - - - - L1 / LB - - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - - - - - - L2 / LT - - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - - - - - - - - Qt::Orientation::Vertical - - - QSizePolicy::Policy::Preferred - - - - 20 - 40 - - - - - - 10 - - - - Back - - - - - - - + + + + + + 0 + 0 + + + + L1 + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + unmapped + + + + + + + + + + Qt::Orientation::Horizontal + + + QSizePolicy::Policy::Fixed + + + + 133 + 20 + + + + + + + + + 0 + 0 + + + + R1 + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + unmapped + + + + + + + + + + + + + + L2 + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + unmapped + + + + + + + + + + Options + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + unmapped + + + + + + + + + + R2 + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + unmapped + + + + + + + - - - - - - R1 / RB - - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - - - - - - R2 / RT - - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - - - - @@ -806,7 +836,7 @@ - + 10 @@ -814,76 +844,144 @@ QLayout::SizeConstraint::SetDefaultConstraint - - - L3 - - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - + + + + + + 0 + 0 + + + + L3 + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + unmapped + + + + + + + + + + Qt::Orientation::Horizontal + + + QSizePolicy::Policy::Fixed + + + + 133 + 20 + + + + + + + + + 0 + 0 + + + + R3 + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + unmapped + + + + + + + - - - Options / Start - - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - - - - - - R3 - - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - + + + + + Touchpad Left + + + + + + unmapped + + + + + + + + + + Touchpad Center + + + + + + unmapped + + + + + + + + + + Touchpad Right + + + + + + unmapped + + + + + + + @@ -1104,7 +1202,7 @@ - 124 + 152 0 @@ -1115,19 +1213,13 @@ - Triangle / Y + Triangle - - - true - - - - 0 - 0 - + + + unmapped @@ -1142,7 +1234,7 @@ - Square / X + Square @@ -1158,7 +1250,11 @@ 5 - + + + unmapped + + @@ -1166,7 +1262,7 @@ - Circle / B + Circle @@ -1182,7 +1278,11 @@ 5 - + + + unmapped + + @@ -1208,7 +1308,7 @@ - 124 + 152 0 @@ -1219,11 +1319,15 @@ - Cross / A + Cross - + + + unmapped + + @@ -1361,7 +1465,7 @@ - 124 + 152 1231321 @@ -1370,9 +1474,9 @@ - - - true + + + unmapped @@ -1403,9 +1507,9 @@ 5 - - - true + + + unmapped @@ -1431,7 +1535,11 @@ 5 - + + + unmapped + + @@ -1457,7 +1565,7 @@ - 124 + 152 0 @@ -1472,9 +1580,9 @@ - - - true + + + unmapped diff --git a/src/qt_gui/kbm_gui.cpp b/src/qt_gui/kbm_gui.cpp index 3f41e1f08..ab1a7b845 100644 --- a/src/qt_gui/kbm_gui.cpp +++ b/src/qt_gui/kbm_gui.cpp @@ -7,16 +7,20 @@ #include #include #include +#include #include "common/path_util.h" +#include "input/input_handler.h" #include "kbm_config_dialog.h" #include "kbm_gui.h" #include "kbm_help_dialog.h" #include "ui_kbm_gui.h" HelpDialog* HelpWindow; -KBMSettings::KBMSettings(std::shared_ptr game_info_get, QWidget* parent) - : QDialog(parent), m_game_info(game_info_get), ui(new Ui::KBMSettings) { +KBMSettings::KBMSettings(std::shared_ptr game_info_get, bool isGameRunning, + std::string GameRunningSerial, QWidget* parent) + : QDialog(parent), m_game_info(game_info_get), GameRunning(isGameRunning), + RunningGameSerial(GameRunningSerial), ui(new Ui::KBMSettings) { ui->setupUi(this); ui->PerGameCheckBox->setChecked(!Config::GetUseUnifiedInputConfig()); @@ -271,9 +275,17 @@ void KBMSettings::SaveKBMConfig(bool close_on_save) { output_string = line.substr(0, equal_pos - 1); input_string = line.substr(equal_pos + 2); - if (std::find(ControllerInputs.begin(), ControllerInputs.end(), input_string) != - ControllerInputs.end() || - output_string == "analog_deadzone" || output_string == "override_controller_color") { + bool controllerInputdetected = false; + for (std::string input : ControllerInputs) { + // Needed to avoid detecting backspace while detecting back + if (input_string.contains(input) && !input_string.contains("backspace")) { + controllerInputdetected = true; + break; + } + } + + if (controllerInputdetected || output_string == "analog_deadzone" || + output_string == "override_controller_color") { lines.push_back(line); } } @@ -324,6 +336,11 @@ QString(tr("Cannot bind any unique input more than once. Duplicate inputs mapped Config::SetUseUnifiedInputConfig(!ui->PerGameCheckBox->isChecked()); Config::save(Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "config.toml"); + if (GameRunning) { + Config::GetUseUnifiedInputConfig() ? Input::ParseInputConfig("default") + : Input::ParseInputConfig(RunningGameSerial); + } + if (close_on_save) QWidget::close(); } @@ -390,8 +407,16 @@ void KBMSettings::SetUIValuestoMappings(std::string config_id) { std::string output_string = line.substr(0, equal_pos - 1); std::string input_string = line.substr(equal_pos + 2); - if (std::find(ControllerInputs.begin(), ControllerInputs.end(), input_string) == - ControllerInputs.end()) { + bool controllerInputdetected = false; + for (std::string input : ControllerInputs) { + // Needed to avoid detecting backspace while detecting back + if (input_string.contains(input) && !input_string.contains("backspace")) { + controllerInputdetected = true; + break; + } + } + + if (!controllerInputdetected) { if (output_string == "cross") { ui->CrossButton->setText(QString::fromStdString(input_string)); } else if (output_string == "circle") { @@ -1000,7 +1025,6 @@ bool KBMSettings::eventFilter(QObject* obj, QEvent* event) { if (event->type() == QEvent::KeyRelease || event->type() == QEvent::MouseButtonRelease) emit PushKBMEvent(); } - return QDialog::eventFilter(obj, event); } diff --git a/src/qt_gui/kbm_gui.h b/src/qt_gui/kbm_gui.h index b14b506bd..1161fc30d 100644 --- a/src/qt_gui/kbm_gui.h +++ b/src/qt_gui/kbm_gui.h @@ -23,7 +23,8 @@ class KBMSettings; class KBMSettings : public QDialog { Q_OBJECT public: - explicit KBMSettings(std::shared_ptr game_info_get, QWidget* parent = nullptr); + explicit KBMSettings(std::shared_ptr game_info_get, bool GameRunning, + std::string GameRunningSerial, QWidget* parent = nullptr); ~KBMSettings(); signals: @@ -47,8 +48,11 @@ private: void DisableMappingButtons(); void EnableMappingButtons(); void SetMapping(QString input); + void Cleanup(); + std::string RunningGameSerial; QSet pressedKeys; + bool GameRunning; bool EnableMapping = false; bool MappingCompleted = false; bool HelpWindowOpen = false; diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 166a31d72..f561bf392 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -473,12 +473,13 @@ void MainWindow::CreateConnects() { }); connect(ui->controllerButton, &QPushButton::clicked, this, [this]() { - auto configWindow = new ControlSettings(m_game_info, this); - configWindow->exec(); + ControlSettings* remapWindow = + new ControlSettings(m_game_info, isGameRunning, runningGameSerial, this); + remapWindow->exec(); }); connect(ui->keyboardButton, &QPushButton::clicked, this, [this]() { - auto kbmWindow = new KBMSettings(m_game_info, this); + auto kbmWindow = new KBMSettings(m_game_info, isGameRunning, runningGameSerial, this); kbmWindow->exec(); }); @@ -846,12 +847,14 @@ void MainWindow::StartGame() { if (m_game_list_frame->currentItem()) { int itemID = m_game_list_frame->currentItem()->row(); Common::FS::PathToQString(gamePath, m_game_info->m_games[itemID].path / "eboot.bin"); + runningGameSerial = m_game_info->m_games[itemID].serial; } } else if (table_mode == 1) { if (m_game_grid_frame->cellClicked) { int itemID = (m_game_grid_frame->crtRow * m_game_grid_frame->columnCnt) + m_game_grid_frame->crtColumn; Common::FS::PathToQString(gamePath, m_game_info->m_games[itemID].path / "eboot.bin"); + runningGameSerial = m_game_info->m_games[itemID].serial; } } else { if (m_elf_viewer->currentItem()) { diff --git a/src/qt_gui/main_window.h b/src/qt_gui/main_window.h index eec1a65de..5b880c15e 100644 --- a/src/qt_gui/main_window.h +++ b/src/qt_gui/main_window.h @@ -75,11 +75,13 @@ private: void PlayBackgroundMusic(); QIcon RecolorIcon(const QIcon& icon, bool isWhite); void StartEmulator(std::filesystem::path); + bool isIconBlack = false; bool isTableList = true; bool isGameRunning = false; bool isWhite = false; bool is_paused = false; + std::string runningGameSerial = ""; QActionGroup* m_icon_size_act_group = nullptr; QActionGroup* m_list_mode_act_group = nullptr; diff --git a/src/qt_gui/sdl_event_wrapper.cpp b/src/qt_gui/sdl_event_wrapper.cpp new file mode 100644 index 000000000..608acbbc5 --- /dev/null +++ b/src/qt_gui/sdl_event_wrapper.cpp @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "sdl_event_wrapper.h" + +using namespace SdlEventWrapper; + +Wrapper* Wrapper::WrapperInstance = nullptr; +bool Wrapper::wrapperActive = false; + +Wrapper::Wrapper(QObject* parent) : QObject(parent) {} + +Wrapper* Wrapper::GetInstance() { + if (WrapperInstance == nullptr) { + WrapperInstance = new Wrapper(); + } + return WrapperInstance; +} + +bool Wrapper::ProcessEvent(SDL_Event* event) { + switch (event->type) { + case SDL_EVENT_WINDOW_RESTORED: + return false; + case SDL_EVENT_WINDOW_EXPOSED: + return false; + case SDL_EVENT_GAMEPAD_ADDED: + return false; + case SDL_EVENT_GAMEPAD_REMOVED: + return false; + case SDL_EVENT_QUIT: + emit SDLEvent(SDL_EVENT_QUIT, 0, 0); + return true; + case SDL_EVENT_GAMEPAD_BUTTON_DOWN: + emit SDLEvent(SDL_EVENT_GAMEPAD_BUTTON_DOWN, event->gbutton.button, 0); + return true; + case SDL_EVENT_GAMEPAD_BUTTON_UP: + emit SDLEvent(SDL_EVENT_GAMEPAD_BUTTON_UP, event->gbutton.button, 0); + return true; + case SDL_EVENT_GAMEPAD_AXIS_MOTION: + emit SDLEvent(SDL_EVENT_GAMEPAD_AXIS_MOTION, event->gaxis.axis, event->gaxis.value); + return true; + // block all other SDL events while wrapper is active + default: + return true; + } +} +Wrapper::~Wrapper() {} diff --git a/src/qt_gui/sdl_event_wrapper.h b/src/qt_gui/sdl_event_wrapper.h new file mode 100644 index 000000000..54d8c9cd1 --- /dev/null +++ b/src/qt_gui/sdl_event_wrapper.h @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once +#include +#include + +namespace SdlEventWrapper { + +class Wrapper : public QObject { + Q_OBJECT + +public: + explicit Wrapper(QObject* parent = nullptr); + ~Wrapper(); + bool ProcessEvent(SDL_Event* event); + static Wrapper* GetInstance(); + static bool wrapperActive; + static Wrapper* WrapperInstance; + +signals: + void SDLEvent(int Type, int Input, int Value); +}; + +} // namespace SdlEventWrapper diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index 735f14639..69819a00f 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -20,6 +20,10 @@ #include "sdl_window.h" #include "video_core/renderdoc.h" +#ifdef ENABLE_QT_GUI +#include "qt_gui/sdl_event_wrapper.h" +#endif + #ifdef __APPLE__ #include "SDL3/SDL_metal.h" #endif @@ -340,6 +344,13 @@ void WindowSDL::WaitEvent() { return; } +#ifdef ENABLE_QT_GUI + if (SdlEventWrapper::Wrapper::wrapperActive) { + if (SdlEventWrapper::Wrapper::GetInstance()->ProcessEvent(&event)) + return; + } +#endif + if (ImGui::Core::ProcessEvent(&event)) { return; } From 83056712e0902ff764f8ffcead0ef8a8bd4ca2c8 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Sat, 28 Jun 2025 09:39:59 +0300 Subject: [PATCH 09/13] [ci skip] Qt GUI: Update Translation. (#3164) Co-authored-by: georgemoralis <4313123+georgemoralis@users.noreply.github.com> --- src/qt_gui/translations/en_US.ts | 106 +++++++++++++++++++------------ 1 file changed, 66 insertions(+), 40 deletions(-) diff --git a/src/qt_gui/translations/en_US.ts b/src/qt_gui/translations/en_US.ts index 14394ea72..c2e35f4ee 100644 --- a/src/qt_gui/translations/en_US.ts +++ b/src/qt_gui/translations/en_US.ts @@ -453,34 +453,10 @@ Use per-game configs - - L1 / LB - - - - L2 / LT - - - - Back - - - - R1 / RB - - - - R2 / RT - - L3 - - Options / Start - - R3 @@ -489,22 +465,6 @@ Face Buttons - - Triangle / Y - - - - Square / X - - - - Circle / B - - - - Cross / A - - Right Stick Deadzone (def:2, max:127) @@ -565,6 +525,72 @@ Cancel Cancel + + unmapped + + + + L1 + + + + R1 + + + + L2 + + + + Options + + + + R2 + + + + Touchpad Left + Touchpad Left + + + Touchpad Center + Touchpad Center + + + Touchpad Right + Touchpad Right + + + Triangle + + + + Square + + + + Circle + + + + Cross + + + + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + + + + Press a button + + + + Move analog stick + + EditorDialog From 59dd73492ba5e92ad5b0a3e8abcafc9fbe982f11 Mon Sep 17 00:00:00 2001 From: TheTurtle Date: Sun, 29 Jun 2025 00:53:14 +0300 Subject: [PATCH 10/13] Avoid clearing depth on partial HTILE writes (#3167) * vk_rasterizer: Avoid full depth clear in case of partial HTILE update * resource_tracking: Mark image as written when its used with atomics --- src/shader_recompiler/info.h | 1 + .../ir/passes/resource_tracking_pass.cpp | 11 ++++-- .../renderer_vulkan/vk_rasterizer.cpp | 3 +- src/video_core/texture_cache/image.h | 7 ++-- .../texture_cache/texture_cache.cpp | 36 ++++++++----------- 5 files changed, 28 insertions(+), 30 deletions(-) diff --git a/src/shader_recompiler/info.h b/src/shader_recompiler/info.h index b2b03bbbf..eb56f28f6 100644 --- a/src/shader_recompiler/info.h +++ b/src/shader_recompiler/info.h @@ -58,6 +58,7 @@ struct BufferResource { BufferType buffer_type; u8 instance_attrib{}; bool is_written{}; + bool is_read{}; bool is_formatted{}; bool IsSpecial() 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 f758d8e7b..40282cfcb 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -197,6 +197,7 @@ public: auto& buffer = buffer_resources[index]; buffer.used_types |= desc.used_types; buffer.is_written |= desc.is_written; + buffer.is_read |= desc.is_read; buffer.is_formatted |= desc.is_formatted; return index; } @@ -367,6 +368,7 @@ s32 TryHandleInlineCbuf(IR::Inst& inst, Info& info, Descriptors& descriptors, .used_types = BufferDataType(inst, cbuf.GetNumberFmt()), .inline_cbuf = cbuf, .buffer_type = BufferType::Guest, + .is_read = true, }); } @@ -378,11 +380,13 @@ void PatchBufferSharp(IR::Block& block, IR::Inst& inst, Info& info, Descriptors& IR::Inst* producer = handle->Arg(0).InstRecursive(); SharpLocation sharp; std::tie(sharp, buffer) = TrackSharp(producer, info); + const bool is_written = IsBufferStore(inst); binding = descriptors.Add(BufferResource{ .sharp_idx = sharp, .used_types = BufferDataType(inst, buffer.GetNumberFmt()), .buffer_type = BufferType::Guest, - .is_written = IsBufferStore(inst), + .is_written = is_written, + .is_read = !is_written, .is_formatted = inst.GetOpcode() == IR::Opcode::LoadBufferFormatF32 || inst.GetOpcode() == IR::Opcode::StoreBufferFormatF32, }); @@ -409,11 +413,12 @@ void PatchImageSharp(IR::Block& block, IR::Inst& inst, Info& info, Descriptors& // Read image sharp. const auto tsharp = TrackSharp(tsharp_handle, info); const auto inst_info = inst.Flags(); - const bool is_written = inst.GetOpcode() == IR::Opcode::ImageWrite; + const bool is_atomic = IsImageAtomicInstruction(inst); + const bool is_written = inst.GetOpcode() == IR::Opcode::ImageWrite || is_atomic; const ImageResource image_res = { .sharp_idx = tsharp, .is_depth = bool(inst_info.is_depth), - .is_atomic = IsImageAtomicInstruction(inst), + .is_atomic = is_atomic, .is_array = bool(inst_info.is_array), .is_written = is_written, .is_r128 = bool(inst_info.is_r128), diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index fbeaaf9dc..86adfcaa5 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -511,7 +511,8 @@ bool Rasterizer::IsComputeMetaClear(const Pipeline* pipeline) { // will need its full emulation anyways. for (const auto& desc : info.buffers) { const VAddr address = desc.GetSharp(info).base_address; - if (!desc.IsSpecial() && desc.is_written && texture_cache.ClearMeta(address)) { + if (!desc.IsSpecial() && desc.is_written && !desc.is_read && + texture_cache.ClearMeta(address)) { // Assume all slices were updates LOG_TRACE(Render_Vulkan, "Metadata update skipped"); return true; diff --git a/src/video_core/texture_cache/image.h b/src/video_core/texture_cache/image.h index 31b67e021..2dbaff053 100644 --- a/src/video_core/texture_cache/image.h +++ b/src/video_core/texture_cache/image.h @@ -27,10 +27,9 @@ enum ImageFlagBits : u32 { 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 = MaybeCpuDirty | CpuDirty | GpuDirty, - GpuModified = 1 << 3, ///< Contents have been modified from the GPU - 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 + GpuModified = 1 << 3, ///< Contents have been modified from the GPU + Registered = 1 << 6, ///< True when the image is registered + Picked = 1 << 7, ///< Temporary flag to mark the image as picked }; DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits) diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index a50601af6..41c776bfd 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -451,20 +451,16 @@ ImageView& TextureCache::FindRenderTarget(BaseDesc& desc) { UpdateImage(image_id); // 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}); - image.info.meta_info.cmask_addr = desc.info.meta_info.cmask_addr; - 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}); + image.info.meta_info.cmask_addr = desc.info.meta_info.cmask_addr; + } - if (desc.info.meta_info.fmask_addr) { - 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; - } + if (desc.info.meta_info.fmask_addr) { + 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; } return RegisterImageView(image_id, desc.view_info); @@ -479,15 +475,11 @@ ImageView& TextureCache::FindDepthTarget(BaseDesc& desc) { UpdateImage(image_id); // 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, - .clear_mask = image.info.meta_info.htile_clear_mask}); - image.info.meta_info.htile_addr = desc.info.meta_info.htile_addr; - 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, + .clear_mask = image.info.meta_info.htile_clear_mask}); + image.info.meta_info.htile_addr = desc.info.meta_info.htile_addr; } // If there is a stencil attachment, link depth and stencil. From 7f727340aaca02de29d9f0b69cbcf040b164ab49 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Sun, 29 Jun 2025 11:48:44 +0300 Subject: [PATCH 11/13] New Crowdin updates (#3165) * New translations en_us.ts (Turkish) * New translations en_us.ts (Portuguese, Brazilian) * New translations en_us.ts (Arabic) * New translations en_us.ts (Persian) * New translations en_us.ts (Catalan) * New translations en_us.ts (Serbian (Latin)) * New translations en_us.ts (Swedish) * New translations en_us.ts (Romanian) * New translations en_us.ts (French) * New translations en_us.ts (Spanish) * New translations en_us.ts (Danish) * New translations en_us.ts (German) * New translations en_us.ts (Greek) * New translations en_us.ts (Finnish) * New translations en_us.ts (Hungarian) * New translations en_us.ts (Italian) * New translations en_us.ts (Japanese) * New translations en_us.ts (Korean) * New translations en_us.ts (Lithuanian) * New translations en_us.ts (Dutch) * New translations en_us.ts (Polish) * New translations en_us.ts (Portuguese) * New translations en_us.ts (Russian) * New translations en_us.ts (Slovenian) * New translations en_us.ts (Albanian) * New translations en_us.ts (Ukrainian) * New translations en_us.ts (Chinese Simplified) * New translations en_us.ts (Chinese Traditional) * New translations en_us.ts (Vietnamese) * New translations en_us.ts (Indonesian) * New translations en_us.ts (Norwegian Bokmal) * New translations en_us.ts (Russian) * New translations en_us.ts (Dutch) * New translations en_us.ts (Turkish) * New translations en_us.ts (Turkish) * New translations en_us.ts (Portuguese, Brazilian) --- src/qt_gui/translations/ar_SA.ts | 108 ++++++++++++++++++----------- src/qt_gui/translations/ca_ES.ts | 108 ++++++++++++++++++----------- src/qt_gui/translations/da_DK.ts | 108 ++++++++++++++++++----------- src/qt_gui/translations/de_DE.ts | 108 ++++++++++++++++++----------- src/qt_gui/translations/el_GR.ts | 108 ++++++++++++++++++----------- src/qt_gui/translations/es_ES.ts | 108 ++++++++++++++++++----------- src/qt_gui/translations/fa_IR.ts | 108 ++++++++++++++++++----------- src/qt_gui/translations/fi_FI.ts | 108 ++++++++++++++++++----------- src/qt_gui/translations/fr_FR.ts | 108 ++++++++++++++++++----------- src/qt_gui/translations/hu_HU.ts | 108 ++++++++++++++++++----------- src/qt_gui/translations/id_ID.ts | 108 ++++++++++++++++++----------- src/qt_gui/translations/it_IT.ts | 108 ++++++++++++++++++----------- src/qt_gui/translations/ja_JP.ts | 108 ++++++++++++++++++----------- src/qt_gui/translations/ko_KR.ts | 108 ++++++++++++++++++----------- src/qt_gui/translations/lt_LT.ts | 108 ++++++++++++++++++----------- src/qt_gui/translations/nb_NO.ts | 108 ++++++++++++++++++----------- src/qt_gui/translations/nl_NL.ts | 110 +++++++++++++++++++----------- src/qt_gui/translations/pl_PL.ts | 108 ++++++++++++++++++----------- src/qt_gui/translations/pt_BR.ts | 108 ++++++++++++++++++----------- src/qt_gui/translations/pt_PT.ts | 108 ++++++++++++++++++----------- src/qt_gui/translations/ro_RO.ts | 108 ++++++++++++++++++----------- src/qt_gui/translations/ru_RU.ts | 108 ++++++++++++++++++----------- src/qt_gui/translations/sl_SI.ts | 108 ++++++++++++++++++----------- src/qt_gui/translations/sq_AL.ts | 108 ++++++++++++++++++----------- src/qt_gui/translations/sr_CS.ts | 108 ++++++++++++++++++----------- src/qt_gui/translations/sv_SE.ts | 108 ++++++++++++++++++----------- src/qt_gui/translations/tr_TR.ts | 112 +++++++++++++++++++------------ src/qt_gui/translations/uk_UA.ts | 108 ++++++++++++++++++----------- src/qt_gui/translations/vi_VN.ts | 108 ++++++++++++++++++----------- src/qt_gui/translations/zh_CN.ts | 108 ++++++++++++++++++----------- src/qt_gui/translations/zh_TW.ts | 108 ++++++++++++++++++----------- 31 files changed, 2111 insertions(+), 1243 deletions(-) diff --git a/src/qt_gui/translations/ar_SA.ts b/src/qt_gui/translations/ar_SA.ts index 9e974a365..9f36c3b82 100644 --- a/src/qt_gui/translations/ar_SA.ts +++ b/src/qt_gui/translations/ar_SA.ts @@ -453,34 +453,10 @@ Use per-game configs استخدام إعدادات كل لُعْبَة - - L1 / LB - L1 / LB - - - L2 / LT - L2 / LT - - - Back - رجوع - - - R1 / RB - R1 / RB - - - R2 / RT - R2 / RT - L3 L3 - - Options / Start - الخيارات / البَدْء - R3 R3 @@ -489,22 +465,6 @@ Face Buttons الأزرار - - Triangle / Y - مثلث / Y - - - Square / X - مربع / X - - - Circle / B - دائرة / B - - - Cross / A - إكس / A - Right Stick Deadzone (def:2, max:127) النقطة العمياء للعصا اليمنى (الافتراضي: 2، الحد الأقصى: 127) @@ -565,6 +525,74 @@ Cancel إلغاء + + unmapped + unmapped + + + L1 + L1 + + + R1 + R1 + + + L2 + L2 + + + Options + Options + + + R2 + R2 + + + Touchpad Left + Touchpad Left + + + Touchpad Center + Touchpad Center + + + Touchpad Right + Touchpad Right + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + + + Press a button + Press a button + + + Move analog stick + Move analog stick + EditorDialog diff --git a/src/qt_gui/translations/ca_ES.ts b/src/qt_gui/translations/ca_ES.ts index 0ab2a1680..c508a7146 100644 --- a/src/qt_gui/translations/ca_ES.ts +++ b/src/qt_gui/translations/ca_ES.ts @@ -453,34 +453,10 @@ Use per-game configs Fes servir configuracions per cada joc - - L1 / LB - L1 / LB - - - L2 / LT - L2 / LT - - - Back - Torna - - - R1 / RB - R1 / RB - - - R2 / RT - R2 / RT - L3 L3 - - Options / Start - Opcions / Executa - R3 R3 @@ -489,22 +465,6 @@ Face Buttons Botons d'acció - - Triangle / Y - Triangle / Y - - - Square / X - Quadrat / X - - - Circle / B - Cercle / B - - - Cross / A - Creu / A - Right Stick Deadzone (def:2, max:127) Zona morta de la palanca dreta (per defecte:2 màxim:127) @@ -565,6 +525,74 @@ Cancel Cancel·la + + unmapped + unmapped + + + L1 + L1 + + + R1 + R1 + + + L2 + L2 + + + Options + Options + + + R2 + R2 + + + Touchpad Left + Touchpad Left + + + Touchpad Center + Touchpad Center + + + Touchpad Right + Touchpad Right + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + + + Press a button + Press a button + + + Move analog stick + Move analog stick + EditorDialog diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts index 6f32b3f52..a359bd148 100644 --- a/src/qt_gui/translations/da_DK.ts +++ b/src/qt_gui/translations/da_DK.ts @@ -453,34 +453,10 @@ Use per-game configs Use per-game configs - - L1 / LB - L1 / LB - - - L2 / LT - L2 / LT - - - Back - Back - - - R1 / RB - R1 / RB - - - R2 / RT - R2 / RT - L3 L3 - - Options / Start - Options / Start - R3 R3 @@ -489,22 +465,6 @@ Face Buttons Face Buttons - - Triangle / Y - Triangle / Y - - - Square / X - Square / X - - - Circle / B - Circle / B - - - Cross / A - Cross / A - Right Stick Deadzone (def:2, max:127) Right Stick Deadzone (def:2, max:127) @@ -565,6 +525,74 @@ Cancel Cancel + + unmapped + unmapped + + + L1 + L1 + + + R1 + R1 + + + L2 + L2 + + + Options + Options + + + R2 + R2 + + + Touchpad Left + Touchpad Left + + + Touchpad Center + Touchpad Center + + + Touchpad Right + Touchpad Right + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + + + Press a button + Press a button + + + Move analog stick + Move analog stick + EditorDialog diff --git a/src/qt_gui/translations/de_DE.ts b/src/qt_gui/translations/de_DE.ts index d34e1db20..46295c164 100644 --- a/src/qt_gui/translations/de_DE.ts +++ b/src/qt_gui/translations/de_DE.ts @@ -453,34 +453,10 @@ Use per-game configs Benutze Per-Game Einstellungen - - L1 / LB - L1 / LB - - - L2 / LT - L2 / LT - - - Back - Zurück - - - R1 / RB - R1 / RB - - - R2 / RT - R2 / RT - L3 L3 - - Options / Start - Options / Start - R3 R3 @@ -489,22 +465,6 @@ Face Buttons Aktionstasten - - Triangle / Y - Dreieck / Y - - - Square / X - Quadrat / X - - - Circle / B - Kreis / B - - - Cross / A - Kreuz / A - Right Stick Deadzone (def:2, max:127) Rechter Stick tote Zone (def:2, max:127) @@ -565,6 +525,74 @@ Cancel Abbrechen + + unmapped + unmapped + + + L1 + L1 + + + R1 + R1 + + + L2 + L2 + + + Options + Options + + + R2 + R2 + + + Touchpad Left + Touchpad Left + + + Touchpad Center + Touchpad Center + + + Touchpad Right + Touchpad Right + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + + + Press a button + Press a button + + + Move analog stick + Move analog stick + EditorDialog diff --git a/src/qt_gui/translations/el_GR.ts b/src/qt_gui/translations/el_GR.ts index a7ed888d7..824a2a970 100644 --- a/src/qt_gui/translations/el_GR.ts +++ b/src/qt_gui/translations/el_GR.ts @@ -453,34 +453,10 @@ Use per-game configs Use per-game configs - - L1 / LB - L1 / LB - - - L2 / LT - L2 / LT - - - Back - Back - - - R1 / RB - R1 / RB - - - R2 / RT - R2 / RT - L3 L3 - - Options / Start - Options / Start - R3 R3 @@ -489,22 +465,6 @@ Face Buttons Face Buttons - - Triangle / Y - Triangle / Y - - - Square / X - Square / X - - - Circle / B - Circle / B - - - Cross / A - Cross / A - Right Stick Deadzone (def:2, max:127) Right Stick Deadzone (def:2, max:127) @@ -565,6 +525,74 @@ Cancel Cancel + + unmapped + unmapped + + + L1 + L1 + + + R1 + R1 + + + L2 + L2 + + + Options + Options + + + R2 + R2 + + + Touchpad Left + Touchpad Left + + + Touchpad Center + Touchpad Center + + + Touchpad Right + Touchpad Right + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + + + Press a button + Press a button + + + Move analog stick + Move analog stick + EditorDialog diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index 33ddb69f0..a31279d37 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -453,34 +453,10 @@ Use per-game configs Usar configuraciones por juego - - L1 / LB - L1/LB - - - L2 / LT - L2/LT - - - Back - Back - - - R1 / RB - R1/RB - - - R2 / RT - R2/RT - L3 L3 - - Options / Start - Options/Start - R3 R3 @@ -489,22 +465,6 @@ Face Buttons Botones de acción - - Triangle / Y - Triángulo/Y - - - Square / X - Cuadrado/X - - - Circle / B - Círculo/B - - - Cross / A - Cruz / A - Right Stick Deadzone (def:2, max:127) Zona muerta del stick derecho (defecto: 2, máx.: 127) @@ -565,6 +525,74 @@ Cancel Cancelar + + unmapped + unmapped + + + L1 + L1 + + + R1 + R1 + + + L2 + L2 + + + Options + Options + + + R2 + R2 + + + Touchpad Left + Touchpad Left + + + Touchpad Center + Touchpad Center + + + Touchpad Right + Touchpad Right + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + + + Press a button + Press a button + + + Move analog stick + Move analog stick + EditorDialog diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index 12de2dfe3..bb91cc11b 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -453,34 +453,10 @@ Use per-game configs از پیکربندی‌های مخصوص هر بازی استفاده کنید - - L1 / LB - L1 / LB - - - L2 / LT - L2 / LT - - - Back - Back - - - R1 / RB - R1 / RB - - - R2 / RT - R2 / RT - L3 L3 - - Options / Start - Options / Start - R3 R3 @@ -489,22 +465,6 @@ Face Buttons Face Buttons - - Triangle / Y - مثلث / Y - - - Square / X - Square / X - - - Circle / B - Circle / B - - - Cross / A - Cross / A - Right Stick Deadzone (def:2, max:127) Right Stick Deadzone (def:2, max:127) @@ -565,6 +525,74 @@ Cancel Cancel + + unmapped + unmapped + + + L1 + L1 + + + R1 + R1 + + + L2 + L2 + + + Options + Options + + + R2 + R2 + + + Touchpad Left + Touchpad Left + + + Touchpad Center + Touchpad Center + + + Touchpad Right + Touchpad Right + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + + + Press a button + Press a button + + + Move analog stick + Move analog stick + EditorDialog diff --git a/src/qt_gui/translations/fi_FI.ts b/src/qt_gui/translations/fi_FI.ts index 192cbd09f..99cf2ea3e 100644 --- a/src/qt_gui/translations/fi_FI.ts +++ b/src/qt_gui/translations/fi_FI.ts @@ -453,34 +453,10 @@ Use per-game configs Käytä pelikohtaisia asetuksia - - L1 / LB - L1 / LB - - - L2 / LT - L2 / LT - - - Back - Back - - - R1 / RB - R1 / RB - - - R2 / RT - R2 / RT - L3 L3 - - Options / Start - Options / Start - R3 R3 @@ -489,22 +465,6 @@ Face Buttons Etunäppäimet - - Triangle / Y - Kolmio / Y - - - Square / X - Neliö / X - - - Circle / B - Ympyrä / B - - - Cross / A - Rasti / A - Right Stick Deadzone (def:2, max:127) Oikean Analogin Deadzone (oletus:2 max:127) @@ -565,6 +525,74 @@ Cancel Peruuta + + unmapped + unmapped + + + L1 + L1 + + + R1 + R1 + + + L2 + L2 + + + Options + Options + + + R2 + R2 + + + Touchpad Left + Touchpad Left + + + Touchpad Center + Touchpad Center + + + Touchpad Right + Touchpad Right + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + + + Press a button + Press a button + + + Move analog stick + Move analog stick + EditorDialog diff --git a/src/qt_gui/translations/fr_FR.ts b/src/qt_gui/translations/fr_FR.ts index 17a078c87..c7833737a 100644 --- a/src/qt_gui/translations/fr_FR.ts +++ b/src/qt_gui/translations/fr_FR.ts @@ -453,34 +453,10 @@ Use per-game configs Utiliser les configurations par jeu - - L1 / LB - L1 / LB - - - L2 / LT - L2 / LT - - - Back - Retour - - - R1 / RB - R1 / RB - - - R2 / RT - R2 / RT - L3 L3 - - Options / Start - Options / Start - R3 R3 @@ -489,22 +465,6 @@ Face Buttons Touches d'action - - Triangle / Y - Triangle / Y - - - Square / X - Carré / X - - - Circle / B - Rond / B - - - Cross / A - Croix / A - Right Stick Deadzone (def:2, max:127) Joystick Gauche Deadzone (def:2 max:127) @@ -565,6 +525,74 @@ Cancel Annuler + + unmapped + unmapped + + + L1 + L1 + + + R1 + R1 + + + L2 + L2 + + + Options + Options + + + R2 + R2 + + + Touchpad Left + Touchpad Left + + + Touchpad Center + Touchpad Center + + + Touchpad Right + Touchpad Right + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + + + Press a button + Press a button + + + Move analog stick + Move analog stick + EditorDialog diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index 37a4d2efb..febfb1b73 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -453,34 +453,10 @@ Use per-game configs Use per-game configs - - L1 / LB - L1 / LB - - - L2 / LT - L2 / LT - - - Back - Back - - - R1 / RB - R1 / RB - - - R2 / RT - R2 / RT - L3 L3 - - Options / Start - Options / Start - R3 R3 @@ -489,22 +465,6 @@ Face Buttons Face Buttons - - Triangle / Y - Triangle / Y - - - Square / X - Square / X - - - Circle / B - Circle / B - - - Cross / A - Cross / A - Right Stick Deadzone (def:2, max:127) Right Stick Deadzone (def:2, max:127) @@ -565,6 +525,74 @@ Cancel Cancel + + unmapped + unmapped + + + L1 + L1 + + + R1 + R1 + + + L2 + L2 + + + Options + Options + + + R2 + R2 + + + Touchpad Left + Touchpad Left + + + Touchpad Center + Touchpad Center + + + Touchpad Right + Touchpad Right + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + + + Press a button + Press a button + + + Move analog stick + Move analog stick + EditorDialog diff --git a/src/qt_gui/translations/id_ID.ts b/src/qt_gui/translations/id_ID.ts index 9aeb3934c..5f3661395 100644 --- a/src/qt_gui/translations/id_ID.ts +++ b/src/qt_gui/translations/id_ID.ts @@ -453,34 +453,10 @@ Use per-game configs Use per-game configs - - L1 / LB - L1 / LB - - - L2 / LT - L2 / LT - - - Back - Back - - - R1 / RB - R1 / RB - - - R2 / RT - R2 / RT - L3 L3 - - Options / Start - Options / Start - R3 R3 @@ -489,22 +465,6 @@ Face Buttons Face Buttons - - Triangle / Y - Triangle / Y - - - Square / X - Square / X - - - Circle / B - Circle / B - - - Cross / A - Cross / A - Right Stick Deadzone (def:2, max:127) Right Stick Deadzone (def:2, max:127) @@ -565,6 +525,74 @@ Cancel Cancel + + unmapped + unmapped + + + L1 + L1 + + + R1 + R1 + + + L2 + L2 + + + Options + Options + + + R2 + R2 + + + Touchpad Left + Touchpad Left + + + Touchpad Center + Touchpad Center + + + Touchpad Right + Touchpad Right + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + + + Press a button + Press a button + + + Move analog stick + Move analog stick + EditorDialog diff --git a/src/qt_gui/translations/it_IT.ts b/src/qt_gui/translations/it_IT.ts index d689214d6..c636d48e4 100644 --- a/src/qt_gui/translations/it_IT.ts +++ b/src/qt_gui/translations/it_IT.ts @@ -453,34 +453,10 @@ Use per-game configs Usa configurazioni per gioco - - L1 / LB - L1 / LB - - - L2 / LT - L2 / LT - - - Back - Indietro - - - R1 / RB - R1 / RB - - - R2 / RT - R2 / RT - L3 L3 - - Options / Start - Opzioni / Avvio - R3 R3 @@ -489,22 +465,6 @@ Face Buttons Pulsanti Frontali - - Triangle / Y - Triangolo / Y - - - Square / X - Quadrato / X - - - Circle / B - Cerchio / B - - - Cross / A - Croce / A - Right Stick Deadzone (def:2, max:127) Zona Morta Levetta Destra (def:2 max:127) @@ -565,6 +525,74 @@ Cancel Annulla + + unmapped + unmapped + + + L1 + L1 + + + R1 + R1 + + + L2 + L2 + + + Options + Options + + + R2 + R2 + + + Touchpad Left + Touchpad Left + + + Touchpad Center + Touchpad Center + + + Touchpad Right + Touchpad Right + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + + + Press a button + Press a button + + + Move analog stick + Move analog stick + EditorDialog diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index 3f378d3d0..ce917b991 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -453,34 +453,10 @@ Use per-game configs Use per-game configs - - L1 / LB - L1 / LB - - - L2 / LT - L2 / LT - - - Back - 戻る - - - R1 / RB - R1 / RB - - - R2 / RT - R2 / RT - L3 L3 - - Options / Start - Options / Start - R3 R3 @@ -489,22 +465,6 @@ Face Buttons Face Buttons - - Triangle / Y - 三角 / Y - - - Square / X - 四角 / X - - - Circle / B - 丸 / B - - - Cross / A - バツ / A - Right Stick Deadzone (def:2, max:127) 右スティックデッドゾーン(既定:2, 最大:127) @@ -565,6 +525,74 @@ Cancel キャンセル + + unmapped + unmapped + + + L1 + L1 + + + R1 + R1 + + + L2 + L2 + + + Options + Options + + + R2 + R2 + + + Touchpad Left + Touchpad Left + + + Touchpad Center + Touchpad Center + + + Touchpad Right + Touchpad Right + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + + + Press a button + Press a button + + + Move analog stick + Move analog stick + EditorDialog diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts index 83b27910c..827b1c881 100644 --- a/src/qt_gui/translations/ko_KR.ts +++ b/src/qt_gui/translations/ko_KR.ts @@ -453,34 +453,10 @@ Use per-game configs 게임 별 설정 사용 - - L1 / LB - L1 / LB - - - L2 / LT - L2 / LT - - - Back - 뒤로 - - - R1 / RB - R1 / RB - - - R2 / RT - R2 / RT - L3 L3 - - Options / Start - 옵션 / 시작 - R3 R3 @@ -489,22 +465,6 @@ Face Buttons Face Buttons - - Triangle / Y - Triangle / Y - - - Square / X - Square / X - - - Circle / B - Circle / B - - - Cross / A - Cross / A - Right Stick Deadzone (def:2, max:127) Right Stick Deadzone (def:2, max:127) @@ -565,6 +525,74 @@ Cancel Cancel + + unmapped + unmapped + + + L1 + L1 + + + R1 + R1 + + + L2 + L2 + + + Options + Options + + + R2 + R2 + + + Touchpad Left + Touchpad Left + + + Touchpad Center + Touchpad Center + + + Touchpad Right + Touchpad Right + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + + + Press a button + Press a button + + + Move analog stick + Move analog stick + EditorDialog diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts index 8c6496ac6..b915b9adb 100644 --- a/src/qt_gui/translations/lt_LT.ts +++ b/src/qt_gui/translations/lt_LT.ts @@ -453,34 +453,10 @@ Use per-game configs Use per-game configs - - L1 / LB - L1 / LB - - - L2 / LT - L2 / LT - - - Back - Atgal - - - R1 / RB - R1 / RB - - - R2 / RT - R2 / RT - L3 L3 - - Options / Start - Options / Start - R3 R3 @@ -489,22 +465,6 @@ Face Buttons Face Buttons - - Triangle / Y - Triangle / Y - - - Square / X - Square / X - - - Circle / B - Circle / B - - - Cross / A - Cross / A - Right Stick Deadzone (def:2, max:127) Right Stick Deadzone (def:2, max:127) @@ -565,6 +525,74 @@ Cancel Atšaukti + + unmapped + unmapped + + + L1 + L1 + + + R1 + R1 + + + L2 + L2 + + + Options + Options + + + R2 + R2 + + + Touchpad Left + Touchpad Left + + + Touchpad Center + Touchpad Center + + + Touchpad Right + Touchpad Right + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + + + Press a button + Press a button + + + Move analog stick + Move analog stick + EditorDialog diff --git a/src/qt_gui/translations/nb_NO.ts b/src/qt_gui/translations/nb_NO.ts index 53fa461bd..302229933 100644 --- a/src/qt_gui/translations/nb_NO.ts +++ b/src/qt_gui/translations/nb_NO.ts @@ -453,34 +453,10 @@ Use per-game configs Bruk oppsett per spill - - L1 / LB - L1 / LB - - - L2 / LT - L2 / LT - - - Back - Tilbake - - - R1 / RB - R1 / RB - - - R2 / RT - R2 / RT - L3 L3 - - Options / Start - Options / Start - R3 R3 @@ -489,22 +465,6 @@ Face Buttons Handlingsknapper - - Triangle / Y - Triangel / Y - - - Square / X - Firkant / X - - - Circle / B - Sirkel / B - - - Cross / A - Kryss / A - Right Stick Deadzone (def:2, max:127) Høyre analog dødsone (def:2, maks:127) @@ -565,6 +525,74 @@ Cancel Avbryt + + unmapped + unmapped + + + L1 + L1 + + + R1 + R1 + + + L2 + L2 + + + Options + Options + + + R2 + R2 + + + Touchpad Left + Touchpad Left + + + Touchpad Center + Touchpad Center + + + Touchpad Right + Touchpad Right + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + + + Press a button + Press a button + + + Move analog stick + Move analog stick + EditorDialog diff --git a/src/qt_gui/translations/nl_NL.ts b/src/qt_gui/translations/nl_NL.ts index 3fd718cb1..5a6674beb 100644 --- a/src/qt_gui/translations/nl_NL.ts +++ b/src/qt_gui/translations/nl_NL.ts @@ -453,34 +453,10 @@ Use per-game configs Use per-game configs - - L1 / LB - L1 / LB - - - L2 / LT - L2 / LT - - - Back - Back - - - R1 / RB - R1 / RB - - - R2 / RT - R2 / RT - L3 L3 - - Options / Start - Options / Start - R3 R3 @@ -489,22 +465,6 @@ Face Buttons Face Buttons - - Triangle / Y - Triangle / Y - - - Square / X - Square / X - - - Circle / B - Circle / B - - - Cross / A - Cross / A - Right Stick Deadzone (def:2, max:127) Right Stick Deadzone (def:2, max:127) @@ -565,6 +525,74 @@ Cancel Cancel + + unmapped + unmapped + + + L1 + L1 + + + R1 + R1 + + + L2 + L2 + + + Options + Options + + + R2 + R2 + + + Touchpad Left + Touchpad Left + + + Touchpad Center + Touchpad Center + + + Touchpad Right + Touchpad Right + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + + + Press a button + Press a button + + + Move analog stick + Move analog stick + EditorDialog @@ -582,7 +610,7 @@ Could not open the file for reading - + Could not open the file for reading Could not open the file for writing diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index d14dabec7..c5611c5b1 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -453,34 +453,10 @@ Use per-game configs Użyj osobnej konfiguracji dla każdej gry - - L1 / LB - L1 / LB - - - L2 / LT - L2 / LT - - - Back - Wstecz - - - R1 / RB - R1 / RB - - - R2 / RT - R2 / RT - L3 L3 - - Options / Start - Opcje / Start - R3 R3 @@ -489,22 +465,6 @@ Face Buttons Przyciski akcji - - Triangle / Y - Trójkąt / Y - - - Square / X - Kwadrat / X - - - Circle / B - Kółko / B - - - Cross / A - Krzyżyk / A - Right Stick Deadzone (def:2, max:127) Martwa strefa prawego drążka (def:2 max:127) @@ -565,6 +525,74 @@ Cancel Anuluj + + unmapped + unmapped + + + L1 + L1 + + + R1 + R1 + + + L2 + L2 + + + Options + Options + + + R2 + R2 + + + Touchpad Left + Touchpad Left + + + Touchpad Center + Touchpad Center + + + Touchpad Right + Touchpad Right + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + + + Press a button + Press a button + + + Move analog stick + Move analog stick + EditorDialog diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index 3ff06d213..c2058bf09 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -453,34 +453,10 @@ Use per-game configs Usar configurações por jogo - - L1 / LB - L1 / LB - - - L2 / LT - L2 / LT - - - Back - Voltar - - - R1 / RB - R1 / RB - - - R2 / RT - R2 / RT - L3 L3 - - Options / Start - Options / Start - R3 R3 @@ -489,22 +465,6 @@ Face Buttons Botões de Ação - - Triangle / Y - Triângulo / Y - - - Square / X - Quadrado / X - - - Circle / B - Círculo / B - - - Cross / A - Cruz / A - Right Stick Deadzone (def:2, max:127) Zona Morta do Analógico Direito (Pad: 2, Máx: 127) @@ -565,6 +525,74 @@ Cancel Cancelar + + unmapped + não mapeado + + + L1 + L1 + + + R1 + R1 + + + L2 + L2 + + + Options + Opções + + + R2 + R2 + + + Touchpad Left + Touchpad Esquerdo + + + Touchpad Center + Touchpad Center + + + Touchpad Right + Touchpad Direito + + + Triangle + Triângulo + + + Square + Quadrado + + + Circle + Circle + + + Cross + Cross + + + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + Não é possível atribuir a mesma entrada mais de uma vez. Entradas duplicadas foram atribuídas aos seguintes botões: + +%1 + + + Press a button + Pressione um botão + + + Move analog stick + Move analog stick + EditorDialog diff --git a/src/qt_gui/translations/pt_PT.ts b/src/qt_gui/translations/pt_PT.ts index a8aa10d85..53497b7b9 100644 --- a/src/qt_gui/translations/pt_PT.ts +++ b/src/qt_gui/translations/pt_PT.ts @@ -453,34 +453,10 @@ Use per-game configs Utilizar configurações por jogo - - L1 / LB - L1 / LB - - - L2 / LT - L2 / LT - - - Back - Voltar - - - R1 / RB - R1 / RB - - - R2 / RT - R2 / RT - L3 L3 - - Options / Start - Opções / Start - R3 R3 @@ -489,22 +465,6 @@ Face Buttons Botões Frontais - - Triangle / Y - Triângulo / Y - - - Square / X - Quadrado / X - - - Circle / B - Círculo / B - - - Cross / A - Cruz / A - Right Stick Deadzone (def:2, max:127) Zona Morta do Manípulo Direito (def: 2, max: 127) @@ -565,6 +525,74 @@ Cancel Cancelar + + unmapped + unmapped + + + L1 + L1 + + + R1 + R1 + + + L2 + L2 + + + Options + Options + + + R2 + R2 + + + Touchpad Left + Touchpad Left + + + Touchpad Center + Touchpad Center + + + Touchpad Right + Touchpad Right + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + + + Press a button + Press a button + + + Move analog stick + Move analog stick + EditorDialog diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index a7ca9800d..0e1647245 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -453,34 +453,10 @@ Use per-game configs Use per-game configs - - L1 / LB - L1 / LB - - - L2 / LT - L2 / LT - - - Back - Back - - - R1 / RB - R1 / RB - - - R2 / RT - R2 / RT - L3 L3 - - Options / Start - Options / Start - R3 R3 @@ -489,22 +465,6 @@ Face Buttons Face Buttons - - Triangle / Y - Triangle / Y - - - Square / X - Square / X - - - Circle / B - Circle / B - - - Cross / A - Cross / A - Right Stick Deadzone (def:2, max:127) Right Stick Deadzone (def:2, max:127) @@ -565,6 +525,74 @@ Cancel Cancel + + unmapped + unmapped + + + L1 + L1 + + + R1 + R1 + + + L2 + L2 + + + Options + Options + + + R2 + R2 + + + Touchpad Left + Touchpad Left + + + Touchpad Center + Touchpad Center + + + Touchpad Right + Touchpad Right + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + + + Press a button + Press a button + + + Move analog stick + Move analog stick + EditorDialog diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index 844508603..7293a956e 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -453,34 +453,10 @@ Use per-game configs Использовать настройки для каждой игры - - L1 / LB - L1 / LB - - - L2 / LT - L2 / LT - - - Back - Назад - - - R1 / RB - R1 / RB - - - R2 / RT - R2 / RT - L3 L3 - - Options / Start - Options / Start - R3 R3 @@ -489,22 +465,6 @@ Face Buttons Кнопки действий - - Triangle / Y - Треугольник / Y - - - Square / X - Квадрат / X - - - Circle / B - Круг / B - - - Cross / A - Крест / A - Right Stick Deadzone (def:2, max:127) Мёртвая зона правого стика (по умолч:2 макс:127) @@ -565,6 +525,74 @@ Cancel Отмена + + unmapped + не назначено + + + L1 + L1 + + + R1 + R1 + + + L2 + L2 + + + Options + Options + + + R2 + R2 + + + Touchpad Left + Тачпад слева + + + Touchpad Center + Тачпад центр + + + Touchpad Right + Тачпад справа + + + Triangle + Треугольник + + + Square + Квадрат + + + Circle + Круг + + + Cross + Крест + + + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + Невозможно привязать уникальный ввод более одного раза. Дублированные вводы назначены на следующие кнопки: + +%1 + + + Press a button + Нажмите кнопку + + + Move analog stick + Move analog stick + EditorDialog diff --git a/src/qt_gui/translations/sl_SI.ts b/src/qt_gui/translations/sl_SI.ts index 333b999aa..97d36b223 100644 --- a/src/qt_gui/translations/sl_SI.ts +++ b/src/qt_gui/translations/sl_SI.ts @@ -453,34 +453,10 @@ Use per-game configs Use per-game configs - - L1 / LB - L1 / LB - - - L2 / LT - L2 / LT - - - Back - Back - - - R1 / RB - R1 / RB - - - R2 / RT - R2 / RT - L3 L3 - - Options / Start - Options / Start - R3 R3 @@ -489,22 +465,6 @@ Face Buttons Face Buttons - - Triangle / Y - Triangle / Y - - - Square / X - Square / X - - - Circle / B - Circle / B - - - Cross / A - Cross / A - Right Stick Deadzone (def:2, max:127) Right Stick Deadzone (def:2, max:127) @@ -565,6 +525,74 @@ Cancel Cancel + + unmapped + unmapped + + + L1 + L1 + + + R1 + R1 + + + L2 + L2 + + + Options + Options + + + R2 + R2 + + + Touchpad Left + Touchpad Left + + + Touchpad Center + Touchpad Center + + + Touchpad Right + Touchpad Right + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + + + Press a button + Press a button + + + Move analog stick + Move analog stick + EditorDialog diff --git a/src/qt_gui/translations/sq_AL.ts b/src/qt_gui/translations/sq_AL.ts index 365e33f5e..71308a99f 100644 --- a/src/qt_gui/translations/sq_AL.ts +++ b/src/qt_gui/translations/sq_AL.ts @@ -453,34 +453,10 @@ Use per-game configs Përdor konfigurime të veçanta për secilën lojë - - L1 / LB - L1 / LB - - - L2 / LT - L2 / LT - - - Back - Mbrapa - - - R1 / RB - R1 / RB - - - R2 / RT - R2 / RT - L3 L3 - - Options / Start - Options / Start - R3 R3 @@ -489,22 +465,6 @@ Face Buttons Butonat kryesore - - Triangle / Y - Trekëndësh / Y - - - Square / X - Katror / X - - - Circle / B - Rreth / B - - - Cross / A - Kryq / A - Right Stick Deadzone (def:2, max:127) Zona e vdekur e levës së djathtë (def:2, max:127) @@ -565,6 +525,74 @@ Cancel Anulo + + unmapped + unmapped + + + L1 + L1 + + + R1 + R1 + + + L2 + L2 + + + Options + Options + + + R2 + R2 + + + Touchpad Left + Touchpad Left + + + Touchpad Center + Touchpad Center + + + Touchpad Right + Touchpad Right + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + + + Press a button + Press a button + + + Move analog stick + Move analog stick + EditorDialog diff --git a/src/qt_gui/translations/sr_CS.ts b/src/qt_gui/translations/sr_CS.ts index aa9fcd18d..1619d7b70 100644 --- a/src/qt_gui/translations/sr_CS.ts +++ b/src/qt_gui/translations/sr_CS.ts @@ -453,34 +453,10 @@ Use per-game configs Use per-game configs - - L1 / LB - L1 / LB - - - L2 / LT - L2 / LT - - - Back - Back - - - R1 / RB - R1 / RB - - - R2 / RT - R2 / RT - L3 L3 - - Options / Start - Options / Start - R3 R3 @@ -489,22 +465,6 @@ Face Buttons Face Buttons - - Triangle / Y - Triangle / Y - - - Square / X - Square / X - - - Circle / B - Circle / B - - - Cross / A - Cross / A - Right Stick Deadzone (def:2, max:127) Right Stick Deadzone (def:2, max:127) @@ -565,6 +525,74 @@ Cancel Cancel + + unmapped + unmapped + + + L1 + L1 + + + R1 + R1 + + + L2 + L2 + + + Options + Options + + + R2 + R2 + + + Touchpad Left + Touchpad Left + + + Touchpad Center + Touchpad Center + + + Touchpad Right + Touchpad Right + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + + + Press a button + Press a button + + + Move analog stick + Move analog stick + EditorDialog diff --git a/src/qt_gui/translations/sv_SE.ts b/src/qt_gui/translations/sv_SE.ts index 813198c42..83081bf1a 100644 --- a/src/qt_gui/translations/sv_SE.ts +++ b/src/qt_gui/translations/sv_SE.ts @@ -453,34 +453,10 @@ Use per-game configs Använd konfigurationer per spel - - L1 / LB - L1 / LB - - - L2 / LT - L2 / LT - - - Back - Bakåt - - - R1 / RB - R1 / RB - - - R2 / RT - R2 / RT - L3 L3 - - Options / Start - Options / Start - R3 R3 @@ -489,22 +465,6 @@ Face Buttons Handlingsknappar - - Triangle / Y - Triangel / Y - - - Square / X - Fyrkant / X - - - Circle / B - Cirkel / B - - - Cross / A - Kryss / A - Right Stick Deadzone (def:2, max:127) Dödläge för höger spak (standard:2, max:127) @@ -565,6 +525,74 @@ Cancel Avbryt + + unmapped + unmapped + + + L1 + L1 + + + R1 + R1 + + + L2 + L2 + + + Options + Options + + + R2 + R2 + + + Touchpad Left + Touchpad Left + + + Touchpad Center + Touchpad Center + + + Touchpad Right + Touchpad Right + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + + + Press a button + Press a button + + + Move analog stick + Move analog stick + EditorDialog diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index e58542f98..d4a94b42f 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -453,34 +453,10 @@ Use per-game configs Oyuna özel yapılandırma kullan - - L1 / LB - L1 / LB - - - L2 / LT - L2 / LT - - - Back - Geri - - - R1 / RB - R1 / RB - - - R2 / RT - R2 / RT - L3 L3 - - Options / Start - Seçenekler / Başlat - R3 R3 @@ -489,22 +465,6 @@ Face Buttons Eylem Düğmeleri - - Triangle / Y - Üçgen / Y - - - Square / X - Kare / X - - - Circle / B - Daire / B - - - Cross / A - Çarpı / A - Right Stick Deadzone (def:2, max:127) Sağ Analog Ölü Bölgesi (varsayılan: 2, en çok: 127) @@ -565,6 +525,74 @@ Cancel İptal + + unmapped + atanmamış + + + L1 + L1 + + + R1 + R1 + + + L2 + L2 + + + Options + Seçenekler + + + R2 + R2 + + + Touchpad Left + Touchpad Left + + + Touchpad Center + Touchpad Center + + + Touchpad Right + Touchpad Right + + + Triangle + Üçgen + + + Square + Kare + + + Circle + Yuvarlak + + + Cross + Çarpı + + + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + Aynı tuş birden fazla kez atanamaz. Aşağıdaki tuşlara birden fazla giriş atanmış: + +%1 + + + Press a button + Bir Düğmeye Bas + + + Move analog stick + Move analog stick + EditorDialog @@ -750,7 +778,7 @@ Favorite - Favorite + Sık Kullanılan @@ -960,7 +988,7 @@ Add to Favorites - Add to Favorites + Favorilere Ekle diff --git a/src/qt_gui/translations/uk_UA.ts b/src/qt_gui/translations/uk_UA.ts index 85442c4e6..73aa2c352 100644 --- a/src/qt_gui/translations/uk_UA.ts +++ b/src/qt_gui/translations/uk_UA.ts @@ -453,34 +453,10 @@ Use per-game configs Використовувати ігрові конфігурації - - L1 / LB - L1 / Лівий Бампер - - - L2 / LT - L2 / Лівий Тригер - - - Back - Назад - - - R1 / RB - R1 / Правий Бампер - - - R2 / RT - R2 / Правий Тригер - L3 Кнопка лівого стику - - Options / Start - Опції / Старт - R3 Кнопка правого стику @@ -489,22 +465,6 @@ Face Buttons Лицьові кнопки - - Triangle / Y - Трикутник / Y - - - Square / X - Квадрат / X - - - Circle / B - Коло / B - - - Cross / A - Хрест / A - Right Stick Deadzone (def:2, max:127) Мертва зона правого стику (за замов.: 2, максимум: 127) @@ -565,6 +525,74 @@ Cancel Відмінити + + unmapped + unmapped + + + L1 + L1 + + + R1 + R1 + + + L2 + L2 + + + Options + Options + + + R2 + R2 + + + Touchpad Left + Touchpad Left + + + Touchpad Center + Touchpad Center + + + Touchpad Right + Touchpad Right + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + + + Press a button + Press a button + + + Move analog stick + Move analog stick + EditorDialog diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index 544add7e1..ebc6a8a52 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -453,34 +453,10 @@ Use per-game configs Cấu hình riêng cho từng game - - L1 / LB - L1 / LB - - - L2 / LT - L2 / LT - - - Back - Quay Lại - - - R1 / RB - R1 / RB - - - R2 / RT - R2 / RT - L3 L3 - - Options / Start - Tuỳ chọn / Bắt đầu - R3 R3 @@ -489,22 +465,6 @@ Face Buttons Nút bấm mặt trước - - Triangle / Y - Tam giác / Y - - - Square / X - Vuông / X - - - Circle / B - Tròn / B - - - Cross / A - Chéo / A - Right Stick Deadzone (def:2, max:127) Right Stick Deadzone (def:2, max:127) @@ -565,6 +525,74 @@ Cancel Hủy + + unmapped + unmapped + + + L1 + L1 + + + R1 + R1 + + + L2 + L2 + + + Options + Options + + + R2 + R2 + + + Touchpad Left + Touchpad Left + + + Touchpad Center + Touchpad Center + + + Touchpad Right + Touchpad Right + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + + + Press a button + Press a button + + + Move analog stick + Move analog stick + EditorDialog diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index 2b3f67b1f..2956d9621 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -453,34 +453,10 @@ Use per-game configs 使用每个游戏的配置 - - L1 / LB - L1 / LB - - - L2 / LT - L2 / LT - - - Back - Back - - - R1 / RB - R1 / RB - - - R2 / RT - R2 / RT - L3 L3 - - Options / Start - Options / Start - R3 R3 @@ -489,22 +465,6 @@ Face Buttons 功能键(动作键) - - Triangle / Y - 三角 / Y - - - Square / X - 方框 / X - - - Circle / B - 圈 / B - - - Cross / A - 叉 / A - Right Stick Deadzone (def:2, max:127) 右摇杆死区(默认:2 最大:127) @@ -565,6 +525,74 @@ Cancel 取消 + + unmapped + unmapped + + + L1 + L1 + + + R1 + R1 + + + L2 + L2 + + + Options + Options + + + R2 + R2 + + + Touchpad Left + Touchpad Left + + + Touchpad Center + Touchpad Center + + + Touchpad Right + Touchpad Right + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + + + Press a button + Press a button + + + Move analog stick + Move analog stick + EditorDialog diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index 0d334f9ef..8d2d88966 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -453,34 +453,10 @@ Use per-game configs 使用個別遊戲組態 - - L1 / LB - L1 / LB - - - L2 / LT - L2 / LT - - - Back - Back - - - R1 / RB - R1 / RB - - - R2 / RT - R2 / RT - L3 L3 - - Options / Start - Options / Start - R3 R3 @@ -489,22 +465,6 @@ Face Buttons 功能鍵(動作按鈕) - - Triangle / Y - 三角 / Y - - - Square / X - 正方 / X - - - Circle / B - 圖形 / B - - - Cross / A - 交叉 / A - Right Stick Deadzone (def:2, max:127) 右搖桿無效區域(預設:2 最大:127) @@ -565,6 +525,74 @@ Cancel 取消 + + unmapped + unmapped + + + L1 + L1 + + + R1 + R1 + + + L2 + L2 + + + Options + Options + + + R2 + R2 + + + Touchpad Left + Touchpad Left + + + Touchpad Center + Touchpad Center + + + Touchpad Right + Touchpad Right + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons: + +%1 + + + Press a button + Press a button + + + Move analog stick + Move analog stick + EditorDialog From bdd2419de94a785b1fa078d3f3f7ca2998d3f464 Mon Sep 17 00:00:00 2001 From: Osyotr Date: Sun, 29 Jun 2025 20:38:06 +0300 Subject: [PATCH 12/13] [cmake] Show Windows presets only on Windows (#3172) Also did a small cleanup to remove duplicated cache variables. --- CMakePresets.json | 47 ++++++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/CMakePresets.json b/CMakePresets.json index 7d23903d6..9dda9b68c 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -7,14 +7,31 @@ }, "configurePresets": [ { - "name": "x64-Clang-Base", + "name": "Base", "hidden": true, "generator": "Ninja", "binaryDir": "${sourceDir}/Build/${presetName}", + "installDir": "${sourceDir}/Install/${presetName}" + }, + { + "name": "Qt-GUI", + "hidden": true, + "cacheVariables": { + "ENABLE_QT_GUI": "ON" + } + }, + { + "name": "x64-Windows-Base", + "inherits": [ "Base" ], + "hidden": true, "cacheVariables": { "CMAKE_C_COMPILER": "clang-cl", - "CMAKE_CXX_COMPILER": "clang-cl", - "CMAKE_INSTALL_PREFIX": "${sourceDir}/Build/${presetName}" + "CMAKE_CXX_COMPILER": "clang-cl" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" }, "vendor": { "microsoft.com/VisualStudioSettings/CMake/1.0": { @@ -25,7 +42,7 @@ { "name": "x64-Clang-Debug", "displayName": "Clang x64 Debug", - "inherits": ["x64-Clang-Base"], + "inherits": [ "x64-Windows-Base" ], "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug" } @@ -33,16 +50,12 @@ { "name": "x64-Clang-Debug-Qt", "displayName": "Clang x64 Debug with Qt", - "inherits": ["x64-Clang-Base"], - "cacheVariables": { - "CMAKE_BUILD_TYPE": "Debug", - "ENABLE_QT_GUI": "ON" - } + "inherits": [ "x64-Clang-Debug", "Qt-GUI" ] }, { "name": "x64-Clang-Release", "displayName": "Clang x64 Release", - "inherits": ["x64-Clang-Base"], + "inherits": [ "x64-Windows-Base" ], "cacheVariables": { "CMAKE_BUILD_TYPE": "Release" } @@ -50,16 +63,12 @@ { "name": "x64-Clang-Release-Qt", "displayName": "Clang x64 Release with Qt", - "inherits": ["x64-Clang-Base"], - "cacheVariables": { - "CMAKE_BUILD_TYPE": "Release", - "ENABLE_QT_GUI": "ON" - } + "inherits": [ "x64-Clang-Release", "Qt-GUI" ] }, { "name": "x64-Clang-RelWithDebInfo", "displayName": "Clang x64 RelWithDebInfo", - "inherits": ["x64-Clang-Base"], + "inherits": [ "x64-Windows-Base" ], "cacheVariables": { "CMAKE_BUILD_TYPE": "RelWithDebInfo" } @@ -67,11 +76,7 @@ { "name": "x64-Clang-RelWithDebInfo-Qt", "displayName": "Clang x64 RelWithDebInfo with Qt", - "inherits": ["x64-Clang-Base"], - "cacheVariables": { - "CMAKE_BUILD_TYPE": "RelWithDebInfo", - "ENABLE_QT_GUI": "ON" - } + "inherits": [ "x64-Clang-RelWithDebInfo", "Qt-GUI" ] } ] } From 434dcde9f3598104f3610bdab2463a5db0ee6274 Mon Sep 17 00:00:00 2001 From: Missake212 Date: Sun, 29 Jun 2025 19:17:44 +0100 Subject: [PATCH 13/13] Update template hyperlink (#3174) --- .github/ISSUE_TEMPLATE/game-bug-report.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/game-bug-report.yaml b/.github/ISSUE_TEMPLATE/game-bug-report.yaml index a9c669ff9..d9ebd8347 100644 --- a/.github/ISSUE_TEMPLATE/game-bug-report.yaml +++ b/.github/ISSUE_TEMPLATE/game-bug-report.yaml @@ -35,7 +35,7 @@ body: required: true - 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. + - label: I have all the required [system modules](https://github.com/shadps4-emu/shadPS4/wiki/I.-Quick-start-%5BUsers%5D#4-adding-modules) installed. required: true - type: textarea id: desc