ESP32 runs out of some RAM when using 64x96

I can successfully use 64x96 with small code.
However, when I plugged SmartMatrix into bigger code, it runs out of RAM, but I’m not sure which one.
Will I be better off if I switch to teensy 3.6?

Going back to 64x64 works, and I can artificially go to 64x86 (which displays wrong, but is useful to debug memory use).
With that resolution, here is what I get:

I only use 2 layers:

const uint8_t kMatrixWidth = 64;
const uint8_t kMatrixHeight = 86;
const uint8_t kRefreshDepth = 24;       // known working: 24, 36, 48
const uint8_t kDmaBufferRows = 2;       // known working: 2-4, use 2 to save memory, more to keep from dropping frames and automatically lowering refresh rate
const uint8_t kMatrixOptions = (SMARTMATRIX_OPTIONS_NONE);      // see http://docs.pixelmatix.com/SmartMatrix for options
const uint8_t kBackgroundLayerOptions = (SM_BACKGROUND_OPTIONS_NONE);
SMARTMATRIX_ALLOCATE_BUFFERS(matrixLayer, kMatrixWidth, kMatrixHeight, kRefreshDepth, kDmaBufferRows, kPanelType, kMatrixOptions);
SMARTMATRIX_ALLOCATE_BACKGROUND_LAYER(backgroundLayer, kMatrixWidth, kMatrixHeight, COLOR_DEPTH, kBackgroundLayerOptions);

and I have a 3rd 24bit array in which I render my pixels for compatibility with NeoMatrix.
I suppose I must use at least 2 layers, correct?

Starting SmartMatrix Mallocs
Heap Memory Available: 281636 bytes total, 113792 bytes largest free block: 
8-bit Accessible Memory Available: 194284 bytes total, 113792 bytes largest free block: 
32-bit Memory Available: 281636 bytes total, 113792 bytes largest free block: 
DMA Memory Available: 194284 bytes total, 113792 bytes largest free block: 
SmartMatrix Layers Allocated from Heap:
Heap Memory Available: 247096 bytes total, 113792 bytes largest free block: 
Starting SmartMatrix DMA Mallocs
sizeof framestruct: 0000AC00
matrixUpdateFrames[0] pointer: 3FFE4374
matrixUpdateFrames[1] pointer: 3FFEEF84
Frame Structs Allocated from Heap:
Heap Memory Available: 157936 bytes total, 87352 bytes largest free block: 
8-bit Accessible Memory Available: 70584 bytes total, 25696 bytes largest free block: 
32-bit Memory Available: 157936 bytes total, 87352 bytes largest free block: 
DMA Memory Available: 70584 bytes total, 25696 bytes largest free block: 
Allocating refresh buffer:
DMA Memory Available: 70584 bytes total, 25696 bytes largest free block: 
lsbMsbTransitionBit of 0 requires 49152 RAM, 25696 available, leaving -23456 free: 
lsbMsbTransitionBit of 1 requires 24576 RAM, 25696 available, leaving 1120 free: 
Raised lsbMsbTransitionBit to 1/7 to fit in RAM
lsbMsbTransitionBit of 1 gives 56 Hz refresh, 120 requested: 
lsbMsbTransitionBit of 2 gives 111 Hz refresh, 120 requested: 
lsbMsbTransitionBit of 3 gives 213 Hz refresh, 120 requested: 
Raised lsbMsbTransitionBit to 3/7 to meet minimum refresh rate
Descriptors for lsbMsbTransitionBit 3/7 with 16 rows require 6144 bytes of DMA RAM
SmartMatrix Mallocs Complete
Heap Memory Available: 151760 bytes total, 87352 bytes largest free block: 
8-bit Accessible Memory Available: 64408 bytes total, 25696 bytes largest free block: 
32-bit Memory Available: 151760 bytes total, 87352 bytes largest free block: 
DMA Memory Available: 64408 bytes total, 25696 bytes largest free block:

Oh, 2 more things I forgot:

  1. if I compile with height of 96, and lzwMaxBits=12, I get this at build time:
    xtensa-esp32-elf/bin/ld: region `dram0_0_seg’ overflowed by 12040 bytes

  2. Now, if I lower lzwMaxBits to 11, it gets closer:
    region `dram0_0_seg’ overflowed by 3848 bytes

  3. 10 finally builds, but crashes at start immediately

Global variables use 114948 bytes (35%) of dynamic memory, leaving 212732 bytes for local variables. Maximum is 327680 bytes

06:49:29.342 -> rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
06:49:29.343 -> configsip: 0, SPIWP:0xee
06:49:29.343 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
06:49:29.344 -> mode:DIO, clock div:1
06:49:29.345 -> load:0x3fff0018,len:4
06:49:29.345 -> load:0x3fff001c,len:808
06:49:29.345 -> load:0x40078000,len:6084
06:49:29.346 -> load:0x40080000,len:6696
06:49:29.346 -> entry 0x400802e4
06:49:29.412 -> E (172) boot: Assert failed in start_cpu0_default, /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/esp32/cpu_start.c:378 (res == pdTRUE)

Ah, this could be related to your GIF decoding issue in the other thread. ESP32 doesn’t seem to like large static buffers. As you might be able to tell if you compare the Teensy and ESP32 SmartMatrix Library code, I replaced a lot of static buffers with malloc() calls to get code to work better with large display sizes. I have a feeling static buffers are allocated before the ESP32 sets up its RAM tables or something, and there are overlaps created, or some other bad problem.

I’d go through the GIF decoder and replace the large static buffers with malloc() calls.

Thanks for the quick answer and that suggestion. I gave it a shot:

(hopefully I got that patch right, I’m starting to get rusty in C++)

On the plus side, I verified the standalone gif viewer still works when I use it with this new code.
On the 2nd plus side, if I remove gif animations from my master code which includes a lot of other demo code, I also verified that it now works in 96x64
On the 3rd plus side, the combined code now builds with lzwMaxBits == 12, when it would fail at compilation time before.

But as you probably guessed by now, the combined code does not work anymore as soon as I re-add gif decoding in it. It crashes at boot, apparently before SmartMatrix even has a chance to init.

Global variables use 110860 bytes (33%) of dynamic memory, leaving 216820 bytes for local variables. Maximum is 327680 bytes.

rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
0x40088960: invoke_abort at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/esp32/panic.c line 140
0x40088b63: abort at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/esp32/panic.c line 149
0x40082b72: start_cpu0_default at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/esp32/cpu_start.c line 381
0x40082d04: call_start_cpu0 at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/esp32/cpu_start.c line 213

What’s weird is that it crashes before I can get my first Serial.println in setup() (before the matrix is initialized or gif decoding is intialized).
Did I miss other arrays maybe?

I think you might have confused prefix and suffix. You’re allocating LZW_SIZTABLE*2 for suffix and not prefix.

Not sure if that’s the issue

Good catch, thank you. Another proof that friends don’t let sleep deprived friends, code :wink:
But indeed it doesn’t fix the problem at hand in the combined code as it just crashes as long as the gif decoding code is linked into the final binary.
It crashes before the first Serial.println in setup() has a chance to run (and obviously before the mallocs get to run too). Perplexing…
It still seems that the resulting code or static variables is too big to run, but not detected by the compiler at build time. Said compiler is even quite happy with the result it seems:

Global variables use 110860 bytes (33%) of dynamic memory, leaving 216820 bytes for local variables. Maximum is 327680 bytes.

Mmmh, right, the problem is indeed
GifDecoder<kMatrixWidth, kMatrixHeight, lzwMaxBits> decoder;
which happens before setup() ever has a chance to run but must be global due to the use of templates. Sigh for templates…

As soon as I comment this out, the rest of my code works. Now I need to find what’s left in it that causes the ESP32 to crash so early.

Edit: found more arrays (not super easy to grep for)
rgb_24 palette[256];
char tempBuffer[260];
uint8_t imageData[maxGifWidth * maxGifHeight];
uint8_t imageDataBU[maxGifWidth * maxGifHeight];

working on removing those, will report back

@louis, I’m stumped, I’ll admit.
The good news is that converting the last arrays to malloc fixes the problem of code integration and crash at start.
The bad news is that standalone animatedgifs does not do the right thing anymore when I switch those 2 vars to malloc:

    // Buffer image data is decoded into
    //uint8_t *imageData = (uint8_t *) malloc(maxGifWidth * maxGifHeight);
    uint8_t imageData[maxGifWidth * maxGifHeight];

    // Backup image data buffer for saving portions of image disposal method == 3
    //uint8_t *imageDataBU = (uint8_t *) malloc(maxGifWidth * maxGifHeight);;
    uint8_t imageDataBU[maxGifWidth * maxGifHeight];

See patch:

It’s not a malloc failure problem, I just moved the malloc inline with where the arrays are defined, just to make sure I’m not doing it wrong.
Maybe the malloc pointer somehow is in a location that can’t be accessed from where the code needs it later? (given that ESP32 has multiple memory regions it seems)
Can you guess what’s going on better than me?

From what I can tell, I am not out of RAM

Malloc stack in GifDecoder startDecoding. Requested bytes: 4096
Malloc prefix in GifDecoder startDecoding. Requested bytes: 8192
Malloc suffix in GifDecoder startDecoding. Requested bytes: 4096
Malloc palette in GifDecoder startDecoding. Requested bytes: 768
Malloc tempBuffer in GifDecoder startDecoding. Requested bytes: 260
Heap Memory Available: 241984 bytes total, 113792 bytes largest free block: 
8-bit Accessible Memory Available: 153764 bytes total, 113792 bytes largest free block: 
32-bit Memory Available: 241984 bytes total, 113792 bytes largest free block: 
DMA Memory Available: 153764 bytes total, 113792 bytes largest free block: 

Got a possible clue here espressif/arduino-esp32 - Gitter
Will scan for that tomorrow

got bitten by sizeof
Convert imageData imageDataBU to malloc (fix for sizeof not working a… · marcmerlin/AnimatedGIFs@4787f84 · GitHub fixes it.

@Louis , you’ll want to apply something like this to your tree (minus 2 files that you don’t have). See diff below:

As for Animated gif crashes ESP32 output? - #4 I got that GIF to works once in ESP32 while debugging/shifting things around, but it hangs again. So, the decoder is likely ok indeed, but there seems to be some weird bug triggered somewhere I haven’t found when that specific GIF is decoded on ESP32, even after my malloc changes.

Glad you got it working Marc! I’ve been bitten by the same sizeof/malloc number of bytes vs number of array units issues.

As for that one GIF, obviously we’d like to fix the decoder, but in the meantime, can you run it through ezgif.com or another tool that will re-encode it and see if you can get it playing through the current decoder?

@louis I opened this ESP32 bug Compiler/linker should do a better job handling global arrays instead of crashing at runtime before setup() runs · Issue #2567 · espressif/arduino-esp32 · GitHub that talks about stack allocation issues on ESP32. Feel free to add some personal things you have seen and more details if you’d like.

@Louis as another followup, I was pointed out to this post that explains the RAM limits on ESP32: [Question]: Internal RAM 520, allowed for allocation 301KiB ? missing 219? · Issue #1934 · espressif/esp-idf · GitHub

1 Like

That’s a good reference, thanks for sharing