I'm getting the following, pretty sparse crash message on device:
rendering step 77601 406801
num Notes 0
num Notes 3
num Notes 2
num Notes 2
num Notes 2
num Notes 2
num Notes 1
num Notes 4
num Notes 0
num Notes 0
rendering step 78001 406801
num Notes 0
num Notes 1
num Notes 2
num Notes 2
num Notes 2
num Notes 2
num Notes 1
num Notes 2
num Notes 0
num Notes 0
stack overflow at Core/minilua/lapi.c:510
stack traceback:
[C]: in ?
On simulator, the code works well.
Here is the function where this stack overflow happens. The general purpose is to draw midi notes as pixels to images called strips. steps
is a time-unit in midi, so the larger stepWindow
is the more notes will be processed during each coroutine invocation. It is a very demanding job that would take many seconds to complete. That's why it is run in a coroutine. There is no recursion, but there are some nested for-loops in here.
function View:buildStripsYielding()
local stepWindow = lume.clamp(viewModel:getTempo(), 1, 400)
local numTracks = viewModel.numTracks
local numSteps = viewModel:getNumSteps()
local stepsPerPixel = lume.clamp(
numSteps / screenW,
1, 4
)
if numSteps / stepsPerPixel > maxStripWidth then
-- this would be too wide for comfort, bitmap size is limiting factor
stepsPerPixel = numSteps / maxStripWidth
end
stripWidth = numSteps / stepsPerPixel
print("steps per pixel", stepsPerPixel, "stripWidth", stripWidth)
listView:setNumberOfRows(numTracks)
for i = 1, numTracks do
coroutine.yield()
local curStrip = gfx.image.new(stripWidth, rowHeight)
trackStrips[i] = curStrip
end
for step = 1, numSteps, stepWindow do
coroutine.yield()
print("rendering step", step, numSteps)
for i = 1, numTracks do
local curStrip = trackStrips[i]
gfx.pushContext(curStrip)
local notes = viewModel:getNotes(i, step, step + stepWindow)
for _, note in ipairs(notes) do
for curStep = note.step, note.step + note.length do
gfx.drawPixel(floor(curStep/stepsPerPixel), ((127-note.note) / 127) * rowHeight)
end
end
gfx.popContext()
end
end
end
More specifically, when I remove the for step = 1, numSteps, stepWindow do
loop and al its nested contents, the crash does not occur.
Lowering the stepWindow size (400) to a lower value might pospone the inevitable crash.
The crash does not happen immediately, it manages to run for a few seconds
Can someone explain what causes the stack overflow and how to resolve it?
The max for-loop depth seems to be 4, which I do not consider to be extreme.
For the playing song, the tempo and thus stepWindow is clamped to the maximum of 400. The program runs at 20 fps. We can then deduce that it crashed after 10 seconds (step 78000 / 400 / 20 = 9.75). That might raise suspicion as 10 seconds is the limit at which the sdk kills a game for being unresponsive. But I would say that's a red herring in this case, as the game loop is not blocked and the screen is indeed updated 20x per second while it is running.
Full project code: MIDI-Master/EditorView.lua at da865ffb03c730fe675605331dd1e5ae9b2efb75 · ninovanhooff/MIDI-Master · GitHub