Skip to content

Commit d7b1b86

Browse files
authored
FIX: JsonParser / ParseStringValue now creates unescaped strings (#1986)
FIX: Improved performance of disconnected device activation (ISX-1450)
1 parent 66b4ae9 commit d7b1b86

6 files changed

Lines changed: 81 additions & 20 deletions

File tree

Assets/Tests/InputSystem/CoreTests_Devices.cs

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2648,7 +2648,7 @@ public void Devices_DeltaControlsResetBetweenUpdates(string layoutName, string c
26482648
[TestCase("Joystick", typeof(Joystick))]
26492649
[TestCase("Accelerometer", typeof(Accelerometer))]
26502650
[TestCase("Gyroscope", typeof(Gyroscope))]
2651-
public void Devices_CanCreateDevice(string layout, Type type)
2651+
public void Devices_CanCreateDevice(string layout, System.Type type)
26522652
{
26532653
var device = InputSystem.AddDevice(layout);
26542654

@@ -4130,25 +4130,45 @@ public void Devices_RemovingAndReaddingDevice_DoesNotAllocateMemory()
41304130
// Doesn't happen when a native backend reports a device.
41314131
var descriptionJson = description.ToJson();
41324132

4133-
Assert.That(() =>
4134-
{
4135-
Profiler.BeginSample(kProfilerRegion);
4133+
var recorder = Recorder.Get("GC.Alloc");
4134+
// The recorder was created enabled, which means it captured the creation of the Recorder object itself, etc.
4135+
// Disabling it flushes its data, so that we can retrieve the sample block count and have it correctly account
4136+
// for these initial allocations.
4137+
recorder.enabled = false;
4138+
#if !UNITY_WEBGL
4139+
recorder.FilterToCurrentThread();
4140+
#endif
4141+
recorder.enabled = true;
41364142

4137-
// "Plug" it back in.
4138-
deviceId = runtime.ReportNewInputDevice(descriptionJson);
4139-
InputSystem.Update();
4143+
Profiler.BeginSample(kProfilerRegion);
41404144

4141-
// "Unplug" device.
4142-
var removeEvent2 = DeviceRemoveEvent.Create(deviceId);
4143-
InputSystem.QueueEvent(ref removeEvent2);
4144-
InputSystem.Update();
4145+
// "Plug" it back in.
4146+
deviceId = runtime.ReportNewInputDevice(descriptionJson);
4147+
InputSystem.Update();
41454148

4146-
// "Plug" it back in.
4147-
runtime.ReportNewInputDevice(descriptionJson);
4148-
InputSystem.Update();
4149+
// "Unplug" device.
4150+
var removeEvent2 = DeviceRemoveEvent.Create(deviceId);
4151+
InputSystem.QueueEvent(ref removeEvent2);
4152+
InputSystem.Update();
41494153

4150-
Profiler.EndSample();
4151-
}, Is.Not.AllocatingGCMemory());
4154+
// "Plug" it back in.
4155+
runtime.ReportNewInputDevice(descriptionJson);
4156+
InputSystem.Update();
4157+
4158+
Profiler.EndSample();
4159+
4160+
recorder.enabled = false;
4161+
#if !UNITY_WEBGL
4162+
recorder.CollectFromAllThreads();
4163+
#endif
4164+
4165+
// We expect a single allocation for each call to ReportNewInputDevice when there is one disconnected device
4166+
//
4167+
int numberOfRepeats = 2;
4168+
int numberOfDisconnectedDevices = 1;
4169+
int numberOfCallsToReportNewInputDevicePerRun = 2;
4170+
int expectedAllocations = numberOfRepeats * numberOfDisconnectedDevices * numberOfCallsToReportNewInputDevicePerRun;
4171+
Assert.AreEqual(expectedAllocations, recorder.sampleBlockCount);
41524172
}
41534173

41544174
[Test]

Packages/com.unity.inputsystem/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ however, it has to be formatted properly to pass verification tests.
1818
- Fixed Package compilation when Unity Analytics module is not enabled on 2022.3. [ISXB-996](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-996)
1919
- Fixed 'OnDrop' event not called when 'IPointerDownHandler' is also listened. [ISXB-1014](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-1014)
2020
- Fixed InputSystemUIInputModule calling pointer events on parent objects even when the "Send Pointer Hover To Parent" is off. [ISXB-586](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-586)
21+
- Improved performance of disconnected device activation (ISX-1450)
2122

2223
### Changed
2324
- Use `ProfilerMarker` instead of `Profiler.BeginSample` and `Profiler.EndSample` when appropriate to enable recording of profiling data.

Packages/com.unity.inputsystem/InputSystem/Devices/InputDeviceDescription.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -380,14 +380,14 @@ public static InputDeviceDescription FromJson(string json)
380380
};
381381
}
382382

383-
internal static bool ComparePropertyToDeviceDescriptor(string propertyName, string propertyValue, string deviceDescriptor)
383+
internal static bool ComparePropertyToDeviceDescriptor(string propertyName, JsonParser.JsonString propertyValue, string deviceDescriptor)
384384
{
385385
// We use JsonParser instead of JsonUtility.Parse in order to not allocate GC memory here.
386386

387387
var json = new JsonParser(deviceDescriptor);
388388
if (!json.NavigateToProperty(propertyName))
389389
{
390-
if (string.IsNullOrEmpty(propertyValue))
390+
if (propertyValue.text.isEmpty)
391391
return true;
392392
return false;
393393
}

Packages/com.unity.inputsystem/InputSystem/InputManager.cs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2534,6 +2534,45 @@ private void OnNativeDeviceDiscovered(int deviceId, string deviceDescriptor)
25342534
}
25352535
}
25362536

2537+
private JsonParser.JsonString MakeEscapedJsonString(string theString)
2538+
{
2539+
//
2540+
// When we create the device description from the (passed from native) deviceDescriptor string in OnNativeDeviceDiscovered()
2541+
// we remove any escape characters from the capabilties field when we do InputDeviceDescription.FromJson() - this decoded
2542+
// description is used to create the device.
2543+
//
2544+
// This means that the native and managed code can have slightly different representations of the capabilities field.
2545+
//
2546+
// Managed: description.capabilities string, unescaped
2547+
// eg "{"deviceName":"Oculus Quest", ..."
2548+
//
2549+
// Native: deviceDescriptor string, containing a Json encoded "capabilities" name/value pair represented by an escaped Json string
2550+
// eg "{\"deviceName\":\"Oculus Quest\", ..."
2551+
//
2552+
// To avoid a very costly escape-skipping character-by-character string comparison in JsonParser.Json.Equals() we
2553+
// reconstruct an escaped string and make an escaped JsonParser.JsonString and use that for the comparison instead.
2554+
//
2555+
var builder = new StringBuilder();
2556+
var length = theString.Length;
2557+
var hasEscapes = false;
2558+
for (var j = 0; j < length; ++j)
2559+
{
2560+
var ch = theString[j];
2561+
if (ch == '\\' || ch == '\"')
2562+
{
2563+
builder.Append('\\');
2564+
hasEscapes = true;
2565+
}
2566+
builder.Append(ch);
2567+
}
2568+
var jsonStringWithEscapes = new JsonParser.JsonString
2569+
{
2570+
text = builder.ToString(),
2571+
hasEscapes = hasEscapes
2572+
};
2573+
return jsonStringWithEscapes;
2574+
}
2575+
25372576
private InputDevice TryMatchDisconnectedDevice(string deviceDescriptor)
25382577
{
25392578
for (var i = 0; i < m_DisconnectedDevicesCount; ++i)
@@ -2552,7 +2591,7 @@ private InputDevice TryMatchDisconnectedDevice(string deviceDescriptor)
25522591
continue;
25532592
if (!InputDeviceDescription.ComparePropertyToDeviceDescriptor("type", description.deviceClass, deviceDescriptor))
25542593
continue;
2555-
if (!InputDeviceDescription.ComparePropertyToDeviceDescriptor("capabilities", description.capabilities, deviceDescriptor))
2594+
if (!InputDeviceDescription.ComparePropertyToDeviceDescriptor("capabilities", MakeEscapedJsonString(description.capabilities), deviceDescriptor))
25562595
continue;
25572596
if (!InputDeviceDescription.ComparePropertyToDeviceDescriptor("serial", description.serial, deviceDescriptor))
25582597
continue;

Packages/com.unity.inputsystem/InputSystem/Utilities/JsonParser.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ public bool ParseStringValue(out JsonValue result)
260260
else if (ch == '"')
261261
{
262262
++m_Position;
263+
263264
result = new JsonString
264265
{
265266
text = new Substring(m_Text, startIndex, m_Position - startIndex - 1),

Packages/com.unity.inputsystem/InputSystem/Utilities/PrimitiveValue.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,7 @@ public bool ToBoolean(IFormatProvider provider = null)
450450
case TypeCode.Single:
451451
return !Mathf.Approximately(m_FloatValue, default);
452452
case TypeCode.Double:
453-
return NumberHelpers.Approximately(m_DoubleValue, default);
453+
return !NumberHelpers.Approximately(m_DoubleValue, default);
454454
default:
455455
return default;
456456
}

0 commit comments

Comments
 (0)