aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/wlr/interfaces/wlr_output.h3
-rw-r--r--include/wlr/types/wlr_output.h3
-rw-r--r--types/wlr_output.c148
3 files changed, 142 insertions, 12 deletions
diff --git a/include/wlr/interfaces/wlr_output.h b/include/wlr/interfaces/wlr_output.h
index c74ea156..a727da61 100644
--- a/include/wlr/interfaces/wlr_output.h
+++ b/include/wlr/interfaces/wlr_output.h
@@ -17,8 +17,7 @@
/**
* A backend implementation of wlr_output.
*
- * The functions commit, attach_render and rollback_render are mandatory. Other
- * functions are optional.
+ * The commit function is mandatory. Other functions are optional.
*/
struct wlr_output_impl {
/**
diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h
index dd5051c9..bab6d4ed 100644
--- a/include/wlr/types/wlr_output.h
+++ b/include/wlr/types/wlr_output.h
@@ -185,6 +185,9 @@ struct wlr_output {
struct wlr_buffer *cursor_front_buffer;
int software_cursor_locks; // number of locks forcing software cursors
+ struct wlr_swapchain *swapchain;
+ struct wlr_buffer *back_buffer;
+
struct wl_listener display_destroy;
void *data;
diff --git a/types/wlr_output.c b/types/wlr_output.c
index 982eeea7..f50189ab 100644
--- a/types/wlr_output.c
+++ b/types/wlr_output.c
@@ -232,6 +232,13 @@ void wlr_output_update_custom_mode(struct wlr_output *output, int32_t width,
output->refresh = refresh;
+ if (output->swapchain != NULL &&
+ (output->swapchain->width != output->width ||
+ output->swapchain->height != output->height)) {
+ wlr_swapchain_destroy(output->swapchain);
+ output->swapchain = NULL;
+ }
+
struct wl_resource *resource;
wl_resource_for_each(resource, &output->resources) {
send_current_mode(resource);
@@ -336,7 +343,10 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) {
void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend,
const struct wlr_output_impl *impl, struct wl_display *display) {
- assert(impl->attach_render && impl->rollback_render && impl->commit);
+ assert(impl->commit);
+ if (impl->attach_render || impl->rollback_render) {
+ assert(impl->attach_render && impl->rollback_render);
+ }
if (impl->set_cursor || impl->move_cursor) {
assert(impl->set_cursor && impl->move_cursor);
}
@@ -373,6 +383,8 @@ void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend,
wl_display_add_destroy_listener(display, &output->display_destroy);
}
+static void output_clear_back_buffer(struct wlr_output *output);
+
void wlr_output_destroy(struct wlr_output *output) {
if (!output) {
return;
@@ -380,6 +392,7 @@ void wlr_output_destroy(struct wlr_output *output) {
wl_list_remove(&output->display_destroy.link);
wlr_output_destroy_global(output);
+ output_clear_back_buffer(output);
wlr_signal_emit_safe(&output->events.destroy, output);
@@ -393,6 +406,8 @@ void wlr_output_destroy(struct wlr_output *output) {
wlr_swapchain_destroy(output->cursor_swapchain);
wlr_buffer_unlock(output->cursor_front_buffer);
+ wlr_swapchain_destroy(output->swapchain);
+
if (output->idle_frame != NULL) {
wl_event_source_remove(output->idle_frame);
}
@@ -457,14 +472,106 @@ static void output_state_clear_buffer(struct wlr_output_state *state) {
state->committed &= ~WLR_OUTPUT_STATE_BUFFER;
}
-bool wlr_output_attach_render(struct wlr_output *output, int *buffer_age) {
- if (!output->impl->attach_render(output, buffer_age)) {
+static struct wlr_drm_format *output_pick_format(struct wlr_output *output,
+ const struct wlr_drm_format_set *display_formats);
+
+static bool output_create_swapchain(struct wlr_output *output) {
+ if (output->swapchain != NULL) {
+ return true;
+ }
+
+ struct wlr_allocator *allocator = backend_get_allocator(output->backend);
+ if (allocator == NULL) {
+ wlr_log(WLR_ERROR, "Failed to get backend allocator");
return false;
}
- output_state_clear_buffer(&output->pending);
- output->pending.committed |= WLR_OUTPUT_STATE_BUFFER;
- output->pending.buffer_type = WLR_OUTPUT_STATE_BUFFER_RENDER;
+ const struct wlr_drm_format_set *display_formats = NULL;
+ if (output->impl->get_primary_formats) {
+ display_formats =
+ output->impl->get_primary_formats(output, allocator->buffer_caps);
+ if (display_formats == NULL) {
+ wlr_log(WLR_ERROR, "Failed to get primary display formats");
+ return false;
+ }
+ }
+
+ struct wlr_drm_format *format = output_pick_format(output, display_formats);
+ if (format == NULL) {
+ wlr_log(WLR_ERROR, "Failed to pick primary buffer format for output '%s'",
+ output->name);
+ return false;
+ }
+ wlr_log(WLR_DEBUG, "Choosing primary buffer format 0x%"PRIX32" for output '%s'",
+ format->format, output->name);
+
+ output->swapchain = wlr_swapchain_create(allocator, output->width,
+ output->height, format);
+ free(format);
+ if (output->swapchain == NULL) {
+ wlr_log(WLR_ERROR, "Failed to create output swapchain");
+ return false;
+ }
+
+ return true;
+}
+
+static bool output_attach_back_buffer(struct wlr_output *output,
+ int *buffer_age) {
+ assert(output->back_buffer == NULL);
+
+ if (!output_create_swapchain(output)) {
+ return false;
+ }
+
+ struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend);
+ assert(renderer != NULL);
+
+ struct wlr_buffer *buffer =
+ wlr_swapchain_acquire(output->swapchain, buffer_age);
+ if (buffer == NULL) {
+ return false;
+ }
+
+ if (!wlr_renderer_bind_buffer(renderer, buffer)) {
+ wlr_buffer_unlock(buffer);
+ return false;
+ }
+
+ output->back_buffer = buffer;
+ return true;
+}
+
+static void output_clear_back_buffer(struct wlr_output *output) {
+ if (output->back_buffer == NULL) {
+ return;
+ }
+
+ struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend);
+ assert(renderer != NULL);
+
+ wlr_renderer_bind_buffer(renderer, NULL);
+
+ wlr_buffer_unlock(output->back_buffer);
+ output->back_buffer = NULL;
+}
+
+bool wlr_output_attach_render(struct wlr_output *output, int *buffer_age) {
+ if (output->impl->attach_render) {
+ if (!output->impl->attach_render(output, buffer_age)) {
+ return false;
+ }
+
+ output_state_clear_buffer(&output->pending);
+ output->pending.committed |= WLR_OUTPUT_STATE_BUFFER;
+ output->pending.buffer_type = WLR_OUTPUT_STATE_BUFFER_RENDER;
+ } else {
+ if (!output_attach_back_buffer(output, buffer_age)) {
+ return false;
+ }
+ wlr_output_attach_buffer(output, output->back_buffer);
+ }
+
return true;
}
@@ -474,11 +581,24 @@ uint32_t wlr_output_preferred_read_format(struct wlr_output *output) {
return DRM_FORMAT_INVALID;
}
- if (!output->impl->attach_render(output, NULL)) {
- return DRM_FORMAT_INVALID;
+ if (output->impl->attach_render) {
+ if (!output->impl->attach_render(output, NULL)) {
+ return false;
+ }
+ } else {
+ if (!output_attach_back_buffer(output, NULL)) {
+ return false;
+ }
}
+
uint32_t fmt = renderer->impl->preferred_read_format(renderer);
- output->impl->rollback_render(output);
+
+ if (output->impl->rollback_render) {
+ output->impl->rollback_render(output);
+ } else {
+ output_clear_back_buffer(output);
+ }
+
return fmt;
}
@@ -529,7 +649,8 @@ static bool output_basic_test(struct wlr_output *output) {
return false;
}
- if (output->pending.buffer_type == WLR_OUTPUT_STATE_BUFFER_SCANOUT) {
+ if (output->pending.buffer_type == WLR_OUTPUT_STATE_BUFFER_SCANOUT &&
+ output->back_buffer == NULL) {
if (output->attach_render_locks > 0) {
wlr_log(WLR_DEBUG, "Direct scan-out disabled by lock");
return false;
@@ -660,6 +781,12 @@ bool wlr_output_commit(struct wlr_output *output) {
if (output->pending.committed & WLR_OUTPUT_STATE_BUFFER) {
output->frame_pending = true;
output->needs_frame = false;
+
+ if (output->back_buffer != NULL) {
+ wlr_swapchain_set_buffer_submitted(output->swapchain,
+ output->back_buffer);
+ output_clear_back_buffer(output);
+ }
}
uint32_t committed = output->pending.committed;
@@ -681,6 +808,7 @@ void wlr_output_rollback(struct wlr_output *output) {
output->pending.buffer_type == WLR_OUTPUT_STATE_BUFFER_RENDER) {
output->impl->rollback_render(output);
}
+ output_clear_back_buffer(output);
output_state_clear(&output->pending);
}