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