Last active
June 9, 2020 11:41
-
-
Save ardera/884b3ba034f357a37b78978319815a8d to your computer and use it in GitHub Desktop.
Reproduction of a `drmModeSetPlane` issue of the raspbian linux kernel (https://github.com/raspberrypi/linux/issues/3661). This time with two planes being drawn into concurrently.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <fcntl.h> | |
#include <stdio.h> | |
#include <stdint.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <errno.h> | |
#include <time.h> | |
#include <xf86drm.h> | |
#include <xf86drmMode.h> | |
static int find_crtc_comma_connector_comma_mode_and_overlay_plane( | |
int drm_fd, | |
uint32_t *connector_id_out, | |
drmModeModeInfo **mode_out, | |
uint32_t *crtc_id_out, | |
uint32_t *plane_id_out | |
) { | |
drmModeConnector *connector; | |
drmModePlaneRes *plane_res; | |
drmModeModeInfo *mode; | |
drmModeEncoder *encoder; | |
drmModeRes *resources; | |
uint32_t connector_id; | |
uint32_t encoder_id; | |
uint32_t crtc_id; | |
uint32_t plane_id; | |
drmSetClientCap(drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); | |
resources = drmModeGetResources(drm_fd); | |
if (resources == NULL) { | |
fprintf(stderr, "drmModeGetResources failed: %s\n", strerror(errno)); | |
return errno; | |
} | |
encoder_id = 0; | |
connector_id = 0; | |
for (int i = 0; (i < resources->count_connectors) && ((encoder_id == 0) || (connector_id == 0)); i++) { | |
connector = drmModeGetConnector(drm_fd, resources->connectors[i]); | |
if (connector->connection == DRM_MODE_CONNECTED) { | |
connector_id = connector->connector_id; | |
encoder_id = connector->encoder_id; | |
mode = NULL; | |
for (int j = 0; (j < connector->count_modes) && (mode == NULL); j++) { | |
drmModeModeInfo *mode_cursor = &connector->modes[j]; | |
if (mode_cursor->type & DRM_MODE_TYPE_PREFERRED) { | |
mode = mode_cursor; | |
} | |
} | |
break; | |
} | |
drmModeFreeConnector(connector); | |
} | |
if (encoder_id == 0) { | |
fprintf(stderr, "could not find a connected connector!\n"); | |
return EINVAL; | |
} | |
crtc_id = 0; | |
for (int i = 0; (i < resources->count_encoders) && (crtc_id == 0); i++) { | |
encoder = drmModeGetEncoder(drm_fd, resources->encoders[i]); | |
if (encoder->encoder_id == encoder_id) { | |
crtc_id = encoder->crtc_id; | |
} | |
drmModeFreeEncoder(encoder); | |
} | |
if (crtc_id == 0) { | |
fprintf(stderr, "could not find a suitable crtc!\n"); | |
return EINVAL; | |
} | |
drmModeFreeResources(resources); | |
plane_id = 0; | |
plane_res = drmModeGetPlaneResources(drm_fd); | |
for (int i = 0; (i < plane_res->count_planes) && (plane_id == 0); i++) { | |
drmModePlane *plane = drmModeGetPlane(drm_fd, plane_res->planes[i]); | |
drmModeObjectProperties *props = drmModeObjectGetProperties(drm_fd, plane->plane_id, DRM_MODE_OBJECT_ANY); | |
for (int j = 0; (j < props->count_props) && (plane_id == 0); j++) { | |
drmModePropertyRes *prop = drmModeGetProperty(drm_fd, props->props[j]); | |
if ((strcmp(prop->name, "type") == 0) && (props->prop_values[j] == DRM_PLANE_TYPE_OVERLAY)) { | |
plane_id = plane->plane_id; | |
} | |
drmModeFreeProperty(prop); | |
} | |
drmModeFreeObjectProperties(props); | |
drmModeFreePlane(plane); | |
} | |
drmModeFreePlaneResources(plane_res); | |
*connector_id_out = connector_id; | |
*mode_out = mode; | |
*crtc_id_out = crtc_id; | |
*plane_id_out = plane_id; | |
return 0; | |
} | |
static int create_fb( | |
int drm_fd, | |
int width, int height, | |
uint32_t *bo_handle_out, | |
uint32_t *stride_out, | |
uint32_t *size_out, | |
uint32_t *fb_id_out | |
) { | |
struct drm_mode_create_dumb create_req; | |
int ok; | |
memset(&create_req, 0, sizeof(create_req)); | |
create_req.width = width; | |
create_req.height = height; | |
create_req.bpp = 32; | |
ok = drmIoctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_req); | |
if (ok < 0) { | |
fprintf(stderr, "Could not create dumb buffer: %s\n", strerror(errno)); | |
return errno; | |
} | |
if (bo_handle_out) | |
*bo_handle_out = create_req.handle; | |
if (stride_out) | |
*stride_out = create_req.pitch; | |
if (size_out) | |
*size_out = create_req.size; | |
ok = drmModeAddFB( | |
drm_fd, | |
width, | |
height, | |
24, | |
32, | |
create_req.pitch, | |
create_req.handle, | |
fb_id_out | |
); | |
if (ok < 0) { | |
fprintf(stderr, "Could not add dumb buffer as FB: %s\n", strerror(-errno)); | |
return errno; | |
} | |
return 0; | |
} | |
int main(int argc, char **argv) { | |
drmModeModeInfo *mode; | |
struct timespec t0, t1; | |
uint32_t connector_id, crtc_id, plane_id, fb_ids[4]; | |
int width, height; | |
int ok, drm_fd, current_fb_id; | |
drm_fd = open("/dev/dri/card1", O_RDWR); | |
if (drm_fd < 0) { | |
fprintf(stderr, "Could not open DRM device\n"); | |
return EXIT_FAILURE; | |
} | |
ok = find_crtc_comma_connector_comma_mode_and_overlay_plane(drm_fd, &connector_id, &mode, &crtc_id, &plane_id); | |
if (ok != 0) return EXIT_FAILURE; | |
width = mode->hdisplay; | |
height = mode->vdisplay; | |
ok = create_fb(drm_fd, width, height, NULL, NULL, NULL, fb_ids + 0); | |
if (ok != 0) return EXIT_FAILURE; | |
ok = create_fb(drm_fd, width, height, NULL, NULL, NULL, fb_ids + 1); | |
if (ok != 0) return EXIT_FAILURE; | |
ok = create_fb(drm_fd, width, height, NULL, NULL, NULL, fb_ids + 2); | |
if (ok != 0) return EXIT_FAILURE; | |
ok = create_fb(drm_fd, width, height, NULL, NULL, NULL, fb_ids + 3); | |
if (ok != 0) return EXIT_FAILURE; | |
ok = drmModeSetCrtc(drm_fd, crtc_id, fb_ids[current_fb_id], 0, 0, &connector_id, 1, mode); | |
if (ok) { | |
fprintf(stderr, "failed to set mode: %s\n", strerror(errno)); | |
return EXIT_FAILURE; | |
} | |
current_fb_id = 0; | |
while (1) { | |
clock_gettime(CLOCK_MONOTONIC, &t0); | |
ok = drmModePageFlip( | |
drm_fd, | |
crtc_id, | |
fb_ids[current_fb_id], | |
DRM_MODE_PAGE_FLIP_EVENT, | |
NULL | |
); | |
if (ok < 0) { | |
fprintf(stderr, "Could not schedule page flip: %s\n", strerror(errno)); | |
return EXIT_FAILURE; | |
} | |
ok = drmModeSetPlane( | |
drm_fd, | |
plane_id, | |
crtc_id, | |
fb_ids[current_fb_id + 2], | |
0, | |
0, 0, | |
width, height, | |
0, 0, | |
((uint16_t) width) << 16, ((uint16_t) height) << 16 | |
); | |
if (ok < 0) { | |
fprintf(stderr, "Could not set new fb: %s\n", strerror(errno)); | |
return EXIT_FAILURE; | |
} | |
current_fb_id ^= 1; | |
clock_gettime(CLOCK_MONOTONIC, &t1); | |
uint64_t delta = ((t1.tv_sec - t0.tv_sec) * 1000000000L) + (t1.tv_nsec - t0.tv_nsec); | |
printf("drmModeSetPlane took %09lluns\n", delta); | |
} | |
return EXIT_SUCCESS; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment