Upgraded CRTC and Encoder matching

This commit is contained in:
Scott Anderson 2017-07-29 22:14:29 +12:00
parent 31867a1b2e
commit 67b51f0922
2 changed files with 232 additions and 33 deletions

View file

@ -348,17 +348,155 @@ static void wlr_drm_output_enable(struct wlr_output_state *output, bool enable)
} }
} }
static inline bool is_taken(size_t n, const uint32_t arr[static n], uint32_t key)
{
for (size_t i = 0; i < n; ++i) {
if (arr[i] == key) {
return true;
}
}
return false;
}
/*
* Store all of the non-recursive state in a struct, so we aren't literally
* passing 12 arguments to a function.
*/
struct match_state {
const size_t num_objs;
const uint32_t *restrict objs;
const size_t num_res;
size_t score;
size_t replaced;
uint32_t *restrict res;
uint32_t *restrict best;
const uint32_t *restrict orig;
bool exit_early;
};
#define UNMATCHED (uint32_t)-1
/*
* score: The number of resources we've matched so far.
* replaced: The number of changes from the original solution.
* i: The index of the current element.
*
* This tries to match a solution as close to st->orig as it can.
*
* Returns whether we've set a new best element with this solution.
*/
static bool match_obj_(struct match_state *st, size_t score, size_t replaced, size_t i) {
// Finished
if (i >= st->num_res) {
if (score > st->score || (score == st->score && replaced < st->replaced)) {
st->score = score;
st->replaced = replaced;
memcpy(st->best, st->res, sizeof st->best[0] * st->num_res);
if (st->score == st->num_objs && st->replaced == 0) {
st->exit_early = true;
}
return true;
} else {
return false;
}
}
/*
* Attempt to use the current solution first, to try and avoid
* recalculating everything
*/
if (st->orig[i] != UNMATCHED && !is_taken(i, st->res, st->orig[i])) {
st->res[i] = st->orig[i];
if (match_obj_(st, score + 1, replaced, i + 1)) {
return true;
}
}
if (st->orig[i] != UNMATCHED) {
++replaced;
}
bool is_best = false;
for (st->res[i] = 0; st->res[i] < st->num_objs; ++st->res[i]) {
// We tried this earlier
if (st->res[i] == st->orig[i]) {
continue;
}
// Not compatable
if (!(st->objs[st->res[i]] & (1 << i))) {
continue;
}
// Already taken
if (is_taken(i, st->res, st->res[i])) {
continue;
}
if (match_obj_(st, score + 1, replaced, i + 1)) {
is_best = true;
}
if (st->exit_early) {
return true;
}
}
if (is_best) {
return true;
}
// Maybe this resource can't be matched
st->res[i] = UNMATCHED;
return match_obj_(st, score, replaced, i + 1);
}
/*
* Tries to match some DRM objects with some other DRM resource.
* e.g. Match CRTCs with Encoders, CRTCs with Planes.
*
* objs contains a bit array which resources it can be matched with.
* e.g. Bit 0 set means can be matched with res[0]
*
* res contains an index of which objs it is matched with or UNMATCHED.
*
* This solution is left in out.
*/
static size_t match_obj(size_t num_objs, const uint32_t objs[static restrict num_objs],
size_t num_res, const uint32_t res[static restrict num_res],
uint32_t out[static restrict num_res]) {
uint32_t solution[num_res];
struct match_state st = {
.num_objs = num_objs,
.num_res = num_res,
.score = 0,
.replaced = SIZE_MAX,
.objs = objs,
.res = solution,
.best = out,
.orig = res,
.exit_early = false,
};
match_obj_(&st, 0, 0, 0);
return st.score;
}
static bool wlr_drm_output_set_mode(struct wlr_output_state *output, static bool wlr_drm_output_set_mode(struct wlr_output_state *output,
struct wlr_output_mode *mode) { struct wlr_output_mode *mode) {
struct wlr_backend_state *state = struct wlr_backend_state *drm =
wl_container_of(output->renderer, state, renderer); wl_container_of(output->renderer, drm, renderer);
wlr_log(L_INFO, "Modesetting '%s' with '%ux%u@%u mHz'", output->base->name, wlr_log(L_INFO, "Modesetting '%s' with '%ux%u@%u mHz'", output->base->name,
mode->width, mode->height, mode->refresh); mode->width, mode->height, mode->refresh);
drmModeConnector *conn = drmModeGetConnector(state->fd, output->connector); drmModeConnector *conn = drmModeGetConnector(drm->fd, output->connector);
if (!conn) { if (!conn) {
wlr_log(L_ERROR, "Failed to get DRM connector"); wlr_log_errno(L_ERROR, "Failed to get DRM connector");
goto error; goto error;
} }
@ -367,40 +505,81 @@ static bool wlr_drm_output_set_mode(struct wlr_output_state *output,
goto error; goto error;
} }
drmModeRes *res = drmModeGetResources(state->fd); {
if (!res) { size_t index;
wlr_log(L_ERROR, "Failed to get DRM resources"); uint32_t crtc[drm->num_crtcs];
goto error; uint32_t possible_crtc[drm->outputs->length];
memset(possible_crtc, 0, sizeof(possible_crtc));
for (size_t i = 0; i < drm->num_crtcs; ++i) {
crtc[i] = UNMATCHED;
} }
bool success = false; for (size_t i = 0; i < drm->outputs->length; ++i) {
for (int i = 0; !success && i < conn->count_encoders; ++i) { struct wlr_output_state *o = drm->outputs->items[i];
drmModeEncoder *enc = drmModeGetEncoder(state->fd, conn->encoders[i]); if (o == output) {
index = i;
}
if (o->state != WLR_DRM_OUTPUT_CONNECTED) {
continue;
}
possible_crtc[i] = o->possible_crtc;
crtc[o->crtc_ - drm->crtcs] = i;
}
for (int i = 0; i < conn->count_encoders; ++i) {
drmModeEncoder *enc = drmModeGetEncoder(drm->fd, conn->encoders[i]);
if (!enc) { if (!enc) {
continue; continue;
} }
for (int j = 0; j < res->count_crtcs; ++j) { uint32_t res[drm->num_crtcs];
if ((enc->possible_crtcs & (1 << j)) == 0) {
possible_crtc[index] = enc->possible_crtcs;
match_obj(drm->outputs->length, possible_crtc, drm->num_crtcs, crtc, res);
wlr_log(L_DEBUG, "-----");
for (size_t i = 0; i < drm->num_crtcs; ++i) {
wlr_log(L_DEBUG, "%d", (int)res[i]);
}
wlr_log(L_DEBUG, "-----");
bool handled[drm->outputs->length];
memset(handled, 0, sizeof(handled));
for (size_t i = 0; i < drm->num_crtcs; ++i) {
if (res[i] == UNMATCHED) {
continue; continue;
} }
if ((state->taken_crtcs & (1 << j)) == 0) { handled[res[i]] = true;
state->taken_crtcs |= 1 << j;
output->crtc = res->crtcs[j]; if (res[i] != crtc[i]) {
success = true; struct wlr_output_state *o = drm->outputs->items[res[i]];
o->crtc_ = &drm->crtcs[i];
o->crtc = o->crtc_->id;
}
}
for (size_t i = 0; i < drm->num_crtcs; ++i) {
if (!handled[i]) {
wlr_drm_output_cleanup(drm->outputs->items[i], false);
}
}
if (!output->crtc_) {
wlr_log(L_ERROR, "Unable to match %s with a CRTC", output->base->name);
goto error;
}
output->possible_crtc = enc->possible_crtcs;
drmModeFreeEncoder(enc);
break; break;
} }
} }
drmModeFreeEncoder(enc);
}
drmModeFreeResources(res);
if (!success) {
wlr_log(L_ERROR, "Failed to find CRTC for %s", output->base->name);
goto error;
}
output->state = WLR_DRM_OUTPUT_CONNECTED; output->state = WLR_DRM_OUTPUT_CONNECTED;
output->width = output->base->width = mode->width; output->width = output->base->width = mode->width;
@ -408,7 +587,7 @@ static bool wlr_drm_output_set_mode(struct wlr_output_state *output,
output->base->current_mode = mode; output->base->current_mode = mode;
wl_signal_emit(&output->base->events.resolution, output->base); wl_signal_emit(&output->base->events.resolution, output->base);
if (!display_init_renderer(&state->renderer, output)) { if (!display_init_renderer(&drm->renderer, output)) {
wlr_log(L_ERROR, "Failed to initalise renderer for %s", output->base->name); wlr_log(L_ERROR, "Failed to initalise renderer for %s", output->base->name);
goto error; goto error;
} }
@ -522,12 +701,17 @@ static int32_t calculate_refresh_rate(drmModeModeInfo *mode) {
int32_t refresh = (mode->clock * 1000000LL / mode->htotal + int32_t refresh = (mode->clock * 1000000LL / mode->htotal +
mode->vtotal / 2) / mode->vtotal; mode->vtotal / 2) / mode->vtotal;
if (mode->flags & DRM_MODE_FLAG_INTERLACE) if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
refresh *= 2; refresh *= 2;
if (mode->flags & DRM_MODE_FLAG_DBLSCAN) }
if (mode->flags & DRM_MODE_FLAG_DBLSCAN) {
refresh /= 2; refresh /= 2;
if (mode->vscan > 1) }
if (mode->vscan > 1) {
refresh /= mode->vscan; refresh /= mode->vscan;
}
return refresh; return refresh;
} }
@ -793,10 +977,22 @@ void wlr_drm_output_cleanup(struct wlr_output_state *output, bool restore) {
restore_output(output, renderer->fd); restore_output(output, renderer->fd);
restore = false; restore = false;
} }
if (output->bo[0]) {
gbm_surface_release_buffer(output->gbm, output->bo[0]);
output->bo[0] = NULL;
}
if (output->bo[1]) {
gbm_surface_release_buffer(output->gbm, output->bo[1]);
output->bo[1] = NULL;
}
eglDestroySurface(renderer->egl.display, output->egl); eglDestroySurface(renderer->egl.display, output->egl);
gbm_surface_destroy(output->gbm); gbm_surface_destroy(output->gbm);
output->egl = EGL_NO_SURFACE; output->egl = EGL_NO_SURFACE;
output->gbm = NULL; output->gbm = NULL;
output->crtc_ = NULL;
output->possible_crtc = 0;
/* Fallthrough */ /* Fallthrough */
case WLR_DRM_OUTPUT_NEEDS_MODESET: case WLR_DRM_OUTPUT_NEEDS_MODESET:
output->state = WLR_DRM_OUTPUT_DISCONNECTED; output->state = WLR_DRM_OUTPUT_DISCONNECTED;

View file

@ -115,6 +115,9 @@ struct wlr_output_state {
enum wlr_drm_output_state state; enum wlr_drm_output_state state;
uint32_t connector; uint32_t connector;
struct wlr_drm_crtc *crtc_;
uint32_t possible_crtc;
union wlr_drm_connector_props props; union wlr_drm_connector_props props;
uint32_t width; uint32_t width;