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 */
45static 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      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 */
81static EGLint
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 */
355EGLBoolean
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
397static EGLint
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
413EGLBoolean
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 */
451static _EGLContext *
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 */
477static EGLBoolean
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 */
558EGLBoolean
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