eglcontext.c revision 63beb3df98147f34fd0965cb0afbb97444206d0c
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         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 */
80static 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 */
327EGLBoolean
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
369static 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
385EGLBoolean
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 */
423static _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 */
449static 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 */
530EGLBoolean
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}
585