diff --git a/radio/src/gui/480x272/bitmapbuffer.h b/radio/src/gui/480x272/bitmapbuffer.h index 7a5892f2a..1ff0ee34f 100644 --- a/radio/src/gui/480x272/bitmapbuffer.h +++ b/radio/src/gui/480x272/bitmapbuffer.h @@ -255,9 +255,9 @@ class BitmapBuffer: public BitmapBufferBase template void drawBitmap(coord_t x, coord_t y, const T * bmp, coord_t srcx=0, coord_t srcy=0, coord_t w=0, coord_t h=0, float scale=0) { - if (!data || !bmp || x >= width || y >= height) + if (!data || !bmp || x < 0 || x >= width || y < 0 || y >= height) return; - + coord_t srcw = bmp->getWidth(); coord_t srch = bmp->getHeight(); diff --git a/radio/src/lua/api_lcd.cpp b/radio/src/lua/api_lcd.cpp index d2a9ed02e..58d2b882f 100644 --- a/radio/src/lua/api_lcd.cpp +++ b/radio/src/lua/api_lcd.cpp @@ -325,10 +325,19 @@ static int luaLcdDrawSource(lua_State *L) /*luadoc @function Bitmap.open(name) -Loads a bitmap in memory, for later use with lcd.drawBitmap() +Loads a bitmap in memory, for later use with lcd.drawBitmap(). Bitmaps should be loaded only +once, returned object should be stored and used for drawing. If loading fails for whatever +reason the resulting bitmap object will have width and height set to zero. + +Bitmap loading can fail if: + * File is not found or contains invalid image + * System is low on memory + * Combined memory usage of all Lua script bitmaps exceeds certain value @param name (string) full path to the bitmap on SD card (i.e. “/IMAGES/test.bmp”) +@retval bitmap (object) a bitmap object that can be used with other bitmap functions + @notice Only available on Horus @status current Introduced in 2.2.0 @@ -337,18 +346,25 @@ static int luaOpenBitmap(lua_State * L) { const char * filename = luaL_checkstring(L, 1); - BitmapBuffer ** ptr = (BitmapBuffer **)lua_newuserdata(L, sizeof(BitmapBuffer *)); - *ptr = BitmapBuffer::load(filename); + BitmapBuffer ** b = (BitmapBuffer **)lua_newuserdata(L, sizeof(BitmapBuffer *)); - if (*ptr == NULL && G(L)->gcrunning) { - luaC_fullgc(L, 1); /* try to free some memory... */ - *ptr = BitmapBuffer::load(filename); /* try again */ + if (luaExtraMemoryUsage > LUA_MEM_EXTRA_MAX) { + // already allocated more than max allowed, fail + TRACE("luaOpenBitmap: Error, using too much memory %u/%u", luaExtraMemoryUsage, LUA_MEM_EXTRA_MAX); + *b = 0; + } + else { + *b = BitmapBuffer::load(filename); + if (*b == NULL && G(L)->gcrunning) { + luaC_fullgc(L, 1); /* try to free some memory... */ + *b = BitmapBuffer::load(filename); /* try again */ + } } - if (*ptr) { - uint32_t size = (*ptr)->getDataSize(); + if (*b) { + uint32_t size = (*b)->getDataSize(); luaExtraMemoryUsage += size; - TRACE("luaOpenBitmap: %p (%u)", *ptr, size); + TRACE("luaOpenBitmap: %p (%u)", *b, size); } luaL_getmetatable(L, LUA_BITMAPHANDLE); @@ -364,13 +380,16 @@ static BitmapBuffer * checkBitmap(lua_State * L, int index) return *b; } - /*luadoc @function Bitmap.getSize(name) -Return width, heigh of a bitmap in memory +Return width, height of a bitmap object -@param bitmap (pointer) point to a bitmap previously opened with Bipmap.open() +@param bitmap (pointer) point to a bitmap previously opened with Bitmap.open() + +@retval multiple returns 2 values: + * (number) width in pixels + * (number) height in pixels @notice Only available on Horus @@ -378,7 +397,7 @@ Return width, heigh of a bitmap in memory */ static int luaGetBitmapSize(lua_State * L) { - BitmapBuffer * b = checkBitmap(L, 1); + const BitmapBuffer * b = checkBitmap(L, 1); if (b) { lua_pushinteger(L, b->getWidth()); lua_pushinteger(L, b->getHeight()); @@ -392,11 +411,18 @@ static int luaGetBitmapSize(lua_State * L) static int luaDestroyBitmap(lua_State * L) { - BitmapBuffer * ptr = checkBitmap(L, 1); - uint32_t size = ptr->getDataSize(); - TRACE("luaDestroyBitmap: %p (%u)", ptr, size); - if (luaExtraMemoryUsage > size) luaExtraMemoryUsage -= size; - delete ptr; + BitmapBuffer * b = checkBitmap(L, 1); + if (b) { + uint32_t size = b->getDataSize(); + TRACE("luaDestroyBitmap: %p (%u)", b, size); + if (luaExtraMemoryUsage >= size) { + luaExtraMemoryUsage -= size; + } + else { + luaExtraMemoryUsage = 0; + } + delete b; + } return 0; } @@ -421,11 +447,12 @@ void registerBitmapClass(lua_State * L) Displays a bitmap at (x,y) -@param bitmap (pointer) point to a bitmap previously opened with Bipmap.open() +@param bitmap (pointer) point to a bitmap previously opened with Bitmap.open() @param x,y (positive numbers) starting coordinates -@param scale (positive numbers) scale in %, 50 divides size by two, 100 is unchanged, 200 doubles size +@param scale (positive numbers) scale in %, 50 divides size by two, 100 is unchanged, 200 doubles size. +Omitting scale draws image in 1:1 scale and is faster than specifying 100 for scale. @notice Only available on Horus @@ -434,15 +461,19 @@ Displays a bitmap at (x,y) static int luaLcdDrawBitmap(lua_State *L) { if (!luaLcdAllowed) return 0; - const BitmapBuffer * bitmap = checkBitmap(L, 1); - int x = luaL_checkinteger(L, 2); - int y = luaL_checkinteger(L, 3); - unsigned scale = luaL_optunsigned(L, 4, 0); + const BitmapBuffer * b = checkBitmap(L, 1); - if (bitmap) { - lcd->drawBitmap(x, y, bitmap, 0, 0, 0, 0, (float) scale/100); + if (b) { + unsigned int x = luaL_checkunsigned(L, 2); + unsigned int y = luaL_checkunsigned(L, 3); + unsigned int scale = luaL_optunsigned(L, 4, 0); + if (scale) { + lcd->drawBitmap(x, y, b, 0, 0, 0, 0, scale/100.0f); + } + else { + lcd->drawBitmap(x, y, b); + } } - return 0; } #elif LCD_DEPTH > 1 diff --git a/radio/src/lua/interface.cpp b/radio/src/lua/interface.cpp index 4e944ab87..654716e55 100644 --- a/radio/src/lua/interface.cpp +++ b/radio/src/lua/interface.cpp @@ -165,7 +165,7 @@ void luaClose(lua_State ** L) } else { // we can only disable Lua for the rest of the session - luaDisable(); + if (*L == lsScripts) luaDisable(); } UNPROTECT_LUA(); *L = NULL; @@ -954,6 +954,27 @@ bool luaTask(event_t evt, uint8_t scriptType, bool allowLcdUsage) return scriptWasRun; } +void checkLuaMemoryUsage() +{ +#if (LUA_MEM_MAX > 0) + uint32_t totalMemUsed = luaGetMemUsed(lsScripts); +#if defined(COLORLCD) + totalMemUsed += luaGetMemUsed(lsWidgets); + totalMemUsed += luaExtraMemoryUsage; +#endif + if (totalMemUsed > LUA_MEM_MAX) { + TRACE("checkLuaMemoryUsage(): max limit reached (%u), killing Lua", totalMemUsed); + // disable Lua scripts + luaClose(&lsScripts); + luaDisable(); +#if defined(COLORLCD) + // disable widgets + luaClose(&lsWidgets); +#endif + } +#endif +} + uint32_t luaGetMemUsed(lua_State * L) { return L ? (lua_gc(L, LUA_GCCOUNT, 0) << 10) + lua_gc(L, LUA_GCCOUNTB, 0) : 0; diff --git a/radio/src/lua/lua_api.h b/radio/src/lua/lua_api.h index 5aaa8f88c..af69f6583 100644 --- a/radio/src/lua/lua_api.h +++ b/radio/src/lua/lua_api.h @@ -118,6 +118,7 @@ extern ScriptInternalData scriptInternalData[MAX_SCRIPTS]; extern ScriptInputsOutputs scriptInputsOutputs[MAX_SCRIPTS]; void luaClose(lua_State ** L); bool luaTask(event_t evt, uint8_t scriptType, bool allowLcdUsage); +void checkLuaMemoryUsage(); void luaExec(const char * filename); void luaDoGc(lua_State * L, bool full); void luaError(lua_State * L, uint8_t error, bool acknowledge=true); diff --git a/radio/src/lua/widgets.cpp b/radio/src/lua/widgets.cpp index f7142c1cd..5f9d30a28 100644 --- a/radio/src/lua/widgets.cpp +++ b/radio/src/lua/widgets.cpp @@ -519,6 +519,7 @@ void luaInitThemesAndWidgets() // if we got panic during registration // we disable Lua for this session // luaDisable(); + luaClose(&lsWidgets); lsWidgets = 0; } UNPROTECT_LUA(); diff --git a/radio/src/main_arm.cpp b/radio/src/main_arm.cpp index aa5724195..b7cddef18 100644 --- a/radio/src/main_arm.cpp +++ b/radio/src/main_arm.cpp @@ -138,6 +138,9 @@ void periodicTick_1s() void periodicTick_10s() { checkBatteryAlarms(); +#if defined(LUA) + checkLuaMemoryUsage(); +#endif } void periodicTick() diff --git a/radio/src/targets/horus/board.h b/radio/src/targets/horus/board.h index 3c3464c2e..5fab2da26 100644 --- a/radio/src/targets/horus/board.h +++ b/radio/src/targets/horus/board.h @@ -62,7 +62,7 @@ extern "C" { #pragma clang diagnostic pop #endif - + #if !defined(SIMU) #include "usbd_cdc_core.h" #include "usbd_msc_core.h" @@ -83,6 +83,10 @@ extern "C" { #define BOOTLOADER_SIZE 0x8000 #define FIRMWARE_ADDRESS 0x08000000 +#define MB *1024*1024 +#define LUA_MEM_EXTRA_MAX (2 MB) // max allowed memory usage for Lua bitmaps (in bytes) +#define LUA_MEM_MAX (6 MB) // max allowed memory usage for complete Lua (in bytes), 0 means unlimited + // HSI is at 168Mhz (over-drive is not enabled!) #define PERI1_FREQUENCY 42000000 #define PERI2_FREQUENCY 84000000 diff --git a/radio/src/targets/taranis/board.h b/radio/src/targets/taranis/board.h index 70ac9657c..debd32833 100644 --- a/radio/src/targets/taranis/board.h +++ b/radio/src/targets/taranis/board.h @@ -91,6 +91,8 @@ extern "C" { #define BOOTLOADER_SIZE 0x8000 #define FIRMWARE_ADDRESS 0x08000000 +#define LUA_MEM_MAX (0) // max allowed memory usage for complete Lua (in bytes), 0 means unlimited + #if defined(PCBX9E) #define PERI1_FREQUENCY 42000000 #define PERI2_FREQUENCY 84000000