git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

commite6a8dc52c3bccfcfab4df98af6b0f1740b179a7a
parenta8062e26d3
authorIsaac Freund <[email protected]>
date2020-06-09 23:13
transactions: save and render subsurface buffers

 river/Root.zig   | 11 ++++++-----
 river/View.zig   | 60 ++++++++++++++++++++++++++++++++++++++++----------------
 river/render.zig | 59 +++++++++++++++++++++++++++----------------------------
 3 files changed, 78 insertions(+), 52 deletions(-)

diff --git a/river/Root.zig b/river/Root.zig
index f032665..601b415 100644
--- a/river/Root.zig
+++ b/river/Root.zig
@@ -132,6 +132,7 @@ fn startTransaction(self: *Self) void {
         var view_it = ViewStack(View).iterator(output.views.first, std.math.maxInt(u32));
         while (view_it.next()) |view_node| {
             const view = &view_node.view;
+
             // Clear the serial in case this transaction is interrupting a prior one.
             view.pending_serial = null;
 
@@ -145,10 +146,10 @@ fn startTransaction(self: *Self) void {
                 view.sendFrameDone();
             }
 
-            // If there is a saved buffer present, then this transaction is interrupting
-            // a previous transaction and we should keep the old buffer.
-            if (view.stashed_buffer == null) {
-                view.stashBuffer();
+            // If there are saved buffers present, then this transaction is interrupting
+            // a previous transaction and we should keep the old buffers.
+            if (view.saved_buffers.items.len == 0) {
+                view.saveBuffers();
             }
         }
     }
@@ -239,7 +240,7 @@ fn commitTransaction(self: *Self) void {
                 view_tags_changed = true;
             }
 
-            view.dropStashedBuffer();
+            view.dropSavedBuffers();
         }
 
         if (view_tags_changed) output.sendViewTags();
diff --git a/river/View.zig b/river/View.zig
index cc07252..608a7fb 100644
--- a/river/View.zig
+++ b/river/View.zig
@@ -38,6 +38,11 @@ const Impl = union(enum) {
     xwayland_view: XwaylandView,
 };
 
+const SavedBuffer = struct {
+    wlr_buffer: *c.wlr_buffer,
+    box: Box,
+};
+
 /// The implementation of this view
 impl: Impl,
 
@@ -66,8 +71,8 @@ pending_tags: ?u32,
 
 pending_serial: ?u32,
 
-// This is what we render while a transaction is in progress
-stashed_buffer: ?*c.wlr_buffer,
+/// These are what we render while a transaction is in progress
+saved_buffers: std.ArrayList(SavedBuffer),
 
 pub fn init(
     self: *Self,
@@ -94,7 +99,7 @@ pub fn init(
 
     self.pending_serial = null;
 
-    self.stashed_buffer = null;
+    self.saved_buffers = std.ArrayList(SavedBuffer).init(output.root.server.allocator);
 
     if (@TypeOf(surface) == *c.wlr_xdg_surface) {
         self.impl = .{ .xdg_toplevel = undefined };
@@ -106,9 +111,8 @@ pub fn init(
 }
 
 pub fn deinit(self: Self) void {
-    if (self.stashed_buffer) |buffer| {
-        c.wlr_buffer_unref(buffer);
-    }
+    for (self.saved_buffers.items) |buffer| c.wlr_buffer_unref(buffer.wlr_buffer);
+    self.saved_buffers.deinit();
 }
 
 pub fn needsConfigure(self: Self) bool {
@@ -135,20 +139,42 @@ pub fn sendFrameDone(self: Self) void {
     c.wlr_surface_send_frame_done(self.wlr_surface.?, &now);
 }
 
-pub fn dropStashedBuffer(self: *Self) void {
-    // TODO: log debug error
-    if (self.stashed_buffer) |buffer| {
-        c.wlr_buffer_unref(buffer);
-        self.stashed_buffer = null;
+pub fn dropSavedBuffers(self: *Self) void {
+    for (self.saved_buffers.items) |buffer| c.wlr_buffer_unref(buffer.wlr_buffer);
+    self.saved_buffers.items.len = 0;
+}
+
+pub fn saveBuffers(self: *Self) void {
+    if (self.saved_buffers.items.len > 0) {
+        Log.Error.log("view already has buffers saved, overwriting", .{});
+        self.saved_buffers.items.len = 0;
     }
+
+    self.forEachSurface(saveBuffersIterator, &self.saved_buffers);
 }
 
-pub fn stashBuffer(self: *Self) void {
-    // TODO: log debug error if there is already a saved buffer
-    if (self.wlr_surface) |wlr_surface| {
-        if (c.wlr_surface_has_buffer(wlr_surface)) {
-            _ = c.wlr_buffer_ref(wlr_surface.buffer);
-            self.stashed_buffer = wlr_surface.buffer;
+fn saveBuffersIterator(
+    wlr_surface: ?*c.wlr_surface,
+    surface_x: c_int,
+    surface_y: c_int,
+    data: ?*c_void,
+) callconv(.C) void {
+    const saved_buffers = @ptrCast(
+        *std.ArrayList(SavedBuffer),
+        @alignCast(@alignOf(*std.ArrayList(SavedBuffer)), data),
+    );
+    if (wlr_surface) |surface| {
+        if (c.wlr_surface_has_buffer(surface)) {
+            saved_buffers.append(.{
+                .wlr_buffer = surface.buffer,
+                .box = Box{
+                    .x = surface_x,
+                    .y = surface_y,
+                    .width = @intCast(u32, surface.current.width),
+                    .height = @intCast(u32, surface.current.height),
+                },
+            }) catch return;
+            _ = c.wlr_buffer_ref(surface.buffer);
         }
     }
 }
diff --git a/river/render.zig b/river/render.zig
index a7055ef..9ba3631 100644
--- a/river/render.zig
+++ b/river/render.zig
@@ -136,36 +136,35 @@ fn renderLayer(output: Output, layer: std.TailQueue(LayerSurface), now: *c.times
 }
 
 fn renderView(output: Output, view: *View, now: *c.timespec) void {
-    // If we have a stashed buffer, we are in the middle of a transaction
-    // and need to render that buffer until the transaction is complete.
-    if (view.stashed_buffer) |buffer| {
-        var box = c.wlr_box{
-            .x = view.current_box.x,
-            .y = view.current_box.y,
-            .width = @intCast(c_int, view.current_box.width),
-            .height = @intCast(c_int, view.current_box.height),
-        };
-
-        // Scale the box to the output's current scaling factor
-        scaleBox(&box, output.wlr_output.scale);
-
-        var matrix: [9]f32 = undefined;
-        c.wlr_matrix_project_box(
-            &matrix,
-            &box,
-            .WL_OUTPUT_TRANSFORM_NORMAL,
-            0.0,
-            &output.wlr_output.transform_matrix,
-        );
-
-        // This takes our matrix, the texture, and an alpha, and performs the actual
-        // rendering on the GPU.
-        _ = c.wlr_render_texture_with_matrix(
-            output.getRenderer(),
-            buffer.texture,
-            &matrix,
-            1.0,
-        );
+    // If we have saved buffers, we are in the middle of a transaction
+    // and need to render those buffers until the transaction is complete.
+    if (view.saved_buffers.items.len != 0) {
+        for (view.saved_buffers.items) |saved_buffer| {
+            var box = saved_buffer.box.toWlrBox();
+            box.x += view.current_box.x;
+            box.y += view.current_box.y;
+
+            // Scale the box to the output's current scaling factor
+            scaleBox(&box, output.wlr_output.scale);
+
+            var matrix: [9]f32 = undefined;
+            c.wlr_matrix_project_box(
+                &matrix,
+                &box,
+                .WL_OUTPUT_TRANSFORM_NORMAL,
+                0.0,
+                &output.wlr_output.transform_matrix,
+            );
+
+            // This takes our matrix, the texture, and an alpha, and performs the actual
+            // rendering on the GPU.
+            _ = c.wlr_render_texture_with_matrix(
+                output.getRenderer(),
+                saved_buffer.wlr_buffer.texture,
+                &matrix,
+                1.0,
+            );
+        }
     } else {
         // Since there is no stashed buffer, we are not in the middle of
         // a transaction and may simply render each toplevel surface.