egl: Implement front-end support for EGL_KHR_create_context
[mesa.git] / src / egl / main / eglcontext.c
1 /**************************************************************************
2 *
3 * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
4 * Copyright 2009-2010 Chia-I Wu <olvaffe@gmail.com>
5 * Copyright 2010-2011 LunarG, Inc.
6 * All Rights Reserved.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the
10 * "Software"), to deal in the Software without restriction, including
11 * without limitation the rights to use, copy, modify, merge, publish,
12 * distribute, sub license, and/or sell copies of the Software, and to
13 * permit persons to whom the Software is furnished to do so, subject to
14 * the following conditions:
15 *
16 * The above copyright notice and this permission notice (including the
17 * next paragraph) shall be included in all copies or substantial portions
18 * of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 * DEALINGS IN THE SOFTWARE.
27 *
28 **************************************************************************/
29
30
31 #include <assert.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include "eglconfig.h"
35 #include "eglcontext.h"
36 #include "egldisplay.h"
37 #include "eglcurrent.h"
38 #include "eglsurface.h"
39 #include "egllog.h"
40
41
42 /**
43 * Return the API bit (one of EGL_xxx_BIT) of the context.
44 */
45 static EGLint
46 _eglGetContextAPIBit(_EGLContext *ctx)
47 {
48 EGLint bit = 0;
49
50 switch (ctx->ClientAPI) {
51 case EGL_OPENGL_ES_API:
52 switch (ctx->ClientMajorVersion) {
53 case 1:
54 bit = EGL_OPENGL_ES_BIT;
55 break;
56 case 2:
57 bit = EGL_OPENGL_ES2_BIT;
58 break;
59 default:
60 break;
61 }
62 break;
63 case EGL_OPENVG_API:
64 bit = EGL_OPENVG_BIT;
65 break;
66 case EGL_OPENGL_API:
67 bit = EGL_OPENGL_BIT;
68 break;
69 default:
70 break;
71 }
72
73 return bit;
74 }
75
76
77 /**
78 * Parse the list of context attributes and return the proper error code.
79 */
80 static EGLint
81 _eglParseContextAttribList(_EGLContext *ctx, _EGLDisplay *dpy,
82 const EGLint *attrib_list)
83 {
84 EGLenum api = ctx->ClientAPI;
85 EGLint i, err = EGL_SUCCESS;
86
87 if (!attrib_list)
88 return EGL_SUCCESS;
89
90 if (api == EGL_OPENVG_API && attrib_list[0] != EGL_NONE) {
91 _eglLog(_EGL_DEBUG, "bad context attribute 0x%04x", attrib_list[0]);
92 return EGL_BAD_ATTRIBUTE;
93 }
94
95 for (i = 0; attrib_list[i] != EGL_NONE; i++) {
96 EGLint attr = attrib_list[i++];
97 EGLint val = attrib_list[i];
98
99 switch (attr) {
100 case EGL_CONTEXT_CLIENT_VERSION:
101 ctx->ClientMajorVersion = val;
102 break;
103
104 case EGL_CONTEXT_MINOR_VERSION_KHR:
105 if (!dpy->Extensions.KHR_create_context) {
106 err = EGL_BAD_ATTRIBUTE;
107 break;
108 }
109
110 ctx->ClientMinorVersion = val;
111 break;
112
113 case EGL_CONTEXT_FLAGS_KHR:
114 if (!dpy->Extensions.KHR_create_context) {
115 err = EGL_BAD_ATTRIBUTE;
116 break;
117 }
118
119 /* The EGL_KHR_create_context spec says:
120 *
121 * "Flags are only defined for OpenGL context creation, and
122 * specifying a flags value other than zero for other types of
123 * contexts, including OpenGL ES contexts, will generate an
124 * error."
125 */
126 if (api != EGL_OPENGL_API && val != 0) {
127 err = EGL_BAD_ATTRIBUTE;
128 break;
129 }
130
131 ctx->Flags = val;
132 break;
133
134 case EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR:
135 if (!dpy->Extensions.KHR_create_context) {
136 err = EGL_BAD_ATTRIBUTE;
137 break;
138 }
139
140 /* The EGL_KHR_create_context spec says:
141 *
142 * "[EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR] is only meaningful for
143 * OpenGL contexts, and specifying it for other types of
144 * contexts, including OpenGL ES contexts, will generate an
145 * error."
146 */
147 if (api != EGL_OPENGL_API) {
148 err = EGL_BAD_ATTRIBUTE;
149 break;
150 }
151
152 ctx->Profile = val;
153 break;
154
155 case EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR:
156 /* The EGL_KHR_create_context spec says:
157 *
158 * "[EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR] is only
159 * meaningful for OpenGL contexts, and specifying it for other
160 * types of contexts, including OpenGL ES contexts, will generate
161 * an error."
162 */
163 if (!dpy->Extensions.KHR_create_context
164 || api != EGL_OPENGL_API) {
165 err = EGL_BAD_ATTRIBUTE;
166 break;
167 }
168
169 ctx->ResetNotificationStrategy = val;
170 break;
171
172 default:
173 err = EGL_BAD_ATTRIBUTE;
174 break;
175 }
176
177 if (err != EGL_SUCCESS) {
178 _eglLog(_EGL_DEBUG, "bad context attribute 0x%04x", attr);
179 break;
180 }
181 }
182
183 if (api == EGL_OPENGL_API) {
184 /* The EGL_KHR_create_context spec says:
185 *
186 * "If the requested OpenGL version is less than 3.2,
187 * EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR is ignored and the
188 * functionality of the context is determined solely by the
189 * requested version."
190 *
191 * Since the value is ignored, only validate the setting if the version
192 * is >= 3.2.
193 */
194 if (ctx->ClientMajorVersion >= 4
195 || (ctx->ClientMajorVersion == 3 && ctx->ClientMinorVersion >= 2)) {
196 switch (ctx->Profile) {
197 case EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR:
198 case EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR:
199 break;
200
201 default:
202 /* The EGL_KHR_create_context spec says:
203 *
204 * "* If an OpenGL context is requested, the requested version
205 * is greater than 3.2, and the value for attribute
206 * EGL_CONTEXT_PROFILE_MASK_KHR has no bits set; has any
207 * bits set other than EGL_CONTEXT_CORE_PROFILE_BIT_KHR and
208 * EGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_KHR; has more than
209 * one of these bits set; or if the implementation does not
210 * support the requested profile, then an
211 * EGL_BAD_PROFILE_KHR error is generated."
212 *
213 * However, it does not define EGL_BAD_PROFILE_KHR. For now use
214 * EGL_BAD_ATTRIBUTE.
215 */
216 err = EGL_BAD_ATTRIBUTE;
217 break;
218 }
219 }
220
221 /* The EGL_KHR_create_context spec says:
222 *
223 * "* If an OpenGL context is requested and the values for
224 * attributes EGL_CONTEXT_MAJOR_VERSION_KHR and
225 * EGL_CONTEXT_MINOR_VERSION_KHR, when considered together with
226 * the value for attribute
227 * EGL_CONTEXT_FORWARD_COMPATIBLE_BIT_KHR, specify an OpenGL
228 * version and feature set that are not defined, than an
229 * EGL_BAD_MATCH error is generated.
230 *
231 * ... Thus, examples of invalid combinations of attributes
232 * include:
233 *
234 * - Major version < 1 or > 4
235 * - Major version == 1 and minor version < 0 or > 5
236 * - Major version == 2 and minor version < 0 or > 1
237 * - Major version == 3 and minor version < 0 or > 2
238 * - Major version == 4 and minor version < 0 or > 2
239 * - Forward-compatible flag set and major version < 3"
240 */
241 if (ctx->ClientMajorVersion < 1 || ctx->ClientMinorVersion < 0)
242 err = EGL_BAD_MATCH;
243
244 switch (ctx->ClientMajorVersion) {
245 case 1:
246 if (ctx->ClientMinorVersion > 5
247 || (ctx->Flags & EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR) != 0)
248 err = EGL_BAD_MATCH;
249 break;
250
251 case 2:
252 if (ctx->ClientMinorVersion > 1
253 || (ctx->Flags & EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR) != 0)
254 err = EGL_BAD_MATCH;
255 break;
256
257 case 3:
258 /* Note: The text above is incorrect. There *is* an OpenGL 3.3!
259 */
260 if (ctx->ClientMinorVersion > 3)
261 err = EGL_BAD_MATCH;
262 break;
263
264 case 4:
265 default:
266 /* Don't put additional version checks here. We don't know that
267 * there won't be versions > 4.2.
268 */
269 break;
270 }
271 } else if (api == EGL_OPENGL_ES_API) {
272 /* The EGL_KHR_create_context spec says:
273 *
274 * "* If an OpenGL ES context is requested and the values for
275 * attributes EGL_CONTEXT_MAJOR_VERSION_KHR and
276 * EGL_CONTEXT_MINOR_VERSION_KHR specify an OpenGL ES version that
277 * is not defined, than an EGL_BAD_MATCH error is generated.
278 *
279 * ... Examples of invalid combinations of attributes include:
280 *
281 * - Major version < 1 or > 2
282 * - Major version == 1 and minor version < 0 or > 1
283 * - Major version == 2 and minor version != 0
284 */
285 if (ctx->ClientMajorVersion < 1 || ctx->ClientMinorVersion < 0)
286 err = EGL_BAD_MATCH;
287
288 switch (ctx->ClientMajorVersion) {
289 case 1:
290 if (ctx->ClientMinorVersion > 1)
291 err = EGL_BAD_MATCH;
292 break;
293
294 case 2:
295 default:
296 /* Don't put additional version checks here. We don't know that
297 * there won't be versions > 2.0.
298 */
299 break;
300 }
301 }
302
303 switch (ctx->ResetNotificationStrategy) {
304 case EGL_NO_RESET_NOTIFICATION_KHR:
305 case EGL_LOSE_CONTEXT_ON_RESET_KHR:
306 break;
307
308 default:
309 err = EGL_BAD_ATTRIBUTE;
310 break;
311 }
312
313 if ((ctx->Flags & (EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR
314 | EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR
315 | EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR)) != 0) {
316 err = EGL_BAD_ATTRIBUTE;
317 }
318
319 return err;
320 }
321
322
323 /**
324 * Initialize the given _EGLContext object to defaults and/or the values
325 * in the attrib_list.
326 */
327 EGLBoolean
328 _eglInitContext(_EGLContext *ctx, _EGLDisplay *dpy, _EGLConfig *conf,
329 const EGLint *attrib_list)
330 {
331 const EGLenum api = eglQueryAPI();
332 EGLint err;
333
334 if (api == EGL_NONE) {
335 _eglError(EGL_BAD_MATCH, "eglCreateContext(no client API)");
336 return EGL_FALSE;
337 }
338
339 _eglInitResource(&ctx->Resource, sizeof(*ctx), dpy);
340 ctx->ClientAPI = api;
341 ctx->Config = conf;
342 ctx->WindowRenderBuffer = EGL_NONE;
343 ctx->Profile = EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR;
344
345 ctx->ClientMajorVersion = 1; /* the default, per EGL spec */
346 ctx->ClientMinorVersion = 0;
347 ctx->Flags = 0;
348 ctx->Profile = EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR;
349 ctx->ResetNotificationStrategy = EGL_NO_RESET_NOTIFICATION_KHR;
350
351 err = _eglParseContextAttribList(ctx, dpy, attrib_list);
352 if (err == EGL_SUCCESS && ctx->Config) {
353 EGLint api_bit;
354
355 api_bit = _eglGetContextAPIBit(ctx);
356 if (!(ctx->Config->RenderableType & api_bit)) {
357 _eglLog(_EGL_DEBUG, "context api is 0x%x while config supports 0x%x",
358 api_bit, ctx->Config->RenderableType);
359 err = EGL_BAD_CONFIG;
360 }
361 }
362 if (err != EGL_SUCCESS)
363 return _eglError(err, "eglCreateContext");
364
365 return EGL_TRUE;
366 }
367
368
369 static EGLint
370 _eglQueryContextRenderBuffer(_EGLContext *ctx)
371 {
372 _EGLSurface *surf = ctx->DrawSurface;
373 EGLint rb;
374
375 if (!surf)
376 return EGL_NONE;
377 if (surf->Type == EGL_WINDOW_BIT && ctx->WindowRenderBuffer != EGL_NONE)
378 rb = ctx->WindowRenderBuffer;
379 else
380 rb = surf->RenderBuffer;
381 return rb;
382 }
383
384
385 EGLBoolean
386 _eglQueryContext(_EGLDriver *drv, _EGLDisplay *dpy, _EGLContext *c,
387 EGLint attribute, EGLint *value)
388 {
389 (void) drv;
390 (void) dpy;
391
392 if (!value)
393 return _eglError(EGL_BAD_PARAMETER, "eglQueryContext");
394
395 switch (attribute) {
396 case EGL_CONFIG_ID:
397 if (!c->Config)
398 return _eglError(EGL_BAD_ATTRIBUTE, "eglQueryContext");
399 *value = c->Config->ConfigID;
400 break;
401 case EGL_CONTEXT_CLIENT_VERSION:
402 *value = c->ClientMajorVersion;
403 break;
404 case EGL_CONTEXT_CLIENT_TYPE:
405 *value = c->ClientAPI;
406 break;
407 case EGL_RENDER_BUFFER:
408 *value = _eglQueryContextRenderBuffer(c);
409 break;
410 default:
411 return _eglError(EGL_BAD_ATTRIBUTE, "eglQueryContext");
412 }
413
414 return EGL_TRUE;
415 }
416
417
418 /**
419 * Bind the context to the thread and return the previous context.
420 *
421 * Note that the context may be NULL.
422 */
423 static _EGLContext *
424 _eglBindContextToThread(_EGLContext *ctx, _EGLThreadInfo *t)
425 {
426 EGLint apiIndex;
427 _EGLContext *oldCtx;
428
429 apiIndex = (ctx) ?
430 _eglConvertApiToIndex(ctx->ClientAPI) : t->CurrentAPIIndex;
431
432 oldCtx = t->CurrentContexts[apiIndex];
433 if (ctx != oldCtx) {
434 if (oldCtx)
435 oldCtx->Binding = NULL;
436 if (ctx)
437 ctx->Binding = t;
438
439 t->CurrentContexts[apiIndex] = ctx;
440 }
441
442 return oldCtx;
443 }
444
445
446 /**
447 * Return true if the given context and surfaces can be made current.
448 */
449 static EGLBoolean
450 _eglCheckMakeCurrent(_EGLContext *ctx, _EGLSurface *draw, _EGLSurface *read)
451 {
452 _EGLThreadInfo *t = _eglGetCurrentThread();
453 _EGLDisplay *dpy;
454 EGLint conflict_api;
455
456 if (_eglIsCurrentThreadDummy())
457 return _eglError(EGL_BAD_ALLOC, "eglMakeCurrent");
458
459 /* this is easy */
460 if (!ctx) {
461 if (draw || read)
462 return _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
463 return EGL_TRUE;
464 }
465
466 dpy = ctx->Resource.Display;
467 if (!dpy->Extensions.KHR_surfaceless_context
468 && (draw == NULL || read == NULL))
469 return _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
470
471 /*
472 * The spec says
473 *
474 * "If ctx is current to some other thread, or if either draw or read are
475 * bound to contexts in another thread, an EGL_BAD_ACCESS error is
476 * generated."
477 *
478 * and
479 *
480 * "at most one context may be bound to a particular surface at a given
481 * time"
482 */
483 if (ctx->Binding && ctx->Binding != t)
484 return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
485 if (draw && draw->CurrentContext && draw->CurrentContext != ctx) {
486 if (draw->CurrentContext->Binding != t ||
487 draw->CurrentContext->ClientAPI != ctx->ClientAPI)
488 return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
489 }
490 if (read && read->CurrentContext && read->CurrentContext != ctx) {
491 if (read->CurrentContext->Binding != t ||
492 read->CurrentContext->ClientAPI != ctx->ClientAPI)
493 return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
494 }
495
496 /* simply require the configs to be equal */
497 if ((draw && draw->Config != ctx->Config) ||
498 (read && read->Config != ctx->Config))
499 return _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
500
501 switch (ctx->ClientAPI) {
502 /* OpenGL and OpenGL ES are conflicting */
503 case EGL_OPENGL_ES_API:
504 conflict_api = EGL_OPENGL_API;
505 break;
506 case EGL_OPENGL_API:
507 conflict_api = EGL_OPENGL_ES_API;
508 break;
509 default:
510 conflict_api = -1;
511 break;
512 }
513
514 if (conflict_api >= 0 && _eglGetAPIContext(conflict_api))
515 return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
516
517 return EGL_TRUE;
518 }
519
520
521 /**
522 * Bind the context to the current thread and given surfaces. Return the
523 * previous bound context and surfaces. The caller should unreference the
524 * returned context and surfaces.
525 *
526 * Making a second call with the resources returned by the first call
527 * unsurprisingly undoes the first call, except for the resouce reference
528 * counts.
529 */
530 EGLBoolean
531 _eglBindContext(_EGLContext *ctx, _EGLSurface *draw, _EGLSurface *read,
532 _EGLContext **old_ctx,
533 _EGLSurface **old_draw, _EGLSurface **old_read)
534 {
535 _EGLThreadInfo *t = _eglGetCurrentThread();
536 _EGLContext *prev_ctx;
537 _EGLSurface *prev_draw, *prev_read;
538
539 if (!_eglCheckMakeCurrent(ctx, draw, read))
540 return EGL_FALSE;
541
542 /* increment refcounts before binding */
543 _eglGetContext(ctx);
544 _eglGetSurface(draw);
545 _eglGetSurface(read);
546
547 /* bind the new context */
548 prev_ctx = _eglBindContextToThread(ctx, t);
549
550 /* break previous bindings */
551 if (prev_ctx) {
552 prev_draw = prev_ctx->DrawSurface;
553 prev_read = prev_ctx->ReadSurface;
554
555 if (prev_draw)
556 prev_draw->CurrentContext = NULL;
557 if (prev_read)
558 prev_read->CurrentContext = NULL;
559
560 prev_ctx->DrawSurface = NULL;
561 prev_ctx->ReadSurface = NULL;
562 }
563 else {
564 prev_draw = prev_read = NULL;
565 }
566
567 /* establish new bindings */
568 if (ctx) {
569 if (draw)
570 draw->CurrentContext = ctx;
571 if (read)
572 read->CurrentContext = ctx;
573
574 ctx->DrawSurface = draw;
575 ctx->ReadSurface = read;
576 }
577
578 assert(old_ctx && old_draw && old_read);
579 *old_ctx = prev_ctx;
580 *old_draw = prev_draw;
581 *old_read = prev_read;
582
583 return EGL_TRUE;
584 }