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.
This commit is contained in:
warriorofwire 2020-05-12 21:55:18 -07:00
parent 2314f988b1
commit 4bbb80e31a
3 changed files with 65 additions and 61 deletions

View File

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

View File

@ -4,6 +4,8 @@
#include "shared-module/displayio/area.h"
#include "py/runtime.h"
#include "py/gc.h"
#include "stdlib.h"
#include <stdio.h>
@ -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 ) {

View File

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