eglcontext.c revision 3fd79dd9886f05f45f3dc94c028a38dd75b8ee1b
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, 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->ClientMajorVersion = 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->ClientMajorVersion = 1; /* the default, per EGL spec */
142   ctx->ClientMinorVersion = 0;
143
144   err = _eglParseContextAttribList(ctx, attrib_list);
145   if (err == EGL_SUCCESS && ctx->Config) {
146      EGLint api_bit;
147
148      api_bit = _eglGetContextAPIBit(ctx);
149      if (!(ctx->Config->RenderableType & api_bit)) {
150         _eglLog(_EGL_DEBUG, "context api is 0x%x while config supports 0x%x",
151               api_bit, ctx->Config->RenderableType);
152         err = EGL_BAD_CONFIG;
153      }
154   }
155   if (err != EGL_SUCCESS)
156      return _eglError(err, "eglCreateContext");
157
158   return EGL_TRUE;
159}
160
161
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
177
178EGLBoolean
179_eglQueryContext(_EGLDriver *drv, _EGLDisplay *dpy, _EGLContext *c,
180                 EGLint attribute, EGLint *value)
181{
182   (void) drv;
183   (void) dpy;
184
185   if (!value)
186      return _eglError(EGL_BAD_PARAMETER, "eglQueryContext");
187
188   switch (attribute) {
189   case EGL_CONFIG_ID:
190      if (!c->Config)
191         return _eglError(EGL_BAD_ATTRIBUTE, "eglQueryContext");
192      *value = c->Config->ConfigID;
193      break;
194   case EGL_CONTEXT_CLIENT_VERSION:
195      *value = c->ClientMajorVersion;
196      break;
197   case EGL_CONTEXT_CLIENT_TYPE:
198      *value = c->ClientAPI;
199      break;
200   case EGL_RENDER_BUFFER:
201      *value = _eglQueryContextRenderBuffer(c);
202      break;
203   default:
204      return _eglError(EGL_BAD_ATTRIBUTE, "eglQueryContext");
205   }
206
207   return EGL_TRUE;
208}
209
210
211/**
212 * Bind the context to the thread and return the previous context.
213 *
214 * Note that the context may be NULL.
215 */
216static _EGLContext *
217_eglBindContextToThread(_EGLContext *ctx, _EGLThreadInfo *t)
218{
219   EGLint apiIndex;
220   _EGLContext *oldCtx;
221
222   apiIndex = (ctx) ?
223      _eglConvertApiToIndex(ctx->ClientAPI) : t->CurrentAPIIndex;
224
225   oldCtx = t->CurrentContexts[apiIndex];
226   if (ctx != oldCtx) {
227      if (oldCtx)
228         oldCtx->Binding = NULL;
229      if (ctx)
230         ctx->Binding = t;
231
232      t->CurrentContexts[apiIndex] = ctx;
233   }
234
235   return oldCtx;
236}
237
238
239/**
240 * Return true if the given context and surfaces can be made current.
241 */
242static EGLBoolean
243_eglCheckMakeCurrent(_EGLContext *ctx, _EGLSurface *draw, _EGLSurface *read)
244{
245   _EGLThreadInfo *t = _eglGetCurrentThread();
246   _EGLDisplay *dpy;
247   EGLint conflict_api;
248
249   if (_eglIsCurrentThreadDummy())
250      return _eglError(EGL_BAD_ALLOC, "eglMakeCurrent");
251
252   /* this is easy */
253   if (!ctx) {
254      if (draw || read)
255         return _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
256      return EGL_TRUE;
257   }
258
259   dpy = ctx->Resource.Display;
260   if (!dpy->Extensions.KHR_surfaceless_context
261       && (draw == NULL || read == NULL))
262      return _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
263
264   /*
265    * The spec says
266    *
267    * "If ctx is current to some other thread, or if either draw or read are
268    * bound to contexts in another thread, an EGL_BAD_ACCESS error is
269    * generated."
270    *
271    * and
272    *
273    * "at most one context may be bound to a particular surface at a given
274    * time"
275    */
276   if (ctx->Binding && ctx->Binding != t)
277      return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
278   if (draw && draw->CurrentContext && draw->CurrentContext != ctx) {
279      if (draw->CurrentContext->Binding != t ||
280          draw->CurrentContext->ClientAPI != ctx->ClientAPI)
281         return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
282   }
283   if (read && read->CurrentContext && read->CurrentContext != ctx) {
284      if (read->CurrentContext->Binding != t ||
285          read->CurrentContext->ClientAPI != ctx->ClientAPI)
286         return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
287   }
288
289   /* simply require the configs to be equal */
290   if ((draw && draw->Config != ctx->Config) ||
291       (read && read->Config != ctx->Config))
292      return _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
293
294   switch (ctx->ClientAPI) {
295   /* OpenGL and OpenGL ES are conflicting */
296   case EGL_OPENGL_ES_API:
297      conflict_api = EGL_OPENGL_API;
298      break;
299   case EGL_OPENGL_API:
300      conflict_api = EGL_OPENGL_ES_API;
301      break;
302   default:
303      conflict_api = -1;
304      break;
305   }
306
307   if (conflict_api >= 0 && _eglGetAPIContext(conflict_api))
308      return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
309
310   return EGL_TRUE;
311}
312
313
314/**
315 * Bind the context to the current thread and given surfaces.  Return the
316 * previous bound context and surfaces.  The caller should unreference the
317 * returned context and surfaces.
318 *
319 * Making a second call with the resources returned by the first call
320 * unsurprisingly undoes the first call, except for the resouce reference
321 * counts.
322 */
323EGLBoolean
324_eglBindContext(_EGLContext *ctx, _EGLSurface *draw, _EGLSurface *read,
325                _EGLContext **old_ctx,
326                _EGLSurface **old_draw, _EGLSurface **old_read)
327{
328   _EGLThreadInfo *t = _eglGetCurrentThread();
329   _EGLContext *prev_ctx;
330   _EGLSurface *prev_draw, *prev_read;
331
332   if (!_eglCheckMakeCurrent(ctx, draw, read))
333      return EGL_FALSE;
334
335   /* increment refcounts before binding */
336   _eglGetContext(ctx);
337   _eglGetSurface(draw);
338   _eglGetSurface(read);
339
340   /* bind the new context */
341   prev_ctx = _eglBindContextToThread(ctx, t);
342
343   /* break previous bindings */
344   if (prev_ctx) {
345      prev_draw = prev_ctx->DrawSurface;
346      prev_read = prev_ctx->ReadSurface;
347
348      if (prev_draw)
349         prev_draw->CurrentContext = NULL;
350      if (prev_read)
351         prev_read->CurrentContext = NULL;
352
353      prev_ctx->DrawSurface = NULL;
354      prev_ctx->ReadSurface = NULL;
355   }
356   else {
357      prev_draw = prev_read = NULL;
358   }
359
360   /* establish new bindings */
361   if (ctx) {
362      if (draw)
363         draw->CurrentContext = ctx;
364      if (read)
365         read->CurrentContext = ctx;
366
367      ctx->DrawSurface = draw;
368      ctx->ReadSurface = read;
369   }
370
371   assert(old_ctx && old_draw && old_read);
372   *old_ctx = prev_ctx;
373   *old_draw = prev_draw;
374   *old_read = prev_read;
375
376   return EGL_TRUE;
377}
378