egl: Fix a false negative check in _eglCheckMakeCurrent.
[mesa.git] / src / egl / main / eglcontext.c
1 #include <assert.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include "eglconfig.h"
5 #include "eglcontext.h"
6 #include "egldisplay.h"
7 #include "eglcurrent.h"
8 #include "eglsurface.h"
9 #include "egllog.h"
10
11
12 /**
13 * Return the API bit (one of EGL_xxx_BIT) of the context.
14 */
15 static EGLint
16 _eglGetContextAPIBit(_EGLContext *ctx)
17 {
18 EGLint bit = 0;
19
20 switch (ctx->ClientAPI) {
21 case EGL_OPENGL_ES_API:
22 switch (ctx->ClientVersion) {
23 case 1:
24 bit = EGL_OPENGL_ES_BIT;
25 break;
26 case 2:
27 bit = EGL_OPENGL_ES2_BIT;
28 break;
29 default:
30 break;
31 }
32 break;
33 case EGL_OPENVG_API:
34 bit = EGL_OPENVG_BIT;
35 break;
36 case EGL_OPENGL_API:
37 bit = EGL_OPENGL_BIT;
38 break;
39 default:
40 break;
41 }
42
43 return bit;
44 }
45
46
47 /**
48 * Parse the list of context attributes and return the proper error code.
49 */
50 static EGLint
51 _eglParseContextAttribList(_EGLContext *ctx, const EGLint *attrib_list)
52 {
53 EGLenum api = ctx->ClientAPI;
54 EGLint i, err = EGL_SUCCESS;
55
56 if (!attrib_list)
57 return EGL_SUCCESS;
58
59 for (i = 0; attrib_list[i] != EGL_NONE; i++) {
60 EGLint attr = attrib_list[i++];
61 EGLint val = attrib_list[i];
62
63 switch (attr) {
64 case EGL_CONTEXT_CLIENT_VERSION:
65 if (api != EGL_OPENGL_ES_API) {
66 err = EGL_BAD_ATTRIBUTE;
67 break;
68 }
69 if (val != 1 && val != 2) {
70 err = EGL_BAD_ATTRIBUTE;
71 break;
72 }
73 ctx->ClientVersion = val;
74 break;
75 default:
76 err = EGL_BAD_ATTRIBUTE;
77 break;
78 }
79
80 if (err != EGL_SUCCESS) {
81 _eglLog(_EGL_DEBUG, "bad context attribute 0x%04x", attr);
82 break;
83 }
84 }
85
86 return err;
87 }
88
89
90 /**
91 * Initialize the given _EGLContext object to defaults and/or the values
92 * in the attrib_list.
93 */
94 EGLBoolean
95 _eglInitContext(_EGLContext *ctx, _EGLDisplay *dpy, _EGLConfig *conf,
96 const EGLint *attrib_list)
97 {
98 const EGLenum api = eglQueryAPI();
99 EGLint err;
100
101 if (api == EGL_NONE) {
102 _eglError(EGL_BAD_MATCH, "eglCreateContext(no client API)");
103 return EGL_FALSE;
104 }
105
106 _eglInitResource(&ctx->Resource, sizeof(*ctx), dpy);
107 ctx->ClientAPI = api;
108 ctx->Config = conf;
109 ctx->WindowRenderBuffer = EGL_NONE;
110
111 ctx->ClientVersion = 1; /* the default, per EGL spec */
112
113 err = _eglParseContextAttribList(ctx, attrib_list);
114 if (err == EGL_SUCCESS && ctx->Config) {
115 EGLint api_bit;
116
117 api_bit = _eglGetContextAPIBit(ctx);
118 if (!(ctx->Config->RenderableType & api_bit)) {
119 _eglLog(_EGL_DEBUG, "context api is 0x%x while config supports 0x%x",
120 api_bit, ctx->Config->RenderableType);
121 err = EGL_BAD_CONFIG;
122 }
123 }
124 if (err != EGL_SUCCESS)
125 return _eglError(err, "eglCreateContext");
126
127 return EGL_TRUE;
128 }
129
130
131 #ifdef EGL_VERSION_1_2
132 static EGLint
133 _eglQueryContextRenderBuffer(_EGLContext *ctx)
134 {
135 _EGLSurface *surf = ctx->DrawSurface;
136 EGLint rb;
137
138 if (!surf)
139 return EGL_NONE;
140 if (surf->Type == EGL_WINDOW_BIT && ctx->WindowRenderBuffer != EGL_NONE)
141 rb = ctx->WindowRenderBuffer;
142 else
143 rb = surf->RenderBuffer;
144 return rb;
145 }
146 #endif /* EGL_VERSION_1_2 */
147
148
149 EGLBoolean
150 _eglQueryContext(_EGLDriver *drv, _EGLDisplay *dpy, _EGLContext *c,
151 EGLint attribute, EGLint *value)
152 {
153 (void) drv;
154 (void) dpy;
155
156 if (!value)
157 return _eglError(EGL_BAD_PARAMETER, "eglQueryContext");
158
159 switch (attribute) {
160 case EGL_CONFIG_ID:
161 if (!c->Config)
162 return _eglError(EGL_BAD_ATTRIBUTE, "eglQueryContext");
163 *value = c->Config->ConfigID;
164 break;
165 case EGL_CONTEXT_CLIENT_VERSION:
166 *value = c->ClientVersion;
167 break;
168 #ifdef EGL_VERSION_1_2
169 case EGL_CONTEXT_CLIENT_TYPE:
170 *value = c->ClientAPI;
171 break;
172 case EGL_RENDER_BUFFER:
173 *value = _eglQueryContextRenderBuffer(c);
174 break;
175 #endif /* EGL_VERSION_1_2 */
176 default:
177 return _eglError(EGL_BAD_ATTRIBUTE, "eglQueryContext");
178 }
179
180 return EGL_TRUE;
181 }
182
183
184 /**
185 * Bind the context to the thread and return the previous context.
186 *
187 * Note that the context may be NULL.
188 */
189 static _EGLContext *
190 _eglBindContextToThread(_EGLContext *ctx, _EGLThreadInfo *t)
191 {
192 EGLint apiIndex;
193 _EGLContext *oldCtx;
194
195 apiIndex = (ctx) ?
196 _eglConvertApiToIndex(ctx->ClientAPI) : t->CurrentAPIIndex;
197
198 oldCtx = t->CurrentContexts[apiIndex];
199 if (ctx != oldCtx) {
200 if (oldCtx)
201 oldCtx->Binding = NULL;
202 if (ctx)
203 ctx->Binding = t;
204
205 t->CurrentContexts[apiIndex] = ctx;
206 }
207
208 return oldCtx;
209 }
210
211
212 /**
213 * Return true if the given context and surfaces can be made current.
214 */
215 static EGLBoolean
216 _eglCheckMakeCurrent(_EGLContext *ctx, _EGLSurface *draw, _EGLSurface *read)
217 {
218 _EGLThreadInfo *t = _eglGetCurrentThread();
219 _EGLDisplay *dpy;
220 EGLint conflict_api;
221 EGLBoolean surfaceless;
222
223 if (_eglIsCurrentThreadDummy())
224 return _eglError(EGL_BAD_ALLOC, "eglMakeCurrent");
225
226 /* this is easy */
227 if (!ctx) {
228 if (draw || read)
229 return _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
230 return EGL_TRUE;
231 }
232
233 dpy = ctx->Resource.Display;
234 switch (_eglGetContextAPIBit(ctx)) {
235 case EGL_OPENGL_ES_BIT:
236 surfaceless = dpy->Extensions.KHR_surfaceless_gles1;
237 break;
238 case EGL_OPENGL_ES2_BIT:
239 surfaceless = dpy->Extensions.KHR_surfaceless_gles2;
240 break;
241 case EGL_OPENGL_BIT:
242 surfaceless = dpy->Extensions.KHR_surfaceless_opengl;
243 break;
244 default:
245 surfaceless = EGL_FALSE;
246 break;
247 }
248
249 if (!surfaceless && (draw == NULL || read == NULL))
250 return _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
251
252 /*
253 * The spec says
254 *
255 * "If ctx is current to some other thread, or if either draw or read are
256 * bound to contexts in another thread, an EGL_BAD_ACCESS error is
257 * generated."
258 *
259 * and
260 *
261 * "at most one context may be bound to a particular surface at a given
262 * time"
263 */
264 if (ctx->Binding && ctx->Binding != t)
265 return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
266 if (draw && draw->CurrentContext && draw->CurrentContext != ctx) {
267 if (draw->CurrentContext->Binding != t ||
268 draw->CurrentContext->ClientAPI != ctx->ClientAPI)
269 return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
270 }
271 if (read && read->CurrentContext && read->CurrentContext != ctx) {
272 if (read->CurrentContext->Binding != t ||
273 read->CurrentContext->ClientAPI != ctx->ClientAPI)
274 return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
275 }
276
277 /* simply require the configs to be equal */
278 if ((draw && draw->Config != ctx->Config) ||
279 (read && read->Config != ctx->Config))
280 return _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
281
282 switch (ctx->ClientAPI) {
283 #ifdef EGL_VERSION_1_4
284 /* OpenGL and OpenGL ES are conflicting */
285 case EGL_OPENGL_ES_API:
286 conflict_api = EGL_OPENGL_API;
287 break;
288 case EGL_OPENGL_API:
289 conflict_api = EGL_OPENGL_ES_API;
290 break;
291 #endif
292 default:
293 conflict_api = -1;
294 break;
295 }
296
297 if (conflict_api >= 0 && _eglGetAPIContext(conflict_api))
298 return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
299
300 return EGL_TRUE;
301 }
302
303
304 /**
305 * Bind the context to the current thread and given surfaces. Return the
306 * previous bound context and surfaces. The caller should unreference the
307 * returned context and surfaces.
308 *
309 * Making a second call with the resources returned by the first call
310 * unsurprisingly undoes the first call, except for the resouce reference
311 * counts.
312 */
313 EGLBoolean
314 _eglBindContext(_EGLContext *ctx, _EGLSurface *draw, _EGLSurface *read,
315 _EGLContext **old_ctx,
316 _EGLSurface **old_draw, _EGLSurface **old_read)
317 {
318 _EGLThreadInfo *t = _eglGetCurrentThread();
319 _EGLContext *prev_ctx;
320 _EGLSurface *prev_draw, *prev_read;
321
322 if (!_eglCheckMakeCurrent(ctx, draw, read))
323 return EGL_FALSE;
324
325 /* increment refcounts before binding */
326 _eglGetContext(ctx);
327 _eglGetSurface(draw);
328 _eglGetSurface(read);
329
330 /* bind the new context */
331 prev_ctx = _eglBindContextToThread(ctx, t);
332
333 /* break previous bindings */
334 if (prev_ctx) {
335 prev_draw = prev_ctx->DrawSurface;
336 prev_read = prev_ctx->ReadSurface;
337
338 if (prev_draw)
339 prev_draw->CurrentContext = NULL;
340 if (prev_read)
341 prev_read->CurrentContext = NULL;
342
343 prev_ctx->DrawSurface = NULL;
344 prev_ctx->ReadSurface = NULL;
345 }
346 else {
347 prev_draw = prev_read = NULL;
348 }
349
350 /* establish new bindings */
351 if (ctx) {
352 if (draw)
353 draw->CurrentContext = ctx;
354 if (read)
355 read->CurrentContext = ctx;
356
357 ctx->DrawSurface = draw;
358 ctx->ReadSurface = read;
359 }
360
361 assert(old_ctx && old_draw && old_read);
362 *old_ctx = prev_ctx;
363 *old_draw = prev_draw;
364 *old_read = prev_read;
365
366 return EGL_TRUE;
367 }