surfaceless support
Rob Clark
3 years ago
21 | 21 | * DEALINGS IN THE SOFTWARE. |
22 | 22 | */ |
23 | 23 | |
24 | #include <assert.h> | |
24 | 25 | #include <errno.h> |
25 | 26 | #include <fcntl.h> |
26 | 27 | #include <stdbool.h> |
28 | 29 | #include <stdlib.h> |
29 | 30 | #include <string.h> |
30 | 31 | #include <time.h> |
32 | #include <unistd.h> | |
31 | 33 | |
32 | 34 | #include "common.h" |
33 | 35 | |
39 | 41 | uint32_t format, |
40 | 42 | const uint64_t *modifiers, |
41 | 43 | const unsigned int count); |
44 | WEAK struct gbm_bo * | |
45 | gbm_bo_create_with_modifiers(struct gbm_device *gbm, | |
46 | uint32_t width, uint32_t height, | |
47 | uint32_t format, | |
48 | const uint64_t *modifiers, | |
49 | const unsigned int count); | |
50 | ||
51 | static struct gbm_bo * init_bo(uint64_t modifier) | |
52 | { | |
53 | struct gbm_bo *bo = NULL; | |
54 | ||
55 | if (gbm_bo_create_with_modifiers) { | |
56 | bo = gbm_bo_create_with_modifiers(gbm.dev, | |
57 | gbm.width, gbm.height, | |
58 | gbm.format, | |
59 | &modifier, 1); | |
60 | } | |
61 | ||
62 | if (!bo) { | |
63 | if (modifier != DRM_FORMAT_MOD_LINEAR) { | |
64 | fprintf(stderr, "Modifiers requested but support isn't available\n"); | |
65 | return NULL; | |
66 | } | |
67 | ||
68 | bo = gbm_bo_create(gbm.dev, | |
69 | gbm.width, gbm.height, | |
70 | gbm.format, | |
71 | GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); | |
72 | } | |
73 | ||
74 | if (!bo) { | |
75 | printf("failed to create gbm bo\n"); | |
76 | return NULL; | |
77 | } | |
78 | ||
79 | return bo; | |
80 | } | |
81 | ||
82 | static struct gbm * init_surfaceless(uint64_t modifier) | |
83 | { | |
84 | for (unsigned i = 0; i < ARRAY_SIZE(gbm.bos); i++) { | |
85 | gbm.bos[i] = init_bo(modifier); | |
86 | if (!gbm.bos[i]) | |
87 | return NULL; | |
88 | } | |
89 | return &gbm; | |
90 | } | |
42 | 91 | |
43 | 92 | static struct gbm * init_surface(uint64_t modifier) |
44 | 93 | { |
70 | 119 | return &gbm; |
71 | 120 | } |
72 | 121 | |
73 | const struct gbm * init_gbm(int drm_fd, int w, int h, uint32_t format, uint64_t modifier) | |
122 | const struct gbm * init_gbm(int drm_fd, int w, int h, uint32_t format, | |
123 | uint64_t modifier, bool surfaceless) | |
74 | 124 | { |
75 | 125 | gbm.dev = gbm_create_device(drm_fd); |
76 | 126 | gbm.format = format; |
78 | 128 | |
79 | 129 | gbm.width = w; |
80 | 130 | gbm.height = h; |
131 | ||
132 | if (surfaceless) | |
133 | return init_surfaceless(modifier); | |
81 | 134 | |
82 | 135 | return init_surface(modifier); |
83 | 136 | } |
164 | 217 | free(configs); |
165 | 218 | if (config_index == -1) |
166 | 219 | return false; |
220 | ||
221 | return true; | |
222 | } | |
223 | ||
224 | static bool | |
225 | create_framebuffer(const struct egl *egl, struct gbm_bo *bo, | |
226 | struct framebuffer *fb) { | |
227 | assert(egl->eglCreateImageKHR); | |
228 | assert(bo); | |
229 | assert(fb); | |
230 | ||
231 | // 1. Create EGLImage. | |
232 | int fd = gbm_bo_get_fd(bo); | |
233 | if (fd < 0) { | |
234 | printf("failed to get fd for bo: %d\n", fd); | |
235 | return false; | |
236 | } | |
237 | ||
238 | EGLint khr_image_attrs[17] = { | |
239 | EGL_WIDTH, gbm_bo_get_width(bo), | |
240 | EGL_HEIGHT, gbm_bo_get_height(bo), | |
241 | EGL_LINUX_DRM_FOURCC_EXT, (int)gbm_bo_get_format(bo), | |
242 | EGL_DMA_BUF_PLANE0_FD_EXT, fd, | |
243 | EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0, | |
244 | EGL_DMA_BUF_PLANE0_PITCH_EXT, gbm_bo_get_stride(bo), | |
245 | EGL_NONE, EGL_NONE, /* modifier lo */ | |
246 | EGL_NONE, EGL_NONE, /* modifier hi */ | |
247 | EGL_NONE, | |
248 | }; | |
249 | ||
250 | if (egl->modifiers_supported) { | |
251 | const uint64_t modifier = gbm_bo_get_modifier(bo); | |
252 | if (modifier != DRM_FORMAT_MOD_LINEAR) { | |
253 | size_t attrs_index = 12; | |
254 | khr_image_attrs[attrs_index++] = | |
255 | EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT; | |
256 | khr_image_attrs[attrs_index++] = modifier & 0xfffffffful; | |
257 | khr_image_attrs[attrs_index++] = | |
258 | EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT; | |
259 | khr_image_attrs[attrs_index++] = modifier >> 32; | |
260 | } | |
261 | } | |
262 | ||
263 | fb->image = egl->eglCreateImageKHR(egl->display, EGL_NO_CONTEXT, | |
264 | EGL_LINUX_DMA_BUF_EXT, NULL /* no client buffer */, | |
265 | khr_image_attrs); | |
266 | ||
267 | if (fb->image == EGL_NO_IMAGE_KHR) { | |
268 | printf("failed to make image from buffer object\n"); | |
269 | return false; | |
270 | } | |
271 | ||
272 | // EGLImage takes the fd ownership. | |
273 | close(fd); | |
274 | ||
275 | // 2. Create GL texture and framebuffer. | |
276 | glGenTextures(1, &fb->tex); | |
277 | glBindTexture(GL_TEXTURE_2D, fb->tex); | |
278 | egl->glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, fb->image); | |
279 | glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
280 | glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
281 | glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
282 | glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
283 | glBindTexture(GL_TEXTURE_2D, 0); | |
284 | ||
285 | glGenFramebuffers(1, &fb->fb); | |
286 | glBindFramebuffer(GL_FRAMEBUFFER, fb->fb); | |
287 | glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, | |
288 | fb->tex, 0); | |
289 | ||
290 | if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { | |
291 | printf("failed framebuffer check for created target buffer\n"); | |
292 | glDeleteFramebuffers(1, &fb->fb); | |
293 | glDeleteTextures(1, &fb->tex); | |
294 | return false; | |
295 | } | |
167 | 296 | |
168 | 297 | return true; |
169 | 298 | } |
259 | 388 | return -1; |
260 | 389 | } |
261 | 390 | |
262 | egl->surface = eglCreateWindowSurface(egl->display, egl->config, | |
263 | (EGLNativeWindowType)gbm->surface, NULL); | |
264 | if (egl->surface == EGL_NO_SURFACE) { | |
265 | printf("failed to create egl surface\n"); | |
266 | return -1; | |
391 | if (!gbm->surface) { | |
392 | egl->surface = EGL_NO_SURFACE; | |
393 | } else { | |
394 | egl->surface = eglCreateWindowSurface(egl->display, egl->config, | |
395 | (EGLNativeWindowType)gbm->surface, NULL); | |
396 | if (egl->surface == EGL_NO_SURFACE) { | |
397 | printf("failed to create egl surface\n"); | |
398 | return -1; | |
399 | } | |
267 | 400 | } |
268 | 401 | |
269 | 402 | /* connect the context to the surface */ |
292 | 425 | get_proc_gl(GL_AMD_performance_monitor, glEndPerfMonitorAMD); |
293 | 426 | get_proc_gl(GL_AMD_performance_monitor, glGetPerfMonitorCounterDataAMD); |
294 | 427 | |
428 | if (!gbm->surface) { | |
429 | for (unsigned i = 0; i < ARRAY_SIZE(gbm->bos); i++) { | |
430 | if (!create_framebuffer(egl, gbm->bos[i], &egl->fbs[i])) { | |
431 | printf("failed to create framebuffer\n"); | |
432 | return -1; | |
433 | } | |
434 | } | |
435 | } | |
436 | ||
295 | 437 | return 0; |
296 | 438 | } |
297 | 439 |
95 | 95 | #define EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT 0x344A |
96 | 96 | #endif |
97 | 97 | |
98 | #define NUM_BUFFERS 2 | |
99 | ||
98 | 100 | struct gbm { |
99 | 101 | struct gbm_device *dev; |
100 | 102 | struct gbm_surface *surface; |
103 | struct gbm_bo *bos[NUM_BUFFERS]; /* for the surfaceless case */ | |
101 | 104 | uint32_t format; |
102 | 105 | int width, height; |
103 | 106 | }; |
104 | 107 | |
105 | const struct gbm * init_gbm(int drm_fd, int w, int h, uint32_t format, uint64_t modifier); | |
106 | ||
108 | const struct gbm * init_gbm(int drm_fd, int w, int h, uint32_t format, uint64_t modifier, bool surfaceless); | |
109 | ||
110 | struct framebuffer { | |
111 | EGLImageKHR image; | |
112 | GLuint tex; | |
113 | GLuint fb; | |
114 | }; | |
107 | 115 | |
108 | 116 | struct egl { |
109 | 117 | EGLDisplay display; |
110 | 118 | EGLConfig config; |
111 | 119 | EGLContext context; |
112 | 120 | EGLSurface surface; |
121 | struct framebuffer fbs[NUM_BUFFERS]; /* for the surfaceless case */ | |
113 | 122 | |
114 | 123 | PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT; |
115 | 124 | PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR; |
195 | 195 | start_time = report_time = get_time_ns(); |
196 | 196 | |
197 | 197 | while (i < drm.count) { |
198 | unsigned frame = i; | |
198 | 199 | struct gbm_bo *next_bo; |
199 | 200 | EGLSyncKHR gpu_fence = NULL; /* out-fence from gpu, in-fence to kms */ |
200 | 201 | EGLSyncKHR kms_fence = NULL; /* in-fence to gpu, out-fence from kms */ |
221 | 222 | start_time = report_time = get_time_ns(); |
222 | 223 | } |
223 | 224 | |
225 | if (!gbm->surface) { | |
226 | glBindFramebuffer(GL_FRAMEBUFFER, egl->fbs[frame % NUM_BUFFERS].fb); | |
227 | } | |
228 | ||
224 | 229 | egl->draw(i++); |
225 | 230 | |
226 | 231 | /* insert fence to be singled in cmdstream.. this fence will be |
229 | 234 | gpu_fence = create_fence(egl, EGL_NO_NATIVE_FENCE_FD_ANDROID); |
230 | 235 | assert(gpu_fence); |
231 | 236 | |
232 | eglSwapBuffers(egl->display, egl->surface); | |
237 | if (gbm->surface) { | |
238 | eglSwapBuffers(egl->display, egl->surface); | |
239 | } | |
233 | 240 | |
234 | 241 | /* after swapbuffers, gpu_fence should be flushed, so safe |
235 | 242 | * to get fd: |
238 | 245 | egl->eglDestroySyncKHR(egl->display, gpu_fence); |
239 | 246 | assert(drm.kms_in_fence_fd != -1); |
240 | 247 | |
241 | next_bo = gbm_surface_lock_front_buffer(gbm->surface); | |
248 | if (gbm->surface) { | |
249 | next_bo = gbm_surface_lock_front_buffer(gbm->surface); | |
250 | } else { | |
251 | next_bo = gbm->bos[frame % NUM_BUFFERS]; | |
252 | } | |
242 | 253 | if (!next_bo) { |
243 | 254 | printf("Failed to lock frontbuffer\n"); |
244 | 255 | return -1; |
299 | 310 | } |
300 | 311 | |
301 | 312 | /* release last buffer to render on again: */ |
302 | if (bo) | |
313 | if (bo && gbm->surface) | |
303 | 314 | gbm_surface_release_buffer(gbm->surface, bo); |
304 | 315 | bo = next_bo; |
305 | 316 |
53 | 53 | int64_t start_time, report_time, cur_time; |
54 | 54 | int ret; |
55 | 55 | |
56 | eglSwapBuffers(egl->display, egl->surface); | |
57 | bo = gbm_surface_lock_front_buffer(gbm->surface); | |
56 | if (gbm->surface) { | |
57 | eglSwapBuffers(egl->display, egl->surface); | |
58 | bo = gbm_surface_lock_front_buffer(gbm->surface); | |
59 | } else { | |
60 | bo = gbm->bos[0]; | |
61 | } | |
58 | 62 | fb = drm_fb_get_from_bo(bo); |
59 | 63 | if (!fb) { |
60 | 64 | fprintf(stderr, "Failed to get a new framebuffer BO\n"); |
72 | 76 | start_time = report_time = get_time_ns(); |
73 | 77 | |
74 | 78 | while (i < drm.count) { |
79 | unsigned frame = i; | |
75 | 80 | struct gbm_bo *next_bo; |
76 | 81 | int waiting_for_flip = 1; |
77 | 82 | |
82 | 87 | start_time = report_time = get_time_ns(); |
83 | 88 | } |
84 | 89 | |
90 | if (!gbm->surface) { | |
91 | glBindFramebuffer(GL_FRAMEBUFFER, egl->fbs[frame % NUM_BUFFERS].fb); | |
92 | } | |
93 | ||
85 | 94 | egl->draw(i++); |
86 | 95 | |
87 | eglSwapBuffers(egl->display, egl->surface); | |
88 | next_bo = gbm_surface_lock_front_buffer(gbm->surface); | |
96 | if (gbm->surface) { | |
97 | eglSwapBuffers(egl->display, egl->surface); | |
98 | next_bo = gbm_surface_lock_front_buffer(gbm->surface); | |
99 | } else { | |
100 | glFinish(); | |
101 | next_bo = gbm->bos[frame % NUM_BUFFERS]; | |
102 | } | |
89 | 103 | fb = drm_fb_get_from_bo(next_bo); |
90 | 104 | if (!fb) { |
91 | 105 | fprintf(stderr, "Failed to get a new framebuffer BO\n"); |
134 | 148 | } |
135 | 149 | |
136 | 150 | /* release last buffer to render on again: */ |
137 | gbm_surface_release_buffer(gbm->surface, bo); | |
151 | if (gbm->surface) { | |
152 | gbm_surface_release_buffer(gbm->surface, bo); | |
153 | } | |
138 | 154 | bo = next_bo; |
139 | 155 | } |
140 | 156 |
40 | 40 | static const struct gbm *gbm; |
41 | 41 | static const struct drm *drm; |
42 | 42 | |
43 | static const char *shortopts = "Ac:D:f:M:m:p:S:s:V:v:"; | |
43 | static const char *shortopts = "Ac:D:f:M:m:p:S:s:V:v:x"; | |
44 | 44 | |
45 | 45 | static const struct option longopts[] = { |
46 | 46 | {"atomic", no_argument, 0, 'A'}, |
53 | 53 | {"samples", required_argument, 0, 's'}, |
54 | 54 | {"video", required_argument, 0, 'V'}, |
55 | 55 | {"vmode", required_argument, 0, 'v'}, |
56 | {"surfaceless", no_argument, 0, 'x'}, | |
56 | 57 | {0, 0, 0, 0} |
57 | 58 | }; |
58 | 59 | |
59 | 60 | static void usage(const char *name) |
60 | 61 | { |
61 | printf("Usage: %s [-ADfMmSsVv]\n" | |
62 | printf("Usage: %s [-ADfMmSsVvx]\n" | |
62 | 63 | "\n" |
63 | 64 | "options:\n" |
64 | 65 | " -A, --atomic use atomic modesetting and fencing\n" |
78 | 79 | " -s, --samples=N use MSAA\n" |
79 | 80 | " -V, --video=FILE video textured cube (comma separated list)\n" |
80 | 81 | " -v, --vmode=VMODE specify the video mode in the format\n" |
81 | " <mode>[-<vrefresh>]\n", | |
82 | " <mode>[-<vrefresh>]\n" | |
83 | " -x, --surfaceless use surfaceless mode, instead of gbm surface\n" | |
84 | , | |
82 | 85 | name); |
83 | 86 | } |
84 | 87 | |
99 | 102 | unsigned int len; |
100 | 103 | unsigned int vrefresh = 0; |
101 | 104 | unsigned int count = ~0; |
105 | bool surfaceless = false; | |
102 | 106 | |
103 | 107 | #ifdef HAVE_GST |
104 | 108 | gst_init(&argc, &argv); |
176 | 180 | strncpy(mode_str, optarg, len); |
177 | 181 | mode_str[len] = '\0'; |
178 | 182 | break; |
183 | case 'x': | |
184 | surfaceless = true; | |
185 | break; | |
179 | 186 | default: |
180 | 187 | usage(argv[0]); |
181 | 188 | return -1; |
192 | 199 | } |
193 | 200 | |
194 | 201 | gbm = init_gbm(drm->fd, drm->mode->hdisplay, drm->mode->vdisplay, |
195 | format, modifier); | |
202 | format, modifier, surfaceless); | |
196 | 203 | if (!gbm) { |
197 | 204 | printf("failed to initialize GBM\n"); |
198 | 205 | return -1; |