eglcontext.c revision 3464ebd5
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->ClientVersion) {
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, const EGLint *attrib_list)
82{
83   EGLenum api = ctx->ClientAPI;
84   EGLint i, err = EGL_SUCCESS;
85
86   if (!attrib_list)
87      return EGL_SUCCESS;
88
89   for (i = 0; attrib_list[i] != EGL_NONE; i++) {
90      EGLint attr = attrib_list[i++];
91      EGLint val = attrib_list[i];
92
93      switch (attr) {
94      case EGL_CONTEXT_CLIENT_VERSION:
95         if (api != EGL_OPENGL_ES_API) {
96            err = EGL_BAD_ATTRIBUTE;
97            break;
98         }
99         if (val != 1 && val != 2) {
100            err = EGL_BAD_ATTRIBUTE;
101            break;
102         }
103         ctx->ClientVersion = val;
104         break;
105      default:
106         err = EGL_BAD_ATTRIBUTE;
107         break;
108      }
109
110      if (err != EGL_SUCCESS) {
111         _eglLog(_EGL_DEBUG, "bad context attribute 0x%04x", attr);
112         break;
113      }
114   }
115
116   return err;
117}
118
119
120/**
121 * Initialize the given _EGLContext object to defaults and/or the values
122 * in the attrib_list.
123 */
124EGLBoolean
125_eglInitContext(_EGLContext *ctx, _EGLDisplay *dpy, _EGLConfig *conf,
126                const EGLint *attrib_list)
127{
128   const EGLenum api = eglQueryAPI();
129   EGLint err;
130
131   if (api == EGL_NONE) {
132      _eglError(EGL_BAD_MATCH, "eglCreateContext(no client API)");
133      return EGL_FALSE;
134   }
135
136   _eglInitResource(&ctx->Resource, sizeof(*ctx), dpy);
137   ctx->ClientAPI = api;
138   ctx->Config = conf;
139   ctx->WindowRenderBuffer = EGL_NONE;
140
141   ctx->ClientVersion = 1; /* the default, per EGL spec */
142
143   err = _eglParseContextAttribList(ctx, attrib_list);
144   if (err == EGL_SUCCESS && ctx->Config) {
145      EGLint api_bit;
146
147      api_bit = _eglGetContextAPIBit(ctx);
148      if (!(ctx->Config->RenderableType & api_bit)) {
149         _eglLog(_EGL_DEBUG, "context api is 0x%x while config supports 0x%x",
150               api_bit, ctx->Config->RenderableType);
151         err = EGL_BAD_CONFIG;
152      }
153   }
154   if (err != EGL_SUCCESS)
155      return _eglError(err, "eglCreateContext");
156
157   return EGL_TRUE;
158}
159
160
161#ifdef EGL_VERSION_1_2
162static EGLint
163_eglQueryContextRenderBuffer(_EGLContext *ctx)
164{
165   _EGLSurface *surf = ctx->DrawSurface;
166   EGLint rb;
167
168   if (!surf)
169      return EGL_NONE;
170   if (surf->Type == EGL_WINDOW_BIT && ctx->WindowRenderBuffer != EGL_NONE)
171      rb = ctx->WindowRenderBuffer;
172   else
173      rb = surf->RenderBuffer;
174   return rb;
175}
176#endif /* EGL_VERSION_1_2 */
177
178
179EGLBoolean
180_eglQueryContext(_EGLDriver *drv, _EGLDisplay *dpy, _EGLContext *c,
181                 EGLint attribute, EGLint *value)
182{
183   (void) drv;
184   (void) dpy;
185
186   if (!value)
187      return _eglError(EGL_BAD_PARAMETER, "eglQueryContext");
188
189   switch (attribute) {
190   case EGL_CONFIG_ID:
191      if (!c->Config)
192         return _eglError(EGL_BAD_ATTRIBUTE, "eglQueryContext");
193      *value = c->Config->ConfigID;
194      break;
195   case EGL_CONTEXT_CLIENT_VERSION:
196      *value = c->ClientVersion;
197      break;
198#ifdef EGL_VERSION_1_2
199   case EGL_CONTEXT_CLIENT_TYPE:
200      *value = c->ClientAPI;
201      break;
202   case EGL_RENDER_BUFFER:
203      *value = _eglQueryContextRenderBuffer(c);
204      break;
205#endif /* EGL_VERSION_1_2 */
206   default:
207      return _eglError(EGL_BAD_ATTRIBUTE, "eglQueryContext");
208   }
209
210   return EGL_TRUE;
211}
212
213
214/**
215 * Bind the context to the thread and return the previous context.
216 *
217 * Note that the context may be NULL.
218 */
219static _EGLContext *
220_eglBindContextToThread(_EGLContext *ctx, _EGLThreadInfo *t)
221{
222   EGLint apiIndex;
223   _EGLContext *oldCtx;
224
225   apiIndex = (ctx) ?
226      _eglConvertApiToIndex(ctx->ClientAPI) : t->CurrentAPIIndex;
227
228   oldCtx = t->CurrentContexts[apiIndex];
229   if (ctx != oldCtx) {
230      if (oldCtx)
231         oldCtx->Binding = NULL;
232      if (ctx)
233         ctx->Binding = t;
234
235      t->CurrentContexts[apiIndex] = ctx;
236   }
237
238   return oldCtx;
239}
240
241
242/**
243 * Return true if the given context and surfaces can be made current.
244 */
245static EGLBoolean
246_eglCheckMakeCurrent(_EGLContext *ctx, _EGLSurface *draw, _EGLSurface *read)
247{
248   _EGLThreadInfo *t = _eglGetCurrentThread();
249   _EGLDisplay *dpy;
250   EGLint conflict_api;
251   EGLBoolean surfaceless;
252
253   if (_eglIsCurrentThreadDummy())
254      return _eglError(EGL_BAD_ALLOC, "eglMakeCurrent");
255
256   /* this is easy */
257   if (!ctx) {
258      if (draw || read)
259         return _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
260      return EGL_TRUE;
261   }
262
263   dpy = ctx->Resource.Display;
264   switch (_eglGetContextAPIBit(ctx)) {
265   case EGL_OPENGL_ES_BIT:
266      surfaceless = dpy->Extensions.KHR_surfaceless_gles1;
267      break;
268   case EGL_OPENGL_ES2_BIT:
269      surfaceless = dpy->Extensions.KHR_surfaceless_gles2;
270      break;
271   case EGL_OPENGL_BIT:
272      surfaceless = dpy->Extensions.KHR_surfaceless_opengl;
273      break;
274   default:
275      surfaceless = EGL_FALSE;
276      break;
277   }
278
279   if (!surfaceless && (draw == NULL || read == NULL))
280      return _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
281
282   /*
283    * The spec says
284    *
285    * "If ctx is current to some other thread, or if either draw or read are
286    * bound to contexts in another thread, an EGL_BAD_ACCESS error is
287    * generated."
288    *
289    * and
290    *
291    * "at most one context may be bound to a particular surface at a given
292    * time"
293    */
294   if (ctx->Binding && ctx->Binding != t)
295      return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
296   if (draw && draw->CurrentContext && draw->CurrentContext != ctx) {
297      if (draw->CurrentContext->Binding != t ||
298          draw->CurrentContext->ClientAPI != ctx->ClientAPI)
299         return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
300   }
301   if (read && read->CurrentContext && read->CurrentContext != ctx) {
302      if (read->CurrentContext->Binding != t ||
303          read->CurrentContext->ClientAPI != ctx->ClientAPI)
304         return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
305   }
306
307   /* simply require the configs to be equal */
308   if ((draw && draw->Config != ctx->Config) ||
309       (read && read->Config != ctx->Config))
310      return _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
311
312   switch (ctx->ClientAPI) {
313#ifdef EGL_VERSION_1_4
314   /* OpenGL and OpenGL ES are conflicting */
315   case EGL_OPENGL_ES_API:
316      conflict_api = EGL_OPENGL_API;
317      break;
318   case EGL_OPENGL_API:
319      conflict_api = EGL_OPENGL_ES_API;
320      break;
321#endif
322   default:
323      conflict_api = -1;
324      break;
325   }
326
327   if (conflict_api >= 0 && _eglGetAPIContext(conflict_api))
328      return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
329
330   return EGL_TRUE;
331}
332
333
334/**
335 * Bind the context to the current thread and given surfaces.  Return the
336 * previous bound context and surfaces.  The caller should unreference the
337 * returned context and surfaces.
338 *
339 * Making a second call with the resources returned by the first call
340 * unsurprisingly undoes the first call, except for the resouce reference
341 * counts.
342 */
343EGLBoolean
344_eglBindContext(_EGLContext *ctx, _EGLSurface *draw, _EGLSurface *read,
345                _EGLContext **old_ctx,
346                _EGLSurface **old_draw, _EGLSurface **old_read)
347{
348   _EGLThreadInfo *t = _eglGetCurrentThread();
349   _EGLContext *prev_ctx;
350   _EGLSurface *prev_draw, *prev_read;
351
352   if (!_eglCheckMakeCurrent(ctx, draw, read))
353      return EGL_FALSE;
354
355   /* increment refcounts before binding */
356   _eglGetContext(ctx);
357   _eglGetSurface(draw);
358   _eglGetSurface(read);
359
360   /* bind the new context */
361   prev_ctx = _eglBindContextToThread(ctx, t);
362
363   /* break previous bindings */
364   if (prev_ctx) {
365      prev_draw = prev_ctx->DrawSurface;
366      prev_read = prev_ctx->ReadSurface;
367
368      if (prev_draw)
369         prev_draw->CurrentContext = NULL;
370      if (prev_read)
371         prev_read->CurrentContext = NULL;
372
373      prev_ctx->DrawSurface = NULL;
374      prev_ctx->ReadSurface = NULL;
375   }
376   else {
377      prev_draw = prev_read = NULL;
378   }
379
380   /* establish new bindings */
381   if (ctx) {
382      if (draw)
383         draw->CurrentContext = ctx;
384      if (read)
385         read->CurrentContext = ctx;
386
387      ctx->DrawSurface = draw;
388      ctx->ReadSurface = read;
389   }
390
391   assert(old_ctx && old_draw && old_read);
392   *old_ctx = prev_ctx;
393   *old_draw = prev_draw;
394   *old_read = prev_read;
395
396   return EGL_TRUE;
397}
398