Norns: "error in write" talking to grid

So, it looks like we can’t go full wack at the grid LEDs. I was trying to do some simple speed-testing with this:

local g = grid.connect()

for x = 1, 16 do
    for y = 1, 8 do
        g:all(0)
        g:led(x, y, 15)
        g:refresh()
    end
end

It generally terminates early with

libmonome: error in write: Resource temporarily unavailable

FWIW, take out the g:all(0) and it’s fine.

I’m happy to start coroutining with sleep calls if that’s what’s needed. On the other hand, this might just be a pathological thing to try and do.

IIRC g:all(0) is setting every led to brightness 0 so it shouldn’t need to be called for each position on the grid. I’d recommend taking that out of the loop. The g:refresh() call sends the currently dirtied led quadrants to the device so that should normally be called outside the loop after the leds have been set. As is the code is effectively updating an entire quad per grid position.

Without g:all(0) the LEDs stay on. I can probably speed it up by just setting the previous one to 0 - more efficient. In any case, I’m writing a high-level renderer so for now I’m mostly interested in performance testing rather than specific output.

Odd that g:all() causes the problem - I wonder what makes that particularly expensive? (I have the grid serial communication spec. somewhere, so should look it up.)

Sending all(0) with each refresh cycle is essentially marking all four quardants (4x64) as dirty which means that the next call to refresh will send all 256 leds values across the serial bus:

…the implementation of the all function - it is looping over every position in the buffer and setting the led value:

…the implementation of the refresh function:

The expensive part is the result of calling all(0) and refresh() inside of a tight loop because it is forcing the entire 256 leds grid state to be updated (redundantly) for every position in quick succession thus overwhelming the grid’s usb bus. If the goal is to essentially draw a single “pixel” moving over the grid I fairly confident you’ll have much more success:

  • keeping track of the previously enabled led and turning it off specifically to avoid dirtying every grid quad
  • using a metro or the new clock system to rate limit calls to refresh() to something under 60Hz (which is about the fastest I’ve seen)

EDIT: The 4th study shows how many/most scripts work by redrawing grid state on demand or periodically (from a metro) when the leds state is known to need updating.

hey nick, nice to see you here :slight_smile:

grid should be fine at 30 or even 60 fps with full-frame updates. i think the issue is the test you’ve devised (i believe) doesn’t actually function as a speed-test. it’s doing a fast-as-possible (ie, all-at-once) refresh 128 times in as short of time as possible. (@ngwese explains how the core code is operating above).

here’s maybe a better way to speed test:

function draw()
  while true do
    x = (x+15) % 16
    grid.all(x)
    grid.refresh()
    clock.sleep(1/60)
  end
end

clock.run(draw)

and then tune the 1/60 to the desired frame rate.

you can do a fancier animation if you want of course. the only “bandwidth” to the grid via serial (libmonome) is grid.refresh so that’s what should be conditionally called at high speeds. complex drawing is just memory manipulation and should have no real impact on performance.

3 Likes

Hi Brian, glad to be here…

Yep, I guess what I should be doing for my purposes right now is sleeping at 1/60 between refreshes, and then for my own code measuring whether I’m getting the full 60fps or starting to slow down. Maybe I should write a quick amortized frame rate calculator.

(Btw, this is all for the shado renderer, which I now have partially up and running in Lua.)

1 Like

Great - thanks for the code snippets, that makes it clearer what’s going on.

shado in lua is going to blow some minds. when you released the original everyone was max patching so it didn’t get the attention it deserved!

2 Likes

True; shado 10 years ago was written in Java and scripted in Python, so there was a definite barrier between the Max world and the JVM world. With the entire environment in Lua, shado is just another norns library and there’s no impedance mismatch for users.

The impedance mismatch for me, on the other hand, is that shado is essentially a scene-graph library so I’m not sure it can benefit from the quadrant optimisations at all, unless I duplicate that machinery at the Lua level, which feels a bit daft. But its performance might be quite acceptable anyway doing full refreshes - certainly, it was when driving monomes from a MacBook over OSC.

As an aside: it would be good to dig out my original m128 and greyscale 64. What are your thoughts on supporting the original on/off models? Is there still a user community for them?

there are a huge number of mono-brightness grids out there. libmonome does an auto-conversion from vari to mono… so i’m not sure how much extra plumbing you’d need.

Possibly none, then - I’ll try it out.

i’ve mentioned this before, but also:

we could expose the single-byte led commands directly, in addition to the “default” behavior of buffering full quad updates. there’s no technical barrier, just more API for people to keep track of.

I don’t know the precise nature of the Lua/C interface or the computational complexity of Lua’s data structures (though everything seems to be stack and index based), but it might be that a primitive to blatt out an entire quad at a time from a numerically indexed Lua table is more useful.