diff options
-rw-r--r-- | backend/wayland/wl_seat.c | 40 | ||||
-rw-r--r-- | include/wlr/render/egl.h | 14 | ||||
-rw-r--r-- | include/wlr/types/wlr_buffer.h | 56 | ||||
-rw-r--r-- | include/wlr/types/wlr_keyboard.h | 2 | ||||
-rw-r--r-- | include/wlr/types/wlr_surface.h | 1 | ||||
-rw-r--r-- | render/egl.c | 142 | ||||
-rw-r--r-- | render/gles2/texture.c | 2 | ||||
-rw-r--r-- | rootston/main.c | 3 | ||||
-rw-r--r-- | types/meson.build | 1 | ||||
-rw-r--r-- | types/wlr_buffer.c | 187 | ||||
-rw-r--r-- | types/wlr_surface.c | 132 |
11 files changed, 429 insertions, 151 deletions
diff --git a/backend/wayland/wl_seat.c b/backend/wayland/wl_seat.c index cf9b9372..8ed61409 100644 --- a/backend/wayland/wl_seat.c +++ b/backend/wayland/wl_seat.c @@ -3,6 +3,7 @@ #include <stdint.h> #include <stdlib.h> #include <string.h> +#include <time.h> #include <wayland-client.h> #include <wlr/interfaces/wlr_input_device.h> #include <wlr/interfaces/wlr_keyboard.h> @@ -169,12 +170,51 @@ static void keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard, // TODO: set keymap } +static uint32_t get_current_time_msec() { + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + return now.tv_nsec / 1000; +} + static void keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { + struct wlr_input_device *dev = data; + + uint32_t time = get_current_time_msec(); + + uint32_t *keycode_ptr; + wl_array_for_each(keycode_ptr, keys) { + struct wlr_event_keyboard_key event = { + .keycode = *keycode_ptr, + .state = WLR_KEY_PRESSED, + .time_msec = time, + .update_state = false, + }; + wlr_keyboard_notify_key(dev->keyboard, &event); + } } static void keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface) { + struct wlr_input_device *dev = data; + + uint32_t time = get_current_time_msec(); + + uint32_t pressed[dev->keyboard->num_keycodes]; + memcpy(pressed, dev->keyboard->keycodes, + dev->keyboard->num_keycodes * sizeof(uint32_t)); + + for (size_t i = 0; i < sizeof(pressed)/sizeof(pressed[0]); ++i) { + uint32_t keycode = pressed[i]; + + struct wlr_event_keyboard_key event = { + .keycode = keycode, + .state = WLR_KEY_RELEASED, + .time_msec = time, + .update_state = false, + }; + wlr_keyboard_notify_key(dev->keyboard, &event); + } } static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard, diff --git a/include/wlr/render/egl.h b/include/wlr/render/egl.h index 6d79cbd2..0f591d60 100644 --- a/include/wlr/render/egl.h +++ b/include/wlr/render/egl.h @@ -16,12 +16,14 @@ struct wlr_egl { const char *exts_str; struct { - bool buffer_age; - bool swap_buffers_with_damage; - bool dmabuf_import; - bool dmabuf_import_modifiers; - bool bind_wayland_display; - } egl_exts; + bool bind_wayland_display_wl; + bool buffer_age_ext; + bool image_dmabuf_import_modifiers_ext; + bool image_dmabuf_import_ext; + bool image_base_khr; + bool swap_buffers_with_damage_ext; + bool swap_buffers_with_damage_khr; + } exts; struct wl_display *wl_display; }; diff --git a/include/wlr/types/wlr_buffer.h b/include/wlr/types/wlr_buffer.h new file mode 100644 index 00000000..fc348a1c --- /dev/null +++ b/include/wlr/types/wlr_buffer.h @@ -0,0 +1,56 @@ +#ifndef WLR_TYPES_WLR_BUFFER_H +#define WLR_TYPES_WLR_BUFFER_H + +#include <pixman.h> +#include <wayland-server.h> + +/** + * A client buffer. + */ +struct wlr_buffer { + struct wl_resource *resource; // can be NULL + struct wlr_texture *texture; // can be NULL + bool released; + size_t n_refs; + + struct wl_listener resource_destroy; +}; + +struct wlr_renderer; + +/** + * Check if a resource is a wl_buffer resource. + */ +bool wlr_resource_is_buffer(struct wl_resource *resource); +/** + * Get the size of a wl_buffer resource. + */ +bool wlr_buffer_get_resource_size(struct wl_resource *resource, + struct wlr_renderer *renderer, int *width, int *height); + +/** + * Upload a buffer to the GPU and reference it. + */ +struct wlr_buffer *wlr_buffer_create(struct wlr_renderer *renderer, + struct wl_resource *resource); +/** + * Reference the buffer. + */ +struct wlr_buffer *wlr_buffer_ref(struct wlr_buffer *buffer); +/** + * Unreference the buffer. After this call, `buffer` may not be accessed + * anymore. + */ +void wlr_buffer_unref(struct wlr_buffer *buffer); +/** + * Try to update the buffer's content. On success, returns the updated buffer + * and destroys the provided `buffer`. On error, `buffer` is intact and NULL is + * returned. + * + * Fails if there's more than one reference to the buffer or if the texture + * isn't mutable. + */ +struct wlr_buffer *wlr_buffer_apply_damage(struct wlr_buffer *buffer, + struct wl_resource *resource, pixman_region32_t *damage); + +#endif diff --git a/include/wlr/types/wlr_keyboard.h b/include/wlr/types/wlr_keyboard.h index 97288508..67d4e5be 100644 --- a/include/wlr/types/wlr_keyboard.h +++ b/include/wlr/types/wlr_keyboard.h @@ -89,7 +89,7 @@ enum wlr_key_state { struct wlr_event_keyboard_key { uint32_t time_msec; uint32_t keycode; - bool update_state; + bool update_state; // if backend doesn't update modifiers on its own enum wlr_key_state state; }; diff --git a/include/wlr/types/wlr_surface.h b/include/wlr/types/wlr_surface.h index 526e4e2c..64503e78 100644 --- a/include/wlr/types/wlr_surface.h +++ b/include/wlr/types/wlr_surface.h @@ -69,6 +69,7 @@ struct wlr_subsurface { struct wlr_surface { struct wl_resource *resource; struct wlr_renderer *renderer; + struct wlr_buffer *buffer; struct wlr_texture *texture; struct wlr_surface_state *current, *pending; const char *role; // the lifetime-bound role or null diff --git a/render/egl.c b/render/egl.c index 676a0208..450562db 100644 --- a/render/egl.c +++ b/render/egl.c @@ -7,10 +7,6 @@ #include <wlr/util/log.h> #include "glapi.h" -// Extension documentation -// https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_image_base.txt. -// https://cgit.freedesktop.org/mesa/mesa/tree/docs/specs/WL_bind_wayland_display.spec - static bool egl_get_config(EGLDisplay disp, EGLint *attribs, EGLConfig *out, EGLint visual_id) { EGLint count = 0, matched = 0, ret; @@ -61,27 +57,27 @@ static void egl_log(EGLenum error, const char *command, EGLint msg_type, _wlr_log(egl_log_importance_to_wlr(msg_type), "[EGL] %s: %s", command, msg); } -static bool check_egl_ext(const char *egl_exts, const char *ext) { +static bool check_egl_ext(const char *exts, const char *ext) { size_t extlen = strlen(ext); - const char *end = egl_exts + strlen(egl_exts); + const char *end = exts + strlen(exts); - while (egl_exts < end) { - if (*egl_exts == ' ') { - egl_exts++; + while (exts < end) { + if (*exts == ' ') { + exts++; continue; } - size_t n = strcspn(egl_exts, " "); - if (n == extlen && strncmp(ext, egl_exts, n) == 0) { + size_t n = strcspn(exts, " "); + if (n == extlen && strncmp(ext, exts, n) == 0) { return true; } - egl_exts += n; + exts += n; } return false; } static void print_dmabuf_formats(struct wlr_egl *egl) { /* Avoid log msg if extension is not present */ - if (!egl->egl_exts.dmabuf_import_modifiers) { + if (!egl->exts.image_dmabuf_import_modifiers_ext) { return; } @@ -144,44 +140,83 @@ bool wlr_egl_init(struct wlr_egl *egl, EGLenum platform, void *remote_display, goto error; } - static const EGLint attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; - - egl->context = eglCreateContext(egl->display, egl->config, - EGL_NO_CONTEXT, attribs); - - if (egl->context == EGL_NO_CONTEXT) { - wlr_log(L_ERROR, "Failed to create EGL context"); - goto error; - } - - eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE, egl->context); egl->exts_str = eglQueryString(egl->display, EGL_EXTENSIONS); wlr_log(L_INFO, "Using EGL %d.%d", (int)major, (int)minor); wlr_log(L_INFO, "Supported EGL extensions: %s", egl->exts_str); wlr_log(L_INFO, "EGL vendor: %s", eglQueryString(egl->display, EGL_VENDOR)); - if (!check_egl_ext(egl->exts_str, "EGL_KHR_image_base")) { - wlr_log(L_ERROR, "Required EGL_KHR_image_base extension not supported"); - goto error; - } + egl->exts.image_base_khr = + check_egl_ext(egl->exts_str, "EGL_KHR_image_base") + && eglCreateImageKHR && eglDestroyImageKHR; - egl->egl_exts.buffer_age = + egl->exts.buffer_age_ext = check_egl_ext(egl->exts_str, "EGL_EXT_buffer_age"); - egl->egl_exts.swap_buffers_with_damage = - check_egl_ext(egl->exts_str, "EGL_EXT_swap_buffers_with_damage") || - check_egl_ext(egl->exts_str, "EGL_KHR_swap_buffers_with_damage"); - - egl->egl_exts.dmabuf_import = + egl->exts.swap_buffers_with_damage_ext = + (check_egl_ext(egl->exts_str, "EGL_EXT_swap_buffers_with_damage") && + eglSwapBuffersWithDamageEXT); + egl->exts.swap_buffers_with_damage_khr = + (check_egl_ext(egl->exts_str, "EGL_KHR_swap_buffers_with_damage") && + eglSwapBuffersWithDamageKHR); + + egl->exts.image_dmabuf_import_ext = check_egl_ext(egl->exts_str, "EGL_EXT_image_dma_buf_import"); - egl->egl_exts.dmabuf_import_modifiers = + egl->exts.image_dmabuf_import_modifiers_ext = check_egl_ext(egl->exts_str, "EGL_EXT_image_dma_buf_import_modifiers") && eglQueryDmaBufFormatsEXT && eglQueryDmaBufModifiersEXT; - - egl->egl_exts.bind_wayland_display = - check_egl_ext(egl->exts_str, "EGL_WL_bind_wayland_display"); print_dmabuf_formats(egl); + egl->exts.bind_wayland_display_wl = + check_egl_ext(egl->exts_str, "EGL_WL_bind_wayland_display") + && eglBindWaylandDisplayWL && eglUnbindWaylandDisplayWL + && eglQueryWaylandBufferWL; + + bool ext_context_priority = + check_egl_ext(egl->exts_str, "EGL_IMG_context_priority"); + + size_t atti = 0; + EGLint attribs[5]; + attribs[atti++] = EGL_CONTEXT_CLIENT_VERSION; + attribs[atti++] = 2; + + // On DRM, request a high priority context if possible + bool request_high_priority = ext_context_priority && + platform == EGL_PLATFORM_GBM_MESA; + + // Try to reschedule all of our rendering to be completed first. If it + // fails, it will fallback to the default priority (MEDIUM). + if (request_high_priority) { + attribs[atti++] = EGL_CONTEXT_PRIORITY_LEVEL_IMG; + attribs[atti++] = EGL_CONTEXT_PRIORITY_HIGH_IMG; + } + + attribs[atti++] = EGL_NONE; + assert(atti <= sizeof(attribs)/sizeof(attribs[0])); + + egl->context = eglCreateContext(egl->display, egl->config, + EGL_NO_CONTEXT, attribs); + if (egl->context == EGL_NO_CONTEXT) { + wlr_log(L_ERROR, "Failed to create EGL context"); + goto error; + } + + if (request_high_priority) { + EGLint priority = EGL_CONTEXT_PRIORITY_MEDIUM_IMG; + eglQueryContext(egl->display, egl->context, + EGL_CONTEXT_PRIORITY_LEVEL_IMG, &priority); + if (priority != EGL_CONTEXT_PRIORITY_HIGH_IMG) { + wlr_log(L_INFO, "Failed to obtain a high priority context"); + } else { + wlr_log(L_DEBUG, "Obtained high priority context"); + } + } + + if (!eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE, + egl->context)) { + wlr_log(L_ERROR, "Failed to make EGL context current"); + goto error; + } + return true; error: @@ -199,7 +234,8 @@ void wlr_egl_finish(struct wlr_egl *egl) { } eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - if (egl->wl_display && egl->egl_exts.bind_wayland_display) { + if (egl->wl_display) { + assert(egl->exts.bind_wayland_display_wl); eglUnbindWaylandDisplayWL(egl->display, egl->wl_display); } @@ -209,7 +245,7 @@ void wlr_egl_finish(struct wlr_egl *egl) { } bool wlr_egl_bind_display(struct wlr_egl *egl, struct wl_display *local_display) { - if (!egl->egl_exts.bind_wayland_display) { + if (!egl->exts.bind_wayland_display_wl) { return false; } @@ -222,7 +258,7 @@ bool wlr_egl_bind_display(struct wlr_egl *egl, struct wl_display *local_display) } bool wlr_egl_destroy_image(struct wlr_egl *egl, EGLImage image) { - if (!eglDestroyImageKHR) { + if (!egl->exts.image_base_khr) { return false; } if (!image) { @@ -243,7 +279,7 @@ EGLSurface wlr_egl_create_surface(struct wlr_egl *egl, void *window) { } static int egl_get_buffer_age(struct wlr_egl *egl, EGLSurface surface) { - if (!egl->egl_exts.buffer_age) { + if (!egl->exts.buffer_age_ext) { return -1; } @@ -278,7 +314,8 @@ bool wlr_egl_is_current(struct wlr_egl *egl) { bool wlr_egl_swap_buffers(struct wlr_egl *egl, EGLSurface surface, pixman_region32_t *damage) { EGLBoolean ret; - if (damage != NULL && egl->egl_exts.swap_buffers_with_damage) { + if (damage != NULL && (egl->exts.swap_buffers_with_damage_ext || + egl->exts.swap_buffers_with_damage_khr)) { int nrects; pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); @@ -290,8 +327,7 @@ bool wlr_egl_swap_buffers(struct wlr_egl *egl, EGLSurface surface, egl_damage[4*i + 3] = rects[i].y2 - rects[i].y1; } - assert(eglSwapBuffersWithDamageEXT || eglSwapBuffersWithDamageKHR); - if (eglSwapBuffersWithDamageEXT) { + if (egl->exts.swap_buffers_with_damage_ext) { ret = eglSwapBuffersWithDamageEXT(egl->display, surface, egl_damage, nrects); } else { @@ -312,7 +348,7 @@ bool wlr_egl_swap_buffers(struct wlr_egl *egl, EGLSurface surface, EGLImageKHR wlr_egl_create_image_from_wl_drm(struct wlr_egl *egl, struct wl_resource *data, EGLint *fmt, int *width, int *height, bool *inverted_y) { - if (!eglQueryWaylandBufferWL || !eglCreateImageKHR) { + if (!egl->exts.bind_wayland_display_wl || !egl->exts.image_base_khr) { return NULL; } @@ -341,9 +377,13 @@ EGLImageKHR wlr_egl_create_image_from_wl_drm(struct wlr_egl *egl, EGLImageKHR wlr_egl_create_image_from_dmabuf(struct wlr_egl *egl, struct wlr_dmabuf_attributes *attributes) { + if (!egl->exts.image_base_khr) { + return NULL; + } + bool has_modifier = false; if (attributes->modifier != DRM_FORMAT_MOD_INVALID) { - if (!egl->egl_exts.dmabuf_import_modifiers) { + if (!egl->exts.image_dmabuf_import_modifiers_ext) { return NULL; } has_modifier = true; @@ -415,8 +455,8 @@ EGLImageKHR wlr_egl_create_image_from_dmabuf(struct wlr_egl *egl, int wlr_egl_get_dmabuf_formats(struct wlr_egl *egl, int **formats) { - if (!egl->egl_exts.dmabuf_import || - !egl->egl_exts.dmabuf_import_modifiers) { + if (!egl->exts.image_dmabuf_import_ext || + !egl->exts.image_dmabuf_import_modifiers_ext) { wlr_log(L_DEBUG, "dmabuf extension not present"); return -1; } @@ -443,8 +483,8 @@ int wlr_egl_get_dmabuf_formats(struct wlr_egl *egl, int wlr_egl_get_dmabuf_modifiers(struct wlr_egl *egl, int format, uint64_t **modifiers) { - if (!egl->egl_exts.dmabuf_import || - !egl->egl_exts.dmabuf_import_modifiers) { + if (!egl->exts.image_dmabuf_import_ext || + !egl->exts.image_dmabuf_import_modifiers_ext) { wlr_log(L_DEBUG, "dmabuf extension not present"); return -1; } diff --git a/render/gles2/texture.c b/render/gles2/texture.c index 89a175c4..da98a523 100644 --- a/render/gles2/texture.c +++ b/render/gles2/texture.c @@ -210,7 +210,7 @@ struct wlr_texture *wlr_gles2_texture_from_dmabuf(struct wlr_egl *egl, return NULL; } - if (!egl->egl_exts.dmabuf_import) { + if (!egl->exts.image_dmabuf_import_ext) { wlr_log(L_ERROR, "Cannot create DMA-BUF texture: EGL extension " "unavailable"); return NULL; diff --git a/rootston/main.c b/rootston/main.c index 07a41d5d..8a0205f2 100644 --- a/rootston/main.c +++ b/rootston/main.c @@ -72,6 +72,9 @@ int main(int argc, char **argv) { } wl_display_run(server.wl_display); +#ifdef WLR_HAS_XWAYLAND + wlr_xwayland_destroy(server.desktop->xwayland); +#endif wl_display_destroy_clients(server.wl_display); wl_display_destroy(server.wl_display); return 0; diff --git a/types/meson.build b/types/meson.build index f9f5b469..87f21c55 100644 --- a/types/meson.build +++ b/types/meson.build @@ -20,6 +20,7 @@ lib_wlr_types = static_library( 'xdg_shell_v6/wlr_xdg_surface_v6.c', 'xdg_shell_v6/wlr_xdg_toplevel_v6.c', 'wlr_box.c', + 'wlr_buffer.c', 'wlr_compositor.c', 'wlr_cursor.c', 'wlr_gamma_control.c', diff --git a/types/wlr_buffer.c b/types/wlr_buffer.c new file mode 100644 index 00000000..82a359f0 --- /dev/null +++ b/types/wlr_buffer.c @@ -0,0 +1,187 @@ +#include <assert.h> +#include <stdlib.h> +#include <wlr/types/wlr_buffer.h> +#include <wlr/types/wlr_linux_dmabuf.h> +#include <wlr/render/wlr_renderer.h> +#include <wlr/util/log.h> + +bool wlr_resource_is_buffer(struct wl_resource *resource) { + return strcmp(wl_resource_get_class(resource), wl_buffer_interface.name) == 0; +} + +bool wlr_buffer_get_resource_size(struct wl_resource *resource, + struct wlr_renderer *renderer, int *width, int *height) { + assert(wlr_resource_is_buffer(resource)); + + struct wl_shm_buffer *shm_buf = wl_shm_buffer_get(resource); + if (shm_buf != NULL) { + *width = wl_shm_buffer_get_width(shm_buf); + *height = wl_shm_buffer_get_height(shm_buf); + } else if (wlr_renderer_resource_is_wl_drm_buffer(renderer, + resource)) { + wlr_renderer_wl_drm_buffer_get_size(renderer, resource, + width, height); + } else if (wlr_dmabuf_resource_is_buffer(resource)) { + struct wlr_dmabuf_buffer *dmabuf = + wlr_dmabuf_buffer_from_buffer_resource(resource); + *width = dmabuf->attributes.width; + *height = dmabuf->attributes.height; + } else { + *width = *height = 0; + return false; + } + + return true; +} + + +static void buffer_resource_handle_destroy(struct wl_listener *listener, + void *data) { + struct wlr_buffer *buffer = + wl_container_of(listener, buffer, resource_destroy); + wl_list_remove(&buffer->resource_destroy.link); + wl_list_init(&buffer->resource_destroy.link); + buffer->resource = NULL; + + if (!buffer->released) { + // The texture becomes invalid + wlr_texture_destroy(buffer->texture); + buffer->texture = NULL; + } +} + +struct wlr_buffer *wlr_buffer_create(struct wlr_renderer *renderer, + struct wl_resource *resource) { + assert(wlr_resource_is_buffer(resource)); + + struct wlr_texture *texture = NULL; + bool released = false; + + struct wl_shm_buffer *shm_buf = wl_shm_buffer_get(resource); + if (shm_buf != NULL) { + enum wl_shm_format fmt = wl_shm_buffer_get_format(shm_buf); + int32_t stride = wl_shm_buffer_get_stride(shm_buf); + int32_t width = wl_shm_buffer_get_width(shm_buf); + int32_t height = wl_shm_buffer_get_height(shm_buf); + + wl_shm_buffer_begin_access(shm_buf); + void *data = wl_shm_buffer_get_data(shm_buf); + texture = wlr_texture_from_pixels(renderer, fmt, stride, + width, height, data); + wl_shm_buffer_end_access(shm_buf); + + // We have uploaded the data, we don't need to access the wl_buffer + // anymore + wl_buffer_send_release(resource); + released = true; + } else if (wlr_renderer_resource_is_wl_drm_buffer(renderer, resource)) { + texture = wlr_texture_from_wl_drm(renderer, resource); + } else if (wlr_dmabuf_resource_is_buffer(resource)) { + struct wlr_dmabuf_buffer *dmabuf = + wlr_dmabuf_buffer_from_buffer_resource(resource); + texture = wlr_texture_from_dmabuf(renderer, &dmabuf->attributes); + } else { + wlr_log(L_ERROR, "Cannot upload texture: unknown buffer type"); + return NULL; + } + + if (texture == NULL) { + wlr_log(L_ERROR, "Failed to upload texture"); + return NULL; + } + + struct wlr_buffer *buffer = calloc(1, sizeof(struct wlr_buffer)); + if (buffer == NULL) { + return NULL; + } + buffer->resource = resource; + buffer->texture = texture; + buffer->released = released; + buffer->n_refs = 1; + + wl_resource_add_destroy_listener(resource, &buffer->resource_destroy); + buffer->resource_destroy.notify = buffer_resource_handle_destroy; + + return buffer; +} + +struct wlr_buffer *wlr_buffer_ref(struct wlr_buffer *buffer) { + buffer->n_refs++; + return buffer; +} + +void wlr_buffer_unref(struct wlr_buffer *buffer) { + if (buffer == NULL) { + return; + } + + assert(buffer->n_refs > 0); + buffer->n_refs--; + if (buffer->n_refs > 0) { + return; + } + + if (!buffer->released && buffer->resource != NULL) { + wl_buffer_send_release(buffer->resource); + } + + wl_list_remove(&buffer->resource_destroy.link); + wlr_texture_destroy(buffer->texture); + free(buffer); +} + +struct wlr_buffer *wlr_buffer_apply_damage(struct wlr_buffer *buffer, + struct wl_resource *resource, pixman_region32_t *damage) { + assert(wlr_resource_is_buffer(resource)); + + if (buffer->n_refs > 1) { + // Someone else still has a reference to the buffer + return NULL; + } + + struct wl_shm_buffer *shm_buf = wl_shm_buffer_get(resource); + if (shm_buf == NULL) { + // Uploading only damaged regions only works for wl_shm buffers + return NULL; + } + + enum wl_shm_format fmt = wl_shm_buffer_get_format(shm_buf); + int32_t stride = wl_shm_buffer_get_stride(shm_buf); + int32_t width = wl_shm_buffer_get_width(shm_buf); + int32_t height = wl_shm_buffer_get_height(shm_buf); + + int32_t texture_width, texture_height; + wlr_texture_get_size(buffer->texture, &texture_width, &texture_height); + if (width != texture_width || height != texture_height) { + return NULL; + } + + wl_shm_buffer_begin_access(shm_buf); + void *data = wl_shm_buffer_get_data(shm_buf); + + int n; + pixman_box32_t *rects = pixman_region32_rectangles(damage, &n); + for (int i = 0; i < n; ++i) { + pixman_box32_t *r = &rects[i]; + if (!wlr_texture_write_pixels(buffer->texture, fmt, stride, + r->x2 - r->x1, r->y2 - r->y1, r->x1, r->y1, + r->x1, r->y1, data)) { + wl_shm_buffer_end_access(shm_buf); + return NULL; + } + } + + wl_shm_buffer_end_access(shm_buf); + + // We have uploaded the data, we don't need to access the wl_buffer + // anymore + wl_buffer_send_release(resource); + + wl_list_remove(&buffer->resource_destroy.link); + wl_resource_add_destroy_listener(resource, &buffer->resource_destroy); + buffer->resource_destroy.notify = buffer_resource_handle_destroy; + + buffer->resource = resource; + buffer->released = true; + return buffer; +} diff --git a/types/wlr_surface.c b/types/wlr_surface.c index 9c3eb86d..7d8da02f 100644 --- a/types/wlr_surface.c +++ b/types/wlr_surface.c @@ -1,10 +1,9 @@ #include <assert.h> #include <stdlib.h> #include <wayland-server.h> -#include <wlr/render/egl.h> #include <wlr/render/interface.h> +#include <wlr/types/wlr_buffer.h> #include <wlr/types/wlr_compositor.h> -#include <wlr/types/wlr_linux_dmabuf.h> #include <wlr/types/wlr_matrix.h> #include <wlr/types/wlr_region.h> #include <wlr/types/wlr_surface.h> @@ -46,13 +45,6 @@ static void surface_handle_buffer_destroy(struct wl_listener *listener, surface_state_reset_buffer(state); } -static void surface_state_release_buffer(struct wlr_surface_state *state) { - if (state->buffer) { - wl_buffer_send_release(state->buffer); - surface_state_reset_buffer(state); - } -} - static void surface_state_set_buffer(struct wlr_surface_state *state, struct wl_resource *buffer) { surface_state_reset_buffer(state); @@ -175,24 +167,8 @@ static bool surface_update_size(struct wlr_surface *surface, int scale = state->scale; enum wl_output_transform transform = state->transform; - struct wl_shm_buffer *buf = wl_shm_buffer_get(state->buffer); - if (buf != NULL) { - state->buffer_width = wl_shm_buffer_get_width(buf); - state->buffer_height = wl_shm_buffer_get_height(buf); - } else if (wlr_renderer_resource_is_wl_drm_buffer(surface->renderer, - state->buffer)) { - wlr_renderer_wl_drm_buffer_get_size(surface->renderer, state->buffer, - &state->buffer_width, &state->buffer_height); - } else if (wlr_dmabuf_resource_is_buffer(state->buffer)) { - struct wlr_dmabuf_buffer *dmabuf = - wlr_dmabuf_buffer_from_buffer_resource(state->buffer); - state->buffer_width = dmabuf->attributes.width; - state->buffer_height = dmabuf->attributes.height; - } else { - wlr_log(L_ERROR, "Unknown buffer handle attached"); - state->buffer_width = 0; - state->buffer_height = 0; - } + wlr_buffer_get_resource_size(state->buffer, surface->renderer, + &state->buffer_width, &state->buffer_height); int width = state->buffer_width / scale; int height = state->buffer_height / scale; @@ -243,7 +219,6 @@ static void surface_move_state(struct wlr_surface *surface, update_size = true; } if ((next->invalid & WLR_SURFACE_INVALID_BUFFER)) { - surface_state_release_buffer(state); surface_state_set_buffer(state, next->buffer); surface_state_reset_buffer(next); state->sx = next->sx; @@ -351,86 +326,60 @@ static void surface_damage_subsurfaces(struct wlr_subsurface *subsurface) { } static void surface_apply_damage(struct wlr_surface *surface, - bool invalid_buffer, bool reupload_buffer) { + bool invalid_buffer) { struct wl_resource *resource = surface->current->buffer; if (resource == NULL) { + // NULL commit + wlr_buffer_unref(surface->buffer); + surface->buffer = NULL; + surface->texture = NULL; return; } - struct wl_shm_buffer *buf = wl_shm_buffer_get(resource); - if (buf != NULL) { - wl_shm_buffer_begin_access(buf); + if (surface->buffer != NULL && !surface->buffer->released && + !invalid_buffer) { + // The buffer is still the same, no need to re-upload + return; + } - enum wl_shm_format fmt = wl_shm_buffer_get_format(buf); - int32_t stride = wl_shm_buffer_get_stride(buf); - int32_t width = wl_shm_buffer_get_width(buf); - int32_t height = wl_shm_buffer_get_height(buf); - void *data = wl_shm_buffer_get_data(buf); + if (surface->buffer != NULL && surface->buffer->released) { + pixman_region32_t damage; + pixman_region32_init(&damage); + pixman_region32_copy(&damage, &surface->current->buffer_damage); + pixman_region32_intersect_rect(&damage, &damage, 0, 0, + surface->current->buffer_width, surface->current->buffer_height); - if (surface->texture == NULL || reupload_buffer) { - wlr_texture_destroy(surface->texture); - surface->texture = wlr_texture_from_pixels(surface->renderer, fmt, - stride, width, height, data); - } else { - pixman_region32_t damage; - pixman_region32_init(&damage); - pixman_region32_copy(&damage, &surface->current->buffer_damage); - pixman_region32_intersect_rect(&damage, &damage, 0, 0, - surface->current->buffer_width, - surface->current->buffer_height); - - int n; - pixman_box32_t *rects = pixman_region32_rectangles(&damage, &n); - for (int i = 0; i < n; ++i) { - pixman_box32_t *r = &rects[i]; - if (!wlr_texture_write_pixels(surface->texture, fmt, stride, - r->x2 - r->x1, r->y2 - r->y1, r->x1, r->y1, - r->x1, r->y1, data)) { - break; - } - } - - pixman_region32_fini(&damage); - } + struct wlr_buffer *updated_buffer = + wlr_buffer_apply_damage(surface->buffer, resource, &damage); - wl_shm_buffer_end_access(buf); - } else if (invalid_buffer || reupload_buffer) { - wlr_texture_destroy(surface->texture); - - if (wlr_renderer_resource_is_wl_drm_buffer(surface->renderer, resource)) { - surface->texture = - wlr_texture_from_wl_drm(surface->renderer, resource); - } else if (wlr_dmabuf_resource_is_buffer(resource)) { - struct wlr_dmabuf_buffer *dmabuf = - wlr_dmabuf_buffer_from_buffer_resource(resource); - surface->texture = - wlr_texture_from_dmabuf(surface->renderer, &dmabuf->attributes); - } else { - surface->texture = NULL; - wlr_log(L_ERROR, "Unknown buffer handle attached"); + pixman_region32_fini(&damage); + + if (updated_buffer != NULL) { + surface->buffer = updated_buffer; + return; } } - surface_state_release_buffer(surface->current); + wlr_buffer_unref(surface->buffer); + surface->buffer = NULL; + surface->texture = NULL; + + struct wlr_buffer *buffer = wlr_buffer_create(surface->renderer, resource); + if (buffer == NULL) { + wlr_log(L_ERROR, "Failed to upload buffer"); + return; + } + + surface->buffer = buffer; + surface->texture = buffer->texture; } static void surface_commit_pending(struct wlr_surface *surface) { - int32_t oldw = surface->current->buffer_width; - int32_t oldh = surface->current->buffer_height; - bool invalid_buffer = surface->pending->invalid & WLR_SURFACE_INVALID_BUFFER; - bool null_buffer_commit = invalid_buffer && surface->pending->buffer == NULL; surface_move_state(surface, surface->pending, surface->current); - if (null_buffer_commit) { - wlr_texture_destroy(surface->texture); - surface->texture = NULL; - } - - bool reupload_buffer = oldw != surface->current->buffer_width || - oldh != surface->current->buffer_height; - surface_apply_damage(surface, invalid_buffer, reupload_buffer); + surface_apply_damage(surface, invalid_buffer); // commit subsurface order struct wlr_subsurface *subsurface; @@ -652,10 +601,9 @@ static void surface_handle_resource_destroy(struct wl_resource *resource) { wl_list_remove(wl_resource_get_link(surface->resource)); wl_list_remove(&surface->renderer_destroy.link); - wlr_texture_destroy(surface->texture); surface_state_destroy(surface->pending); surface_state_destroy(surface->current); - + wlr_buffer_unref(surface->buffer); free(surface); } |