e72664c23ccbf1b41d355141f5cc8e8f90535dcd
[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 memset(ctx, 0, sizeof(_EGLContext));
107 ctx->Resource.Display = dpy;
108 ctx->ClientAPI = api;
109 ctx->Config = conf;
110 ctx->WindowRenderBuffer = EGL_NONE;
111
112 ctx->ClientVersion = 1; /* the default, per EGL spec */
113
114 err = _eglParseContextAttribList(ctx, attrib_list);
115 if (err == EGL_SUCCESS && ctx->Config) {
116 EGLint renderable_type, api_bit;
117
118 renderable_type = GET_CONFIG_ATTRIB(ctx->Config, EGL_RENDERABLE_TYPE);
119 api_bit = _eglGetContextAPIBit(ctx);
120 if (!(renderable_type & api_bit)) {
121 _eglLog(_EGL_DEBUG, "context api is 0x%x while config supports 0x%x",
122 api_bit, renderable_type);
123 err = EGL_BAD_CONFIG;
124 }
125 }
126 if (err != EGL_SUCCESS)
127 return _eglError(err, "eglCreateContext");
128
129 return EGL_TRUE;
130 }
131
132
133 /**
134 * Just a placeholder/demo function. Real driver will never use this!
135 */
136 _EGLContext *
137 _eglCreateContext(_EGLDriver *drv, _EGLDisplay *dpy, _EGLConfig *conf,
138 _EGLContext *share_list, const EGLint *attrib_list)
139 {
140 return NULL;
141 }
142
143
144 /**
145 * Default fallback routine - drivers should usually override this.
146 */
147 EGLBoolean
148 _eglDestroyContext(_EGLDriver *drv, _EGLDisplay *dpy, _EGLContext *ctx)
149 {
150 if (!_eglIsContextBound(ctx))
151 free(ctx);
152 return EGL_TRUE;
153 }
154
155
156 #ifdef EGL_VERSION_1_2
157 static EGLint
158 _eglQueryContextRenderBuffer(_EGLContext *ctx)
159 {
160 _EGLSurface *surf = ctx->DrawSurface;
161 EGLint rb;
162
163 if (!surf)
164 return EGL_NONE;
165 if (surf->Type == EGL_WINDOW_BIT && ctx->WindowRenderBuffer != EGL_NONE)
166 rb = ctx->WindowRenderBuffer;
167 else
168 rb = surf->RenderBuffer;
169 return rb;
170 }
171 #endif /* EGL_VERSION_1_2 */
172
173
174 EGLBoolean
175 _eglQueryContext(_EGLDriver *drv, _EGLDisplay *dpy, _EGLContext *c,
176 EGLint attribute, EGLint *value)
177 {
178 (void) drv;
179 (void) dpy;
180
181 if (!value)
182 return _eglError(EGL_BAD_PARAMETER, "eglQueryContext");
183
184 switch (attribute) {
185 case EGL_CONFIG_ID:
186 *value = GET_CONFIG_ATTRIB(c->Config, EGL_CONFIG_ID);
187 break;
188 case EGL_CONTEXT_CLIENT_VERSION:
189 *value = c->ClientVersion;
190 break;
191 #ifdef EGL_VERSION_1_2
192 case EGL_CONTEXT_CLIENT_TYPE:
193 *value = c->ClientAPI;
194 break;
195 case EGL_RENDER_BUFFER:
196 *value = _eglQueryContextRenderBuffer(c);
197 break;
198 #endif /* EGL_VERSION_1_2 */
199 default:
200 return _eglError(EGL_BAD_ATTRIBUTE, "eglQueryContext");
201 }
202
203 return EGL_TRUE;
204 }
205
206
207 /**
208 * Bind the context to the surfaces. Return the surfaces that are "orphaned".
209 * That is, when the context is not NULL, return the surfaces it previously
210 * bound to; when the context is NULL, the same surfaces are returned.
211 */
212 static void
213 _eglBindContextToSurfaces(_EGLContext *newCtx,
214 _EGLSurface **draw, _EGLSurface **read)
215 {
216 _EGLSurface *newDraw = *draw, *newRead = *read;
217 _EGLContext *oldCtx;
218
219 /*
220 * The goal is to bind a newCtx to newDraw. Since newDraw may already have
221 * a binding context (oldCtx), and newCtx may already be bound to another
222 * surface (oldDraw), the old bindings are broken first and the new one is
223 * created.
224 */
225 if (newDraw) {
226 oldCtx = newDraw->CurrentContext;
227 if (newCtx != oldCtx) {
228 if (oldCtx) {
229 assert(oldCtx->DrawSurface == newDraw);
230 oldCtx->DrawSurface = NULL;
231 }
232
233 newDraw->CurrentContext = newCtx;
234 }
235 }
236
237 if (newCtx) {
238 _EGLSurface *oldDraw = newCtx->DrawSurface;
239 if (oldDraw)
240 oldDraw->CurrentContext = NULL;
241
242 newCtx->DrawSurface = newDraw;
243 *draw = oldDraw;
244 }
245
246 /* likewise */
247 if (newRead && newRead != newDraw) {
248 oldCtx = newRead->CurrentContext;
249 if (newCtx != oldCtx) {
250 if (oldCtx) {
251 assert(oldCtx->ReadSurface == newRead);
252 oldCtx->ReadSurface = NULL;
253 }
254
255 newRead->CurrentContext = newCtx;
256 }
257 }
258
259 if (newCtx) {
260 _EGLSurface *oldRead = newCtx->ReadSurface;
261 if (oldRead)
262 oldRead->CurrentContext = NULL;
263
264 newCtx->ReadSurface = newRead;
265 *read = oldRead;
266 }
267
268 }
269
270
271 /**
272 * Bind the context to the thread and return the previous context.
273 *
274 * Note that the context may be NULL.
275 */
276 static _EGLContext *
277 _eglBindContextToThread(_EGLContext *ctx, _EGLThreadInfo *t)
278 {
279 EGLint apiIndex;
280 _EGLContext *oldCtx;
281
282 apiIndex = (ctx) ?
283 _eglConvertApiToIndex(ctx->ClientAPI) : t->CurrentAPIIndex;
284
285 oldCtx = t->CurrentContexts[apiIndex];
286 if (ctx != oldCtx) {
287 if (oldCtx)
288 oldCtx->Binding = NULL;
289 if (ctx)
290 ctx->Binding = t;
291
292 t->CurrentContexts[apiIndex] = ctx;
293 }
294
295 return oldCtx;
296 }
297
298
299 /**
300 * Return true if the given context and surfaces can be made current.
301 */
302 static EGLBoolean
303 _eglCheckMakeCurrent(_EGLContext *ctx, _EGLSurface *draw, _EGLSurface *read)
304 {
305 _EGLThreadInfo *t = _eglGetCurrentThread();
306 _EGLDisplay *dpy;
307 EGLint conflict_api;
308 EGLBoolean surfaceless;
309
310 if (_eglIsCurrentThreadDummy())
311 return _eglError(EGL_BAD_ALLOC, "eglMakeCurrent");
312
313 /* this is easy */
314 if (!ctx) {
315 if (draw || read)
316 return _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
317 return EGL_TRUE;
318 }
319
320 dpy = ctx->Resource.Display;
321 switch (_eglGetContextAPIBit(ctx)) {
322 case EGL_OPENGL_ES_BIT:
323 surfaceless = dpy->Extensions.KHR_surfaceless_gles1;
324 break;
325 case EGL_OPENGL_ES2_BIT:
326 surfaceless = dpy->Extensions.KHR_surfaceless_gles2;
327 break;
328 case EGL_OPENGL_BIT:
329 surfaceless = dpy->Extensions.KHR_surfaceless_opengl;
330 break;
331 default:
332 surfaceless = EGL_FALSE;
333 break;
334 }
335
336 if (!surfaceless && (draw == NULL || read == NULL))
337 return _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
338
339 /* context stealing from another thread is not allowed */
340 if (ctx->Binding && ctx->Binding != t)
341 return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
342
343 /*
344 * The spec says
345 *
346 * "If ctx is current to some other thread, or if either draw or read are
347 * bound to contexts in another thread, an EGL_BAD_ACCESS error is
348 * generated."
349 *
350 * But it also says
351 *
352 * "at most one context may be bound to a particular surface at a given
353 * time"
354 *
355 * The latter is more restrictive so we can check only the latter case.
356 */
357 if ((draw && draw->CurrentContext && draw->CurrentContext != ctx) ||
358 (read && read->CurrentContext && read->CurrentContext != ctx))
359 return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
360
361 /* simply require the configs to be equal */
362 if ((draw && draw->Config != ctx->Config) ||
363 (read && read->Config != ctx->Config))
364 return _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
365
366 switch (ctx->ClientAPI) {
367 #ifdef EGL_VERSION_1_4
368 /* OpenGL and OpenGL ES are conflicting */
369 case EGL_OPENGL_ES_API:
370 conflict_api = EGL_OPENGL_API;
371 break;
372 case EGL_OPENGL_API:
373 conflict_api = EGL_OPENGL_ES_API;
374 break;
375 #endif
376 default:
377 conflict_api = -1;
378 break;
379 }
380
381 if (conflict_api >= 0 && _eglGetAPIContext(conflict_api))
382 return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
383
384 return EGL_TRUE;
385 }
386
387
388 /**
389 * Bind the context to the current thread and given surfaces. Return the
390 * previously bound context and the surfaces it bound to. Each argument is
391 * both input and output.
392 */
393 EGLBoolean
394 _eglBindContext(_EGLContext **ctx, _EGLSurface **draw, _EGLSurface **read)
395 {
396 _EGLThreadInfo *t = _eglGetCurrentThread();
397 _EGLContext *newCtx = *ctx, *oldCtx;
398
399 if (!_eglCheckMakeCurrent(newCtx, *draw, *read))
400 return EGL_FALSE;
401
402 /* bind the new context */
403 oldCtx = _eglBindContextToThread(newCtx, t);
404
405 if (newCtx)
406 _eglBindContextToSurfaces(newCtx, draw, read);
407
408 /* unbind the old context from its binding surfaces */
409 if (oldCtx && oldCtx != newCtx) {
410 assert(!*draw && !*read);
411
412 *draw = oldCtx->DrawSurface;
413 *read = oldCtx->ReadSurface;
414
415 _eglBindContextToSurfaces(NULL, draw, read);
416 }
417
418 *ctx = oldCtx;
419 /* draw and read have been updated in _eglBindContextToSurfaces */
420
421 return EGL_TRUE;
422 }
423
424
425 /**
426 * Just a placeholder/demo function. Drivers should override this.
427 */
428 EGLBoolean
429 _eglMakeCurrent(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *draw,
430 _EGLSurface *read, _EGLContext *ctx)
431 {
432 return EGL_FALSE;
433 }
434
435
436 /**
437 * This is defined by the EGL_MESA_copy_context extension.
438 */
439 EGLBoolean
440 _eglCopyContextMESA(_EGLDriver *drv, EGLDisplay dpy, EGLContext source,
441 EGLContext dest, EGLint mask)
442 {
443 /* This function will always have to be overridden/implemented in the
444 * device driver. If the driver is based on Mesa, use _mesa_copy_context().
445 */
446 return EGL_FALSE;
447 }