1 /*
2 * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
21 */
22
23 #include "wayland-eglstream.h"
24 #include "wayland-eglstream-server.h"
25 #include "wayland-thread.h"
26 #include "wayland-eglhandle.h"
27 #include "wayland-egldisplay.h"
28 #include "wayland-eglutils.h"
29 #include "wayland-egl-ext.h"
30 #include <unistd.h>
31 #include <stdlib.h>
32 #include <assert.h>
33
34 #define WL_EGL_CONN_WAIT_USECS 1e3 /* 1 msec */
35 #define WL_EGL_CONN_TIMEOUT_USECS 1e6 /* 1 sec */
36
wlEglCreateStreamAttribHook(EGLDisplay dpy,const EGLAttrib * attribs)37 EGLStreamKHR wlEglCreateStreamAttribHook(EGLDisplay dpy,
38 const EGLAttrib *attribs)
39 {
40 WlEglPlatformData *data = NULL;
41 EGLStreamKHR stream = EGL_NO_STREAM_KHR;
42 struct wl_eglstream_display *wlStreamDpy = NULL;
43 struct wl_resource *resource = NULL;
44 struct wl_eglstream *wlStream = NULL;
45 int nAttribs = 0;
46 int idx = 0;
47 int fd = -1;
48 EGLint err = EGL_SUCCESS;
49
50 /* Parse attribute list and count internal attributes */
51 if (attribs) {
52 while (attribs[idx] != EGL_NONE) {
53 if (attribs[idx] == EGL_WAYLAND_EGLSTREAM_WL) {
54 if (resource != NULL) {
55 err = EGL_BAD_MATCH;
56 break;
57 }
58
59 resource = (struct wl_resource *)attribs[idx + 1];
60 if (resource == NULL) {
61 err = EGL_BAD_ACCESS;
62 break;
63 }
64 } else {
65 /* Internal attribute */
66 nAttribs++;
67 }
68 idx += 2;
69 }
70 }
71
72 if ((err == EGL_SUCCESS) && (resource == NULL)) {
73 /* No EGL_WAYLAND_EGLSTREAM_WL attribute provided, which means dpy is
74 * external. Forward this call to the underlying driver as there's
75 * nothing to do here */
76 WlEglDisplay *display = (WlEglDisplay *)dpy;
77 return display->data->egl.createStreamAttrib(display->devDpy->eglDisplay,
78 attribs);
79 }
80
81 /* Otherwise, we must create a server-side stream */
82 wlExternalApiLock();
83
84 wlStreamDpy = wl_eglstream_display_get(dpy);
85 if (wlStreamDpy == NULL) {
86 err = EGL_BAD_ACCESS;
87 } else {
88 data = wlStreamDpy->data;
89 }
90
91 if (err != EGL_SUCCESS) {
92 goto fail;
93 }
94
95 wlStream = wl_eglstream_display_get_stream(wlStreamDpy, resource);
96 if (wlStream == NULL) {
97 err = EGL_BAD_ACCESS;
98 goto fail;
99 }
100
101 if (wlStream->eglStream != EGL_NO_STREAM_KHR ||
102 wlStream->handle == -1) {
103 err = EGL_BAD_STREAM_KHR;
104 goto fail;
105 }
106
107 if (wlStream->fromFd) {
108 /* Check for EGL_KHR_stream_cross_process_fd support */
109 if (!wlStreamDpy->exts.stream_cross_process_fd) {
110 err = EGL_BAD_ACCESS;
111 goto fail;
112 }
113
114 /* eglCreateStreamFromFileDescriptorKHR from
115 * EGL_KHR_stream_cross_process_fd does not take attributes. Thus, only
116 * EGL_WAYLAND_EGLSTREAM_WL should have been specified and processed
117 * above. caps_override is an exception to this, since the wayland
118 * compositor calling into this function wouldn't be aware of an
119 * override in place */
120 if (nAttribs != 0 && !wlStreamDpy->caps_override) {
121 err = EGL_BAD_ATTRIBUTE;
122 goto fail;
123 }
124
125 fd = wlStream->handle;
126 stream = data->egl.createStreamFromFD(dpy, wlStream->handle);
127
128 /* Clean up */
129 close(fd);
130 wlStream->handle = -1;
131 }
132 #if defined(EGL_NV_stream_attrib) && \
133 defined(EGL_NV_stream_remote) && \
134 defined(EGL_NV_stream_socket)
135 else {
136 EGLAttrib *attribs2 = NULL;
137
138 /* Check for required extensions support */
139 if (!wlStreamDpy->exts.stream_attrib ||
140 !wlStreamDpy->exts.stream_remote ||
141 !wlStreamDpy->exts.stream_socket ||
142 (!wlStreamDpy->exts.stream_socket_inet &&
143 !wlStreamDpy->exts.stream_socket_unix)) {
144 err = EGL_BAD_ACCESS;
145 goto fail;
146 }
147
148 /* If not inet connection supported, wlStream should not be inet */
149 if (!wlStreamDpy->exts.stream_socket_inet &&
150 wlStream->isInet) {
151 err = EGL_BAD_ACCESS;
152 goto fail;
153 }
154
155 /* Create attributes array to pass down to the actual EGL stream
156 * creation function */
157 attribs2 = (EGLAttrib *)malloc((2*(nAttribs + 5) + 1)*sizeof(*attribs2));
158
159 nAttribs = 0;
160 attribs2[nAttribs++] = EGL_STREAM_TYPE_NV;
161 attribs2[nAttribs++] = EGL_STREAM_CROSS_PROCESS_NV;
162 attribs2[nAttribs++] = EGL_STREAM_PROTOCOL_NV;
163 attribs2[nAttribs++] = EGL_STREAM_PROTOCOL_SOCKET_NV;
164 attribs2[nAttribs++] = EGL_STREAM_ENDPOINT_NV;
165 attribs2[nAttribs++] = EGL_STREAM_CONSUMER_NV;
166 attribs2[nAttribs++] = EGL_SOCKET_TYPE_NV;
167 attribs2[nAttribs++] = (wlStream->isInet ? EGL_SOCKET_TYPE_INET_NV :
168 EGL_SOCKET_TYPE_UNIX_NV);
169 attribs2[nAttribs++] = EGL_SOCKET_HANDLE_NV;
170 attribs2[nAttribs++] = (EGLAttrib)wlStream->handle;
171
172 /* Include internal attributes given by the application */
173 while (attribs && attribs[0] != EGL_NONE) {
174 switch (attribs[0]) {
175 /* Filter out external attributes */
176 case EGL_WAYLAND_EGLSTREAM_WL:
177 break;
178
179 /* EGL_NV_stream_remote attributes shouldn't be set by the
180 * application */
181 case EGL_STREAM_TYPE_NV:
182 case EGL_STREAM_PROTOCOL_NV:
183 case EGL_STREAM_ENDPOINT_NV:
184 case EGL_SOCKET_TYPE_NV:
185 case EGL_SOCKET_HANDLE_NV:
186 free(attribs2);
187 err = EGL_BAD_ATTRIBUTE;
188 goto fail;
189
190 /* Everything else is fine and will be handled by EGL */
191 default:
192 attribs2[nAttribs++] = attribs[0];
193 attribs2[nAttribs++] = attribs[1];
194 }
195
196 attribs += 2;
197 }
198 attribs2[nAttribs] = EGL_NONE;
199
200 stream = data->egl.createStreamAttrib(dpy, attribs2);
201
202 /* Clean up */
203 free(attribs2);
204
205 if (stream != EGL_NO_STREAM_KHR) {
206 /* Wait for the stream to establish connection with the producer's
207 * side */
208 uint32_t timeout = WL_EGL_CONN_TIMEOUT_USECS;
209 EGLint state = EGL_STREAM_STATE_INITIALIZING_NV;
210
211 do {
212 usleep(WL_EGL_CONN_WAIT_USECS);
213 timeout -= WL_EGL_CONN_WAIT_USECS;
214
215 if (!data->egl.queryStream(dpy,
216 stream,
217 EGL_STREAM_STATE_KHR,
218 &state)) {
219 break;
220 }
221 } while ((state == EGL_STREAM_STATE_INITIALIZING_NV) &&
222 (timeout > 0));
223
224 if (state == EGL_STREAM_STATE_INITIALIZING_NV) {
225 data->egl.destroyStream(dpy, stream);
226 stream = EGL_NO_STREAM_KHR;
227 }
228 }
229 }
230 #endif
231
232 if (stream == EGL_NO_STREAM_KHR) {
233 err = EGL_BAD_ACCESS;
234 goto fail;
235 }
236
237 wlStream->eglStream = stream;
238 wlStream->handle = -1;
239
240 wlExternalApiUnlock();
241
242 return stream;
243
244 fail:
245 wlExternalApiUnlock();
246 wlEglSetError(data, err);
247 return EGL_NO_STREAM_KHR;
248 }
249
250