backend/drm: Simplify object matching code
We originally used match_obj on planes, but this was largely unnecessary. Instead, this assigns planes statically at startup.
This commit is contained in:
		
							parent
							
								
									d80acadfd8
								
							
						
					
					
						commit
						b3f42548d0
					
				|  | @ -1,4 +1,4 @@ | ||||||
| #define _POSIX_C_SOURCE 200112L | #define _XOPEN_SOURCE 700 | ||||||
| #include <assert.h> | #include <assert.h> | ||||||
| #include <drm_fourcc.h> | #include <drm_fourcc.h> | ||||||
| #include <drm_mode.h> | #include <drm_mode.h> | ||||||
|  | @ -10,6 +10,7 @@ | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
|  | #include <strings.h> | ||||||
| #include <time.h> | #include <time.h> | ||||||
| #include <wayland-server.h> | #include <wayland-server.h> | ||||||
| #include <wayland-util.h> | #include <wayland-util.h> | ||||||
|  | @ -72,60 +73,29 @@ bool check_drm_features(struct wlr_drm_backend *drm) { | ||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int cmp_plane(const void *arg1, const void *arg2) { | static bool add_plane(struct wlr_drm_backend *drm, | ||||||
| 	const struct wlr_drm_plane *a = arg1; | 		struct wlr_drm_crtc *crtc, drmModePlane *drm_plane, | ||||||
| 	const struct wlr_drm_plane *b = arg2; | 		uint32_t type, union wlr_drm_plane_props *props) { | ||||||
|  | 	assert(!(type == DRM_PLANE_TYPE_PRIMARY && crtc->primary)); | ||||||
| 
 | 
 | ||||||
| 	return (int)a->type - (int)b->type; | 	if (type == DRM_PLANE_TYPE_CURSOR && crtc->cursor) { | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static bool init_planes(struct wlr_drm_backend *drm) { |  | ||||||
| 	drmModePlaneRes *plane_res = drmModeGetPlaneResources(drm->fd); |  | ||||||
| 	if (!plane_res) { |  | ||||||
| 		wlr_log_errno(WLR_ERROR, "Failed to get DRM plane resources"); |  | ||||||
| 		return false; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	wlr_log(WLR_INFO, "Found %"PRIu32" DRM planes", plane_res->count_planes); |  | ||||||
| 
 |  | ||||||
| 	if (plane_res->count_planes == 0) { |  | ||||||
| 		drmModeFreePlaneResources(plane_res); |  | ||||||
| 		return true; | 		return true; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	drm->num_planes = plane_res->count_planes; | 	struct wlr_drm_plane *p = calloc(1, sizeof(*p)); | ||||||
| 	drm->planes = calloc(drm->num_planes, sizeof(*drm->planes)); | 	if (!p) { | ||||||
| 	if (!drm->planes) { |  | ||||||
| 		wlr_log_errno(WLR_ERROR, "Allocation failed"); | 		wlr_log_errno(WLR_ERROR, "Allocation failed"); | ||||||
| 		goto error_res; | 		return false; | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	for (size_t i = 0; i < drm->num_planes; ++i) { |  | ||||||
| 		struct wlr_drm_plane *p = &drm->planes[i]; |  | ||||||
| 
 |  | ||||||
| 		drmModePlane *plane = drmModeGetPlane(drm->fd, plane_res->planes[i]); |  | ||||||
| 		if (!plane) { |  | ||||||
| 			wlr_log_errno(WLR_ERROR, "Failed to get DRM plane"); |  | ||||||
| 			goto error_planes; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		p->id = plane->plane_id; |  | ||||||
| 		p->possible_crtcs = plane->possible_crtcs; |  | ||||||
| 
 |  | ||||||
| 		uint64_t type; |  | ||||||
| 		if (!get_drm_plane_props(drm->fd, p->id, &p->props) || |  | ||||||
| 				!get_drm_prop(drm->fd, p->id, p->props.type, &type)) { |  | ||||||
| 			drmModeFreePlane(plane); |  | ||||||
| 			goto error_planes; |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	p->type = type; | 	p->type = type; | ||||||
| 		drm->num_type_planes[type]++; | 	p->id = drm_plane->plane_id; | ||||||
|  | 	p->props = *props; | ||||||
| 
 | 
 | ||||||
| 	// Choose an RGB format for the plane
 | 	// Choose an RGB format for the plane
 | ||||||
| 	uint32_t rgb_format = DRM_FORMAT_INVALID; | 	uint32_t rgb_format = DRM_FORMAT_INVALID; | ||||||
| 		for (size_t j = 0; j < plane->count_formats; ++j) { | 	for (size_t j = 0; j < drm_plane->count_formats; ++j) { | ||||||
| 			uint32_t fmt = plane->formats[j]; | 		uint32_t fmt = drm_plane->formats[j]; | ||||||
| 		wlr_drm_format_set_add(&p->formats, fmt, DRM_FORMAT_MOD_INVALID); | 		wlr_drm_format_set_add(&p->formats, fmt, DRM_FORMAT_MOD_INVALID); | ||||||
| 
 | 
 | ||||||
| 		if (fmt == DRM_FORMAT_ARGB8888) { | 		if (fmt == DRM_FORMAT_ARGB8888) { | ||||||
|  | @ -136,30 +106,19 @@ static bool init_planes(struct wlr_drm_backend *drm) { | ||||||
| 			rgb_format = fmt; | 			rgb_format = fmt; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 		// Some overlays exist which don't support XRGB8888/ARGB8888
 |  | ||||||
| 		// We aren't even using overlay planes currently, so don't fail
 |  | ||||||
| 		// on something unnecessary.
 |  | ||||||
| 		if (type != DRM_PLANE_TYPE_OVERLAY && rgb_format == DRM_FORMAT_INVALID) { |  | ||||||
| 			wlr_log(WLR_ERROR, "Failed to find an RGB format for plane %zu", i); |  | ||||||
| 			drmModeFreePlane(plane); |  | ||||||
| 			goto error_planes; |  | ||||||
| 		} |  | ||||||
| 	p->drm_format = rgb_format; | 	p->drm_format = rgb_format; | ||||||
| 
 | 
 | ||||||
| 	if (p->props.in_formats) { | 	if (p->props.in_formats) { | ||||||
| 		uint64_t blob_id; | 		uint64_t blob_id; | ||||||
| 		if (!get_drm_prop(drm->fd, p->id, p->props.in_formats, &blob_id)) { | 		if (!get_drm_prop(drm->fd, p->id, p->props.in_formats, &blob_id)) { | ||||||
| 			wlr_log(WLR_ERROR, "Failed to read IN_FORMATS property"); | 			wlr_log(WLR_ERROR, "Failed to read IN_FORMATS property"); | ||||||
| 				drmModeFreePlane(plane); | 			goto error; | ||||||
| 				goto error_planes; |  | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 			drmModePropertyBlobRes *blob = | 		drmModePropertyBlobRes *blob = drmModeGetPropertyBlob(drm->fd, blob_id); | ||||||
| 				drmModeGetPropertyBlob(drm->fd, blob_id); |  | ||||||
| 		if (!blob) { | 		if (!blob) { | ||||||
| 			wlr_log(WLR_ERROR, "Failed to read IN_FORMATS blob"); | 			wlr_log(WLR_ERROR, "Failed to read IN_FORMATS blob"); | ||||||
| 				drmModeFreePlane(plane); | 			goto error; | ||||||
| 				goto error_planes; |  | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		struct drm_format_modifier_blob *data = blob->data; | 		struct drm_format_modifier_blob *data = blob->data; | ||||||
|  | @ -178,28 +137,99 @@ static bool init_planes(struct wlr_drm_backend *drm) { | ||||||
| 		drmModeFreePropertyBlob(blob); | 		drmModeFreePropertyBlob(blob); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 		drmModeFreePlane(plane); | 	switch (type) { | ||||||
|  | 	case DRM_PLANE_TYPE_PRIMARY: | ||||||
|  | 		crtc->primary = p; | ||||||
|  | 		break; | ||||||
|  | 	case DRM_PLANE_TYPE_CURSOR: | ||||||
|  | 		crtc->cursor = p; | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		abort(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	wlr_log(WLR_INFO, "(%zu overlay, %zu primary, %zu cursor)", | 	return true; | ||||||
| 		drm->num_overlay_planes, |  | ||||||
| 		drm->num_primary_planes, |  | ||||||
| 		drm->num_cursor_planes); |  | ||||||
| 
 | 
 | ||||||
| 	qsort(drm->planes, drm->num_planes, sizeof(*drm->planes), cmp_plane); | error: | ||||||
|  | 	free(p); | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| 	drm->overlay_planes = drm->planes; | static bool init_planes(struct wlr_drm_backend *drm) { | ||||||
| 	drm->primary_planes = drm->overlay_planes | 	drmModePlaneRes *plane_res = drmModeGetPlaneResources(drm->fd); | ||||||
| 		+ drm->num_overlay_planes; | 	if (!plane_res) { | ||||||
| 	drm->cursor_planes = drm->primary_planes | 		wlr_log_errno(WLR_ERROR, "Failed to get DRM plane resources"); | ||||||
| 		+ drm->num_primary_planes; | 		return false; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	wlr_log(WLR_INFO, "Found %"PRIu32" DRM planes", plane_res->count_planes); | ||||||
|  | 
 | ||||||
|  | 	for (uint32_t i = 0; i < plane_res->count_planes; ++i) { | ||||||
|  | 		uint32_t id = plane_res->planes[i]; | ||||||
|  | 
 | ||||||
|  | 		drmModePlane *plane = drmModeGetPlane(drm->fd, id); | ||||||
|  | 		if (!plane) { | ||||||
|  | 			wlr_log_errno(WLR_ERROR, "Failed to get DRM plane"); | ||||||
|  | 			goto error; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		union wlr_drm_plane_props props = {0}; | ||||||
|  | 		if (!get_drm_plane_props(drm->fd, id, &props)) { | ||||||
|  | 			drmModeFreePlane(plane); | ||||||
|  | 			goto error; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		uint64_t type; | ||||||
|  | 		if (!get_drm_prop(drm->fd, id, props.type, &type)) { | ||||||
|  | 			drmModeFreePlane(plane); | ||||||
|  | 			goto error; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		/*
 | ||||||
|  | 		 * This is a very naive implementation of the plane matching | ||||||
|  | 		 * logic. Primary and cursor planes should only work on a | ||||||
|  | 		 * single CRTC, and this should be perfectly adequate, but | ||||||
|  | 		 * overlay planes can potentially work with multiple CRTCs, | ||||||
|  | 		 * meaning this could return inefficent/skewed results. | ||||||
|  | 		 * | ||||||
|  | 		 * However, we don't really care about overlay planes, as we | ||||||
|  | 		 * don't support them yet. We only bother to keep basic | ||||||
|  | 		 * tracking of them for DRM lease clients. | ||||||
|  | 		 * | ||||||
|  | 		 * possible_crtcs is a bitmask of crtcs, where each bit is an | ||||||
|  | 		 * index into drmModeRes.crtcs. So if bit 0 is set (ffs starts | ||||||
|  | 		 * counting from 1), crtc 0 is possible. | ||||||
|  | 		 */ | ||||||
|  | 		int crtc_bit = ffs(plane->possible_crtcs) - 1; | ||||||
|  | 
 | ||||||
|  | 		// This would be a kernel bug
 | ||||||
|  | 		assert(crtc_bit >= 0 && (size_t)crtc_bit < drm->num_crtcs); | ||||||
|  | 
 | ||||||
|  | 		struct wlr_drm_crtc *crtc = &drm->crtcs[crtc_bit]; | ||||||
|  | 
 | ||||||
|  | 		if (type == DRM_PLANE_TYPE_OVERLAY) { | ||||||
|  | 			uint32_t *tmp = realloc(crtc->overlays, | ||||||
|  | 				sizeof(*crtc->overlays) * (crtc->num_overlays + 1)); | ||||||
|  | 			if (tmp) { | ||||||
|  | 				crtc->overlays = tmp; | ||||||
|  | 				crtc->overlays[crtc->num_overlays++] = id; | ||||||
|  | 			} | ||||||
|  | 			drmModeFreePlane(plane); | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (!add_plane(drm, crtc, plane, type, &props)) { | ||||||
|  | 			drmModeFreePlane(plane); | ||||||
|  | 			goto error; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		drmModeFreePlane(plane); | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	drmModeFreePlaneResources(plane_res); | 	drmModeFreePlaneResources(plane_res); | ||||||
| 	return true; | 	return true; | ||||||
| 
 | 
 | ||||||
| error_planes: | error: | ||||||
| 	free(drm->planes); |  | ||||||
| error_res: |  | ||||||
| 	drmModeFreePlaneResources(plane_res); | 	drmModeFreePlaneResources(plane_res); | ||||||
| 	return false; | 	return false; | ||||||
| } | } | ||||||
|  | @ -252,26 +282,33 @@ void finish_drm_resources(struct wlr_drm_backend *drm) { | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	for (size_t i = 0; i < drm->num_planes; ++i) { |  | ||||||
| 		struct wlr_drm_plane *p = &drm->planes[i]; |  | ||||||
| 		wlr_drm_format_set_finish(&p->formats); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	for (size_t i = 0; i < drm->num_crtcs; ++i) { | 	for (size_t i = 0; i < drm->num_crtcs; ++i) { | ||||||
| 		struct wlr_drm_crtc *crtc = &drm->crtcs[i]; | 		struct wlr_drm_crtc *crtc = &drm->crtcs[i]; | ||||||
|  | 
 | ||||||
| 		drmModeAtomicFree(crtc->atomic); | 		drmModeAtomicFree(crtc->atomic); | ||||||
| 		drmModeFreeCrtc(crtc->legacy_crtc); | 		drmModeFreeCrtc(crtc->legacy_crtc); | ||||||
|  | 
 | ||||||
| 		if (crtc->mode_id) { | 		if (crtc->mode_id) { | ||||||
| 			drmModeDestroyPropertyBlob(drm->fd, crtc->mode_id); | 			drmModeDestroyPropertyBlob(drm->fd, crtc->mode_id); | ||||||
| 		} | 		} | ||||||
| 		if (crtc->gamma_lut) { | 		if (crtc->gamma_lut) { | ||||||
| 			drmModeDestroyPropertyBlob(drm->fd, crtc->gamma_lut); | 			drmModeDestroyPropertyBlob(drm->fd, crtc->gamma_lut); | ||||||
| 		} | 		} | ||||||
|  | 
 | ||||||
| 		free(crtc->gamma_table); | 		free(crtc->gamma_table); | ||||||
|  | 
 | ||||||
|  | 		if (crtc->primary) { | ||||||
|  | 			wlr_drm_format_set_finish(&crtc->primary->formats); | ||||||
|  | 			free(crtc->primary); | ||||||
|  | 		} | ||||||
|  | 		if (crtc->cursor) { | ||||||
|  | 			wlr_drm_format_set_finish(&crtc->cursor->formats); | ||||||
|  | 			free(crtc->cursor); | ||||||
|  | 		} | ||||||
|  | 		free(crtc->overlays); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	free(drm->crtcs); | 	free(drm->crtcs); | ||||||
| 	free(drm->planes); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static struct wlr_drm_connector *get_drm_connector_from_output( | static struct wlr_drm_connector *get_drm_connector_from_output( | ||||||
|  | @ -477,7 +514,7 @@ static void drm_connector_start_renderer(struct wlr_drm_connector *conn) { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void realloc_crtcs(struct wlr_drm_backend *drm, bool *changed_outputs); | static void realloc_crtcs(struct wlr_drm_backend *drm); | ||||||
| 
 | 
 | ||||||
| static void attempt_enable_needs_modeset(struct wlr_drm_backend *drm) { | static void attempt_enable_needs_modeset(struct wlr_drm_backend *drm) { | ||||||
| 	// Try to modeset any output that has a desired mode and a CRTC (ie. was
 | 	// Try to modeset any output that has a desired mode and a CRTC (ie. was
 | ||||||
|  | @ -506,7 +543,7 @@ bool enable_drm_connector(struct wlr_output *output, bool enable) { | ||||||
| 
 | 
 | ||||||
| 	if (enable && conn->crtc == NULL) { | 	if (enable && conn->crtc == NULL) { | ||||||
| 		// Maybe we can steal a CRTC from a disabled output
 | 		// Maybe we can steal a CRTC from a disabled output
 | ||||||
| 		realloc_crtcs(drm, NULL); | 		realloc_crtcs(drm); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	bool ok = drm->iface->conn_enable(drm, conn, enable); | 	bool ok = drm->iface->conn_enable(drm, conn, enable); | ||||||
|  | @ -517,7 +554,7 @@ bool enable_drm_connector(struct wlr_output *output, bool enable) { | ||||||
| 	if (enable) { | 	if (enable) { | ||||||
| 		drm_connector_start_renderer(conn); | 		drm_connector_start_renderer(conn); | ||||||
| 	} else { | 	} else { | ||||||
| 		realloc_crtcs(drm, NULL); | 		realloc_crtcs(drm); | ||||||
| 
 | 
 | ||||||
| 		attempt_enable_needs_modeset(drm); | 		attempt_enable_needs_modeset(drm); | ||||||
| 	} | 	} | ||||||
|  | @ -526,82 +563,6 @@ bool enable_drm_connector(struct wlr_output *output, bool enable) { | ||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static ssize_t connector_index_from_crtc(struct wlr_drm_backend *drm, |  | ||||||
| 		struct wlr_drm_crtc *crtc) { |  | ||||||
| 	size_t i = 0; |  | ||||||
| 	struct wlr_drm_connector *conn; |  | ||||||
| 	wl_list_for_each(conn, &drm->outputs, link) { |  | ||||||
| 		if (conn->crtc == crtc) { |  | ||||||
| 			return i; |  | ||||||
| 		} |  | ||||||
| 		++i; |  | ||||||
| 	} |  | ||||||
| 	return -1; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void realloc_planes(struct wlr_drm_backend *drm, const uint32_t *crtc_in, |  | ||||||
| 		bool *changed_outputs) { |  | ||||||
| 	wlr_log(WLR_DEBUG, "Reallocating planes"); |  | ||||||
| 
 |  | ||||||
| 	// overlay, primary, cursor
 |  | ||||||
| 	for (size_t type = 0; type < 3; ++type) { |  | ||||||
| 		if (drm->num_type_planes[type] == 0) { |  | ||||||
| 			continue; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		uint32_t possible[drm->num_type_planes[type] + 1]; |  | ||||||
| 		uint32_t crtc[drm->num_crtcs + 1]; |  | ||||||
| 		uint32_t crtc_res[drm->num_crtcs + 1]; |  | ||||||
| 
 |  | ||||||
| 		for (size_t i = 0; i < drm->num_type_planes[type]; ++i) { |  | ||||||
| 			possible[i] = drm->type_planes[type][i].possible_crtcs; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		for (size_t i = 0; i < drm->num_crtcs; ++i) { |  | ||||||
| 			if (crtc_in[i] == UNMATCHED) { |  | ||||||
| 				crtc[i] = SKIP; |  | ||||||
| 			} else if (drm->crtcs[i].planes[type]) { |  | ||||||
| 				crtc[i] = drm->crtcs[i].planes[type] |  | ||||||
| 					- drm->type_planes[type]; |  | ||||||
| 			} else { |  | ||||||
| 				crtc[i] = UNMATCHED; |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		match_obj(drm->num_type_planes[type], possible, |  | ||||||
| 			drm->num_crtcs, crtc, crtc_res); |  | ||||||
| 
 |  | ||||||
| 		for (size_t i = 0; i < drm->num_crtcs; ++i) { |  | ||||||
| 			if (crtc_res[i] == UNMATCHED || crtc_res[i] == SKIP) { |  | ||||||
| 				continue; |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			struct wlr_drm_crtc *c = &drm->crtcs[i]; |  | ||||||
| 			struct wlr_drm_plane **old = &c->planes[type]; |  | ||||||
| 			struct wlr_drm_plane *new = &drm->type_planes[type][crtc_res[i]]; |  | ||||||
| 
 |  | ||||||
| 			if (*old != new) { |  | ||||||
| 				wlr_log(WLR_DEBUG, |  | ||||||
| 					"Assigning plane %d -> %d (type %zu) to CRTC %d", |  | ||||||
| 					*old ? (int)(*old)->id : -1, |  | ||||||
| 					new ? (int)new->id : -1, |  | ||||||
| 					type, |  | ||||||
| 					c->id); |  | ||||||
| 
 |  | ||||||
| 				ssize_t conn_idx = connector_index_from_crtc(drm, c); |  | ||||||
| 				if (conn_idx >= 0) { |  | ||||||
| 					changed_outputs[conn_idx] = true; |  | ||||||
| 				} |  | ||||||
| 				if (*old) { |  | ||||||
| 					finish_drm_surface(&(*old)->surf); |  | ||||||
| 				} |  | ||||||
| 				finish_drm_surface(&new->surf); |  | ||||||
| 				*old = new; |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void drm_connector_cleanup(struct wlr_drm_connector *conn); | static void drm_connector_cleanup(struct wlr_drm_connector *conn); | ||||||
| 
 | 
 | ||||||
| bool drm_connector_set_mode(struct wlr_output *output, | bool drm_connector_set_mode(struct wlr_output *output, | ||||||
|  | @ -610,7 +571,7 @@ bool drm_connector_set_mode(struct wlr_output *output, | ||||||
| 	struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend); | 	struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend); | ||||||
| 	if (conn->crtc == NULL) { | 	if (conn->crtc == NULL) { | ||||||
| 		// Maybe we can steal a CRTC from a disabled output
 | 		// Maybe we can steal a CRTC from a disabled output
 | ||||||
| 		realloc_crtcs(drm, NULL); | 		realloc_crtcs(drm); | ||||||
| 	} | 	} | ||||||
| 	if (conn->crtc == NULL) { | 	if (conn->crtc == NULL) { | ||||||
| 		wlr_log(WLR_ERROR, "Cannot modeset '%s': no CRTC for this connector", | 		wlr_log(WLR_ERROR, "Cannot modeset '%s': no CRTC for this connector", | ||||||
|  | @ -1001,51 +962,37 @@ static void dealloc_crtc(struct wlr_drm_connector *conn) { | ||||||
| 		conn->crtc - drm->crtcs, conn->output.name); | 		conn->crtc - drm->crtcs, conn->output.name); | ||||||
| 
 | 
 | ||||||
| 	set_drm_connector_gamma(&conn->output, 0, NULL, NULL, NULL); | 	set_drm_connector_gamma(&conn->output, 0, NULL, NULL, NULL); | ||||||
| 
 | 	finish_drm_surface(&conn->crtc->primary->surf); | ||||||
| 	for (size_t type = 0; type < 3; ++type) { | 	finish_drm_surface(&conn->crtc->cursor->surf); | ||||||
| 		struct wlr_drm_plane *plane = conn->crtc->planes[type]; |  | ||||||
| 		if (plane == NULL) { |  | ||||||
| 			continue; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		finish_drm_surface(&plane->surf); |  | ||||||
| 		conn->crtc->planes[type] = NULL; |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	drm->iface->conn_enable(drm, conn, false); | 	drm->iface->conn_enable(drm, conn, false); | ||||||
| 
 | 
 | ||||||
| 	conn->crtc = NULL; | 	conn->crtc = NULL; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void realloc_crtcs(struct wlr_drm_backend *drm, bool *changed_outputs) { | static void realloc_crtcs(struct wlr_drm_backend *drm) { | ||||||
| 	size_t num_outputs = wl_list_length(&drm->outputs); | 	assert(drm->num_crtcs > 0); | ||||||
| 	bool changed_local = changed_outputs ? false : true; |  | ||||||
| 
 | 
 | ||||||
| 	if (changed_local) { | 	size_t num_outputs = wl_list_length(&drm->outputs); | ||||||
| 		changed_outputs = calloc(num_outputs, sizeof(bool)); | 	if (num_outputs == 0) { | ||||||
| 		if (changed_outputs == NULL) { |  | ||||||
| 			wlr_log(WLR_ERROR, "Allocation failed"); |  | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	wlr_log(WLR_DEBUG, "Reallocating CRTCs"); | 	wlr_log(WLR_DEBUG, "Reallocating CRTCs"); | ||||||
| 
 | 
 | ||||||
| 	uint32_t crtc[drm->num_crtcs + 1]; | 	struct wlr_drm_connector *connectors[num_outputs]; | ||||||
|  | 	uint32_t connector_constraints[num_outputs]; | ||||||
|  | 	uint32_t previous_match[drm->num_crtcs]; | ||||||
|  | 	uint32_t new_match[drm->num_crtcs]; | ||||||
|  | 
 | ||||||
| 	for (size_t i = 0; i < drm->num_crtcs; ++i) { | 	for (size_t i = 0; i < drm->num_crtcs; ++i) { | ||||||
| 		crtc[i] = UNMATCHED; | 		previous_match[i] = UNMATCHED; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	struct wlr_drm_connector *connectors[num_outputs + 1]; |  | ||||||
| 
 |  | ||||||
| 	uint32_t possible_crtc[num_outputs + 1]; |  | ||||||
| 	memset(possible_crtc, 0, sizeof(possible_crtc)); |  | ||||||
| 
 |  | ||||||
| 	wlr_log(WLR_DEBUG, "State before reallocation:"); | 	wlr_log(WLR_DEBUG, "State before reallocation:"); | ||||||
| 	ssize_t i = -1; | 	size_t i = 0; | ||||||
| 	struct wlr_drm_connector *conn; | 	struct wlr_drm_connector *conn; | ||||||
| 	wl_list_for_each(conn, &drm->outputs, link) { | 	wl_list_for_each(conn, &drm->outputs, link) { | ||||||
| 		i++; |  | ||||||
| 		connectors[i] = conn; | 		connectors[i] = conn; | ||||||
| 
 | 
 | ||||||
| 		wlr_log(WLR_DEBUG, "  '%s' crtc=%d state=%d desired_enabled=%d", | 		wlr_log(WLR_DEBUG, "  '%s' crtc=%d state=%d desired_enabled=%d", | ||||||
|  | @ -1054,7 +1001,7 @@ static void realloc_crtcs(struct wlr_drm_backend *drm, bool *changed_outputs) { | ||||||
| 			conn->state, conn->desired_enabled); | 			conn->state, conn->desired_enabled); | ||||||
| 
 | 
 | ||||||
| 		if (conn->crtc) { | 		if (conn->crtc) { | ||||||
| 			crtc[conn->crtc - drm->crtcs] = i; | 			previous_match[conn->crtc - drm->crtcs] = i; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// Only search CRTCs for user-enabled outputs (that are already
 | 		// Only search CRTCs for user-enabled outputs (that are already
 | ||||||
|  | @ -1062,86 +1009,84 @@ static void realloc_crtcs(struct wlr_drm_backend *drm, bool *changed_outputs) { | ||||||
| 		if ((conn->state == WLR_DRM_CONN_CONNECTED || | 		if ((conn->state == WLR_DRM_CONN_CONNECTED || | ||||||
| 				conn->state == WLR_DRM_CONN_NEEDS_MODESET) && | 				conn->state == WLR_DRM_CONN_NEEDS_MODESET) && | ||||||
| 				conn->desired_enabled) { | 				conn->desired_enabled) { | ||||||
| 			possible_crtc[i] = conn->possible_crtc; | 			connector_constraints[i] = conn->possible_crtc; | ||||||
| 		} | 		} else { | ||||||
|  | 			// Will always fail to match anything
 | ||||||
|  | 			connector_constraints[i] = 0; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 	uint32_t crtc_res[drm->num_crtcs + 1]; | 		++i; | ||||||
| 	match_obj(wl_list_length(&drm->outputs), possible_crtc, | 	} | ||||||
| 		drm->num_crtcs, crtc, crtc_res); |  | ||||||
| 
 | 
 | ||||||
| 	bool matched[num_outputs + 1]; | 	match_obj(num_outputs, connector_constraints, | ||||||
| 	memset(matched, false, sizeof(matched)); | 		drm->num_crtcs, previous_match, new_match); | ||||||
|  | 
 | ||||||
|  | 	// Converts our crtc=>connector result into a connector=>crtc one.
 | ||||||
|  | 	ssize_t connector_match[num_outputs]; | ||||||
|  | 	for (size_t i = 0 ; i < num_outputs; ++i) { | ||||||
|  | 		connector_match[i] = -1; | ||||||
|  | 	} | ||||||
| 	for (size_t i = 0; i < drm->num_crtcs; ++i) { | 	for (size_t i = 0; i < drm->num_crtcs; ++i) { | ||||||
| 		if (crtc_res[i] != UNMATCHED) { | 		if (new_match[i] != UNMATCHED) { | ||||||
| 			matched[crtc_res[i]] = true; | 			connector_match[new_match[i]] = i; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	for (size_t i = 0; i < drm->num_crtcs; ++i) { | 	/*
 | ||||||
| 		// We don't want any of the current monitors to be deactivated
 | 	 * In the case that we add a new connector (hotplug) and we fail to | ||||||
| 		if (crtc[i] != UNMATCHED && !matched[crtc[i]] && | 	 * match everything, we prefer to fail the new connector and keep all | ||||||
| 				connectors[crtc[i]]->desired_enabled) { | 	 * of the old mappings instead. | ||||||
| 			wlr_log(WLR_DEBUG, "Could not match a CRTC for connected output %d", | 	 */ | ||||||
| 				crtc[i]); | 	for (size_t i = 0; i < num_outputs; ++i) { | ||||||
| 			goto free_changed_outputs; | 		struct wlr_drm_connector *conn = connectors[i]; | ||||||
|  | 		if (conn->state == WLR_DRM_CONN_CONNECTED && | ||||||
|  | 				conn->desired_enabled && | ||||||
|  | 				connector_match[i] == -1) { | ||||||
|  | 			wlr_log(WLR_DEBUG, "Could not match a CRTC for previously connected output; " | ||||||
|  | 					"keeping old configuration"); | ||||||
|  | 			return; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 |  | ||||||
| 	for (size_t i = 0; i < drm->num_crtcs; ++i) { |  | ||||||
| 		if (crtc_res[i] == crtc[i]) { |  | ||||||
| 			continue; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// De-allocate this CRTC on previous output
 |  | ||||||
| 		if (crtc[i] != UNMATCHED) { |  | ||||||
| 			changed_outputs[crtc[i]] = true; |  | ||||||
| 			dealloc_crtc(connectors[crtc[i]]); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// Assign this CRTC to next output
 |  | ||||||
| 		if (crtc_res[i] != UNMATCHED) { |  | ||||||
| 			changed_outputs[crtc_res[i]] = true; |  | ||||||
| 
 |  | ||||||
| 			struct wlr_drm_connector *conn = connectors[crtc_res[i]]; |  | ||||||
| 			dealloc_crtc(conn); |  | ||||||
| 			conn->crtc = &drm->crtcs[i]; |  | ||||||
| 
 |  | ||||||
| 			wlr_log(WLR_DEBUG, "Assigning CRTC %zu to output %d -> %d '%s'", |  | ||||||
| 				i, crtc[i], crtc_res[i], conn->output.name); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	wlr_log(WLR_DEBUG, "State after reallocation:"); | 	wlr_log(WLR_DEBUG, "State after reallocation:"); | ||||||
| 	wl_list_for_each(conn, &drm->outputs, link) { | 
 | ||||||
| 		wlr_log(WLR_DEBUG, "  '%s' crtc=%d state=%d desired_enabled=%d", | 	// Apply new configuration
 | ||||||
|  | 	for (size_t i = 0; i < num_outputs; ++i) { | ||||||
|  | 		struct wlr_drm_connector *conn = connectors[i]; | ||||||
|  | 		bool prev_enabled = conn->crtc; | ||||||
|  | 
 | ||||||
|  | 		wlr_log(WLR_DEBUG, "  '%s' crtc=%zd state=%d desired_enabled=%d", | ||||||
| 			conn->output.name, | 			conn->output.name, | ||||||
| 			conn->crtc ? (int)(conn->crtc - drm->crtcs) : -1, | 			connector_match[i], | ||||||
| 			conn->state, conn->desired_enabled); | 			conn->state, conn->desired_enabled); | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	realloc_planes(drm, crtc_res, changed_outputs); | 		// We don't need to change anything.
 | ||||||
| 
 | 		if (prev_enabled && connector_match[i] == conn->crtc - drm->crtcs) { | ||||||
| 	// We need to reinitialize any plane that has changed
 |  | ||||||
| 	i = -1; |  | ||||||
| 	wl_list_for_each(conn, &drm->outputs, link) { |  | ||||||
| 		i++; |  | ||||||
| 		struct wlr_output_mode *mode = conn->output.current_mode; |  | ||||||
| 
 |  | ||||||
| 		if (conn->state != WLR_DRM_CONN_CONNECTED || !changed_outputs[i]) { |  | ||||||
| 			continue; | 			continue; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (conn->crtc == NULL) { | 		dealloc_crtc(conn); | ||||||
|  | 
 | ||||||
|  | 		if (connector_match[i] == -1) { | ||||||
|  | 			if (prev_enabled) { | ||||||
| 				wlr_log(WLR_DEBUG, "Output has %s lost its CRTC", | 				wlr_log(WLR_DEBUG, "Output has %s lost its CRTC", | ||||||
| 					conn->output.name); | 					conn->output.name); | ||||||
| 				conn->state = WLR_DRM_CONN_NEEDS_MODESET; | 				conn->state = WLR_DRM_CONN_NEEDS_MODESET; | ||||||
| 				wlr_output_update_enabled(&conn->output, false); | 				wlr_output_update_enabled(&conn->output, false); | ||||||
| 				conn->desired_mode = conn->output.current_mode; | 				conn->desired_mode = conn->output.current_mode; | ||||||
| 				wlr_output_update_mode(&conn->output, NULL); | 				wlr_output_update_mode(&conn->output, NULL); | ||||||
|  | 			} | ||||||
| 			continue; | 			continue; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		conn->crtc = &drm->crtcs[connector_match[i]]; | ||||||
|  | 
 | ||||||
|  | 		// Only realloc buffers if we have actually been modeset
 | ||||||
|  | 		if (conn->state != WLR_DRM_CONN_CONNECTED) { | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		struct wlr_output_mode *mode = conn->output.current_mode; | ||||||
|  | 
 | ||||||
| 		if (!init_drm_plane_surfaces(conn->crtc->primary, drm, | 		if (!init_drm_plane_surfaces(conn->crtc->primary, drm, | ||||||
| 				mode->width, mode->height, drm->renderer.gbm_format)) { | 				mode->width, mode->height, drm->renderer.gbm_format)) { | ||||||
| 			wlr_log(WLR_ERROR, "Failed to initialize renderer for plane"); | 			wlr_log(WLR_ERROR, "Failed to initialize renderer for plane"); | ||||||
|  | @ -1153,11 +1098,6 @@ static void realloc_crtcs(struct wlr_drm_backend *drm, bool *changed_outputs) { | ||||||
| 
 | 
 | ||||||
| 		wlr_output_damage_whole(&conn->output); | 		wlr_output_damage_whole(&conn->output); | ||||||
| 	} | 	} | ||||||
| 
 |  | ||||||
| free_changed_outputs: |  | ||||||
| 	if (changed_local) { |  | ||||||
| 		free(changed_outputs); |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static uint32_t get_possible_crtcs(int fd, drmModeRes *res, | static uint32_t get_possible_crtcs(int fd, drmModeRes *res, | ||||||
|  | @ -1392,25 +1332,7 @@ void scan_drm_connectors(struct wlr_drm_backend *drm) { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	bool changed_outputs[wl_list_length(&drm->outputs) + 1]; | 	realloc_crtcs(drm); | ||||||
| 	memset(changed_outputs, false, sizeof(changed_outputs)); |  | ||||||
| 	for (size_t i = 0; i < new_outputs_len; ++i) { |  | ||||||
| 		struct wlr_drm_connector *conn = new_outputs[i]; |  | ||||||
| 
 |  | ||||||
| 		ssize_t pos = -1; |  | ||||||
| 		struct wlr_drm_connector *c; |  | ||||||
| 		wl_list_for_each(c, &drm->outputs, link) { |  | ||||||
| 			++pos; |  | ||||||
| 			if (c == conn) { |  | ||||||
| 				break; |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		assert(pos >= 0); |  | ||||||
| 
 |  | ||||||
| 		changed_outputs[pos] = true; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	realloc_crtcs(drm, changed_outputs); |  | ||||||
| 
 | 
 | ||||||
| 	for (size_t i = 0; i < new_outputs_len; ++i) { | 	for (size_t i = 0; i < new_outputs_len; ++i) { | ||||||
| 		struct wlr_drm_connector *conn = new_outputs[i]; | 		struct wlr_drm_connector *conn = new_outputs[i]; | ||||||
|  | @ -1536,23 +1458,7 @@ static void drm_connector_cleanup(struct wlr_drm_connector *conn) { | ||||||
| 
 | 
 | ||||||
| 	switch (conn->state) { | 	switch (conn->state) { | ||||||
| 	case WLR_DRM_CONN_CONNECTED: | 	case WLR_DRM_CONN_CONNECTED: | ||||||
| 	case WLR_DRM_CONN_CLEANUP:; | 	case WLR_DRM_CONN_CLEANUP: | ||||||
| 		struct wlr_drm_crtc *crtc = conn->crtc; |  | ||||||
| 		if (crtc != NULL) { |  | ||||||
| 			for (int i = 0; i < 3; ++i) { |  | ||||||
| 				if (!crtc->planes[i]) { |  | ||||||
| 					continue; |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				finish_drm_surface(&crtc->planes[i]->surf); |  | ||||||
| 				finish_drm_surface(&crtc->planes[i]->mgpu_surf); |  | ||||||
| 				if (crtc->planes[i]->id == 0) { |  | ||||||
| 					free(crtc->planes[i]); |  | ||||||
| 					crtc->planes[i] = NULL; |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		conn->output.current_mode = NULL; | 		conn->output.current_mode = NULL; | ||||||
| 		conn->desired_mode = NULL; | 		conn->desired_mode = NULL; | ||||||
| 		struct wlr_drm_mode *mode, *tmp; | 		struct wlr_drm_mode *mode, *tmp; | ||||||
|  |  | ||||||
|  | @ -22,8 +22,6 @@ struct wlr_drm_plane { | ||||||
| 	uint32_t type; | 	uint32_t type; | ||||||
| 	uint32_t id; | 	uint32_t id; | ||||||
| 
 | 
 | ||||||
| 	uint32_t possible_crtcs; |  | ||||||
| 
 |  | ||||||
| 	struct wlr_drm_surface surf; | 	struct wlr_drm_surface surf; | ||||||
| 	struct wlr_drm_surface mgpu_surf; | 	struct wlr_drm_surface mgpu_surf; | ||||||
| 
 | 
 | ||||||
|  | @ -49,14 +47,15 @@ struct wlr_drm_crtc { | ||||||
| 	// Legacy only
 | 	// Legacy only
 | ||||||
| 	drmModeCrtc *legacy_crtc; | 	drmModeCrtc *legacy_crtc; | ||||||
| 
 | 
 | ||||||
| 	union { |  | ||||||
| 		struct { |  | ||||||
| 			struct wlr_drm_plane *overlay; |  | ||||||
| 	struct wlr_drm_plane *primary; | 	struct wlr_drm_plane *primary; | ||||||
| 	struct wlr_drm_plane *cursor; | 	struct wlr_drm_plane *cursor; | ||||||
| 		}; | 
 | ||||||
| 		struct wlr_drm_plane *planes[3]; | 	/*
 | ||||||
| 	}; | 	 * We don't support overlay planes yet, but we keep track of them to | ||||||
|  | 	 * give to DRM lease clients. | ||||||
|  | 	 */ | ||||||
|  | 	size_t num_overlays; | ||||||
|  | 	uint32_t *overlays; | ||||||
| 
 | 
 | ||||||
| 	union wlr_drm_crtc_props props; | 	union wlr_drm_crtc_props props; | ||||||
| 
 | 
 | ||||||
|  | @ -78,26 +77,6 @@ struct wlr_drm_backend { | ||||||
| 
 | 
 | ||||||
| 	size_t num_crtcs; | 	size_t num_crtcs; | ||||||
| 	struct wlr_drm_crtc *crtcs; | 	struct wlr_drm_crtc *crtcs; | ||||||
| 	size_t num_planes; |  | ||||||
| 	struct wlr_drm_plane *planes; |  | ||||||
| 
 |  | ||||||
| 	union { |  | ||||||
| 		struct { |  | ||||||
| 			size_t num_overlay_planes; |  | ||||||
| 			size_t num_primary_planes; |  | ||||||
| 			size_t num_cursor_planes; |  | ||||||
| 		}; |  | ||||||
| 		size_t num_type_planes[3]; |  | ||||||
| 	}; |  | ||||||
| 
 |  | ||||||
| 	union { |  | ||||||
| 		struct { |  | ||||||
| 			struct wlr_drm_plane *overlay_planes; |  | ||||||
| 			struct wlr_drm_plane *primary_planes; |  | ||||||
| 			struct wlr_drm_plane *cursor_planes; |  | ||||||
| 		}; |  | ||||||
| 		struct wlr_drm_plane *type_planes[3]; |  | ||||||
| 	}; |  | ||||||
| 
 | 
 | ||||||
| 	struct wl_display *display; | 	struct wl_display *display; | ||||||
| 	struct wl_event_source *drm_event; | 	struct wl_event_source *drm_event; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue