You've already forked MicroPythonOS
mirror of
https://github.com/m5stack/MicroPythonOS.git
synced 2026-05-20 11:51:27 -07:00
Breakout: support partial buffer rendering
This commit is contained in:
+135
-73
@@ -1,5 +1,6 @@
|
||||
// Include the header file to get access to the MicroPython API
|
||||
#include "py/dynruntime.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
// Provide a local memset for xtensawin native modules (libc isn't linked).
|
||||
void *memset(void *s, int c, size_t n) {
|
||||
@@ -15,6 +16,8 @@ uint16_t *g_framebuffer;
|
||||
size_t g_framebuffer_len;
|
||||
size_t g_framebuffer_width;
|
||||
size_t g_framebuffer_height;
|
||||
size_t g_render_y_offset;
|
||||
size_t g_render_height;
|
||||
|
||||
int g_paddle_x;
|
||||
int g_paddle_width;
|
||||
@@ -62,7 +65,11 @@ static void draw_pixel(int x, int y, uint16_t color) {
|
||||
if ((size_t)x >= g_framebuffer_width || (size_t)y >= g_framebuffer_height) {
|
||||
return;
|
||||
}
|
||||
const size_t idx = (size_t)y * g_framebuffer_width + (size_t)x;
|
||||
if ((size_t)y < g_render_y_offset || (size_t)y >= (g_render_y_offset + g_render_height)) {
|
||||
return;
|
||||
}
|
||||
const size_t local_y = (size_t)y - g_render_y_offset;
|
||||
const size_t idx = local_y * g_framebuffer_width + (size_t)x;
|
||||
const size_t max_pixels = framebuffer_max_pixels();
|
||||
if (idx >= max_pixels) {
|
||||
return;
|
||||
@@ -105,6 +112,8 @@ static mp_obj_t init(mp_obj_t framebuffer_obj, mp_obj_t width_obj, mp_obj_t heig
|
||||
g_framebuffer_len = bufinfo.len;
|
||||
g_framebuffer_width = (size_t)mp_obj_get_int(width_obj);
|
||||
g_framebuffer_height = (size_t)mp_obj_get_int(height_obj);
|
||||
g_render_y_offset = 0;
|
||||
g_render_height = g_framebuffer_height;
|
||||
|
||||
g_paddle_width = (int)g_framebuffer_width / 5;
|
||||
g_paddle_height = 4;
|
||||
@@ -121,8 +130,8 @@ static mp_obj_t init(mp_obj_t framebuffer_obj, mp_obj_t width_obj, mp_obj_t heig
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_3(init_obj, init);
|
||||
|
||||
// render(): draw a simple Breakout-style frame and advance the game simulation.
|
||||
static mp_obj_t render(void) {
|
||||
// render([y_offset, rows, advance]): draw a Breakout frame slice and optionally advance simulation.
|
||||
static mp_obj_t render(size_t n_args, const mp_obj_t *args) {
|
||||
if (g_framebuffer == NULL || g_framebuffer_width == 0 || g_framebuffer_height == 0) {
|
||||
return mp_const_none;
|
||||
}
|
||||
@@ -130,43 +139,135 @@ static mp_obj_t render(void) {
|
||||
const size_t width = g_framebuffer_width;
|
||||
const size_t height = g_framebuffer_height;
|
||||
|
||||
size_t render_y_offset = 0;
|
||||
size_t render_rows = height;
|
||||
bool advance = true;
|
||||
if (n_args >= 1) {
|
||||
int y_offset_arg = mp_obj_get_int(args[0]);
|
||||
if (y_offset_arg > 0) {
|
||||
render_y_offset = (size_t)y_offset_arg;
|
||||
}
|
||||
}
|
||||
if (n_args >= 2) {
|
||||
int rows_arg = mp_obj_get_int(args[1]);
|
||||
if (rows_arg > 0) {
|
||||
render_rows = (size_t)rows_arg;
|
||||
}
|
||||
}
|
||||
if (n_args >= 3) {
|
||||
advance = mp_obj_is_true(args[2]);
|
||||
} else {
|
||||
advance = (render_y_offset == 0);
|
||||
}
|
||||
|
||||
if (render_y_offset >= height) {
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
const size_t max_rows_by_buf = (width > 0) ? (framebuffer_max_pixels() / width) : 0;
|
||||
const size_t max_rows_by_height = height - render_y_offset;
|
||||
if (render_rows > max_rows_by_height) {
|
||||
render_rows = max_rows_by_height;
|
||||
}
|
||||
if (max_rows_by_buf > 0 && render_rows > max_rows_by_buf) {
|
||||
render_rows = max_rows_by_buf;
|
||||
}
|
||||
if (render_rows == 0) {
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
g_render_y_offset = render_y_offset;
|
||||
g_render_height = render_rows;
|
||||
|
||||
//mp_printf(&mp_plat_print, "breakout.c render y=%lu rows=%lu advance=%d\n", (unsigned long)render_y_offset, (unsigned long)render_rows, (int)advance);
|
||||
|
||||
// Clear to black.
|
||||
const size_t fill_pixels = framebuffer_max_pixels();
|
||||
const size_t fill_pixels = width * render_rows;
|
||||
for (size_t i = 0; i < fill_pixels; i++) { g_framebuffer[i] = 0x0000; } // RGB565 black
|
||||
|
||||
g_fps_frames++;
|
||||
const uint32_t now_ms = ticks_ms();
|
||||
const uint32_t elapsed_ms = now_ms - g_fps_last_ms;
|
||||
if (elapsed_ms >= 1000) {
|
||||
const uint32_t fps = (g_fps_frames * 1000) / elapsed_ms;
|
||||
mp_printf(&mp_plat_print, "breakout.c fps: %lu\n", (unsigned long)fps);
|
||||
g_fps_last_ms = now_ms;
|
||||
g_fps_frames = 0;
|
||||
}
|
||||
const int paddle_y = (int)height - g_paddle_height - 4;
|
||||
|
||||
uint32_t tick_delta_ms = now_ms - g_last_tick_ms;
|
||||
g_last_tick_ms = now_ms;
|
||||
if (tick_delta_ms > 50) {
|
||||
tick_delta_ms = 50;
|
||||
}
|
||||
const float dt = (float)tick_delta_ms / 1000.0f;
|
||||
if (advance) {
|
||||
g_fps_frames++;
|
||||
const uint32_t now_ms = ticks_ms();
|
||||
const uint32_t elapsed_ms = now_ms - g_fps_last_ms;
|
||||
if (elapsed_ms >= 1000) {
|
||||
const uint32_t fps = (g_fps_frames * 1000) / elapsed_ms;
|
||||
mp_printf(&mp_plat_print, "breakout.c fps: %lu\n", (unsigned long)fps);
|
||||
g_fps_last_ms = now_ms;
|
||||
g_fps_frames = 0;
|
||||
}
|
||||
|
||||
// Update ball position.
|
||||
g_ball_x += g_ball_vx * dt;
|
||||
g_ball_y += g_ball_vy * dt;
|
||||
uint32_t tick_delta_ms = now_ms - g_last_tick_ms;
|
||||
g_last_tick_ms = now_ms;
|
||||
if (tick_delta_ms > 50) {
|
||||
tick_delta_ms = 50;
|
||||
}
|
||||
const float dt = (float)tick_delta_ms / 1000.0f;
|
||||
|
||||
// Wall collisions.
|
||||
if (g_ball_x <= 0.0f) {
|
||||
g_ball_x = 0.0f;
|
||||
g_ball_vx = 120.0f;
|
||||
} else if (g_ball_x >= (float)width - 1.0f) {
|
||||
g_ball_x = (float)width - 1.0f;
|
||||
g_ball_vx = -120.0f;
|
||||
}
|
||||
// Update ball position.
|
||||
g_ball_x += g_ball_vx * dt;
|
||||
g_ball_y += g_ball_vy * dt;
|
||||
|
||||
if (g_ball_y <= 0.0f) {
|
||||
g_ball_y = 0.0f;
|
||||
g_ball_vy = 120.0f;
|
||||
// Wall collisions.
|
||||
if (g_ball_x <= 0.0f) {
|
||||
g_ball_x = 0.0f;
|
||||
g_ball_vx = 120.0f;
|
||||
} else if (g_ball_x >= (float)width - 1.0f) {
|
||||
g_ball_x = (float)width - 1.0f;
|
||||
g_ball_vx = -120.0f;
|
||||
}
|
||||
|
||||
if (g_ball_y <= 0.0f) {
|
||||
g_ball_y = 0.0f;
|
||||
g_ball_vy = 120.0f;
|
||||
}
|
||||
|
||||
// Brick collision.
|
||||
const int brick_gap = 2;
|
||||
const int brick_rows = BRICK_ROWS;
|
||||
const int brick_cols = BRICK_COLS;
|
||||
const int brick_height = 6;
|
||||
const int brick_area_width = (int)width - (brick_gap * (brick_cols + 1));
|
||||
const int brick_width = (brick_area_width > 0) ? (brick_area_width / brick_cols) : 0;
|
||||
const int brick_offset_y = 8;
|
||||
|
||||
if (brick_width > 0 && g_ball_y <= (float)(brick_offset_y + brick_rows * (brick_height + brick_gap))) {
|
||||
for (int row = 0; row < brick_rows; row++) {
|
||||
for (int col = 0; col < brick_cols; col++) {
|
||||
if (!g_bricks[row][col]) {
|
||||
continue;
|
||||
}
|
||||
const int bx = brick_gap + col * (brick_width + brick_gap);
|
||||
const int by = brick_offset_y + row * (brick_height + brick_gap);
|
||||
if (g_ball_x >= (float)bx && g_ball_x < (float)(bx + brick_width) && g_ball_y >= (float)by && g_ball_y < (float)(by + brick_height)) {
|
||||
g_bricks[row][col] = 0;
|
||||
g_ball_vy = -g_ball_vy;
|
||||
row = brick_rows;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Paddle collision.
|
||||
if (g_ball_y >= (float)(paddle_y - 1) && g_ball_y <= (float)(paddle_y + g_paddle_height)) {
|
||||
if (g_ball_x >= (float)g_paddle_x && g_ball_x <= (float)(g_paddle_x + g_paddle_width)) {
|
||||
g_ball_y = (float)(paddle_y - 1);
|
||||
g_ball_vy = -120.0f;
|
||||
const int paddle_center = g_paddle_x + g_paddle_width / 2;
|
||||
if (g_ball_x < (float)paddle_center) {
|
||||
g_ball_vx = -120.0f;
|
||||
} else if (g_ball_x > (float)paddle_center) {
|
||||
g_ball_vx = 120.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ball fell below paddle: reset.
|
||||
if (g_ball_y >= (float)((int)height - 1)) {
|
||||
reset_ball();
|
||||
}
|
||||
}
|
||||
|
||||
// Brick layout.
|
||||
@@ -178,45 +279,6 @@ static mp_obj_t render(void) {
|
||||
const int brick_width = (brick_area_width > 0) ? (brick_area_width / brick_cols) : 0;
|
||||
const int brick_offset_y = 8;
|
||||
|
||||
// Brick collision.
|
||||
if (brick_width > 0 && g_ball_y <= (float)(brick_offset_y + brick_rows * (brick_height + brick_gap))) {
|
||||
for (int row = 0; row < brick_rows; row++) {
|
||||
for (int col = 0; col < brick_cols; col++) {
|
||||
if (!g_bricks[row][col]) {
|
||||
continue;
|
||||
}
|
||||
const int bx = brick_gap + col * (brick_width + brick_gap);
|
||||
const int by = brick_offset_y + row * (brick_height + brick_gap);
|
||||
if (g_ball_x >= (float)bx && g_ball_x < (float)(bx + brick_width) && g_ball_y >= (float)by && g_ball_y < (float)(by + brick_height)) {
|
||||
g_bricks[row][col] = 0;
|
||||
g_ball_vy = -g_ball_vy;
|
||||
row = brick_rows;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Paddle collision.
|
||||
const int paddle_y = (int)height - g_paddle_height - 4;
|
||||
if (g_ball_y >= (float)(paddle_y - 1) && g_ball_y <= (float)(paddle_y + g_paddle_height)) {
|
||||
if (g_ball_x >= (float)g_paddle_x && g_ball_x <= (float)(g_paddle_x + g_paddle_width)) {
|
||||
g_ball_y = (float)(paddle_y - 1);
|
||||
g_ball_vy = -120.0f;
|
||||
const int paddle_center = g_paddle_x + g_paddle_width / 2;
|
||||
if (g_ball_x < (float)paddle_center) {
|
||||
g_ball_vx = -120.0f;
|
||||
} else if (g_ball_x > (float)paddle_center) {
|
||||
g_ball_vx = 120.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ball fell below paddle: reset.
|
||||
if (g_ball_y >= (float)((int)height - 1)) {
|
||||
reset_ball();
|
||||
}
|
||||
|
||||
// Draw bricks.
|
||||
if (brick_width > 0) {
|
||||
for (int row = 0; row < brick_rows; row++) {
|
||||
@@ -237,7 +299,7 @@ static mp_obj_t render(void) {
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_0(render_obj, render);
|
||||
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(render_obj, 0, 3, render);
|
||||
|
||||
// move_paddle(delta): move the paddle horizontally by delta.
|
||||
static mp_obj_t move_paddle(mp_obj_t delta_obj) {
|
||||
|
||||
@@ -1,21 +1,5 @@
|
||||
# On the 320x170 T-Display-S3:
|
||||
# This gets just 7.5 FPS on actual ESP32S3 hardware
|
||||
# Probably because the double buffer copies.
|
||||
# With a direct buffer, it's still only 10 FPS. (and flickering buttons on black screen)
|
||||
# direct framebuffer + without self.canvas.invalidate() and self.canvas.center(), it's still only 13.5 FPS (and black screen)
|
||||
# AHA! with a send_to_display() it is running at 21.5 FPS but with heavy flicker
|
||||
# adding a wait for the render of 10ms gives a non-flicker 17.5 FPS
|
||||
# not waiting for the render but adding a callback brings the FPS to 25.5 !
|
||||
|
||||
# on the emulator, it gets around 8 FPS (LVGL) and 3.5 FPS (breakout.c)
|
||||
# at 33ms, it's 4.5 and 2.5 FPS (mpong)
|
||||
|
||||
# On the 320x230 (instead of 240 because memory limitation) it gets 17 FPS (LVGL) or 12 FPS (breakout.c)
|
||||
|
||||
import lvgl as lv
|
||||
|
||||
import time
|
||||
|
||||
import mpos.ui
|
||||
from mpos import Activity, DisplayMetrics, InputManager
|
||||
|
||||
@@ -40,6 +24,12 @@ class Breakout(Activity):
|
||||
old_callback = None
|
||||
|
||||
render_next = True
|
||||
flush_ready = False
|
||||
chunk_in_progress = False
|
||||
chunk_waiting = False
|
||||
chunk_rows_per = 0
|
||||
chunk_total = 0
|
||||
chunk_index = 0
|
||||
|
||||
# Widgets:
|
||||
screen = None
|
||||
@@ -58,18 +48,6 @@ class Breakout(Activity):
|
||||
self.paddle_move_step = round(self.hor_res/16)
|
||||
self.ver_res = d.get_vertical_resolution()
|
||||
|
||||
'''
|
||||
self.canvas = lv.canvas(self.screen)
|
||||
self.canvas.set_size(self.hor_res, self.ver_res)
|
||||
#self.buffer = bytearray(self.hor_res * self.ver_res * 2)
|
||||
#self.canvas.set_buffer(self.buffer, self.hor_res, self.ver_res, lv.COLOR_FORMAT.NATIVE)
|
||||
#self.canvas.set_buffer(mpos.ui.main_display._frame_buffer1, self.hor_res, self.ver_res, lv.COLOR_FORMAT.NATIVE)
|
||||
#self.canvas.add_flag(lv.obj.FLAG.CLICKABLE)
|
||||
#self.canvas.add_event_cb(self.touch_cb, lv.EVENT.ALL, None)
|
||||
self.layer = lv.layer_t()
|
||||
self.canvas.init_layer(self.layer)
|
||||
'''
|
||||
|
||||
self.leftbutton = lv.button(self.screen)
|
||||
self.leftbutton.align(lv.ALIGN.BOTTOM_LEFT, 0, 0)
|
||||
leftlabel = lv.label(self.leftbutton)
|
||||
@@ -94,20 +72,7 @@ class Breakout(Activity):
|
||||
|
||||
def onResume(self, screen):
|
||||
lv.log_register_print_cb(self.log_callback)
|
||||
#mpong.init(self.buffer, self.hor_res, self.ver_res)
|
||||
|
||||
#self.old_callback = mpos.ui.main_display._data_bus.
|
||||
#self.refresh_timer = lv.timer_create(self.run_mpong, 5, None) # max 1000ms/60fps = 16ms/frame
|
||||
#self.refresh_timer = lv.timer_create(self.run_mpong, 16, None) # max 1000ms/60fps = 16ms/frame
|
||||
#self.refresh_timer = lv.timer_create(self.run_mpong, 33, None) # max 1000ms/30fps = 33ms/frame
|
||||
#mpos.ui.task_handler.add_event_cb(self.run_mpong, mpos.ui.task_handler.TASK_HANDLER_STARTED)
|
||||
#mpos.ui.task_handler.add_event_cb(self.run_mpong, mpos.ui.task_handler.TASK_HANDLER_FINISHED) # just 18 FPS
|
||||
|
||||
#mpos.ui.main_display.delete_refr_timer() # how to enable after? also it doesnt help
|
||||
#lv.timer_create(self.startit, 1000, None).set_repeat_count(1)
|
||||
# 10ms is fine on real hardware but needs > 1000ms on emulator
|
||||
lv.timer_create(self.startit, 5000, None).set_repeat_count(1) # this needs to be delayed, otherwise the whole thing hangs
|
||||
#lv.async_call(self.startit, None)
|
||||
lv.timer_create(self.startit, 4000, None).set_repeat_count(1) # this needs to be delayed, otherwise the whole thing hangs
|
||||
|
||||
def onPause(self, screen):
|
||||
if self.refresh_timer:
|
||||
@@ -117,19 +82,13 @@ class Breakout(Activity):
|
||||
|
||||
def startit(self, arg1=None):
|
||||
print("starting it!")
|
||||
breakout.init(mpos.ui.main_display._frame_buffer1, self.hor_res, min(self.ver_res, 230))
|
||||
breakout.init(mpos.ui.main_display._frame_buffer1, self.hor_res, self.ver_res)
|
||||
mpos.ui.main_display._data_bus.register_callback(self.flush_ready_cb)
|
||||
self.refresh_timer = lv.timer_create(self.drawframe, 20, None) # max 1000ms/50fps = 20ms/frame
|
||||
#self.refresh_timer = lv.timer_create(self.run_mpong, 33, None).set_repeat_count(1) # max 1000ms/60fps = 16ms/frame
|
||||
#lv.async_call(self.run_mpong, None)
|
||||
self.refresh_timer = lv.timer_create(self.drawframe, 16, None) # max 1000ms/60fps ~= 16ms/frame
|
||||
|
||||
def flush_ready_cb(self, arg1=None, arg2=None):
|
||||
#print("cbb")
|
||||
mpos.ui.main_display._disp_drv.flush_ready() # with this, it hangs, and without it, the device crashes
|
||||
#print("cba")
|
||||
#self.refresh_timer = lv.timer_create(self.run_mpong, 33, None).set_repeat_count(1) # max 1000ms/60fps = 16ms/frame
|
||||
#lv.async_call(self.run_mpong, None)
|
||||
self.render_next = True
|
||||
self.flush_ready = True
|
||||
|
||||
def move_left(self):
|
||||
breakout.move_paddle(-self.paddle_move_step)
|
||||
@@ -155,67 +114,100 @@ class Breakout(Activity):
|
||||
return
|
||||
focused = focusgroup.get_focused()
|
||||
if focused:
|
||||
#print(f"got focus button: {focused}")
|
||||
#label = focused.get_child(0)
|
||||
#print(f"got label for button: {label.get_text()}")
|
||||
#focused.remove_state(lv.STATE.FOCUSED) # this doesn't seem to work to remove focus
|
||||
#print("checking which button is focused")
|
||||
if focused == self.rightbutton:
|
||||
#print("next is focused")
|
||||
focusgroup.focus_prev()
|
||||
elif focused == self.leftbutton:
|
||||
#print("prev is focused")
|
||||
focusgroup.focus_next()
|
||||
else:
|
||||
print("focus isn't on next or previous, leaving it...")
|
||||
|
||||
def send_to_display(self):
|
||||
# full-screen area
|
||||
x1, y1 = 0, 0
|
||||
def send_to_display(self, y_offset=0, rows=None, is_last=True):
|
||||
x1 = 0
|
||||
x2 = mpos.ui.main_display.get_horizontal_resolution() - 1
|
||||
x2 = x2 + mpos.ui.main_display._offset_x
|
||||
x1 = x1 + mpos.ui.main_display._offset_x
|
||||
y2 = mpos.ui.main_display.get_vertical_resolution() - 1
|
||||
y2 = y2 + mpos.ui.main_display._offset_y
|
||||
|
||||
if rows is None:
|
||||
rows = mpos.ui.main_display.get_vertical_resolution()
|
||||
y1 = y_offset
|
||||
y2 = y_offset + rows - 1
|
||||
y1 = y1 + mpos.ui.main_display._offset_y
|
||||
y2 = y2 + mpos.ui.main_display._offset_y
|
||||
|
||||
cmd = mpos.ui.main_display._set_memory_location(x1, y1, x2, y2)
|
||||
data_view = mpos.ui.main_display._frame_buffer1
|
||||
bytes_needed = rows * mpos.ui.main_display.get_horizontal_resolution() * 2
|
||||
data_view = memoryview(mpos.ui.main_display._frame_buffer1)[:bytes_needed]
|
||||
|
||||
tx_last = True
|
||||
mpos.ui.main_display._data_bus.tx_color(
|
||||
cmd,
|
||||
data_view,
|
||||
x1, y1, x2, y2,
|
||||
mpos.ui.main_display._rotation,
|
||||
True,
|
||||
tx_last,
|
||||
)
|
||||
|
||||
def drawframe(self, arg1=None, arg2=None):
|
||||
if self.render_next == False:
|
||||
if self.chunk_waiting:
|
||||
if self.flush_ready:
|
||||
self.flush_ready = False
|
||||
self.chunk_waiting = False
|
||||
self.chunk_index += 1
|
||||
if self.chunk_index >= self.chunk_total:
|
||||
self.chunk_in_progress = False
|
||||
self.render_next = True
|
||||
else:
|
||||
self._render_and_send_chunk()
|
||||
return
|
||||
|
||||
if self.chunk_in_progress or not self.render_next:
|
||||
return
|
||||
|
||||
self.render_next = False
|
||||
breakout.render()
|
||||
#self.play_button.set_style_opa(lv.OPA.TRANSP, lv.PART.MAIN) # works to force refresh on desktop but not esp32
|
||||
#self.screen.invalidate()
|
||||
#lv.refr_now(None)
|
||||
#self.canvas.invalidate() # force redraw
|
||||
#self.canvas.center()
|
||||
#self.canvas.refre
|
||||
#self.screen.invalidate()
|
||||
#self.screen.center()
|
||||
#mpong.render()
|
||||
'''
|
||||
import lvgl as lv
|
||||
area = lv.area_t()
|
||||
area.x1 = 0
|
||||
area.y1 = 0
|
||||
area.x2 = 170
|
||||
area.y2 = 170
|
||||
import mpos.ui
|
||||
mpos.ui.main_display._flush_cb(None, area, mpos.ui.main_display._frame_buffer1) # color_p should be pointer, not memoryview
|
||||
'''
|
||||
self.send_to_display()
|
||||
#time.sleep_ms(10) # give it time to flush, otherwise there's heavy flicker. 5ms is fine!
|
||||
|
||||
buffer_len = len(mpos.ui.main_display._frame_buffer1)
|
||||
bytes_per_row = self.hor_res * 2
|
||||
if bytes_per_row <= 0:
|
||||
self.render_next = True
|
||||
return
|
||||
|
||||
rows_per_chunk = buffer_len // bytes_per_row
|
||||
if rows_per_chunk <= 0:
|
||||
self.render_next = True
|
||||
return
|
||||
|
||||
if rows_per_chunk >= self.ver_res:
|
||||
self.chunk_rows_per = self.ver_res
|
||||
self.chunk_index = 0
|
||||
self.chunk_total = 1
|
||||
else:
|
||||
self.chunk_rows_per = rows_per_chunk
|
||||
self.chunk_index = 0
|
||||
self.chunk_total = (self.ver_res + rows_per_chunk - 1) // rows_per_chunk
|
||||
|
||||
self.chunk_in_progress = True
|
||||
self.chunk_waiting = False
|
||||
self.flush_ready = False
|
||||
self._render_and_send_chunk()
|
||||
|
||||
def _render_and_send_chunk(self):
|
||||
if not self.chunk_in_progress:
|
||||
return
|
||||
if self.chunk_waiting:
|
||||
return
|
||||
if self.chunk_index >= self.chunk_total:
|
||||
self.chunk_in_progress = False
|
||||
self.render_next = True
|
||||
return
|
||||
|
||||
y_offset = self.chunk_index * self.chunk_rows_per
|
||||
rows = min(self.chunk_rows_per, self.ver_res - y_offset)
|
||||
advance = (self.chunk_index == 0)
|
||||
is_last = (self.chunk_index + 1) == self.chunk_total
|
||||
|
||||
self.chunk_waiting = True
|
||||
breakout.render(y_offset, rows, advance)
|
||||
self.send_to_display(y_offset, rows, is_last)
|
||||
|
||||
def touch_cb(self, event):
|
||||
event_code = event.get_code()
|
||||
|
||||
Reference in New Issue
Block a user