diff options
Diffstat (limited to 'src')
37 files changed, 390 insertions, 124 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index e216eb3de..57cbb9d07 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -174,6 +174,15 @@ if(ANDROID) ) endif() +if (UNIX) + target_sources(common PRIVATE + linux/gamemode.cpp + linux/gamemode.h + ) + + target_link_libraries(common PRIVATE gamemode) +endif() + if(ARCHITECTURE_x86_64) target_sources(common PRIVATE diff --git a/src/common/linux/gamemode.cpp b/src/common/linux/gamemode.cpp new file mode 100644 index 000000000..8876d8dc4 --- /dev/null +++ b/src/common/linux/gamemode.cpp @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <gamemode_client.h> + +#include "common/linux/gamemode.h" +#include "common/settings.h" + +namespace Common::Linux { + +void StartGamemode() { + if (Settings::values.enable_gamemode) { + if (gamemode_request_start() < 0) { + LOG_WARNING(Frontend, "Failed to start gamemode: {}", gamemode_error_string()); + } else { + LOG_INFO(Frontend, "Started gamemode"); + } + } +} + +void StopGamemode() { + if (Settings::values.enable_gamemode) { + if (gamemode_request_end() < 0) { + LOG_WARNING(Frontend, "Failed to stop gamemode: {}", gamemode_error_string()); + } else { + LOG_INFO(Frontend, "Stopped gamemode"); + } + } +} + +void SetGamemodeState(bool state) { + if (state) { + StartGamemode(); + } else { + StopGamemode(); + } +} + +} // namespace Common::Linux diff --git a/src/common/linux/gamemode.h b/src/common/linux/gamemode.h new file mode 100644 index 000000000..b80646ae2 --- /dev/null +++ b/src/common/linux/gamemode.h @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +namespace Common::Linux { + +/** + * Start the (Feral Interactive) Linux gamemode if it is installed and it is activated + */ +void StartGamemode(); + +/** + * Stop the (Feral Interactive) Linux gamemode if it is installed and it is activated + */ +void StopGamemode(); + +/** + * Start or stop the (Feral Interactive) Linux gamemode if it is installed and it is activated + * @param state The new state the gamemode should have + */ +void SetGamemodeState(bool state); + +} // namespace Common::Linux diff --git a/src/common/settings.cpp b/src/common/settings.cpp index a10131eb2..3e829253f 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -219,6 +219,8 @@ const char* TranslateCategory(Category category) { return "Services"; case Category::Paths: return "Paths"; + case Category::Linux: + return "Linux"; case Category::MaxEnum: break; } diff --git a/src/common/settings.h b/src/common/settings.h index b929fd957..6425cd98f 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -429,6 +429,9 @@ struct Values { true, true}; + // Linux + SwitchableSetting<bool> enable_gamemode{linkage, true, "enable_gamemode", Category::Linux}; + // Controls InputSetting<std::array<PlayerInput, 10>> players; diff --git a/src/common/settings_common.h b/src/common/settings_common.h index 7943223eb..344c04439 100644 --- a/src/common/settings_common.h +++ b/src/common/settings_common.h @@ -41,6 +41,7 @@ enum class Category : u32 { Multiplayer, Services, Paths, + Linux, MaxEnum, }; diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index a72df034e..c7b48a58d 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h @@ -167,6 +167,11 @@ protected: */ std::pair<f32, f32> MapToTouchScreen(u32 framebuffer_x, u32 framebuffer_y) const; + /** + * Clip the provided coordinates to be inside the touchscreen area. + */ + std::pair<u32, u32> ClipToTouchScreen(u32 new_x, u32 new_y) const; + WindowSystemInfo window_info; bool strict_context_required = false; @@ -181,11 +186,6 @@ private: // By default, ignore this request and do nothing. } - /** - * Clip the provided coordinates to be inside the touchscreen area. - */ - std::pair<u32, u32> ClipToTouchScreen(u32 new_x, u32 new_y) const; - Layout::FramebufferLayout framebuffer_layout; ///< Current framebuffer layout u32 client_area_width; ///< Current client width, should be set by window impl. diff --git a/src/core/hle/service/am/applets/applet_cabinet.cpp b/src/core/hle/service/am/applets/applet_cabinet.cpp index 9d1960cb7..3906c0fa4 100644 --- a/src/core/hle/service/am/applets/applet_cabinet.cpp +++ b/src/core/hle/service/am/applets/applet_cabinet.cpp @@ -131,7 +131,7 @@ void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name) nfp_device->DeleteApplicationArea(); break; case Service::NFP::CabinetMode::StartRestorer: - nfp_device->RestoreAmiibo(); + nfp_device->Restore(); break; case Service::NFP::CabinetMode::StartFormatter: nfp_device->Format(); diff --git a/src/core/hle/service/mii/types/ver3_store_data.cpp b/src/core/hle/service/mii/types/ver3_store_data.cpp index a019cc9f7..c27646fcf 100644 --- a/src/core/hle/service/mii/types/ver3_store_data.cpp +++ b/src/core/hle/service/mii/types/ver3_store_data.cpp @@ -98,7 +98,7 @@ void Ver3StoreData::BuildToStoreData(StoreData& out_store_data) const { } void Ver3StoreData::BuildFromStoreData(const StoreData& store_data) { - version = 1; + version = 3; mii_information.gender.Assign(static_cast<u8>(store_data.GetGender())); mii_information.favorite_color.Assign(static_cast<u8>(store_data.GetFavoriteColor())); height = store_data.GetHeight(); diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp index e7a00deb3..47516f883 100644 --- a/src/core/hle/service/nfc/common/device.cpp +++ b/src/core/hle/service/nfc/common/device.cpp @@ -401,6 +401,12 @@ Result NfcDevice::SendCommandByPassThrough(const Time::Clock::TimeSpanType& time } Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target_) { + bool is_corrupted = false; + + if (model_type != NFP::ModelType::Amiibo) { + return ResultInvalidArgument; + } + if (device_state != DeviceState::TagFound) { LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); return ResultWrongDeviceState; @@ -420,26 +426,32 @@ Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target if (is_plain_amiibo) { std::vector<u8> data(sizeof(NFP::NTAG215File)); memcpy(data.data(), &tag_data, sizeof(tag_data)); - WriteBackupData(tag_data.uid, data); + } - device_state = DeviceState::TagMounted; - mount_target = mount_target_; - return ResultSuccess; + if (!is_plain_amiibo && !NFP::AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) { + LOG_ERROR(Service_NFP, "Can't decode amiibo"); + is_corrupted = true; } - if (!NFP::AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) { - bool has_backup = HasBackup(encrypted_tag_data.uuid).IsSuccess(); - LOG_ERROR(Service_NFP, "Can't decode amiibo, has_backup= {}", has_backup); - return has_backup ? ResultCorruptedDataWithBackup : ResultCorruptedData; + if (tag_data.settings.settings.amiibo_initialized && !tag_data.owner_mii.IsValid()) { + LOG_ERROR(Service_NFP, "Invalid mii data"); + is_corrupted = true; } - std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File)); - memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data)); - WriteBackupData(encrypted_tag_data.uuid, data); + if (!is_corrupted) { + std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File)); + memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data)); + WriteBackupData(encrypted_tag_data.uuid, data); + } device_state = DeviceState::TagMounted; mount_target = mount_target_; + if (is_corrupted) { + bool has_backup = HasBackup(encrypted_tag_data.uuid).IsSuccess(); + return has_backup ? ResultCorruptedDataWithBackup : ResultCorruptedData; + } + return ResultSuccess; } @@ -606,6 +618,17 @@ Result NfcDevice::Restore() { } } + // Restore mii data in case is corrupted by previous instances of yuzu + if (tag_data.settings.settings.amiibo_initialized && !tag_data.owner_mii.IsValid()) { + LOG_ERROR(Service_NFP, "Regenerating mii data"); + Mii::StoreData new_mii{}; + new_mii.BuildRandom(Mii::Age::All, Mii::Gender::All, Mii::Race::All); + new_mii.SetNickname({u'y', u'u', u'z', u'u', u'\0'}); + + tag_data.owner_mii.BuildFromStoreData(new_mii); + tag_data.mii_extension.SetFromStoreData(new_mii); + } + // Overwrite tag contents with backup and mount the tag tag_data = temporary_tag_data; encrypted_tag_data = temporary_encrypted_tag_data; @@ -851,25 +874,6 @@ Result NfcDevice::SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& registe return Flush(); } -Result NfcDevice::RestoreAmiibo() { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return ResultTagRemoved; - } - return ResultWrongDeviceState; - } - - if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) { - LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); - return ResultWrongDeviceState; - } - - // TODO: Load amiibo from backup on system - LOG_ERROR(Service_NFP, "Not Implemented"); - return ResultSuccess; -} - Result NfcDevice::Format() { Result result = ResultSuccess; @@ -877,7 +881,9 @@ Result NfcDevice::Format() { result = Mount(NFP::ModelType::Amiibo, NFP::MountTarget::All); } - if (result.IsError()) { + // We are formatting all data. Corruption is not an issue. + if (result.IsError() && + (result != ResultCorruptedData && result != ResultCorruptedDataWithBackup)) { return result; } diff --git a/src/core/hle/service/nfc/common/device.h b/src/core/hle/service/nfc/common/device.h index 0ed1ff34c..d8efe25ec 100644 --- a/src/core/hle/service/nfc/common/device.h +++ b/src/core/hle/service/nfc/common/device.h @@ -68,7 +68,6 @@ public: Result DeleteRegisterInfo(); Result SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& register_info); - Result RestoreAmiibo(); Result Format(); Result OpenApplicationArea(u32 access_id); diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp index 2705ab140..9319ea007 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp @@ -5,6 +5,7 @@ #include "shader_recompiler/backend/glasm/glasm_emit_context.h" #include "shader_recompiler/frontend/ir/program.h" #include "shader_recompiler/frontend/ir/value.h" +#include "shader_recompiler/profile.h" #include "shader_recompiler/runtime_info.h" namespace Shader::Backend::GLASM { @@ -35,7 +36,9 @@ void GlobalStorageOp(EmitContext& ctx, Register address, bool pointer_based, std continue; } const auto& ssbo{ctx.info.storage_buffers_descriptors[index]}; - ctx.Add("LDC.U64 DC.x,c{}[{}];" // ssbo_addr + const u64 ssbo_align_mask{~(ctx.profile.min_ssbo_alignment - 1U)}; + ctx.Add("LDC.U64 DC.x,c{}[{}];" // unaligned_ssbo_addr + "AND.U64 DC.x,DC.x,{};" // ssbo_addr = unaligned_ssbo_addr & ssbo_align_mask "LDC.U32 RC.x,c{}[{}];" // ssbo_size_u32 "CVT.U64.U32 DC.y,RC.x;" // ssbo_size = ssbo_size_u32 "ADD.U64 DC.y,DC.y,DC.x;" // ssbo_end = ssbo_addr + ssbo_size @@ -44,8 +47,8 @@ void GlobalStorageOp(EmitContext& ctx, Register address, bool pointer_based, std "AND.U.CC RC.x,RC.x,RC.y;" // cond = a && b "IF NE.x;" // if cond "SUB.U64 DC.x,{}.x,DC.x;", // offset = input_addr - ssbo_addr - ssbo.cbuf_index, ssbo.cbuf_offset, ssbo.cbuf_index, ssbo.cbuf_offset + 8, address, - address, address); + ssbo.cbuf_index, ssbo.cbuf_offset, ssbo_align_mask, ssbo.cbuf_index, + ssbo.cbuf_offset + 8, address, address, address); if (pointer_based) { ctx.Add("PK64.U DC.y,c[{}];" // host_ssbo = cbuf "ADD.U64 DC.x,DC.x,DC.y;" // host_addr = host_ssbo + offset diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp index 9ff4028c2..fd9a99449 100644 --- a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp +++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp @@ -601,7 +601,10 @@ std::string EmitContext::DefineGlobalMemoryFunctions() { addr_xy[i] = fmt::format("ftou({}[{}].{})", cbuf, addr_loc / 16, Swizzle(addr_loc)); size_xy[i] = fmt::format("ftou({}[{}].{})", cbuf, size_loc / 16, Swizzle(size_loc)); } - const auto addr_pack{fmt::format("packUint2x32(uvec2({},{}))", addr_xy[0], addr_xy[1])}; + const u32 ssbo_align_mask{~(static_cast<u32>(profile.min_ssbo_alignment) - 1U)}; + const auto aligned_low_addr{fmt::format("{}&{}", addr_xy[0], ssbo_align_mask)}; + const auto aligned_addr{fmt::format("uvec2({},{})", aligned_low_addr, addr_xy[1])}; + const auto addr_pack{fmt::format("packUint2x32({})", aligned_addr)}; const auto addr_statment{fmt::format("uint64_t {}={};", ssbo_addr, addr_pack)}; func += addr_statment; diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 57df6fc34..3350f1f85 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -891,7 +891,9 @@ void EmitContext::DefineGlobalMemoryFunctions(const Info& info) { const Id ssbo_size_pointer{OpAccessChain(uniform_types.U32, cbufs[ssbo.cbuf_index].U32, zero, ssbo_size_cbuf_offset)}; - const Id ssbo_addr{OpBitcast(U64, OpLoad(U32[2], ssbo_addr_pointer))}; + const u64 ssbo_align_mask{~(profile.min_ssbo_alignment - 1U)}; + const Id unaligned_addr{OpBitcast(U64, OpLoad(U32[2], ssbo_addr_pointer))}; + const Id ssbo_addr{OpBitwiseAnd(U64, unaligned_addr, Constant(U64, ssbo_align_mask))}; const Id ssbo_size{OpUConvert(U64, OpLoad(U32[1], ssbo_size_pointer))}; const Id ssbo_end{OpIAdd(U64, ssbo_addr, ssbo_size)}; const Id cond{OpLogicalAnd(U1, OpUGreaterThanEqual(U1, addr, ssbo_addr), diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp index 8fac6bad3..321ea625b 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp @@ -298,7 +298,7 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo Optimization::PositionPass(env, program); - Optimization::GlobalMemoryToStorageBufferPass(program); + Optimization::GlobalMemoryToStorageBufferPass(program, host_info); Optimization::TexturePass(env, program, host_info); if (Settings::values.resolution_info.active) { diff --git a/src/shader_recompiler/host_translate_info.h b/src/shader_recompiler/host_translate_info.h index 7d2ded907..1b53404fc 100644 --- a/src/shader_recompiler/host_translate_info.h +++ b/src/shader_recompiler/host_translate_info.h @@ -16,6 +16,7 @@ struct HostTranslateInfo { bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers bool support_viewport_index_layer{}; ///< True when the device supports gl_Layer in VS + u32 min_ssbo_alignment{}; ///< Minimum alignment supported by the device for SSBOs bool support_geometry_shader_passthrough{}; ///< True when the device supports geometry ///< passthrough shaders bool support_conditional_barrier{}; ///< True when the device supports barriers in conditional diff --git a/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp b/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp index d1e59f22e..0cea79945 100644 --- a/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp +++ b/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp @@ -11,6 +11,7 @@ #include "shader_recompiler/frontend/ir/breadth_first_search.h" #include "shader_recompiler/frontend/ir/ir_emitter.h" #include "shader_recompiler/frontend/ir/value.h" +#include "shader_recompiler/host_translate_info.h" #include "shader_recompiler/ir_opt/passes.h" namespace Shader::Optimization { @@ -408,7 +409,7 @@ void CollectStorageBuffers(IR::Block& block, IR::Inst& inst, StorageInfo& info) } /// Returns the offset in indices (not bytes) for an equivalent storage instruction -IR::U32 StorageOffset(IR::Block& block, IR::Inst& inst, StorageBufferAddr buffer) { +IR::U32 StorageOffset(IR::Block& block, IR::Inst& inst, StorageBufferAddr buffer, u32 alignment) { IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; IR::U32 offset; if (const std::optional<LowAddrInfo> low_addr{TrackLowAddress(&inst)}) { @@ -421,7 +422,10 @@ IR::U32 StorageOffset(IR::Block& block, IR::Inst& inst, StorageBufferAddr buffer } // Subtract the least significant 32 bits from the guest offset. The result is the storage // buffer offset in bytes. - const IR::U32 low_cbuf{ir.GetCbuf(ir.Imm32(buffer.index), ir.Imm32(buffer.offset))}; + IR::U32 low_cbuf{ir.GetCbuf(ir.Imm32(buffer.index), ir.Imm32(buffer.offset))}; + + // Align the offset base to match the host alignment requirements + low_cbuf = ir.BitwiseAnd(low_cbuf, ir.Imm32(~(alignment - 1U))); return ir.ISub(offset, low_cbuf); } @@ -516,7 +520,7 @@ void Replace(IR::Block& block, IR::Inst& inst, const IR::U32& storage_index, } } // Anonymous namespace -void GlobalMemoryToStorageBufferPass(IR::Program& program) { +void GlobalMemoryToStorageBufferPass(IR::Program& program, const HostTranslateInfo& host_info) { StorageInfo info; for (IR::Block* const block : program.post_order_blocks) { for (IR::Inst& inst : block->Instructions()) { @@ -540,7 +544,8 @@ void GlobalMemoryToStorageBufferPass(IR::Program& program) { const IR::U32 index{IR::Value{static_cast<u32>(info.set.index_of(it))}}; IR::Block* const block{storage_inst.block}; IR::Inst* const inst{storage_inst.inst}; - const IR::U32 offset{StorageOffset(*block, *inst, storage_buffer)}; + const IR::U32 offset{ + StorageOffset(*block, *inst, storage_buffer, host_info.min_ssbo_alignment)}; Replace(*block, *inst, index, offset); } } diff --git a/src/shader_recompiler/ir_opt/passes.h b/src/shader_recompiler/ir_opt/passes.h index d4d5285e5..1e637cb23 100644 --- a/src/shader_recompiler/ir_opt/passes.h +++ b/src/shader_recompiler/ir_opt/passes.h @@ -16,7 +16,7 @@ void CollectShaderInfoPass(Environment& env, IR::Program& program); void ConditionalBarrierPass(IR::Program& program); void ConstantPropagationPass(Environment& env, IR::Program& program); void DeadCodeEliminationPass(IR::Program& program); -void GlobalMemoryToStorageBufferPass(IR::Program& program); +void GlobalMemoryToStorageBufferPass(IR::Program& program, const HostTranslateInfo& host_info); void IdentityRemovalPass(IR::Program& program); void LowerFp64ToFp32(IR::Program& program); void LowerFp16ToFp32(IR::Program& program); diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index a9de9f4a9..66901a965 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h @@ -85,6 +85,8 @@ struct Profile { /// Maxwell and earlier nVidia architectures have broken robust support bool has_broken_robust{}; + + u64 min_ssbo_alignment{}; }; } // namespace Shader diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 90dbd352f..6d1fc3887 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -1753,15 +1753,25 @@ Binding BufferCache<P>::StorageBufferBinding(GPUVAddr ssbo_addr, u32 cbuf_index, const u32 memory_layout_size = static_cast<u32>(gpu_memory->GetMemoryLayoutSize(gpu_addr)); return std::min(memory_layout_size, static_cast<u32>(8_MiB)); }(); - const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr); - if (!cpu_addr || size == 0) { + // Alignment only applies to the offset of the buffer + const u32 alignment = runtime.GetStorageBufferAlignment(); + const GPUVAddr aligned_gpu_addr = Common::AlignDown(gpu_addr, alignment); + const u32 aligned_size = static_cast<u32>(gpu_addr - aligned_gpu_addr) + size; + + const std::optional<VAddr> aligned_cpu_addr = gpu_memory->GpuToCpuAddress(aligned_gpu_addr); + if (!aligned_cpu_addr || size == 0) { LOG_WARNING(HW_GPU, "Failed to find storage buffer for cbuf index {}", cbuf_index); return NULL_BINDING; } - const VAddr cpu_end = Common::AlignUp(*cpu_addr + size, YUZU_PAGESIZE); + const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr); + ASSERT_MSG(cpu_addr, "Unaligned storage buffer address not found for cbuf index {}", + cbuf_index); + // The end address used for size calculation does not need to be aligned + const VAddr cpu_end = Common::AlignUp(*cpu_addr + size, Core::Memory::YUZU_PAGESIZE); + const Binding binding{ - .cpu_addr = *cpu_addr, - .size = is_written ? size : static_cast<u32>(cpu_end - *cpu_addr), + .cpu_addr = *aligned_cpu_addr, + .size = is_written ? aligned_size : static_cast<u32>(cpu_end - *aligned_cpu_addr), .buffer_id = BufferId{}, }; return binding; diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h index feccf06f9..000f29a82 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.h +++ b/src/video_core/renderer_opengl/gl_buffer_cache.h @@ -191,6 +191,10 @@ public: return device.CanReportMemoryUsage(); } + u32 GetStorageBufferAlignment() const { + return static_cast<u32>(device.GetShaderStorageBufferAlignment()); + } + private: static constexpr std::array PABO_LUT{ GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV, GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV, diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index 94258ccd0..993438a27 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp @@ -265,33 +265,33 @@ std::string Device::GetVendorName() const { if (vendor_name == "Intel") { // For Mesa, `Intel` is an overloaded vendor string that could mean crocus or iris. // Simply return `INTEL` for those as well as the Windows driver. - return "INTEL"; + return "Intel"; } if (vendor_name == "Intel Open Source Technology Center") { - return "I965"; + return "i965"; } if (vendor_name == "Mesa Project") { - return "I915"; + return "i915"; } if (vendor_name == "Mesa/X.org") { // This vendor string is overloaded between llvmpipe, softpipe, and virgl, so just return // MESA instead of one of those driver names. - return "MESA"; + return "Mesa"; } if (vendor_name == "AMD") { - return "RADEONSI"; + return "RadeonSI"; } if (vendor_name == "nouveau") { - return "NOUVEAU"; + return "Nouveau"; } if (vendor_name == "X.Org") { return "R600"; } if (vendor_name == "Collabora Ltd") { - return "ZINK"; + return "Zink"; } if (vendor_name == "Intel Corporation") { - return "OPENSWR"; + return "OpenSWR"; } if (vendor_name == "Microsoft Corporation") { return "D3D12"; @@ -300,7 +300,7 @@ std::string Device::GetVendorName() const { // Mesa's tegra driver reports `NVIDIA`. Only present in this list because the default // strategy would have returned `NVIDIA` here for this driver, the same result as the // proprietary driver. - return "TEGRA"; + return "Tegra"; } return vendor_name; } diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 2888e0238..26f2d0ea7 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -232,6 +232,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo .has_gl_bool_ref_bug = device.HasBoolRefBug(), .ignore_nan_fp_comparisons = true, .gl_max_compute_smem_size = device.GetMaxComputeSharedMemorySize(), + .min_ssbo_alignment = device.GetShaderStorageBufferAlignment(), }, host_info{ .support_float64 = true, @@ -240,6 +241,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo .needs_demote_reorder = device.IsAmd(), .support_snorm_render_buffer = false, .support_viewport_index_layer = device.HasVertexViewportLayer(), + .min_ssbo_alignment = static_cast<u32>(device.GetShaderStorageBufferAlignment()), .support_geometry_shader_passthrough = device.HasGeometryShaderPassthrough(), .support_conditional_barrier = device.SupportsConditionalBarriers(), } { diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 7691cc2ba..5958f52f7 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -355,6 +355,10 @@ bool BufferCacheRuntime::CanReportMemoryUsage() const { return device.CanReportMemoryUsage(); } +u32 BufferCacheRuntime::GetStorageBufferAlignment() const { + return static_cast<u32>(device.GetStorageBufferAlignment()); +} + void BufferCacheRuntime::TickFrame(VideoCommon::SlotVector<Buffer>& slot_buffers) noexcept { for (auto it = slot_buffers.begin(); it != slot_buffers.end(); it++) { it->ResetUsageTracking(); diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index 4416a902f..0b3fbd6d0 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h @@ -91,6 +91,8 @@ public: bool CanReportMemoryUsage() const; + u32 GetStorageBufferAlignment() const; + [[nodiscard]] StagingBufferRef UploadStagingBuffer(size_t size); [[nodiscard]] StagingBufferRef DownloadStagingBuffer(size_t size, bool deferred = false); diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 89b455bff..2a13b2a72 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -373,6 +373,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY, .has_broken_robust = device.IsNvidia() && device.GetNvidiaArch() <= NvidiaArchitecture::Arch_Pascal, + .min_ssbo_alignment = device.GetStorageBufferAlignment(), }; host_info = Shader::HostTranslateInfo{ @@ -383,6 +384,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE, .support_snorm_render_buffer = true, .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(), + .min_ssbo_alignment = static_cast<u32>(device.GetStorageBufferAlignment()), .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(), .support_conditional_barrier = device.SupportsConditionalBarriers(), }; diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index fde36a49c..188ceeed7 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -847,11 +847,41 @@ std::string Device::GetDriverName() const { case VK_DRIVER_ID_NVIDIA_PROPRIETARY: return "NVIDIA"; case VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS: - return "INTEL"; + return "Intel"; case VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA: return "ANV"; + case VK_DRIVER_ID_IMAGINATION_PROPRIETARY: + return "PowerVR"; + case VK_DRIVER_ID_QUALCOMM_PROPRIETARY: + return "Qualcomm"; + case VK_DRIVER_ID_ARM_PROPRIETARY: + return "Mali"; + case VK_DRIVER_ID_GOOGLE_SWIFTSHADER: + return "SwiftShader"; + case VK_DRIVER_ID_BROADCOM_PROPRIETARY: + return "Broadcom"; case VK_DRIVER_ID_MESA_LLVMPIPE: - return "LAVAPIPE"; + return "Lavapipe"; + case VK_DRIVER_ID_MOLTENVK: + return "MoltenVK"; + case VK_DRIVER_ID_VERISILICON_PROPRIETARY: + return "Vivante"; + case VK_DRIVER_ID_MESA_TURNIP: + return "Turnip"; + case VK_DRIVER_ID_MESA_V3DV: + return "V3DV"; + case VK_DRIVER_ID_MESA_PANVK: + return "PanVK"; + case VK_DRIVER_ID_MESA_VENUS: + return "Venus"; + case VK_DRIVER_ID_MESA_DOZEN: + return "Dozen"; + case VK_DRIVER_ID_MESA_NVK: + return "NVK"; + case VK_DRIVER_ID_IMAGINATION_OPEN_SOURCE_MESA: + return "PVR"; + // case VK_DRIVER_ID_MESA_AGXV: + // return "Asahi"; default: return properties.driver.driverName; } diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 90278052a..f3ad2214b 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -386,7 +386,7 @@ if (NOT WIN32) target_include_directories(yuzu PRIVATE ${Qt${QT_MAJOR_VERSION}Gui_PRIVATE_INCLUDE_DIRS}) endif() if (UNIX AND NOT APPLE) - target_link_libraries(yuzu PRIVATE Qt${QT_MAJOR_VERSION}::DBus) + target_link_libraries(yuzu PRIVATE Qt${QT_MAJOR_VERSION}::DBus gamemode) endif() target_compile_definitions(yuzu PRIVATE diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 2afa72140..ed5750155 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -30,7 +30,6 @@ #include <QSize> #include <QStringLiteral> #include <QSurfaceFormat> -#include <QTimer> #include <QWindow> #include <QtCore/qobjectdefs.h> @@ -66,6 +65,8 @@ class QObject; class QPaintEngine; class QSurface; +constexpr int default_mouse_constrain_timeout = 10; + EmuThread::EmuThread(Core::System& system) : m_system{system} {} EmuThread::~EmuThread() = default; @@ -304,6 +305,9 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_, Qt::QueuedConnection); connect(this, &GRenderWindow::ExitSignal, parent, &GMainWindow::OnExit, Qt::QueuedConnection); connect(this, &GRenderWindow::TasPlaybackStateChanged, parent, &GMainWindow::OnTasStateChanged); + + mouse_constrain_timer.setInterval(default_mouse_constrain_timeout); + connect(&mouse_constrain_timer, &QTimer::timeout, this, &GRenderWindow::ConstrainMouse); } void GRenderWindow::ExecuteProgram(std::size_t program_index) { @@ -393,6 +397,22 @@ void GRenderWindow::closeEvent(QCloseEvent* event) { QWidget::closeEvent(event); } +void GRenderWindow::leaveEvent(QEvent* event) { + if (Settings::values.mouse_panning) { + const QRect& rect = QWidget::geometry(); + QPoint position = QCursor::pos(); + + qint32 x = qBound(rect.left(), position.x(), rect.right()); + qint32 y = qBound(rect.top(), position.y(), rect.bottom()); + // Only start the timer if the mouse has left the window bound. + // The leave event is also triggered when the window looses focus. + if (x != position.x() || y != position.y()) { + mouse_constrain_timer.start(); + } + event->accept(); + } +} + int GRenderWindow::QtKeyToSwitchKey(Qt::Key qt_key) { static constexpr std::array<std::pair<Qt::Key, Settings::NativeKeyboard::Keys>, 106> key_map = { std::pair<Qt::Key, Settings::NativeKeyboard::Keys>{Qt::Key_A, Settings::NativeKeyboard::A}, @@ -658,10 +678,19 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { input_subsystem->GetMouse()->TouchMove(touch_x, touch_y); input_subsystem->GetMouse()->Move(pos.x(), pos.y(), center_x, center_y); + // Center mouse for mouse panning if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) { QCursor::setPos(mapToGlobal(QPoint{center_x, center_y})); } + // Constrain mouse for mouse emulation with mouse panning + if (Settings::values.mouse_panning && Settings::values.mouse_enabled) { + const auto [clamped_mouse_x, clamped_mouse_y] = ClipToTouchScreen(x, y); + QCursor::setPos(mapToGlobal( + QPoint{static_cast<int>(clamped_mouse_x), static_cast<int>(clamped_mouse_y)})); + } + + mouse_constrain_timer.stop(); emit MouseActivity(); } @@ -675,6 +704,31 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { input_subsystem->GetMouse()->ReleaseButton(button); } +void GRenderWindow::ConstrainMouse() { + if (emu_thread == nullptr || !Settings::values.mouse_panning) { + mouse_constrain_timer.stop(); + return; + } + if (!this->isActiveWindow()) { + mouse_constrain_timer.stop(); + return; + } + + if (Settings::values.mouse_enabled) { + const auto pos = mapFromGlobal(QCursor::pos()); + const int new_pos_x = std::clamp(pos.x(), 0, width()); + const int new_pos_y = std::clamp(pos.y(), 0, height()); + + QCursor::setPos(mapToGlobal(QPoint{new_pos_x, new_pos_y})); + return; + } + + const int center_x = width() / 2; + const int center_y = height() / 2; + + QCursor::setPos(mapToGlobal(QPoint{center_x, center_y})); +} + void GRenderWindow::wheelEvent(QWheelEvent* event) { const int x = event->angleDelta().x(); const int y = event->angleDelta().y(); diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 87b23df12..60edd464c 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -17,6 +17,7 @@ #include <QString> #include <QStringList> #include <QThread> +#include <QTimer> #include <QWidget> #include <qglobal.h> #include <qnamespace.h> @@ -38,7 +39,6 @@ class QMouseEvent; class QObject; class QResizeEvent; class QShowEvent; -class QTimer; class QTouchEvent; class QWheelEvent; @@ -166,6 +166,7 @@ public: std::pair<u32, u32> ScaleTouch(const QPointF& pos) const; void closeEvent(QCloseEvent* event) override; + void leaveEvent(QEvent* event) override; void resizeEvent(QResizeEvent* event) override; @@ -229,6 +230,7 @@ private: void TouchBeginEvent(const QTouchEvent* event); void TouchUpdateEvent(const QTouchEvent* event); void TouchEndEvent(); + void ConstrainMouse(); void RequestCameraCapture(); void OnCameraCapture(int requestId, const QImage& img); @@ -268,6 +270,8 @@ private: std::unique_ptr<QTimer> camera_timer; #endif + QTimer mouse_constrain_timer; + Core::System& system; protected: diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index c727fadd1..701b895e7 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp @@ -36,12 +36,29 @@ ConfigureGeneral::~ConfigureGeneral() = default; void ConfigureGeneral::SetConfiguration() {} void ConfigureGeneral::Setup(const ConfigurationShared::Builder& builder) { - QLayout& layout = *ui->general_widget->layout(); + QLayout& general_layout = *ui->general_widget->layout(); + QLayout& linux_layout = *ui->linux_widget->layout(); - std::map<u32, QWidget*> hold{}; + std::map<u32, QWidget*> general_hold{}; + std::map<u32, QWidget*> linux_hold{}; - for (const auto setting : - UISettings::values.linkage.by_category[Settings::Category::UiGeneral]) { + std::vector<Settings::BasicSetting*> settings; + + auto push = [&settings](auto& list) { + for (auto setting : list) { + settings.push_back(setting); + } + }; + + push(UISettings::values.linkage.by_category[Settings::Category::UiGeneral]); + push(Settings::values.linkage.by_category[Settings::Category::Linux]); + + // Only show Linux group on Unix +#ifndef __unix__ + ui->LinuxGroupBox->setVisible(false); +#endif + + for (const auto setting : settings) { auto* widget = builder.BuildWidget(setting, apply_funcs); if (widget == nullptr) { @@ -52,11 +69,23 @@ void ConfigureGeneral::Setup(const ConfigurationShared::Builder& builder) { continue; } - hold.emplace(setting->Id(), widget); + switch (setting->GetCategory()) { + case Settings::Category::UiGeneral: + general_hold.emplace(setting->Id(), widget); + break; + case Settings::Category::Linux: + linux_hold.emplace(setting->Id(), widget); + break; + default: + widget->deleteLater(); + } } - for (const auto& [id, widget] : hold) { - layout.addWidget(widget); + for (const auto& [id, widget] : general_hold) { + general_layout.addWidget(widget); + } + for (const auto& [id, widget] : linux_hold) { + linux_layout.addWidget(widget); } } diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui index a10e7d3a5..ef20891a3 100644 --- a/src/yuzu/configuration/configure_general.ui +++ b/src/yuzu/configuration/configure_general.ui @@ -47,6 +47,33 @@ </widget> </item> <item> + <widget class="QGroupBox" name="LinuxGroupBox"> + <property name="title"> + <string>Linux</string> + </property> + <layout class="QVBoxLayout" name="LinuxVerticalLayout_1"> + <item> + <widget class="QWidget" name="linux_widget" native="true"> + <layout class="QVBoxLayout" name="LinuxVerticalLayout_2"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item> <spacer name="verticalSpacer"> <property name="orientation"> <enum>Qt::Vertical</enum> diff --git a/src/yuzu/configuration/configure_system.ui b/src/yuzu/configuration/configure_system.ui index 2a735836e..04b771129 100644 --- a/src/yuzu/configuration/configure_system.ui +++ b/src/yuzu/configuration/configure_system.ui @@ -57,7 +57,7 @@ </widget> </item> <item> - <widget class="QGroupBox" name="groupBox"> + <widget class="QGroupBox" name="coreGroup"> <property name="title"> <string>Core</string> </property> diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp index a7b5def32..ee0ca4aa7 100644 --- a/src/yuzu/configuration/shared_translation.cpp +++ b/src/yuzu/configuration/shared_translation.cpp @@ -176,6 +176,9 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) { INSERT(UISettings, controller_applet_disabled, tr("Disable controller applet"), QStringLiteral()); + // Linux + INSERT(Settings, enable_gamemode, tr("Enable Gamemode"), QStringLiteral()); + // Ui Debugging // Ui Multiplayer diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index dcf68460a..10c788290 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -17,6 +17,7 @@ #ifdef __unix__ #include <csignal> #include <sys/socket.h> +#include "common/linux/gamemode.h" #endif #include <boost/container/flat_set.hpp> @@ -187,7 +188,6 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; #endif constexpr int default_mouse_hide_timeout = 2500; -constexpr int default_mouse_center_timeout = 10; constexpr int default_input_update_timeout = 1; constexpr size_t CopyBufferSize = 1_MiB; @@ -320,6 +320,7 @@ GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulk provider{std::make_unique<FileSys::ManualContentProvider>()} { #ifdef __unix__ SetupSigInterrupts(); + SetGamemodeEnabled(Settings::values.enable_gamemode.GetValue()); #endif system->Initialize(); @@ -437,9 +438,6 @@ GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulk connect(&mouse_hide_timer, &QTimer::timeout, this, &GMainWindow::HideMouseCursor); connect(ui->menubar, &QMenuBar::hovered, this, &GMainWindow::ShowMouseCursor); - mouse_center_timer.setInterval(default_mouse_center_timeout); - connect(&mouse_center_timer, &QTimer::timeout, this, &GMainWindow::CenterMouseCursor); - update_input_timer.setInterval(default_input_update_timeout); connect(&update_input_timer, &QTimer::timeout, this, &GMainWindow::UpdateInputDrivers); update_input_timer.start(); @@ -1376,14 +1374,6 @@ void GMainWindow::InitializeHotkeys() { } }); connect_shortcut(QStringLiteral("Toggle Mouse Panning"), [&] { - if (Settings::values.mouse_enabled) { - Settings::values.mouse_panning = false; - QMessageBox::warning( - this, tr("Emulated mouse is enabled"), - tr("Real mouse input and mouse panning are incompatible. Please disable the " - "emulated mouse in input advanced settings to allow mouse panning.")); - return; - } Settings::values.mouse_panning = !Settings::values.mouse_panning; if (Settings::values.mouse_panning) { render_window->installEventFilter(render_window); @@ -2132,6 +2122,10 @@ void GMainWindow::OnEmulationStopped() { discord_rpc->Update(); +#ifdef __unix__ + Common::Linux::StopGamemode(); +#endif + // The emulation is stopped, so closing the window or not does not matter anymore disconnect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); @@ -3514,6 +3508,10 @@ void GMainWindow::OnStartGame() { play_time_manager->Start(); discord_rpc->Update(); + +#ifdef __unix__ + Common::Linux::StartGamemode(); +#endif } void GMainWindow::OnRestartGame() { @@ -3534,6 +3532,10 @@ void GMainWindow::OnPauseGame() { play_time_manager->Stop(); UpdateMenuState(); AllowOSSleep(); + +#ifdef __unix__ + Common::Linux::StopGamemode(); +#endif } void GMainWindow::OnPauseContinueGame() { @@ -3815,6 +3817,9 @@ void GMainWindow::OnConfigure() { const auto old_theme = UISettings::values.theme; const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue(); const auto old_language_index = Settings::values.language_index.GetValue(); +#ifdef __unix__ + const bool old_gamemode = Settings::values.enable_gamemode.GetValue(); +#endif Settings::SetConfiguringGlobal(true); ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get(), @@ -3876,6 +3881,11 @@ void GMainWindow::OnConfigure() { if (UISettings::values.enable_discord_presence.GetValue() != old_discord_presence) { SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue()); } +#ifdef __unix__ + if (Settings::values.enable_gamemode.GetValue() != old_gamemode) { + SetGamemodeEnabled(Settings::values.enable_gamemode.GetValue()); + } +#endif if (!multiplayer_state->IsHostingPublicRoom()) { multiplayer_state->UpdateCredentials(); @@ -4711,26 +4721,10 @@ void GMainWindow::ShowMouseCursor() { } } -void GMainWindow::CenterMouseCursor() { - if (emu_thread == nullptr || !Settings::values.mouse_panning) { - mouse_center_timer.stop(); - return; - } - if (!this->isActiveWindow()) { - mouse_center_timer.stop(); - return; - } - const int center_x = render_window->width() / 2; - const int center_y = render_window->height() / 2; - - QCursor::setPos(mapToGlobal(QPoint{center_x, center_y})); -} - void GMainWindow::OnMouseActivity() { if (!Settings::values.mouse_panning) { ShowMouseCursor(); } - mouse_center_timer.stop(); } void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { @@ -5031,22 +5025,6 @@ void GMainWindow::dragMoveEvent(QDragMoveEvent* event) { AcceptDropEvent(event); } -void GMainWindow::leaveEvent(QEvent* event) { - if (Settings::values.mouse_panning) { - const QRect& rect = geometry(); - QPoint position = QCursor::pos(); - - qint32 x = qBound(rect.left(), position.x(), rect.right()); - qint32 y = qBound(rect.top(), position.y(), rect.bottom()); - // Only start the timer if the mouse has left the window bound. - // The leave event is also triggered when the window looses focus. - if (x != position.x() || y != position.y()) { - mouse_center_timer.start(); - } - event->accept(); - } -} - bool GMainWindow::ConfirmChangeGame() { if (emu_thread == nullptr) return true; @@ -5216,6 +5194,14 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) { discord_rpc->Update(); } +#ifdef __unix__ +void GMainWindow::SetGamemodeEnabled(bool state) { + if (emulation_running) { + Common::Linux::SetGamemodeState(state); + } +} +#endif + void GMainWindow::changeEvent(QEvent* event) { #ifdef __unix__ // PaletteChange event appears to only reach so far into the GUI, explicitly asking to diff --git a/src/yuzu/main.h b/src/yuzu/main.h index e99d58995..530e445f9 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -340,6 +340,7 @@ private: void SetupSigInterrupts(); static void HandleSigInterrupt(int); void OnSigInterruptNotifierActivated(); + void SetGamemodeEnabled(bool state); #endif private slots: @@ -451,7 +452,6 @@ private: void UpdateInputDrivers(); void HideMouseCursor(); void ShowMouseCursor(); - void CenterMouseCursor(); void OpenURL(const QUrl& url); void LoadTranslation(); void OpenPerGameConfiguration(u64 title_id, const std::string& file_name); @@ -535,7 +535,6 @@ private: bool auto_paused = false; bool auto_muted = false; QTimer mouse_hide_timer; - QTimer mouse_center_timer; QTimer update_input_timer; QString startup_icon_theme; @@ -592,5 +591,4 @@ protected: void dropEvent(QDropEvent* event) override; void dragEnterEvent(QDragEnterEvent* event) override; void dragMoveEvent(QDragMoveEvent* event) override; - void leaveEvent(QEvent* event) override; }; diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 0416d5951..a81635fa4 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -63,6 +63,10 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; } #endif +#ifdef __unix__ +#include "common/linux/gamemode.h" +#endif + static void PrintHelp(const char* argv0) { std::cout << "Usage: " << argv0 << " [options] <filename>\n" @@ -425,6 +429,10 @@ int main(int argc, char** argv) { exit(0); }); +#ifdef __unix__ + Common::Linux::StartGamemode(); +#endif + void(system.Run()); if (system.DebuggerEnabled()) { system.InitializeDebugger(); @@ -436,6 +444,10 @@ int main(int argc, char** argv) { void(system.Pause()); system.ShutdownMainProcess(); +#ifdef __unix__ + Common::Linux::StopGamemode(); +#endif + detached_tasks.WaitForAllTasks(); return 0; } |