Compare commits

..

5 Commits

Author SHA1 Message Date
FrozenAra
e21f96ffde Fixed controller applet crashing when on FW17+ 2023-11-27 20:01:30 +01:00
liamwhite
c7649a0cdb Merge pull request #12183 from german77/justmii
service: nfc: Validate mii data
2023-11-27 11:56:37 -05:00
liamwhite
5a96c525e3 Merge pull request #12160 from german77/mouse_constrain
yuzu: Constrain mouse in render window when emulated
2023-11-27 11:56:24 -05:00
german77
281eb020ea service: nfc: Validate mii data 2023-11-25 23:40:01 -06:00
Narr the Reg
f61cf14646 yuzu: Constrain mouse in render window when emulated 2023-11-24 19:32:35 -06:00
27 changed files with 151 additions and 148 deletions

View File

@@ -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.

View File

@@ -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();

View File

@@ -16,7 +16,8 @@ namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
TouchScreen::TouchScreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
: ControllerBase{hid_core_} {
: ControllerBase{hid_core_}, touchscreen_width(Layout::ScreenUndocked::Width),
touchscreen_height(Layout::ScreenUndocked::Height) {
static_assert(SHARED_MEMORY_OFFSET + sizeof(TouchSharedMemory) < shared_memory_size,
"TouchSharedMemory is bigger than the shared memory");
shared_memory = std::construct_at(
@@ -95,8 +96,8 @@ void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (id < active_fingers_count) {
const auto& [active_x, active_y] = active_fingers[id].position;
touch_entry.position = {
.x = static_cast<u16>(active_x * Layout::ScreenUndocked::Width),
.y = static_cast<u16>(active_y * Layout::ScreenUndocked::Height),
.x = static_cast<u16>(active_x * static_cast<float>(touchscreen_width)),
.y = static_cast<u16>(active_y * static_cast<float>(touchscreen_height)),
};
touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
@@ -120,4 +121,9 @@ void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
shared_memory->touch_screen_lifo.WriteNextEntry(next_state);
}
void TouchScreen::SetTouchscreenDimensions(u32 width, u32 height) {
touchscreen_width = width;
touchscreen_height = height;
}
} // namespace Service::HID

View File

@@ -28,6 +28,8 @@ public:
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
void SetTouchscreenDimensions(u32 width, u32 height);
private:
static constexpr std::size_t MAX_FINGERS = 16;
@@ -53,5 +55,7 @@ private:
Core::HID::EmulatedConsole* console = nullptr;
std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers{};
u32 touchscreen_width;
u32 touchscreen_height;
};
} // namespace Service::HID

View File

@@ -208,6 +208,7 @@ IHidServer::IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> r
{1001, &IHidServer::GetNpadCommunicationMode, "GetNpadCommunicationMode"},
{1002, &IHidServer::SetTouchScreenConfiguration, "SetTouchScreenConfiguration"},
{1003, &IHidServer::IsFirmwareUpdateNeededForNotification, "IsFirmwareUpdateNeededForNotification"},
{1004, &IHidServer::SetTouchScreenResolution, "SetTouchScreenResolution"},
{2000, nullptr, "ActivateDigitizer"},
};
// clang-format on
@@ -2363,6 +2364,21 @@ void IHidServer::IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx) {
rb.Push(false);
}
void IHidServer::SetTouchScreenResolution(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto width{rp.Pop<u32>()};
const auto height{rp.Pop<u32>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
GetResourceManager()->GetTouchScreen()->SetTouchscreenDimensions(width, height);
LOG_INFO(Service_HID, "called, width={}, height={}, applet_resource_user_id={}", width, height,
applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
std::shared_ptr<ResourceManager> IHidServer::GetResourceManager() {
resource_manager->Initialize();
return resource_manager;

View File

@@ -141,6 +141,7 @@ private:
void GetNpadCommunicationMode(HLERequestContext& ctx);
void SetTouchScreenConfiguration(HLERequestContext& ctx);
void IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx);
void SetTouchScreenResolution(HLERequestContext& ctx);
std::shared_ptr<ResourceManager> resource_manager;
std::shared_ptr<HidFirmwareSettings> firmware_settings;

View File

@@ -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();

View File

@@ -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 (!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 (!is_plain_amiibo && !NFP::AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) {
LOG_ERROR(Service_NFP, "Can't decode amiibo");
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 (tag_data.settings.settings.amiibo_initialized && !tag_data.owner_mii.IsValid()) {
LOG_ERROR(Service_NFP, "Invalid mii data");
is_corrupted = true;
}
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;
}

View File

@@ -68,7 +68,6 @@ public:
Result DeleteRegisterInfo();
Result SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& register_info);
Result RestoreAmiibo();
Result Format();
Result OpenApplicationArea(u32 access_id);

View File

@@ -5,7 +5,6 @@
#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 {
@@ -36,9 +35,7 @@ void GlobalStorageOp(EmitContext& ctx, Register address, bool pointer_based, std
continue;
}
const auto& ssbo{ctx.info.storage_buffers_descriptors[index]};
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
ctx.Add("LDC.U64 DC.x,c{}[{}];" // ssbo_addr
"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
@@ -47,8 +44,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_align_mask, ssbo.cbuf_index,
ssbo.cbuf_offset + 8, address, address, address);
ssbo.cbuf_index, ssbo.cbuf_offset, 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

View File

@@ -601,10 +601,7 @@ 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 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_pack{fmt::format("packUint2x32(uvec2({},{}))", addr_xy[0], addr_xy[1])};
const auto addr_statment{fmt::format("uint64_t {}={};", ssbo_addr, addr_pack)};
func += addr_statment;

View File

@@ -891,9 +891,7 @@ 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 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_addr{OpBitcast(U64, OpLoad(U32[2], ssbo_addr_pointer))};
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),

View File

@@ -298,7 +298,7 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
Optimization::PositionPass(env, program);
Optimization::GlobalMemoryToStorageBufferPass(program, host_info);
Optimization::GlobalMemoryToStorageBufferPass(program);
Optimization::TexturePass(env, program, host_info);
if (Settings::values.resolution_info.active) {

View File

@@ -16,7 +16,6 @@ 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

View File

@@ -11,7 +11,6 @@
#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 {
@@ -409,7 +408,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, u32 alignment) {
IR::U32 StorageOffset(IR::Block& block, IR::Inst& inst, StorageBufferAddr buffer) {
IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
IR::U32 offset;
if (const std::optional<LowAddrInfo> low_addr{TrackLowAddress(&inst)}) {
@@ -422,10 +421,7 @@ 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.
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)));
const IR::U32 low_cbuf{ir.GetCbuf(ir.Imm32(buffer.index), ir.Imm32(buffer.offset))};
return ir.ISub(offset, low_cbuf);
}
@@ -520,7 +516,7 @@ void Replace(IR::Block& block, IR::Inst& inst, const IR::U32& storage_index,
}
} // Anonymous namespace
void GlobalMemoryToStorageBufferPass(IR::Program& program, const HostTranslateInfo& host_info) {
void GlobalMemoryToStorageBufferPass(IR::Program& program) {
StorageInfo info;
for (IR::Block* const block : program.post_order_blocks) {
for (IR::Inst& inst : block->Instructions()) {
@@ -544,8 +540,7 @@ void GlobalMemoryToStorageBufferPass(IR::Program& program, const HostTranslateIn
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, host_info.min_ssbo_alignment)};
const IR::U32 offset{StorageOffset(*block, *inst, storage_buffer)};
Replace(*block, *inst, index, offset);
}
}

View File

@@ -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, const HostTranslateInfo& host_info);
void GlobalMemoryToStorageBufferPass(IR::Program& program);
void IdentityRemovalPass(IR::Program& program);
void LowerFp64ToFp32(IR::Program& program);
void LowerFp16ToFp32(IR::Program& program);

View File

@@ -85,8 +85,6 @@ struct Profile {
/// Maxwell and earlier nVidia architectures have broken robust support
bool has_broken_robust{};
u64 min_ssbo_alignment{};
};
} // namespace Shader

View File

@@ -1753,25 +1753,15 @@ 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));
}();
// 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) {
const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
if (!cpu_addr || size == 0) {
LOG_WARNING(HW_GPU, "Failed to find storage buffer for cbuf index {}", cbuf_index);
return NULL_BINDING;
}
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 VAddr cpu_end = Common::AlignUp(*cpu_addr + size, YUZU_PAGESIZE);
const Binding binding{
.cpu_addr = *aligned_cpu_addr,
.size = is_written ? aligned_size : static_cast<u32>(cpu_end - *aligned_cpu_addr),
.cpu_addr = *cpu_addr,
.size = is_written ? size : static_cast<u32>(cpu_end - *cpu_addr),
.buffer_id = BufferId{},
};
return binding;

View File

@@ -191,10 +191,6 @@ 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,

View File

@@ -232,7 +232,6 @@ 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,
@@ -241,7 +240,6 @@ 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(),
} {

View File

@@ -355,10 +355,6 @@ 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();

View File

@@ -91,8 +91,6 @@ public:
bool CanReportMemoryUsage() const;
u32 GetStorageBufferAlignment() const;
[[nodiscard]] StagingBufferRef UploadStagingBuffer(size_t size);
[[nodiscard]] StagingBufferRef DownloadStagingBuffer(size_t size, bool deferred = false);

View File

@@ -373,7 +373,6 @@ 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{
@@ -384,7 +383,6 @@ 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(),
};

View File

@@ -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();

View File

@@ -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:

View File

@@ -187,7 +187,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;
@@ -437,9 +436,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 +1372,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);
@@ -4711,26 +4699,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 +5003,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;

View File

@@ -451,7 +451,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 +534,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 +590,4 @@ protected:
void dropEvent(QDropEvent* event) override;
void dragEnterEvent(QDragEnterEvent* event) override;
void dragMoveEvent(QDragMoveEvent* event) override;
void leaveEvent(QEvent* event) override;
};