From a5e791770486dcbf17db68c0ba01e0a90813b558 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Thu, 3 Nov 2022 21:54:21 +0100 Subject: [PATCH] ESP32 DMX ArtNet optimization to avoid any object allocation and avoid garbage collector pauses --- CHANGELOG.md | 1 + lib/libesp32/berry_tasmota/src/be_udp_lib.cpp | 26 ++++++++-- .../berry_tasmota/src/embedded/leds.be | 12 +++-- .../src/solidify/solidified_leds.h | 51 +++++++++++++------ tasmota/berry/artnet/artnet.be | 21 +++++--- .../xdrv_52_3_berry_leds.ino | 8 --- 6 files changed, 83 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac4a43954..d5c140e23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file. - Berry ``bytes().reverse()`` method (#16977) - ESP32 Support for DMX ArtNet Led matrix animations (#16984) - Command ``SetOption47 1..255`` to delay power on relay state in seconds reducing power surge. ``SO47 1`` delays until network connected. ``SO47 2`` delays until mqtt connected +- ESP32 DMX ArtNet optimization to avoid any object allocation and avoid garbage collector pauses ### Breaking Changed diff --git a/lib/libesp32/berry_tasmota/src/be_udp_lib.cpp b/lib/libesp32/berry_tasmota/src/be_udp_lib.cpp index 439771fba..4186673c5 100644 --- a/lib/libesp32/berry_tasmota/src/be_udp_lib.cpp +++ b/lib/libesp32/berry_tasmota/src/be_udp_lib.cpp @@ -94,10 +94,30 @@ extern "C" { int32_t be_udp_read(struct bvm *vm) { WiFiUDP *udp = (WiFiUDP*) be_convert_single_elt(vm, 1, NULL, NULL); if (udp->parsePacket()) { - int btr = udp->available(); - uint8_t * buf = (uint8_t*) be_pushbuffer(vm, btr); + int btr = udp->available(); // btr contains the size of bytes_to_read + + int argc = be_top(vm); + if (argc >= 2 && be_isbytes(vm, 2)) { + // we have already a bytes() buffer + be_pushvalue(vm, 2); // push on top + // resize to btr + be_getmember(vm, -1, "resize"); + be_pushvalue(vm, -2); + be_pushint(vm, btr); + be_call(vm, 2); + be_pop(vm, 3); + } else { + be_pushbytes(vm, nullptr, btr); // allocate a buffer of size btr filled with zeros + } + + // get the address of the buffer + be_getmember(vm, -1, "_buffer"); + be_pushvalue(vm, -2); + be_call(vm, 1); + uint8_t * buf = (uint8_t*) be_tocomptr(vm, -2); + be_pop(vm, 2); + int32_t btr2 = udp->read(buf, btr); - be_pushbytes(vm, buf, btr2); // set remotet ip IPAddress remote_ip = udp->remoteIP(); diff --git a/lib/libesp32/berry_tasmota/src/embedded/leds.be b/lib/libesp32/berry_tasmota/src/embedded/leds.be index 82b154186..23f24ef07 100644 --- a/lib/libesp32/berry_tasmota/src/embedded/leds.be +++ b/lib/libesp32/berry_tasmota/src/embedded/leds.be @@ -117,8 +117,14 @@ class Leds : Leds_ntv def dirty() self.call_native(5) end - def pixels_buffer() - return self.call_native(6) + def pixels_buffer(old_buf) + var buf = self.call_native(6) # address of buffer in memory + if old_buf == nil + return bytes(buf, self.pixel_size() * self.pixel_count()) + else + old_buf._change_buffer(buf) + return old_buf + end end def pixel_size() return self.call_native(7) @@ -275,7 +281,7 @@ class Leds : Leds_ntv # don't trigger on segment, you will need to trigger on full strip instead if bool(force) || (self.offset == 0 && self.w * self.h == self.strip.leds) self.strip.show() - self.pix_buffer = self.strip.pixels_buffer() # update buffer after show() + self.pix_buffer = self.strip.pixels_buffer(self.pix_buffer) # update buffer after show() end end def can_show() diff --git a/lib/libesp32/berry_tasmota/src/solidify/solidified_leds.h b/lib/libesp32/berry_tasmota/src/solidify/solidified_leds.h index 589c59460..8c8692405 100644 --- a/lib/libesp32/berry_tasmota/src/solidify/solidified_leds.h +++ b/lib/libesp32/berry_tasmota/src/solidify/solidified_leds.h @@ -1118,7 +1118,7 @@ be_local_closure(Leds_matrix_pixel_count, /* name */ ********************************************************************/ be_local_closure(Leds_matrix_show, /* name */ be_nested_proto( - 4, /* nstack */ + 5, /* nstack */ 2, /* argc */ 2, /* varg */ 0, /* has upvals */ @@ -1139,29 +1139,30 @@ be_local_closure(Leds_matrix_show, /* name */ }), &be_const_str_show, &be_const_str_solidified, - ( &(const binstruction[22]) { /* code */ + ( &(const binstruction[23]) { /* code */ 0x60080017, // 0000 GETGBL R2 G23 0x5C0C0200, // 0001 MOVE R3 R1 0x7C080200, // 0002 CALL R2 1 0x740A0009, // 0003 JMPT R2 #000E 0x88080100, // 0004 GETMBR R2 R0 K0 0x1C080501, // 0005 EQ R2 R2 K1 - 0x780A000D, // 0006 JMPF R2 #0015 + 0x780A000E, // 0006 JMPF R2 #0016 0x88080102, // 0007 GETMBR R2 R0 K2 0x880C0103, // 0008 GETMBR R3 R0 K3 0x08080403, // 0009 MUL R2 R2 R3 0x880C0104, // 000A GETMBR R3 R0 K4 0x880C0705, // 000B GETMBR R3 R3 K5 0x1C080403, // 000C EQ R2 R2 R3 - 0x780A0006, // 000D JMPF R2 #0015 + 0x780A0007, // 000D JMPF R2 #0016 0x88080104, // 000E GETMBR R2 R0 K4 0x8C080506, // 000F GETMET R2 R2 K6 0x7C080200, // 0010 CALL R2 1 0x88080104, // 0011 GETMBR R2 R0 K4 0x8C080508, // 0012 GETMET R2 R2 K8 - 0x7C080200, // 0013 CALL R2 1 - 0x90020E02, // 0014 SETMBR R0 K7 R2 - 0x80000000, // 0015 RET 0 + 0x88100107, // 0013 GETMBR R4 R0 K7 + 0x7C080400, // 0014 CALL R2 2 + 0x90020E02, // 0015 SETMBR R0 K7 R2 + 0x80000000, // 0016 RET 0 }) ) ); @@ -1452,24 +1453,44 @@ be_local_closure(Leds_create_matrix, /* name */ ********************************************************************/ be_local_closure(Leds_pixels_buffer, /* name */ be_nested_proto( - 4, /* nstack */ - 1, /* argc */ + 8, /* nstack */ + 2, /* argc */ 2, /* varg */ 0, /* has upvals */ NULL, /* no upvals */ 0, /* has sup protos */ NULL, /* no sub protos */ 1, /* has constants */ - ( &(const bvalue[ 1]) { /* constants */ + ( &(const bvalue[ 4]) { /* constants */ /* K0 */ be_nested_str(call_native), + /* K1 */ be_nested_str(pixel_size), + /* K2 */ be_nested_str(pixel_count), + /* K3 */ be_nested_str(_change_buffer), }), &be_const_str_pixels_buffer, &be_const_str_solidified, - ( &(const binstruction[ 4]) { /* code */ - 0x8C040100, // 0000 GETMET R1 R0 K0 - 0x540E0005, // 0001 LDINT R3 6 - 0x7C040400, // 0002 CALL R1 2 - 0x80040200, // 0003 RET 1 R1 + ( &(const binstruction[21]) { /* code */ + 0x8C080100, // 0000 GETMET R2 R0 K0 + 0x54120005, // 0001 LDINT R4 6 + 0x7C080400, // 0002 CALL R2 2 + 0x4C0C0000, // 0003 LDNIL R3 + 0x1C0C0203, // 0004 EQ R3 R1 R3 + 0x780E0009, // 0005 JMPF R3 #0010 + 0x600C0015, // 0006 GETGBL R3 G21 + 0x5C100400, // 0007 MOVE R4 R2 + 0x8C140101, // 0008 GETMET R5 R0 K1 + 0x7C140200, // 0009 CALL R5 1 + 0x8C180102, // 000A GETMET R6 R0 K2 + 0x7C180200, // 000B CALL R6 1 + 0x08140A06, // 000C MUL R5 R5 R6 + 0x7C0C0400, // 000D CALL R3 2 + 0x80040600, // 000E RET 1 R3 + 0x70020003, // 000F JMP #0014 + 0x8C0C0303, // 0010 GETMET R3 R1 K3 + 0x5C140400, // 0011 MOVE R5 R2 + 0x7C0C0400, // 0012 CALL R3 2 + 0x80040200, // 0013 RET 1 R1 + 0x80000000, // 0014 RET 0 }) ) ); diff --git a/tasmota/berry/artnet/artnet.be b/tasmota/berry/artnet/artnet.be index 001987736..1fcdd5053 100644 --- a/tasmota/berry/artnet/artnet.be +++ b/tasmota/berry/artnet/artnet.be @@ -6,7 +6,9 @@ class ArtNet var udp_server # instance of `udp` class var universe_start # base universe number var universe_end # last universe number allowed (excluded) - static var artnet_sig = bytes().fromstring("Art-Net\x00") # 8 bytes # signature of packet + # static var artnet_sig = bytes().fromstring("Art-Net\x00") # 8 bytes # signature of packet + static var artnet_sig_0 = 0x4172742D # "Art-" + static var artnet_sig_4 = 0x4E657400 # "Net\x00" var packet # try reusing the same packer bytes() object for performance @@ -22,10 +24,11 @@ class ArtNet self.universe_end = universe_start + matrix.h if port == nil port = 6454 end - # self.artnet_sig = bytes().fromstring("Art-Net\x00") # 8 bytes self.port = int(port) if ip_addr == nil ip_addr = "" end + self.packet = bytes() # instanciate a single bytes() buffer that will be used for all received packets + self.udp_server = udp() self.udp_server.begin(ip_addr, self.port) @@ -38,10 +41,13 @@ class ArtNet def fast_loop() var universe_start = self.universe_start var universe_end = self.universe_end + var artnet_sig_0 = self.artnet_sig_0 + var artnet_sig_4 = self.artnet_sig_4 var dirty = false var packet = self.udp_server.read(self.packet) while (packet != nil) - if size(packet) >= 18 && packet[0..7] == self.artnet_sig # check that we have a packet containing the 8 bytes header + if size(packet) >= 18 && + packet.get(0, -4) == artnet_sig_0 && packet.get(4, -4) == artnet_sig_4 var opcode = packet.get(8, 2) # should be 0x5000 var protocol = packet.get(10, -2) # big endian, should be 14 var universe = packet.get(14, 2) @@ -64,13 +70,12 @@ class ArtNet # opcode, protocol, seq, phy, universe, data_len, packet[18..-1].tohex())) end end - packet = self.udp_server.read(packet) + packet = self.udp_server.read(self.packet) if packet == nil tasmota.delay_microseconds(20) # wait 20 us just in case - packet = self.udp_server.read(packet) + packet = self.udp_server.read(self.packet) end end - self.packet = packet # save bytes() object for next iteration and avoid allocation of new object if dirty self.matrix.dirty() @@ -83,7 +88,9 @@ return ArtNet #- # Example for M5Stack ATOM Matrix (5x5 matrix without alternate) +import artnet +# var artnet = ArtNet var strip = Leds(25, gpio.pin(gpio.WS2812, 0)) var m = strip.create_matrix(5, 5, 0) -var dmx = ArtNet(m) +var dmx = artnet(m) -# \ No newline at end of file diff --git a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_leds.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_leds.ino index 54c6c2c15..3d6b8f86b 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_leds.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_leds.ino @@ -151,19 +151,11 @@ extern "C" { break; case 6: // # 06 : Pixels void -> bytes() (mapped to the buffer) { - size_t pixels_bytes; - if (s_ws2812_grb) pixels_bytes = s_ws2812_grb->PixelsSize(); - if (s_sk6812_grbw) pixels_bytes = s_sk6812_grbw->PixelsSize(); - uint8_t * pixels; if (s_ws2812_grb) pixels = s_ws2812_grb->Pixels(); if (s_sk6812_grbw) pixels = s_sk6812_grbw->Pixels(); - be_getbuiltin(vm, "bytes"); be_pushcomptr(vm, pixels); - be_pushint(vm, pixels_bytes); - be_call(vm, 2); - be_pop(vm, 2); } break; case 7: // # 07 : PixelSize void -> int