Reduced input lag by implementing overlapped IO and changing threading

This commit is contained in:
Travis Nickles 2017-03-22 00:52:54 -07:00
parent 0dd442f813
commit 5dbc193351
2 changed files with 106 additions and 13 deletions

View File

@ -121,6 +121,7 @@ namespace DS4Windows
private byte[] accel = new byte[6];
private byte[] gyro = new byte[6];
private byte[] inputReport;
private byte[] inputReport2;
private byte[] btInputReport = null;
private byte[] outputReportBuffer, outputReport;
private readonly DS4Touchpad touchpad = null;
@ -134,6 +135,8 @@ namespace DS4Windows
public DateTime lastActive = DateTime.UtcNow;
public DateTime firstActive = DateTime.UtcNow;
private bool charging;
// Use large value for worst case scenario
private static int readStreamTimeout = 100;
public event EventHandler<EventArgs> Report = null;
public event EventHandler<EventArgs> Removal = null;
@ -221,6 +224,7 @@ namespace DS4Windows
if (conType == ConnectionType.USB)
{
inputReport = new byte[64];
inputReport2 = new byte[64];
outputReport = new byte[hDevice.Capabilities.OutputReportByteLength];
outputReportBuffer = new byte[hDevice.Capabilities.OutputReportByteLength];
}
@ -244,7 +248,13 @@ namespace DS4Windows
ds4Output = new Thread(performDs4Output);
ds4Output.Priority = ThreadPriority.Highest;
ds4Output.Name = "DS4 Output thread: " + Mac;
ds4Output.Start();
if (conType == ConnectionType.BT)
{
// Only use the output thread for Bluetooth connections.
// USB will utilize overlapped IO instead.
ds4Output.Start();
}
ds4Input = new Thread(performDs4Input);
ds4Input.Priority = ThreadPriority.Highest;
ds4Input.Name = "DS4 Input thread: " + Mac;
@ -273,7 +283,7 @@ namespace DS4Windows
private void StopOutputUpdate()
{
if (ds4Output.ThreadState != System.Threading.ThreadState.Stopped || ds4Output.ThreadState != System.Threading.ThreadState.Aborted)
if (ds4Output.ThreadState != System.Threading.ThreadState.Unstarted || ds4Output.ThreadState != System.Threading.ThreadState.Stopped || ds4Output.ThreadState != System.Threading.ThreadState.Aborted)
{
try
{
@ -295,7 +305,7 @@ namespace DS4Windows
}
else
{
return hDevice.WriteOutputReportViaInterrupt(outputReport, 8);
return hDevice.WriteAsyncOutputReportViaInterrupt(outputReport);
}
}
@ -303,10 +313,12 @@ namespace DS4Windows
{
lock (outputReport)
{
int lastError = 0;
//int lastError = 0;
while (true)
{
if (writeOutput())
Monitor.Wait(outputReport);
writeOutput();
/*if (writeOutput())
{
lastError = 0;
if (testRumble.IsRumbleSet()) // repeat test rumbles periodically; rumble has auto-shut-off in the DS4 firmware
@ -323,6 +335,7 @@ namespace DS4Windows
lastError = thisError;
}
}
*/
}
}
}
@ -374,7 +387,8 @@ namespace DS4Windows
readTimeout.Enabled = true;
if (conType != ConnectionType.USB)
{
HidDevice.ReadStatus res = hDevice.ReadFile(btInputReport);
//HidDevice.ReadStatus res = hDevice.ReadFile(btInputReport);
HidDevice.ReadStatus res = hDevice.ReadAsyncWithFileStream(btInputReport, readStreamTimeout);
readTimeout.Enabled = false;
if (res == HidDevice.ReadStatus.Success)
{
@ -394,7 +408,8 @@ namespace DS4Windows
}
else
{
HidDevice.ReadStatus res = hDevice.ReadFile(inputReport);
//HidDevice.ReadStatus res = hDevice.ReadFile(inputReport);
HidDevice.ReadStatus res = hDevice.ReadAsyncWithFileStream(inputReport2, readStreamTimeout);
readTimeout.Enabled = false;
if (res != HidDevice.ReadStatus.Success)
{
@ -405,6 +420,10 @@ namespace DS4Windows
Removal(this, EventArgs.Empty);
return;
}
else
{
Array.Copy(inputReport2, 0, inputReport, 0, inputReport.Length);
}
}
if (ConnectionType == ConnectionType.BT && btInputReport[0] != 0x11)
{
@ -517,7 +536,12 @@ namespace DS4Windows
// XXX fix initialization ordering so the null checks all go away
if (Report != null)
Report(this, EventArgs.Empty);
sendOutputReport(false);
bool syncWriteReport = true;
if (conType == ConnectionType.BT)
{
syncWriteReport = false;
}
sendOutputReport(syncWriteReport);
if (!string.IsNullOrEmpty(error))
error = string.Empty;
if (!string.IsNullOrEmpty(currerror))

View File

@ -202,10 +202,9 @@ namespace DS4Windows
if (safeReadHandle == null)
safeReadHandle = OpenHandle(_devicePath, true);
if (fileStream == null && !safeReadHandle.IsInvalid)
fileStream = new FileStream(safeReadHandle, FileAccess.ReadWrite, inputBuffer.Length, false);
fileStream = new FileStream(safeReadHandle, FileAccess.ReadWrite, inputBuffer.Length, true);
if (!safeReadHandle.IsInvalid && fileStream.CanRead)
{
Task<ReadStatus> readFileTask = new Task<ReadStatus>(() => ReadWithFileStreamTask(inputBuffer));
readFileTask.Start();
bool success = readFileTask.Wait(timeout);
@ -245,6 +244,46 @@ namespace DS4Windows
return ReadStatus.ReadError;
}
public ReadStatus ReadAsyncWithFileStream(byte[] inputBuffer, int timeout)
{
try
{
if (safeReadHandle == null)
safeReadHandle = OpenHandle(_devicePath, true);
if (fileStream == null && !safeReadHandle.IsInvalid)
fileStream = new FileStream(safeReadHandle, FileAccess.ReadWrite, inputBuffer.Length, true);
if (!safeReadHandle.IsInvalid && fileStream.CanRead)
{
Task<int> readTask = fileStream.ReadAsync(inputBuffer, 0, inputBuffer.Length);
readTask.Wait(timeout);
if (readTask.Result > 0)
{
return ReadStatus.Success;
}
else
{
return ReadStatus.NoDataRead;
}
}
}
catch (Exception e)
{
if (e is AggregateException)
{
Console.WriteLine(e.Message);
return ReadStatus.WaitFail;
}
else
{
return ReadStatus.ReadError;
}
}
return ReadStatus.ReadError;
}
@ -285,7 +324,7 @@ namespace DS4Windows
}
if (fileStream == null && !safeReadHandle.IsInvalid)
{
fileStream = new FileStream(safeReadHandle, FileAccess.ReadWrite, outputBuffer.Length, false);
fileStream = new FileStream(safeReadHandle, FileAccess.ReadWrite, outputBuffer.Length, true);
}
if (fileStream != null && fileStream.CanWrite && !safeReadHandle.IsInvalid)
{
@ -304,6 +343,36 @@ namespace DS4Windows
}
public bool WriteAsyncOutputReportViaInterrupt(byte[] outputBuffer)
{
try
{
if (safeReadHandle == null)
{
safeReadHandle = OpenHandle(_devicePath, true);
}
if (fileStream == null && !safeReadHandle.IsInvalid)
{
fileStream = new FileStream(safeReadHandle, FileAccess.ReadWrite, outputBuffer.Length, true);
}
if (fileStream != null && fileStream.CanWrite && !safeReadHandle.IsInvalid)
{
Task writeTask = fileStream.WriteAsync(outputBuffer, 0, outputBuffer.Length);
//fileStream.Write(outputBuffer, 0, outputBuffer.Length);
return true;
}
else
{
return false;
}
}
catch (Exception)
{
return false;
}
}
private SafeFileHandle OpenHandle(String devicePathName, Boolean isExclusive)
{
SafeFileHandle hidHandle;
@ -312,11 +381,11 @@ namespace DS4Windows
{
if (isExclusive)
{
hidHandle = NativeMethods.CreateFile(devicePathName, NativeMethods.GENERIC_READ | NativeMethods.GENERIC_WRITE, 0, IntPtr.Zero, NativeMethods.OpenExisting, 0x20000000 | 0x80000000, 0);
hidHandle = NativeMethods.CreateFile(devicePathName, NativeMethods.GENERIC_READ | NativeMethods.GENERIC_WRITE, 0, IntPtr.Zero, NativeMethods.OpenExisting, 0x20000000 | 0x80000000 | NativeMethods.FILE_FLAG_OVERLAPPED, 0);
}
else
{
hidHandle = NativeMethods.CreateFile(devicePathName, NativeMethods.GENERIC_READ | NativeMethods.GENERIC_WRITE, NativeMethods.FILE_SHARE_READ | NativeMethods.FILE_SHARE_WRITE, IntPtr.Zero, NativeMethods.OpenExisting, 0x20000000 | 0x80000000, 0);
hidHandle = NativeMethods.CreateFile(devicePathName, NativeMethods.GENERIC_READ | NativeMethods.GENERIC_WRITE, NativeMethods.FILE_SHARE_READ | NativeMethods.FILE_SHARE_WRITE, IntPtr.Zero, NativeMethods.OpenExisting, 0x20000000 | 0x80000000 | NativeMethods.FILE_FLAG_OVERLAPPED, 0);
}
}
catch (Exception)