Don't commit multiple transactions at the same time

This commit is contained in:
Ryan Dwyer 2018-08-12 10:44:45 +10:00
parent e7a7306063
commit 4b8e3a885b

View file

@ -33,6 +33,7 @@ struct sway_transaction {
list_t *instructions; // struct sway_transaction_instruction * list_t *instructions; // struct sway_transaction_instruction *
size_t num_waiting; size_t num_waiting;
size_t num_configures; size_t num_configures;
uint32_t con_ids; // Bitwise XOR of view container IDs
struct timespec create_time; struct timespec create_time;
struct timespec commit_time; struct timespec commit_time;
}; };
@ -212,20 +213,14 @@ static void transaction_apply(struct sway_transaction *transaction) {
} }
} }
static void transaction_commit(struct sway_transaction *transaction);
static void transaction_progress_queue() { static void transaction_progress_queue() {
// Check if any transactions in the queue are in a partially ready state. if (!server.transactions->length) {
// If so, we shouldn't progress any transactions, even ones which are fully
// ready at the front of the queue, because the views in the ready
// transactions might have committed past it to a transaction which isn't
// yet ready.
for (int i = 0; i < server.transactions->length; ++i) {
struct sway_transaction *transaction = server.transactions->items[i];
if (transaction->num_waiting != 0 &&
transaction->num_waiting != transaction->num_configures) {
return; return;
} }
} // There's only ever one committed transaction,
while (server.transactions->length) { // and it's the first one in the queue.
struct sway_transaction *transaction = server.transactions->items[0]; struct sway_transaction *transaction = server.transactions->items[0];
if (transaction->num_waiting) { if (transaction->num_waiting) {
return; return;
@ -233,8 +228,28 @@ static void transaction_progress_queue() {
transaction_apply(transaction); transaction_apply(transaction);
transaction_destroy(transaction); transaction_destroy(transaction);
list_del(server.transactions, 0); list_del(server.transactions, 0);
}
if (!server.transactions->length) {
idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1);
return;
}
// If there's a bunch of consecutive transactions which all apply to the
// same views, skip all except the last one.
while (server.transactions->length >= 2) {
struct sway_transaction *a = server.transactions->items[0];
struct sway_transaction *b = server.transactions->items[1];
if (a->con_ids == b->con_ids) {
list_del(server.transactions, 0);
transaction_destroy(a);
} else {
break;
}
}
transaction = server.transactions->items[0];
transaction_commit(transaction);
transaction_progress_queue();
} }
static int handle_timeout(void *data) { static int handle_timeout(void *data) {
@ -288,6 +303,7 @@ static void transaction_commit(struct sway_transaction *transaction) {
instruction->state.view_width, instruction->state.view_width,
instruction->state.view_height); instruction->state.view_height);
++transaction->num_waiting; ++transaction->num_waiting;
transaction->con_ids ^= con->id;
// From here on we are rendering a saved buffer of the view, which // From here on we are rendering a saved buffer of the view, which
// means we can send a frame done event to make the client redraw it // means we can send a frame done event to make the client redraw it
@ -305,17 +321,6 @@ static void transaction_commit(struct sway_transaction *transaction) {
if (server.debug_txn_timings) { if (server.debug_txn_timings) {
clock_gettime(CLOCK_MONOTONIC, &transaction->commit_time); clock_gettime(CLOCK_MONOTONIC, &transaction->commit_time);
} }
if (server.transactions->length || transaction->num_waiting) {
list_add(server.transactions, transaction);
} else {
// There are no other transactions in progress, and this one has nothing
// to wait for, so we can skip the queue.
wlr_log(WLR_DEBUG, "Transaction %p has nothing to wait for", transaction);
transaction_apply(transaction);
transaction_destroy(transaction);
idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1);
return;
}
if (transaction->num_waiting) { if (transaction->num_waiting) {
// Set up a timer which the views must respond within // Set up a timer which the views must respond within
@ -329,6 +334,9 @@ static void transaction_commit(struct sway_transaction *transaction) {
strerror(errno)); strerror(errno));
handle_timeout(transaction); handle_timeout(transaction);
} }
} else {
wlr_log(WLR_DEBUG,
"Transaction %p has nothing to wait for", transaction);
} }
// The debug tree shows the pending/live tree. Here is a good place to // The debug tree shows the pending/live tree. Here is a good place to
@ -415,5 +423,15 @@ void transaction_commit_dirty(void) {
container->dirty = false; container->dirty = false;
} }
server.dirty_containers->length = 0; server.dirty_containers->length = 0;
list_add(server.transactions, transaction);
// There's only ever one committed transaction,
// and it's the first one in the queue.
if (server.transactions->length == 1) {
transaction_commit(transaction); transaction_commit(transaction);
// Attempting to progress the queue here is useful
// if the transaction has nothing to wait for.
transaction_progress_queue();
}
} }