E0 crash for no apparent reason

Hi! I'm currently making a reimagining of Flipnote Studio for the Playdate, but I did run into quite an issue. I want the resolution to be 400x240, nothing smaller. That is 96000 pixels, which is a tad bit too much. I made a function in the pd.update() function to do the drawing and frame filling with patterns.

local animation = {"name", {}}
table.insert(animation[2], {})
local tool_mode = true
local ui_selected_button = 0
local __ui_selected_last_frame = 0
local current_tool = 0
local pen_pattern = 0
local redrawing = false
local last_redrawn_frame = 0
local draws_per_frame = 400*2
local currently_editing_frame = 1
local frame_bg_redrawing = true
local frame_bg_last_redrawn_frame = 0
local frame_bg_draws_per_frame = 400
local frame_bg_currently_editing_frame = 1
    if redrawing and frame_bg_redrawing == false then
        local __i = 0
        for i = 1, draws_per_frame, 1 do
            __i = i
            if __i+last_redrawn_frame >= 96000 then
                redrawing = false
                last_redrawn_frame = 0
                print("Frame redrawn")
                collectgarbage("collect")
            else
                gfx.setColor(animation[2][currently_editing_frame][last_redrawn_frame+i][3])
                gfx.drawPixel(animation[2][currently_editing_frame][last_redrawn_frame+i][1], animation[2][currently_editing_frame][last_redrawn_frame+i][2])
            end
        end
        last_redrawn_frame += __i
    end

    if frame_bg_redrawing then
        local __i = 0
        if frame_bg_last_redrawn_frame == 0 then
            animation[2][frame_bg_currently_editing_frame] = {}
        end
        for i = 1, frame_bg_draws_per_frame, 1 do
            __i = i
            if __i+frame_bg_last_redrawn_frame >= 96000 then
                frame_bg_redrawing = false
                frame_bg_last_redrawn_frame = 0
                print("Frame filled")
                collectgarbage("collect")

                redrawing = true
                last_redrawn_frame = 0
                gfx.clear(white)
            else
                local x = ((__i+frame_bg_last_redrawn_frame-1)%400)
                local y = math.floor((__i+frame_bg_last_redrawn_frame)/400)
                local color = get_dithered_color(x, y, dither_pattern_list[pen_pattern+1])
                table.insert(animation[2][frame_bg_currently_editing_frame], {x, y, color})
                print("Insert x: " .. x .. " y: " .. y .. " color: " .. color)
                image_processing:draw(315, 0)
            end
        end
        frame_bg_last_redrawn_frame += __i
    end
     frame_bg_last_redrawn_frame += __i
    end

I also patternized all dither paterns:

---- DITHER PATTERNS ----
local dither_pattern_01 = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
local dither_pattern_02 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
local dither_pattern_03 = {1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1}
local dither_pattern_04 = {0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0}
local dither_pattern_05 = {0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1}
local dither_pattern_06 = {1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}
local dither_pattern_07 = {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
local dither_pattern_08 = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
local dither_pattern_09 = {0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1}
local dither_pattern_10 = {1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}
local dither_pattern_list = {dither_pattern_01, dither_pattern_02, dither_pattern_03, dither_pattern_04, dither_pattern_05, dither_pattern_06, dither_pattern_07, dither_pattern_08, dither_pattern_09, dither_pattern_10}

---- FUNCTIONS ----
function get_dithered_color(x, y, dither_pattern)
    local pattern_size = math.sqrt(#dither_pattern)
    local wrapped_x = x % pattern_size
    local wrapped_y = y % pattern_size
    return dither_pattern[(wrapped_y * pattern_size + wrapped_x)+1]
end

It works well! There's one small issue though, after filling the screen 2-3 times the playdate just throws an unexplained e0 error. Not a memory leak, I use collectgarbage at the end of filling the screen and drawing, and it cannot be the cpu being overloaded, as it runs with a static 9 fps on device.

If anybody wants to test it out on device, here is a pdx:
main.pdx.zip (34.1 KB)

I'm really hoping I can fix it, becuase this project has been on my mind for months.

What does the device info say at the time of the crash? Also, is there a crash log?

It returns nothing, but luckily even thought it took a couple of days, I fixed it. So, a single frame was 14.08 mb, so I made small optimization, that halved the memory by basically just seeing which color is in the pattern more, and then filling that color in while drawing instead of adding a bunch of extra pixels for it in the filling algorithm, so now a fully white background is an empty table of frames.

1 Like

Interesting! So it was ran after all. Hmm

A single frame should not take that much RAM. You should use:

playdate.graphics.image.new(400, 240, playdate.graphics.kColorWhite)

To create an image instead of using tables. Each pixel is only 1 bit.

yeah, that might work, but sadly I need to fill it with custom patterns, and also edit it later, as it's a drawing program

You can draw pixels, lines and other things on bitmaps. You have to use graphics::pushContext(yourBitmap) first, then you can use the usual drawing functions (like drawPixel, drawLine, etc.).

Just don’t forget to call popContext when you are done.

1 Like

Thanks so much! I'll try figuring it out!

1 Like