mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-06 16:40:41 -07:00
misc: various performance clean up (#844)
## Link to GitHub Issue or related Pull Request, if one exists n/a ## Description of change - Replace unnecessary precision timers with standard sleeps to reduce wakeups and background CPU usage. - Reuse buffers in raw input, touchscreen, and HID output paths to eliminate steady-state allocations. - Pre-index HID button groups and correctly process batched HID reports. No functional changes. ## Testing
This commit is contained in:
@@ -4,7 +4,6 @@
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
@@ -103,6 +102,13 @@ namespace rawinput {
|
||||
std::vector<HIDTouchPoint> touch_points;
|
||||
};
|
||||
|
||||
struct HIDButtonInputGroup {
|
||||
USAGE usage_page;
|
||||
USHORT link_collection;
|
||||
std::vector<size_t> cap_indices;
|
||||
std::vector<USAGE> usages;
|
||||
};
|
||||
|
||||
struct DeviceHIDInfo {
|
||||
HANDLE handle;
|
||||
_HIDP_CAPS caps;
|
||||
@@ -125,11 +131,12 @@ namespace rawinput {
|
||||
|
||||
std::vector<std::vector<bool>> button_states;
|
||||
std::vector<std::vector<double>> button_up, button_down;
|
||||
std::vector<std::vector<uint8_t>> button_report_states;
|
||||
std::vector<std::vector<bool>> button_output_states;
|
||||
|
||||
// key: usage page, link collection
|
||||
// value: number of buttons for that key (combine ranges and nonranges)
|
||||
std::map<std::pair<USAGE, ULONG>, ULONG> button_usage_pages;
|
||||
std::vector<HIDButtonInputGroup> button_input_groups;
|
||||
std::vector<CHAR> output_report;
|
||||
std::vector<USAGE> button_output_usages;
|
||||
std::vector<USAGE> button_output_usages_off;
|
||||
|
||||
std::vector<float> value_states;
|
||||
std::vector<LONG> value_states_raw;
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
#include "rawinput.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cstdarg>
|
||||
#include <map>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -9,7 +13,6 @@
|
||||
|
||||
#include "util/logging.h"
|
||||
#include "external/robin_hood.h"
|
||||
#include "util/precise_timer.h"
|
||||
#include "util/time.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
@@ -169,9 +172,8 @@ void rawinput::RawInputManager::input_hwnd_create() {
|
||||
});
|
||||
|
||||
// wait for window creation being done
|
||||
timeutils::PreciseSleepTimer timer;
|
||||
while (!this->input_hwnd) {
|
||||
timer.sleep(1);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -439,7 +441,9 @@ void rawinput::RawInputManager::devices_scan_rawinput(RAWINPUTDEVICELIST *device
|
||||
std::vector<std::string> button_caps_names;
|
||||
std::vector<std::vector<bool>> button_states;
|
||||
std::vector<std::vector<double>> button_up, button_down;
|
||||
std::map<std::pair<USAGE, ULONG>, ULONG> button_usage_pages;
|
||||
std::vector<std::vector<uint8_t>> button_report_states;
|
||||
std::vector<HIDButtonInputGroup> button_input_groups;
|
||||
std::map<std::pair<USAGE, ULONG>, size_t> button_input_group_indices;
|
||||
for (int button_cap_num = 0; button_cap_num < button_cap_length; button_cap_num++) {
|
||||
auto &button_caps = button_cap_data[button_cap_num];
|
||||
|
||||
@@ -458,7 +462,7 @@ void rawinput::RawInputManager::devices_scan_rawinput(RAWINPUTDEVICELIST *device
|
||||
int button_count = button_caps.Range.UsageMax - button_caps.Range.UsageMin + 1;
|
||||
|
||||
// ignore bad ranges reported by bad devices
|
||||
if (button_count >= 0xffff) {
|
||||
if (button_count <= 0 || button_count >= 0xffff) {
|
||||
log_warning("rawinput", "skipping bad button cap range for device {}, range [{}, {}]",
|
||||
device_name,
|
||||
button_caps.Range.UsageMin,
|
||||
@@ -467,11 +471,30 @@ void rawinput::RawInputManager::devices_scan_rawinput(RAWINPUTDEVICELIST *device
|
||||
}
|
||||
|
||||
// fill vectors
|
||||
const size_t cap_index = button_caps_list.size();
|
||||
button_caps_list.emplace_back(button_caps);
|
||||
button_states.emplace_back(std::vector<bool>(static_cast<unsigned int>(button_count), false));
|
||||
button_up.emplace_back(std::vector<double>(static_cast<unsigned int>(button_count), 0.0));
|
||||
button_down.emplace_back(std::vector<double>(static_cast<unsigned int>(button_count), 0.0));
|
||||
button_usage_pages[std::make_pair(button_caps.UsagePage, button_caps.LinkCollection)] += button_count;
|
||||
button_report_states.emplace_back(static_cast<size_t>(button_count), 0);
|
||||
|
||||
const auto group_key = std::make_pair(
|
||||
button_caps.UsagePage,
|
||||
static_cast<ULONG>(button_caps.LinkCollection));
|
||||
auto [group_it, inserted] = button_input_group_indices.try_emplace(
|
||||
group_key,
|
||||
button_input_groups.size());
|
||||
if (inserted) {
|
||||
button_input_groups.emplace_back(HIDButtonInputGroup {
|
||||
.usage_page = button_caps.UsagePage,
|
||||
.link_collection = button_caps.LinkCollection,
|
||||
.cap_indices = {},
|
||||
.usages = {},
|
||||
});
|
||||
}
|
||||
auto &input_group = button_input_groups[group_it->second];
|
||||
input_group.cap_indices.push_back(cap_index);
|
||||
input_group.usages.resize(input_group.usages.size() + button_count);
|
||||
|
||||
// names
|
||||
for (USAGE usg = button_caps.Range.UsageMin; usg <= button_caps.Range.UsageMax; usg++) {
|
||||
@@ -512,7 +535,7 @@ void rawinput::RawInputManager::devices_scan_rawinput(RAWINPUTDEVICELIST *device
|
||||
int button_count = button_caps.Range.UsageMax - button_caps.Range.UsageMin + 1;
|
||||
|
||||
// ignore bad ranges reported by bad devices
|
||||
if (button_count >= 0xffff) {
|
||||
if (button_count <= 0 || button_count >= 0xffff) {
|
||||
log_warning("rawinput", "skipping bad button output cap range for device {}, range [{}, {}]",
|
||||
device_name,
|
||||
button_caps.Range.UsageMin,
|
||||
@@ -800,8 +823,18 @@ void rawinput::RawInputManager::devices_scan_rawinput(RAWINPUTDEVICELIST *device
|
||||
new_device.hidInfo->button_states = std::move(button_states);
|
||||
new_device.hidInfo->button_up = std::move(button_up);
|
||||
new_device.hidInfo->button_down = std::move(button_down);
|
||||
new_device.hidInfo->button_report_states = std::move(button_report_states);
|
||||
new_device.hidInfo->button_output_states = std::move(button_output_states);
|
||||
new_device.hidInfo->button_usage_pages = std::move(button_usage_pages);
|
||||
new_device.hidInfo->button_input_groups = std::move(button_input_groups);
|
||||
new_device.hidInfo->output_report.resize(caps.OutputReportByteLength);
|
||||
size_t max_button_output_count = 0;
|
||||
for (const auto &button_output_state : new_device.hidInfo->button_output_states) {
|
||||
if (button_output_state.size() > max_button_output_count) {
|
||||
max_button_output_count = button_output_state.size();
|
||||
}
|
||||
}
|
||||
new_device.hidInfo->button_output_usages.reserve(max_button_output_count);
|
||||
new_device.hidInfo->button_output_usages_off.reserve(max_button_output_count);
|
||||
new_device.hidInfo->value_states = std::move(value_states);
|
||||
new_device.hidInfo->value_states_raw = std::move(value_states_raw);
|
||||
new_device.hidInfo->value_output_states = std::move(value_output_states);
|
||||
@@ -1573,11 +1606,17 @@ LRESULT CALLBACK rawinput::RawInputManager::input_wnd_proc(
|
||||
if (!data_size) {
|
||||
break;
|
||||
}
|
||||
std::shared_ptr<RAWINPUT> data((RAWINPUT *) new char[data_size]{});
|
||||
thread_local std::vector<RAWINPUT> data_buffer;
|
||||
const size_t data_count =
|
||||
(data_size + sizeof(RAWINPUT) - 1) / sizeof(RAWINPUT);
|
||||
if (data_buffer.size() < data_count) {
|
||||
data_buffer.resize(data_count);
|
||||
}
|
||||
auto data = data_buffer.data();
|
||||
if (GetRawInputData(
|
||||
(HRAWINPUT) lParam,
|
||||
RID_INPUT,
|
||||
data.get(),
|
||||
data,
|
||||
&data_size,
|
||||
sizeof(RAWINPUTHEADER)) != data_size) {
|
||||
break;
|
||||
@@ -1791,17 +1830,13 @@ LRESULT CALLBACK rawinput::RawInputManager::input_wnd_proc(
|
||||
+ (size_t) hid_report_index * data_hid.dwSizeHid;
|
||||
|
||||
// parse reports
|
||||
for (const auto &pair : device.hidInfo->button_usage_pages) {
|
||||
const auto usage_page = pair.first.first;
|
||||
const auto link_collection = pair.first.second;
|
||||
const auto button_count = pair.second;
|
||||
|
||||
ULONG usages_length = button_count;
|
||||
std::vector<USAGE> usages(static_cast<size_t>(usages_length));
|
||||
for (auto &input_group : device.hidInfo->button_input_groups) {
|
||||
auto &usages = input_group.usages;
|
||||
ULONG usages_length = static_cast<ULONG>(usages.size());
|
||||
if (HidP_GetUsages(
|
||||
HidP_Input,
|
||||
usage_page,
|
||||
link_collection,
|
||||
input_group.usage_page,
|
||||
input_group.link_collection,
|
||||
usages.data(),
|
||||
&usages_length,
|
||||
reinterpret_cast<PHIDP_PREPARSED_DATA>(device.hidInfo->preparsed_data.get()),
|
||||
@@ -1819,29 +1854,22 @@ LRESULT CALLBACK rawinput::RawInputManager::input_wnd_proc(
|
||||
// log_info(
|
||||
// "rawinput",
|
||||
// "processing HID input for device {}, usage page {:x} and link collection {:x} with {} buttons, got {} reports",
|
||||
// device.desc,
|
||||
// usage_page, link_collection, button_count, usages_length);
|
||||
// device.desc, input_group.usage_page,
|
||||
// input_group.link_collection, usages.size(), usages_length);
|
||||
|
||||
// buttons
|
||||
for (size_t cap_num = 0; cap_num < device.hidInfo->button_caps_list.size(); cap_num++) {
|
||||
for (const size_t cap_num : input_group.cap_indices) {
|
||||
auto &button_caps = device.hidInfo->button_caps_list[cap_num];
|
||||
auto &button_states = device.hidInfo->button_states[cap_num];
|
||||
auto &button_down = device.hidInfo->button_down[cap_num];
|
||||
auto &button_up = device.hidInfo->button_up[cap_num];
|
||||
|
||||
// is this the right usage page and link collection?
|
||||
if (button_caps.UsagePage != usage_page || button_caps.LinkCollection != link_collection) {
|
||||
continue;
|
||||
}
|
||||
auto &new_states = device.hidInfo->button_report_states[cap_num];
|
||||
|
||||
// get button count
|
||||
int button_count = button_caps.Range.UsageMax - button_caps.Range.UsageMin + 1;
|
||||
if (button_count <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// update buttons
|
||||
std::vector<bool> new_states(button_count);
|
||||
std::fill(new_states.begin(), new_states.end(), 0);
|
||||
for (ULONG usage_num = 0; usage_num < usages_length; usage_num++) {
|
||||
if (usages[usage_num] < button_caps.Range.UsageMin ||
|
||||
usages[usage_num] > button_caps.Range.UsageMax) {
|
||||
@@ -1852,17 +1880,18 @@ LRESULT CALLBACK rawinput::RawInputManager::input_wnd_proc(
|
||||
|
||||
// guard against some buggy device sending an event for a usage below UsageMin
|
||||
if (usage < button_count) {
|
||||
new_states[usage] = true;
|
||||
new_states[usage] = 1;
|
||||
}
|
||||
}
|
||||
for (int button_num = 0; button_num < button_count; button_num++) {
|
||||
if (!new_states[button_num] && button_states[button_num]) {
|
||||
const bool new_state = new_states[button_num] != 0;
|
||||
if (!new_state && button_states[button_num]) {
|
||||
device.updated = true;
|
||||
button_states[button_num] = new_states[button_num];
|
||||
button_states[button_num] = new_state;
|
||||
button_down[button_num] = input_time;
|
||||
} else if (new_states[button_num] && !button_states[button_num]) {
|
||||
} else if (new_state && !button_states[button_num]) {
|
||||
device.updated = true;
|
||||
button_states[button_num] = new_states[button_num];
|
||||
button_states[button_num] = new_state;
|
||||
button_up[button_num] = input_time;
|
||||
}
|
||||
}
|
||||
@@ -2017,8 +2046,10 @@ void rawinput::RawInputManager::device_write_output(Device *device, bool only_up
|
||||
switch (hid->driver) {
|
||||
case HIDDriver::Default: {
|
||||
|
||||
// allocate report
|
||||
CHAR *report_data = new CHAR[hid->caps.OutputReportByteLength] {};
|
||||
auto &report_data = hid->output_report;
|
||||
auto &usage_list = hid->button_output_usages;
|
||||
auto &usage_off_list = hid->button_output_usages_off;
|
||||
std::fill(report_data.begin(), report_data.end(), 0);
|
||||
|
||||
// set buttons
|
||||
for (size_t cap_no = 0; cap_no < hid->button_output_caps_list.size(); cap_no++) {
|
||||
@@ -2026,10 +2057,8 @@ void rawinput::RawInputManager::device_write_output(Device *device, bool only_up
|
||||
auto &button_state_list = hid->button_output_states[cap_no];
|
||||
|
||||
// determine which buttons to turn on
|
||||
std::vector<USAGE> usage_list;
|
||||
std::vector<USAGE> usage_off_list;
|
||||
usage_list.reserve(button_state_list.size());
|
||||
usage_off_list.reserve(button_state_list.size());
|
||||
usage_list.clear();
|
||||
usage_off_list.clear();
|
||||
for (size_t state_no = 0; state_no < button_state_list.size(); state_no++) {
|
||||
if (button_state_list[state_no]) {
|
||||
usage_list.push_back(button_cap.Range.UsageMin + (USAGE) state_no);
|
||||
@@ -2044,15 +2073,18 @@ void rawinput::RawInputManager::device_write_output(Device *device, bool only_up
|
||||
HidP_Output,
|
||||
button_cap.UsagePage,
|
||||
button_cap.LinkCollection,
|
||||
&usage_list[0],
|
||||
usage_list.data(),
|
||||
&usage_list_length,
|
||||
reinterpret_cast<PHIDP_PREPARSED_DATA>(hid->preparsed_data.get()),
|
||||
report_data,
|
||||
report_data.data(),
|
||||
hid->caps.OutputReportByteLength) == HIDP_STATUS_INCOMPATIBLE_REPORT_ID) {
|
||||
|
||||
// flush report
|
||||
HidD_SetOutputReport(hid->handle, report_data, hid->caps.OutputReportByteLength);
|
||||
memset(report_data, 0, hid->caps.OutputReportByteLength);
|
||||
HidD_SetOutputReport(
|
||||
hid->handle,
|
||||
report_data.data(),
|
||||
hid->caps.OutputReportByteLength);
|
||||
std::fill(report_data.begin(), report_data.end(), 0);
|
||||
}
|
||||
|
||||
// clear the buttons
|
||||
@@ -2061,22 +2093,22 @@ void rawinput::RawInputManager::device_write_output(Device *device, bool only_up
|
||||
HidP_Output,
|
||||
button_cap.UsagePage,
|
||||
button_cap.LinkCollection,
|
||||
&usage_off_list[0],
|
||||
usage_off_list.data(),
|
||||
&usage_off_list_length,
|
||||
reinterpret_cast<PHIDP_PREPARSED_DATA>(hid->preparsed_data.get()),
|
||||
report_data,
|
||||
report_data.data(),
|
||||
hid->caps.OutputReportByteLength) == HIDP_STATUS_INCOMPATIBLE_REPORT_ID) {
|
||||
|
||||
// flush report
|
||||
DWORD written_bytes = 0;
|
||||
WriteFile(
|
||||
hid->handle,
|
||||
reinterpret_cast<void *>(report_data),
|
||||
report_data.data(),
|
||||
hid->caps.OutputReportByteLength,
|
||||
&written_bytes,
|
||||
nullptr
|
||||
);
|
||||
memset(report_data, 0, hid->caps.OutputReportByteLength);
|
||||
std::fill(report_data.begin(), report_data.end(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2105,19 +2137,19 @@ void rawinput::RawInputManager::device_write_output(Device *device, bool only_up
|
||||
value_cap.NotRange.Usage,
|
||||
static_cast<ULONG>(usage_value),
|
||||
reinterpret_cast<PHIDP_PREPARSED_DATA>(hid->preparsed_data.get()),
|
||||
report_data,
|
||||
report_data.data(),
|
||||
hid->caps.OutputReportByteLength) == HIDP_STATUS_INCOMPATIBLE_REPORT_ID) {
|
||||
|
||||
// flush report
|
||||
DWORD written_bytes = 0;
|
||||
WriteFile(
|
||||
hid->handle,
|
||||
reinterpret_cast<void *>(report_data),
|
||||
report_data.data(),
|
||||
hid->caps.OutputReportByteLength,
|
||||
&written_bytes,
|
||||
nullptr
|
||||
);
|
||||
memset(report_data, 0, hid->caps.OutputReportByteLength);
|
||||
std::fill(report_data.begin(), report_data.end(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2151,15 +2183,12 @@ void rawinput::RawInputManager::device_write_output(Device *device, bool only_up
|
||||
DWORD written_bytes = 0;
|
||||
WriteFile(
|
||||
hid->handle,
|
||||
reinterpret_cast<void *>(report_data),
|
||||
report_data.data(),
|
||||
hid->caps.OutputReportByteLength,
|
||||
&written_bytes,
|
||||
nullptr
|
||||
);
|
||||
|
||||
// delete report
|
||||
delete[] report_data;
|
||||
|
||||
break;
|
||||
}
|
||||
case HIDDriver::PacDrive: {
|
||||
|
||||
@@ -319,7 +319,8 @@ namespace rawinput::touch {
|
||||
}
|
||||
|
||||
// iterate all input data and get touch points
|
||||
std::vector<HIDTouchPoint> touch_points;
|
||||
thread_local std::vector<HIDTouchPoint> touch_points;
|
||||
touch_points.clear();
|
||||
touch_points.reserve(touch.elements_x.size());
|
||||
for (size_t i = 0; i < touch.elements_x.size(); i++) {
|
||||
|
||||
@@ -424,9 +425,15 @@ namespace rawinput::touch {
|
||||
}
|
||||
|
||||
// process touch points
|
||||
std::vector<DWORD> touch_removes;
|
||||
std::vector<TouchPoint> touch_writes;
|
||||
std::vector<DWORD> touch_modifications;
|
||||
thread_local std::vector<DWORD> touch_removes;
|
||||
thread_local std::vector<TouchPoint> touch_writes;
|
||||
thread_local std::vector<DWORD> touch_modifications;
|
||||
touch_removes.clear();
|
||||
touch_writes.clear();
|
||||
touch_modifications.clear();
|
||||
touch_removes.reserve(touch.touch_points.size() + touch_points.size());
|
||||
touch_writes.reserve(touch_points.size());
|
||||
touch_modifications.reserve(touch_points.size());
|
||||
|
||||
// drop every touch point with the given id (marking it as released)
|
||||
auto remove_touch_point = [&] (DWORD id) {
|
||||
@@ -581,7 +588,9 @@ namespace rawinput::touch {
|
||||
auto deadline = get_system_milliseconds() - 50;
|
||||
|
||||
// check touch points
|
||||
std::vector<DWORD> touch_removes;
|
||||
thread_local std::vector<DWORD> touch_removes;
|
||||
touch_removes.clear();
|
||||
touch_removes.reserve(touch.touch_points.size());
|
||||
auto touch_it = touch.touch_points.begin();
|
||||
while (touch_it != touch.touch_points.end()) {
|
||||
auto &hid_tp = *touch_it;
|
||||
|
||||
Reference in New Issue
Block a user