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