From 667f51ecc8273c1ef72260a1cf19dc97d113e624 Mon Sep 17 00:00:00 2001 From: Jeffy Chen Date: Wed, 24 Jun 2020 11:59:42 +0800 Subject: [PATCH 14/74] backend-drm: Support virtual screen size Support setting virtual screen size, for example: export WESTON_DRM_VIRTUAL_SIZE=1024x768 Signed-off-by: Jeffy Chen --- libweston/backend-drm/drm-internal.h | 23 ++++++ libweston/backend-drm/drm.c | 28 ++++++- libweston/backend-drm/kms.c | 115 ++++++++++++++++++++++----- libweston/backend-drm/modes.c | 22 ++++- 4 files changed, 162 insertions(+), 26 deletions(-) diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index 3f42a25..996f587 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -166,6 +166,7 @@ enum wdrm_plane_property { WDRM_PLANE_IN_FENCE_FD, WDRM_PLANE_FB_DAMAGE_CLIPS, WDRM_PLANE_ZPOS, + WDRM_PLANE_FEATURE, WDRM_PLANE__COUNT }; @@ -179,6 +180,15 @@ enum wdrm_plane_type { WDRM_PLANE_TYPE__COUNT }; +/** + * Possible values for the WDRM_PLANE_FEATURE property. + */ +enum wdrm_plane_feature { + WDRM_PLANE_FEATURE_SCALE = 0, + WDRM_PLANE_FEATURE_ALPHA, + WDRM_PLANE_FEATURE__COUNT +}; + /** * List of properties attached to a DRM connector */ @@ -325,6 +335,7 @@ struct drm_backend { bool pending_update; int64_t last_update_ms; int64_t last_resize_ms; + int64_t resize_freeze_ms; bool single_head; bool head_fallback; @@ -332,6 +343,9 @@ struct drm_backend { drm_head_match_t *head_matches; struct drm_head *primary_head; struct wl_listener output_create_listener; + + int virtual_width; + int virtual_height; }; struct drm_mode { @@ -501,6 +515,8 @@ struct drm_plane { struct wl_list link; struct weston_drm_format_array formats; + + bool can_scale; }; struct drm_connector { @@ -597,6 +613,9 @@ struct drm_output { submit_frame_cb virtual_submit_frame; bool state_invalid; + + /* The dummy framebuffer for SET_CRTC. */ + struct drm_fb *fb_dummy; }; static inline struct drm_head * @@ -694,6 +713,10 @@ uint64_t drm_property_get_value(struct drm_property_info *info, const drmModeObjectProperties *props, uint64_t def); +bool +drm_property_has_feature(struct drm_property_info *infos, + const drmModeObjectProperties *props, + enum wdrm_plane_feature feature); uint64_t * drm_property_get_range_values(struct drm_property_info *info, const drmModeObjectProperties *props); diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index 4e00933..1c839c5 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -324,6 +324,11 @@ drm_output_update_complete(struct drm_output *output, uint32_t flags, output->state_last = NULL; } + if (output->fb_dummy) { + drm_fb_unref(output->fb_dummy); + output->fb_dummy = NULL; + } + if (output->destroy_pending) { output->destroy_pending = false; output->disable_pending = false; @@ -397,6 +402,7 @@ drm_output_render(struct drm_output_state *state, pixman_region32_t *damage) struct drm_property_info *damage_info = &scanout_plane->props[WDRM_PLANE_FB_DAMAGE_CLIPS]; struct drm_backend *b = to_drm_backend(c); + struct drm_mode *mode; struct drm_fb *fb; pixman_region32_t scanout_damage; pixman_box32_t *rects; @@ -440,10 +446,11 @@ drm_output_render(struct drm_output_state *state, pixman_region32_t *damage) scanout_state->src_w = fb->width << 16; scanout_state->src_h = fb->height << 16; + mode = to_drm_mode(output->base.current_mode); scanout_state->dest_x = 0; scanout_state->dest_y = 0; - scanout_state->dest_w = output->base.current_mode->width; - scanout_state->dest_h = output->base.current_mode->height; + scanout_state->dest_w = mode->mode_info.hdisplay; + scanout_state->dest_h = mode->mode_info.vdisplay; pixman_region32_subtract(&c->primary_plane.damage, &c->primary_plane.damage, damage); @@ -516,7 +523,7 @@ drm_output_repaint(struct weston_output *output_base, weston_compositor_read_presentation_clock(b->compositor, &now); now_ms = timespec_to_msec(&now); - if (now_ms < b->last_resize_ms + DRM_RESIZE_FREEZE_MS) { + if (now_ms < b->last_resize_ms + b->resize_freeze_ms) { /* Resize fullscreen/maxmium views(not always success) */ if (now_ms < b->last_resize_ms + DRM_RESIZE_FREEZE_MS) wl_signal_emit(&b->compositor->output_resized_signal, @@ -852,6 +859,11 @@ drm_plane_create(struct drm_backend *b, const drmModePlane *kplane) props, WDRM_PLANE_TYPE__COUNT); + plane->can_scale = + drm_property_has_feature(plane->props, + props, + WDRM_PLANE_FEATURE_SCALE); + zpos_range_values = drm_property_get_range_values(&plane->props[WDRM_PLANE_ZPOS], props); @@ -3298,6 +3310,16 @@ drm_backend_create(struct weston_compositor *compositor, b->head_matches = drm_head_matches[head_mode]; + buf = getenv("WESTON_DRM_VIRTUAL_SIZE"); + if (buf) + sscanf(buf, "%dx%d", &b->virtual_width, &b->virtual_height); + + buf = getenv("WESTON_DRM_RESIZE_FREEZE_MS"); + if (buf) + b->resize_freeze_ms = atoi(buf); + else + b->resize_freeze_ms = DRM_RESIZE_FREEZE_MS; + b->state_invalid = true; b->drm.fd = -1; diff --git a/libweston/backend-drm/kms.c b/libweston/backend-drm/kms.c index 4426af4..1fcbdeb 100644 --- a/libweston/backend-drm/kms.c +++ b/libweston/backend-drm/kms.c @@ -56,6 +56,15 @@ struct drm_property_enum_info plane_type_enums[] = { }, }; +struct drm_property_enum_info plane_feature_enums[] = { + [WDRM_PLANE_FEATURE_SCALE] = { + .name = "scale", + }, + [WDRM_PLANE_FEATURE_ALPHA] = { + .name = "alpha", + }, +}; + const struct drm_property_info plane_props[] = { [WDRM_PLANE_TYPE] = { .name = "type", @@ -76,6 +85,11 @@ const struct drm_property_info plane_props[] = { [WDRM_PLANE_IN_FENCE_FD] = { .name = "IN_FENCE_FD" }, [WDRM_PLANE_FB_DAMAGE_CLIPS] = { .name = "FB_DAMAGE_CLIPS" }, [WDRM_PLANE_ZPOS] = { .name = "zpos" }, + [WDRM_PLANE_FEATURE] = { + .name = "FEATURE", + .enum_values = plane_feature_enums, + .num_enum_values = WDRM_PLANE_FEATURE__COUNT, + }, }; struct drm_property_enum_info dpms_state_enums[] = { @@ -213,6 +227,31 @@ drm_property_get_value(struct drm_property_info *info, return def; } +bool +drm_property_has_feature(struct drm_property_info *infos, + const drmModeObjectProperties *props, + enum wdrm_plane_feature feature) +{ + struct drm_property_info *info = &infos[WDRM_PLANE_FEATURE]; + unsigned int i; + + if (info->prop_id == 0 || + feature >= info->num_enum_values || + !info->enum_values[feature].valid) + return false; + + for (i = 0; i < props->count_props; i++) { + if (props->props[i] != info->prop_id) + continue; + + if (props->prop_values[i] & + (1LL << info->enum_values[feature].value)) + return true; + } + + return false; +} + /** * Get the current range values of a KMS property * @@ -333,9 +372,11 @@ drm_property_info_populate(struct drm_backend *b, } if (info[j].num_enum_values == 0 && - (prop->flags & DRM_MODE_PROP_ENUM)) { + (prop->flags & DRM_MODE_PROP_ENUM || + prop->flags & DRM_MODE_PROP_BITMASK)) { weston_log("DRM: expected property %s to not be an" - " enum, but it is; ignoring\n", prop->name); + " enum or bitmask, but it is; ignoring\n", + prop->name); drmModeFreeProperty(prop); continue; } @@ -356,9 +397,11 @@ drm_property_info_populate(struct drm_backend *b, continue; } - if (!(prop->flags & DRM_MODE_PROP_ENUM)) { - weston_log("DRM: expected property %s to be an enum," - " but it is not; ignoring\n", prop->name); + if (!(prop->flags & DRM_MODE_PROP_ENUM || + prop->flags & DRM_MODE_PROP_BITMASK)) { + weston_log("DRM: expected property %s to be an enum or " + "bitmask, but it is not; ignoring\n", + prop->name); drmModeFreeProperty(prop); info[j].prop_id = 0; continue; @@ -660,6 +703,7 @@ drm_output_apply_state_legacy(struct drm_output_state *state) int n_conn = 0; struct timespec now; int ret = 0; + bool scaling; wl_list_for_each(head, &output->base.head_list, base.output_link) { assert(n_conn < MAX_CLONED_CONNECTORS); @@ -707,30 +751,36 @@ drm_output_apply_state_legacy(struct drm_output_state *state) if (!scanout_state || !scanout_state->fb) return 0; - /* The legacy SetCrtc API doesn't allow us to do scaling, and the - * legacy PageFlip API doesn't allow us to do clipping either. */ - assert(scanout_state->src_x == 0); - assert(scanout_state->src_y == 0); - assert(scanout_state->src_w == - (unsigned) (output->base.current_mode->width << 16)); - assert(scanout_state->src_h == - (unsigned) (output->base.current_mode->height << 16)); - assert(scanout_state->dest_x == 0); - assert(scanout_state->dest_y == 0); - assert(scanout_state->dest_w == scanout_state->src_w >> 16); - assert(scanout_state->dest_h == scanout_state->src_h >> 16); /* The legacy SetCrtc API doesn't support fences */ assert(scanout_state->in_fence_fd == -1); mode = to_drm_mode(output->base.current_mode); + + scaling = scanout_state->src_w >> 16 != scanout_state->dest_w || + scanout_state->src_h >> 16 != scanout_state->dest_h; + if (output->state_invalid || - !scanout_plane->state_cur->fb || - scanout_plane->state_cur->fb->strides[0] != - scanout_state->fb->strides[0]) { + !scanout_plane->state_cur->fb) { + int fb_id = scanout_state->fb->fb_id; + + /* Use a dummy fb for initial mode setting */ + if (!output->fb_dummy) { + output->fb_dummy = + drm_fb_create_dumb(backend, + mode->mode_info.hdisplay, + mode->mode_info.vdisplay, + output->gbm_format); + if (!output->fb_dummy) { + weston_log("failed to create fb_dummy\n"); + goto err; + } + } + + if (n_conn == 1 || scaling) + fb_id = output->fb_dummy->fb_id; ret = drmModeSetCrtc(backend->drm.fd, crtc->crtc_id, - scanout_state->fb->fb_id, - 0, 0, + fb_id, 0, 0, connectors, n_conn, &mode->mode_info); if (ret) { @@ -741,6 +791,27 @@ drm_output_apply_state_legacy(struct drm_output_state *state) output->state_invalid = false; } + if (scaling && !output->scanout_plane->can_scale) { + weston_log("Couldn't do scaling on output %s\n", + output->base.name); + weston_output_finish_frame(&output->base, NULL, + WP_PRESENTATION_FEEDBACK_INVALID); + return 0; + } + + ret = drmModeSetPlane(backend->drm.fd, + scanout_state->plane->plane_id, + crtc->crtc_id, + scanout_state->fb->fb_id, 0, + scanout_state->dest_x, scanout_state->dest_y, + scanout_state->dest_w, scanout_state->dest_h, + scanout_state->src_x, scanout_state->src_y, + scanout_state->src_w, scanout_state->src_h); + if (ret) { + weston_log("set plane failed: %s\n", strerror(errno)); + goto err; + } + pinfo = scanout_state->fb->format; drm_debug(backend, "\t[CRTC:%u, PLANE:%u] FORMAT: %s\n", crtc->crtc_id, scanout_state->plane->plane_id, diff --git a/libweston/backend-drm/modes.c b/libweston/backend-drm/modes.c index a071375..e7bbfa5 100644 --- a/libweston/backend-drm/modes.c +++ b/libweston/backend-drm/modes.c @@ -378,6 +378,7 @@ drm_refresh_rate_mHz(const drmModeModeInfo *info) static struct drm_mode * drm_output_add_mode(struct drm_output *output, const drmModeModeInfo *info) { + struct drm_backend *b = to_drm_backend(output->base.compositor); struct drm_mode *mode; mode = malloc(sizeof *mode); @@ -388,6 +389,11 @@ drm_output_add_mode(struct drm_output *output, const drmModeModeInfo *info) mode->base.width = info->hdisplay; mode->base.height = info->vdisplay; + if (b->virtual_width && b->virtual_height) { + mode->base.width = b->virtual_width; + mode->base.height = b->virtual_height; + } + mode->base.refresh = drm_refresh_rate_mHz(info); mode->mode_info = *info; mode->blob_id = 0; @@ -434,20 +440,34 @@ drm_output_print_modes(struct drm_output *output) struct weston_mode *m; struct drm_mode *dm; const char *aspect_ratio; + bool virtual_size = false; wl_list_for_each(m, &output->base.mode_list, link) { dm = to_drm_mode(m); aspect_ratio = aspect_ratio_to_string(m->aspect_ratio); weston_log_continue(STAMP_SPACE "%dx%d@%.1f%s%s%s, %.1f MHz\n", - m->width, m->height, m->refresh / 1000.0, + dm->mode_info.hdisplay, + dm->mode_info.vdisplay, + m->refresh / 1000.0, aspect_ratio, m->flags & WL_OUTPUT_MODE_PREFERRED ? ", preferred" : "", m->flags & WL_OUTPUT_MODE_CURRENT ? ", current" : "", dm->mode_info.clock / 1000.0); + + if(m->flags & WL_OUTPUT_MODE_CURRENT && + (dm->mode_info.hdisplay != m->width || + dm->mode_info.vdisplay != m->height)) + virtual_size = true; } + + if (virtual_size) + weston_log("Output %s: using virtual size %dx%d\n", + output->base.name, + output->base.current_mode->width, + output->base.current_mode->height); } -- 2.20.1