diff --git a/wayland-system-compositor.c b/wayland-system-compositor.c index daca3391..c135aea0 100644 --- a/wayland-system-compositor.c +++ b/wayland-system-compositor.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -71,9 +72,11 @@ struct wlsc_output { int32_t x, y, width, height; drmModeModeInfo *mode; - uint32_t fb_id; uint32_t crtc_id; uint32_t connector_id; + + uint32_t fb_id[2]; + uint32_t current; }; struct wlsc_input_device { @@ -126,6 +129,7 @@ struct wlsc_compositor { int repaint_on_timeout; struct timespec previous_swap; uint32_t current_frame; + struct wl_event_source *drm_source; uint32_t meta_state; struct wl_list animate_list; @@ -582,6 +586,34 @@ wlsc_vector_scalar(struct wlsc_vector *v1, GLdouble s) v1->z *= s; } +static void +page_flip_handler(int fd, unsigned int frame, + unsigned int sec, unsigned int usec, void *data) +{ + struct wlsc_output *output = data; + struct wlsc_animate *animate, *next; + struct wlsc_compositor *compositor = output->compositor; + uint32_t msecs; + + msecs = sec * 1000 + usec / 1000; + wl_display_post_frame(compositor->wl_display, + &compositor->base, + compositor->current_frame, msecs); + + wl_event_source_timer_update(compositor->timer_source, 10); + compositor->repaint_on_timeout = 1; + + animate = container_of(compositor->animate_list.next, struct wlsc_animate, link); + while (&animate->link != &compositor->animate_list) { + next = container_of(animate->link.next, + struct wlsc_animate, link); + animate->animate(animate, compositor, compositor->current_frame, msecs); + animate = next; + } + + compositor->current_frame++; +} + static void repaint_output(struct wlsc_output *output) { @@ -589,6 +621,7 @@ repaint_output(struct wlsc_output *output) struct wlsc_surface *es; struct wlsc_input_device *eid; double s = 3000; + int fd; if (!eglMakeCurrent(ec->display, output->surface, output->surface, ec->context)) { fprintf(stderr, "failed to make context current\n"); @@ -628,7 +661,10 @@ repaint_output(struct wlsc_output *output) struct wlsc_input_device, link); } - eglSwapBuffers(ec->display, output->surface); + fd = eglGetDisplayFD(ec->display); + output->current ^= 1; + eglBindColorBuffer(ec->display, output->surface, output->current); + drmModePageFlip(fd, output->crtc_id, output->fb_id[output->current ^ 1], output); } static void @@ -636,9 +672,6 @@ repaint(void *data) { struct wlsc_compositor *ec = data; struct wlsc_output *output; - struct wlsc_animate *animate, *next; - struct timespec ts; - uint32_t msecs; if (!ec->repaint_needed) { ec->repaint_on_timeout = 0; @@ -653,24 +686,6 @@ repaint(void *data) } ec->repaint_needed = 0; - - clock_gettime(CLOCK_MONOTONIC, &ts); - msecs = ts.tv_sec * 1000 + ts.tv_nsec / (1000 * 1000); - wl_display_post_frame(ec->wl_display, &ec->base, - ec->current_frame, msecs); - - wl_event_source_timer_update(ec->timer_source, 10); - ec->repaint_on_timeout = 1; - - animate = container_of(ec->animate_list.next, struct wlsc_animate, link); - while (&animate->link != &ec->animate_list) { - next = container_of(animate->link.next, - struct wlsc_animate, link); - animate->animate(animate, ec, ec->current_frame, msecs); - animate = next; - } - - ec->current_frame++; } static void @@ -759,16 +774,16 @@ surface_copy(struct wl_client *client, struct wlsc_compositor *ec = es->compositor; EGLSurface src; - /* FIXME: glCopyPixels should work, but then we'll have to - * call eglMakeCurrent to set up the src and dest surfaces - * first. This seems cheaper, but maybe there's a better way - * to accomplish this. */ - src = eglCreateSurfaceForName(ec->display, ec->config, name, x + width, y + height, stride, NULL); - eglCopyNativeBuffers(ec->display, es->surface, GL_FRONT_LEFT, dst_x, dst_y, - src, GL_FRONT_LEFT, x, y, width, height); + eglMakeCurrent(ec->display, es->surface, src, ec->context); + glDrawBuffer(GL_FRONT); + glReadBuffer(GL_FRONT); + glRasterPos2d(0, 0); + fprintf(stderr, "copypixels\n"); + glCopyPixels(x, y, width, height, GL_COLOR); + eglDestroySurface(ec->display, src); } @@ -1307,6 +1322,17 @@ post_output_geometry(struct wl_client *client, struct wl_object *global) output->width, output->height); } +static void +on_drm_input(int fd, uint32_t mask, void *data) +{ + drmEventContext evctx; + + memset(&evctx, 0, sizeof evctx); + evctx.version = DRM_EVENT_CONTEXT_VERSION; + evctx.page_flip_handler = page_flip_handler; + drmHandleEvent(fd, &evctx); +} + static int init_egl(struct wlsc_compositor *ec, struct udev_device *device) { @@ -1318,7 +1344,9 @@ init_egl(struct wlsc_compositor *ec, struct udev_device *device) EGL_NONE }; + struct wl_event_loop *loop; EGLint major, minor; + int fd; ec->display = eglCreateDisplayNative(device); if (ec->display == NULL) { @@ -1339,17 +1367,19 @@ init_egl(struct wlsc_compositor *ec, struct udev_device *device) fprintf(stderr, "failed to create context\n"); return -1; } + + loop = wl_display_get_event_loop(ec->wl_display); + fd = eglGetDisplayFD(ec->display); + ec->drm_source = + wl_event_loop_add_fd(loop, fd, + WL_EVENT_READABLE, on_drm_input, ec); + return 0; } static int create_output(struct wlsc_compositor *ec, struct udev_device *device) { - const static EGLint surface_attribs[] = { - EGL_RENDER_BUFFER, EGL_BACK_BUFFER, - EGL_NONE - }; - drmModeConnector *connector; drmModeRes *resources; drmModeEncoder *encoder; @@ -1414,27 +1444,31 @@ create_output(struct wlsc_compositor *ec, struct udev_device *device) output->width = mode->hdisplay; output->height = mode->vdisplay; - output->surface = eglCreateSurfaceForName(ec->display, - ec->config, - 0, - output->width, - output->height, - 0, surface_attribs); + output->surface = eglCreateSurface(ec->display, + ec->config, + output->width, + output->height, + 2, NULL); if (output->surface == NULL) { fprintf(stderr, "failed to create surface\n"); return -1; } - eglGetNativeBuffer(output->surface, - GL_FRONT_LEFT, &name, &handle, &stride); - ret = drmModeAddFB(fd, output->width, output->height, - 32, 32, stride, handle, &output->fb_id); - if (ret) { - fprintf(stderr, "failed to add fb: %m\n"); - return -1; + for (i = 0; i < 2; i++) { + eglGetColorBuffer(output->surface, + i, &name, &handle, &stride); + + ret = drmModeAddFB(fd, mode->hdisplay, mode->vdisplay, + 32, 32, stride, handle, &output->fb_id[i]); + if (ret) { + fprintf(stderr, "failed to add fb %d: %m\n", i); + return -1; + } } - ret = drmModeSetCrtc(fd, encoder->crtc_id, output->fb_id, 0, 0, + output->current = 0; + ret = drmModeSetCrtc(fd, encoder->crtc_id, + output->fb_id[output->current ^ 1], 0, 0, &connector->connector_id, 1, mode); if (ret) { fprintf(stderr, "failed to set mode: %m\n"); @@ -1493,7 +1527,8 @@ static void on_enter_vt(int signal_number, void *data) fd = eglGetDisplayFD(ec->display); output = container_of(ec->output_list.next, struct wlsc_output, link); while (&output->link != &ec->output_list) { - ret = drmModeSetCrtc(fd, output->crtc_id, output->fb_id, 0, 0, + ret = drmModeSetCrtc(fd, output->crtc_id, + output->fb_id[output->current], 0, 0, &output->connector_id, 1, output->mode); if (ret) fprintf(stderr, "failed to set mode for connector %d: %m\n",