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