From: Alexandros Frantzis alexandros.frantzis@collabora.com
Track the buffer regions that require an update since the last buffer commit, and copy only those parts from the window_surface to the buffer. --- dlls/winewayland.drv/wayland_surface.c | 12 ++- dlls/winewayland.drv/waylanddrv.h | 18 ++++ dlls/winewayland.drv/window_surface.c | 122 ++++++++++++++++++++++++- 3 files changed, 147 insertions(+), 5 deletions(-)
diff --git a/dlls/winewayland.drv/wayland_surface.c b/dlls/winewayland.drv/wayland_surface.c index e200be769f7..cf49140d8bc 100644 --- a/dlls/winewayland.drv/wayland_surface.c +++ b/dlls/winewayland.drv/wayland_surface.c @@ -33,9 +33,6 @@
WINE_DEFAULT_DEBUG_CHANNEL(waylanddrv);
-/* We only use 4 byte formats. */ -#define WINEWAYLAND_BYTES_PER_PIXEL 4 - /* Protects access to the user data of xdg_surface */ static pthread_mutex_t xdg_data_mutex = PTHREAD_MUTEX_INITIALIZER;
@@ -258,6 +255,8 @@ void wayland_shm_buffer_unref(struct wayland_shm_buffer *shm_buffer) wl_buffer_destroy(shm_buffer->wl_buffer); if (shm_buffer->map_data) NtUnmapViewOfSection(GetCurrentProcess(), shm_buffer->map_data); + if (shm_buffer->damage_region) + NtGdiDeleteObjectApp(shm_buffer->damage_region);
free(shm_buffer); } @@ -301,6 +300,13 @@ struct wayland_shm_buffer *wayland_shm_buffer_create(int width, int height, shm_buffer->height = height; shm_buffer->map_size = size;
+ shm_buffer->damage_region = NtGdiCreateRectRgn(0, 0, width, height); + if (!shm_buffer->damage_region) + { + ERR("Failed to create buffer damage region\n"); + goto err; + } + section_size.QuadPart = size; status = NtCreateSection(&handle, GENERIC_READ | SECTION_MAP_READ | SECTION_MAP_WRITE, diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index 5abc5ae2d43..992f504e3d7 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -32,11 +32,15 @@
#include "windef.h" #include "winbase.h" +#include "ntgdi.h" #include "wine/gdi_driver.h" #include "wine/rbtree.h"
#include "unixlib.h"
+/* We only use 4 byte formats. */ +#define WINEWAYLAND_BYTES_PER_PIXEL 4 + /********************************************************************** * Globals */ @@ -114,6 +118,7 @@ struct wayland_shm_buffer size_t map_size; BOOL busy; LONG ref; + HRGN damage_region; };
/********************************************************************** @@ -160,6 +165,19 @@ void wayland_window_surface_update_wayland_surface(struct window_surface *surfac struct wayland_surface *wayland_surface) DECLSPEC_HIDDEN; void wayland_window_flush(HWND hwnd) DECLSPEC_HIDDEN;
+/********************************************************************** + * Helpers + */ + +static inline BOOL intersect_rect(RECT *dst, const RECT *src1, const RECT *src2) +{ + dst->left = max(src1->left, src2->left); + dst->top = max(src1->top, src2->top); + dst->right = min(src1->right, src2->right); + dst->bottom = min(src1->bottom, src2->bottom); + return !IsRectEmpty(dst); +} + /********************************************************************** * USER driver functions */ diff --git a/dlls/winewayland.drv/window_surface.c b/dlls/winewayland.drv/window_surface.c index 76084a7c8ad..d3bded821a7 100644 --- a/dlls/winewayland.drv/window_surface.c +++ b/dlls/winewayland.drv/window_surface.c @@ -211,6 +211,20 @@ out: return shm_buffer; }
+/********************************************************************** + * wayland_buffer_queue_add_damage + */ +static void wayland_buffer_queue_add_damage(struct wayland_buffer_queue *queue, HRGN damage) +{ + struct wayland_shm_buffer *shm_buffer; + + wl_list_for_each(shm_buffer, &queue->buffer_list, link) + { + NtGdiCombineRgn(shm_buffer->damage_region, shm_buffer->damage_region, + damage, RGN_OR); + } +} + /*********************************************************************** * wayland_window_surface_lock */ @@ -260,6 +274,95 @@ static void wayland_window_surface_set_region(struct window_surface *window_surf /* TODO */ }
+/********************************************************************** + * get_region_data + */ +static RGNDATA *get_region_data(HRGN region) +{ + RGNDATA *data; + DWORD size; + + if (!region) return NULL; + if (!(size = NtGdiGetRegionData(region, 0, NULL))) return NULL; + if (!(data = malloc(size))) return NULL; + if (!NtGdiGetRegionData(region, size, data)) + { + free(data); + return NULL; + } + + return data; +} + +/********************************************************************** + * copy_pixel_region + */ +static void copy_pixel_region(char *src_pixels, RECT *src_rect, + char *dst_pixels, RECT *dst_rect, + HRGN region) +{ + static const int bpp = WINEWAYLAND_BYTES_PER_PIXEL; + RGNDATA *rgndata = get_region_data(region); + RECT *rgn_rect; + RECT *rgn_rect_end; + int src_stride, dst_stride; + + if (!rgndata) return; + + src_stride = (src_rect->right - src_rect->left) * bpp; + dst_stride = (dst_rect->right - dst_rect->left) * bpp; + + rgn_rect = (RECT *)rgndata->Buffer; + rgn_rect_end = rgn_rect + rgndata->rdh.nCount; + + for (;rgn_rect < rgn_rect_end; rgn_rect++) + { + char *src, *dst; + int y, width_bytes, height; + RECT rc; + + TRACE("rect %s\n", wine_dbgstr_rect(rgn_rect)); + + if (!intersect_rect(&rc, rgn_rect, src_rect)) continue; + if (!intersect_rect(&rc, &rc, dst_rect)) continue; + + src = src_pixels + rc.top * src_stride + rc.left * bpp; + dst = dst_pixels + rc.top * dst_stride + rc.left * bpp; + width_bytes = (rc.right - rc.left) * bpp; + height = rc.bottom - rc.top; + + /* Fast path for full width rectangles. */ + if (width_bytes == src_stride && width_bytes == dst_stride) + { + memcpy(dst, src, height * width_bytes); + continue; + } + + for (y = 0; y < height; y++) + { + memcpy(dst, src, width_bytes); + src += src_stride; + dst += dst_stride; + } + } + + free(rgndata); +} + +/********************************************************************** + * wayland_window_surface_copy_to_buffer + */ +static void wayland_window_surface_copy_to_buffer(struct wayland_window_surface *wws, + struct wayland_shm_buffer *buffer, + HRGN region) +{ + RECT wws_rect = {0, 0, wws->info.bmiHeader.biWidth, + abs(wws->info.bmiHeader.biHeight)}; + RECT buffer_rect = {0, 0, buffer->width, buffer->height}; + TRACE("wws=%p buffer=%p\n", wws, buffer); + copy_pixel_region(wws->bits, &wws_rect, buffer->map_data, &buffer_rect, region); +} + /*********************************************************************** * wayland_window_surface_flush */ @@ -268,10 +371,12 @@ static void wayland_window_surface_flush(struct window_surface *window_surface) struct wayland_window_surface *wws = wayland_window_surface_cast(window_surface); struct wayland_shm_buffer *shm_buffer = NULL; BOOL flushed = FALSE; + RECT damage_rect; + HRGN surface_damage_region;
wayland_window_surface_lock(window_surface);
- if (IsRectEmpty(&wws->bounds)) goto done; + if (!intersect_rect(&damage_rect, &wws->header.rect, &wws->bounds)) goto done;
if (!wws->wayland_surface || !wws->wayland_buffer_queue) { @@ -283,6 +388,17 @@ static void wayland_window_surface_flush(struct window_surface *window_surface) TRACE("surface=%p hwnd=%p surface_rect=%s bounds=%s\n", wws, wws->hwnd, wine_dbgstr_rect(&wws->header.rect), wine_dbgstr_rect(&wws->bounds));
+ surface_damage_region = NtGdiCreateRectRgn(damage_rect.left, damage_rect.top, + damage_rect.right, damage_rect.bottom); + if (!surface_damage_region) + { + ERR("failed to create surface damage region\n"); + goto done; + } + + wayland_buffer_queue_add_damage(wws->wayland_buffer_queue, surface_damage_region); + NtGdiDeleteObjectApp(surface_damage_region); + shm_buffer = wayland_buffer_queue_acquire_buffer(wws->wayland_buffer_queue); if (!shm_buffer) { @@ -290,7 +406,7 @@ static void wayland_window_surface_flush(struct window_surface *window_surface) goto done; }
- memcpy(shm_buffer->map_data, wws->bits, shm_buffer->map_size); + wayland_window_surface_copy_to_buffer(wws, shm_buffer, shm_buffer->damage_region);
pthread_mutex_lock(&wws->wayland_surface->mutex); if (wws->wayland_surface->current_serial) @@ -306,6 +422,8 @@ static void wayland_window_surface_flush(struct window_surface *window_surface) pthread_mutex_unlock(&wws->wayland_surface->mutex); wl_display_flush(process_wayland.wl_display);
+ NtGdiSetRectRgn(shm_buffer->damage_region, 0, 0, 0, 0); + done: if (flushed) {