winewayland.drv: Track damaged buffer regions.

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.
This commit is contained in:
Alexandros Frantzis 2023-05-18 16:20:18 +03:00 committed by Alexandre Julliard
parent 4371a26041
commit d5b3ac08c9
3 changed files with 147 additions and 5 deletions

View file

@ -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;
@ -264,6 +261,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);
}
@ -307,6 +306,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,

View file

@ -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
*/

View file

@ -205,6 +205,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
*/
@ -254,6 +268,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
*/
@ -262,10 +365,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)
{
@ -277,6 +382,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_get_free_buffer(wws->wayland_buffer_queue);
if (!shm_buffer)
{
@ -284,7 +400,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)
@ -300,6 +416,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) reset_bounds(&wws->bounds);
wayland_window_surface_unlock(window_surface);