Skip to content

Commit eb21fc3

Browse files
committed
Add partial display update support.
Different operations to the display tree have different costs. Be aware of these costs when optimizing your code. * Changing tiles indices in a TileGrid will update an area covering them all. * Changing a palette will refresh every object that references it. * Moving a TileGrid will update both where it was and where it moved to. * Adding something to a Group will refresh each individual area it covers. * Removing things from a Group will refresh one area that covers all previous locations. (Not separate areas like add.) * Setting a new top level Group will refresh the entire display. Only TileGrid moves are optimized for overlap. All other overlaps cause sending of duplicate pixels. This also adds flip_x, flip_y and transpose_xy to TileGrid. They change the direction of the pixels but not the location. Fixes adafruit#1169. Fixes adafruit#1705. Fixes adafruit#1923.
1 parent a06a97e commit eb21fc3

15 files changed

Lines changed: 905 additions & 279 deletions

File tree

‎ports/atmel-samd/boards/pybadge/board.c‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ void board_init(void) {
8787
display->base.type = &displayio_display_type;
8888
common_hal_displayio_display_construct(display,
8989
bus,
90-
160, // Width
91-
128, // Height
90+
160, // Width (after rotation)
91+
128, // Height (after rotation)
9292
0, // column start
9393
0, // row start
9494
270, // rotation

‎shared-bindings/displayio/Display.h‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ bool displayio_display_frame_queued(displayio_display_obj_t* self);
5959

6060
bool displayio_display_refresh_queued(displayio_display_obj_t* self);
6161
void displayio_display_finish_refresh(displayio_display_obj_t* self);
62-
void displayio_display_send_pixels(displayio_display_obj_t* self, uint32_t* pixels, uint32_t length);
62+
void displayio_display_send_pixels(displayio_display_obj_t* self, uint8_t* pixels, uint32_t length);
6363

6464
bool common_hal_displayio_display_get_auto_brightness(displayio_display_obj_t* self);
6565
void common_hal_displayio_display_set_auto_brightness(displayio_display_obj_t* self, bool auto_brightness);

‎shared-bindings/displayio/TileGrid.c‎

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,83 @@ const mp_obj_property_t displayio_tilegrid_y_obj = {
196196
(mp_obj_t)&mp_const_none_obj},
197197
};
198198

199+
//| .. attribute:: flip_x
200+
//|
201+
//| If true, the left edge rendered will be the right edge of the right-most tile.
202+
//|
203+
STATIC mp_obj_t displayio_tilegrid_obj_get_flip_x(mp_obj_t self_in) {
204+
displayio_tilegrid_t *self = native_tilegrid(self_in);
205+
return mp_obj_new_bool(common_hal_displayio_tilegrid_get_flip_x(self));
206+
}
207+
MP_DEFINE_CONST_FUN_OBJ_1(displayio_tilegrid_get_flip_x_obj, displayio_tilegrid_obj_get_flip_x);
208+
209+
STATIC mp_obj_t displayio_tilegrid_obj_set_flip_x(mp_obj_t self_in, mp_obj_t flip_x_obj) {
210+
displayio_tilegrid_t *self = native_tilegrid(self_in);
211+
212+
common_hal_displayio_tilegrid_set_flip_x(self, mp_obj_is_true(flip_x_obj));
213+
return mp_const_none;
214+
}
215+
MP_DEFINE_CONST_FUN_OBJ_2(displayio_tilegrid_set_flip_x_obj, displayio_tilegrid_obj_set_flip_x);
216+
217+
const mp_obj_property_t displayio_tilegrid_flip_x_obj = {
218+
.base.type = &mp_type_property,
219+
.proxy = {(mp_obj_t)&displayio_tilegrid_get_flip_x_obj,
220+
(mp_obj_t)&displayio_tilegrid_set_flip_x_obj,
221+
(mp_obj_t)&mp_const_none_obj},
222+
};
223+
224+
//| .. attribute:: flip_y
225+
//|
226+
//| If true, the top edge rendered will be the bottom edge of the bottom-most tile.
227+
//|
228+
STATIC mp_obj_t displayio_tilegrid_obj_get_flip_y(mp_obj_t self_in) {
229+
displayio_tilegrid_t *self = native_tilegrid(self_in);
230+
return mp_obj_new_bool(common_hal_displayio_tilegrid_get_flip_y(self));
231+
}
232+
MP_DEFINE_CONST_FUN_OBJ_1(displayio_tilegrid_get_flip_y_obj, displayio_tilegrid_obj_get_flip_y);
233+
234+
STATIC mp_obj_t displayio_tilegrid_obj_set_flip_y(mp_obj_t self_in, mp_obj_t flip_y_obj) {
235+
displayio_tilegrid_t *self = native_tilegrid(self_in);
236+
237+
common_hal_displayio_tilegrid_set_flip_y(self, mp_obj_is_true(flip_y_obj));
238+
return mp_const_none;
239+
}
240+
MP_DEFINE_CONST_FUN_OBJ_2(displayio_tilegrid_set_flip_y_obj, displayio_tilegrid_obj_set_flip_y);
241+
242+
const mp_obj_property_t displayio_tilegrid_flip_y_obj = {
243+
.base.type = &mp_type_property,
244+
.proxy = {(mp_obj_t)&displayio_tilegrid_get_flip_y_obj,
245+
(mp_obj_t)&displayio_tilegrid_set_flip_y_obj,
246+
(mp_obj_t)&mp_const_none_obj},
247+
};
248+
249+
250+
//| .. attribute:: transpose_xy
251+
//|
252+
//| If true, the TileGrid will be rotate 90 degrees. When combined with mirroring any 90 degree
253+
//| rotation can be achieved.
254+
//|
255+
STATIC mp_obj_t displayio_tilegrid_obj_get_transpose_xy(mp_obj_t self_in) {
256+
displayio_tilegrid_t *self = native_tilegrid(self_in);
257+
return mp_obj_new_bool(common_hal_displayio_tilegrid_get_transpose_xy(self));
258+
}
259+
MP_DEFINE_CONST_FUN_OBJ_1(displayio_tilegrid_get_transpose_xy_obj, displayio_tilegrid_obj_get_transpose_xy);
260+
261+
STATIC mp_obj_t displayio_tilegrid_obj_set_transpose_xy(mp_obj_t self_in, mp_obj_t transpose_xy_obj) {
262+
displayio_tilegrid_t *self = native_tilegrid(self_in);
263+
264+
common_hal_displayio_tilegrid_set_transpose_xy(self, mp_obj_is_true(transpose_xy_obj));
265+
return mp_const_none;
266+
}
267+
MP_DEFINE_CONST_FUN_OBJ_2(displayio_tilegrid_set_transpose_xy_obj, displayio_tilegrid_obj_set_transpose_xy);
268+
269+
const mp_obj_property_t displayio_tilegrid_transpose_xy_obj = {
270+
.base.type = &mp_type_property,
271+
.proxy = {(mp_obj_t)&displayio_tilegrid_get_transpose_xy_obj,
272+
(mp_obj_t)&displayio_tilegrid_set_transpose_xy_obj,
273+
(mp_obj_t)&mp_const_none_obj},
274+
};
275+
199276
//| .. attribute:: pixel_shader
200277
//|
201278
//| The pixel shader of the tilegrid.
@@ -292,6 +369,9 @@ STATIC const mp_rom_map_elem_t displayio_tilegrid_locals_dict_table[] = {
292369
// Properties
293370
{ MP_ROM_QSTR(MP_QSTR_x), MP_ROM_PTR(&displayio_tilegrid_x_obj) },
294371
{ MP_ROM_QSTR(MP_QSTR_y), MP_ROM_PTR(&displayio_tilegrid_y_obj) },
372+
{ MP_ROM_QSTR(MP_QSTR_flip_x), MP_ROM_PTR(&displayio_tilegrid_flip_x_obj) },
373+
{ MP_ROM_QSTR(MP_QSTR_flip_y), MP_ROM_PTR(&displayio_tilegrid_flip_y_obj) },
374+
{ MP_ROM_QSTR(MP_QSTR_transpose_xy), MP_ROM_PTR(&displayio_tilegrid_transpose_xy_obj) },
295375
{ MP_ROM_QSTR(MP_QSTR_pixel_shader), MP_ROM_PTR(&displayio_tilegrid_pixel_shader_obj) },
296376
};
297377
STATIC MP_DEFINE_CONST_DICT(displayio_tilegrid_locals_dict, displayio_tilegrid_locals_dict_table);

‎shared-bindings/displayio/TileGrid.h‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,14 @@ void common_hal_displayio_tilegrid_set_y(displayio_tilegrid_t *self, mp_int_t y)
4242
mp_obj_t common_hal_displayio_tilegrid_get_pixel_shader(displayio_tilegrid_t *self);
4343
void common_hal_displayio_tilegrid_set_pixel_shader(displayio_tilegrid_t *self, mp_obj_t pixel_shader);
4444

45+
46+
bool common_hal_displayio_tilegrid_get_flip_x(displayio_tilegrid_t *self);
47+
void common_hal_displayio_tilegrid_set_flip_x(displayio_tilegrid_t *self, bool flip_x);
48+
bool common_hal_displayio_tilegrid_get_flip_y(displayio_tilegrid_t *self);
49+
void common_hal_displayio_tilegrid_set_flip_y(displayio_tilegrid_t *self, bool flip_y);
50+
bool common_hal_displayio_tilegrid_get_transpose_xy(displayio_tilegrid_t *self);
51+
void common_hal_displayio_tilegrid_set_transpose_xy(displayio_tilegrid_t *self, bool transpose_xy);
52+
4553
uint16_t common_hal_displayio_tilegrid_get_width(displayio_tilegrid_t *self);
4654
uint16_t common_hal_displayio_tilegrid_get_height(displayio_tilegrid_t *self);
4755

‎shared-module/displayio/Display.c‎

Lines changed: 79 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -107,28 +107,26 @@ void common_hal_displayio_display_construct(displayio_display_obj_t* self,
107107

108108
supervisor_start_terminal(width, height);
109109

110-
// Set the group after initialization otherwise we may send pixels while we delay in
111-
// initialization.
112-
self->refresh = true;
113-
self->current_group = &circuitpython_splash;
114-
115110
self->width = width;
116111
self->height = height;
117112
rotation = rotation % 360;
118-
self->mirror_x = false;
119-
self->mirror_y = false;
120-
self->transpose_xy = false;
113+
self->transform.x = 0;
114+
self->transform.y = 0;
115+
self->transform.scale = 1;
116+
self->transform.mirror_x = false;
117+
self->transform.mirror_y = false;
118+
self->transform.transpose_xy = false;
121119
if (rotation == 0 || rotation == 180) {
122120
if (rotation == 180) {
123-
self->mirror_x = true;
124-
self->mirror_y = true;
121+
self->transform.mirror_x = true;
122+
self->transform.mirror_y = true;
125123
}
126124
} else {
127-
self->transpose_xy = true;
128-
if (rotation == 90) {
129-
self->mirror_y = true;
125+
self->transform.transpose_xy = true;
126+
if (rotation == 270) {
127+
self->transform.mirror_y = true;
130128
} else {
131-
self->mirror_x = true;
129+
self->transform.mirror_x = true;
132130
}
133131
}
134132

@@ -148,20 +146,68 @@ void common_hal_displayio_display_construct(displayio_display_obj_t* self,
148146
}
149147
}
150148
}
149+
150+
self->area.x1 = 0;
151+
self->area.y1 = 0;
152+
self->area.next = NULL;
153+
154+
self->transform.dx = 1;
155+
self->transform.dy = 1;
156+
if (self->transform.transpose_xy) {
157+
self->area.x2 = height;
158+
self->area.y2 = width;
159+
if (self->transform.mirror_x) {
160+
self->transform.x = height;
161+
self->transform.dx = -1;
162+
}
163+
if (self->transform.mirror_y) {
164+
self->transform.y = width;
165+
self->transform.dy = -1;
166+
}
167+
} else {
168+
self->area.x2 = width;
169+
self->area.y2 = height;
170+
if (self->transform.mirror_x) {
171+
self->transform.x = width;
172+
self->transform.dx = -1;
173+
}
174+
if (self->transform.mirror_y) {
175+
self->transform.y = height;
176+
self->transform.dy = -1;
177+
}
178+
}
179+
180+
// Set the group after initialization otherwise we may send pixels while we delay in
181+
// initialization.
182+
common_hal_displayio_display_show(self, &circuitpython_splash);
151183
}
152184

153185
void common_hal_displayio_display_show(displayio_display_obj_t* self, displayio_group_t* root_group) {
154186
if (root_group == NULL) {
155187
root_group = &circuitpython_splash;
156188
}
189+
if (root_group == self->current_group) {
190+
return;
191+
}
192+
displayio_group_update_transform(root_group, &self->transform);
157193
self->current_group = root_group;
194+
self->full_refresh = true;
158195
common_hal_displayio_display_refresh_soon(self);
159196
}
160197

161198
void common_hal_displayio_display_refresh_soon(displayio_display_obj_t* self) {
162199
self->refresh = true;
163200
}
164201

202+
const displayio_area_t* displayio_display_get_refresh_areas(displayio_display_obj_t *self) {
203+
if (self->full_refresh) {
204+
self->area.next = NULL;
205+
return &self->area;
206+
} else {
207+
return displayio_group_get_refresh_areas(self->current_group, NULL);
208+
}
209+
}
210+
165211
int32_t common_hal_displayio_display_wait_for_frame(displayio_display_obj_t* self) {
166212
uint64_t last_refresh = self->last_refresh;
167213
// Don't try to refresh if we got an exception.
@@ -224,7 +270,6 @@ void displayio_display_end_transaction(displayio_display_obj_t* self) {
224270
}
225271

226272
void displayio_display_set_region_to_update(displayio_display_obj_t* self, uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) {
227-
228273
self->send(self->bus, true, &self->set_column_command, 1);
229274
bool isCommand = self->data_as_commands;
230275
if (self->single_byte_bounds) {
@@ -255,25 +300,29 @@ void displayio_display_set_region_to_update(displayio_display_obj_t* self, uint1
255300
}
256301
}
257302

258-
bool displayio_display_frame_queued(displayio_display_obj_t* self) {
259-
// Refresh at ~30 fps.
260-
return (ticks_ms - self->last_refresh) > 32;
303+
void displayio_display_start_refresh(displayio_display_obj_t* self) {
304+
self->last_refresh = ticks_ms;
261305
}
262306

263-
bool displayio_display_refresh_queued(displayio_display_obj_t* self) {
264-
return self->refresh || (self->current_group != NULL && displayio_group_needs_refresh(self->current_group));
307+
bool displayio_display_frame_queued(displayio_display_obj_t* self) {
308+
if (self->current_group == NULL) {
309+
return false;
310+
}
311+
// Refresh at ~60 fps.
312+
return (ticks_ms - self->last_refresh) > 16;
265313
}
266314

267315
void displayio_display_finish_refresh(displayio_display_obj_t* self) {
268316
if (self->current_group != NULL) {
269317
displayio_group_finish_refresh(self->current_group);
270318
}
271319
self->refresh = false;
320+
self->full_refresh = false;
272321
self->last_refresh = ticks_ms;
273322
}
274323

275-
void displayio_display_send_pixels(displayio_display_obj_t* self, uint32_t* pixels, uint32_t length) {
276-
self->send(self->bus, false, (uint8_t*) pixels, length * 4);
324+
void displayio_display_send_pixels(displayio_display_obj_t* self, uint8_t* pixels, uint32_t length) {
325+
self->send(self->bus, false, pixels, length);
277326
}
278327

279328
void displayio_display_update_backlight(displayio_display_obj_t* self) {
@@ -298,3 +347,11 @@ void release_display(displayio_display_obj_t* self) {
298347
common_hal_digitalio_digitalinout_deinit(&self->backlight_inout);
299348
}
300349
}
350+
351+
bool displayio_display_fill_area(displayio_display_obj_t *self, displayio_area_t* area, uint32_t* mask, uint32_t *buffer) {
352+
return displayio_group_fill_area(self->current_group, area, mask, buffer);
353+
}
354+
355+
bool displayio_display_clip_area(displayio_display_obj_t *self, const displayio_area_t* area, displayio_area_t* clipped) {
356+
return displayio_area_compute_overlap(&self->area, area, clipped);
357+
}

‎shared-module/displayio/Display.h‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
#include "shared-bindings/displayio/Group.h"
3232
#include "shared-bindings/pulseio/PWMOut.h"
3333

34+
#include "shared-module/displayio/area.h"
35+
3436
typedef bool (*display_bus_begin_transaction)(mp_obj_t bus);
3537
typedef void (*display_bus_send)(mp_obj_t bus, bool command, uint8_t *data, uint32_t data_length);
3638
typedef void (*display_bus_end_transaction)(mp_obj_t bus);
@@ -61,12 +63,16 @@ typedef struct {
6163
uint64_t last_backlight_refresh;
6264
bool auto_brightness:1;
6365
bool updating_backlight:1;
64-
bool mirror_x;
65-
bool mirror_y;
66-
bool transpose_xy;
66+
bool full_refresh; // New group means we need to refresh the whole display.
67+
displayio_buffer_transform_t transform;
68+
displayio_area_t area;
6769
} displayio_display_obj_t;
6870

71+
void displayio_display_start_refresh(displayio_display_obj_t* self);
72+
const displayio_area_t* displayio_display_get_refresh_areas(displayio_display_obj_t *self);
73+
bool displayio_display_fill_area(displayio_display_obj_t *self, displayio_area_t* area, uint32_t* mask, uint32_t *buffer);
6974
void displayio_display_update_backlight(displayio_display_obj_t* self);
75+
bool displayio_display_clip_area(displayio_display_obj_t *self, const displayio_area_t* area, displayio_area_t* clipped);
7076
void release_display(displayio_display_obj_t* self);
7177

7278
#endif // MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_DISPLAY_H

0 commit comments

Comments
 (0)