Skip to content

Commit

Permalink
wayland: add wp-fractional-scale-v1 support
Browse files Browse the repository at this point in the history
This protocol is pretty important since it finally lets us solve the
longstanding issue of fractional scaling in wayland (no more mpv doing
rendering over the target resolution and then being scaled down). This
protocol also can completely replace the buffer_scale usage that we are
currently using for integer scaling so hopefully this can be removed
sometime in the future. Note that vo_dmabuf_wayland is omitted because
we want the compositor to handle all the scaling for that VO (i.e. just
leave the default buffer scale of one). However, we are making viewport
required (everyone supports this anyway) with this commit so go ahead
and remove the redundant check in vo_dmabuf_wayland.

Fixes #9443.
  • Loading branch information
Dudemanguy committed Nov 29, 2022
1 parent e97e0e4 commit c4ed894
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 42 deletions.
1 change: 1 addition & 0 deletions generated/wayland/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ wl_protocol_dir = wayland['deps'][2].get_variable(pkgconfig: 'pkgdatadir', inter
protocols = [[wl_protocol_dir, 'stable/presentation-time/presentation-time.xml'],
[wl_protocol_dir, 'stable/viewporter/viewporter.xml'],
[wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
[wl_protocol_dir, 'staging/fractional-scale/fractional-scale-v1.xml'], #TODO: guard this
[wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/xdg-decoration/xdg-decoration-unstable-v1.xml']]
Expand Down
5 changes: 4 additions & 1 deletion video/out/opengl/context_wayland.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,17 @@ static void resize(struct ra_ctx *ctx)
egl_create_window(ctx);

const int32_t width = wl->scaling * mp_rect_w(wl->geometry);
const int32_t height = wl->scaling * mp_rect_h(wl->geometry);
const int32_t height =wl->scaling * mp_rect_h(wl->geometry);

vo_wayland_set_opaque_region(wl, ctx->opts.want_alpha);
if (p->egl_window)
wl_egl_window_resize(p->egl_window, width, height, 0, 0);

wl->vo->dwidth = width;
wl->vo->dheight = height;

if (wl->fractional_viewport)
vo_wayland_handle_fractional_scale(wl);
}

static bool wayland_egl_check_visible(struct ra_ctx *ctx)
Expand Down
7 changes: 0 additions & 7 deletions video/out/vo_dmabuf_wayland.c
Original file line number Diff line number Diff line change
Expand Up @@ -336,13 +336,6 @@ static int preinit(struct vo *vo)
return VO_ERROR;
}

if (!vo->wl->viewport) {
MP_FATAL(vo->wl, "Compositor doesn't support the %s protocol!\n",
wp_viewporter_interface.name);
return VO_ERROR;
}


if (vo->wl->single_pixel_manager) {
#if HAVE_WAYLAND_PROTOCOLS_1_27
p->solid_buffer = wp_single_pixel_buffer_manager_v1_create_u32_rgba_buffer(
Expand Down
4 changes: 4 additions & 0 deletions video/out/vo_wlshm.c
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,10 @@ static int resize(struct vo *vo)
p->free_buffers = buf->next;
talloc_free(buf);
}

if (wl->fractional_viewport)
vo_wayland_handle_fractional_scale(wl);

return mp_sws_reinit(p->sws);
}

Expand Down
4 changes: 4 additions & 0 deletions video/out/vulkan/context_wayland.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ static bool resize(struct ra_ctx *ctx)
const int32_t height = wl->scaling * mp_rect_h(wl->geometry);

vo_wayland_set_opaque_region(wl, ctx->opts.want_alpha);

if (wl->fractional_viewport)
vo_wayland_handle_fractional_scale(wl);

return ra_vk_ctx_resize(ctx, width, height);
}

Expand Down
136 changes: 103 additions & 33 deletions video/out/wayland_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
#include "generated/wayland/single-pixel-buffer-v1.h"
#endif

//TODO: guard
#include "generated/wayland/fractional-scale-v1.h"

#if WAYLAND_VERSION_MAJOR > 1 || WAYLAND_VERSION_MINOR >= 20
#define HAVE_WAYLAND_1_20
#endif
Expand Down Expand Up @@ -171,7 +174,7 @@ static int spawn_cursor(struct vo_wayland_state *wl);
static void greatest_common_divisor(struct vo_wayland_state *wl, int a, int b);
static void remove_output(struct vo_wayland_output *out);
static void request_decoration_mode(struct vo_wayland_state *wl, uint32_t mode);
static void set_geometry(struct vo_wayland_state *wl);
static void set_geometry(struct vo_wayland_state *wl, bool resize);
static void set_surface_scaling(struct vo_wayland_state *wl);
static void window_move(struct vo_wayland_state *wl, uint32_t serial);

Expand Down Expand Up @@ -676,7 +679,7 @@ static void output_handle_done(void *data, struct wl_output *wl_output)
if (wl->current_output && wl->current_output->output == wl_output) {
set_surface_scaling(wl);
spawn_cursor(wl);
set_geometry(wl);
set_geometry(wl, false);
wl->pending_vo_events |= VO_EVENT_DPI;
wl->pending_vo_events |= VO_EVENT_RESIZE;
}
Expand Down Expand Up @@ -750,14 +753,14 @@ static void surface_handle_enter(void *data, struct wl_surface *wl_surface,
}

if (!mp_rect_equals(&old_output_geometry, &wl->current_output->geometry)) {
set_geometry(wl);
set_geometry(wl, false);
force_resize = true;
}

if (!mp_rect_equals(&old_geometry, &wl->geometry) || force_resize)
wl->pending_vo_events |= VO_EVENT_RESIZE;

MP_VERBOSE(wl, "Surface entered output %s %s (0x%x), scale = %i, refresh rate = %f Hz\n",
MP_VERBOSE(wl, "Surface entered output %s %s (0x%x), scale = %f, refresh rate = %f Hz\n",
o->make, o->model, o->id, wl->scaling, o->refresh_rate);

wl->pending_vo_events |= VO_EVENT_WIN_STATE;
Expand Down Expand Up @@ -922,8 +925,8 @@ static void handle_toplevel_config(void *data, struct xdg_toplevel *toplevel,

resize:
MP_VERBOSE(wl, "Resizing due to xdg from %ix%i to %ix%i\n",
mp_rect_w(old_geometry)*wl->scaling, mp_rect_h(old_geometry)*wl->scaling,
mp_rect_w(wl->geometry)*wl->scaling, mp_rect_h(wl->geometry)*wl->scaling);
(int)round(mp_rect_w(old_geometry) * wl->scaling), (int)round(mp_rect_h(old_geometry) * wl->scaling),
(int)round(mp_rect_w(wl->geometry) * wl->scaling), (int)round(mp_rect_h(wl->geometry) * wl->scaling));

wl->pending_vo_events |= VO_EVENT_RESIZE;
wl->toplevel_configured = true;
Expand Down Expand Up @@ -953,6 +956,23 @@ static const struct xdg_toplevel_listener xdg_toplevel_listener = {
#endif
};

//TODO: Guard
static void preferred_scale(void *data,
struct wp_fractional_scale_v1 *fractional_scale,
uint32_t scale)
{
struct vo_wayland_state *wl = data;
wl->scaling = (double)scale / 120;
MP_VERBOSE(wl, "Obtained preferred scale, %f, from the compositor.\n",
wl->scaling);
if (wl->current_output)
set_geometry(wl, true);
}

static const struct wp_fractional_scale_v1_listener fractional_scale_listener = {
preferred_scale,
};

static const char *zxdg_decoration_mode_to_str(const uint32_t mode)
{
switch (mode) {
Expand Down Expand Up @@ -1247,6 +1267,11 @@ static void registry_handle_add(void *data, struct wl_registry *reg, uint32_t id
}
#endif

//TODO: guard this
if (!strcmp(interface, wp_fractional_scale_manager_v1_interface.name) && found++) {
wl->fractional_scale_manager = wl_registry_bind(reg, id, &wp_fractional_scale_manager_v1_interface, 1);
}

if (!strcmp(interface, wp_presentation_interface.name) && found++) {
wl->presentation = wl_registry_bind(reg, id, &wp_presentation_interface, 1);
wp_presentation_add_listener(wl->presentation, &pres_listener, wl);
Expand Down Expand Up @@ -1385,6 +1410,21 @@ static bool create_input(struct vo_wayland_state *wl)
return 0;
}

static int create_viewports(struct vo_wayland_state *wl)
{
if (wl->viewporter) {
wl->fractional_viewport = wp_viewporter_get_viewport(wl->viewporter, wl->surface);
wl->viewport = wp_viewporter_get_viewport(wl->viewporter, wl->surface);
wl->video_viewport = wp_viewporter_get_viewport(wl->viewporter, wl->video_surface);
}

if (!wl->viewporter || !wl->fractional_viewport || !wl->viewport || !wl->video_viewport) {
MP_ERR(wl, "failed to create viewport interfaces!\n");
return 1;
}
return 0;
}

static int create_xdg_surface(struct vo_wayland_state *wl)
{
wl->xdg_surface = xdg_wm_base_get_xdg_surface(wl->wm_base, wl->surface);
Expand All @@ -1393,8 +1433,10 @@ static int create_xdg_surface(struct vo_wayland_state *wl)
wl->xdg_toplevel = xdg_surface_get_toplevel(wl->xdg_surface);
xdg_toplevel_add_listener(wl->xdg_toplevel, &xdg_toplevel_listener, wl);

if (!wl->xdg_surface || !wl->xdg_toplevel)
if (!wl->xdg_surface || !wl->xdg_toplevel) {
MP_ERR(wl, "failled to create xdg_surface and xdg_toplevel!\n");
return 1;
}
return 0;
}

Expand Down Expand Up @@ -1561,7 +1603,8 @@ static int set_cursor_visibility(struct vo_wayland_state *wl, bool on)
return VO_FALSE;
wl_pointer_set_cursor(wl->pointer, wl->pointer_id, wl->cursor_surface,
img->hotspot_x/wl->scaling, img->hotspot_y/wl->scaling);
wl_surface_set_buffer_scale(wl->cursor_surface, wl->scaling);
if (!wl->fractional_scale_manager)
wl_surface_set_buffer_scale(wl->cursor_surface, wl->scaling);
wl_surface_attach(wl->cursor_surface, buffer, 0, 0);
wl_surface_damage_buffer(wl->cursor_surface, 0, 0, img->width, img->height);
wl_surface_commit(wl->cursor_surface);
Expand All @@ -1571,14 +1614,14 @@ static int set_cursor_visibility(struct vo_wayland_state *wl, bool on)
return VO_TRUE;
}

static void set_geometry(struct vo_wayland_state *wl)
static void set_geometry(struct vo_wayland_state *wl, bool resize)
{
struct vo *vo = wl->vo;
assert(wl->current_output);

struct vo_win_geometry geo;
struct mp_rect screenrc = wl->current_output->geometry;
vo_calc_window_geometry(vo, &screenrc, &geo);
vo_calc_window_geometry2(vo, &screenrc, wl->scaling, &geo);
vo_apply_window_geometry(vo, &geo);

greatest_common_divisor(wl, vo->dwidth, vo->dheight);
Expand All @@ -1587,8 +1630,15 @@ static void set_geometry(struct vo_wayland_state *wl)

wl->vdparams.x0 = 0;
wl->vdparams.y0 = 0;
wl->vdparams.x1 = vo->dwidth / wl->scaling;
wl->vdparams.y1 = vo->dheight / wl->scaling;
wl->vdparams.x1 = round(vo->dwidth / wl->scaling);
wl->vdparams.y1 = round(vo->dheight / wl->scaling);

if (resize) {
wl->window_size = wl->vdparams;
if (!wl->vo_opts->fullscreen && !wl->vo_opts->window_maximized)
wl->geometry = wl->window_size;
wl->pending_vo_events |= VO_EVENT_RESIZE;
}
}

static int set_screensaver_inhibitor(struct vo_wayland_state *wl, int state)
Expand All @@ -1612,19 +1662,21 @@ static int set_screensaver_inhibitor(struct vo_wayland_state *wl, int state)
static void set_surface_scaling(struct vo_wayland_state *wl)
{
bool dmabuf_wayland = !strcmp(wl->vo->driver->name, "dmabuf-wayland");
int old_scale = wl->scaling;
if (wl->vo_opts->hidpi_window_scale && !dmabuf_wayland) {
double old_scale = wl->scaling;
if (wl->vo_opts->hidpi_window_scale && !dmabuf_wayland && !wl->fractional_scale_manager) {
wl->scaling = wl->current_output->scale;
} else {
wl->scaling = 1;
}

double factor = (double)old_scale / wl->scaling;
wl->vdparams.x1 *= factor;
wl->vdparams.y1 *= factor;
wl->window_size.x1 *= factor;
wl->window_size.y1 *= factor;
wl_surface_set_buffer_scale(wl->surface, wl->scaling);
if (!wl->fractional_scale_manager) {
double factor = old_scale / wl->scaling;
wl->vdparams.x1 *= factor;
wl->vdparams.y1 *= factor;
wl->window_size.x1 *= factor;
wl->window_size.y1 *= factor;
wl_surface_set_buffer_scale(wl->surface, wl->scaling);
}
}

static void set_window_bounds(struct vo_wayland_state *wl)
Expand Down Expand Up @@ -1845,13 +1897,8 @@ int vo_wayland_control(struct vo *vo, int *events, int request, void *arg)
if (opt == &opts->geometry || opt == &opts->autofit ||
opt == &opts->autofit_smaller || opt == &opts->autofit_larger)
{
if (wl->current_output) {
set_geometry(wl);
wl->window_size = wl->vdparams;
if (!wl->vo_opts->fullscreen && !wl->vo_opts->window_maximized)
wl->geometry = wl->window_size;
wl->pending_vo_events |= VO_EVENT_RESIZE;
}
if (wl->current_output)
set_geometry(wl, true);
}
}
return VO_TRUE;
Expand Down Expand Up @@ -1935,6 +1982,12 @@ int vo_wayland_control(struct vo *vo, int *events, int request, void *arg)
return VO_NOTIMPL;
}

void vo_wayland_handle_fractional_scale(struct vo_wayland_state *wl)
{
wp_viewport_set_destination(wl->fractional_viewport, mp_rect_w(wl->geometry),
mp_rect_h(wl->geometry));
}

int vo_wayland_init(struct vo *vo)
{
vo->wl = talloc_zero(NULL, struct vo_wayland_state);
Expand Down Expand Up @@ -1988,6 +2041,9 @@ int vo_wayland_init(struct vo *vo)
}

/* Can't be initialized during registry due to multi-protocol dependence */
if (create_viewports(wl))
return false;

if (create_xdg_surface(wl))
return false;

Expand All @@ -1996,11 +2052,6 @@ int vo_wayland_init(struct vo *vo)
wl_subsurface_set_desync(wl->video_subsurface);
}

if (wl->viewporter) {
wl->viewport = wp_viewporter_get_viewport(wl->viewporter, wl->surface);
wl->video_viewport = wp_viewporter_get_viewport(wl->viewporter, wl->video_surface);
}

const char *xdg_current_desktop = getenv("XDG_CURRENT_DESKTOP");
if (xdg_current_desktop != NULL && strstr(xdg_current_desktop, "GNOME"))
MP_WARN(wl, "GNOME's wayland compositor lacks support for the idle inhibit protocol. This means the screen can blank during playback.\n");
Expand All @@ -2019,6 +2070,15 @@ int vo_wayland_init(struct vo *vo)
}
#endif

//TODO: guard
if (wl->fractional_scale_manager) {
wl->fractional_scale = wp_fractional_scale_manager_v1_get_fractional_scale(wl->fractional_scale_manager, wl->surface);
wp_fractional_scale_v1_add_listener(wl->fractional_scale, &fractional_scale_listener, wl);
} else {
MP_VERBOSE(wl, "Compositor doesn't support the %s protocol!\n",
wp_fractional_scale_manager_v1_interface.name);
}

if (wl->dnd_devman && wl->seat) {
wl->dnd_ddev = wl_data_device_manager_get_data_device(wl->dnd_devman, wl->seat);
wl_data_device_add_listener(wl->dnd_ddev, &data_device_listener, wl);
Expand Down Expand Up @@ -2084,7 +2144,7 @@ int vo_wayland_reconfig(struct vo *vo)
wl->pending_vo_events |= VO_EVENT_DPI;
}

set_geometry(wl);
set_geometry(wl, false);
wl->window_size = wl->vdparams;

if (wl->opts->configure_bounds)
Expand Down Expand Up @@ -2194,6 +2254,13 @@ void vo_wayland_uninit(struct vo *vo)
if (wl->frame_callback)
wl_callback_destroy(wl->frame_callback);

//TODO: guard these
if (wl->fractional_scale)
wp_fractional_scale_v1_destroy(wl->fractional_scale);

if (wl->fractional_scale_manager)
wp_fractional_scale_manager_v1_destroy(wl->fractional_scale_manager);

if (wl->idle_inhibitor)
zwp_idle_inhibitor_v1_destroy(wl->idle_inhibitor);

Expand All @@ -2215,6 +2282,9 @@ void vo_wayland_uninit(struct vo *vo)
if (wl->viewporter)
wp_viewporter_destroy(wl->viewporter);

if (wl->fractional_viewport)
wp_viewport_destroy(wl->fractional_viewport);

if (wl->viewport)
wp_viewport_destroy(wl->viewport);

Expand Down
8 changes: 7 additions & 1 deletion video/out/wayland_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ struct vo_wayland_state {
int mouse_x;
int mouse_y;
int pending_vo_events;
int scaling;
double scaling;
int timeout_count;
int wakeup_pipe[2];

Expand All @@ -84,6 +84,10 @@ struct vo_wayland_state {
void *content_type;
int current_content_type;

/* fractional-scale */
void *fractional_scale_manager;
void *fractional_scale;

/* idle-inhibit */
struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager;
struct zwp_idle_inhibitor_v1 *idle_inhibitor;
Expand Down Expand Up @@ -122,6 +126,7 @@ struct vo_wayland_state {

/* viewporter */
struct wp_viewporter *viewporter;
struct wp_viewport *fractional_viewport;
struct wp_viewport *viewport;
struct wp_viewport *video_viewport;

Expand Down Expand Up @@ -161,6 +166,7 @@ int vo_wayland_control(struct vo *vo, int *events, int request, void *arg);
int vo_wayland_init(struct vo *vo);
int vo_wayland_reconfig(struct vo *vo);

void vo_wayland_handle_fractional_scale(struct vo_wayland_state *wl);
void vo_wayland_set_opaque_region(struct vo_wayland_state *wl, int alpha);
void vo_wayland_sync_swap(struct vo_wayland_state *wl);
void vo_wayland_uninit(struct vo *vo);
Expand Down

0 comments on commit c4ed894

Please sign in to comment.