diff --git a/render.c b/render.c index 2eab275e..c4ea9bc6 100644 --- a/render.c +++ b/render.c @@ -602,14 +602,6 @@ grid_render_scroll(struct terminal *term, struct buffer *buf, term->wl->shm, buf, dmg->scroll.lines * term->cell_height, term->margins.top, dmg->scroll.region.start * term->cell_height, term->margins.bottom, (term->rows - dmg->scroll.region.end) * term->cell_height); - - if (!did_shm_scroll) - LOG_DBG("fast scroll failed"); - } else { - LOG_DBG( - "skipping SHM scroll " - "(region.start=%d, bottom-region-lines=%d, term-lines=%d)", - dmb->scroll.region.start, scroll_region_lines, term->rows); } if (did_shm_scroll) { @@ -647,26 +639,65 @@ static void grid_render_scroll_reverse(struct terminal *term, struct buffer *buf, const struct damage *dmg) { - int src_y = term->margins.top + (dmg->scroll.region.start + 0) * term->cell_height; - int dst_y = term->margins.top + (dmg->scroll.region.start + dmg->scroll.lines) * term->cell_height; int height = (dmg->scroll.region.end - dmg->scroll.region.start - dmg->scroll.lines) * term->cell_height; - LOG_DBG("damage: SCROLL REVERSE: %d-%d by %d lines (dst-y: %d, src-y: %d, " - "height: %d, stride: %d, mmap-size: %zu)", - dmg->scroll.region.start, dmg->scroll.region.end, - dmg->scroll.lines, - dst_y, src_y, height, buf->stride, - buf->size); + LOG_DBG( + "damage: SCROLL REVERSE: %d-%d by %d lines"m + dmg->scroll.region.start, dmg->scroll.region.end, dmg->scroll.lines); - if (height > 0) { + if (height <= 0) + return; + +#if TIME_SCROLL_DAMAGE + struct timeval start_time; + gettimeofday(&start_time, NULL); +#endif + + int src_y = term->margins.top + (dmg->scroll.region.start + 0) * term->cell_height; + int dst_y = term->margins.top + (dmg->scroll.region.start + dmg->scroll.lines) * term->cell_height; + + bool try_shm_scroll = + shm_can_scroll() && (dmg->scroll.lines + + dmg->scroll.region.start + + (term->rows - dmg->scroll.region.end)) < term->rows / 2; + + bool did_shm_scroll = false; + + if (try_shm_scroll) { + did_shm_scroll = shm_scroll( + term->wl->shm, buf, -dmg->scroll.lines * term->cell_height, + term->margins.top, dmg->scroll.region.start * term->cell_height, + term->margins.bottom, (term->rows - dmg->scroll.region.end) * term->cell_height); + } + + if (did_shm_scroll) { + /* Restore margins */ + render_margin( + term, buf, dmg->scroll.region.start, dmg->scroll.region.start + dmg->scroll.lines, + true, true); + } else { + /* Fallback for when we either cannot do SHM scrolling, or it failed */ uint8_t *raw = buf->mmapped; memmove(raw + dst_y * buf->stride, raw + src_y * buf->stride, height * buf->stride); - - wl_surface_damage_buffer( - term->window->surface, term->margins.left, dst_y, term->width - term->margins.left - term->margins.right, height); } + +#if TIME_SCROLL_DAMAGE + struct timeval end_time; + gettimeofday(&end_time, NULL); + + struct timeval memmove_time; + timersub(&end_time, &start_time, &memmove_time); + LOG_INFO("scrolled REVERSE %dKB (%d lines) using %s in %lds %ldus", + height * buf->stride / 1024, dmg->scroll.lines, + did_shm_scroll ? "SHM" : try_shm_scroll ? "memmove (SHM failed)" : "memmove", + memmove_time.tv_sec, memmove_time.tv_usec); +#endif + + wl_surface_damage_buffer( + term->window->surface, term->margins.left, dst_y, + term->width - term->margins.left - term->margins.right, height); } static void diff --git a/shm.c b/shm.c index d05210a2..1f2bd1a6 100644 --- a/shm.c +++ b/shm.c @@ -64,8 +64,6 @@ instantiate_offset(struct wl_shm *shm, struct buffer *buf, size_t new_offset) assert(buf->wl_buf == NULL); assert(buf->pix == NULL); - assert(new_offset >= buf->offset); - static size_t page_size = 0; if (page_size == 0) { page_size = sysconf(_SC_PAGE_SIZE); @@ -286,10 +284,10 @@ shm_can_scroll(void) return can_punch_hole; } -bool -shm_scroll(struct wl_shm *shm, struct buffer *buf, int rows, - int top_margin, int top_keep_rows, - int bottom_margin, int bottom_keep_rows) +static bool +shm_scroll_forward(struct wl_shm *shm, struct buffer *buf, int rows, + int top_margin, int top_keep_rows, + int bottom_margin, int bottom_keep_rows) { assert(can_punch_hole); assert(buf->busy); @@ -365,7 +363,6 @@ shm_scroll(struct wl_shm *shm, struct buffer *buf, int rows, LOG_INFO("PUNCH HOLE: %lds %ldus", tot.tv_sec, tot.tv_usec); #endif - bool ret = instantiate_offset(shm, buf, new_offset); if (ret && bottom_keep_rows > 0) { @@ -387,7 +384,94 @@ shm_scroll(struct wl_shm *shm, struct buffer *buf, int rows, return ret; } -void +static bool +shm_scroll_reverse(struct wl_shm *shm, struct buffer *buf, int rows, + int top_margin, int top_keep_rows, + int bottom_margin, int bottom_keep_rows) +{ + assert(rows > 0); + + size_t diff = rows * buf->stride; + if (diff > buf->offset) + return false; + + const size_t new_offset = buf->offset - rows * buf->stride; + assert(new_offset < buf->offset); + +#if TIME_SCROLL + struct timeval time0; + gettimeofday(&time0, NULL); + + struct timeval tot; + struct timeval time1 = time0; +#endif + + if (bottom_keep_rows > 0) { + /* Copy 'bottom' region to its new location */ + memmove( + (uint8_t *)buf->mmapped + buf->size - (bottom_margin + rows + bottom_keep_rows) * buf->stride, + (uint8_t *)buf->mmapped + buf->size - (bottom_margin + bottom_keep_rows) * buf->stride, + bottom_keep_rows * buf->stride); + +#if TIME_SCROLL + gettimeofday(&time1, NULL); + timersub(&time1, &time0, &tot); + LOG_INFO("memmove (bottom region): %lds %ldus", tot.tv_sec, tot.tv_usec); +#endif + } + + /* Destroy old objects (they point to the old offset) */ + pixman_image_unref(buf->pix); + wl_buffer_destroy(buf->wl_buf); + munmap(buf->real_mmapped, buf->mmap_size); + + buf->pix = NULL; + buf->wl_buf = NULL; + buf->real_mmapped = buf->mmapped = NULL; + + if (ftruncate(buf->fd, new_offset + buf->size) < 0) { + abort(); + } + +#if TIME_SCROLL + struct timeval time2; + gettimeofday(&time2, NULL); + timersub(&time2, &time1, &tot); + LOG_INFO("ftruncate: %lds %ldus", tot.tv_sec, tot.tv_usec); +#endif + + bool ret = instantiate_offset(shm, buf, new_offset); + + if (ret && top_keep_rows > 0) { + /* Copy current 'top' region to its new location */ + memmove( + (uint8_t *)buf->mmapped + (top_margin + 0) * buf->stride, + (uint8_t *)buf->mmapped + (top_margin + rows) * buf->stride, + top_keep_rows * buf->stride); + +#if TIME_SCROLL + struct timeval time3; + gettimeofday(&time3, NULL); + timersub(&time3, &time2, &tot); + LOG_INFO("memmove (top region): %lds %ldus", tot.tv_sec, tot.tv_usec); +#endif + } + + return ret; +} + +bool +shm_scroll(struct wl_shm *shm, struct buffer *buf, int rows, + int top_margin, int top_keep_rows, + int bottom_margin, int bottom_keep_rows) +{ + assert(rows != 0); + return rows > 0 + ? shm_scroll_forward(shm, buf, rows, top_margin, top_keep_rows, bottom_margin, bottom_keep_rows) + : shm_scroll_reverse(shm, buf, -rows, top_margin, top_keep_rows, bottom_margin, bottom_keep_rows); +} + + void shm_purge(struct wl_shm *shm, unsigned long cookie) { LOG_DBG("cookie=%lx: purging all buffers", cookie);