-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Atomic layout updates #2072
Atomic layout updates #2072
Conversation
I've updated the PR so it's now a basic working implementation. Atomic updates are implemented for the The PR is ready to stash the last good surface texture away, but we need to have a good discussion on whether that's actually what we want to do, because apparently reading textures back from the GPU is expensive. So in lieu of a solution, if the transaction stalls for a view then you'll see the other surfaces resize before their borders and everything else does. Ideas mentioned in chat for preserving textures are:
Anyway, when you resize a split, here's what's happening:
A good test is to change the
Then open an xdg_shell terminal, use that terminal to launch hello-wayland, then use the Moving forward from here:
|
This seems pretty hard, there's no direct way to do it. We could:
I'd rather "steal" the texture from the
The pixel data is in the GPU, so it's tricky to just read it. |
Read through your details about how this works, seems good to me. Can we worry about "what to do with old textures" later? |
I'm making good progress on this. I decided to make it so the main swayc/view properties are the pending state rather than the current state. This is because so much of the code works with these properties, and they need to work with the pending state. The only exception is rendering code which works with the current state. So the good news is everyone can continue to use BTW, I consider the main/pending properties to be the "live" state, and the current state to be what the renderer displays which might be a few frames behind. So IPC events are sent when the live/pending state is changed. I discovered we called the I've made floating, fullscreen, and floating + fullscreen all work with transactions. What's left now is:
|
Texture discussion: swaywm/wlroots#1046 |
* The arrange_foo functions are now replaced with arrange_and_commit, or with manually created transactions and arrange_windows x2. * The arrange functions are now only called from the highest level functions rather than from both high level and low level functions. * Due to the previous point, view_set_fullscreen_raw and view_set_fullscreen are both merged into one function again. * Floating and fullscreen are now working with transactions.
* Also fix parts of the rendering where it was rendering the pending state instead of current.
Update on this: It now preserves the buffer/texture using the |
Note that this should already work with xdg-shell views. |
This implements atomic layout updates for when views map, reparent or unmap.
This is now ready for testing. I'm going to start using this as my daily driver. My latest commit implements atomic layouts for containers that are mapping, reparenting or unmapping. How it works is we still have the regular tree, but the properties you're currently familiar with may not reflect what is actually rendered. Each container has a Another key point is that destroying containers is now split into two steps: destroying and freeing. Destroying removes it from the tree (but keeps it in the For views that are unmapping and destroying, a similar system is used. An unmapped view will still contain a reference to its swayc until it's no longer in a transaction. For this reason we can't use The To test what happens during a transaction, you can set If you use Possible issues or other points of discussion:
|
Works well, haven't found faults yet. One thing is that when toggling fullscreen any clients using the layer shell don't get damaged until they flip, but I'm guessing this would be fixed by proper layer shell client management (which would fix other bugs too probably, such as the damage from layer shell clients not affecting fullscreen clients). |
Actually I can crash it easily. Create 2 windows, make either of them stacking, create a few tabs in the stacking container and then close all of them one by one. Closing the last one will crash the compositor. |
There was no `current` child because the container was destroyed. This makes it fall back to looking in the parent's current children list.
Running mpv on one workspace and quickly opening and closing another mpv instance on the same workspace crashes the compositor. Works with any video and works even if mpv hasn't had a chance to open a window yet. |
This works pretty well on a cursory test, but it occurs to me that we're not going to be able to stress test this without something like interactive resize support. |
Edit: actually it's not much better on master: https://sr.ht/Cr1W.mkv |
sway/desktop/transaction.c
Outdated
struct sway_container *con = instruction->container; | ||
if (con->type == C_VIEW && !con->destroying && | ||
(con->current.view_width != instruction->state.view_width || | ||
con->current.view_height != instruction->state.view_height)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These parens aren't necessary, || has a higher precedence than &&
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removing these parens also makes the code unreadable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nah
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lol I'm dumb
Codewise this looks pretty good. I'm a little concerned about thrashing the heap all the time but I'm not sure what alternative to offer. Need to test it more but I'm gonna wait until the current round of bugs is fixed. |
sway/desktop/xwayland.c
Outdated
wl_list_remove(&xwayland_view->commit.link); | ||
view_unmap(&xwayland_view->view); | ||
view->surface = NULL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't this be done in view_unmap
?
sway/tree/container.c
Outdated
/** | ||
* Implement the actual destroy logic, without reaping. | ||
*/ | ||
struct sway_container *container_destroy_noreaping(struct sway_container *con) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be static
sway/tree/container.c
Outdated
@@ -976,3 +986,12 @@ bool container_is_floating(struct sway_container *container) { | |||
} | |||
return container->parent == workspace->sway_workspace->floating; | |||
} | |||
|
|||
struct wlr_box *container_get_box(struct sway_container *container) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function allocates a box, and callers don't free it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent work, I like it.
A few comments below. Will post more comments tomorrow.
Instead, damage each container when applying the transaction.
Rather than allocate a structure and expect callers to free it, take a pointer to an existing struct as an argument. This function is no longer called anywhere though.
To do this properly, the transaction queue will only be processed if it can be completely processed.
common/list.c
Outdated
@@ -62,6 +62,13 @@ void list_cat(list_t *list, list_t *source) { | |||
} | |||
} | |||
|
|||
void list_empty(list_t *list) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd be much less subtle and just set list->length to 0, list_add does reallocs but list_del never bothers with reducing capacity either so meh.
wlr_texture_destroy(cont->title_focused); | ||
wlr_texture_destroy(cont->title_focused_inactive); | ||
wlr_texture_destroy(cont->title_unfocused); | ||
wlr_texture_destroy(cont->title_urgent); | ||
|
||
for (int i = 0; i < server.destroying_containers->length; ++i) { |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
Do we even need to store damage? Can we just damage all containers in the transaction when applied? |
for (int i = 0; i < view->swayc->instructions->length; ++i) { | ||
struct sway_transaction_instruction *instruction = | ||
view->swayc->instructions->items[i]; | ||
if (!instruction->ready && instruction->state.view_width == width && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if a client receives a configure with size WxH, and commits a W'xH' surface?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then it wouldn't match any instruction in the list, the transaction would time out and then the transaction would apply. I don't think there's any other option. Keep in mind this function is only used by xwayland views.
common/list.c
Outdated
list->capacity = 10; | ||
list->length = 0; | ||
free(list->items); | ||
list->items = malloc(sizeof(void*) * list->capacity); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style: missing space between void
and *
My bad. I failed to see that you've ACKed this one. |
sway/desktop/output.c
Outdated
struct wlr_box box = { | ||
.x = view->swayc->current.view_x - output->swayc->current.swayc_x, | ||
.y = view->swayc->current.view_y - output->swayc->current.swayc_y, | ||
.width = view->swayc->current.view_width, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using the view width/height will render incorrectly if the surface size is different. This happens with unresizable windows for instance. We should crop surfaces instead.
Thinking again about subsurfaces: this is tricky because right now subsurfaces will be hidden while applying a transaction. Subsurfaces are part of the view content (ie. most of the view can be subsurfaces). You can try |
We were arranging a parent which may have been deleted by the reaper, which meant the `current` children list of the surviving parent had a dangling pointer. Instead, we now reap the workspace.
After discussion on IRC, @RyanDwyer convinced me the destroy/free wording is a good idea.
|
A flash of background was happening for two reasons: 1) We were using the xsurface's dimensions to check if the surface is ready, but these are pending dimensions. 2) In my particular setup, the default geometry of the xsurface does not intersect any output, which prevented it from receiving a frame done event. This made the transaction time out and the client would only redraw once it's been rendered.
Follow-up issues:
|
Thanks! \o/ |
This is a very early concept/WIP of atomic layout updates. It's so early that my code won't even compile. You should consider it a proposal with some example code attached. I'm submitting it here so everyone can see the direction I'm going in and give feedback as the feature is developed.
The new data structures I'm proposing are:
struct sway_transaction
struct sway_transaction_instruction
struct sway_container_state
The basic idea here is that when we want to arrange layout, we create a
struct sway_transaction
. We add instructions to the transaction - one instruction per container involved in the arrangement. Then we commit the transaction which sends all the configure events. Then when all the configures are done, or a time has passed, we apply the transaction which updates the tree accordingly.Each instruction has a pointer to a container, a pending state which it'll apply to the container, and the configure serial.
I was originally going to have a
current
andpending
state in each container, but realised that would be tedious to update the codebase to changecontainer->x
tocontainer->current.x
, so at the moment there's justpending
and the current stuff uses the normal properties.When arranging layout, it builds the new pending state from the container's existing pending state. This is so a second transaction running at the same time won't revert what the first transaction is trying to change. This means if no transactions are in progress, every container's pending state must be an accurate reflection of the current state.
As for how windows are arranged by commands and so on, if you only want to do one arrange, you'll be able to call
arrange_and_commit(container)
. This creates a new transaction and commits it for you. If you need to do two arranges in the one transaction, such as moving a view from one visible workspace to another visible workspace, you can take the more manual approach:You may notice this means there's only two entry points for the arrange functions, rather than one for each container type as we had before.
Fixes #1650
Fixes #220