Skip to content

Maximum update rate

wischi edited this page Feb 9, 2018 · 2 revisions

StreamDeckSharp uses internal caching to prevent blocking while sending bitmaps to the device. This means you can call SetKeyBitmap as often and as fast as you like. The overhead is <1µ (measured with .Examples.MeasureSetBitmapSpeed using a AMD FX-8350 CPU)

Make sure to use the latest version, because <= v0.1.8 glitches could happen under some circumstances.

Technical Challenges

StreamDeckSharp uses a single background thread for writing data to the device. In early implementations of the caching system image glitches appeared (see https://www.youtube.com/watch?v=tNwUG0sPmKw).

Guess 1: Data to fast for the stream deck hardware

To test this claim I rewrote the caching system to support cooldown and tested different cooldown times. With cooldown set to 70ms all of the glitches dissapeared in the video example. At this point I was pretty sure I nailed it but after some more testing (see .Examples.ImageGlitchTest) glitches showed up again 😢

I tried to set the cooldown value to 1000ms but still glitching... hm? Looks like this was not (the only) problem.

Guess 2: Race condition in StreamDeckSharp code, that overwrites a byte array that is currently beeing processed

To prevent that, I cloned the buffer directly before sending it to WriteFile this should prevent side effects if the array is changed on a different thread, but the artefacts are still there...

I wrote a new test application that sends only grayscale images to the hardware and after cloning the array I tested it if every byte is the same - and it was - but the hardware sometimes showed glitched / split images. So my best guess so far: The problem occurs after calling WriteFile, outside of StreamDeckSharp and before the USB-Wire (because we set the cooldown to 1000ms - and thats slow enough for sure)

During testing I saw something really strange. A glitch happened with data that was never sent to WriteFile. The test created millions of KeyBitmaps (single vertical white line) and threw them away (let the GC handle that) but sent a differnt kind of image (filled random color bitmap) to WriteFile - almost all glitches contained the white line 💥

Data that was never sent to WriteFile started to appear on the device. WHAT?

Guess 3: Reallocation of same memory section after GC while still in use by WinAPI WriteFile

At this point I'm pretty puzzled and started to think of more creative (aka. "crazy") ideas. I disabled optimization, made the HidClient class public, added a public List (public to prevent JIT optimizations) and added every image-data array and every report array to that list before calling WriteFile to prevent that the GC frees/reuses the memory.

You guessed it - still glitching - still containing data that wasn't sent - (and the library uses about 8GB of RAM after a few seconds ^^)

Down the rabbit hole...

I'm not skilled with kernel debugging, so I skipped a few layers and installed USBPcap to capture USB traffic. I pressed "record" - started a test application that sends only grayscale images (because it's easier to verify hex data if every byte should be the same) and to my surprise the data is perfect - but the device still shows glitches - even with with single write thread and 1000ms cooldown.

Guess what: Fixed it.

Until now every time a report was generated, a new buffer was created - filled with data and sent to WriteFile. Even when that happened only once a second (and the mainthread was creating - but not using - other byte arrays like crazy) glitches happened.

I changed the code that was responsible for creating reports to reuse the same array and copy data to that array instead of using a new array. No more glitches.

Disabled the cooldown (maybe that was not a problem in the first place) artefacts are back again - set to 75ms - works like a charm

What went wrong?

Honestly. I've no idea. Summary of what we know so far:

  • Data right before WriteFile never contains glitches.
  • Glitches contain data from other images/memory locations, even if never(!) sent to WriteFile
  • USBPCap (+Wireshark) shows correct data
  • Glitches are sent down the wire (because they can be seen on the hardware)
  • This can also happen without the caching construct in the library (if a thread floods the RAM with data)

I've not found a single case (since those two fixes) that causes problems but I'm not sure what's what the problem is if I disable cooldown, or create new buffers for each report.

If you have an idea, or know something I missed - please let me know. Thanks.