Implement output scaling aware rendering

output-scaling
Andri Yngvason 2022-08-01 22:12:19 +00:00
parent 9ef4184e87
commit cbd5544ff1
5 changed files with 227 additions and 16 deletions

View File

@ -34,6 +34,7 @@ struct buffer {
enum buffer_type type; enum buffer_type type;
int width, height; int width, height;
int32_t scale;
size_t size; size_t size;
uint32_t format; uint32_t format;
struct wl_buffer* wl_buffer; struct wl_buffer* wl_buffer;

36
include/output.h 100644
View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2019 - 2022 Andri Yngvason
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include <wayland-client.h>
#include <stdint.h>
struct output {
struct wl_output* wl_output;
struct wl_list link;
uint32_t id;
int32_t scale;
};
struct output* output_new(struct wl_output* wl_output, uint32_t id);
void output_destroy(struct output* output);
void output_list_destroy(struct wl_list* list);
struct output* output_find_by_id(struct wl_list* list, uint32_t id);
int32_t output_list_get_max_scale(const struct wl_list* list);

View File

@ -65,6 +65,7 @@ sources = [
'src/main.c', 'src/main.c',
'src/shm.c', 'src/shm.c',
'src/seat.c', 'src/seat.c',
'src/output.c',
'src/pointer.c', 'src/pointer.c',
'src/keyboard.c', 'src/keyboard.c',
'src/vnc.c', 'src/vnc.c',

View File

@ -46,10 +46,15 @@
#include "renderer-egl.h" #include "renderer-egl.h"
#include "linux-dmabuf-unstable-v1.h" #include "linux-dmabuf-unstable-v1.h"
#include "time-util.h" #include "time-util.h"
#include "output.h"
#define CANARY_TICK_PERIOD INT64_C(100000) // us #define CANARY_TICK_PERIOD INT64_C(100000) // us
#define CANARY_LETHALITY_LEVEL INT64_C(8000) // us #define CANARY_LETHALITY_LEVEL INT64_C(8000) // us
struct point {
double x, y;
};
struct window { struct window {
struct wl_surface* wl_surface; struct wl_surface* wl_surface;
struct xdg_surface* xdg_surface; struct xdg_surface* xdg_surface;
@ -77,6 +82,7 @@ struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf_v1 = NULL;
struct gbm_device* gbm_device = NULL; struct gbm_device* gbm_device = NULL;
static struct xdg_wm_base* xdg_wm_base; static struct xdg_wm_base* xdg_wm_base;
static struct wl_list seats; static struct wl_list seats;
static struct wl_list outputs;
struct pointer_collection* pointers; struct pointer_collection* pointers;
struct keyboard_collection* keyboards; struct keyboard_collection* keyboards;
static int drm_fd = -1; static int drm_fd = -1;
@ -138,6 +144,17 @@ static void registry_add(void* data, struct wl_registry* registry, uint32_t id,
wl_list_insert(&seats, &seat->link); wl_list_insert(&seats, &seat->link);
seat->on_capability_change = on_seat_capability_change; seat->on_capability_change = on_seat_capability_change;
} else if (strcmp(interface, "wl_output") == 0) {
struct wl_output* wl_output;
wl_output = wl_registry_bind(registry, id, &wl_output_interface, 2);
struct output* output = output_new(wl_output, id);
if (!output) {
wl_output_destroy(wl_output);
return;
}
wl_list_insert(&outputs, &output->link);
} }
} }
@ -259,6 +276,32 @@ static void window_attach(struct window* w, int x, int y)
{ {
w->back_buffer->is_attached = true; w->back_buffer->is_attached = true;
wl_surface_attach(w->wl_surface, w->back_buffer->wl_buffer, x, y); wl_surface_attach(w->wl_surface, w->back_buffer->wl_buffer, x, y);
wl_surface_set_buffer_scale(window->wl_surface,
window->back_buffer->scale);
}
static struct point surface_coord_to_buffer_coord(double x, double y)
{
double scale = output_list_get_max_scale(&outputs);
struct point result = {
.x = round(x * scale),
.y = round(y * scale),
};
return result;
}
static struct point buffer_coord_to_surface_coord(double x, double y)
{
double scale = output_list_get_max_scale(&outputs);
struct point result = {
.x = x / scale,
.y = y / scale,
};
return result;
} }
static void window_calculate_transform(struct window* w, double* scale, static void window_calculate_transform(struct window* w, double* scale,
@ -348,22 +391,27 @@ static const struct xdg_surface_listener xdg_surface_listener = {
.configure = xdg_surface_configure, .configure = xdg_surface_configure,
}; };
static void window_resize(struct window* w, int width, int height) static void window_resize(struct window* w, int width, int height, int scale)
{ {
if (width == 0 || height == 0) if (width == 0 || height == 0 || scale == 0)
return; return;
if (w->back_buffer && w->back_buffer->width == width && if (w->back_buffer && w->back_buffer->width == width &&
w->back_buffer->height == height) w->back_buffer->height == height &&
w->back_buffer->scale == scale)
return; return;
for (int i = 0; i < 3; ++i) for (int i = 0; i < 3; ++i)
buffer_destroy(w->buffers[i]); buffer_destroy(w->buffers[i]);
for (int i = 0; i < 3; ++i) for (int i = 0; i < 3; ++i) {
w->buffers[i] = have_egl ? w->buffers[i] = have_egl
buffer_create_dmabuf(width, height, dmabuf_format) : ? buffer_create_dmabuf(scale * width, scale * height,
buffer_create_shm(width, height, 4 * width, shm_format); dmabuf_format)
: buffer_create_shm(scale * width, scale * height,
scale * 4 * width, shm_format);
w->buffers[i]->scale = scale;
}
w->back_buffer = w->buffers[0]; w->back_buffer = w->buffers[0];
} }
@ -371,7 +419,8 @@ static void window_resize(struct window* w, int width, int height)
static void xdg_toplevel_configure(void* data, struct xdg_toplevel* toplevel, static void xdg_toplevel_configure(void* data, struct xdg_toplevel* toplevel,
int32_t width, int32_t height, struct wl_array* state) int32_t width, int32_t height, struct wl_array* state)
{ {
window_resize(data, width, height); int32_t scale = output_list_get_max_scale(&outputs);
window_resize(data, width, height, scale);
} }
static void xdg_toplevel_close(void* data, struct xdg_toplevel* toplevel) static void xdg_toplevel_close(void* data, struct xdg_toplevel* toplevel)
@ -445,8 +494,12 @@ void on_pointer_event(struct pointer_collection* collection,
int x_pos, y_pos; int x_pos, y_pos;
window_calculate_transform(window, &scale, &x_pos, &y_pos); window_calculate_transform(window, &scale, &x_pos, &y_pos);
int x = round((wl_fixed_to_double(pointer->x) - (double)x_pos) / scale); struct point coord = surface_coord_to_buffer_coord(
int y = round((wl_fixed_to_double(pointer->y) - (double)y_pos) / scale); wl_fixed_to_double(pointer->x),
wl_fixed_to_double(pointer->y));
int x = round((coord.x - (double)x_pos) / scale);
int y = round((coord.y - (double)y_pos) / scale);
enum pointer_button_mask pressed = pointer->pressed; enum pointer_button_mask pressed = pointer->pressed;
int vertical_steps = pointer->vertical_scroll_steps; int vertical_steps = pointer->vertical_scroll_steps;
@ -506,7 +559,9 @@ int on_vnc_client_alloc_fb(struct vnc_client* client)
if (!window) { if (!window) {
window = window_create(app_id, vnc_client_get_desktop_name(client)); window = window_create(app_id, vnc_client_get_desktop_name(client));
window->vnc = client; window->vnc = client;
window_resize(window, width, height);
int32_t scale = output_list_get_max_scale(&outputs);
window_resize(window, width, height, scale);
} }
free(window->vnc_fb); free(window->vnc_fb);
@ -571,16 +626,24 @@ static void render_from_vnc(void)
int x_pos, y_pos; int x_pos, y_pos;
window_calculate_transform(window, &scale, &x_pos, &y_pos); window_calculate_transform(window, &scale, &x_pos, &y_pos);
struct pixman_region16 damage_scaled = { 0 }, damage = { 0 }; struct pixman_region16 damage_scaled = { 0 }, buffer_damage = { 0 },
surface_damage = { 0 };
region_scale(&damage_scaled, &window->current_damage, scale); region_scale(&damage_scaled, &window->current_damage, scale);
region_translate(&buffer_damage, &damage_scaled, x_pos, y_pos);
pixman_region_clear(&damage_scaled);
region_translate(&damage, &damage_scaled, x_pos, y_pos); double output_scale = output_list_get_max_scale(&outputs);
struct point scoord = buffer_coord_to_surface_coord(x_pos, y_pos);
region_scale(&damage_scaled, &window->current_damage,
scale / output_scale);
region_translate(&surface_damage, &damage_scaled, scoord.x, scoord.y);
pixman_region_fini(&damage_scaled); pixman_region_fini(&damage_scaled);
apply_buffer_damage(&damage); apply_buffer_damage(&buffer_damage);
window_damage_region(window, &damage); window_damage_region(window, &surface_damage);
pixman_region_fini(&damage); pixman_region_fini(&surface_damage);
pixman_region_fini(&buffer_damage);
window_transfer_pixels(window); window_transfer_pixels(window);
@ -877,6 +940,7 @@ int main(int argc, char* argv[])
goto registry_failure; goto registry_failure;
wl_list_init(&seats); wl_list_init(&seats);
wl_list_init(&outputs);
wl_registry_add_listener(wl_registry, &registry_listener, wl_display); wl_registry_add_listener(wl_registry, &registry_listener, wl_display);
wl_display_roundtrip(wl_display); wl_display_roundtrip(wl_display);
@ -955,6 +1019,7 @@ int main(int argc, char* argv[])
vnc_setup_failure: vnc_setup_failure:
vnc_client_destroy(vnc); vnc_client_destroy(vnc);
vnc_failure: vnc_failure:
output_list_destroy(&outputs);
seat_list_destroy(&seats); seat_list_destroy(&seats);
wl_compositor_destroy(wl_compositor); wl_compositor_destroy(wl_compositor);
wl_shm_destroy(wl_shm); wl_shm_destroy(wl_shm);

108
src/output.c 100644
View File

@ -0,0 +1,108 @@
/*
* Copyright (c) 2019 - 2022 Andri Yngvason
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdint.h>
#include <unistd.h>
#include <stdlib.h>
#include <wayland-client.h>
#include "output.h"
static void output_handle_geometry(void* data, struct wl_output* wl_output,
int32_t x, int32_t y, int32_t phys_width, int32_t phys_height,
int32_t subpixel, const char* make, const char* model,
int32_t transform)
{
}
static void output_handle_mode(void* data, struct wl_output* wl_output,
uint32_t flags, int32_t width, int32_t height, int32_t refresh)
{
}
static void output_handle_done(void* data, struct wl_output* wl_output)
{
}
static void output_handle_scale(void* data, struct wl_output* wl_output,
int32_t factor)
{
struct output* output = data;
output->scale = factor;
}
static const struct wl_output_listener output_listener = {
.geometry = output_handle_geometry,
.mode = output_handle_mode,
.done = output_handle_done,
.scale = output_handle_scale,
};
void output_destroy(struct output* output)
{
wl_output_destroy(output->wl_output);
free(output);
}
void output_list_destroy(struct wl_list* list)
{
struct output* output;
struct output* tmp;
wl_list_for_each_safe(output, tmp, list, link) {
wl_list_remove(&output->link);
output_destroy(output);
}
}
int32_t output_list_get_max_scale(const struct wl_list* list)
{
struct output* output;
int32_t scale = 1;
wl_list_for_each(output, list, link)
if (output->scale > scale)
scale = output->scale;
return scale;
}
struct output* output_new(struct wl_output* wl_output, uint32_t id)
{
struct output* output = calloc(1, sizeof(*output));
if (!output)
return NULL;
output->wl_output = wl_output;
output->id = id;
wl_output_add_listener(output->wl_output, &output_listener,
output);
return output;
}
struct output* output_find_by_id(struct wl_list* list, uint32_t id)
{
struct output* output;
wl_list_for_each(output, list, link)
if (output->id == id)
return output;
return NULL;
}