diff options
author | Erik Reider <[email protected]> | 2024-01-06 02:31:14 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2024-01-06 02:31:14 +0100 |
commit | 6759e8da7ab53a46b0eb04e5045b8c67262c3a11 (patch) | |
tree | c65ed83ca04b61bdbae7e1b8a7f2c16f29b89730 /types/scene/wlr_scene.c | |
parent | b929a2bbadf467864796ad4ec90882ce86cfebff (diff) | |
parent | ace97585b2b4d8cbb5ead6cd0f72fa8e8889c9d7 (diff) |
Merge pull request #24 from wlrfx/wlroots-0.17-rebase
Rebase to wlroots 0.17
Diffstat (limited to 'types/scene/wlr_scene.c')
-rw-r--r-- | types/scene/wlr_scene.c | 934 |
1 files changed, 585 insertions, 349 deletions
diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 4a32a6b..8ded6a8 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -4,18 +4,21 @@ #include <string.h> #include <wlr/backend.h> #include <wlr/render/gles2.h> -#include <wlr/types/wlr_xdg_shell.h> #include <wlr/types/wlr_compositor.h> #include <wlr/types/wlr_damage_ring.h> #include <wlr/types/wlr_matrix.h> #include <wlr/types/wlr_presentation_time.h> #include <wlr/types/wlr_scene.h> #include <wlr/types/wlr_subcompositor.h> +#include <wlr/types/wlr_xdg_shell.h> #include <wlr/util/log.h> #include <wlr/util/region.h> -#include "render/fx_renderer/fx_renderer.h" +#include <wlr/render/swapchain.h> + +#include "render/pass.h" #include "types/fx/shadow_data.h" #include "types/wlr_buffer.h" +#include "types/wlr_output.h" #include "types/wlr_scene.h" #include "util/array.h" #include "util/env.h" @@ -23,14 +26,13 @@ #define HIGHLIGHT_DAMAGE_FADEOUT_TIME 250 -static struct wlr_scene_tree *scene_tree_from_node(struct wlr_scene_node *node) { +struct wlr_scene_tree *wlr_scene_tree_from_node(struct wlr_scene_node *node) { assert(node->type == WLR_SCENE_NODE_TREE); struct wlr_scene_tree *tree = wl_container_of(node, tree, node); return tree; } -static struct wlr_scene_rect *scene_rect_from_node( - struct wlr_scene_node *node) { +struct wlr_scene_rect *wlr_scene_rect_from_node(struct wlr_scene_node *node) { assert(node->type == WLR_SCENE_NODE_RECT); struct wlr_scene_rect *rect = wl_container_of(node, rect, node); return rect; @@ -46,7 +48,7 @@ struct wlr_scene_buffer *wlr_scene_buffer_from_node( struct wlr_scene *scene_node_get_root(struct wlr_scene_node *node) { struct wlr_scene_tree *tree; if (node->type == WLR_SCENE_NODE_TREE) { - tree = scene_tree_from_node(node); + tree = wlr_scene_tree_from_node(node); } else { tree = node->parent; } @@ -54,15 +56,17 @@ struct wlr_scene *scene_node_get_root(struct wlr_scene_node *node) { while (tree->node.parent != NULL) { tree = tree->node.parent; } - return (struct wlr_scene *)tree; + struct wlr_scene *scene = wl_container_of(tree, scene, tree); + return scene; } static void scene_node_init(struct wlr_scene_node *node, enum wlr_scene_node_type type, struct wlr_scene_tree *parent) { - memset(node, 0, sizeof(*node)); - node->type = type; - node->parent = parent; - node->enabled = true; + *node = (struct wlr_scene_node){ + .type = type, + .parent = parent, + .enabled = true, + }; wl_list_init(&node->link); @@ -114,7 +118,7 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) { wlr_buffer_unlock(scene_buffer->buffer); pixman_region32_fini(&scene_buffer->opaque_region); } else if (node->type == WLR_SCENE_NODE_TREE) { - struct wlr_scene_tree *scene_tree = scene_tree_from_node(node); + struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); if (scene_tree == &scene->tree) { assert(!node->parent); @@ -124,6 +128,7 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) { } wl_list_remove(&scene->presentation_destroy.link); + wl_list_remove(&scene->linux_dmabuf_v1_destroy.link); } else { assert(node->parent); } @@ -142,13 +147,13 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) { static void scene_tree_init(struct wlr_scene_tree *tree, struct wlr_scene_tree *parent) { - memset(tree, 0, sizeof(*tree)); + *tree = (struct wlr_scene_tree){0}; scene_node_init(&tree->node, WLR_SCENE_NODE_TREE, parent); wl_list_init(&tree->children); } struct wlr_scene *wlr_scene_create(void) { - struct wlr_scene *scene = calloc(1, sizeof(struct wlr_scene)); + struct wlr_scene *scene = calloc(1, sizeof(*scene)); if (scene == NULL) { return NULL; } @@ -157,6 +162,7 @@ struct wlr_scene *wlr_scene_create(void) { wl_list_init(&scene->outputs); wl_list_init(&scene->presentation_destroy.link); + wl_list_init(&scene->linux_dmabuf_v1_destroy.link); const char *debug_damage_options[] = { "none", @@ -175,7 +181,7 @@ struct wlr_scene *wlr_scene_create(void) { struct wlr_scene_tree *wlr_scene_tree_create(struct wlr_scene_tree *parent) { assert(parent); - struct wlr_scene_tree *tree = calloc(1, sizeof(struct wlr_scene_tree)); + struct wlr_scene_tree *tree = calloc(1, sizeof(*tree)); if (tree == NULL) { return NULL; } @@ -197,7 +203,7 @@ static bool _scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box switch (node->type) { case WLR_SCENE_NODE_TREE:; - struct wlr_scene_tree *scene_tree = scene_tree_from_node(node); + struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); struct wlr_scene_node *child; wl_list_for_each_reverse(child, &scene_tree->children, link) { if (_scene_nodes_in_box(child, box, iterator, user_data, lx + child->x, ly + child->y)) { @@ -230,8 +236,11 @@ static bool scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box, static void scene_node_opaque_region(struct wlr_scene_node *node, int x, int y, pixman_region32_t *opaque) { + int width, height; + scene_node_get_size(node, &width, &height); + if (node->type == WLR_SCENE_NODE_RECT) { - struct wlr_scene_rect *scene_rect = scene_rect_from_node(node); + struct wlr_scene_rect *scene_rect = wlr_scene_rect_from_node(node); if (scene_rect->color[3] != 1) { return; } @@ -242,20 +251,22 @@ static void scene_node_opaque_region(struct wlr_scene_node *node, int x, int y, return; } - // Buffer is translucent - if (scene_buffer->opacity != 1 || scene_buffer->corner_radius > 0) { + if (scene_buffer->opacity != 1) { + return; + } + + if (scene_buffer->corner_radius > 0) { return; } if (!buffer_is_opaque(scene_buffer->buffer)) { pixman_region32_copy(opaque, &scene_buffer->opaque_region); + pixman_region32_intersect_rect(opaque, opaque, 0, 0, width, height); pixman_region32_translate(opaque, x, y); return; } } - int width, height; - scene_node_get_size(node, &width, &height); pixman_region32_fini(opaque); pixman_region32_init_rect(opaque, x, y, width, height); } @@ -287,6 +298,28 @@ static void scale_output_damage(pixman_region32_t *damage, float scale) { } } +struct render_data { + enum wl_output_transform transform; + float scale; + struct wlr_box logical; + int trans_width, trans_height; + + struct wlr_scene_output *output; + + struct fx_gles_render_pass *render_pass; + pixman_region32_t damage; +}; + +static void transform_output_damage(pixman_region32_t *damage, const struct render_data *data) { + enum wl_output_transform transform = wlr_output_transform_invert(data->transform); + wlr_region_transform(damage, damage, transform, data->trans_width, data->trans_height); +} + +static void transform_output_box(struct wlr_box *box, const struct render_data *data) { + enum wl_output_transform transform = wlr_output_transform_invert(data->transform); + wlr_box_transform(box, box, transform, data->trans_width, data->trans_height); +} + static void scene_damage_outputs(struct wlr_scene *scene, pixman_region32_t *damage) { if (!pixman_region32_not_empty(damage)) { return; @@ -308,7 +341,8 @@ static void scene_damage_outputs(struct wlr_scene *scene, pixman_region32_t *dam } static void update_node_update_outputs(struct wlr_scene_node *node, - struct wl_list *outputs, struct wlr_scene_output *ignore) { + struct wl_list *outputs, struct wlr_scene_output *ignore, + struct wlr_scene_output *force) { if (node->type != WLR_SCENE_NODE_BUFFER) { return; } @@ -316,8 +350,10 @@ static void update_node_update_outputs(struct wlr_scene_node *node, struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); uint32_t largest_overlap = 0; + struct wlr_scene_output *old_primary_output = scene_buffer->primary_output; scene_buffer->primary_output = NULL; + size_t count = 0; uint64_t active_outputs = 0; // let's update the outputs in two steps: @@ -356,11 +392,17 @@ static void update_node_update_outputs(struct wlr_scene_node *node, } active_outputs |= 1ull << scene_output->index; + count++; } pixman_region32_fini(&intersection); } + if (old_primary_output != scene_buffer->primary_output) { + scene_buffer->prev_feedback_options = + (struct wlr_linux_dmabuf_feedback_v1_init_options){0}; + } + uint64_t old_active = scene_buffer->active_outputs; scene_buffer->active_outputs = active_outputs; @@ -379,6 +421,31 @@ static void update_node_update_outputs(struct wlr_scene_node *node, // if there are active outputs on this node, we should always have a primary // output assert(!scene_buffer->active_outputs || scene_buffer->primary_output); + + // Skip output update event if nothing was updated + if (old_active == active_outputs && + (!force || ((1ull << force->index) & ~active_outputs)) && + old_primary_output == scene_buffer->primary_output) { + return; + } + + struct wlr_scene_output *outputs_array[64]; + struct wlr_scene_outputs_update_event event = { + .active = outputs_array, + .size = count, + }; + + size_t i = 0; + wl_list_for_each(scene_output, outputs, link) { + if (~active_outputs & (1ull << scene_output->index)) { + continue; + } + + assert(i < count); + outputs_array[i++] = scene_output; + } + + wl_signal_emit_mutable(&scene_buffer->events.outputs_update, &event); } static bool scene_node_update_iterator(struct wlr_scene_node *node, @@ -404,13 +471,14 @@ static bool scene_node_update_iterator(struct wlr_scene_node *node, // Expand the nodes visible region by the shadow size if (node->type == WLR_SCENE_NODE_BUFFER) { struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); - struct shadow_data *data = &buffer->shadow_data; - if (scene_buffer_has_shadow(data)) { - wlr_region_expand(&node->visible, &node->visible, data->blur_sigma); + struct shadow_data *shadow_data = &buffer->shadow_data; + if (scene_buffer_has_shadow(shadow_data)) { + wlr_region_expand(&node->visible, &node->visible, shadow_data->blur_sigma); + wlr_region_expand(data->visible, data->visible, shadow_data->blur_sigma); } } - update_node_update_outputs(node, data->outputs, NULL); + update_node_update_outputs(node, data->outputs, NULL, NULL); return false; } @@ -422,7 +490,7 @@ static void scene_node_visibility(struct wlr_scene_node *node, } if (node->type == WLR_SCENE_NODE_TREE) { - struct wlr_scene_tree *scene_tree = scene_tree_from_node(node); + struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); struct wlr_scene_node *child; wl_list_for_each(child, &scene_tree->children, link) { scene_node_visibility(child, visible); @@ -440,7 +508,7 @@ static void scene_node_bounds(struct wlr_scene_node *node, } if (node->type == WLR_SCENE_NODE_TREE) { - struct wlr_scene_tree *scene_tree = scene_tree_from_node(node); + struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); struct wlr_scene_node *child; wl_list_for_each(child, &scene_tree->children, link) { scene_node_bounds(child, x + child->x, y + child->y, visible); @@ -517,8 +585,7 @@ static void scene_node_update(struct wlr_scene_node *node, struct wlr_scene_rect *wlr_scene_rect_create(struct wlr_scene_tree *parent, int width, int height, const float color[static 4]) { - struct wlr_scene_rect *scene_rect = - calloc(1, sizeof(struct wlr_scene_rect)); + struct wlr_scene_rect *scene_rect = calloc(1, sizeof(*scene_rect)); if (scene_rect == NULL) { return NULL; } @@ -566,12 +633,12 @@ struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent, scene_buffer->buffer = wlr_buffer_lock(buffer); } + wl_signal_init(&scene_buffer->events.outputs_update); wl_signal_init(&scene_buffer->events.output_enter); wl_signal_init(&scene_buffer->events.output_leave); - wl_signal_init(&scene_buffer->events.output_present); + wl_signal_init(&scene_buffer->events.output_sample); wl_signal_init(&scene_buffer->events.frame_done); pixman_region32_init(&scene_buffer->opaque_region); - scene_buffer->opacity = 1; scene_buffer->corner_radius = 0; scene_buffer->shadow_data = shadow_data_get_default(); @@ -582,14 +649,13 @@ struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent, } void wlr_scene_buffer_set_buffer_with_damage(struct wlr_scene_buffer *scene_buffer, - struct wlr_buffer *buffer, pixman_region32_t *damage) { + struct wlr_buffer *buffer, const pixman_region32_t *damage) { // specifying a region for a NULL buffer doesn't make sense. We need to know // about the buffer to scale the buffer local coordinates down to scene // coordinates. assert(buffer || !damage); bool update = false; - wlr_buffer_unlock(scene_buffer->buffer); wlr_texture_destroy(scene_buffer->texture); scene_buffer->texture = NULL; @@ -603,8 +669,10 @@ void wlr_scene_buffer_set_buffer_with_damage(struct wlr_scene_buffer *scene_buff (scene_buffer->buffer->width != buffer->width || scene_buffer->buffer->height != buffer->height)); + wlr_buffer_unlock(scene_buffer->buffer); scene_buffer->buffer = wlr_buffer_lock(buffer); } else { + wlr_buffer_unlock(scene_buffer->buffer); update = true; scene_buffer->buffer = NULL; } @@ -694,8 +762,8 @@ void wlr_scene_buffer_set_buffer_with_damage(struct wlr_scene_buffer *scene_buff pixman_region32_fini(&cull_region); pixman_region32_translate(&output_damage, - (lx - scene_output->x) * output_scale, - (ly - scene_output->y) * output_scale); + (int)round((lx - scene_output->x) * output_scale), + (int)round((ly - scene_output->y) * output_scale)); if (wlr_damage_ring_add(&scene_output->damage_ring, &output_damage)) { wlr_output_schedule_frame(scene_output->output); } @@ -712,27 +780,35 @@ void wlr_scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer, } void wlr_scene_buffer_set_opaque_region(struct wlr_scene_buffer *scene_buffer, - pixman_region32_t *region) { + const pixman_region32_t *region) { if (pixman_region32_equal(&scene_buffer->opaque_region, region)) { return; } pixman_region32_copy(&scene_buffer->opaque_region, region); - scene_node_update(&scene_buffer->node, NULL); + + int x, y; + if (!wlr_scene_node_coords(&scene_buffer->node, &x, &y)) { + return; + } + + pixman_region32_t update_region; + pixman_region32_init(&update_region); + scene_node_bounds(&scene_buffer->node, x, y, &update_region); + scene_update_region(scene_node_get_root(&scene_buffer->node), &update_region); + pixman_region32_fini(&update_region); } void wlr_scene_buffer_set_source_box(struct wlr_scene_buffer *scene_buffer, const struct wlr_fbox *box) { - struct wlr_fbox *cur = &scene_buffer->src_box; - if ((wlr_fbox_empty(box) && wlr_fbox_empty(cur)) || - (box != NULL && wlr_fbox_equal(cur, box))) { + if (wlr_fbox_equal(&scene_buffer->src_box, box)) { return; } if (box != NULL) { - memcpy(cur, box, sizeof(*box)); + scene_buffer->src_box = *box; } else { - memset(cur, 0, sizeof(*cur)); + scene_buffer->src_box = (struct wlr_fbox){0}; } scene_node_update(&scene_buffer->node, NULL); @@ -776,6 +852,16 @@ void wlr_scene_buffer_set_opacity(struct wlr_scene_buffer *scene_buffer, scene_node_update(&scene_buffer->node, NULL); } +void wlr_scene_buffer_set_filter_mode(struct wlr_scene_buffer *scene_buffer, + enum wlr_scale_filter_mode filter_mode) { + if (scene_buffer->filter_mode == filter_mode) { + return; + } + + scene_buffer->filter_mode = filter_mode; + scene_node_update(&scene_buffer->node, NULL); +} + void wlr_scene_buffer_set_corner_radius(struct wlr_scene_buffer *scene_buffer, int radii) { if (scene_buffer->corner_radius == radii) { @@ -791,7 +877,10 @@ void wlr_scene_buffer_set_shadow_data(struct wlr_scene_buffer *scene_buffer, struct shadow_data *buff_data = &scene_buffer->shadow_data; if (buff_data->enabled == shadow_data.enabled && buff_data->blur_sigma == shadow_data.blur_sigma && - buff_data->color && shadow_data.color) { + buff_data->color.r && shadow_data.color.r && + buff_data->color.g && shadow_data.color.g && + buff_data->color.b && shadow_data.color.b && + buff_data->color.a && shadow_data.color.a) { return; } @@ -826,7 +915,7 @@ static void scene_node_get_size(struct wlr_scene_node *node, case WLR_SCENE_NODE_TREE: return; case WLR_SCENE_NODE_RECT:; - struct wlr_scene_rect *scene_rect = scene_rect_from_node(node); + struct wlr_scene_rect *scene_rect = wlr_scene_rect_from_node(node); *width = scene_rect->width; *height = scene_rect->height; break; @@ -995,7 +1084,7 @@ static void scene_node_for_each_scene_buffer(struct wlr_scene_node *node, struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); user_iterator(scene_buffer, lx, ly, user_data); } else if (node->type == WLR_SCENE_NODE_TREE) { - struct wlr_scene_tree *scene_tree = scene_tree_from_node(node); + struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); struct wlr_scene_node *child; wl_list_for_each(child, &scene_tree->children, link) { scene_node_for_each_scene_buffer(child, lx, ly, user_iterator, user_data); @@ -1025,7 +1114,7 @@ static bool scene_node_at_iterator(struct wlr_scene_node *node, struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); if (scene_buffer->point_accepts_input && - !scene_buffer->point_accepts_input(scene_buffer, rx, ry)) { + !scene_buffer->point_accepts_input(scene_buffer, &rx, &ry)) { return false; } } @@ -1063,208 +1152,175 @@ struct wlr_scene_node *wlr_scene_node_at(struct wlr_scene_node *node, return NULL; } -static void scissor_output(struct wlr_output *output, pixman_box32_t *rect) { - struct wlr_box box = { - .x = rect->x1, - .y = rect->y1, - .width = rect->x2 - rect->x1, - .height = rect->y2 - rect->y1, - }; - - int ow, oh; - wlr_output_transformed_resolution(output, &ow, &oh); - - enum wl_output_transform transform = - wlr_output_transform_invert(output->transform); - wlr_box_transform(&box, &box, transform, ow, oh); - - fx_renderer_scissor(&box); -} - -static void render_rect(struct fx_renderer *fx_renderer, struct wlr_output *output, - pixman_region32_t *damage, const float color[static 4], - const struct wlr_box *box, const float matrix[static 9]) { - assert(fx_renderer); - - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(output, &rects[i]); - fx_render_rect(fx_renderer, box, color, matrix); - } -} - -static void render_texture(struct fx_renderer *fx_renderer, struct wlr_output *output, - pixman_region32_t *damage, struct wlr_texture *texture, - const struct wlr_fbox *src_box, const struct wlr_box *dst_box, - const float matrix[static 9], float opacity, int corner_radius) { - assert(fx_renderer); - - struct wlr_fbox default_src_box = {0}; - if (wlr_fbox_empty(src_box)) { - default_src_box.width = texture->width; - default_src_box.height = texture->height; - src_box = &default_src_box; - } - - // ensure the box is updated as per the output orientation - struct wlr_box transformed_box; - int width, height; - wlr_output_transformed_resolution(output, &width, &height); - wlr_box_transform(&transformed_box, dst_box, - wlr_output_transform_invert(output->transform), width, height); - - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(output, &rects[i]); - - fx_render_subtexture_with_matrix(fx_renderer, texture, src_box, - &transformed_box, matrix, opacity, corner_radius); +// Some surfaces (mostly GTK 4) decorate their windows with shadows +// which extends the node size past the actual window size. This gets +// the actual surface geometry, mostly ignoring CSD decorations +// but only if we need to. +static void clip_xdg(struct wlr_scene_node *node, + pixman_region32_t *clip, struct wlr_box *dst_box, + int x, int y, float scale) { + struct wlr_scene_buffer *scene_buffer = NULL; + switch (node->type) { + default: + return; + case WLR_SCENE_NODE_BUFFER: + scene_buffer = wlr_scene_buffer_from_node(node); + break; } -} - -static void render_box_shadow(struct fx_renderer *fx_renderer, - struct wlr_output *output, pixman_region32_t *surface_damage, - const struct wlr_box *surface_box, int corner_radius, - struct shadow_data *shadow_data) { - // don't damage area behind window since we dont render it anyway - pixman_region32_t inner_region; - pixman_region32_init(&inner_region); - pixman_region32_union_rect(&inner_region, &inner_region, - surface_box->x + corner_radius * 0.5, - surface_box->y + corner_radius * 0.5, - surface_box->width - corner_radius, - surface_box->height - corner_radius); - pixman_region32_intersect(&inner_region, &inner_region, surface_damage); - pixman_region32_t damage; - pixman_region32_init(&damage); - pixman_region32_subtract(&damage, surface_damage, &inner_region); - if (!pixman_region32_not_empty(&damage)) { - goto damage_finish; + if (!scene_buffer || (scene_buffer->corner_radius == 0 && + !scene_buffer_has_shadow(&scene_buffer->shadow_data))) { + return; } - struct wlr_box shadow_box = { - .x = surface_box->x - shadow_data->blur_sigma, - .y = surface_box->y - shadow_data->blur_sigma, - .width = surface_box->width + 2 * shadow_data->blur_sigma, - .height = surface_box->height + 2 * shadow_data->blur_sigma, - }; - float matrix[9]; - wlr_matrix_project_box(matrix, &shadow_box, WL_OUTPUT_TRANSFORM_NORMAL, 0, - output->transform_matrix); + struct wlr_scene_surface *scene_surface = wlr_scene_surface_try_from_buffer(scene_buffer); + struct wlr_xdg_surface *xdg_surface = NULL; + if (scene_surface && + (xdg_surface = wlr_xdg_surface_try_from_wlr_surface(scene_surface->surface))) { + struct wlr_box geometry; + wlr_xdg_surface_get_geometry(xdg_surface, &geometry); + scale_box(&geometry, scale); - // ensure the box is updated as per the output orientation - struct wlr_box transformed_box; - int width, height; - wlr_output_transformed_resolution(output, &width, &height); - wlr_box_transform(&transformed_box, &shadow_box, - wlr_output_transform_invert(output->transform), width, height); + if (dst_box->width > geometry.width) { + dst_box->width = geometry.width; + dst_box->x = geometry.x + x; + } + if (dst_box->height > geometry.height) { + dst_box->height = geometry.height; + dst_box->y = geometry.y + y; + } - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(output, &rects[i]); - fx_render_box_shadow(fx_renderer, &transformed_box, surface_box, matrix, - corner_radius, shadow_data); + pixman_region32_intersect_rect(clip, clip, + dst_box->x, dst_box->y, + dst_box->width, dst_box->height); } - -damage_finish: - pixman_region32_fini(&damage); - pixman_region32_fini(&inner_region); } -static void scene_node_render(struct fx_renderer *fx_renderer, struct wlr_scene_node *node, - struct wlr_scene_output *scene_output, pixman_region32_t *damage) { +struct render_list_entry { + struct wlr_scene_node *node; + bool sent_dmabuf_feedback; int x, y; - wlr_scene_node_coords(node, &x, &y); - x -= scene_output->x; - y -= scene_output->y; +}; - struct wlr_output *output = scene_output->output; +static void scene_entry_render(struct render_list_entry *entry, const struct render_data *data) { + struct wlr_scene_node *node = entry->node; pixman_region32_t render_region; pixman_region32_init(&render_region); pixman_region32_copy(&render_region, &node->visible); - pixman_region32_translate(&render_region, -scene_output->x, -scene_output->y); - scale_output_damage(&render_region, output->scale); - pixman_region32_intersect(&render_region, &render_region, damage); + pixman_region32_translate(&render_region, -data->logical.x, -data->logical.y); + scale_output_damage(&render_region, data->scale); + pixman_region32_intersect(&render_region, &render_region, &data->damage); if (!pixman_region32_not_empty(&render_region)) { pixman_region32_fini(&render_region); return; } struct wlr_box dst_box = { - .x = x, - .y = y, + .x = entry->x - data->logical.x, + .y = entry->y - data->logical.y, }; scene_node_get_size(node, &dst_box.width, &dst_box.height); - scale_box(&dst_box, output->scale); + scale_box(&dst_box, data->scale); + + pixman_region32_t opaque; + pixman_region32_init(&opaque); + scene_node_opaque_region(node, dst_box.x, dst_box.y, &opaque); + scale_output_damage(&opaque, data->scale); + pixman_region32_subtract(&opaque, &render_region, &opaque); + + struct wlr_box xdg_box = dst_box; + // Tries to clip + clip_xdg(node, &render_region, &xdg_box, dst_box.x, dst_box.y, data->scale); + + transform_output_box(&dst_box, data); + transform_output_box(&xdg_box, data); + transform_output_damage(&render_region, data); - struct wlr_texture *texture; - float matrix[9]; - enum wl_output_transform transform; switch (node->type) { case WLR_SCENE_NODE_TREE: assert(false); break; case WLR_SCENE_NODE_RECT:; - struct wlr_scene_rect *scene_rect = scene_rect_from_node(node); - - render_rect(fx_renderer, output, &render_region, scene_rect->color, &dst_box, - output->transform_matrix); + struct wlr_scene_rect *scene_rect = wlr_scene_rect_from_node(node); + + struct fx_render_rect_options rect_options = { + .base = { + .box = dst_box, + .color = { + .r = scene_rect->color[0], + .g = scene_rect->color[1], + .b = scene_rect->color[2], + .a = scene_rect->color[3], + }, + .clip = &render_region, + }, + .scale = data->scale, + }; + fx_render_pass_add_rect(data->render_pass, &rect_options); break; case WLR_SCENE_NODE_BUFFER:; struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); assert(scene_buffer->buffer); - struct wlr_renderer *renderer = output->renderer; - texture = scene_buffer_get_texture(scene_buffer, renderer); - - transform = wlr_output_transform_invert(scene_buffer->transform); - wlr_matrix_project_box(matrix, &dst_box, transform, 0.0, - output->transform_matrix); - - // Some surfaces (mostly GTK 4) decorate their windows with shadows - // which extends the node size past the actual window size. This gets - // the actual surface geometry, mostly ignoring CSD decorations - // but only if we need to. - if (scene_buffer->corner_radius != 0 || - scene_buffer_has_shadow(&scene_buffer->shadow_data)) { - struct wlr_scene_surface *scene_surface = NULL; - if ((scene_surface = wlr_scene_surface_from_buffer(scene_buffer)) && - wlr_surface_is_xdg_surface(scene_surface->surface)) { - struct wlr_xdg_surface *xdg_surface = - wlr_xdg_surface_from_wlr_surface(scene_surface->surface); - - struct wlr_box geometry; - wlr_xdg_surface_get_geometry(xdg_surface, &geometry); - dst_box.width = fmin(dst_box.width, geometry.width); - dst_box.height = fmin(dst_box.height, geometry.height); - dst_box.x = fmax(dst_box.x, geometry.x + x); - dst_box.y = fmax(dst_box.y, geometry.y + y); - } + struct wlr_texture *texture = scene_buffer_get_texture(scene_buffer, + data->output->output->renderer); + if (texture == NULL) { + break; } + enum wl_output_transform transform = + wlr_output_transform_invert(scene_buffer->transform); + transform = wlr_output_transform_compose(transform, data->transform); + // Shadow if (scene_buffer_has_shadow(&scene_buffer->shadow_data)) { // TODO: Compensate for SSD borders here - render_box_shadow(fx_renderer, output, &render_region, &dst_box, - scene_buffer->corner_radius, &scene_buffer->shadow_data); + pixman_region32_t shadow_clip; + pixman_region32_init(&shadow_clip); + // Extend the size of the clip box + wlr_region_expand(&shadow_clip, &render_region, + scene_buffer->shadow_data.blur_sigma * data->scale); + struct fx_render_rect_options shadow_options = { + .base = { + .box = xdg_box, + .clip = &shadow_clip, + }, + .scale = data->scale, + }; + fx_render_pass_add_box_shadow(data->render_pass, &shadow_options, + scene_buffer->corner_radius * data->scale, + &scene_buffer->shadow_data); + pixman_region32_fini(&shadow_clip); } - // Clip the damage to the dst_box before rendering the texture - pixman_region32_intersect_rect(&render_region, &render_region, - dst_box.x, dst_box.y, dst_box.width, dst_box.height); - - render_texture(fx_renderer, output, &render_region, texture, &scene_buffer->src_box, - &dst_box, matrix, scene_buffer->opacity, scene_buffer->corner_radius); + struct fx_render_texture_options tex_options = { + .base = (struct wlr_render_texture_options){ + .texture = texture, + .src_box = scene_buffer->src_box, + .dst_box = dst_box, + .transform = transform, + .clip = &render_region, + .alpha = &scene_buffer->opacity, + .filter_mode = scene_buffer->filter_mode, + .blend_mode = pixman_region32_not_empty(&opaque) ? + WLR_RENDER_BLEND_MODE_PREMULTIPLIED : WLR_RENDER_BLEND_MODE_NONE, + }, + .scale = data->scale, + .clip_box = &xdg_box, + .corner_radius = scene_buffer->corner_radius * data->scale, + }; + fx_render_pass_add_texture(data->render_pass, &tex_options); - wl_signal_emit_mutable(&scene_buffer->events.output_present, scene_output); + struct wlr_scene_output_sample_event sample_event = { + .output = data->output, + .direct_scanout = false, + }; + wl_signal_emit_mutable(&scene_buffer->events.output_sample, &sample_event); break; } + pixman_region32_fini(&opaque); pixman_region32_fini(&render_region); } @@ -1285,6 +1341,23 @@ void wlr_scene_set_presentation(struct wlr_scene *scene, wl_signal_add(&presentation->events.destroy, &scene->presentation_destroy); } +static void scene_handle_linux_dmabuf_v1_destroy(struct wl_listener *listener, + void *data) { + struct wlr_scene *scene = + wl_container_of(listener, scene, linux_dmabuf_v1_destroy); + wl_list_remove(&scene->linux_dmabuf_v1_destroy.link); + wl_list_init(&scene->linux_dmabuf_v1_destroy.link); + scene->linux_dmabuf_v1 = NULL; +} + +void wlr_scene_set_linux_dmabuf_v1(struct wlr_scene *scene, + struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1) { + assert(scene->linux_dmabuf_v1 == NULL); + scene->linux_dmabuf_v1 = linux_dmabuf_v1; + scene->linux_dmabuf_v1_destroy.notify = scene_handle_linux_dmabuf_v1_destroy; + wl_signal_add(&linux_dmabuf_v1->events.destroy, &scene->linux_dmabuf_v1_destroy); +} + static void scene_output_handle_destroy(struct wlr_addon *addon) { struct wlr_scene_output *scene_output = wl_container_of(addon, scene_output, addon); @@ -1296,50 +1369,47 @@ static const struct wlr_addon_interface output_addon_impl = { .destroy = scene_output_handle_destroy, }; - static void scene_node_output_update(struct wlr_scene_node *node, - struct wl_list *outputs, struct wlr_scene_output *ignore) { + struct wl_list *outputs, struct wlr_scene_output *ignore, + struct wlr_scene_output *force) { if (node->type == WLR_SCENE_NODE_TREE) { - struct wlr_scene_tree *scene_tree = scene_tree_from_node(node); + struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); struct wlr_scene_node *child; wl_list_for_each(child, &scene_tree->children, link) { - scene_node_output_update(child, outputs, ignore); + scene_node_output_update(child, outputs, ignore, force); } return; } - update_node_update_outputs(node, outputs, ignore); + update_node_update_outputs(node, outputs, ignore, force); } -static void scene_output_update_geometry(struct wlr_scene_output *scene_output) { - int width, height; - wlr_output_transformed_resolution(scene_output->output, &width, &height); - wlr_damage_ring_set_bounds(&scene_output->damage_ring, width, height); +static void scene_output_update_geometry(struct wlr_scene_output *scene_output, + bool force_update) { + wlr_damage_ring_add_whole(&scene_output->damage_ring); wlr_output_schedule_frame(scene_output->output); scene_node_output_update(&scene_output->scene->tree.node, - &scene_output->scene->outputs, NULL); + &scene_output->scene->outputs, NULL, force_update ? scene_output : NULL); } static void scene_output_handle_commit(struct wl_listener *listener, void *data) { struct wlr_scene_output *scene_output = wl_container_of(listener, scene_output, output_commit); struct wlr_output_event_commit *event = data; + const struct wlr_output_state *state = event->state; + + bool force_update = state->committed & ( + WLR_OUTPUT_STATE_TRANSFORM | + WLR_OUTPUT_STATE_SCALE | + WLR_OUTPUT_STATE_SUBPIXEL); - if (event->committed & (WLR_OUTPUT_STATE_MODE | - WLR_OUTPUT_STATE_TRANSFORM | - WLR_OUTPUT_STATE_SCALE | + if (force_update || state->committed & (WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_ENABLED)) { - scene_output_update_geometry(scene_output); + scene_output_update_geometry(scene_output, force_update); } } -static void scene_output_handle_mode(struct wl_listener *listener, void *data) { - struct wlr_scene_output *scene_output = wl_container_of(listener, - scene_output, output_mode); - scene_output_update_geometry(scene_output); -} - static void scene_output_handle_damage(struct wl_listener *listener, void *data) { struct wlr_scene_output *scene_output = wl_container_of(listener, scene_output, output_damage); @@ -1366,10 +1436,6 @@ struct wlr_scene_output *wlr_scene_output_create(struct wlr_scene *scene, scene_output->scene = scene; wlr_addon_init(&scene_output->addon, &output->addons, scene, &output_addon_impl); - // Init FX Renderer - struct wlr_egl *egl = wlr_gles2_renderer_get_egl(output->renderer); - fx_renderer_init_addon(egl, &output->addons, scene); - wlr_damage_ring_init(&scene_output->damage_ring); wl_list_init(&scene_output->damage_highlight_regions); @@ -1395,16 +1461,13 @@ struct wlr_scene_output *wlr_scene_output_create(struct wlr_scene *scene, scene_output->output_commit.notify = scene_output_handle_commit; wl_signal_add(&output->events.commit, &scene_output->output_commit); - scene_output->output_mode.notify = scene_output_handle_mode; - wl_signal_add(&output->events.mode, &scene_output->output_mode); - scene_output->output_damage.notify = scene_output_handle_damage; wl_signal_add(&output->events.damage, &scene_output->output_damage); scene_output->output_needs_frame.notify = scene_output_handle_needs_frame; wl_signal_add(&output->events.needs_frame, &scene_output->output_needs_frame); - scene_output_update_geometry(scene_output); + scene_output_update_geometry(scene_output, false); return scene_output; } @@ -1423,7 +1486,7 @@ void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) { wl_signal_emit_mutable(&scene_output->events.destroy, NULL); scene_node_output_update(&scene_output->scene->tree.node, - &scene_output->scene->outputs, scene_output); + &scene_output->scene->outputs, scene_output, NULL); struct highlight_region *damage, *tmp_damage; wl_list_for_each_safe(damage, tmp_damage, &scene_output->damage_highlight_regions, link) { @@ -1434,7 +1497,6 @@ void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) { wlr_damage_ring_finish(&scene_output->damage_ring); wl_list_remove(&scene_output->link); wl_list_remove(&scene_output->output_commit.link); - wl_list_remove(&scene_output->output_mode.link); wl_list_remove(&scene_output->output_damage.link); wl_list_remove(&scene_output->output_needs_frame.link); @@ -1463,14 +1525,14 @@ void wlr_scene_output_set_position(struct wlr_scene_output *scene_output, scene_output->x = lx; scene_output->y = ly; - scene_output_update_geometry(scene_output); + scene_output_update_geometry(scene_output, false); } static bool scene_node_invisible(struct wlr_scene_node *node) { if (node->type == WLR_SCENE_NODE_TREE) { return true; } else if (node->type == WLR_SCENE_NODE_RECT) { - struct wlr_scene_rect *rect = scene_rect_from_node(node); + struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node); return rect->color[3] == 0.f; } else if (node->type == WLR_SCENE_NODE_BUFFER) { @@ -1500,7 +1562,7 @@ static bool construct_render_list_iterator(struct wlr_scene_node *node, // If we see a black rect, we can ignore rendering everything under the rect // and even the rect itself. if (node->type == WLR_SCENE_NODE_RECT && data->calculate_visibility) { - struct wlr_scene_rect *rect = scene_rect_from_node(node); + struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node); float *black = (float[4]){ 0.f, 0.f, 0.f, 1.f }; if (memcmp(rect->color, black, sizeof(float) * 4) == 0) { @@ -1520,16 +1582,66 @@ static bool construct_render_list_iterator(struct wlr_scene_node *node, pixman_region32_fini(&intersection); - struct wlr_scene_node **entry = wl_array_add(data->render_list, - sizeof(struct wlr_scene_node *)); - if (entry) { - *entry = node; + struct render_list_entry *entry = wl_array_add(data->render_list, sizeof(*entry)); + if (!entry) { + return false; } + + *entry = (struct render_list_entry){ + .node = node, + .x = lx, + .y = ly, + }; + return false; } -static bool scene_node_try_direct_scanout(struct wlr_scene_node *node, - struct wlr_scene_output *scene_output, struct wlr_box *box) { +static void output_state_apply_damage(const struct render_data *data, + struct wlr_output_state *state) { + pixman_region32_t frame_damage; + pixman_region32_init(&frame_damage); + pixman_region32_copy(&frame_damage, &data->output->damage_ring.current); + transform_output_damage(&frame_damage, data); + wlr_output_state_set_damage(state, &frame_damage); + pixman_region32_fini(&frame_damage); +} + +static void scene_buffer_send_dmabuf_feedback(const struct wlr_scene *scene, + struct wlr_scene_buffer *scene_buffer, + const struct wlr_linux_dmabuf_feedback_v1_init_options *options) { + if (!scene->linux_dmabuf_v1) { + return; + } + + struct wlr_scene_surface *surface = wlr_scene_surface_try_from_buffer(scene_buffer); + if (!surface) { + return; + } + + // compare to the previous options so that we don't send + // duplicate feedback events. + if (memcmp(options, &scene_buffer->prev_feedback_options, sizeof(*options)) == 0) { + return; + } + + scene_buffer->prev_feedback_options = *options; + + struct wlr_linux_dmabuf_feedback_v1 feedback = {0}; + if (!wlr_linux_dmabuf_feedback_v1_init_with_options(&feedback, options)) { + return; + } + + wlr_linux_dmabuf_v1_set_surface_feedback(scene->linux_dmabuf_v1, + surface->surface, &feedback); + + wlr_linux_dmabuf_feedback_v1_finish(&feedback); +} + +static bool scene_entry_try_direct_scanout(struct render_list_entry *entry, + struct wlr_output_state *state, const struct render_data *data) { + struct wlr_scene_output *scene_output = data->output; + struct wlr_scene_node *node = entry->node; + if (!scene_output->scene->direct_scanout) { return false; } @@ -1545,8 +1657,18 @@ static bool scene_node_try_direct_scanout(struct wlr_scene_node *node, return false; } + if (state->committed & (WLR_OUTPUT_STATE_MODE | + WLR_OUTPUT_STATE_ENABLED | + WLR_OUTPUT_STATE_RENDER_FORMAT)) { + // Legacy DRM will explode if we try to modeset with a direct scanout buffer + return false; + } + + if (!wlr_output_is_direct_scanout_allowed(scene_output->output)) { + return false; + } + struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); - struct wlr_output *output = scene_output->output; struct wlr_fbox default_box = {0}; if (buffer->transform & WL_OUTPUT_TRANSFORM_90) { @@ -1562,75 +1684,170 @@ static bool scene_node_try_direct_scanout(struct wlr_scene_node *node, return false; } - if (buffer->transform != output->transform) { + if (buffer->transform != data->transform) { return false; } - struct wlr_box node_box; - wlr_scene_node_coords(node, &node_box.x, &node_box.y); + struct wlr_box node_box = { .x = entry->x, .y = entry->y }; scene_node_get_size(node, &node_box.width, &node_box.height); - if (!wlr_box_equal(box, &node_box)) { + if (!wlr_box_equal(&data->logical, &node_box)) { return false; } - wlr_output_attach_buffer(output, buffer->buffer); - if (!wlr_output_test(output)) { - wlr_output_rollback(output); + if (buffer->primary_output == scene_output) { + struct wlr_linux_dmabuf_feedback_v1_init_options options = { + .main_renderer = scene_output->output->renderer, + .scanout_primary_output = scene_output->output, + }; + + scene_buffer_send_dmabuf_feedback(scene_output->scene, buffer, &options); + entry->sent_dmabuf_feedback = true; + } + + struct wlr_output_state pending; + wlr_output_state_init(&pending); + if (!wlr_output_state_copy(&pending, state)) { + return false; + } + + wlr_output_state_set_buffer(&pending, buffer->buffer); + output_state_apply_damage(data, &pending); + + if (!wlr_output_test_state(scene_output->output, &pending)) { + wlr_output_state_finish(&pending); return false; } - return wlr_output_commit(output); + wlr_output_state_copy(state, &pending); + wlr_output_state_finish(&pending); + + struct wlr_scene_output_sample_event sample_event = { + .output = scene_output, + .direct_scanout = true, + }; + wl_signal_emit_mutable(&buffer->events.output_sample, &sample_event); + return true; } -bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) { +bool wlr_scene_output_commit(struct wlr_scene_output *scene_output, + const struct wlr_scene_output_state_options *options) { + if (!scene_output->output->needs_frame && !pixman_region32_not_empty( + &scene_output->damage_ring.current)) { + return true; + } + + bool ok = false; + struct wlr_output_state state; + wlr_output_state_init(&state); + if (!wlr_scene_output_build_state(scene_output, &state, options)) { + goto out; + } + + ok = wlr_output_commit_state(scene_output->output, &state); + if (!ok) { + goto out; + } + + wlr_damage_ring_rotate(&scene_output->damage_ring); + +out: + wlr_output_state_finish(&state); + return ok; +} + +bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, + struct wlr_output_state *state, const struct wlr_scene_output_state_options *options) { + struct wlr_scene_output_state_options default_options = {0}; + if (!options) { + options = &default_options; + } + struct wlr_scene_timer *timer = options->timer; + struct timespec start_time; + if (timer) { + clock_gettime(CLOCK_MONOTONIC, &start_time); + wlr_scene_timer_finish(timer); + *timer = (struct wlr_scene_timer){0}; + } + + if ((state->committed & WLR_OUTPUT_STATE_ENABLED) && !state->enabled) { + // if the state is being disabled, do nothing. + return true; + } + struct wlr_output *output = scene_output->output; enum wlr_scene_debug_damage_option debug_damage = scene_output->scene->debug_damage_option; - // Find the fx_renderer addon - struct fx_renderer *renderer = - fx_renderer_addon_find(&output->addons, scene_output->scene); - assert(renderer != NULL); + struct render_data render_data = { + .transform = output->transform, + .scale = output->scale, + .logical = { .x = scene_output->x, .y = scene_output->y }, + .output = scene_output, + }; + + output_pending_resolution(output, state, + &render_data.trans_width, &render_data.trans_height); + + if (state->committed & WLR_OUTPUT_STATE_TRANSFORM) { + if (render_data.transform != state->transform) { + wlr_damage_ring_add_whole(&scene_output->damage_ring); + } + + render_data.transform = state->transform; + } + + if (state->committed & WLR_OUTPUT_STATE_SCALE) { + if (render_data.scale != state->scale) { + wlr_damage_ring_add_whole(&scene_output->damage_ring); + } + + render_data.scale = state->scale; + } + + if (render_data.transform & WL_OUTPUT_TRANSFORM_90) { + int tmp = render_data.trans_width; + render_data.trans_width = render_data.trans_height; + render_data.trans_height = tmp; + } + + render_data.logical.width = render_data.trans_width / render_data.scale; + render_data.logical.height = render_data.trans_height / render_data.scale; struct render_list_constructor_data list_con = { - .box = { .x = scene_output->x, .y = scene_output->y }, + .box = render_data.logical, .render_list = &scene_output->render_list, .calculate_visibility = scene_output->scene->calculate_visibility, }; - wlr_output_effective_resolution(output, - &list_con.box.width, &list_con.box.height); list_con.render_list->size = 0; scene_nodes_in_box(&scene_output->scene->tree.node, &list_con.box, construct_render_list_iterator, &list_con); array_realloc(list_con.render_list, list_con.render_list->size); - int list_len = list_con.render_list->size / sizeof(struct wlr_scene_node *); - struct wlr_scene_node **list_data = list_con.render_list->data; + struct render_list_entry *list_data = list_con.render_list->data; + int list_len = list_con.render_list->size / sizeof(*list_data); - // if there is only one thing to render let's see if that thing can be - // directly scanned out - bool scanout = false; - if (list_len == 1) { - struct wlr_scene_node *node = list_data[0]; - scanout = scene_node_try_direct_scanout(node, scene_output, &list_con.box); - } + bool scanout = list_len == 1 && + scene_entry_try_direct_scanout(&list_data[0], state, &render_data); if (scene_output->prev_scanout != scanout) { scene_output->prev_scanout = scanout; wlr_log(WLR_DEBUG, "Direct scan-out %s", scanout ? "enabled" : "disabled"); - // When exiting direct scan-out, damage everything - wlr_damage_ring_add_whole(&scene_output->damage_ring); + if (!scanout) { + // When exiting direct scan-out, damage everything + wlr_damage_ring_add_whole(&scene_output->damage_ring); + } } if (scanout) { - struct wlr_scene_node *node = list_data[0]; - - assert(node->type == WLR_SCENE_NODE_BUFFER); - struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); - wl_signal_emit_mutable(&buffer->events.output_present, scene_output); + if (timer) { + struct timespec end_time, duration; + clock_gettime(CLOCK_MONOTONIC, &end_time); + timespec_sub(&duration, &end_time, &start_time); + timer->pre_render_duration = timespec_to_nsec(&duration); + } return true; } @@ -1645,8 +1862,7 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) { // add the current frame's damage if there is damage if (pixman_region32_not_empty(&scene_output->damage_ring.current)) { - struct highlight_region *current_damage = - calloc(1, sizeof(*current_damage)); + struct highlight_region *current_damage = calloc(1, sizeof(*current_damage)); if (current_damage) { pixman_region32_init(¤t_damage->region); pixman_region32_copy(¤t_damage->region, @@ -1677,38 +1893,54 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) { pixman_region32_fini(&acc_damage); } + wlr_damage_ring_set_bounds(&scene_output->damage_ring, + render_data.trans_width, render_data.trans_height); + + if (!wlr_output_configure_primary_swapchain(output, state, &output->swapchain)) { + return false; + } + int buffer_age; - if (!wlr_output_attach_render(output, &buffer_age)) { + struct wlr_buffer *buffer = wlr_swapchain_acquire(output->swapchain, &buffer_age); + if (buffer == NULL) { return false; } - pixman_region32_t damage; - pixman_region32_init(&damage); - wlr_damage_ring_get_buffer_damage(&scene_output->damage_ring, - buffer_age, &damage); - if (!output->needs_frame && !pixman_region32_not_empty( - &scene_output->damage_ring.current)) { - pixman_region32_fini(&damage); - wlr_output_rollback(output); - return true; + if (timer) { + timer->render_timer = wlr_render_timer_create(output->renderer); + + struct timespec end_time, duration; + clock_gettime(CLOCK_MONOTONIC, &end_time); + timespec_sub(&duration, &end_time, &start_time); + timer->pre_render_duration = timespec_to_nsec(&duration); } - fx_renderer_begin(renderer, output->width, output->height); + struct fx_gles_render_pass *render_pass = + fx_renderer_begin_buffer_pass(output->renderer, buffer, + &(struct wlr_buffer_pass_options){ + .timer = timer ? timer->render_timer : NULL, + } + ); + if (render_pass == NULL) { + wlr_buffer_unlock(buffer); + return false; + } + + render_data.render_pass = render_pass; + pixman_region32_init(&render_data.damage); + wlr_damage_ring_get_buffer_damage(&scene_output->damage_ring, + buffer_age, &render_data.damage); pixman_region32_t background; pixman_region32_init(&background); - pixman_region32_copy(&background, &damage); + pixman_region32_copy(&background, &render_data.damage); // Cull areas of the background that are occluded by opaque regions of // scene nodes above. Those scene nodes will just render atop having us // never see the background. if (scene_output->scene->calculate_visibility) { - float output_scale = scene_output->output->scale; - for (int i = list_len - 1; i >= 0; i--) { - struct wlr_scene_node *node = list_data[i]; - int x, y; - wlr_scene_node_coords(node, &x, &y); + struct render_list_entry *entry = &list_data[i]; // We must only cull opaque regions that are visible by the node. // The node's visibility will have the knowledge of a black rect @@ -1717,38 +1949,49 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) { // rendering in that black rect region, consider the node's visibility. pixman_region32_t opaque; pixman_region32_init(&opaque); - scene_node_opaque_region(node, x, y, &opaque); - pixman_region32_intersect(&opaque, &opaque, &node->visible); + scene_node_opaque_region(entry->node, entry->x, entry->y, &opaque); + pixman_region32_intersect(&opaque, &opaque, &entry->node->visible); pixman_region32_translate(&opaque, -scene_output->x, -scene_output->y); - wlr_region_scale(&opaque, &opaque, output_scale); + wlr_region_scale(&opaque, &opaque, render_data.scale); pixman_region32_subtract(&background, &background, &opaque); pixman_region32_fini(&opaque); } - if (floor(output_scale) != output_scale) { + if (floor(render_data.scale) != render_data.scale) { wlr_region_expand(&background, &background, 1); // reintersect with the damage because we never want to render // outside of the damage region - pixman_region32_intersect(&background, &background, &damage); + pixman_region32_intersect(&background, &background, &render_data.damage); } } - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(&background, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(output, &rects[i]); - fx_renderer_clear((float[4]){ 0.0, 0.0, 0.0, 1.0 }); - } + transform_output_damage(&background, &render_data); + wlr_render_pass_add_rect(&render_pass->base, &(struct wlr_render_rect_options){ + .box = { .width = buffer->width, .height = buffer->height }, + .color = { .r = 0, .g = 0, .b = 0, .a = 1 }, + .clip = &background, + }); pixman_region32_fini(&background); for (int i = list_len - 1; i >= 0; i--) { - struct wlr_scene_node *node = list_data[i]; - scene_node_render(renderer, node, scene_output, &damage); - } + struct render_list_entry *entry = &list_data[i]; + scene_entry_render(entry, &render_data); + + if (entry->node->type == WLR_SCENE_NODE_BUFFER) { + struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(entry->node); - fx_renderer_scissor(NULL); + if (buffer->primary_output == scene_output && !entry->sent_dmabuf_feedback) { + struct wlr_linux_dmabuf_feedback_v1_init_options options = { + .main_renderer = output->renderer, + .scanout_primary_output = NULL, + }; + + scene_buffer_send_dmabuf_feedback(scene_output->scene, buffer, &options); + } + } + } if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) { struct highlight_region *damage; @@ -1758,55 +2001,48 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) { int64_t time_diff_ms = timespec_to_msec(&time_diff); float alpha = 1.0 - (double)time_diff_ms / HIGHLIGHT_DAMAGE_FADEOUT_TIME; - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(&damage->region, &nrects); - for (int i = 0; i < nrects; ++i) { - struct wlr_box box = { - .x = rects[i].x1, - .y = rects[i].y1, - .width = rects[i].x2 - rects[i].x1, - .height = rects[i].y2 - rects[i].y1, - }; - - float color[4] = { alpha * .5, 0.0, 0.0, alpha * .5 }; - fx_render_rect(renderer, &box, color, output->transform_matrix); - } + wlr_render_pass_add_rect(&render_pass->base, &(struct wlr_render_rect_options){ + .box = { .width = buffer->width, .height = buffer->height }, + .color = { .r = alpha * 0.5, .g = 0, .b = 0, .a = alpha * 0.5 }, + .clip = &damage->region, + }); } } - // Draw the software cursors - wlr_renderer_begin(output->renderer, output->width, output->height); - wlr_output_render_software_cursors(output, &damage); - wlr_renderer_end(output->renderer); - - pixman_region32_fini(&damage); + wlr_output_add_software_cursors_to_render_pass(output, &render_pass->base, &render_data.damage); - int tr_width, tr_height; - wlr_output_transformed_resolution(output, &tr_width, &tr_height); + pixman_region32_fini(&render_data.damage); - enum wl_output_transform transform = - wlr_output_transform_invert(output->transform); - - pixman_region32_t frame_damage; - pixman_region32_init(&frame_damage); - wlr_region_transform(&frame_damage, - &scene_output->damage_ring.current, - transform, tr_width, tr_height); - wlr_output_set_damage(output, &frame_damage); - pixman_region32_fini(&frame_damage); - - bool success = wlr_output_commit(output); - - if (success) { - wlr_damage_ring_rotate(&scene_output->damage_ring); + if (!wlr_render_pass_submit(&render_pass->base)) { + wlr_buffer_unlock(buffer); + return false; } + wlr_output_state_set_buffer(state, buffer); + wlr_buffer_unlock(buffer); + output_state_apply_damage(&render_data, state); + if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT && !wl_list_empty(&scene_output->damage_highlight_regions)) { wlr_output_schedule_frame(scene_output->output); } - return success; + return true; +} + +int64_t wlr_scene_timer_get_duration_ns(struct wlr_scene_timer *timer) { + int64_t pre_render = timer->pre_render_duration; + if (!timer->render_timer) { + return pre_render; + } + int64_t render = wlr_render_timer_get_duration_ns(timer->render_timer); + return render != -1 ? pre_render + render : -1; +} + +void wlr_scene_timer_finish(struct wlr_scene_timer *timer) { + if (timer->render_timer) { + wlr_render_timer_destroy(timer->render_timer); + } } static void scene_node_send_frame_done(struct wlr_scene_node *node, @@ -1823,7 +2059,7 @@ static void scene_node_send_frame_done(struct wlr_scene_node *node, wlr_scene_buffer_send_frame_done(scene_buffer, now); } } else if (node->type == WLR_SCENE_NODE_TREE) { - struct wlr_scene_tree *scene_tree = scene_tree_from_node(node); + struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); struct wlr_scene_node *child; wl_list_for_each(child, &scene_tree->children, link) { scene_node_send_frame_done(child, scene_output, now); @@ -1858,7 +2094,7 @@ static void scene_output_for_each_scene_buffer(const struct wlr_box *output_box, user_iterator(scene_buffer, lx, ly, user_data); } } else if (node->type == WLR_SCENE_NODE_TREE) { - struct wlr_scene_tree *scene_tree = scene_tree_from_node(node); + struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); struct wlr_scene_node *child; wl_list_for_each(child, &scene_tree->children, link) { scene_output_for_each_scene_buffer(output_box, child, lx, ly, |