Modify the dirty function so it tracks and updates 2d regions. The current dirty() implementation only tracks the y coordinate changes, and then updates all the pixels in between the changed(dirty) y coordinates, such that the entire screen's width is updated even if just a single 16x16 pixel character was updated(dirtied). This patch modifies render_targets to use grub_video_rects to track dirty regions instead of the previous dirty struct. dirty callees are then modified appropriately using 2d coordinates. Finally the doublebuf_*_update_screen functions are modified to update 2d regions by making multiple grub_memcpy calls to the framebuffer, one for each row of pixels being updated(instead of a single call for the contiguous block of framebuffer data as was previously done).
Changes to be committed: modified: grub-core/video/fb/video_fb.c Signed-off-by: kbader94 <kyle.bade...@gmail.com> --- grub-core/video/fb/video_fb.c | 199 +++++++++++++++++++++------------- 1 file changed, 122 insertions(+), 77 deletions(-) diff --git a/grub-core/video/fb/video_fb.c b/grub-core/video/fb/video_fb.c index fa4ebde26..d17c47b11 100644 --- a/grub-core/video/fb/video_fb.c +++ b/grub-core/video/fb/video_fb.c @@ -31,13 +31,6 @@ GRUB_MOD_LICENSE ("GPLv3+"); typedef grub_err_t (*grub_video_fb_doublebuf_update_screen_t) (void); typedef volatile void *framebuf_t; - -struct dirty -{ - int first_line; - int last_line; -}; - static struct { struct grub_video_fbrender_target *render_target; @@ -47,8 +40,8 @@ static struct unsigned int palette_size; - struct dirty current_dirty; - struct dirty previous_dirty; + struct grub_video_rect current_dirty; + struct grub_video_rect previous_dirty; /* For page flipping strategy. */ int displayed_page; /* The page # that is the front buffer. */ @@ -831,14 +824,36 @@ grub_video_fb_unmap_color_int (struct grub_video_fbblit_info * source, } static void -dirty (int y, int height) +dirty (int x, int y, int width, int height) { + + grub_video_rect_t *current_dirty = &framebuffer.current_dirty; + grub_video_rect_t dirty_rect = { + .x = x, + .y = y, + .width = width, + .height = height + }; + if (framebuffer.render_target != framebuffer.back_target) return; - if (framebuffer.current_dirty.first_line > y) - framebuffer.current_dirty.first_line = y; - if (framebuffer.current_dirty.last_line < y + height) - framebuffer.current_dirty.last_line = y + height; + + /* extend dirty_rect bounds if dirty x min is less than current x */ + if (dirty_rect.x < current_dirty->x) + current_dirty->x = dirty_rect.x; + + /* extend dirty_rect bounds if dirty x max is greater than current width */ + if (dirty_rect.x + dirty_rect.width > current_dirty->x + current_dirty->width) + current_dirty->width = (dirty_rect.x + dirty_rect.width) - current_dirty->x; + + /* extend dirty_rect bounds if dirty y min is less than current y */ + if (dirty_rect.y < current_dirty->y) + current_dirty->y = dirty_rect.y; + + /* extend dirty_rect bounds if dirty y max is greater than current height */ + if (dirty_rect.y + dirty_rect.height > current_dirty->y + current_dirty->height) + current_dirty->height = (dirty_rect.y + dirty_rect.height) - current_dirty->y; + } grub_err_t @@ -896,7 +911,7 @@ grub_video_fb_fill_rect (grub_video_color_t color, int x, int y, x += area_x; y += area_y; - dirty (y, height); + dirty (x, y, width, height); /* Use fbblit_info to encapsulate rendering. */ target.mode_info = &framebuffer.render_target->mode_info; @@ -1008,8 +1023,7 @@ grub_video_fb_blit_source (struct grub_video_fbblit_info *source, target.mode_info = &framebuffer.render_target->mode_info; target.data = framebuffer.render_target->data; - /* Do actual blitting. */ - dirty (y, height); + dirty (x, y, width, height); grub_video_fb_dispatch_blit (&target, source, oper, x, y, width, height, offset_x, offset_y); @@ -1061,7 +1075,9 @@ grub_video_fb_scroll (grub_video_color_t color, int dx, int dy) width = framebuffer.render_target->viewport.width - grub_abs (dx); height = framebuffer.render_target->viewport.height - grub_abs (dy); - dirty (framebuffer.render_target->viewport.y, + dirty (framebuffer.render_target->viewport.x, + framebuffer.render_target->viewport.y, + framebuffer.render_target->viewport.width, framebuffer.render_target->viewport.height); if (dx < 0) @@ -1416,28 +1432,33 @@ grub_video_fb_get_active_render_target (struct grub_video_fbrender_target **targ static grub_err_t doublebuf_blit_update_screen (void) { - if (framebuffer.current_dirty.first_line - <= framebuffer.current_dirty.last_line) - { - grub_size_t copy_size; - - if (grub_sub (framebuffer.current_dirty.last_line, - framebuffer.current_dirty.first_line, ©_size) || - grub_mul (framebuffer.back_target->mode_info.pitch, copy_size, ©_size)) - { - /* Shouldn't happen, but if it does we've a bug. */ - return GRUB_ERR_BUG; - } - - grub_memcpy ((char *) framebuffer.pages[0] + framebuffer.current_dirty.first_line * - framebuffer.back_target->mode_info.pitch, - (char *) framebuffer.back_target->data + framebuffer.current_dirty.first_line * - framebuffer.back_target->mode_info.pitch, - copy_size); + if (framebuffer.current_dirty.height > 0 || framebuffer.current_dirty.width > 0) { + /* dirty row size in bytes */ + grub_size_t row_size = framebuffer.current_dirty.width + * framebuffer.back_target->mode_info.bytes_per_pixel; + + /* address of dirty_rect origin on render target */ + char *src_base = (char *)framebuffer.back_target->data + framebuffer.current_dirty.y + * framebuffer.back_target->mode_info.pitch + framebuffer.current_dirty.x + * framebuffer.back_target->mode_info.bytes_per_pixel; + + /* address of dirty_rect origin on display target */ + char *dst_base = (char *)framebuffer.pages[0] + framebuffer.current_dirty.y + * framebuffer.back_target->mode_info.pitch + framebuffer.current_dirty.x + * framebuffer.back_target->mode_info.bytes_per_pixel; + + /* blit each row from render_target to display */ + grub_size_t pitch = framebuffer.back_target->mode_info.pitch; + for (unsigned int y = 0; y < framebuffer.current_dirty.height; y++) { + grub_memcpy(dst_base + y * pitch, src_base + y * pitch, row_size); } - framebuffer.current_dirty.first_line - = framebuffer.back_target->mode_info.height; - framebuffer.current_dirty.last_line = 0; + } + + /* reset current_dirty rect */ + framebuffer.current_dirty.y = framebuffer.back_target->mode_info.height; + framebuffer.current_dirty.height = 0; + framebuffer.current_dirty.x = framebuffer.back_target->mode_info.width; + framebuffer.current_dirty.width = 0; return GRUB_ERR_NONE; } @@ -1470,8 +1491,10 @@ grub_video_fb_doublebuf_blit_init (struct grub_video_fbrender_target **back, framebuffer.pages[0] = framebuf; framebuffer.displayed_page = 0; framebuffer.render_page = 0; - framebuffer.current_dirty.first_line = mode_info.height; - framebuffer.current_dirty.last_line = 0; + framebuffer.current_dirty.y = framebuffer.back_target->mode_info.height; + framebuffer.current_dirty.height = 0; + framebuffer.current_dirty.x = framebuffer.back_target->mode_info.width; + framebuffer.current_dirty.width = 0; return GRUB_ERR_NONE; } @@ -1481,37 +1504,52 @@ doublebuf_pageflipping_update_screen (void) { int new_displayed_page; grub_err_t err; - int first_line, last_line; - - first_line = framebuffer.current_dirty.first_line; - last_line = framebuffer.current_dirty.last_line; - if (first_line > framebuffer.previous_dirty.first_line) - first_line = framebuffer.previous_dirty.first_line; - if (last_line < framebuffer.previous_dirty.last_line) - last_line = framebuffer.previous_dirty.last_line; - if (first_line <= last_line) - { - grub_size_t copy_size; - - if (grub_sub (last_line, first_line, ©_size) || - grub_mul (framebuffer.back_target->mode_info.pitch, copy_size, ©_size)) - { - /* Shouldn't happen, but if it does we've a bug. */ - return GRUB_ERR_BUG; - } - - grub_memcpy ((char *) framebuffer.pages[framebuffer.render_page] + first_line * - framebuffer.back_target->mode_info.pitch, - (char *) framebuffer.back_target->data + first_line * - framebuffer.back_target->mode_info.pitch, - copy_size); + /* compare current and previous dirty_rects, and use greatest extents */ + unsigned int min_x = framebuffer.current_dirty.x; + unsigned int min_y = framebuffer.current_dirty.y; + unsigned int max_x = framebuffer.current_dirty.x + framebuffer.current_dirty.width; + unsigned int max_y = framebuffer.current_dirty.y + framebuffer.current_dirty.height; + if (framebuffer.previous_dirty.x < min_x) + min_x = framebuffer.previous_dirty.x; + if (framebuffer.previous_dirty.y < min_y) + min_y = framebuffer.previous_dirty.y; + if (framebuffer.previous_dirty.x + framebuffer.previous_dirty.width > max_x) + max_x = framebuffer.previous_dirty.x + framebuffer.previous_dirty.width; + if (framebuffer.previous_dirty.y + framebuffer.previous_dirty.height > max_y) + max_y = framebuffer.previous_dirty.y + framebuffer.previous_dirty.height; + int dirty_width = max_x - min_x; + int dirty_height = max_y - min_y; + + /* check if there is anything to do */ + if (dirty_width > 0 && dirty_height > 0) { + /* byte size of dirty row */ + grub_size_t row_size = dirty_width + * framebuffer.back_target->mode_info.bytes_per_pixel; + + /* render target base address */ + char *src_base = (char *)framebuffer.back_target->data + min_y + * framebuffer.back_target->mode_info.pitch + min_x + * framebuffer.back_target->mode_info.bytes_per_pixel; + + /* display target base address */ + char *dst_base = (char *)framebuffer.pages[framebuffer.render_page] + min_y + * framebuffer.back_target->mode_info.pitch + min_x + * framebuffer.back_target->mode_info.bytes_per_pixel; + + /* blit each row from render_target to display */ + grub_size_t pitch = framebuffer.back_target->mode_info.pitch; + for (int y = 0; y < dirty_height; y++) { + grub_memcpy(dst_base + y * pitch, src_base + y * pitch, row_size); } + } + /* reset current_dirty rect */ framebuffer.previous_dirty = framebuffer.current_dirty; - framebuffer.current_dirty.first_line - = framebuffer.back_target->mode_info.height; - framebuffer.current_dirty.last_line = 0; + framebuffer.current_dirty.y = framebuffer.back_target->mode_info.height; + framebuffer.current_dirty.height = 0; + framebuffer.current_dirty.x = framebuffer.back_target->mode_info.width; + framebuffer.current_dirty.width = 0; /* Swap the page numbers in the framebuffer struct. */ new_displayed_page = framebuffer.render_page; @@ -1570,12 +1608,17 @@ doublebuf_pageflipping_init (struct grub_video_mode_info *mode_info, framebuffer.pages[0] = page0_ptr; framebuffer.pages[1] = page1_ptr; - framebuffer.current_dirty.first_line - = framebuffer.back_target->mode_info.height; - framebuffer.current_dirty.last_line = 0; - framebuffer.previous_dirty.first_line - = framebuffer.back_target->mode_info.height; - framebuffer.previous_dirty.last_line = 0; + /* reset current_dirty rect */ + framebuffer.current_dirty.y = framebuffer.back_target->mode_info.height; + framebuffer.current_dirty.height = 0; + framebuffer.current_dirty.x = framebuffer.back_target->mode_info.width; + framebuffer.current_dirty.width = 0; + + /* reset previous_dirty rect */ + framebuffer.previous_dirty.y = framebuffer.back_target->mode_info.height; + framebuffer.previous_dirty.height = 0; + framebuffer.previous_dirty.x = framebuffer.back_target->mode_info.width; + framebuffer.previous_dirty.width = 0; /* Set the framebuffer memory data pointer and display the right page. */ err = set_page_in (framebuffer.displayed_page); @@ -1661,9 +1704,11 @@ grub_video_fb_setup (unsigned int mode_type, unsigned int mode_mask, framebuffer.displayed_page = 0; framebuffer.render_page = 0; framebuffer.set_page = 0; - framebuffer.current_dirty.first_line - = framebuffer.back_target->mode_info.height; - framebuffer.current_dirty.last_line = 0; + /* reset transformed_add_rect */ + framebuffer.current_dirty.y = framebuffer.back_target->mode_info.height; + framebuffer.current_dirty.height = 0; + framebuffer.current_dirty.x = framebuffer.back_target->mode_info.width; + framebuffer.current_dirty.width = 0; mode_info->mode_type &= ~GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; -- 2.34.1 _______________________________________________ Grub-devel mailing list Grub-devel@gnu.org https://lists.gnu.org/mailman/listinfo/grub-devel