2 Commits
Author SHA1 Message Date
KeatonTheBot e6fdf838a1 Add icd.d JSON files for local, non-bundled builds 2026-09-05 00:16:33 -05:00
KeatonTheBot a398002918 Add KosmicKrisp translation layer for macOS 26+
- Mesa 26.0 introduced KosmicKrisp, a Vulkan-on-Metal translation layer and the eventual successor to MoltenVK
2026-08-29 17:43:09 -05:00
31 changed files with 166 additions and 434 deletions
@@ -1,7 +1,7 @@
{ {
"file_format_version" : "1.0.0", "file_format_version" : "1.0.0",
"ICD": { "ICD": {
"library_path": "./libMoltenVK.dylib", "library_path": "../../libMoltenVK.dylib",
"api_version" : "1.4.0", "api_version" : "1.4.0",
"is_portability_driver" : true "is_portability_driver" : true
} }
@@ -1,7 +1,7 @@
{ {
"ICD": { "ICD": {
"api_version": "1.3.0", "api_version": "1.3.0",
"library_path": "./libvulkan_kosmickrisp.dylib" "library_path": "../../libvulkan_kosmickrisp.dylib"
}, },
"file_format_version": "1.0.1" "file_format_version": "1.0.1"
} }
@@ -16,10 +16,5 @@ namespace Ryujinx.Common.Configuration.Hid.Controller
/// Enable Rumble /// Enable Rumble
/// </summary> /// </summary>
public bool EnableRumble { get; set; } public bool EnableRumble { get; set; }
/// <summary>
/// Enable HD Rumble support
/// </summary
public bool UseHDRumble { get; set; }
} }
} }
@@ -126,13 +126,8 @@ namespace Ryujinx.Common
public static string[] GetAllAvailableResources(string path, string ext = "") public static string[] GetAllAvailableResources(string path, string ext = "")
{ {
(Assembly assembly, string resourcePath) = ResolveManifestPath(path); return ResolveManifestPath(path).Item1.GetManifestResourceNames()
.Where(r => r.EndsWith(ext))
string manifestPath = assembly.GetName().Name + "." + resourcePath.Replace('/', '.');
return assembly.GetManifestResourceNames()
.Where(r => r.StartsWith(manifestPath + ".", StringComparison.Ordinal))
.Where(r => r.EndsWith(ext, StringComparison.Ordinal))
.ToArray(); .ToArray();
} }
@@ -1790,7 +1790,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{ {
(_, varType) = IoMap.GetSpirvBuiltIn(ioVariable); (_, varType) = IoMap.GetSpirvBuiltIn(ioVariable);
if (context.Definitions.Stage.IsVtg() && IoMap.IsPerVertexBuiltIn(ioVariable)) if (IoMap.IsPerVertexBuiltIn(ioVariable))
{ {
perVertexBuiltIn = ioVariable; perVertexBuiltIn = ioVariable;
ioVariable = IoVariable.Position; ioVariable = IoVariable.Position;
+1 -1
View File
@@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Vulkan
string[] searchPaths = string[] searchPaths =
[ [
Path.Combine(contentsDir, "Resources", "vulkan", "icd.d", fileName), Path.Combine(contentsDir, "Resources", "vulkan", "icd.d", fileName),
Path.Combine(contentsDir, fileName) Path.Combine(contentsDir, "vulkan", "icd.d", fileName)
]; ];
foreach (string icdPath in searchPaths) foreach (string icdPath in searchPaths)
@@ -26,12 +26,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
{ {
MemoryArrange.MemoryArrange4GiB or MemoryArrange.MemoryArrange4GiB or
MemoryArrange.MemoryArrange4GiBSystemDev or MemoryArrange.MemoryArrange4GiBSystemDev or
MemoryArrange.MemoryArrange6GiBAppletDev => 3173 * MiB, MemoryArrange.MemoryArrange6GiBAppletDev => 3152 * MiB,
MemoryArrange.MemoryArrange4GiBAppletDev => 2048 * MiB, MemoryArrange.MemoryArrange4GiBAppletDev => 2048 * MiB,
MemoryArrange.MemoryArrange6GiB => 4804 * MiB, MemoryArrange.MemoryArrange6GiB => 4783 * MiB,
MemoryArrange.MemoryArrange8GiB => 6852 * MiB, MemoryArrange.MemoryArrange8GiB => 6831 * MiB,
MemoryArrange.MemoryArrange10GiB => 8900 * MiB, MemoryArrange.MemoryArrange10GiB => 8879 * MiB,
MemoryArrange.MemoryArrange12GiB => 10948 * MiB, MemoryArrange.MemoryArrange12GiB => 10927 * MiB,
_ => throw new ArgumentException($"Invalid memory arrange \"{arrange}\"."), _ => throw new ArgumentException($"Invalid memory arrange \"{arrange}\"."),
}; };
} }
@@ -15,6 +15,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
private readonly Dictionary<ulong, List<KThread>> _condVarThreads; private readonly Dictionary<ulong, List<KThread>> _condVarThreads;
private readonly Dictionary<ulong, List<KThread>> _arbiterThreads; private readonly Dictionary<ulong, List<KThread>> _arbiterThreads;
private readonly ByDynamicPriority _byDynamicPriority;
public KAddressArbiter(KernelContext context) public KAddressArbiter(KernelContext context)
{ {
@@ -22,6 +23,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
_condVarThreads = []; _condVarThreads = [];
_arbiterThreads = []; _arbiterThreads = [];
_byDynamicPriority = new ByDynamicPriority();
} }
public Result ArbitrateLock(int ownerHandle, ulong mutexAddress, int requesterHandle) public Result ArbitrateLock(int ownerHandle, ulong mutexAddress, int requesterHandle)
@@ -140,9 +142,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
if (_condVarThreads.TryGetValue(condVarAddress, out List<KThread> threads)) if (_condVarThreads.TryGetValue(condVarAddress, out List<KThread> threads))
{ {
int i = FindDynamicPriorityFifoInsertionIndex(threads, currentThread); int i = 0;
if (threads.Count > 0)
{
i = threads.BinarySearch(currentThread, _byDynamicPriority);
if (i < 0) i = ~i;
}
threads.Insert(i, currentThread); threads.Insert(i, currentThread);
} }
else else
@@ -325,9 +332,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
if (_arbiterThreads.TryGetValue(address, out List<KThread> threads)) if (_arbiterThreads.TryGetValue(address, out List<KThread> threads))
{ {
int i = FindDynamicPriorityFifoInsertionIndex(threads, currentThread); int i = 0;
if (threads.Count > 0)
{
i = threads.BinarySearch(currentThread, _byDynamicPriority);
if (i < 0) i = ~i;
}
threads.Insert(i, currentThread); threads.Insert(i, currentThread);
} }
else else
@@ -412,9 +424,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
if (_arbiterThreads.TryGetValue(address, out List<KThread> threads)) if (_arbiterThreads.TryGetValue(address, out List<KThread> threads))
{ {
int i = FindDynamicPriorityFifoInsertionIndex(threads, currentThread); int i = 0;
if (threads.Count > 0)
{
i = threads.BinarySearch(currentThread, _byDynamicPriority);
if (i < 0) i = ~i;
}
threads.Insert(i, currentThread); threads.Insert(i, currentThread);
} }
else else
@@ -610,28 +627,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
return validCount; return validCount;
} }
private static int FindDynamicPriorityFifoInsertionIndex(List<KThread> threads, KThread currentThread) private class ByDynamicPriority : IComparer<KThread>
{ {
int low = 0; public int Compare(KThread x, KThread y)
int high = threads.Count;
// Lower numeric values represent higher priorities. Use upper-bound insertion
// to preserve FIFO order among waiters with the same dynamic priority.
while (low < high)
{ {
int middle = low + ((high - low) >> 1); return x!.DynamicPriority.CompareTo(y!.DynamicPriority);
if (threads[middle].DynamicPriority <= currentThread.DynamicPriority)
{
low = middle + 1;
}
else
{
high = middle;
}
} }
return low;
} }
} }
} }
+2 -22
View File
@@ -783,28 +783,8 @@ namespace Ryujinx.HLE.HOS
} }
List<Cheat> cheats = mods.Cheats; List<Cheat> cheats = mods.Cheats;
Dictionary<string, ulong> processExes = new(); Dictionary<string, ulong> processExes = tamperInfo.BuildIds.Zip(tamperInfo.CodeAddresses, (k, v) => new { k, v })
.ToDictionary(x => x.k[..Math.Min(Cheat.CheatIdSize, x.k.Length)], x => x.v);
foreach ((string buildId, ulong codeAddress) in tamperInfo.BuildIds.Zip(tamperInfo.CodeAddresses))
{
string normalizedBuildId = buildId[..Math.Min(Cheat.CheatIdSize, buildId.Length)];
if (processExes.TryGetValue(normalizedBuildId, out ulong existingAddress))
{
if (existingAddress != codeAddress)
{
Logger.Warning?.Print(
LogClass.ModLoader,
$"Duplicate BuildId prefix '{normalizedBuildId}' has different code addresses. " +
$"Existing: 0x{existingAddress:X}, duplicate: 0x{codeAddress:X}. " +
$"Keeping the first one.");
}
continue;
}
processExes.Add(normalizedBuildId, codeAddress);
}
foreach (Cheat cheat in cheats) foreach (Cheat cheat in cheats)
{ {
@@ -71,14 +71,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
return _applicationServiceServer.IsUserRegistrationRequestPermitted(context); return _applicationServiceServer.IsUserRegistrationRequestPermitted(context);
} }
[CommandCmif(51)] // 1.0.0 – 18.1.0 [CommandCmif(51)]
// TrySelectUserWithoutInteraction(bool) -> nn::account::Uid
public ResultCode TrySelectUserWithoutInteractionDeprecated(ServiceCtx context)
{
return _applicationServiceServer.TrySelectUserWithoutInteraction(context);
}
[CommandCmif(52)] // 19.0.0+
// TrySelectUserWithoutInteraction(bool) -> nn::account::Uid // TrySelectUserWithoutInteraction(bool) -> nn::account::Uid
public ResultCode TrySelectUserWithoutInteraction(ServiceCtx context) public ResultCode TrySelectUserWithoutInteraction(ServiceCtx context)
{ {
@@ -23,7 +23,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
private readonly bool[] _supportedPlayers; private readonly bool[] _supportedPlayers;
private VibrationValue _neutralVibrationValue = new() private VibrationValue _neutralVibrationValue = new()
{ {
AmplitudeLow = 0.01f, AmplitudeLow = 0f,
FrequencyLow = 160f, FrequencyLow = 160f,
AmplitudeHigh = 0f, AmplitudeHigh = 0f,
FrequencyHigh = 320f, FrequencyHigh = 320f,
+70 -207
View File
@@ -1,4 +1,3 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Hid; using Ryujinx.HLE.HOS.Services.Hid;
using SDL; using SDL;
using static SDL.SDL3; using static SDL.SDL3;
@@ -13,276 +12,140 @@ namespace Ryujinx.Input.SDL3
{ {
private readonly SDL_hid_device* _hidHandle; private readonly SDL_hid_device* _hidHandle;
private byte[] _buffer;
private static ushort _vendor;
private static ushort _product;
private int _globalCount; private int _globalCount;
private ulong _lastWriteTicks;
private NpadHdRumble(SDL_hid_device* hidHandle) private NpadHdRumble(SDL_hid_device* hidHandle)
{ {
_hidHandle = hidHandle; _hidHandle = hidHandle;
InitializeDevice();
} }
public static NpadHdRumble Create(SDL_Gamepad* gamepadHandle) public static NpadHdRumble Create(SDL_Gamepad* gamepadHandle)
{ {
_vendor = SDL_GetGamepadVendor(gamepadHandle); ushort vendor = SDL_GetGamepadVendor(gamepadHandle);
if (!Enum.IsDefined(typeof(HDRumbleSupportedVendor), _vendor)) if (vendor != 0x057e)
{ {
return null; return null;
} }
_product = SDL_GetGamepadProduct(gamepadHandle); ushort product = SDL_GetGamepadProduct(gamepadHandle);
if (!Enum.IsDefined(typeof(HDRumbleSupportedProduct), _product)) if (product != 0x2006 && product != 0x2007 && product != 0x2009 && product != 0x200e)
{ {
return null; return null;
} }
int serialNumber = 0; return new NpadHdRumble(SDL_hid_open(vendor, product, 0));
string? serial = SDL_GetGamepadSerial(gamepadHandle);
if (serial is not null)
{
int.TryParse(serial, out serialNumber);
}
return new NpadHdRumble(SDL_hid_open(_vendor, _product, serialNumber));
} }
// Some of the code was translated from https://github.com/MIZUSHIKI/JoyShockLibrary-plus-HDRumble // Some of the code was translated from https://github.com/MIZUSHIKI/JoyShockLibrary-plus-HDRumble
private bool WriteNintendoHdRumble(VibrationValue left, VibrationValue right) private void WriteHdRumble(
int encLeftLowFreq, int encLeftLowAmp,
int encLeftHighFreq, int encLeftHighAmp,
int encRightLowFreq, int encRightLowAmp,
int encRightHighFreq, int encRightHighAmp)
{ {
int leftLowAmp = EncodeLowAmp(left.AmplitudeLow); byte[] buf = new byte[10];
int leftLowFreq = EncodeLowFreq(left.FrequencyLow) + (leftLowAmp >> 8);
int leftHighFreq = EncodeHighFreq(left.FrequencyHigh); buf[0] = 0x10;
int leftHighAmp = EncodeHighAmp(left.AmplitudeHigh) + (leftHighFreq >> 8); buf[1] = (byte)((++_globalCount) & 0xF);
int rightLowAmp = EncodeLowAmp(right.AmplitudeLow); buf[2] = (byte)(encLeftHighFreq & 0xFF);
int rightLowFreq = EncodeLowFreq(right.FrequencyLow) + (rightLowAmp >> 8); buf[3] = (byte)(encLeftHighAmp + ((encLeftHighFreq >> 8) & 0xFF));
int rightHighFreq = EncodeHighFreq(right.FrequencyHigh); buf[4] = (byte)(encLeftLowFreq + ((encLeftLowAmp >> 8) & 0xFF));
int rightHighAmp = EncodeHighAmp(right.AmplitudeHigh) + (rightHighFreq >> 8); buf[5] = (byte)(encLeftLowAmp & 0xFF);
_buffer[0] = 0x10; buf[6] = (byte)(encRightHighFreq & 0xFF);
_buffer[1] = (byte)((_globalCount++) & 0xF); buf[7] = (byte)(encRightHighAmp + ((encRightHighFreq >> 8) & 0xFF));
buf[8] = (byte)(encRightLowFreq + ((encRightLowAmp >> 8) & 0xFF));
// Left LRA buf[9] = (byte)(encRightLowAmp & 0xFF);
_buffer[2] = (byte)(leftLowFreq & 0xFF);
_buffer[3] = (byte)(leftHighAmp & 0xFF);
_buffer[4] = (byte)(leftHighFreq & 0xFF);
_buffer[5] = (byte)(leftLowAmp & 0xFF);
// Right LRA
_buffer[6] = (byte)(rightLowFreq & 0xFF);
_buffer[7] = (byte)(rightHighAmp & 0xFF);
_buffer[8] = (byte)(rightHighFreq & 0xFF);
_buffer[9] = (byte)(rightLowAmp & 0xFF);
if (_globalCount > 0xF) if (_globalCount > 0xF)
{ {
_globalCount = 0x0; _globalCount = 0x0;
} }
fixed (byte* ptr = _buffer) fixed (byte* ptr = buf)
{ {
if (SendHdRumble(ptr, (nuint)_buffer.Length) >= 0) SDL_hid_write(_hidHandle, ptr, (nuint)buf.Length);
{
return true;
}
Logger.Error?.PrintMsg(LogClass.Hid, SDL_GetError());
SDL_ClearError();
} }
return false;
} }
private static int EncodeLowFreq(float lowFreq) private static int EncodeLowFreq(float lowFreq)
{ {
return (int)Math.Clamp(32 * Math.Log2(lowFreq * 0.1f) - 0x40, 81.75177f, 1252.572266f); float lf = Math.Clamp(lowFreq, 40.875885f, 626.286133f);
return (int)Math.Round(32 * Math.Log2(lf * 0.1f)) - 0x40;
} }
private static int EncodeHighFreq(float highFreq) private static int EncodeHighFreq(float highFreq)
{ {
return (int)Math.Clamp(32 * Math.Log2(highFreq * 0.1f) - 0x60, 81.75177f, 1252.572266f); float hf = Math.Clamp(highFreq, 81.75177f, 1252.572266f);
return ((int)Math.Round(32 * Math.Log2(hf * 0.1f)) - 0x60) * 4;
} }
private static int EncodeLowAmp(float rawAmp) private static int EncodeLowAmp(float rawAmp)
{ {
double encodedAmp = 0; int encodedAmp = 0;
if (rawAmp is > 0 and < 0.012f) if (rawAmp is > 0 and < 0.012f)
{
encodedAmp = 1; encodedAmp = 1;
}
else if (rawAmp is >= 0.012f and < 0.112f) else if (rawAmp is >= 0.012f and < 0.112f)
encodedAmp = 4 * Math.Log2(rawAmp * 110f); {
encodedAmp = (int)Math.Round(4 * Math.Log2(rawAmp * 110f));
}
else if (rawAmp is >= 0.112f and < 0.225f) else if (rawAmp is >= 0.112f and < 0.225f)
encodedAmp = 16 * Math.Log2(rawAmp * 17f); {
encodedAmp = (int)Math.Round(16 * Math.Log2(rawAmp * 17f));
}
else if (rawAmp is >= 0.225f and <= 1f) else if (rawAmp is >= 0.225f and <= 1f)
encodedAmp = 32 * Math.Log2(rawAmp * 8.7f); {
encodedAmp = (int)Math.Round(32 * Math.Log2(rawAmp * 8.7f));
encodedAmp = Math.Round((encodedAmp / 2.0) + 64.0); }
encodedAmp = Math.Clamp(encodedAmp, 0.0, 100.2867);
return (int)Math.Round(encodedAmp); return (int)Math.Floor(encodedAmp / 2.0) + 64;
} }
private static int EncodeHighAmp(float rawAmp) private static int EncodeHighAmp(float rawAmp)
{ {
double encodedAmp = 0; int encodedAmp = 0;
if (rawAmp is > 0 and < 0.012f) if (rawAmp is > 0 and < 0.012f)
{
encodedAmp = 1; encodedAmp = 1;
}
else if (rawAmp is >= 0.012f and < 0.112f) else if (rawAmp is >= 0.012f and < 0.112f)
encodedAmp = 4 * Math.Log2(rawAmp * 110f); {
encodedAmp = (int)Math.Round(4 * Math.Log2(rawAmp * 110f));
}
else if (rawAmp is >= 0.112f and < 0.225f) else if (rawAmp is >= 0.112f and < 0.225f)
encodedAmp = 16 * Math.Log2(rawAmp * 17f); {
encodedAmp = (int)Math.Round(16 * Math.Log2(rawAmp * 17f));
}
else if (rawAmp is >= 0.225f and <= 1f) else if (rawAmp is >= 0.225f and <= 1f)
encodedAmp = 32 * Math.Log2(rawAmp * 8.7f); {
encodedAmp = (int)Math.Round(32 * Math.Log2(rawAmp * 8.7f));
encodedAmp = Math.Round(encodedAmp / 2.0); }
encodedAmp = Math.Clamp(encodedAmp, 0.0, 100.2867);
return (int)encodedAmp; return encodedAmp * 2;
} }
public bool HdRumble(VibrationValue left, VibrationValue right) public bool HdRumble(VibrationValue left, VibrationValue right)
{ {
if(_product is (ushort) HDRumbleSupportedProduct.ProController WriteHdRumble(EncodeLowFreq(left.FrequencyLow),
or (ushort) HDRumbleSupportedProduct.JoyconLeft EncodeLowAmp(left.AmplitudeLow),
or (ushort) HDRumbleSupportedProduct.JoyconRight EncodeHighFreq(left.FrequencyHigh),
or (ushort) HDRumbleSupportedProduct.JoyconPair EncodeHighAmp(left.AmplitudeHigh),
or (ushort) HDRumbleSupportedProduct.JoyconGrip) EncodeLowFreq(right.FrequencyLow),
{ EncodeLowAmp(right.AmplitudeLow),
return WriteNintendoHdRumble(left, right); EncodeHighFreq(right.FrequencyHigh),
} EncodeHighAmp(right.AmplitudeHigh));
return true;
return false;
}
private int SendHdRumble(byte* data, nuint length)
{
int result = 0;
ulong currentTicks = SDL_GetTicks();
// Ditch rumble if we haven't hit the poll-rate yet.
if ((currentTicks - _lastWriteTicks) <= GetPollRate())
{
return result;
}
result = SDL_hid_write(_hidHandle, data, length);
if (result >= 0)
{
_lastWriteTicks = currentTicks;
}
return result;
}
private void InitializeDevice()
{
if (_vendor is (ushort)HDRumbleSupportedVendor.Nintendo)
{
_buffer = new byte[10];
byte[] init = new byte[64];
// Pro Controller and Charge Grip
if (_product
is (ushort)HDRumbleSupportedProduct.ProController
or (ushort)HDRumbleSupportedProduct.JoyconGrip)
{
SDL_LockJoysticks();
fixed (byte* ptr = init)
{
init[0] = 0x80;
init[1] = 0x05; // Allow bluetooth timeout TODO: use 0x04 to force USB only (toggle?)
SDL_hid_write(_hidHandle, ptr, 64);
}
SDL_UnlockJoysticks();
return;
}
// Joycons
if (_product
is (ushort)HDRumbleSupportedProduct.JoyconLeft
or (ushort)HDRumbleSupportedProduct.JoyconRight
or (ushort)HDRumbleSupportedProduct.JoyconPair)
{
SDL_LockJoysticks();
fixed (byte* ptr = init)
{
// we could write data to the controller here (see above)
}
SDL_UnlockJoysticks();
return;
}
}
}
private ulong GetPollRate()
{
ulong pollRate = 0;
if (_vendor is (ushort)HDRumbleSupportedVendor.Nintendo)
{
// https://docs.handheldlegend.com/s/progcc-3/doc/lag-comparison-aAR1mV3JLX
pollRate = (ulong) 16.67;
if (_product is (ushort)HDRumbleSupportedProduct.ProController
&& SDL_hid_get_device_info(_hidHandle)->bus_type == SDL_hid_bus_type.SDL_HID_API_BUS_USB)
{
pollRate = (ulong) 8.33;
}
}
return pollRate;
} }
public void Dispose() public void Dispose()
{ {
GC.SuppressFinalize(this);
SDL_hid_close(_hidHandle); SDL_hid_close(_hidHandle);
} }
} }
public enum HDRumbleSupportedVendor : ushort
{
Nintendo = 0x057e,
Valve = 0x28de,
Sony = 0x054c
}
public enum HDRumbleSupportedProduct : ushort
{
// TODO: Currently, HD Rumble only supports the Pro Controller and JoyCons.
// We need to initialize and report to each device differently.
// Nintendo Switch: 0x057e
JoyconLeft = 0x2006,
JoyconRight = 0x2007,
JoyconPair = 0x2008,
ProController = 0x2009,
JoyconGrip = 0x200e,
// Nintendo Switch 2: 0x057e
Joycon2Right = 0x2066,
Joycon2Left = 0x2067,
Joycon2Pair = 0x2068,
Switch2ProController = 0x2069,
GamecubeController = 0x2073,
// Valve Steam Family: 0x28de
// https://github.com/libsdl-org/SDL/issues/9148
SteamDeck = 0x11ff,
SteamDeckVirtualDevice = 0x1205,
SteamController = 0x1106,
// PlayStation Dualsense: 0x054c
Dualsense = 0x0ce6
}
} }
+2 -13
View File
@@ -177,12 +177,10 @@ namespace Ryujinx.Input.SDL3
return _hdRumble?.HdRumble(left, right) ?? false; return _hdRumble?.HdRumble(left, right) ?? false;
} }
public bool Rumble(float lowFrequency, float highFrequency, uint durationMs) public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
{ {
if ((Features & GamepadFeaturesFlag.Rumble) == 0) if ((Features & GamepadFeaturesFlag.Rumble) == 0)
{ return;
return false;
}
ushort lowFrequencyRaw = (ushort)(lowFrequency * ushort.MaxValue); ushort lowFrequencyRaw = (ushort)(lowFrequency * ushort.MaxValue);
ushort highFrequencyRaw = (ushort)(highFrequency * ushort.MaxValue); ushort highFrequencyRaw = (ushort)(highFrequency * ushort.MaxValue);
@@ -201,15 +199,6 @@ namespace Ryujinx.Input.SDL3
if (!SDL_RumbleGamepad(_gamepadHandle, lowFrequencyRaw, highFrequencyRaw, durationMs)) if (!SDL_RumbleGamepad(_gamepadHandle, lowFrequencyRaw, highFrequencyRaw, durationMs))
Logger.Error?.Print(LogClass.Hid, "Rumble is not supported on this game controller."); Logger.Error?.Print(LogClass.Hid, "Rumble is not supported on this game controller.");
} }
if (!String.IsNullOrEmpty(SDL_GetError()))
{
Logger.Error?.PrintMsg(LogClass.Hid, SDL_GetError());
SDL_ClearError();
return false;
}
return true;
} }
public Vector3 GetMotionData(MotionInputId inputId) public Vector3 GetMotionData(MotionInputId inputId)
+3 -14
View File
@@ -163,7 +163,7 @@ namespace Ryujinx.Input.SDL3
public void SetTriggerThreshold(float triggerThreshold) public void SetTriggerThreshold(float triggerThreshold)
{ {
// No operations
} }
public bool HDRumble(VibrationValue left, VibrationValue right) public bool HDRumble(VibrationValue left, VibrationValue right)
@@ -171,12 +171,10 @@ namespace Ryujinx.Input.SDL3
return _hdRumble?.HdRumble(left, right) ?? false; return _hdRumble?.HdRumble(left, right) ?? false;
} }
public bool Rumble(float lowFrequency, float highFrequency, uint durationMs) public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
{ {
if ((Features & GamepadFeaturesFlag.Rumble) == 0) if ((Features & GamepadFeaturesFlag.Rumble) == 0)
{ return;
return false;
}
ushort lowFrequencyRaw = (ushort)(lowFrequency * ushort.MaxValue); ushort lowFrequencyRaw = (ushort)(lowFrequency * ushort.MaxValue);
ushort highFrequencyRaw = (ushort)(highFrequency * ushort.MaxValue); ushort highFrequencyRaw = (ushort)(highFrequency * ushort.MaxValue);
@@ -195,15 +193,6 @@ namespace Ryujinx.Input.SDL3
if (!SDL_RumbleGamepad(_gamepadHandle, lowFrequencyRaw, highFrequencyRaw, durationMs)) if (!SDL_RumbleGamepad(_gamepadHandle, lowFrequencyRaw, highFrequencyRaw, durationMs))
Logger.Error?.Print(LogClass.Hid, "Rumble is not supported on this game controller."); Logger.Error?.Print(LogClass.Hid, "Rumble is not supported on this game controller.");
} }
if (!String.IsNullOrEmpty(SDL_GetError()))
{
Logger.Error?.PrintMsg(LogClass.Hid, SDL_GetError());
SDL_ClearError();
return false;
}
return true;
} }
public Vector3 GetMotionData(MotionInputId inputId) public Vector3 GetMotionData(MotionInputId inputId)
+2 -21
View File
@@ -1,7 +1,4 @@
using Gommon; using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Hid;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
@@ -64,14 +61,7 @@ namespace Ryujinx.Input.SDL3
return left.IsPressed(inputId) || right.IsPressed(inputId); return left.IsPressed(inputId) || right.IsPressed(inputId);
} }
public bool HDRumble(VibrationValue left, VibrationValue right) public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
{
// return _hdRumble?.HdRumble(left, right) ?? false;
// TODO: Track rumble and motion on both controllers
return false;
}
public bool Rumble(float lowFrequency, float highFrequency, uint durationMs)
{ {
if (lowFrequency != 0) if (lowFrequency != 0)
{ {
@@ -88,15 +78,6 @@ namespace Ryujinx.Input.SDL3
left.Rumble(0, 0, durationMs); left.Rumble(0, 0, durationMs);
right.Rumble(0, 0, durationMs); right.Rumble(0, 0, durationMs);
} }
if (!SDL_GetError().IsNullOrEmpty())
{
Logger.Error?.PrintMsg(LogClass.Hid, SDL_GetError());
SDL_ClearError();
return false;
}
return true;
} }
public void SetConfiguration(InputConfig configuration) public void SetConfiguration(InputConfig configuration)
+2 -8
View File
@@ -1,7 +1,6 @@
using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid.Keyboard; using Ryujinx.Common.Configuration.Hid.Keyboard;
using SDL; using SDL;
using Ryujinx.HLE.HOS.Services.Hid;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Numerics; using System.Numerics;
@@ -386,14 +385,9 @@ namespace Ryujinx.Input.SDL3
// No operations // No operations
} }
public bool HDRumble(VibrationValue left, VibrationValue right) public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
{ {
return false; // No operations
}
public bool Rumble(float lowFrequency, float highFrequency, uint durationMs)
{
return false;
} }
public Vector3 GetMotionData(MotionInputId inputId) public Vector3 GetMotionData(MotionInputId inputId)
+1 -7
View File
@@ -1,5 +1,4 @@
using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid;
using Ryujinx.HLE.HOS.Services.Hid;
using System; using System;
using System.Drawing; using System.Drawing;
using System.Numerics; using System.Numerics;
@@ -67,12 +66,7 @@ namespace Ryujinx.Input.SDL3
throw new NotImplementedException(); throw new NotImplementedException();
} }
public bool HDRumble(VibrationValue left, VibrationValue right) public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
{
throw new NotImplementedException();
}
public bool Rumble(float lowFrequency, float highFrequency, uint durationMs)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
+26 -29
View File
@@ -554,37 +554,34 @@ namespace Ryujinx.Input.HLE
{ {
if (queue.TryDequeue(out (VibrationValue, VibrationValue) dualVibrationValue)) if (queue.TryDequeue(out (VibrationValue, VibrationValue) dualVibrationValue))
{ {
if (_config is not StandardControllerInputConfig controllerConfig || if (_config is StandardControllerInputConfig controllerConfig && controllerConfig.Rumble.EnableRumble)
!controllerConfig.Rumble.EnableRumble)
{ {
return; VibrationValue leftVibrationValue = dualVibrationValue.Item1;
VibrationValue rightVibrationValue = dualVibrationValue.Item2;
float low = Math.Min(1f, (float)((rightVibrationValue.AmplitudeLow * 0.85 + rightVibrationValue.AmplitudeHigh * 0.15) * controllerConfig.Rumble.StrongRumble));
float high = Math.Min(1f, (float)((leftVibrationValue.AmplitudeLow * 0.15 + leftVibrationValue.AmplitudeHigh * 0.85) * controllerConfig.Rumble.WeakRumble));
leftVibrationValue.AmplitudeLow *= controllerConfig.Rumble.WeakRumble;
leftVibrationValue.AmplitudeHigh *= controllerConfig.Rumble.StrongRumble;
rightVibrationValue.AmplitudeLow *= controllerConfig.Rumble.WeakRumble;
rightVibrationValue.AmplitudeHigh *= controllerConfig.Rumble.StrongRumble;
if (_gamepad?.HDRumble(leftVibrationValue, rightVibrationValue) == false)
{
_gamepad.Rumble(low, high, uint.MaxValue);
}
Logger.Debug?.Print(LogClass.Hid, $"Effect for {controllerConfig.PlayerIndex} " +
$"L.low.amp={leftVibrationValue.AmplitudeLow}, " +
$"L.high.amp={leftVibrationValue.AmplitudeHigh}, " +
$"L.low.freq={leftVibrationValue.FrequencyLow}, " +
$"L.high.freq={leftVibrationValue.FrequencyHigh}, " +
$"R.low.amp={rightVibrationValue.AmplitudeLow}, " +
$"R.high.amp={rightVibrationValue.AmplitudeHigh} " +
$"R.low.freq={rightVibrationValue.FrequencyLow}, " +
$"R.high.freq={rightVibrationValue.FrequencyHigh}");
} }
VibrationValue leftVibrationValue = dualVibrationValue.Item1;
VibrationValue rightVibrationValue = dualVibrationValue.Item2;
leftVibrationValue.AmplitudeLow *= controllerConfig.Rumble.WeakRumble;
leftVibrationValue.AmplitudeHigh *= controllerConfig.Rumble.StrongRumble;
rightVibrationValue.AmplitudeLow *= controllerConfig.Rumble.WeakRumble;
rightVibrationValue.AmplitudeHigh *= controllerConfig.Rumble.StrongRumble;
if (!controllerConfig.Rumble.UseHDRumble || _gamepad?.HDRumble(leftVibrationValue, rightVibrationValue) == false)
{
float low = Math.Min(1f, (float)((rightVibrationValue.AmplitudeLow * 0.85 + rightVibrationValue.AmplitudeHigh * 0.15)));
float high = Math.Min(1f, (float)((leftVibrationValue.AmplitudeLow * 0.15 + leftVibrationValue.AmplitudeHigh * 0.85)));
_gamepad.Rumble(low, high, 0xFFFFFFFF);
}
Logger.Debug?.Print(LogClass.Hid, $"Effect for {controllerConfig.PlayerIndex} " +
// Value=value/multiplier * multiplier (result)
$"L.low.amp={leftVibrationValue.AmplitudeLow / controllerConfig.Rumble.WeakRumble} * {controllerConfig.Rumble.WeakRumble} ({leftVibrationValue.AmplitudeLow}), " +
$"L.high.amp={leftVibrationValue.AmplitudeHigh / controllerConfig.Rumble.StrongRumble} * {controllerConfig.Rumble.StrongRumble} ({leftVibrationValue.AmplitudeHigh}), " +
$"L.low.freq={leftVibrationValue.FrequencyLow / controllerConfig.Rumble.WeakRumble} * {controllerConfig.Rumble.WeakRumble} ({leftVibrationValue.FrequencyLow}), " +
$"L.high.freq={leftVibrationValue.FrequencyHigh / controllerConfig.Rumble.StrongRumble} * {controllerConfig.Rumble.StrongRumble} ({leftVibrationValue.FrequencyHigh}), " +
$"R.low.amp={rightVibrationValue.AmplitudeLow / controllerConfig.Rumble.WeakRumble} * {controllerConfig.Rumble.WeakRumble} ({rightVibrationValue.AmplitudeLow}), " +
$"R.high.amp={rightVibrationValue.AmplitudeHigh / controllerConfig.Rumble.StrongRumble} * {controllerConfig.Rumble.StrongRumble} ({rightVibrationValue.AmplitudeHigh}), " +
$"R.low.freq={rightVibrationValue.FrequencyLow / controllerConfig.Rumble.WeakRumble} * {controllerConfig.Rumble.WeakRumble} ({rightVibrationValue.FrequencyLow}), " +
$"R.high.freq={rightVibrationValue.FrequencyHigh / controllerConfig.Rumble.StrongRumble} * {controllerConfig.Rumble.StrongRumble} ({rightVibrationValue.FrequencyHigh})");
} }
} }
} }
+6 -3
View File
@@ -71,7 +71,10 @@ namespace Ryujinx.Input
/// </summary> /// </summary>
/// <param name="left">The vibration data for the left side</param> /// <param name="left">The vibration data for the left side</param>
/// <param name="right">The vibration data for the right side</param> /// <param name="right">The vibration data for the right side</param>
bool HDRumble(VibrationValue left, VibrationValue right); bool HDRumble(VibrationValue left, VibrationValue right)
{
return false;
}
/// <summary> /// <summary>
/// Starts a rumble effect on the gamepad. /// Starts a rumble effect on the gamepad.
@@ -79,10 +82,10 @@ namespace Ryujinx.Input
/// <param name="lowFrequency">The intensity of the low frequency from 0.0f to 1.0f</param> /// <param name="lowFrequency">The intensity of the low frequency from 0.0f to 1.0f</param>
/// <param name="highFrequency">The intensity of the high frequency from 0.0f to 1.0f</param> /// <param name="highFrequency">The intensity of the high frequency from 0.0f to 1.0f</param>
/// <param name="durationMs">The duration of the rumble effect in milliseconds.</param> /// <param name="durationMs">The duration of the rumble effect in milliseconds.</param>
bool Rumble(float lowFrequency, float highFrequency, uint durationMs); void Rumble(float lowFrequency, float highFrequency, uint durationMs);
/// <summary> /// <summary>
/// Get a snaphost of the state of the gamepad that is remapped with the information from the <see cref="InputConfig"/> set via <see cref="SetConfiguration(InputConfig)"/>. /// Get a snaphost of the state of the gamepad that is remapped with the informations from the <see cref="InputConfig"/> set via <see cref="SetConfiguration(InputConfig)"/>.
/// </summary> /// </summary>
/// <returns>A remapped snaphost of the state of the gamepad.</returns> /// <returns>A remapped snaphost of the state of the gamepad.</returns>
GamepadStateSnapshot GetMappedStateSnapshot(); GamepadStateSnapshot GetMappedStateSnapshot();
@@ -16,7 +16,7 @@ namespace Ryujinx.UI.Common.Configuration
/// <summary> /// <summary>
/// The current version of the file format /// The current version of the file format
/// </summary> /// </summary>
public const int CurrentVersion = 61; public const int CurrentVersion = 62;
/// <summary> /// <summary>
/// Version of the configuration file format /// Version of the configuration file format
@@ -1431,7 +1431,6 @@ namespace Ryujinx.UI.Common.Configuration
EnableRumble = false, EnableRumble = false,
StrongRumble = 1f, StrongRumble = 1f,
WeakRumble = 1f, WeakRumble = 1f,
UseHDRumble = false
}; };
} }
} }
-2
View File
@@ -464,8 +464,6 @@
"ControllerSettingsRumble": "Rumble", "ControllerSettingsRumble": "Rumble",
"ControllerSettingsRumbleStrongMultiplier": "Strong Rumble Multiplier", "ControllerSettingsRumbleStrongMultiplier": "Strong Rumble Multiplier",
"ControllerSettingsRumbleWeakMultiplier": "Weak Rumble Multiplier", "ControllerSettingsRumbleWeakMultiplier": "Weak Rumble Multiplier",
"ControllerSettingsRumbleUseHDRumble": "Enable HD Rumble",
"HDRumbleTooltip": "EXPERIMENTAL.\n\nSends more data to the controller for better rumble.\n\nCurrently only supports first-party Nintendo Switch controllers.\n\nLeave OFF if unsure.",
"DialogMessageSaveNotAvailableMessage": "There is no savedata for {0} [{1:x16}]", "DialogMessageSaveNotAvailableMessage": "There is no savedata for {0} [{1:x16}]",
"DialogMessageSaveNotAvailableCreateSaveMessage": "Would you like to create savedata for this game?", "DialogMessageSaveNotAvailableCreateSaveMessage": "Would you like to create savedata for this game?",
"DialogConfirmationTitle": "Ryujinx - Confirmation", "DialogConfirmationTitle": "Ryujinx - Confirmation",
@@ -223,7 +223,6 @@ namespace Ryujinx.Headless
StrongRumble = 1f, StrongRumble = 1f,
WeakRumble = 1f, WeakRumble = 1f,
EnableRumble = false, EnableRumble = false,
UseHDRumble = true
}, },
}; };
} }
+2 -14
View File
@@ -1,6 +1,5 @@
using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid.Keyboard; using Ryujinx.Common.Configuration.Hid.Keyboard;
using Ryujinx.HLE.HOS.Services.Hid;
using Ryujinx.Input; using Ryujinx.Input;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -150,20 +149,9 @@ namespace Ryujinx.Ava.Input
} }
} }
public void SetTriggerThreshold(float triggerThreshold) public void SetTriggerThreshold(float triggerThreshold) { }
{
// No operations
}
public bool HDRumble(VibrationValue left, VibrationValue right) public void Rumble(float lowFrequency, float highFrequency, uint durationMs) { }
{
return false;
}
public bool Rumble(float lowFrequency, float highFrequency, uint durationMs)
{
return false;
}
public Vector3 GetMotionData(MotionInputId inputId) => Vector3.Zero; public Vector3 GetMotionData(MotionInputId inputId) => Vector3.Zero;
+1 -7
View File
@@ -1,5 +1,4 @@
using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid;
using Ryujinx.HLE.HOS.Services.Hid;
using Ryujinx.Input; using Ryujinx.Input;
using System; using System;
using System.Drawing; using System.Drawing;
@@ -64,13 +63,8 @@ namespace Ryujinx.Ava.Input
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public bool HDRumble(VibrationValue left, VibrationValue right)
{
throw new NotImplementedException();
}
public bool Rumble(float lowFrequency, float highFrequency, uint durationMs) public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
+6 -8
View File
@@ -91,17 +91,15 @@
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'osx-arm64' OR '$(RuntimeIdentifier)' == 'osx-x64'"> <ItemGroup Condition="'$(RuntimeIdentifier)' == 'osx-arm64' OR '$(RuntimeIdentifier)' == 'osx-x64'">
<Content Include="..\..\distribution\macos\vulkan\icd.d\MoltenVK_icd-flat.json"> <Content Include="..\..\distribution\macos\vulkan\icd.d\MoltenVK_icd-flat.json"
<CopyToOutputDirectory>Always</CopyToOutputDirectory> CopyToOutputDirectory="PreserveNewest"
<TargetPath>MoltenVK_icd.json</TargetPath> Link="vulkan\icd.d\MoltenVK_icd.json" />
</Content>
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'osx-arm64'"> <ItemGroup Condition="'$(RuntimeIdentifier)' == 'osx-arm64'">
<Content Include="..\..\distribution\macos\vulkan\icd.d\libkosmickrisp_icd-flat.json"> <Content Include="..\..\distribution\macos\vulkan\icd.d\libkosmickrisp_icd-flat.json"
<CopyToOutputDirectory>Always</CopyToOutputDirectory> CopyToOutputDirectory="PreserveNewest"
<TargetPath>libkosmickrisp_icd.json</TargetPath> Link="vulkan\icd.d\libkosmickrisp_icd.json" />
</Content>
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64' OR '$(RuntimeIdentifier)' == 'linux-arm64'" > <ItemGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64' OR '$(RuntimeIdentifier)' == 'linux-arm64'" >
@@ -19,7 +19,6 @@ namespace Ryujinx.Ava.UI.Models.Input
public float WeakRumble { get; set; } public float WeakRumble { get; set; }
public float StrongRumble { get; set; } public float StrongRumble { get; set; }
public bool UseHDRumble { get; set; }
public string Id { get; set; } public string Id { get; set; }
public ControllerType ControllerType { get; set; } public ControllerType ControllerType { get; set; }
@@ -203,7 +202,6 @@ namespace Ryujinx.Ava.UI.Models.Input
EnableRumble = controllerInput.Rumble.EnableRumble; EnableRumble = controllerInput.Rumble.EnableRumble;
WeakRumble = controllerInput.Rumble.WeakRumble; WeakRumble = controllerInput.Rumble.WeakRumble;
StrongRumble = controllerInput.Rumble.StrongRumble; StrongRumble = controllerInput.Rumble.StrongRumble;
UseHDRumble = controllerInput.Rumble.UseHDRumble;
} }
} }
} }
@@ -261,7 +259,6 @@ namespace Ryujinx.Ava.UI.Models.Input
EnableRumble = EnableRumble, EnableRumble = EnableRumble,
WeakRumble = WeakRumble, WeakRumble = WeakRumble,
StrongRumble = StrongRumble, StrongRumble = StrongRumble,
UseHDRumble = UseHDRumble,
}, },
Version = InputConfig.CurrentVersion, Version = InputConfig.CurrentVersion,
DeadzoneLeft = DeadzoneLeft, DeadzoneLeft = DeadzoneLeft,
@@ -678,7 +678,6 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
StrongRumble = 1f, StrongRumble = 1f,
WeakRumble = 1f, WeakRumble = 1f,
EnableRumble = false, EnableRumble = false,
UseHDRumble = false
}, },
}; };
} }
@@ -5,12 +5,9 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
public partial class RumbleInputViewModel : BaseModel public partial class RumbleInputViewModel : BaseModel
{ {
[ObservableProperty] [ObservableProperty]
public partial float StrongRumble { get; set; } private float _strongRumble;
[ObservableProperty] [ObservableProperty]
public partial float WeakRumble { get; set; } private float _weakRumble;
[ObservableProperty]
public partial bool EnableHDRumble { get; set; }
} }
} }
@@ -55,15 +55,6 @@
Margin="5,0" Margin="5,0"
Text="{Binding WeakRumble, StringFormat=\{0:0.00\}}" /> Text="{Binding WeakRumble, StringFormat=\{0:0.00\}}" />
</StackPanel> </StackPanel>
<CheckBox
Margin="5"
IsChecked="{Binding EnableHDRumble}">
<TextBlock
Margin="0,3,0,0"
VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsRumbleUseHDRumble}"
ToolTip.Tip="{locale:Locale HDRumbleTooltip}" />
</CheckBox>
</StackPanel> </StackPanel>
</Grid> </Grid>
</UserControl> </UserControl>
@@ -24,7 +24,6 @@ namespace Ryujinx.Ava.UI.Views.Input
{ {
StrongRumble = config.StrongRumble, StrongRumble = config.StrongRumble,
WeakRumble = config.WeakRumble, WeakRumble = config.WeakRumble,
EnableHDRumble = config.UseHDRumble
}; };
InitializeComponent(); InitializeComponent();
@@ -50,7 +49,6 @@ namespace Ryujinx.Ava.UI.Views.Input
GamepadInputConfig config = viewModel.Config; GamepadInputConfig config = viewModel.Config;
config.StrongRumble = content._viewModel.StrongRumble; config.StrongRumble = content._viewModel.StrongRumble;
config.WeakRumble = content._viewModel.WeakRumble; config.WeakRumble = content._viewModel.WeakRumble;
config.UseHDRumble = content._viewModel.EnableHDRumble;
}; };
await contentDialog.ShowAsync(); await contentDialog.ShowAsync();