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:
parent
2314f988b1
commit
4bbb80e31a
|
@ -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);
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue