Tempe: Efficient Purely-Micropython Graphics #16105
Replies: 7 comments 8 replies
-
This looks impressive! Very pretty visualisations. I do have a query. You state
a statement with which I agree wholeheartedly. Yet the library is based on |
Beta Was this translation helpful? Give feedback.
-
Thanks for that, the penny has dropped :) Your use of You may be familiar with the display drivers for nano-gui and other GUI's. You and I approach the problem of excessive buffer size in different ways: I use a 4-bit color palette with a full screen frame buffer while you use multiple small RGB565 frame buffers blitted to the hardware. I guess that a data visualisation application can free the RAM used by the buffer once it has been blitted, however there could be an issue with RAM fragmentation if you regularly create and release frame buffers. In the GUI applications the one large buffer is instantiated very early and left in place, with drawing done using Perhaps you could clarify the allocation situation. Take the simple case of a text value which is periodically updated. Is a buffer created and destroyed on each update? As a lazy sod, a benefit of my approach is that the display driver is very simple: creating a compatible driver for a new display is straightforward even when the hardware is quite different (e.g. ePaper). NitpicksOne easy byte saver is as follows. Where you have GCTRL = const(b"\xB7") the compiler allocates storage for _GCTRL = const(b"\xB7") No storage is allocated, the symbol is used only at compile time. Another nitpick. The docs have # a buffer one quarter the size of the screen
working_buffer = array('H', bytearray(2*320*61)) This allocates a buffer of 19520 bytes. Should this read (e.g.) # a buffer one quarter the size of the screen
working_buffer = array('H', (0 for _ in range(2*320*61))) which allocates 39_040 bytes (1/4 the size of the screen)? Either the comment or the code seems wrong. |
Beta Was this translation helpful? Give feedback.
-
Bonjour , |
Beta Was this translation helpful? Give feedback.
-
@corranwebster: Very impressing library, Thank you. |
Beta Was this translation helpful? Give feedback.
-
@corranwebster Thank you for that detailed explanation. You've evidently given this a lot of thought.
That is true. As hardware improved I expanded the concept to full GUI's with pushbutton or touch input, and extended support to larger displays. The latter involved the compromise which is a 4-bit color palette. This works well with the widgets, but imposes limits on what you can display. Re working_buffer = array('H', bytearray(2*320*61)) Your observations on buffer size are correct. Age-related brain-fade on my part. However I'm unconvinced by
My understanding is that a few years ago there was a major change in the way Python (and MP) handle generator expressions. I think they are now fast and memory efficient. I produced a file containing from array import array
a = array("I", (0 for x in range(100))) This produced the following bytecode
|
Beta Was this translation helpful? Give feedback.
-
Thanks for that, thought-provoking. Those timings are seriously slow. It's not obvious why allocating an integer array should take orders of magnitude longer than a bytearray. Ideally, as you suggested, we'd have a way to declare an array at a specific address such that it overlays an existing from uctypes import addressof, array_at # I wish...
a = bytearray(320*240) # Fast
buffer = array_at('H', addr=addressof(a), 320*120) # Faster
But it breaks for values > 65535. If you do care about initial values, trivial Viper code can init in ~8ms. |
Beta Was this translation helpful? Give feedback.
-
I've just released Tempe 0.3, which includes significant performance improvements over the initial release (up to an order of magnitude in some cases, and twice as fast in most). In particular, text rendering is much faster. Additional improvements include:
Full release notes are on GitHub. |
Beta Was this translation helpful? Give feedback.
-
A couple of months ago I got ahold of a Pimoroni 320x240 ST7789-based LCD display. Since a lot of my work over the past 20 years has been around data visualization and UIs (I'm a long-time contributor to the Chaco plotting library for Python), I wanted to use it to display pretty plots but immediately ran into memory issues: basically a 320x240 16-bit framebuffer is too big if you are doing anything interesting with data. There are also issues with speed when you are trying to update dynamic visualisations which need to draw the entire buffer and transfer it to the display while remaining reasonably responsive.
These issues might be solvable by reducing to 8-bit color or using a smaller part of the screen, but it should be possible to use the full capabilities of these screens even if you can't have a full, in-memory buffer.
While there are C-based libraries which address this (in particular PicoVector looks interesting, and lvgl looks comprehensive) it seems like there is a place for a purely Micropython-based solution: something that you can just
mip
-install on top of standard Micropython and get to work.Tempe is what grew out of this need.
Documentation
Even though it is in early stage development, Tempe has reasonably comprehensive documentation.
Tempe Documentation
Pretty Pictures
Installation
Either
or
should work.
Only tested on the most recent Micropython, but should work on anything reasonably recent as long as it has framebuf.
Features
Based On
framebuf
Tempe uses the standard
framebuf
module to do all rendering to bitmaps. This is a compromise of universality and speed vs. the somewhat limited API offramebuf
, but it works well enough and allows the library to be written entirely in Micropython. Drawing heavily usesmemoryview
s and strided frame buffers to allow clipping drawing to certain regions.Memory Efficient Drawing
Tempe transparently handles:
The basic object for drawing is a layered
Surface
which tracks objects and knows how to render them efficiently and tracks changes. You then give Tempe as large a buffer as you can afford, and tell it to refresh the display whenever you want to update things:Optimised for Data Visualization
Tempe isn't a full plotting library, but is intended to be usable as part of one: for people familiar with the Grammar of Graphics it's concerned with rendering geometry and aesthetics, but doesn't have any particular support for statistics, scales, etc. Because of this intended use, the API is a bit different from standard drawing libraries: data visualisations usually draw many similar shapes (lines, rectangles, markers, etc.) but with different parameters (coordinates, colours, etc.), so the fundamental units of drawing are groups of similar objects.
For example, a horizontal bar plot is essentially a group of rectangles, which Tempe could create in one command as:
Memory Efficient Geometry
Because Tempe is particularly concerned with memory efficiency, it has ways of more efficiently representing geometries with repeated and predictable values, so the geometry of the rectangles could instead be represented as:
which avoids storing many repeated values in lists or arrays.
Bitmap Fonts
Tempe can use either FontToPy or MicroFont fonts to draw nicer text than the default
framebuf
font.Polar Coordinates
Tempe provides functions that convert geometry expressed in (r, theta) polar coordinates to standard (x, y) cartesian coordinates, making it straightforward to create polar line plots, radar plots, coxcomb plots, donut plots, and even pie charts (if you have to).
Asyncio Support
Tempe
Surface
objects have an asyncioEvent
that is triggered when a refresh is needed, so automatic updating for asyncio applications is as simple as:Where is this all going?
I'd eventually like to have a Micropython plotting library that looks as good as Matplotlib and is reasonably responsive when updated, and Tempe is I think a step in that direction. Basing drawing on FrameBuffer is a bit limiting: in particular it would be nice to have options for sub-pixel rendering and alpha compositing - the latter in particular that may be possible with
viper
-acceleration.Whether Tempe grows these features, or whether something us built on top of it I'm not sure.
How can you help?
This is an early release using hardware that I have access to and solving my problems. If this seems useful to you, please try it and at least give feedback on any rough edges or missing features that you'd like to see.
Beta Was this translation helpful? Give feedback.
All reactions