Skip to content

Commit

Permalink
output-swapchain: new helper
Browse files Browse the repository at this point in the history
Closes: swaywm#3079
  • Loading branch information
emersion authored and bl4ckb0ne committed Sep 24, 2021
1 parent 6575998 commit a114533
Show file tree
Hide file tree
Showing 4 changed files with 312 additions and 0 deletions.
9 changes: 9 additions & 0 deletions include/types/wlr_output_swapchain.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#ifndef TYPES_WLR_OUTPUT_SWAPCHAIN_H
#define TYPES_WLR_OUTPUT_SWAPCHAIN_H

#include <wlr/types/wlr_output_swapchain.h>

struct wlr_output_swapchain_manager *wlr_output_swapchain_manager_create(
struct wlr_renderer *renderer, struct wlr_allocator *allocator);

#endif
48 changes: 48 additions & 0 deletions include/wlr/types/wlr_output_swapchain.h
Original file line number Diff line number Diff line change
@@ -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 <wayland-util.h>

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
1 change: 1 addition & 0 deletions types/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
254 changes: 254 additions & 0 deletions types/wlr_output_swapchain.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
#include <assert.h>
#include <drm_fourcc.h>
#include <stdlib.h>
#include <wlr/backend.h>
#include <wlr/interfaces/wlr_output.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_output_swapchain.h>
#include <wlr/util/log.h>
#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;
}

0 comments on commit a114533

Please sign in to comment.