#include #include #include #include #include #include #include #include "render/drm_format_set.h" #include "types/wlr_output.h" static struct wlr_swapchain *create_swapchain(struct wlr_output *output, int width, int height, uint32_t render_format, bool allow_modifiers) { struct wlr_allocator *allocator = output->allocator; assert(output->allocator != NULL); const struct wlr_drm_format_set *display_formats = wlr_output_get_primary_formats(output, allocator->buffer_caps); struct wlr_drm_format format = {0}; if (!output_pick_format(output, display_formats, &format, render_format)) { wlr_log(WLR_ERROR, "Failed to pick primary buffer format for output '%s'", output->name); return NULL; } char *format_name = drmGetFormatName(format.format); wlr_log(WLR_DEBUG, "Choosing primary buffer format %s (0x%08"PRIX32") for output '%s'", format_name ? format_name : "", format.format, output->name); free(format_name); if (!allow_modifiers && (format.len != 1 || format.modifiers[0] != DRM_FORMAT_MOD_LINEAR)) { if (!wlr_drm_format_has(&format, DRM_FORMAT_MOD_INVALID)) { wlr_log(WLR_DEBUG, "Implicit modifiers not supported"); wlr_drm_format_finish(&format); return NULL; } format.len = 0; if (!wlr_drm_format_add(&format, DRM_FORMAT_MOD_INVALID)) { wlr_log(WLR_DEBUG, "Failed to add implicit modifier to format"); wlr_drm_format_finish(&format); return NULL; } } struct wlr_swapchain *swapchain = wlr_swapchain_create(allocator, width, height, &format); wlr_drm_format_finish(&format); return swapchain; } static bool test_swapchain(struct wlr_output *output, struct wlr_swapchain *swapchain, const struct wlr_output_state *state) { struct wlr_buffer *buffer = wlr_swapchain_acquire(swapchain, NULL); if (buffer == NULL) { return false; } struct wlr_output_state copy = *state; copy.committed |= WLR_OUTPUT_STATE_BUFFER; copy.buffer = buffer; bool ok = wlr_output_test_state(output, ©); wlr_buffer_unlock(buffer); return ok; } bool wlr_output_configure_primary_swapchain(struct wlr_output *output, const struct wlr_output_state *state, struct wlr_swapchain **swapchain_ptr) { struct wlr_output_state empty_state; if (state == NULL) { wlr_output_state_init(&empty_state); state = &empty_state; } int width, height; output_pending_resolution(output, state, &width, &height); uint32_t format = output->render_format; if (state->committed & WLR_OUTPUT_STATE_RENDER_FORMAT) { format = state->render_format; } // Re-use the existing swapchain if possible struct wlr_swapchain *old_swapchain = *swapchain_ptr; if (old_swapchain != NULL && old_swapchain->width == width && old_swapchain->height == height && old_swapchain->format.format == format) { return true; } struct wlr_swapchain *swapchain = create_swapchain(output, width, height, format, true); if (swapchain == NULL) { wlr_log(WLR_ERROR, "Failed to create swapchain for output '%s'", output->name); return false; } wlr_log(WLR_DEBUG, "Testing swapchain for output '%s'", output->name); if (!test_swapchain(output, swapchain, state)) { wlr_log(WLR_DEBUG, "Output test failed on '%s', retrying without modifiers", output->name); wlr_swapchain_destroy(swapchain); swapchain = create_swapchain(output, width, height, format, false); if (swapchain == NULL) { wlr_log(WLR_ERROR, "Failed to create modifier-less swapchain for output '%s'", output->name); return false; } wlr_log(WLR_DEBUG, "Testing modifier-less swapchain for output '%s'", output->name); if (!test_swapchain(output, swapchain, state)) { wlr_log(WLR_ERROR, "Swapchain for output '%s' failed test", output->name); wlr_swapchain_destroy(swapchain); return false; } } wlr_swapchain_destroy(*swapchain_ptr); *swapchain_ptr = swapchain; return true; }