From a1145335d6d29ea2ef3ed01abf53ca57a76fa233 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 4 Sep 2021 16:22:14 +0200 Subject: [PATCH] output-swapchain: new helper Closes: https://github.com/swaywm/wlroots/issues/3079 --- include/types/wlr_output_swapchain.h | 9 + include/wlr/types/wlr_output_swapchain.h | 48 +++++ types/meson.build | 1 + types/wlr_output_swapchain.c | 254 +++++++++++++++++++++++ 4 files changed, 312 insertions(+) create mode 100644 include/types/wlr_output_swapchain.h create mode 100644 include/wlr/types/wlr_output_swapchain.h create mode 100644 types/wlr_output_swapchain.c diff --git a/include/types/wlr_output_swapchain.h b/include/types/wlr_output_swapchain.h new file mode 100644 index 0000000000..0c18a840e6 --- /dev/null +++ b/include/types/wlr_output_swapchain.h @@ -0,0 +1,9 @@ +#ifndef TYPES_WLR_OUTPUT_SWAPCHAIN_H +#define TYPES_WLR_OUTPUT_SWAPCHAIN_H + +#include + +struct wlr_output_swapchain_manager *wlr_output_swapchain_manager_create( + struct wlr_renderer *renderer, struct wlr_allocator *allocator); + +#endif diff --git a/include/wlr/types/wlr_output_swapchain.h b/include/wlr/types/wlr_output_swapchain.h new file mode 100644 index 0000000000..56a364357d --- /dev/null +++ b/include/wlr/types/wlr_output_swapchain.h @@ -0,0 +1,48 @@ +/* + * This an unstable interface of wlroots. No guarantees are made regarding the + * future consistency of this API. + */ +#ifndef WLR_USE_UNSTABLE +#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" +#endif + +#ifndef WLR_TYPES_WLR_OUTPUT_SWAPCHAIN_H +#define WLR_TYPES_WLR_OUTPUT_SWAPCHAIN_H + +#include + +struct wlr_output; + +struct wlr_output_swapchain_manager { + struct wlr_renderer *renderer; + + // private state + + struct wlr_allocator *allocator; +}; + +struct wlr_output_swapchain { + struct wlr_output *output; + struct wlr_output_swapchain_manager *manager; + struct wlr_swapchain *swapchain; + + // private state + + struct wlr_buffer *back_buffer; + + struct wl_listener output_destroy; +}; + +struct wlr_output_swapchain_manager *wlr_output_swapchain_manager_autocreate( + struct wlr_backend *backend); +void wlr_output_swapchain_manager_destroy( + struct wlr_output_swapchain_manager *manager); + +struct wlr_output_swapchain *wlr_output_swapchain_create( + struct wlr_output_swapchain_manager *manager, struct wlr_output *output); +void wlr_output_swapchain_destroy(struct wlr_output_swapchain *output_swapchain); +bool wlr_output_swapchain_begin(struct wlr_output_swapchain *output_swapchain, + int *buffer_age); +void wlr_output_swapchain_end(struct wlr_output_swapchain *output_swapchain); + +#endif diff --git a/types/meson.build b/types/meson.build index bb1e8c8f93..abf6a34e88 100644 --- a/types/meson.build +++ b/types/meson.build @@ -43,6 +43,7 @@ wlr_files += files( 'wlr_output_layout.c', 'wlr_output_management_v1.c', 'wlr_output_power_management_v1.c', + 'wlr_output_swapchain.c', 'wlr_output.c', 'wlr_pointer_constraints_v1.c', 'wlr_pointer_gestures_v1.c', diff --git a/types/wlr_output_swapchain.c b/types/wlr_output_swapchain.c new file mode 100644 index 0000000000..a9c2136d18 --- /dev/null +++ b/types/wlr_output_swapchain.c @@ -0,0 +1,254 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "backend/backend.h" +#include "render/allocator/allocator.h" +#include "render/drm_format_set.h" +#include "render/swapchain.h" +#include "render/wlr_renderer.h" +#include "types/wlr_output_swapchain.h" + +struct wlr_output_swapchain_manager *wlr_output_swapchain_manager_create( + struct wlr_renderer *renderer, struct wlr_allocator *allocator) { + struct wlr_output_swapchain_manager *manager = calloc(1, sizeof(*manager)); + if (manager == NULL) { + return NULL; + } + + manager->renderer = renderer; + manager->allocator = allocator; + + return manager; +} + +struct wlr_output_swapchain_manager *wlr_output_swapchain_manager_autocreate( + struct wlr_backend *backend) { + struct wlr_renderer *renderer = wlr_renderer_autocreate(backend); + if (renderer == NULL) { + return NULL; + } + + struct wlr_allocator *allocator = wlr_allocator_autocreate(backend, renderer); + if (allocator == NULL) { + wlr_renderer_destroy(renderer); + return NULL; + } + + struct wlr_output_swapchain_manager *manager = + wlr_output_swapchain_manager_create(renderer, allocator); + if (manager == NULL) { + wlr_allocator_destroy(allocator); + wlr_renderer_destroy(renderer); + return NULL; + } + + return manager; +} + +void wlr_output_swapchain_manager_destroy( + struct wlr_output_swapchain_manager *manager) { + wlr_allocator_destroy(manager->allocator); + wlr_renderer_destroy(manager->renderer); + free(manager); +} + +static void output_pending_resolution(struct wlr_output *output, int *width, + int *height) { + if (output->pending.committed & WLR_OUTPUT_STATE_MODE) { + switch (output->pending.mode_type) { + case WLR_OUTPUT_STATE_MODE_FIXED: + *width = output->pending.mode->width; + *height = output->pending.mode->height; + return; + case WLR_OUTPUT_STATE_MODE_CUSTOM: + *width = output->pending.custom_mode.width; + *height = output->pending.custom_mode.height; + return; + } + abort(); + } else { + *width = output->width; + *height = output->height; + } +} + +static struct wlr_drm_format *pick_format( + struct wlr_output_swapchain *output_swapchain, + const struct wlr_drm_format_set *display_formats) { + struct wlr_renderer *renderer = output_swapchain->manager->renderer; + + const struct wlr_drm_format_set *render_formats = + wlr_renderer_get_render_formats(renderer); + if (render_formats == NULL) { + wlr_log(WLR_ERROR, "Failed to get render formats"); + return NULL; + } + + struct wlr_drm_format *format = NULL; + const uint32_t candidates[] = { DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB8888 }; + for (size_t i = 0; i < sizeof(candidates) / sizeof(candidates[0]); i++) { + uint32_t fmt = candidates[i]; + + const struct wlr_drm_format *render_format = + wlr_drm_format_set_get(render_formats, fmt); + if (render_format == NULL) { + wlr_log(WLR_DEBUG, "Renderer doesn't support format 0x%"PRIX32, fmt); + continue; + } + + if (display_formats != NULL) { + const struct wlr_drm_format *display_format = + wlr_drm_format_set_get(display_formats, fmt); + if (display_format == NULL) { + wlr_log(WLR_DEBUG, "Output doesn't support format 0x%"PRIX32, fmt); + continue; + } + format = wlr_drm_format_intersect(display_format, render_format); + } else { + // The output can display any format + format = wlr_drm_format_dup(render_format); + } + + if (format == NULL) { + wlr_log(WLR_DEBUG, "Failed to intersect display and render " + "modifiers for format 0x%"PRIX32, fmt); + } else { + break; + } + } + if (format == NULL) { + wlr_log(WLR_ERROR, "Failed to choose a format for output '%s'", + output_swapchain->output->name); + return NULL; + } + + return format; +} + +static bool update_swapchain(struct wlr_output_swapchain *output_swapchain, + bool allow_modifiers) { + struct wlr_output *output = output_swapchain->output; + + int width, height; + output_pending_resolution(output, &width, &height); + + if (output_swapchain->swapchain != NULL && + output_swapchain->swapchain->width == width && + output_swapchain->swapchain->height == height && + (allow_modifiers || output->swapchain->format->len == 0)) { + return true; + } + + struct wlr_allocator *allocator = output_swapchain->manager->allocator; + if (allocator == NULL) { + wlr_log(WLR_ERROR, "Failed to get backend allocator"); + return false; + } + + 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 = + pick_format(output_swapchain, 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); + + if (!allow_modifiers && (format->len != 1 || format->modifiers[0] != DRM_FORMAT_MOD_LINEAR)) { + format->len = 0; + } + + struct wlr_swapchain *swapchain = + wlr_swapchain_create(allocator, width, height, format); + free(format); + if (swapchain == NULL) { + wlr_log(WLR_ERROR, "Failed to create output swapchain"); + return false; + } + + wlr_swapchain_destroy(output_swapchain->swapchain); + output_swapchain->swapchain = swapchain; + return true; +} + +static void handle_output_destroy(struct wl_listener *listener, void *data) { + struct wlr_output_swapchain *output_swapchain = + wl_container_of(listener, output_swapchain, output_destroy); + wlr_output_swapchain_destroy(output_swapchain); +} + +struct wlr_output_swapchain *wlr_output_swapchain_create( + struct wlr_output_swapchain_manager *manager, + struct wlr_output *output) { + struct wlr_output_swapchain *output_swapchain = + calloc(1, sizeof(*output_swapchain)); + if (output_swapchain == NULL) { + return NULL; + } + + output_swapchain->output = output; + output_swapchain->manager = manager; + + output_swapchain->output_destroy.notify = handle_output_destroy; + wl_signal_add(&output->events.destroy, &output_swapchain->output_destroy); + + return output_swapchain; +} + +void wlr_output_swapchain_destroy(struct wlr_output_swapchain *output_swapchain) { + wl_list_remove(&output_swapchain->output_destroy.link); + free(output_swapchain); +} + +bool wlr_output_swapchain_begin(struct wlr_output_swapchain *output_swapchain, + int *buffer_age) { + assert(output_swapchain->back_buffer == NULL); + + if (!update_swapchain(output_swapchain, true)) { + return false; + } + + struct wlr_buffer *buffer = + wlr_swapchain_acquire(output_swapchain->swapchain, buffer_age); + if (buffer == NULL) { + return false; + } + + if (!wlr_renderer_begin_with_buffer(output_swapchain->manager->renderer, + buffer)) { + wlr_buffer_unlock(buffer); + return false; + } + + output_swapchain->back_buffer = buffer; + return true; +} + +void wlr_output_swapchain_end(struct wlr_output_swapchain *output_swapchain) { + assert(output_swapchain->back_buffer != NULL); + + wlr_renderer_end(output_swapchain->manager->renderer); + + wlr_output_attach_buffer(output_swapchain->output, + output_swapchain->back_buffer); + + wlr_buffer_unlock(output_swapchain->back_buffer); + output_swapchain->back_buffer = NULL; +}