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
1 changed files with 50 additions and 32 deletions

View File

@ -33,6 +33,7 @@ struct sway_transaction {
list_t *instructions; // struct sway_transaction_instruction *
size_t num_waiting;
size_t num_configures;
uint32_t con_ids; // Bitwise XOR of view container IDs
struct timespec create_time;
struct timespec commit_time;
};
@ -212,29 +213,43 @@ static void transaction_apply(struct sway_transaction *transaction) {
}
}
static void transaction_commit(struct sway_transaction *transaction);
static void transaction_progress_queue() {
// Check if any transactions in the queue are in a partially ready state.
// 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;
if (!server.transactions->length) {
return;
}
// There's only ever one committed transaction,
// and it's the first one in the queue.
struct sway_transaction *transaction = server.transactions->items[0];
if (transaction->num_waiting) {
return;
}
transaction_apply(transaction);
transaction_destroy(transaction);
list_del(server.transactions, 0);
if (!server.transactions->length) {
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;
}
}
while (server.transactions->length) {
struct sway_transaction *transaction = server.transactions->items[0];
if (transaction->num_waiting) {
return;
}
transaction_apply(transaction);
transaction_destroy(transaction);
list_del(server.transactions, 0);
}
idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1);
transaction = server.transactions->items[0];
transaction_commit(transaction);
transaction_progress_queue();
}
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_height);
++transaction->num_waiting;
transaction->con_ids ^= con->id;
// 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
@ -305,17 +321,6 @@ static void transaction_commit(struct sway_transaction *transaction) {
if (server.debug_txn_timings) {
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) {
// Set up a timer which the views must respond within
@ -329,6 +334,9 @@ static void transaction_commit(struct sway_transaction *transaction) {
strerror(errno));
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
@ -415,5 +423,15 @@ void transaction_commit_dirty(void) {
container->dirty = false;
}
server.dirty_containers->length = 0;
transaction_commit(transaction);
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);
// Attempting to progress the queue here is useful
// if the transaction has nothing to wait for.
transaction_progress_queue();
}
}