-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
FIFO mode option for HID OutputReports #11326
Conversation
…m HidIoOutputReport
Nowadays the logging system prints this information at begin of each line anyway
…bed the new behavior
8a82da4
to
a3cc7d5
Compare
Tested with #11284 and it fixes the issue! |
Use QByteArrays prepend function, which will be a "very fast constant time" function when we switch to Qt6
00a64c0
to
49f5d06
Compare
@@ -204,11 +205,30 @@ void HidIoThread::updateCachedOutputReportData(quint8 reportID, | |||
|
|||
mapLock.unlock(); | |||
|
|||
// If useNonSkippingFIFO is false, the report data are cached here | |||
// If useNonSkippingFIFO is true, this cache is cleared | |||
actualOutputReportIterator->second->updateCachedData( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is the mutex locked and this operation invoked even when using the FIFO strategy? The cache and the mutex should be bypassed. Ideally the dispatch strategy for each reportID would be immutable and configured at construction time. At least the FIFO report IDs need to be known in advance to avoid the locking.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The call of updateCachedData is always needed to guarantee seamless transitions between both modes.
If a skipable report is cached, it must be cleared, because otherwise it would be send after the report from the non-skipable report from the FIFO - and would overwrite with outdated data. This happens in updateCachedData as well as the check for errors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I acknowledge that the current design is not suitable to for this optimization. But in principle the dispatch strategy should be fixed per report ID and not change with every call.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Caching the mode only for the sender would avoid locking the mutex with every call. The cache only needs to be cleared once after changing the mode.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only datastructure per output report is currently HidIoOutputReport. Adding a additional data structure per output report would prevent the mutex locking in FIFO mode. But this mutex is only locked for the very short time of looking up the map for the map iterator of the report.
This takes nanoseconds, while the sending of a report takes several milliseconds. And in FIFO mode, calling this function must not be done in higher rate than the HID subsystem can send reports to the device. This ratio makes a mutex blocking very unlikely - and even when it happens, it will block the other thread only for several nanoseconds.
I don't think, that it makes sense to add additional code here, because the impact will be insignificant in practice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
However short and tiny the critical section might be, you have no control over the OS scheduler -> dependency inversion. Generous use of mutexes is always a red herring. They are very often used for compensating architectural deficiencies.
https://twitter.com/EshlemanMatthew/status/1641073049746251782
I did some spot checks on the latest version and this still seems to work just fine. Update: 30min set and everything looked great with the WIP S4 Mk3 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for this PR. In general it should work good, though I am not able to test. But since this is optional we can merge it anyway.
I have added some suggestions to improve performance and readability.
} // namespace | ||
|
||
HidIoGlobalOutputReportFifo::HidIoGlobalOutputReportFifo() | ||
: m_fifoQueue(kSizeOfFifoInReports) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a naming missmatch beween InReports in the constant and OutputReport in the class name?
Maybe we can avoid input and output which can change form the perspective of different classes and use Received and Transmit or something.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is a missunderstanding. In is not a abreviation for Input here, its just the word in. The whole code is only for OutputReports.
InReports is the counted unit, like InMillis or InSeconds for times.
const RuntimeLoggingCategory& logOutput) { | ||
// First byte must always be the ReportID-Byte | ||
QByteArray report(reportData); | ||
report.prepend(reportId); // In Qt6 this is a very fast operation without reallocation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why? Prepend is implemented as insert()
https://github.com/qt/qtbase/blob/aaaa1e251ea43e4094e79b7161327c07db60d351/src/corelib/text/qbytearray.cpp#L898
I think by luck there are some reserve bytes. But this call also performs a deep copy of the implicit shared.
They might be some reserve bytes that avoid allocation. They should be also there with Qt5.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Qt6 documentation explicitly say, that this is very fast: https://doc.qt.io/qt-6/qbytearray.html#prepend
return false; | ||
} | ||
|
||
QByteArray dataToSend(std::move(*pFront)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dataToSend has the report ID prepended. This is not clear initially.
Can we improve the situation by always putting the report ID at the beginning?
This will also solve the reallocation of the buffer when prepending the report ID before putting it into the buffer.
The same benefit goes for
void HidController::send(QByteArray data, unsigned int reportID)
The we just not store the modified buffer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Our Javascript API has seperated arguments for ReportID and report data. This is not only needed for backwards compatibility, but is also the cleanest way to define such an API.
I will improve naming and add a comment, to make clear that this variable contains ReportID + data.
This is still ready to be merged:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you.
Than let's quickly add it to the 2.4-beta to have the chance for user feedback before release.
Thank you! No HID support in 2.4 is complete without known bugs. I will prepare a blogpost in the comming days. |
While #4585 makes the Mixxx HID subsystem fast and responsive in overload situations for any device, that use reports in the sense of the USB HID Class specification, these optimizations do not work for the Traktor Kontrol S4 Mk3, which uses HID reports to transport the data of it's own protocol.
First attemp to address this S4 specific protocol was #4692 (Option for resending reports with identical data), but this was not enough. It came out #10828, that the S4 protocol also depend on sequences of reports with the same ReportID.
This PR replaces the Resend-Option from #4692 with an Option to use a FIFO for reports, which must not be skipped.
Closes: #10828