diff --git a/Module.mk b/Module.mk index da18e50..b6fa2f3 100644 --- a/Module.mk +++ b/Module.mk @@ -154,6 +154,7 @@ include src/main/iidxhook6/Module.mk include src/main/iidxhook7/Module.mk include src/main/iidxhook8/Module.mk include src/main/iidxhook9/Module.mk +include src/main/iidxio-async/Module.mk include src/main/iidxio-bio2/Module.mk include src/main/iidxio-ezusb/Module.mk include src/main/iidxio-ezusb2/Module.mk @@ -460,6 +461,7 @@ $(zipdir)/iidx-27-to-30.zip: \ $(zipdir)/iidx-hwio-x86.zip: \ build/bin/indep-32/aciomgr.dll \ build/bin/indep-32/eamio-icca.dll \ + build/bin/indep-32/iidxio-async.dll \ build/bin/indep-32/iidxio-bio2.dll \ build/bin/indep-32/iidxio-ezusb.dll \ build/bin/indep-32/iidxio-ezusb2.dll \ @@ -473,6 +475,7 @@ $(zipdir)/iidx-hwio-x86.zip: \ $(zipdir)/iidx-hwio-x64.zip: \ build/bin/indep-64/aciomgr.dll \ build/bin/indep-64/eamio-icca.dll \ + build/bin/indep-64/iidxio-async.dll \ build/bin/indep-64/iidxio-bio2.dll \ build/bin/indep-64/iidxio-ezusb.dll \ build/bin/indep-64/iidxio-ezusb2.dll \ diff --git a/doc/iidxhook/README.md b/doc/iidxhook/README.md index cd1e68d..e39f56e 100644 --- a/doc/iidxhook/README.md +++ b/doc/iidxhook/README.md @@ -48,6 +48,8 @@ Available implementations that can be swapped out depending on which kind of IO use: - `iidxio`: Default implementation supporting keyboard, mouse and USB game controllers +- [iidxio-async](iidxhook/iidxio-async.md): Shim implementation that runs another iidxio implementation in a dedicated + thread - [iidxio-bio2](iidxhook/iidxio-bio2.md): Support BIO2 hardware - [iidxio-ezusb](iidxhook/iidxio-ezusb.md): Support C02 ezusb FX hardware - [iidxio-ezusb2](iidxhook/iidxio-ezusb2.md): Support IO2 ezusb FX2 hardware diff --git a/doc/iidxhook/iidxio-async.md b/doc/iidxhook/iidxio-async.md new file mode 100644 index 0000000..82f7c21 --- /dev/null +++ b/doc/iidxhook/iidxio-async.md @@ -0,0 +1,22 @@ +# IIDXIO async API implementation + +This implementation of the iidxio API is a shim library that takes another iidxio library and +runs the functions `iidx_io_ep1_send`, `iidx_io_ep2_recv` and `iidx_io_ep3_write_16seg` in a +dedicated thread. State synchronization to the getter and setter functions is also handled +transparently. + +Usage of this **may** improve performance of certain iidxio implementations or when using them +in certain integrations, e.g. the send and receive functions of a iidxio implementation for some +target IO hardware calls are synchronous and expensive. + +This is not a fix/solution to a badly implemented iidxio library with poor performance as it cannot +make it go faster and address potential latency issues, for example. + +Use with caution and know why and when you need to use it. + +## Setup + +* Add `iidxio-async.dll` in the same folder as your `iidxhookX.dll` +* Rename your `iidxio.dll` to `iidxio-async-child.dll` +* Rename `iidxio-async.dll` to `iidxio.dll` +* Run the game \ No newline at end of file diff --git a/lib/cimgui/cimgui/imgui/examples/example_glfw_wgpu/web/index.html b/lib/cimgui/cimgui/imgui/examples/example_glfw_wgpu/web/index.html new file mode 100644 index 0000000..a2a91c4 --- /dev/null +++ b/lib/cimgui/cimgui/imgui/examples/example_glfw_wgpu/web/index.html @@ -0,0 +1,84 @@ + + + + + + Dear ImGui Emscripten+GLFW+WebGPU example + + + + + + + diff --git a/lib/cimgui/cimgui/imgui/examples/libs/glfw/lib-vc2010-32/glfw3.lib b/lib/cimgui/cimgui/imgui/examples/libs/glfw/lib-vc2010-32/glfw3.lib new file mode 100644 index 0000000..348abec Binary files /dev/null and b/lib/cimgui/cimgui/imgui/examples/libs/glfw/lib-vc2010-32/glfw3.lib differ diff --git a/lib/cimgui/cimgui/imgui/examples/libs/glfw/lib-vc2010-64/glfw3.lib b/lib/cimgui/cimgui/imgui/examples/libs/glfw/lib-vc2010-64/glfw3.lib new file mode 100644 index 0000000..768f308 Binary files /dev/null and b/lib/cimgui/cimgui/imgui/examples/libs/glfw/lib-vc2010-64/glfw3.lib differ diff --git a/src/main/iidxio-async/Module.mk b/src/main/iidxio-async/Module.mk new file mode 100644 index 0000000..d852e4e --- /dev/null +++ b/src/main/iidxio-async/Module.mk @@ -0,0 +1,9 @@ +dlls += iidxio-async + +ldflags_iidxio-async := \ + +libs_iidxio-async := \ + util \ + +src_iidxio-async := \ + iidxio.c \ diff --git a/src/main/iidxio-async/iidxio-async.def b/src/main/iidxio-async/iidxio-async.def new file mode 100644 index 0000000..403900d --- /dev/null +++ b/src/main/iidxio-async/iidxio-async.def @@ -0,0 +1,18 @@ +LIBRARY iidxio + +EXPORTS + iidx_io_ep1_send + iidx_io_ep1_set_deck_lights + iidx_io_ep1_set_panel_lights + iidx_io_ep1_set_top_lamps + iidx_io_ep1_set_top_neons + iidx_io_ep2_get_keys + iidx_io_ep2_get_panel + iidx_io_ep2_get_sys + iidx_io_ep2_get_slider + iidx_io_ep2_get_turntable + iidx_io_ep2_recv + iidx_io_ep3_write_16seg + iidx_io_fini + iidx_io_init + iidx_io_set_loggers diff --git a/src/main/iidxio-async/iidxio.c b/src/main/iidxio-async/iidxio.c new file mode 100644 index 0000000..b81ec6b --- /dev/null +++ b/src/main/iidxio-async/iidxio.c @@ -0,0 +1,407 @@ +#define LOG_MODULE "iidxio-async" + +#include + +#include +#include +#include +#include + +#include + +#include "bemanitools/iidxio.h" + +#include "util/log.h" +#include "util/thread.h" +#include "util/time.h" + +typedef void (*iidx_io_set_loggers_t)( + log_formatter_t misc, + log_formatter_t info, + log_formatter_t warning, + log_formatter_t fatal); +typedef bool (*iidx_io_init_t)( + thread_create_t thread_create, + thread_join_t thread_join, + thread_destroy_t thread_destroy); +typedef void (*iidx_io_fini_t)(void); +typedef void (*iidx_io_ep1_set_deck_lights_t)(uint16_t deck_lights); +typedef void (*iidx_io_ep1_set_panel_lights_t)(uint8_t panel_lights); +typedef void (*iidx_io_ep1_set_top_lamps_t)(uint8_t top_lamps); +typedef void (*iidx_io_ep1_set_top_neons_t)(bool top_neons); +typedef bool (*iidx_io_ep1_send_t)(void); +typedef bool (*iidx_io_ep2_recv_t)(void); +typedef uint8_t (*iidx_io_ep2_get_turntable_t)(uint8_t player_no); +typedef uint8_t (*iidx_io_ep2_get_slider_t)(uint8_t slider_no); +typedef uint8_t (*iidx_io_ep2_get_sys_t)(void); +typedef uint8_t (*iidx_io_ep2_get_panel_t)(void); +typedef uint16_t (*iidx_io_ep2_get_keys_t)(void); +typedef bool (*iidx_io_ep3_write_16seg_t)(const char *text); + +static HMODULE _child_iidx_io_module; + +static iidx_io_set_loggers_t _child_iidx_io_set_loggers; +static iidx_io_init_t _child_iidx_io_init; +static iidx_io_fini_t _child_iidx_io_fini; + +static iidx_io_ep1_set_deck_lights_t _child_iidx_io_ep1_set_deck_lights; +static iidx_io_ep1_set_panel_lights_t _child_iidx_io_ep1_set_panel_lights; +static iidx_io_ep1_set_top_lamps_t _child_iidx_io_ep1_set_top_lamps; +static iidx_io_ep1_set_top_neons_t _child_iidx_io_ep1_set_top_neons; +static iidx_io_ep1_send_t _child_iidx_io_ep1_send; +static iidx_io_ep2_recv_t _child_iidx_io_ep2_recv; +static iidx_io_ep2_get_turntable_t _child_iidx_io_ep2_get_turntable; +static iidx_io_ep2_get_slider_t _child_iidx_io_ep2_get_slider; +static iidx_io_ep2_get_sys_t _child_iidx_io_ep2_get_sys; +static iidx_io_ep2_get_panel_t _child_iidx_io_ep2_get_panel; +static iidx_io_ep2_get_keys_t _child_iidx_io_ep2_get_keys; +static iidx_io_ep3_write_16seg_t _child_iidx_io_ep3_write_16seg; + +static log_formatter_t _log_formatter_misc; +static log_formatter_t _log_formatter_info; +static log_formatter_t _log_formatter_warning; +static log_formatter_t _log_formatter_fatal; + +static _Atomic(bool) _io_thread_proc_loop; +static _Atomic(bool) _io_thread_proc_running; + +static _Atomic(uint16_t) _child_iidx_io_deck_lights; +static _Atomic(uint8_t) _child_iidx_io_panel_lights; +static _Atomic(uint8_t) _child_iidx_io_top_lamps; +static _Atomic(bool) _child_iidx_io_top_neons; + +static _Atomic(uint8_t) _child_iidx_io_turntable_p1; +static _Atomic(uint8_t) _child_iidx_io_turntable_p2; +static _Atomic(uint8_t) _child_iidx_io_slider_1; +static _Atomic(uint8_t) _child_iidx_io_slider_2; +static _Atomic(uint8_t) _child_iidx_io_slider_3; +static _Atomic(uint8_t) _child_iidx_io_slider_4; +static _Atomic(uint8_t) _child_iidx_io_slider_5; +static _Atomic(uint8_t) _child_iidx_io_sys; +static _Atomic(uint8_t) _child_iidx_io_panel; +static _Atomic(uint16_t) _child_iidx_io_keys; + +static HANDLE _child_iidx_io_16seg_mutex; +static char _child_iidx_16seg[9]; +static bool _child_iidx_io_16seg_dirty; + +static int _io_thread_proc(void *ctx) +{ + uint64_t time_start; + uint64_t time_end; + uint64_t loop_counter; + uint64_t total_time; + char text_16seg[9]; + + bool result; + + atomic_store_explicit(&_io_thread_proc_running, true, memory_order_seq_cst); + + log_info("IO thread running"); + + time_start = time_get_counter(); + loop_counter = 0; + + while (atomic_load_explicit(&_io_thread_proc_loop, memory_order_seq_cst)) { + result = _child_iidx_io_ep2_recv(); + + if (!result) { + log_warning("_child_iidx_io_ep2_recv returned false"); + atomic_store_explicit( + &_io_thread_proc_running, false, memory_order_seq_cst); + + log_info("IO thread shut down"); + + return 0; + } + + atomic_store_explicit(&_child_iidx_io_turntable_p1, _child_iidx_io_ep2_get_turntable(0), memory_order_relaxed); + atomic_store_explicit(&_child_iidx_io_turntable_p2, _child_iidx_io_ep2_get_turntable(1), memory_order_relaxed); + atomic_store_explicit(&_child_iidx_io_slider_1, _child_iidx_io_ep2_get_slider(0), memory_order_relaxed); + atomic_store_explicit(&_child_iidx_io_slider_2, _child_iidx_io_ep2_get_slider(1), memory_order_relaxed); + atomic_store_explicit(&_child_iidx_io_slider_3, _child_iidx_io_ep2_get_slider(2), memory_order_relaxed); + atomic_store_explicit(&_child_iidx_io_slider_4, _child_iidx_io_ep2_get_slider(3), memory_order_relaxed); + atomic_store_explicit(&_child_iidx_io_slider_5, _child_iidx_io_ep2_get_slider(4), memory_order_relaxed); + atomic_store_explicit(&_child_iidx_io_sys, _child_iidx_io_ep2_get_sys(), memory_order_relaxed); + atomic_store_explicit(&_child_iidx_io_panel, _child_iidx_io_ep2_get_panel(), memory_order_relaxed); + atomic_store_explicit(&_child_iidx_io_keys, _child_iidx_io_ep2_get_keys(), memory_order_relaxed); + + _child_iidx_io_ep1_set_deck_lights(atomic_load_explicit(&_child_iidx_io_deck_lights, memory_order_relaxed)); + _child_iidx_io_ep1_set_panel_lights(atomic_load_explicit(&_child_iidx_io_panel_lights, memory_order_relaxed)); + _child_iidx_io_ep1_set_top_lamps(atomic_load_explicit(&_child_iidx_io_top_lamps, memory_order_relaxed)); + _child_iidx_io_ep1_set_top_neons(atomic_load_explicit(&_child_iidx_io_top_neons, memory_order_relaxed)); + + result = _child_iidx_io_ep1_send(); + + if (!result) { + log_warning("_child_iidx_io_ep1_send returned false"); + atomic_store_explicit( + &_io_thread_proc_running, false, memory_order_seq_cst); + + log_info("IO thread shut down"); + + return 0; + } + + if (_child_iidx_io_16seg_dirty) { + WaitForSingleObject(_child_iidx_io_16seg_mutex, INFINITE); + memcpy(text_16seg, _child_iidx_16seg, sizeof(text_16seg)); + _child_iidx_io_16seg_dirty = false; + ReleaseMutex(_child_iidx_io_16seg_mutex); + + result = _child_iidx_io_ep3_write_16seg(text_16seg); + + if (!result) { + log_warning("_child_iidx_io_ep3_write_16seg returned false"); + atomic_store_explicit( + &_io_thread_proc_running, false, memory_order_seq_cst); + + log_info("IO thread shut down"); + + return 0; + } + } + + // Don't hog the CPU + SwitchToThread(); + + loop_counter++; + } + + time_end = time_get_counter(); + total_time = time_get_elapsed_us(time_end - time_start); + + log_info( + "IO thread performance: total iterations %lld, avg. loop cycle time %f " + "us", + loop_counter, + ((double) total_time) / loop_counter); + + atomic_store_explicit( + &_io_thread_proc_running, false, memory_order_seq_cst); + + log_info("IO thread shut down"); + + return 0; +} + +static void *_load_function(HMODULE module, const char *name) +{ + void *ptr; + + ptr = GetProcAddress(module, name); + + if (ptr == NULL) { + log_fatal("Could not find function %s in iidxio child library", name); + } + + return ptr; +} + +void iidx_io_set_loggers( + log_formatter_t misc, + log_formatter_t info, + log_formatter_t warning, + log_formatter_t fatal) +{ + _log_formatter_misc = misc; + _log_formatter_info = info; + _log_formatter_warning = warning; + _log_formatter_fatal = fatal; + + log_to_external(misc, info, warning, fatal); +} + +bool iidx_io_init( + thread_create_t thread_create, + thread_join_t thread_join, + thread_destroy_t thread_destroy) +{ + log_info("Loading iidxio-async-child.dll as child iidxio library..."); + + _child_iidx_io_module = LoadLibraryA("iidxio-async-child.dll"); + + if (_child_iidx_io_module == NULL) { + log_warning("Loading iidxio-async-child.dll failed"); + return false; + } + + _child_iidx_io_set_loggers = + _load_function(_child_iidx_io_module, "iidx_io_set_loggers"); + _child_iidx_io_init = _load_function(_child_iidx_io_module, "iidx_io_init"); + _child_iidx_io_fini = _load_function(_child_iidx_io_module, "iidx_io_fini"); + + _child_iidx_io_ep1_set_deck_lights = + _load_function(_child_iidx_io_module, "iidx_io_ep1_set_deck_lights"); + _child_iidx_io_ep1_set_panel_lights = + _load_function(_child_iidx_io_module, "iidx_io_ep1_set_panel_lights"); + _child_iidx_io_ep1_set_top_lamps = + _load_function(_child_iidx_io_module, "iidx_io_ep1_set_top_lamps"); + _child_iidx_io_ep1_set_top_neons = + _load_function(_child_iidx_io_module, "iidx_io_ep1_set_top_neons"); + _child_iidx_io_ep1_send = + _load_function(_child_iidx_io_module, "iidx_io_ep1_send"); + _child_iidx_io_ep2_recv = + _load_function(_child_iidx_io_module, "iidx_io_ep2_recv"); + _child_iidx_io_ep2_get_turntable = + _load_function(_child_iidx_io_module, "iidx_io_ep2_get_turntable"); + _child_iidx_io_ep2_get_slider = + _load_function(_child_iidx_io_module, "iidx_io_ep2_get_slider"); + _child_iidx_io_ep2_get_sys = + _load_function(_child_iidx_io_module, "iidx_io_ep2_get_sys"); + _child_iidx_io_ep2_get_panel = + _load_function(_child_iidx_io_module, "iidx_io_ep2_get_panel"); + _child_iidx_io_ep2_get_keys = + _load_function(_child_iidx_io_module, "iidx_io_ep2_get_keys"); + _child_iidx_io_ep3_write_16seg = + _load_function(_child_iidx_io_module, "iidx_io_ep3_write_16seg"); + + _child_iidx_io_set_loggers( + _log_formatter_misc, + _log_formatter_info, + _log_formatter_warning, + _log_formatter_fatal); + + _child_iidx_io_16seg_mutex = CreateMutex(NULL, FALSE, NULL); + + log_info("Calling child iidx_io_init..."); + + if (!_child_iidx_io_init(thread_create, thread_join, thread_destroy)) { + log_warning("Child iidx_io_init failed"); + FreeLibrary(_child_iidx_io_module); + CloseHandle(_child_iidx_io_16seg_mutex); + + return false; + } + + atomic_store_explicit(&_io_thread_proc_loop, true, memory_order_seq_cst); + + if (!thread_create(_io_thread_proc, NULL, 16384, 0)) { + log_warning("Creating IO thread failed"); + + _child_iidx_io_fini(); + FreeLibrary(_child_iidx_io_module); + CloseHandle(_child_iidx_io_16seg_mutex); + return false; + } + + return true; +} + +void iidx_io_fini(void) +{ + atomic_store_explicit(&_io_thread_proc_loop, false, memory_order_seq_cst); + + log_info("Shutting down IO thread and waiting for it to finish..."); + + while ( + atomic_load_explicit(&_io_thread_proc_running, memory_order_seq_cst)) { + Sleep(1); + } + + log_info("IO thread finished"); + + _child_iidx_io_fini(); + + FreeLibrary(_child_iidx_io_module); + CloseHandle(_child_iidx_io_16seg_mutex); +} + +void iidx_io_ep1_set_deck_lights(uint16_t deck_lights) +{ + atomic_store_explicit( + &_child_iidx_io_deck_lights, deck_lights, memory_order_relaxed); +} + +void iidx_io_ep1_set_panel_lights(uint8_t panel_lights) +{ + atomic_store_explicit( + &_child_iidx_io_panel_lights, panel_lights, memory_order_relaxed); +} + +void iidx_io_ep1_set_top_lamps(uint8_t top_lamps) +{ + atomic_store_explicit( + &_child_iidx_io_top_lamps, top_lamps, memory_order_relaxed); +} + +void iidx_io_ep1_set_top_neons(bool top_neons) +{ + atomic_store_explicit( + &_child_iidx_io_top_neons, top_neons, memory_order_relaxed); +} + +bool iidx_io_ep1_send(void) +{ + // Any sending and receiving is executed async in a separate thread + return true; +} + +bool iidx_io_ep2_recv(void) +{ + // Any sending and receiving is executed async in a separate thread + return true; +} + +uint8_t iidx_io_ep2_get_turntable(uint8_t player_no) +{ + switch (player_no) + { + case 0: + return atomic_load_explicit(&_child_iidx_io_turntable_p1, memory_order_relaxed); + case 1: + return atomic_load_explicit(&_child_iidx_io_turntable_p2, memory_order_relaxed); + default: + return 0; + } +} + +uint8_t iidx_io_ep2_get_slider(uint8_t slider_no) +{ + switch (slider_no) + { + case 0: + return atomic_load_explicit(&_child_iidx_io_slider_1, memory_order_relaxed); + case 1: + return atomic_load_explicit(&_child_iidx_io_slider_2, memory_order_relaxed); + case 2: + return atomic_load_explicit(&_child_iidx_io_slider_3, memory_order_relaxed); + case 3: + return atomic_load_explicit(&_child_iidx_io_slider_4, memory_order_relaxed); + case 4: + return atomic_load_explicit(&_child_iidx_io_slider_5, memory_order_relaxed); + default: + return 0; + } +} + +uint8_t iidx_io_ep2_get_sys(void) +{ + return atomic_load_explicit(&_child_iidx_io_sys, memory_order_relaxed); +} + +uint8_t iidx_io_ep2_get_panel(void) +{ + return atomic_load_explicit(&_child_iidx_io_panel, memory_order_relaxed); +} + +uint16_t iidx_io_ep2_get_keys(void) +{ + return atomic_load_explicit(&_child_iidx_io_keys, memory_order_relaxed); +} + +bool iidx_io_ep3_write_16seg(const char *text) +{ + // This section is only producer while the thread is the only consumer + // Utilize this to optimize the 16seg writing and only write if the text has actually changed + if (!strcmp(text, _child_iidx_16seg)) { + return true; + } + + WaitForSingleObject(_child_iidx_io_16seg_mutex, INFINITE); + memcpy(_child_iidx_16seg, text, sizeof(_child_iidx_16seg)); + _child_iidx_io_16seg_dirty = true; + ReleaseMutex(_child_iidx_io_16seg_mutex); + + return true; +} \ No newline at end of file