egl: Implement front-end support for EGL_EXT_create_context_robustness
[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 case EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT:
173 /* The EGL_EXT_create_context_robustness spec says:
174 *
175 * "[EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT] is only
176 * meaningful for OpenGL ES contexts, and specifying it for other
177 * types of contexts will generate an EGL_BAD_ATTRIBUTE error."
178 */
179 if (!dpy->Extensions.EXT_create_context_robustness
180 || api != EGL_OPENGL_ES_API) {
181 err = EGL_BAD_ATTRIBUTE;
182 break;
183 }
184
185 ctx->ResetNotificationStrategy = val;
186 break;
187
188 case EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT:
189 if (!dpy->Extensions.EXT_create_context_robustness) {
190 err = EGL_BAD_ATTRIBUTE;
191 break;
192 }
193
194 ctx->Flags = EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR;
195 break;
196
197 default:
198 err = EGL_BAD_ATTRIBUTE;
199 break;
200 }
201
202 if (err != EGL_SUCCESS) {
203 _eglLog(_EGL_DEBUG, "bad context attribute 0x%04x", attr);
204 break;
205 }
206 }
207
208 if (api == EGL_OPENGL_API) {
209 /* The EGL_KHR_create_context spec says:
210 *
211 * "If the requested OpenGL version is less than 3.2,
212 * EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR is ignored and the
213 * functionality of the context is determined solely by the
214 * requested version."
215 *
216 * Since the value is ignored, only validate the setting if the version
217 * is >= 3.2.
218 */
219 if (ctx->ClientMajorVersion >= 4
220 || (ctx->ClientMajorVersion == 3 && ctx->ClientMinorVersion >= 2)) {
221 switch (ctx->Profile) {
222 case EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR:
223 case EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR:
224 break;
225
226 default:
227 /* The EGL_KHR_create_context spec says:
228 *
229 * "* If an OpenGL context is requested, the requested version
230 * is greater than 3.2, and the value for attribute
231 * EGL_CONTEXT_PROFILE_MASK_KHR has no bits set; has any
232 * bits set other than EGL_CONTEXT_CORE_PROFILE_BIT_KHR and
233 * EGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_KHR; has more than
234 * one of these bits set; or if the implementation does not
235 * support the requested profile, then an
236 * EGL_BAD_PROFILE_KHR error is generated."
237 *
238 * However, it does not define EGL_BAD_PROFILE_KHR. For now use
239 * EGL_BAD_ATTRIBUTE.
240 */
241 err = EGL_BAD_ATTRIBUTE;
242 break;
243 }
244 }
245
246 /* The EGL_KHR_create_context spec says:
247 *
248 * "* If an OpenGL context is requested and the values for
249 * attributes EGL_CONTEXT_MAJOR_VERSION_KHR and
250 * EGL_CONTEXT_MINOR_VERSION_KHR, when considered together with
251 * the value for attribute
252 * EGL_CONTEXT_FORWARD_COMPATIBLE_BIT_KHR, specify an OpenGL
253 * version and feature set that are not defined, than an
254 * EGL_BAD_MATCH error is generated.
255 *
256 * ... Thus, examples of invalid combinations of attributes
257 * include:
258 *
259 * - Major version < 1 or > 4
260 * - Major version == 1 and minor version < 0 or > 5
261 * - Major version == 2 and minor version < 0 or > 1
262 * - Major version == 3 and minor version < 0 or > 2
263 * - Major version == 4 and minor version < 0 or > 2
264 * - Forward-compatible flag set and major version < 3"
265 */
266 if (ctx->ClientMajorVersion < 1 || ctx->ClientMinorVersion < 0)
267 err = EGL_BAD_MATCH;
268
269 switch (ctx->ClientMajorVersion) {
270 case 1:
271 if (ctx->ClientMinorVersion > 5
272 || (ctx->Flags & EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR) != 0)
273 err = EGL_BAD_MATCH;
274 break;
275
276 case 2:
277 if (ctx->ClientMinorVersion > 1
278 || (ctx->Flags & EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR) != 0)
279 err = EGL_BAD_MATCH;
280 break;
281
282 case 3:
283 /* Note: The text above is incorrect. There *is* an OpenGL 3.3!
284 */
285 if (ctx->ClientMinorVersion > 3)
286 err = EGL_BAD_MATCH;
287 break;
288
289 case 4:
290 default:
291 /* Don't put additional version checks here. We don't know that
292 * there won't be versions > 4.2.
293 */
294 break;
295 }
296 } else if (api == EGL_OPENGL_ES_API) {
297 /* The EGL_KHR_create_context spec says:
298 *
299 * "* If an OpenGL ES context is requested and the values for
300 * attributes EGL_CONTEXT_MAJOR_VERSION_KHR and
301 * EGL_CONTEXT_MINOR_VERSION_KHR specify an OpenGL ES version that
302 * is not defined, than an EGL_BAD_MATCH error is generated.
303 *
304 * ... Examples of invalid combinations of attributes include:
305 *
306 * - Major version < 1 or > 2
307 * - Major version == 1 and minor version < 0 or > 1
308 * - Major version == 2 and minor version != 0
309 */
310 if (ctx->ClientMajorVersion < 1 || ctx->ClientMinorVersion < 0)
311 err = EGL_BAD_MATCH;
312
313 switch (ctx->ClientMajorVersion) {
314 case 1:
315 if (ctx->ClientMinorVersion > 1)
316 err = EGL_BAD_MATCH;
317 break;
318
319 case 2:
320 default:
321 /* Don't put additional version checks here. We don't know that
322 * there won't be versions > 2.0.
323 */
324 break;
325 }
326 }
327
328 switch (ctx->ResetNotificationStrategy) {
329 case EGL_NO_RESET_NOTIFICATION_KHR:
330 case EGL_LOSE_CONTEXT_ON_RESET_KHR:
331 break;
332
333 default:
334 err = EGL_BAD_ATTRIBUTE;
335 break;
336 }
337
338 if ((ctx->Flags & (EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR
339 | EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR
340 | EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR)) != 0) {
341 err = EGL_BAD_ATTRIBUTE;
342 }
343
344 return err;
345 }
346
347
348 /**
349 * Initialize the given _EGLContext object to defaults and/or the values
350 * in the attrib_list.
351 */
352 EGLBoolean
353 _eglInitContext(_EGLContext *ctx, _EGLDisplay *dpy, _EGLConfig *conf,
354 const EGLint *attrib_list)
355 {
356 const EGLenum api = eglQueryAPI();
357 EGLint err;
358
359 if (api == EGL_NONE) {
360 _eglError(EGL_BAD_MATCH, "eglCreateContext(no client API)");
361 return EGL_FALSE;
362 }
363
364 _eglInitResource(&ctx->Resource, sizeof(*ctx), dpy);
365 ctx->ClientAPI = api;
366 ctx->Config = conf;
367 ctx->WindowRenderBuffer = EGL_NONE;
368 ctx->Profile = EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR;
369
370 ctx->ClientMajorVersion = 1; /* the default, per EGL spec */
371 ctx->ClientMinorVersion = 0;
372 ctx->Flags = 0;
373 ctx->Profile = EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR;
374 ctx->ResetNotificationStrategy = EGL_NO_RESET_NOTIFICATION_KHR;
375
376 err = _eglParseContextAttribList(ctx, dpy, attrib_list);
377 if (err == EGL_SUCCESS && ctx->Config) {
378 EGLint api_bit;
379
380 api_bit = _eglGetContextAPIBit(ctx);
381 if (!(ctx->Config->RenderableType & api_bit)) {
382 _eglLog(_EGL_DEBUG, "context api is 0x%x while config supports 0x%x",
383 api_bit, ctx->Config->RenderableType);
384 err = EGL_BAD_CONFIG;
385 }
386 }
387 if (err != EGL_SUCCESS)
388 return _eglError(err, "eglCreateContext");
389
390 return EGL_TRUE;
391 }
392
393
394 static EGLint
395 _eglQueryContextRenderBuffer(_EGLContext *ctx)
396 {
397 _EGLSurface *surf = ctx->DrawSurface;
398 EGLint rb;
399
400 if (!surf)
401 return EGL_NONE;
402 if (surf->Type == EGL_WINDOW_BIT && ctx->WindowRenderBuffer != EGL_NONE)
403 rb = ctx->WindowRenderBuffer;
404 else
405 rb = surf->RenderBuffer;
406 return rb;
407 }
408
409
410 EGLBoolean
411 _eglQueryContext(_EGLDriver *drv, _EGLDisplay *dpy, _EGLContext *c,
412 EGLint attribute, EGLint *value)
413 {
414 (void) drv;
415 (void) dpy;
416
417 if (!value)
418 return _eglError(EGL_BAD_PARAMETER, "eglQueryContext");
419
420 switch (attribute) {
421 case EGL_CONFIG_ID:
422 if (!c->Config)
423 return _eglError(EGL_BAD_ATTRIBUTE, "eglQueryContext");
424 *value = c->Config->ConfigID;
425 break;
426 case EGL_CONTEXT_CLIENT_VERSION:
427 *value = c->ClientMajorVersion;
428 break;
429 case EGL_CONTEXT_CLIENT_TYPE:
430 *value = c->ClientAPI;
431 break;
432 case EGL_RENDER_BUFFER:
433 *value = _eglQueryContextRenderBuffer(c);
434 break;
435 default:
436 return _eglError(EGL_BAD_ATTRIBUTE, "eglQueryContext");
437 }
438
439 return EGL_TRUE;
440 }
441
442
443 /**
444 * Bind the context to the thread and return the previous context.
445 *
446 * Note that the context may be NULL.
447 */
448 static _EGLContext *
449 _eglBindContextToThread(_EGLContext *ctx, _EGLThreadInfo *t)
450 {
451 EGLint apiIndex;
452 _EGLContext *oldCtx;
453
454 apiIndex = (ctx) ?
455 _eglConvertApiToIndex(ctx->ClientAPI) : t->CurrentAPIIndex;
456
457 oldCtx = t->CurrentContexts[apiIndex];
458 if (ctx != oldCtx) {
459 if (oldCtx)
460 oldCtx->Binding = NULL;
461 if (ctx)
462 ctx->Binding = t;
463
464 t->CurrentContexts[apiIndex] = ctx;
465 }
466
467 return oldCtx;
468 }
469
470
471 /**
472 * Return true if the given context and surfaces can be made current.
473 */
474 static EGLBoolean
475 _eglCheckMakeCurrent(_EGLContext *ctx, _EGLSurface *draw, _EGLSurface *read)
476 {
477 _EGLThreadInfo *t = _eglGetCurrentThread();
478 _EGLDisplay *dpy;
479 EGLint conflict_api;
480
481 if (_eglIsCurrentThreadDummy())
482 return _eglError(EGL_BAD_ALLOC, "eglMakeCurrent");
483
484 /* this is easy */
485 if (!ctx) {
486 if (draw || read)
487 return _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
488 return EGL_TRUE;
489 }
490
491 dpy = ctx->Resource.Display;
492 if (!dpy->Extensions.KHR_surfaceless_context
493 && (draw == NULL || read == NULL))
494 return _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
495
496 /*
497 * The spec says
498 *
499 * "If ctx is current to some other thread, or if either draw or read are
500 * bound to contexts in another thread, an EGL_BAD_ACCESS error is
501 * generated."
502 *
503 * and
504 *
505 * "at most one context may be bound to a particular surface at a given
506 * time"
507 */
508 if (ctx->Binding && ctx->Binding != t)
509 return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
510 if (draw && draw->CurrentContext && draw->CurrentContext != ctx) {
511 if (draw->CurrentContext->Binding != t ||
512 draw->CurrentContext->ClientAPI != ctx->ClientAPI)
513 return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
514 }
515 if (read && read->CurrentContext && read->CurrentContext != ctx) {
516 if (read->CurrentContext->Binding != t ||
517 read->CurrentContext->ClientAPI != ctx->ClientAPI)
518 return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
519 }
520
521 /* simply require the configs to be equal */
522 if ((draw && draw->Config != ctx->Config) ||
523 (read && read->Config != ctx->Config))
524 return _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
525
526 switch (ctx->ClientAPI) {
527 /* OpenGL and OpenGL ES are conflicting */
528 case EGL_OPENGL_ES_API:
529 conflict_api = EGL_OPENGL_API;
530 break;
531 case EGL_OPENGL_API:
532 conflict_api = EGL_OPENGL_ES_API;
533 break;
534 default:
535 conflict_api = -1;
536 break;
537 }
538
539 if (conflict_api >= 0 && _eglGetAPIContext(conflict_api))
540 return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
541
542 return EGL_TRUE;
543 }
544
545
546 /**
547 * Bind the context to the current thread and given surfaces. Return the
548 * previous bound context and surfaces. The caller should unreference the
549 * returned context and surfaces.
550 *
551 * Making a second call with the resources returned by the first call
552 * unsurprisingly undoes the first call, except for the resouce reference
553 * counts.
554 */
555 EGLBoolean
556 _eglBindContext(_EGLContext *ctx, _EGLSurface *draw, _EGLSurface *read,
557 _EGLContext **old_ctx,
558 _EGLSurface **old_draw, _EGLSurface **old_read)
559 {
560 _EGLThreadInfo *t = _eglGetCurrentThread();
561 _EGLContext *prev_ctx;
562 _EGLSurface *prev_draw, *prev_read;
563
564 if (!_eglCheckMakeCurrent(ctx, draw, read))
565 return EGL_FALSE;
566
567 /* increment refcounts before binding */
568 _eglGetContext(ctx);
569 _eglGetSurface(draw);
570 _eglGetSurface(read);
571
572 /* bind the new context */
573 prev_ctx = _eglBindContextToThread(ctx, t);
574
575 /* break previous bindings */
576 if (prev_ctx) {
577 prev_draw = prev_ctx->DrawSurface;
578 prev_read = prev_ctx->ReadSurface;
579
580 if (prev_draw)
581 prev_draw->CurrentContext = NULL;
582 if (prev_read)
583 prev_read->CurrentContext = NULL;
584
585 prev_ctx->DrawSurface = NULL;
586 prev_ctx->ReadSurface = NULL;
587 }
588 else {
589 prev_draw = prev_read = NULL;
590 }
591
592 /* establish new bindings */
593 if (ctx) {
594 if (draw)
595 draw->CurrentContext = ctx;
596 if (read)
597 read->CurrentContext = ctx;
598
599 ctx->DrawSurface = draw;
600 ctx->ReadSurface = read;
601 }
602
603 assert(old_ctx && old_draw && old_read);
604 *old_ctx = prev_ctx;
605 *old_draw = prev_draw;
606 *old_read = prev_read;
607
608 return EGL_TRUE;
609 }