Skip to content

Commit 4bbb80e

Browse files
committed
vectorio: speed up polygon
This change takes polygon from 126k pixels per second fill to 240k pps fill on a reference 5 point star 50x66px polygon, updating both location and shape at 10hz. Tested on an m4 express feather. As a curiosity, the flat-out fill rate of a shape whose get_pixel is `return 0;` fills just shy of 375k pixels per second.
1 parent 2314f98 commit 4bbb80e

File tree

3 files changed

+65
-61
lines changed

3 files changed

+65
-61
lines changed

shared-bindings/vectorio/Polygon.c

Lines changed: 2 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -15,41 +15,6 @@
1515
// #define VECTORIO_POLYGON_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__)
1616

1717

18-
// Converts a list of points tuples to a flat list of ints for speedier internal use.
19-
// Also validates the points.
20-
static mp_obj_t _to_points_list(mp_obj_t points_tuple_list) {
21-
size_t len = 0;
22-
mp_obj_t *items;
23-
mp_obj_list_get(points_tuple_list, &len, &items);
24-
VECTORIO_POLYGON_DEBUG("polygon_points_list len: %d\n", len);
25-
26-
if ( len == 0 ) {
27-
mp_raise_TypeError_varg(translate("empty %q list"), MP_QSTR_point);
28-
}
29-
30-
mp_obj_t points_list = mp_obj_new_list(0, NULL);
31-
32-
for ( size_t i = 0; i < len; ++i) {
33-
size_t tuple_len = 0;
34-
mp_obj_t *tuple_items;
35-
mp_obj_tuple_get(items[i], &tuple_len, &tuple_items);
36-
37-
if (tuple_len != 2) {
38-
mp_raise_ValueError_varg(translate("%q must be a tuple of length 2"), MP_QSTR_point);
39-
}
40-
int value;
41-
if (!mp_obj_get_int_maybe(tuple_items[0], &value)) {
42-
mp_raise_ValueError_varg(translate("unsupported %q type"), MP_QSTR_point);
43-
}
44-
mp_obj_list_append(points_list, MP_OBJ_NEW_SMALL_INT(value));
45-
if (!mp_obj_get_int_maybe(tuple_items[1], &value)) {
46-
mp_raise_ValueError_varg(translate("unsupported %q type"), MP_QSTR_point);
47-
}
48-
mp_obj_list_append(points_list, MP_OBJ_NEW_SMALL_INT(value));
49-
}
50-
return points_list;
51-
}
52-
//| from typing import List, Tuple
5318
//|
5419
//| class Polygon:
5520
//| def __init__(self, points: List[ Tuple[ x, y ], ... ] ):
@@ -68,12 +33,11 @@ static mp_obj_t vectorio_polygon_make_new(const mp_obj_type_t *type, size_t n_ar
6833
if (!MP_OBJ_IS_TYPE(args[ARG_points_list].u_obj, &mp_type_list)) {
6934
mp_raise_TypeError_varg(translate("%q list must be a list"), MP_QSTR_point);
7035
}
71-
mp_obj_t points_list = _to_points_list(args[ARG_points_list].u_obj);
7236

7337
vectorio_polygon_t *self = m_new_obj(vectorio_polygon_t);
7438
self->base.type = &vectorio_polygon_type;
7539

76-
common_hal_vectorio_polygon_construct(self, points_list);
40+
common_hal_vectorio_polygon_construct(self, args[ARG_points_list].u_obj);
7741

7842
return MP_OBJ_FROM_PTR(self);
7943
}
@@ -104,9 +68,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vectorio_polygon_get_points_obj, vectorio_polygon_obj_
10468
STATIC mp_obj_t vectorio_polygon_obj_set_points(mp_obj_t self_in, mp_obj_t points) {
10569
vectorio_polygon_t *self = MP_OBJ_TO_PTR(self_in);
10670

107-
mp_obj_t points_list = _to_points_list(points);
108-
109-
common_hal_vectorio_polygon_set_points(self, points_list);
71+
common_hal_vectorio_polygon_set_points(self, points);
11072
return mp_const_none;
11173
}
11274
MP_DEFINE_CONST_FUN_OBJ_2(vectorio_polygon_set_points_obj, vectorio_polygon_obj_set_points);

shared-module/vectorio/Polygon.c

Lines changed: 60 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#include "shared-module/displayio/area.h"
55

66
#include "py/runtime.h"
7+
#include "py/gc.h"
8+
79
#include "stdlib.h"
810
#include <stdio.h>
911

@@ -12,18 +14,65 @@
1214
// #define VECTORIO_POLYGON_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__)
1315

1416

17+
// Converts a list of points tuples to a flat list of ints for speedier internal use.
18+
// Also validates the points.
19+
static void _clobber_points_list(vectorio_polygon_t *self, mp_obj_t points_tuple_list) {
20+
size_t len = 0;
21+
mp_obj_t *items;
22+
mp_obj_list_get(points_tuple_list, &len, &items);
23+
VECTORIO_POLYGON_DEBUG("polygon_points_list len: %d\n", len);
24+
25+
if ( len < 3 ) {
26+
mp_raise_TypeError_varg(translate("Polygon needs at least 3 points"));
27+
}
28+
29+
if ( self->len < 2*len ) {
30+
if ( self->points_list != NULL ) {
31+
gc_free( self->points_list );
32+
}
33+
self->points_list = gc_alloc( 2 * len * sizeof(int), false, false );
34+
}
35+
self->len = 2*len;
36+
37+
for ( size_t i = 0; i < len; ++i) {
38+
size_t tuple_len = 0;
39+
mp_obj_t *tuple_items;
40+
mp_obj_tuple_get(items[i], &tuple_len, &tuple_items);
41+
42+
if (tuple_len != 2) {
43+
mp_raise_ValueError_varg(translate("%q must be a tuple of length 2"), MP_QSTR_point);
44+
}
45+
if (!mp_obj_get_int_maybe(tuple_items[0], &self->points_list[2*i])) {
46+
self->len = 0;
47+
gc_free( self->points_list );
48+
self->points_list = NULL;
49+
mp_raise_ValueError_varg(translate("unsupported %q type"), MP_QSTR_point);
50+
}
51+
if (!mp_obj_get_int_maybe(tuple_items[1], &self->points_list[2*i + 1])) {
52+
self->len = 0;
53+
gc_free( self->points_list );
54+
self->points_list = NULL;
55+
mp_raise_ValueError_varg(translate("unsupported %q type"), MP_QSTR_point);
56+
}
57+
}
58+
}
59+
60+
61+
1562
void common_hal_vectorio_polygon_construct(vectorio_polygon_t *self, mp_obj_t points_list) {
1663
VECTORIO_POLYGON_DEBUG("%p polygon_construct\n", self);
17-
self->points_list = points_list;
64+
self->points_list = NULL;
65+
self->len = 0;
1866
self->on_dirty.obj = NULL;
67+
_clobber_points_list( self, points_list );
1968
}
2069

2170

2271
mp_obj_t common_hal_vectorio_polygon_get_points(vectorio_polygon_t *self) {
2372
return self->points_list;
2473
}
2574
void common_hal_vectorio_polygon_set_points(vectorio_polygon_t *self, mp_obj_t points_list) {
26-
self->points_list = points_list;
75+
_clobber_points_list( self, points_list );
2776
if (self->on_dirty.obj != NULL) {
2877
self->on_dirty.event(self->on_dirty.obj);
2978
}
@@ -38,21 +87,16 @@ void common_hal_vectorio_polygon_set_on_dirty(vectorio_polygon_t *self, vectorio
3887

3988

4089
void common_hal_vectorio_polygon_get_area(void *polygon, displayio_area_t *area) {
41-
VECTORIO_POLYGON_DEBUG("%p polygon get_area", polygon);
4290
vectorio_polygon_t *self = polygon;
43-
size_t len;
44-
mp_obj_t *points;
45-
mp_obj_list_get(self->points_list, &len, &points);
46-
VECTORIO_POLYGON_DEBUG(" len: %2d, points: %d\n", len, len/2);
4791

4892
area->x1 = SHRT_MAX;
4993
area->y1 = SHRT_MAX;
5094
area->x2 = SHRT_MIN;
5195
area->y2 = SHRT_MIN;
52-
for (size_t i=0; i < len; ++i) {
53-
mp_int_t x = mp_obj_get_int(points[i]);
96+
for (size_t i=0; i < self->len; ++i) {
97+
int x = self->points_list[i];
5498
++i;
55-
mp_int_t y = mp_obj_get_int(points[i]);
99+
int y = self->points_list[i];
56100
if (x <= area->x1) area->x1 = x-1;
57101
if (y <= area->y1) area->y1 = y-1;
58102
if (x >= area->x2) area->x2 = x+1;
@@ -73,22 +117,19 @@ __attribute__((always_inline)) static inline int line_side( mp_int_t x1, mp_int_
73117
uint32_t common_hal_vectorio_polygon_get_pixel(void *obj, int16_t x, int16_t y) {
74118
VECTORIO_POLYGON_DEBUG("%p polygon get_pixel %d, %d\n", obj, x, y);
75119
vectorio_polygon_t *self = obj;
76-
size_t len;
77-
mp_obj_t *points;
78-
mp_obj_list_get(self->points_list, &len, &points);
79120

80-
if (len == 0) {
121+
if (self->len == 0) {
81122
return 0;
82123
}
83124

84125
int winding_number = 0;
85-
mp_int_t x1 = mp_obj_get_int(points[0]);
86-
mp_int_t y1 = mp_obj_get_int(points[1]);
87-
for (size_t i=2; i <= len + 1; ++i) {
126+
int x1 = self->points_list[0];
127+
int y1 = self->points_list[1];
128+
for (size_t i=2; i <= self->len + 1; ++i) {
88129
VECTORIO_POLYGON_DEBUG(" {(%3d, %3d),", x1, y1);
89-
mp_int_t x2 = mp_obj_get_int(points[i % len]);
130+
int x2 = self->points_list[i % self->len];
90131
++i;
91-
mp_int_t y2 = mp_obj_get_int(points[i % len]);
132+
int y2 = self->points_list[i % self->len];
92133
VECTORIO_POLYGON_DEBUG(" (%3d, %3d)}\n", x2, y2);
93134
if ( y1 <= y ) {
94135
if ( y2 > y && line_side(x1, y1, x2, y2, x, y) > 0 ) {

shared-module/vectorio/Polygon.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@
88

99
typedef struct {
1010
mp_obj_base_t base;
11-
// A micropython List[ x, y, ... ]
12-
mp_obj_t points_list;
11+
// An int array[ x, y, ... ]
12+
int *points_list;
13+
size_t len;
1314
vectorio_event_t on_dirty;
1415
} vectorio_polygon_t;
1516

0 commit comments

Comments
 (0)