Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve pretty_poly performance #840

Merged
merged 5 commits into from
Sep 11, 2023

Conversation

MichaelBell
Copy link
Contributor

While looking at the performance of the alpha blend, I noticed there were lots of blank rows. This led me to suspect there would be some easy wins in the polygon rendering side of things.

Main change is to track the actually used tile bounds in render_nodes so the graphics callback only renders the used part of the tile (and indeed only if it is used at all). Potentially this could be done more efficiently earlier, but this way is relatively simple.

Additionally I fixed up the todo in add_line_segment_to_nodes so it's not running through out of bounds rows.

MichaelBell and others added 2 commits September 9, 2023 01:30
(cherry picked from commit 1077a12ff4fd958a7ea6d9e4fa5a86551eba5126)
@MichaelBell
Copy link
Contributor Author

This new commit should bringit into line with lowfatcode/pretty-poly#1

@Gadgetoid Gadgetoid mentioned this pull request Sep 11, 2023
@Gadgetoid
Copy link
Member

Gadgetoid commented Sep 11, 2023

Confirmed with tests on 320x240 PicoW Explorer, significant improvement in regular polygons and quite a bump in the brutal Jokerman test. Awesome stuff, thank you!

Spinny G test

Before

60 frames in 8246.00ms, 137.43ms per frame avg
60 frames in 8248.00ms, 137.47ms per frame avg

After

60 frames in 2068.00ms, 34.47ms per frame avg
60 frames in 2070.00ms, 34.50ms per frame avg

Jokerman test

Before

60 frames in 7571ms, avg 126.18ms per frame, 7.92 FPS
60 frames in 7386ms, avg 123.10ms per frame, 8.12 FPS

After

60 frames in 6457ms, avg 107.62ms per frame, 9.29 FPS
60 frames in 6368ms, avg 106.13ms per frame, 9.42 FPS

Spiny G:

import time
import math
import gc
import random
from picographics import PicoGraphics, DISPLAY_PICO_W_EXPLORER, PEN_RGB332
from picovector import PicoVector, Polygon, RegularPolygon, Rectangle, ANTIALIAS_NONE, ANTIALIAS_X4, ANTIALIAS_X16
import pngdec

display = PicoGraphics(DISPLAY_PICO_W_EXPLORER, pen_type=PEN_RGB332)
display.set_backlight(0.5)

png = pngdec.PNG(display)
png.open_file("Untitled1.png")

vector = PicoVector(display)
vector.set_antialiasing(ANTIALIAS_X4)

BLACK = display.create_pen(0, 0, 0)
WHITE = display.create_pen(255, 255, 255)

WIDTH, HEIGHT = display.get_bounds()


def bake_radial_graph(ox, oy, radius, thickness, resolution=45):
    outer_points = []
    inner_points = []

    angle_per_point = math.radians(360.0 / resolution)
    
    outer = radius
    inner = radius - thickness
    
    for x in range(resolution):
        a = angle_per_point * x

        s = math.cos(a)
        c = math.sin(a)

        in_x = int(c * inner) + ox
        out_x = int(c * outer) + ox

        in_y = int(s * inner) + oy
        out_y = int(s * outer) + oy

        outer_points.append((out_x, out_y))
        inner_points.insert(0, (in_x, in_y))
    
    return outer_points, inner_points


a = 2
d = 1

clr_outer_points, clr_inner_points = bake_radial_graph(int(WIDTH / 2), int(HEIGHT / 2), 122, 24)

outer_points, inner_points = bake_radial_graph(int(WIDTH / 2), int(HEIGHT / 2), 120, 20)
num_points = len(outer_points)

angle = 0

display.set_pen(BLACK)
display.clear()
png.decode(59, 20)


poly_list = []

inner_start = inner_points[0]
outer_start = outer_points[-1]

for hue in range(1, len(inner_points)):
    points = [inner_start, inner_points[hue], outer_points[-(hue + 1)], outer_start]
    inner_start = inner_points[hue]
    outer_start = outer_points[-(hue + 1)]
    poly_list.append(Polygon(*points))
    
points = [inner_start, inner_points[0], outer_points[-1], outer_start]
poly_list.append(Polygon(*points))

hue_map = []

for hue in range(0, len(inner_points)):
    hue_map.append(display.create_pen_hsv(hue / num_points, 1.0, 1.0))

MX = int(WIDTH / 2)
MY = int(HEIGHT / 2)


clra, clrb = Polygon(*clr_inner_points), Polygon(*clr_outer_points)
clrboth = clra, clrb

t_total = 0
f_total = 0

while True:
    t_start = time.ticks_ms()
    display.set_pen(BLACK)
    vector.draw(clra, clrb)
    #vector.draw(*clrboth)

    #vector.draw(Polygon(*clr_inner_points), Polygon(*clr_outer_points))

    for p in poly_list:
        vector.rotate(p, 8, MX, MY)

    for hue in range(0, num_points):
        display.set_pen(hue_map[hue])
        #p = Polygon(*list(poly_list[hue]))
        #vector.rotate(p, angle, int(WIDTH / 2), int(HEIGHT / 2))
        p = poly_list[hue]
        vector.draw(p)

    t_end = time.ticks_ms()
    
    t_total += t_end - t_start
    f_total += 1
    
    if f_total == 60:
        print(f"60 frames in {t_total:.02f}ms, {t_total / 60:.02f}ms per frame avg")
        f_total = 0
        t_total = 0

    display.update()
    a += d
    if a >= num_points:
        d = -1
    if a <= 2:
        d = 1
    angle += 8
    angle %= 360
    time.sleep(1.0 / 60)

Jokerman:

import time
import math
import random
import machine
from picographics import PicoGraphics, DISPLAY_PICO_W_EXPLORER, PEN_RGB332
from picovector import PicoVector, Polygon, RegularPolygon, Rectangle, ANTIALIAS_NONE, ANTIALIAS_X4, ANTIALIAS_X16

machine.freq(250_000_000)

display = PicoGraphics(DISPLAY_PICO_W_EXPLORER, pen_type=PEN_RGB332)
display.set_backlight(0.8)

vector = PicoVector(display)
vector.set_antialiasing(ANTIALIAS_NONE)


RED = display.create_pen(255, 0, 0)
ORANGE = display.create_pen(255, 128, 0)
YELLOW = display.create_pen(255, 255, 0)
GREEN = display.create_pen(0, 255, 0)
BLUE = display.create_pen(0, 0, 255)
VIOLET = display.create_pen(255, 0, 255)

BLACK = display.create_pen(0, 0, 0)
GREY = display.create_pen(128, 128, 128)
WHITE = display.create_pen(255, 255, 255)
result = vector.set_font("/Jokerman-Regular.af", 30)

WIDTH, HEIGHT = display.get_bounds()

X = int(WIDTH / 2)
Y = int(HEIGHT / 2)

a = 0

frames = 0

t_count = 0
t_total = 0

while True:
    tstart = time.ticks_ms()
    display.set_pen(BLACK)
    display.clear()
    for x in range(8):
        c = 31 + (x * 32)
        display.set_pen(display.create_pen(255-c, 0, c))
        vector.text("Hello World\nHello World", X, Y, angle=x * 45 + a)
    display.update()
    a += 1

    
    tfinish = time.ticks_ms()

    total = tfinish - tstart
    t_total += total
    t_count += 1

    if t_count == 60:
        per_frame_avg = t_total / t_count
        print(f"60 frames in {t_total}ms, avg {per_frame_avg:.02f}ms per frame, {1000/per_frame_avg:.02f} FPS")
        t_count = 0
        t_total = 0

    # pause for a moment (important or the USB serial device will fail)
    # try to pace at 60fps or 30fps
    if total > 1000 / 30:
        time.sleep(0.0001)
    elif total > 1000 / 60:
        t = 1000 / 30 - total
        time.sleep(t / 1000)
    else:
        t = 1000 / 60 - total
        time.sleep(t / 1000)

@Gadgetoid Gadgetoid merged commit c3919bd into pimoroni:main Sep 11, 2023
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants