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)
- 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

View File

@ -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();

View File

@ -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()

View File

@ -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
})
)
);

View File

@ -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)
-#

View File

@ -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