@@ -515,9 +515,16 @@ static uint64_t capture_bg_to_tex1_ex(output_t *output, effects_output_t *ctx, b
515515 }
516516 }
517517
518+ // render the scene directly into a blur-resolution buffer by scaling the capture output down
519+ struct wlr_output_state cap_state ;
520+ wlr_output_state_init (& cap_state );
521+ wlr_output_state_set_enabled (& cap_state , true);
522+ wlr_output_state_set_custom_mode (& cap_state , ctx -> blur_w , ctx -> blur_h , 0 );
523+ wlr_output_state_set_scale (& cap_state , (float )ctx -> blur_w / (float )w );
524+
518525 wlr_damage_ring_add_whole (& ctx -> capture_scene_output -> damage_ring );
519526
520- bool ok = wlr_scene_output_build_state (ctx -> capture_scene_output , & ctx -> capture_state , NULL );
527+ bool ok = wlr_scene_output_build_state (ctx -> capture_scene_output , & cap_state , NULL );
521528
522529 if (hide_node ) {
523530 if (* hide_flag )
@@ -558,18 +565,16 @@ static uint64_t capture_bg_to_tex1_ex(output_t *output, effects_output_t *ctx, b
558565
559566 wlr_scene_output_set_position (ctx -> capture_scene_output , -0x7fff , -0x7fff );
560567
561- if (!ok || !ctx -> capture_state .buffer ) {
568+ if (!ok || !cap_state .buffer ) {
562569 wlr_log (WLR_INFO , "capture_bg_to_tex1: no buffer from build_state" );
563- wlr_buffer_unlock (ctx -> capture_state .buffer );
564- ctx -> capture_state .buffer = NULL ;
570+ wlr_output_state_finish (& cap_state );
565571 return 0 ;
566572 }
567573
568574 uint64_t result ;
569- effects_backend -> capture_readback (ctx -> capture_state .buffer , & ctx -> be_state ,
570- ctx -> be_state .pong .native_handle [0 ], ctx -> blur_w , ctx -> blur_h , w , h , & result );
571- wlr_buffer_unlock (ctx -> capture_state .buffer );
572- ctx -> capture_state .buffer = NULL ;
575+ effects_backend -> capture_readback (cap_state .buffer , & ctx -> be_state ,
576+ ctx -> be_state .pong .native_handle [0 ], ctx -> blur_w , ctx -> blur_h , ctx -> blur_w , ctx -> blur_h , & result );
577+ wlr_output_state_finish (& cap_state );
573578
574579 return result ;
575580}
@@ -659,7 +664,9 @@ static bool rebuild_live_blur(output_t *output, uint64_t shared_blurred, pixman_
659664 }
660665
661666 // blur the shared background once and reuse it for every window
662- if (!ensure_output_buf (& ctx -> blur_buf , ctx -> blur_native , w , h )) {
667+ // buffer stays at blur resolution, scene upscales it when drawing
668+ if (!ensure_sized_buf (& ctx -> blur_buf , ctx -> blur_native , & ctx -> blur_buf_w , & ctx -> blur_buf_h ,
669+ ctx -> blur_w , ctx -> blur_h )) {
663670 toplevel_t * tl ;
664671 wl_list_for_each (tl , & server .toplevels , link ) {
665672 if (!tl -> blur || !tl -> blur -> blur_node || !tl -> node || !tl -> node -> client )
@@ -676,7 +683,7 @@ static bool rebuild_live_blur(output_t *output, uint64_t shared_blurred, pixman_
676683
677684 uint64_t blurred ;
678685 effects_backend -> blur (& ctx -> be_state , shared_blurred , ctx -> blur_w , ctx -> blur_h , & bp , & blurred );
679- effects_backend -> blit (blurred , ctx -> blur_native [0 ], w , h , NULL , 0 );
686+ effects_backend -> blit (blurred , ctx -> blur_native [0 ], ctx -> blur_w , ctx -> blur_h , NULL , 0 );
680687 any = true;
681688
682689 // only windows with compositor-rounded corners need a per-window buffer
@@ -761,6 +768,12 @@ static void push_blur_to_toplevels(output_t *output) {
761768 if (tl -> blur -> blur_node -> buffer != buf )
762769 wlr_scene_buffer_set_buffer (tl -> blur -> blur_node , buf );
763770
771+ // source box lives in the buffer's coordinate space
772+ // shared buffer is at blur resolution (upscaled by the scene)
773+ // masked windows use their own full-resolution buffer, so their source box is unchanged
774+ float src_scale_x = masked ? 1.0f : (float )ctx -> blur_w / (float )output -> width ;
775+ float src_scale_y = masked ? 1.0f : (float )ctx -> blur_h / (float )output -> height ;
776+
764777 if (tl -> blur && !pixman_region32_empty (& tl -> blur -> blur_region )) {
765778 int lx = 0 , ly = 0 ;
766779 wlr_scene_node_coords (& tl -> scene_tree -> node , & lx , & ly );
@@ -789,6 +802,10 @@ static void push_blur_to_toplevels(output_t *output) {
789802 int offset_y = (blur_rect .y < output -> ly ) ? (output -> ly - blur_rect .y ) : 0 ;
790803 wlr_scene_node_set_position (& tl -> blur -> blur_node -> node , blur_r_x + offset_x ,
791804 blur_r_y + offset_y );
805+ src .x *= src_scale_x ;
806+ src .y *= src_scale_y ;
807+ src .width *= src_scale_x ;
808+ src .height *= src_scale_y ;
792809 wlr_scene_buffer_set_source_box (tl -> blur -> blur_node , & src );
793810 wlr_scene_buffer_set_dest_size (tl -> blur -> blur_node , dw , dh );
794811 } else {
@@ -805,6 +822,10 @@ static void push_blur_to_toplevels(output_t *output) {
805822 int node_ox = (r .x < output -> lx ) ? (output -> lx - r .x ) : 0 ;
806823 int node_oy = (r .y < output -> ly ) ? (output -> ly - r .y ) : 0 ;
807824 wlr_scene_node_set_position (& tl -> blur -> blur_node -> node , node_ox , node_oy );
825+ src .x *= src_scale_x ;
826+ src .y *= src_scale_y ;
827+ src .width *= src_scale_x ;
828+ src .height *= src_scale_y ;
808829 wlr_scene_buffer_set_source_box (tl -> blur -> blur_node , & src );
809830 wlr_scene_buffer_set_dest_size (tl -> blur -> blur_node , dw , dh );
810831 }
@@ -814,7 +835,6 @@ static void push_blur_to_toplevels(output_t *output) {
814835static bool rebuild_live_blur_layers (output_t * output , uint64_t bg_tex , pixman_region32_t * damage ) {
815836 (void )damage ;
816837 effects_output_t * ctx = output -> effects ;
817- int w = output -> width , h = output -> height ;
818838
819839 struct be_blur_params bp = {
820840 .algorithm = blur_algorithm ,
@@ -828,18 +848,18 @@ static bool rebuild_live_blur_layers(output_t *output, uint64_t bg_tex, pixman_r
828848 };
829849
830850 // capture the background with blur toplevels visible
831-
832851 if (!bg_tex )
833852 bg_tex = capture_bg_combined (output , ctx );
834853 if (!bg_tex )
835854 return false;
836855
837- if (!ensure_output_buf (& ctx -> layer_blur_buf , ctx -> layer_blur_native , w , h ))
856+ if (!ensure_sized_buf (& ctx -> layer_blur_buf , ctx -> layer_blur_native , & ctx -> layer_blur_buf_w ,
857+ & ctx -> layer_blur_buf_h , ctx -> blur_w , ctx -> blur_h ))
838858 return false;
839859
840860 uint64_t blurred ;
841861 effects_backend -> blur (& ctx -> be_state , bg_tex , ctx -> blur_w , ctx -> blur_h , & bp , & blurred );
842- effects_backend -> blit (blurred , ctx -> layer_blur_native [0 ], w , h , NULL , 0 );
862+ effects_backend -> blit (blurred , ctx -> layer_blur_native [0 ], ctx -> blur_w , ctx -> blur_h , NULL , 0 );
843863 return true;
844864}
845865
@@ -873,6 +893,8 @@ static bool layer_blur_needs_rebuild(output_t *output, pixman_region32_t *damage
873893static void push_blur_to_layers (output_t * output ) {
874894 effects_output_t * ctx = output -> effects ;
875895 struct wlr_buffer * buf = ctx -> layer_blur_buf ;
896+ float src_scale_x = (float )ctx -> blur_w / (float )output -> width ;
897+ float src_scale_y = (float )ctx -> blur_h / (float )output -> height ;
876898 for (int i = 0 ; i < 4 ; i ++ ) {
877899 layer_surface_t * ls ;
878900 wl_list_for_each (ls , & output -> layers [i ], link ) {
@@ -932,6 +954,10 @@ static void push_blur_to_layers(output_t *output) {
932954
933955 // position at blur region offset within surface
934956 wlr_scene_node_set_position (& ls -> blur_node -> node , blur_r_x + offset_x , blur_r_y + offset_y );
957+ src .x *= src_scale_x ;
958+ src .y *= src_scale_y ;
959+ src .width *= src_scale_x ;
960+ src .height *= src_scale_y ;
935961 wlr_scene_buffer_set_source_box (ls -> blur_node , & src );
936962 wlr_scene_buffer_set_dest_size (ls -> blur_node , dw , dh );
937963 }
@@ -1669,38 +1695,113 @@ void effects_evict_buffers(void) {
16691695 }
16701696}
16711697
1672- static bool damage_in_tree (pixman_region32_t * damage , struct wlr_scene_tree * tree ) {
1673- int nrects ;
1674- pixman_box32_t * rects = pixman_region32_rectangles (damage , & nrects );
1675- for (int i = 0 ; i < nrects ; i ++ ) {
1676- double cx = (rects [i ].x1 + rects [i ].x2 ) * 0.5 ;
1677- double cy = (rects [i ].y1 + rects [i ].y2 ) * 0.5 ;
1678- double nx , ny ;
1679- if (wlr_scene_node_at (& tree -> node , cx , cy , & nx , & ny ))
1680- return true;
1681- }
1682- return false;
1683- }
1684-
1685- static bool background_capture_needed (struct wlr_scene_output * scene_output ) {
1698+ // returns true if the current damage overlaps any active effect region on this output
1699+ static bool effect_regions_damaged (output_t * output , struct wlr_scene_output * scene_output ) {
16861700 pixman_region32_t * damage = & scene_output -> damage_ring .current ;
16871701 if (pixman_region32_empty (damage ))
16881702 return false;
16891703
1690- // only check trees not hidden during background capture
1691- struct wlr_scene_tree * relevant [] = {
1692- server .bg_tree ,
1693- server .bot_tree ,
1694- server .tile_tree ,
1695- server .float_tree ,
1696- server .shader_tree ,
1697- server .drag_tree ,
1698- };
1704+ effects_output_t * ctx = output -> effects ;
1705+ pixman_region32_t * region = & ctx -> scratch_region_a ;
1706+ pixman_region32_clear (region );
16991707
1700- for (size_t i = 0 ; i < sizeof (relevant ) / sizeof (relevant [0 ]); i ++ )
1701- if (damage_in_tree (damage , relevant [i ]))
1702- return true;
1708+ toplevel_t * tl ;
1709+ wl_list_for_each (tl , & server .toplevels , link ) {
1710+ if (!tl -> node || !tl -> node -> client || !tl -> node -> client -> flags .shown )
1711+ continue ;
1712+ if (!tl -> node -> output || tl -> node -> output != output )
1713+ continue ;
1714+ if (!tl -> blur || (!tl -> blur -> blur_node && !tl -> blur -> acrylic_node && !tl -> blur -> mica_node ))
1715+ continue ;
17031716
1717+ struct wlr_box r ;
1718+ if (tl -> blur -> blur_node && !pixman_region32_empty (& tl -> blur -> blur_region )) {
1719+ int lx , ly ;
1720+ if (!wlr_scene_node_coords (& tl -> scene_tree -> node , & lx , & ly ))
1721+ continue ;
1722+ r = (struct wlr_box ){
1723+ .x = lx - output -> lx + tl -> blur -> blur_region .extents .x1 ,
1724+ .y = ly - output -> ly + tl -> blur -> blur_region .extents .y1 ,
1725+ .width = tl -> blur -> blur_region .extents .x2 - tl -> blur -> blur_region .extents .x1 ,
1726+ .height = tl -> blur -> blur_region .extents .y2 - tl -> blur -> blur_region .extents .y1 ,
1727+ };
1728+ } else {
1729+ r = get_animated_client_rect (tl );
1730+ r .x -= output -> lx ;
1731+ r .y -= output -> ly ;
1732+ }
1733+ if (r .width <= 0 || r .height <= 0 )
1734+ continue ;
1735+ pixman_region32_union_rect (region , region , r .x , r .y , r .width , r .height );
1736+ }
1737+
1738+ for (int i = 0 ; i < 4 ; i ++ ) {
1739+ layer_surface_t * ls ;
1740+ wl_list_for_each (ls , & output -> layers [i ], link ) {
1741+ if (!ls -> blur_node || !ls -> mapped )
1742+ continue ;
1743+ if (pixman_region32_empty (& ls -> blur_region ))
1744+ continue ;
1745+ int lx , ly ;
1746+ if (!wlr_scene_node_coords (& ls -> scene_tree -> node , & lx , & ly ))
1747+ continue ;
1748+ struct wlr_box r = {
1749+ .x = lx - output -> lx + ls -> blur_region .extents .x1 ,
1750+ .y = ly - output -> ly + ls -> blur_region .extents .y1 ,
1751+ .width = ls -> blur_region .extents .x2 - ls -> blur_region .extents .x1 ,
1752+ .height = ls -> blur_region .extents .y2 - ls -> blur_region .extents .y1 ,
1753+ };
1754+ pixman_region32_union_rect (region , region , r .x , r .y , r .width , r .height );
1755+ }
1756+ }
1757+
1758+ // corner-mask windows show the sharp backdrop behind their corners
1759+ wl_list_for_each (tl , & server .toplevels , link ) {
1760+ if (!tl -> node || !tl -> node -> client || !tl -> node -> client -> flags .shown )
1761+ continue ;
1762+ if (!tl -> node -> output || tl -> node -> output != output )
1763+ continue ;
1764+ if (!tl -> rounded || !tl -> rounded -> corner_mask_node )
1765+ continue ;
1766+ client_t * c = tl -> node -> client ;
1767+ if (c -> border_radius <= 0.0f || c -> state == STATE_FULLSCREEN )
1768+ continue ;
1769+ struct wlr_box r = get_animated_client_rect (tl );
1770+ r .x -= output -> lx ;
1771+ r .y -= output -> ly ;
1772+ pixman_region32_union_rect (region , region , r .x , r .y , r .width , r .height );
1773+ }
1774+
1775+ if (pixman_region32_empty (region ))
1776+ return false;
1777+
1778+ pixman_region32_intersect (& ctx -> scratch_region_b , damage , region );
1779+ return !pixman_region32_empty (& ctx -> scratch_region_b );
1780+ }
1781+
1782+ // returns true if a window/layer has a pending change that must be pushed to the
1783+ // scene even when no backdrop damage intersects an effect region
1784+ static bool effects_pending_update (output_t * output ) {
1785+ toplevel_t * tl ;
1786+ wl_list_for_each (tl , & server .toplevels , link ) {
1787+ if (!tl -> node || !tl -> node -> client || !tl -> node -> client -> flags .shown )
1788+ continue ;
1789+ if (!tl -> node -> output || tl -> node -> output != output )
1790+ continue ;
1791+ if (tl -> blur && tl -> blur -> blur_node && tl -> blur -> blur_region_dirty )
1792+ return true;
1793+ if (tl -> shadow && tl -> node -> client -> flags .shadow && (tl -> shadow -> shadow_dirty ||
1794+ tl -> shadow -> shadow_geometry_dirty ))
1795+ return true;
1796+ if (tl -> rounded && tl -> rounded -> corner_mask_dirty )
1797+ return true;
1798+ }
1799+ for (int i = 0 ; i < 4 ; i ++ ) {
1800+ layer_surface_t * ls ;
1801+ wl_list_for_each (ls , & output -> layers [i ], link )
1802+ if (ls -> blur_node && ls -> mapped && ls -> blur_region_dirty )
1803+ return true;
1804+ }
17041805 return false;
17051806}
17061807
@@ -1779,10 +1880,11 @@ void effects_output_frame(output_t *output, struct wlr_scene_output *scene_outpu
17791880
17801881 effects_backend -> frame_begin ();
17811882
1782- bool bg_damaged = workspace_warmup || background_capture_needed (scene_output );
1883+ bool bg_damaged = workspace_warmup || effect_regions_damaged (output ,
1884+ scene_output ) || effects_pending_update (output );
17831885 bool mica_dirty = mica_enabled && ctx -> mica_dirty ;
17841886
1785- // when no relevant damage and mica not dirty , skip all effects work
1887+ // when no effect region is damaged and no effect state is pending , skip all effects work
17861888 if (!bg_damaged && !mica_dirty )
17871889 goto after_capture ;
17881890
0 commit comments