python-elgato-streamdeck icon indicating copy to clipboard operation
python-elgato-streamdeck copied to clipboard

Faster set_key_image

Open pylogger-html opened this issue 4 months ago • 2 comments

I'm using set_key_image to draw several animated keys on my Streamdeck V2, but the performance is quite poor. On the RPI Zero, it takes ~13ms to update one key, which means that I can't have very many keys animated without performance issues. I experimented a bit and the following changes would improve the speed:

  1. Instead of writing each 1kbyte packet to libhid, write the whole key. You would assemble a bytearray with the loop and then use a single call to libhid's write() to update the image. This cuts the time to update a key to ~4ms.

-I realize that the endpoint is only 1kbytes, so for this to work it relies on the transport layer or kernel driver to segment the transfer out again. On my system, the libhid call was the bottleneck for me, so there's a big improvement and no real downside.

  1. I can scale this up to support more keys per libhid call with minimal overhead. Looks like the limit is ~14 keys per call without performance tanking. My guess is there's some internal buffer in the streamdeck limiting this.

12keys = 13ms 13 keys = 14ms 14 keys = 15ms (86kbyte) 15 keys = 35ms (90kbyte)

To take advantage of this, I added a queue_key_image which just appends data to a bytearray and then a flush routine to apply everything. I don't know if this method will work on other models, but it does allow for 60fps smooth animations on the whole deck.

Would you like me to submit a PR with the changes? Or are there other issues where this wouldn't work generally?

pylogger-html avatar Aug 24 '25 16:08 pylogger-html

Something else to consider, is how well the current code performs when using the hidraw backend?
It looks like it was blocked at some point in the past as it didnt work with the original 15 key model, but that was due to a kernel 'bug' that has long since been resolved. (the hidraw backend in the kernal had a max packet size of 1kb, and the original 15 key needed packets 8kb in size. the kernel has supported 16kb for at least 3 years now).
I know that when I made the change from the libusb to hidraw backend in a js project, it got notably faster. TLDR is From some crude performance testing, filling the whole panel on a pi 4 takes 300ms with libusb and 80ms with hidraw.


This got me curious as to at what level this was working, and how likely it was to break;

From a skim of the internals of hidapi, I suspect this will only work on linux and will not work on windows or macos. I suspect it is working because hidapi is simply writing the buffer to a file/node then writing one at a time or multiple at a time has the same effect to a receiver which needs to consider buffering.

~~But interestingly, the hid driver on linux has a max buffer length of 16KB, so I am unsure how the packet will be working at all.~~ Ah, this library is using the libusb backend, not hidraw.
Looking at the libusb backend, it appears to be issuing a write which on modern kernels appears can be passed through directly as a single write to the usb device, or it will perform some internal chunking.

As an outsider (and maintainer of a js library), I am intrigued by this, but am very worried about it breaking randomly as it looks to abuse implementation details of the hid implementations. It is likely that something like this will break with either updates to hidapi, or to the os, which makes me nervous.

Julusian avatar Aug 24 '25 18:08 Julusian

I just tested it on my windows PC and it didn't work at all, so you're totally right. My project only needs to run on linux, so I'll keep living dangerously until libhid decides to tighten up their spec compliance.

Overall, the rpi zero is pretty terrible at doing video on these things. I know they offer an animated icon set and that seems to have good performance on windows. If I can find my USB protocol analyzer in my basement, I'll check out how they're doing that.

pylogger-html avatar Sep 02 '25 03:09 pylogger-html