From 4bbb80e31af4c5626d118995e36ade470935fafe Mon Sep 17 00:00:00 2001 From: warriorofwire <3454741+WarriorOfWire@users.noreply.github.com> Date: Tue, 12 May 2020 21:55:18 -0700 Subject: [PATCH] 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. --- shared-bindings/vectorio/Polygon.c | 42 +--------------- shared-module/vectorio/Polygon.c | 79 +++++++++++++++++++++++------- shared-module/vectorio/Polygon.h | 5 +- 3 files changed, 65 insertions(+), 61 deletions(-) diff --git a/shared-bindings/vectorio/Polygon.c b/shared-bindings/vectorio/Polygon.c index 5f4d85e211..e9e90942a9 100644 --- a/shared-bindings/vectorio/Polygon.c +++ b/shared-bindings/vectorio/Polygon.c @@ -15,41 +15,6 @@ // #define VECTORIO_POLYGON_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__) -// Converts a list of points tuples to a flat list of ints for speedier internal use. -// Also validates the points. -static mp_obj_t _to_points_list(mp_obj_t points_tuple_list) { - size_t len = 0; - mp_obj_t *items; - mp_obj_list_get(points_tuple_list, &len, &items); - VECTORIO_POLYGON_DEBUG("polygon_points_list len: %d\n", len); - - if ( len == 0 ) { - mp_raise_TypeError_varg(translate("empty %q list"), MP_QSTR_point); - } - - mp_obj_t points_list = mp_obj_new_list(0, NULL); - - for ( size_t i = 0; i < len; ++i) { - size_t tuple_len = 0; - mp_obj_t *tuple_items; - mp_obj_tuple_get(items[i], &tuple_len, &tuple_items); - - if (tuple_len != 2) { - mp_raise_ValueError_varg(translate("%q must be a tuple of length 2"), MP_QSTR_point); - } - int value; - if (!mp_obj_get_int_maybe(tuple_items[0], &value)) { - mp_raise_ValueError_varg(translate("unsupported %q type"), MP_QSTR_point); - } - mp_obj_list_append(points_list, MP_OBJ_NEW_SMALL_INT(value)); - if (!mp_obj_get_int_maybe(tuple_items[1], &value)) { - mp_raise_ValueError_varg(translate("unsupported %q type"), MP_QSTR_point); - } - mp_obj_list_append(points_list, MP_OBJ_NEW_SMALL_INT(value)); - } - return points_list; -} -//| from typing import List, Tuple //| //| class Polygon: //| 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 if (!MP_OBJ_IS_TYPE(args[ARG_points_list].u_obj, &mp_type_list)) { mp_raise_TypeError_varg(translate("%q list must be a list"), MP_QSTR_point); } - mp_obj_t points_list = _to_points_list(args[ARG_points_list].u_obj); vectorio_polygon_t *self = m_new_obj(vectorio_polygon_t); self->base.type = &vectorio_polygon_type; - common_hal_vectorio_polygon_construct(self, points_list); + common_hal_vectorio_polygon_construct(self, args[ARG_points_list].u_obj); return MP_OBJ_FROM_PTR(self); } @@ -104,9 +68,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vectorio_polygon_get_points_obj, vectorio_polygon_obj_ STATIC mp_obj_t vectorio_polygon_obj_set_points(mp_obj_t self_in, mp_obj_t points) { vectorio_polygon_t *self = MP_OBJ_TO_PTR(self_in); - mp_obj_t points_list = _to_points_list(points); - - common_hal_vectorio_polygon_set_points(self, points_list); + common_hal_vectorio_polygon_set_points(self, points); return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_2(vectorio_polygon_set_points_obj, vectorio_polygon_obj_set_points); diff --git a/shared-module/vectorio/Polygon.c b/shared-module/vectorio/Polygon.c index 6722912c2d..7e3a3acfe9 100644 --- a/shared-module/vectorio/Polygon.c +++ b/shared-module/vectorio/Polygon.c @@ -4,6 +4,8 @@ #include "shared-module/displayio/area.h" #include "py/runtime.h" +#include "py/gc.h" + #include "stdlib.h" #include @@ -12,10 +14,57 @@ // #define VECTORIO_POLYGON_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__) +// Converts a list of points tuples to a flat list of ints for speedier internal use. +// Also validates the points. +static void _clobber_points_list(vectorio_polygon_t *self, mp_obj_t points_tuple_list) { + size_t len = 0; + mp_obj_t *items; + mp_obj_list_get(points_tuple_list, &len, &items); + VECTORIO_POLYGON_DEBUG("polygon_points_list len: %d\n", len); + + if ( len < 3 ) { + mp_raise_TypeError_varg(translate("Polygon needs at least 3 points")); + } + + if ( self->len < 2*len ) { + if ( self->points_list != NULL ) { + gc_free( self->points_list ); + } + self->points_list = gc_alloc( 2 * len * sizeof(int), false, false ); + } + self->len = 2*len; + + for ( size_t i = 0; i < len; ++i) { + size_t tuple_len = 0; + mp_obj_t *tuple_items; + mp_obj_tuple_get(items[i], &tuple_len, &tuple_items); + + if (tuple_len != 2) { + mp_raise_ValueError_varg(translate("%q must be a tuple of length 2"), MP_QSTR_point); + } + if (!mp_obj_get_int_maybe(tuple_items[0], &self->points_list[2*i])) { + self->len = 0; + gc_free( self->points_list ); + self->points_list = NULL; + mp_raise_ValueError_varg(translate("unsupported %q type"), MP_QSTR_point); + } + if (!mp_obj_get_int_maybe(tuple_items[1], &self->points_list[2*i + 1])) { + self->len = 0; + gc_free( self->points_list ); + self->points_list = NULL; + mp_raise_ValueError_varg(translate("unsupported %q type"), MP_QSTR_point); + } + } +} + + + void common_hal_vectorio_polygon_construct(vectorio_polygon_t *self, mp_obj_t points_list) { VECTORIO_POLYGON_DEBUG("%p polygon_construct\n", self); - self->points_list = points_list; + self->points_list = NULL; + self->len = 0; self->on_dirty.obj = NULL; + _clobber_points_list( self, points_list ); } @@ -23,7 +72,7 @@ mp_obj_t common_hal_vectorio_polygon_get_points(vectorio_polygon_t *self) { return self->points_list; } void common_hal_vectorio_polygon_set_points(vectorio_polygon_t *self, mp_obj_t points_list) { - self->points_list = points_list; + _clobber_points_list( self, points_list ); if (self->on_dirty.obj != NULL) { self->on_dirty.event(self->on_dirty.obj); } @@ -38,21 +87,16 @@ void common_hal_vectorio_polygon_set_on_dirty(vectorio_polygon_t *self, vectorio void common_hal_vectorio_polygon_get_area(void *polygon, displayio_area_t *area) { - VECTORIO_POLYGON_DEBUG("%p polygon get_area", polygon); vectorio_polygon_t *self = polygon; - size_t len; - mp_obj_t *points; - mp_obj_list_get(self->points_list, &len, &points); - VECTORIO_POLYGON_DEBUG(" len: %2d, points: %d\n", len, len/2); area->x1 = SHRT_MAX; area->y1 = SHRT_MAX; area->x2 = SHRT_MIN; area->y2 = SHRT_MIN; - for (size_t i=0; i < len; ++i) { - mp_int_t x = mp_obj_get_int(points[i]); + for (size_t i=0; i < self->len; ++i) { + int x = self->points_list[i]; ++i; - mp_int_t y = mp_obj_get_int(points[i]); + int y = self->points_list[i]; if (x <= area->x1) area->x1 = x-1; if (y <= area->y1) area->y1 = y-1; 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_ uint32_t common_hal_vectorio_polygon_get_pixel(void *obj, int16_t x, int16_t y) { VECTORIO_POLYGON_DEBUG("%p polygon get_pixel %d, %d\n", obj, x, y); vectorio_polygon_t *self = obj; - size_t len; - mp_obj_t *points; - mp_obj_list_get(self->points_list, &len, &points); - if (len == 0) { + if (self->len == 0) { return 0; } int winding_number = 0; - mp_int_t x1 = mp_obj_get_int(points[0]); - mp_int_t y1 = mp_obj_get_int(points[1]); - for (size_t i=2; i <= len + 1; ++i) { + int x1 = self->points_list[0]; + int y1 = self->points_list[1]; + for (size_t i=2; i <= self->len + 1; ++i) { VECTORIO_POLYGON_DEBUG(" {(%3d, %3d),", x1, y1); - mp_int_t x2 = mp_obj_get_int(points[i % len]); + int x2 = self->points_list[i % self->len]; ++i; - mp_int_t y2 = mp_obj_get_int(points[i % len]); + int y2 = self->points_list[i % self->len]; VECTORIO_POLYGON_DEBUG(" (%3d, %3d)}\n", x2, y2); if ( y1 <= y ) { if ( y2 > y && line_side(x1, y1, x2, y2, x, y) > 0 ) { diff --git a/shared-module/vectorio/Polygon.h b/shared-module/vectorio/Polygon.h index 1aef854a7b..70de9036d7 100644 --- a/shared-module/vectorio/Polygon.h +++ b/shared-module/vectorio/Polygon.h @@ -8,8 +8,9 @@ typedef struct { mp_obj_base_t base; - // A micropython List[ x, y, ... ] - mp_obj_t points_list; + // An int array[ x, y, ... ] + int *points_list; + size_t len; vectorio_event_t on_dirty; } vectorio_polygon_t;