Skip to content

Conversation

FoamyGuy
Copy link
Collaborator

@FoamyGuy FoamyGuy commented Mar 4, 2023

attempt to resolve #7485

Added code to run background tasks and bail if interrupted.

Tested successfully on PyPortal Titano with the reproducer code in the issue.

This resolves the problem of getting "stuck" during the fill, but the underlying crux is that boundary_fill is fairly slow for filling large spaces. The current implementation is based on this https://en.wikipedia.org/wiki/Flood_fill#Moving_the_recursion_into_a_data_structure There some other more efficient algorithms discussed further down on that page. Perhaps there is some performance to gain using one of those implementations instead, probably still on the slower side for large spaces on some devices, but maybe better than current version.

Copy link
Member

@gamblor21 gamblor21 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code looks good to me, my only thought is have you looked at performance? If you are checking every loop that may have an effect on speed more then intended.

@FoamyGuy
Copy link
Collaborator Author

FoamyGuy commented Mar 6, 2023

I'm not sure why the unix port build is failing. It reports this error:

../../shared-module/bitmaptools/__init__.c: In function ‘common_hal_bitmaptools_boundary_fill’:
../../shared-module/bitmaptools/__init__.c:380:9: error: ‘RUN_BACKGROUND_TASKS’ undeclared (first use in this function)
  380 |         RUN_BACKGROUND_TASKS;
      |         ^~~~~~~~~~~~~~~~~~~~

Which seems like perhaps there is a missing #include that is needed. But I'm not sure which one if that is the case. Atmel and Esspressif ports do build successfully for me locally.

I found the command to build the unix port locally and I see the same error as in the actions.

It seems like RUN_BACKGROUND_TASKS is defined in py/circuitpy_mpconfig.h so I tired adding this:

#include "py/circuitpy_mpconfig.h"

but it results in this error:

In file included from ../../shared-module/bitmaptools/__init__.c:37:
../../py/circuitpy_mpconfig.h:221:10: fatal error: mpconfigboard.h: No such file or directory
  221 | #include "mpconfigboard.h"
      |          ^~~~~~~~~~~~~~~~~

I'm not not sure that would have been a correct solution anyway though because other usages of RUN_BACKGROUND_TASKS; in other places do not have that import and seem to be working fine without it.

@dhalbert
Copy link
Collaborator

dhalbert commented Mar 6, 2023

Looking at other shared-module/ examples with RUN_BACKGROUND_TASKS, try

#include "py/runtime.h"

@FoamyGuy
Copy link
Collaborator Author

FoamyGuy commented Mar 6, 2023

It's got py/runtime.h include already here:

#include "py/runtime.h"

Could it need a different order or something more specific to the function where it's used?

@dhalbert
Copy link
Collaborator

dhalbert commented Mar 6, 2023

Order shouldn't matter, but I'm wondering if the unix port is not compiling the other files with RUN_BACKGROUND_TASKS. The most likely one to be compiled is shared-module/os/__init__.c. You can try putting an #error in that file and see whether it gets hit when you do the unix port compile. if it does get compiled, then check what that includes and how, and try to replicate.

@FoamyGuy
Copy link
Collaborator Author

FoamyGuy commented Mar 6, 2023

Ahh, it looks like you are correct on that.

I added #error inside of shared-module/os/__init__.c and it does not stop the unix build from completing (if I remove the RUN_BACKGROUND_TASKS stuff that the PR is adding). So it seems unix port doesn't build that file.

I did also double check with the #error in that file an esspresif build does fail due to the #error. So it shows unix port must not be attempting to compile that file since it's able to succeed where the esspressif one did not.

@FoamyGuy
Copy link
Collaborator Author

I'm not really sure how to move this forward. I'm guessing it comes down to two high level options:

  • Make the Unix port have access to RUN_BACKGROUND_TASKS as the other builds do. That should allow it to successfully complete the build
  • Make the Unix port not compile bitmaptools so that it won't have a problem with the fact that it doesn't have access to RUN_BACKGROUND_TASKS

I don't really know how to do either of those, or which would be preferred.

I guessed that the latter would be possible by modifying a .mk file somewhere to disable bitmaptools, and I made an attempt at doing that but was unsuccessful. I tried adding CIRCUITPY_BITMAPTOOLS = 0 here:

CIRCUITPY_ULAB = 1
but it didn't seem to have any effect when building the unix port the same way the actions do.

I also tried setting this to 0

but same thing with that, doesn't seem to have any effect, build still fails due to RUN_BACKGROUND_TASKS undeclared.

@tannewt
Copy link
Member

tannewt commented Mar 14, 2023

How about adding #define RUN_BACKGROUND_TASKS to mpconfigport.h so that it gets replaced with nothing?

Copy link
Collaborator

@dhalbert dhalbert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code looks fine. Do you see any kind of noticeable performance degradation that might make it worthwhile to run the background tasks only sometimes around the loop?

@FoamyGuy
Copy link
Collaborator Author

I only did a handful of trials and there is some variance from run to run, but it seems comparable to me.

I used this test code to measure

import displayio
import board
import bitmaptools
import time

display = board.DISPLAY

bg_bmp = displayio.Bitmap(display.width, display.height, 4)
bg_palette = displayio.Palette(4)
bg_palette[0] = 0xffffff
bg_palette[1] = 0x0000ff
bg_palette[2] = 0x00ff00
bg_palette[3] = 0xff0000
bg_tilegrid = displayio.TileGrid(bitmap=bg_bmp, pixel_shader=bg_palette)
bg_group = displayio.Group()
bg_group.append(bg_tilegrid)

main_group = displayio.Group()
main_group.append(bg_group)

display.root_group = main_group
xs = bytes([4, 101, 101, 19])
ys = bytes([4, 19,  121, 101])
bitmaptools.draw_polygon(bg_bmp, xs, ys, 1)
time.sleep(1)

before_time = time.monotonic()
bitmaptools.boundary_fill(bg_bmp, 50,50, fill_color_value=1, replaced_color_value=0)
print(f"time took: {time.monotonic() - before_time}")

while True:
    pass

With latest build from S3:

time took: 2.34399
time took: 2.292
time took: 2.29901
time took: 2.30101
time took: 2.3

With build from this PR:

time took: 2.306
time took: 2.30002
time took: 2.304
time took: 2.305
time took: 2.30501

All of my testing was on a Feather ESP32-S2 TFT.

@dhalbert dhalbert merged commit c935601 into adafruit:main Mar 22, 2023
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.

bitmaptools.boundary_fill() should do background processing
4 participants