-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
FIFO mode option for HID OutputReports #11326
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
Changes from all commits
9b3be50
cd0d4bc
32ab470
c7be39f
a3cc7d5
06ab280
49f5d06
01e60de
6b58048
e3157c6
c6d8937
0a4833e
6d7ba07
95adfca
af6e60a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| #include "controllers/hid/hidioglobaloutputreportfifo.h" | ||
|
|
||
| #include <hidapi.h> | ||
|
|
||
| #include "controllers/defs_controllers.h" | ||
| #include "controllers/hid/legacyhidcontrollermappingfilehandler.h" | ||
| #include "util/compatibility/qbytearray.h" | ||
| #include "util/compatibility/qmutex.h" | ||
| #include "util/string.h" | ||
| #include "util/time.h" | ||
| #include "util/trace.h" | ||
|
|
||
| namespace { | ||
| constexpr size_t kMaxHidErrorMessageSize = 512; | ||
| constexpr size_t kSizeOfFifoInReports = 32; | ||
| } // namespace | ||
|
|
||
| HidIoGlobalOutputReportFifo::HidIoGlobalOutputReportFifo() | ||
| : m_fifoQueue(kSizeOfFifoInReports) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
| } | ||
|
|
||
| void HidIoGlobalOutputReportFifo::addReportDatasetToFifo(const quint8 reportId, | ||
| const QByteArray& reportData, | ||
| const mixxx::hid::DeviceInfo& deviceInfo, | ||
| 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 | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why? Prepend is implemented as insert()
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
|
|
||
| // Swap report to lockless FIFO queue | ||
| bool success = m_fifoQueue.try_emplace(std::move(report)); | ||
|
|
||
| // Handle the case, that the FIFO queue is full - which is an error case | ||
| if (!success) { | ||
| // If the FIFO is full, we skip the report dataset even | ||
| // in non-skipping mode, to keep the controller mapping thread | ||
| // responsive for InputReports from the controller. | ||
| // Alternative would be to block processing of the controller | ||
| // mapping thread, until the FIFO has space again. | ||
| qCWarning(logOutput) | ||
| << "FIFO overflow: Unable to add OutputReport " << reportId | ||
| << "to the global cache for non-skipping sending of OututReports for" | ||
| << deviceInfo.formatName(); | ||
| } | ||
| } | ||
|
|
||
| bool HidIoGlobalOutputReportFifo::sendNextReportDataset(QMutex* pHidDeviceAndPollMutex, | ||
| hid_device* pHidDevice, | ||
| const mixxx::hid::DeviceInfo& deviceInfo, | ||
| const RuntimeLoggingCategory& logOutput) { | ||
| auto startOfHidWrite = mixxx::Time::elapsed(); | ||
|
|
||
| QByteArray* pFront = m_fifoQueue.front(); | ||
|
|
||
| if (pFront == nullptr) { | ||
| // No data in FIFO to be send | ||
| // Return with false, to signal the caller, that no time consuming IO | ||
| // operation was ncessary | ||
| return false; | ||
| } | ||
|
|
||
| // Array containing the ReportID byte followed by the data to be send | ||
| QByteArray reportToSend(std::move(*pFront)); | ||
| m_fifoQueue.pop(); | ||
|
|
||
| auto hidDeviceLock = lockMutex(pHidDeviceAndPollMutex); | ||
|
|
||
| // hid_write can take several milliseconds, because hidapi synchronizes | ||
| // the asyncron HID communication from the OS | ||
| int result = hid_write(pHidDevice, | ||
| reinterpret_cast<const unsigned char*>(reportToSend.constData()), | ||
| reportToSend.size()); | ||
| if (result == -1) { | ||
| qCWarning(logOutput) << "Unable to send data to" << deviceInfo.formatName() << ":" | ||
| << mixxx::convertWCStringToQString( | ||
| hid_error(pHidDevice), | ||
| kMaxHidErrorMessageSize); | ||
| } | ||
|
|
||
| hidDeviceLock.unlock(); | ||
|
|
||
| if (result != -1) { | ||
| qCDebug(logOutput) << "t:" << startOfHidWrite.formatMillisWithUnit() | ||
| << " " << result << "bytes (including ReportID of" | ||
| << static_cast<quint8>(reportToSend[0]) | ||
| << ") sent from non-skipping FIFO - Needed: " | ||
| << (mixxx::Time::elapsed() - startOfHidWrite) | ||
| .formatMicrosWithUnit(); | ||
| } | ||
|
|
||
| // Return with true, to signal the caller, that the time consuming hid_write | ||
| // operation was executed | ||
| return true; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| #pragma once | ||
|
|
||
| #include "controllers/controller.h" | ||
| #include "controllers/hid/hiddevice.h" | ||
| #include "rigtorp/SPSCQueue.h" | ||
| #include "util/duration.h" | ||
|
|
||
| /// Stores and sends OutputReports (independent of the ReportID) in First In / | ||
| /// First Out (FIFO) order | ||
| class HidIoGlobalOutputReportFifo { | ||
| public: | ||
| HidIoGlobalOutputReportFifo(); | ||
|
|
||
| /// Caches new OutputReport to the FIFO, which will later be send by the IO thread | ||
| void addReportDatasetToFifo(const quint8 reportId, | ||
| const QByteArray& reportData, | ||
| const mixxx::hid::DeviceInfo& deviceInfo, | ||
| const RuntimeLoggingCategory& logOutput); | ||
|
|
||
| /// Sends the next OutputReport from FIFO to the HID device, | ||
| /// when if any report is cached in FIFO. | ||
| /// Returns true if a time consuming hid_write operation was executed. | ||
| bool sendNextReportDataset(QMutex* pHidDeviceAndPollMutex, | ||
| hid_device* pHidDevice, | ||
| const mixxx::hid::DeviceInfo& deviceInfo, | ||
| const RuntimeLoggingCategory& logOutput); | ||
|
|
||
| private: | ||
| // Lockless FIFO queue | ||
| rigtorp::SPSCQueue<QByteArray> m_fifoQueue; | ||
| }; |
Uh oh!
There was an error while loading. Please reload this page.