Merge pull request #16990 from s-hadinger/artnet_optimization

ESP32 DMX ArtNet optimization to avoid any object allocation and avoid garbage collector pauses
This commit is contained in:
s-hadinger 2022-11-03 22:18:18 +01:00 committed by GitHub
commit d0cd3d3778
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 83 additions and 36 deletions

View File

@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file.
- Berry ``bytes().reverse()`` method (#16977) - Berry ``bytes().reverse()`` method (#16977)
- ESP32 Support for DMX ArtNet Led matrix animations (#16984) - 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 - 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 ### Breaking Changed

View File

@ -94,10 +94,30 @@ extern "C" {
int32_t be_udp_read(struct bvm *vm) { int32_t be_udp_read(struct bvm *vm) {
WiFiUDP *udp = (WiFiUDP*) be_convert_single_elt(vm, 1, NULL, NULL); WiFiUDP *udp = (WiFiUDP*) be_convert_single_elt(vm, 1, NULL, NULL);
if (udp->parsePacket()) { if (udp->parsePacket()) {
int btr = udp->available(); int btr = udp->available(); // btr contains the size of bytes_to_read
uint8_t * buf = (uint8_t*) be_pushbuffer(vm, btr);
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); int32_t btr2 = udp->read(buf, btr);
be_pushbytes(vm, buf, btr2);
// set remotet ip // set remotet ip
IPAddress remote_ip = udp->remoteIP(); IPAddress remote_ip = udp->remoteIP();

View File

@ -117,8 +117,14 @@ class Leds : Leds_ntv
def dirty() def dirty()
self.call_native(5) self.call_native(5)
end end
def pixels_buffer() def pixels_buffer(old_buf)
return self.call_native(6) 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 end
def pixel_size() def pixel_size()
return self.call_native(7) 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 # 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) if bool(force) || (self.offset == 0 && self.w * self.h == self.strip.leds)
self.strip.show() 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
end end
def can_show() def can_show()

View File

@ -1118,7 +1118,7 @@ be_local_closure(Leds_matrix_pixel_count, /* name */
********************************************************************/ ********************************************************************/
be_local_closure(Leds_matrix_show, /* name */ be_local_closure(Leds_matrix_show, /* name */
be_nested_proto( be_nested_proto(
4, /* nstack */ 5, /* nstack */
2, /* argc */ 2, /* argc */
2, /* varg */ 2, /* varg */
0, /* has upvals */ 0, /* has upvals */
@ -1139,29 +1139,30 @@ be_local_closure(Leds_matrix_show, /* name */
}), }),
&be_const_str_show, &be_const_str_show,
&be_const_str_solidified, &be_const_str_solidified,
( &(const binstruction[22]) { /* code */ ( &(const binstruction[23]) { /* code */
0x60080017, // 0000 GETGBL R2 G23 0x60080017, // 0000 GETGBL R2 G23
0x5C0C0200, // 0001 MOVE R3 R1 0x5C0C0200, // 0001 MOVE R3 R1
0x7C080200, // 0002 CALL R2 1 0x7C080200, // 0002 CALL R2 1
0x740A0009, // 0003 JMPT R2 #000E 0x740A0009, // 0003 JMPT R2 #000E
0x88080100, // 0004 GETMBR R2 R0 K0 0x88080100, // 0004 GETMBR R2 R0 K0
0x1C080501, // 0005 EQ R2 R2 K1 0x1C080501, // 0005 EQ R2 R2 K1
0x780A000D, // 0006 JMPF R2 #0015 0x780A000E, // 0006 JMPF R2 #0016
0x88080102, // 0007 GETMBR R2 R0 K2 0x88080102, // 0007 GETMBR R2 R0 K2
0x880C0103, // 0008 GETMBR R3 R0 K3 0x880C0103, // 0008 GETMBR R3 R0 K3
0x08080403, // 0009 MUL R2 R2 R3 0x08080403, // 0009 MUL R2 R2 R3
0x880C0104, // 000A GETMBR R3 R0 K4 0x880C0104, // 000A GETMBR R3 R0 K4
0x880C0705, // 000B GETMBR R3 R3 K5 0x880C0705, // 000B GETMBR R3 R3 K5
0x1C080403, // 000C EQ R2 R2 R3 0x1C080403, // 000C EQ R2 R2 R3
0x780A0006, // 000D JMPF R2 #0015 0x780A0007, // 000D JMPF R2 #0016
0x88080104, // 000E GETMBR R2 R0 K4 0x88080104, // 000E GETMBR R2 R0 K4
0x8C080506, // 000F GETMET R2 R2 K6 0x8C080506, // 000F GETMET R2 R2 K6
0x7C080200, // 0010 CALL R2 1 0x7C080200, // 0010 CALL R2 1
0x88080104, // 0011 GETMBR R2 R0 K4 0x88080104, // 0011 GETMBR R2 R0 K4
0x8C080508, // 0012 GETMET R2 R2 K8 0x8C080508, // 0012 GETMET R2 R2 K8
0x7C080200, // 0013 CALL R2 1 0x88100107, // 0013 GETMBR R4 R0 K7
0x90020E02, // 0014 SETMBR R0 K7 R2 0x7C080400, // 0014 CALL R2 2
0x80000000, // 0015 RET 0 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_local_closure(Leds_pixels_buffer, /* name */
be_nested_proto( be_nested_proto(
4, /* nstack */ 8, /* nstack */
1, /* argc */ 2, /* argc */
2, /* varg */ 2, /* varg */
0, /* has upvals */ 0, /* has upvals */
NULL, /* no upvals */ NULL, /* no upvals */
0, /* has sup protos */ 0, /* has sup protos */
NULL, /* no sub protos */ NULL, /* no sub protos */
1, /* has constants */ 1, /* has constants */
( &(const bvalue[ 1]) { /* constants */ ( &(const bvalue[ 4]) { /* constants */
/* K0 */ be_nested_str(call_native), /* 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_pixels_buffer,
&be_const_str_solidified, &be_const_str_solidified,
( &(const binstruction[ 4]) { /* code */ ( &(const binstruction[21]) { /* code */
0x8C040100, // 0000 GETMET R1 R0 K0 0x8C080100, // 0000 GETMET R2 R0 K0
0x540E0005, // 0001 LDINT R3 6 0x54120005, // 0001 LDINT R4 6
0x7C040400, // 0002 CALL R1 2 0x7C080400, // 0002 CALL R2 2
0x80040200, // 0003 RET 1 R1 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
}) })
) )
); );

View File

@ -6,7 +6,9 @@ class ArtNet
var udp_server # instance of `udp` class var udp_server # instance of `udp` class
var universe_start # base universe number var universe_start # base universe number
var universe_end # last universe number allowed (excluded) 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 var packet # try reusing the same packer bytes() object for performance
@ -22,10 +24,11 @@ class ArtNet
self.universe_end = universe_start + matrix.h self.universe_end = universe_start + matrix.h
if port == nil port = 6454 end if port == nil port = 6454 end
# self.artnet_sig = bytes().fromstring("Art-Net\x00") # 8 bytes
self.port = int(port) self.port = int(port)
if ip_addr == nil ip_addr = "" end 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 = udp()
self.udp_server.begin(ip_addr, self.port) self.udp_server.begin(ip_addr, self.port)
@ -38,10 +41,13 @@ class ArtNet
def fast_loop() def fast_loop()
var universe_start = self.universe_start var universe_start = self.universe_start
var universe_end = self.universe_end 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 dirty = false
var packet = self.udp_server.read(self.packet) var packet = self.udp_server.read(self.packet)
while (packet != nil) 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 opcode = packet.get(8, 2) # should be 0x5000
var protocol = packet.get(10, -2) # big endian, should be 14 var protocol = packet.get(10, -2) # big endian, should be 14
var universe = packet.get(14, 2) var universe = packet.get(14, 2)
@ -64,13 +70,12 @@ class ArtNet
# opcode, protocol, seq, phy, universe, data_len, packet[18..-1].tohex())) # opcode, protocol, seq, phy, universe, data_len, packet[18..-1].tohex()))
end end
end end
packet = self.udp_server.read(packet) packet = self.udp_server.read(self.packet)
if packet == nil if packet == nil
tasmota.delay_microseconds(20) # wait 20 us just in case 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
end end
self.packet = packet # save bytes() object for next iteration and avoid allocation of new object
if dirty if dirty
self.matrix.dirty() self.matrix.dirty()
@ -83,7 +88,9 @@ return ArtNet
#- #-
# Example for M5Stack ATOM Matrix (5x5 matrix without alternate) # Example for M5Stack ATOM Matrix (5x5 matrix without alternate)
import artnet
# var artnet = ArtNet
var strip = Leds(25, gpio.pin(gpio.WS2812, 0)) var strip = Leds(25, gpio.pin(gpio.WS2812, 0))
var m = strip.create_matrix(5, 5, 0) var m = strip.create_matrix(5, 5, 0)
var dmx = ArtNet(m) var dmx = artnet(m)
-# -#

View File

@ -151,19 +151,11 @@ extern "C" {
break; break;
case 6: // # 06 : Pixels void -> bytes() (mapped to the buffer) 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; uint8_t * pixels;
if (s_ws2812_grb) pixels = s_ws2812_grb->Pixels(); if (s_ws2812_grb) pixels = s_ws2812_grb->Pixels();
if (s_sk6812_grbw) pixels = s_sk6812_grbw->Pixels(); if (s_sk6812_grbw) pixels = s_sk6812_grbw->Pixels();
be_getbuiltin(vm, "bytes");
be_pushcomptr(vm, pixels); be_pushcomptr(vm, pixels);
be_pushint(vm, pixels_bytes);
be_call(vm, 2);
be_pop(vm, 2);
} }
break; break;
case 7: // # 07 : PixelSize void -> int case 7: // # 07 : PixelSize void -> int