Merge commit 'origin/7.8'
[profile/ivi/mesa.git] / src / egl / main / eglcontext.c
1 #include <assert.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include "eglconfig.h"
5 #include "eglcontext.h"
6 #include "egldisplay.h"
7 #include "eglcurrent.h"
8 #include "eglsurface.h"
9 #include "egllog.h"
10
11
12 /**
13  * Return the API bit (one of EGL_xxx_BIT) of the context.
14  */
15 static EGLint
16 _eglGetContextAPIBit(_EGLContext *ctx)
17 {
18    EGLint bit = 0;
19
20    switch (ctx->ClientAPI) {
21    case EGL_OPENGL_ES_API:
22       switch (ctx->ClientVersion) {
23       case 1:
24          bit = EGL_OPENGL_ES_BIT;
25          break;
26       case 2:
27          bit = EGL_OPENGL_ES2_BIT;
28          break;
29       default:
30          break;
31       }
32       break;
33    case EGL_OPENVG_API:
34       bit = EGL_OPENVG_BIT;
35       break;
36    case EGL_OPENGL_API:
37       bit = EGL_OPENGL_BIT;
38       break;
39    default:
40       break;
41    }
42
43    return bit;
44 }
45
46
47 /**
48  * Parse the list of context attributes and return the proper error code.
49  */
50 static EGLint
51 _eglParseContextAttribList(_EGLContext *ctx, const EGLint *attrib_list)
52 {
53    EGLenum api = ctx->ClientAPI;
54    EGLint i, err = EGL_SUCCESS;
55
56    if (!attrib_list)
57       return EGL_SUCCESS;
58
59    for (i = 0; attrib_list[i] != EGL_NONE; i++) {
60       EGLint attr = attrib_list[i++];
61       EGLint val = attrib_list[i];
62
63       switch (attr) {
64       case EGL_CONTEXT_CLIENT_VERSION:
65          if (api != EGL_OPENGL_ES_API) {
66             err = EGL_BAD_ATTRIBUTE;
67             break;
68          }
69          if (val != 1 && val != 2) {
70             err = EGL_BAD_ATTRIBUTE;
71             break;
72          }
73          ctx->ClientVersion = val;
74          break;
75       default:
76          err = EGL_BAD_ATTRIBUTE;
77          break;
78       }
79
80       if (err != EGL_SUCCESS) {
81          _eglLog(_EGL_DEBUG, "bad context attribute 0x%04x", attr);
82          break;
83       }
84    }
85
86    if (err == EGL_SUCCESS) {
87       EGLint renderable_type, api_bit;
88
89       renderable_type = GET_CONFIG_ATTRIB(ctx->Config, EGL_RENDERABLE_TYPE);
90       api_bit = _eglGetContextAPIBit(ctx);
91       if (!(renderable_type & api_bit))
92          err = EGL_BAD_CONFIG;
93    }
94
95    return err;
96 }
97
98
99 /**
100  * Initialize the given _EGLContext object to defaults and/or the values
101  * in the attrib_list.
102  */
103 EGLBoolean
104 _eglInitContext(_EGLContext *ctx, _EGLDisplay *dpy, _EGLConfig *conf,
105                 const EGLint *attrib_list)
106 {
107    const EGLenum api = eglQueryAPI();
108    EGLint err;
109
110    if (api == EGL_NONE) {
111       _eglError(EGL_BAD_MATCH, "eglCreateContext(no client API)");
112       return EGL_FALSE;
113    }
114
115    memset(ctx, 0, sizeof(_EGLContext));
116    ctx->Resource.Display = dpy;
117    ctx->ClientAPI = api;
118    ctx->Config = conf;
119    ctx->WindowRenderBuffer = EGL_NONE;
120
121    ctx->ClientVersion = 1; /* the default, per EGL spec */
122
123    err = _eglParseContextAttribList(ctx, attrib_list);
124    if (err != EGL_SUCCESS)
125       return _eglError(err, "eglCreateContext");
126
127    return EGL_TRUE;
128 }
129
130
131 /**
132  * Just a placeholder/demo function.  Real driver will never use this!
133  */
134 _EGLContext *
135 _eglCreateContext(_EGLDriver *drv, _EGLDisplay *dpy, _EGLConfig *conf,
136                   _EGLContext *share_list, const EGLint *attrib_list)
137 {
138    return NULL;
139 }
140
141
142 /**
143  * Default fallback routine - drivers should usually override this.
144  */
145 EGLBoolean
146 _eglDestroyContext(_EGLDriver *drv, _EGLDisplay *dpy, _EGLContext *ctx)
147 {
148    if (!_eglIsContextBound(ctx))
149       free(ctx);
150    return EGL_TRUE;
151 }
152
153
154 #ifdef EGL_VERSION_1_2
155 static EGLint
156 _eglQueryContextRenderBuffer(_EGLContext *ctx)
157 {
158    _EGLSurface *surf = ctx->DrawSurface;
159    EGLint rb;
160
161    if (!surf)
162       return EGL_NONE;
163    if (surf->Type == EGL_WINDOW_BIT && ctx->WindowRenderBuffer != EGL_NONE)
164       rb = ctx->WindowRenderBuffer;
165    else
166       rb = surf->RenderBuffer;
167    return rb;
168 }
169 #endif /* EGL_VERSION_1_2 */
170
171
172 EGLBoolean
173 _eglQueryContext(_EGLDriver *drv, _EGLDisplay *dpy, _EGLContext *c,
174                  EGLint attribute, EGLint *value)
175 {
176    (void) drv;
177    (void) dpy;
178
179    if (!value)
180       return _eglError(EGL_BAD_PARAMETER, "eglQueryContext");
181
182    switch (attribute) {
183    case EGL_CONFIG_ID:
184       *value = GET_CONFIG_ATTRIB(c->Config, EGL_CONFIG_ID);
185       break;
186    case EGL_CONTEXT_CLIENT_VERSION:
187       *value = c->ClientVersion;
188       break;
189 #ifdef EGL_VERSION_1_2
190    case EGL_CONTEXT_CLIENT_TYPE:
191       *value = c->ClientAPI;
192       break;
193    case EGL_RENDER_BUFFER:
194       *value = _eglQueryContextRenderBuffer(c);
195       break;
196 #endif /* EGL_VERSION_1_2 */
197    default:
198       return _eglError(EGL_BAD_ATTRIBUTE, "eglQueryContext");
199    }
200
201    return EGL_TRUE;
202 }
203
204
205 /**
206  * Bind the context to the surfaces.  Return the surfaces that are "orphaned".
207  * That is, when the context is not NULL, return the surfaces it previously
208  * bound to;  when the context is NULL, the same surfaces are returned.
209  */
210 static void
211 _eglBindContextToSurfaces(_EGLContext *newCtx,
212                           _EGLSurface **draw, _EGLSurface **read)
213 {
214    _EGLSurface *newDraw = *draw, *newRead = *read;
215    _EGLContext *oldCtx;
216
217    /*
218     * The goal is to bind a newCtx to newDraw.  Since newDraw may already have
219     * a binding context (oldCtx), and newCtx may already be bound to another
220     * surface (oldDraw), the old bindings are broken first and the new one is
221     * created.
222     */
223    oldCtx = newDraw->CurrentContext;
224    if (newCtx != oldCtx) {
225       if (oldCtx) {
226          assert(oldCtx->DrawSurface == newDraw);
227          oldCtx->DrawSurface = NULL;
228       }
229
230       if (newCtx) {
231          _EGLSurface *oldDraw = newCtx->DrawSurface;
232          if (oldDraw)
233             oldDraw->CurrentContext = NULL;
234
235          newCtx->DrawSurface = newDraw;
236          *draw = oldDraw;
237       }
238
239       newDraw->CurrentContext = newCtx;
240    }
241
242    /* likewise */
243    if (newRead != newDraw)
244       oldCtx = newRead->CurrentContext;
245    if (newCtx != oldCtx) {
246       if (oldCtx) {
247          assert(oldCtx->ReadSurface == newRead);
248          oldCtx->ReadSurface = NULL;
249       }
250
251       if (newCtx) {
252          _EGLSurface *oldRead = newCtx->ReadSurface;
253          if (oldRead)
254             oldRead->CurrentContext = NULL;
255
256          newCtx->ReadSurface = newRead;
257          *read = oldRead;
258       }
259
260       newRead->CurrentContext = newCtx;
261    }
262 }
263
264
265 /**
266  * Bind the context to the thread and return the previous context.
267  *
268  * Note that the context may be NULL.
269  */
270 static _EGLContext *
271 _eglBindContextToThread(_EGLContext *ctx, _EGLThreadInfo *t)
272 {
273    EGLint apiIndex;
274    _EGLContext *oldCtx;
275
276    apiIndex = (ctx) ?
277       _eglConvertApiToIndex(ctx->ClientAPI) : t->CurrentAPIIndex;
278
279    oldCtx = t->CurrentContexts[apiIndex];
280    if (ctx != oldCtx) {
281       if (oldCtx)
282          oldCtx->Binding = NULL;
283       if (ctx)
284          ctx->Binding = t;
285
286       t->CurrentContexts[apiIndex] = ctx;
287    }
288
289    return oldCtx;
290 }
291
292
293 /**
294  * Return true if the given context and surfaces can be made current.
295  */
296 static EGLBoolean
297 _eglCheckMakeCurrent(_EGLContext *ctx, _EGLSurface *draw, _EGLSurface *read)
298 {
299    _EGLThreadInfo *t = _eglGetCurrentThread();
300    EGLint conflict_api;
301
302    if (_eglIsCurrentThreadDummy())
303       return _eglError(EGL_BAD_ALLOC, "eglMakeCurrent");
304
305    /* this is easy */
306    if (!ctx) {
307       if (draw || read)
308          return _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
309       return EGL_TRUE;
310    }
311
312    /* ctx/draw/read must be all given */
313    if (draw == NULL || read == NULL)
314       return _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
315
316    /* context stealing from another thread is not allowed */
317    if (ctx->Binding && ctx->Binding != t)
318       return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
319
320    /*
321     * The spec says
322     *
323     * "If ctx is current to some other thread, or if either draw or read are
324     * bound to contexts in another thread, an EGL_BAD_ACCESS error is
325     * generated."
326     *
327     * But it also says
328     *
329     * "at most one context may be bound to a particular surface at a given
330     * time"
331     *
332     * The latter is more restrictive so we can check only the latter case.
333     */
334    if ((draw->CurrentContext && draw->CurrentContext != ctx) ||
335        (read->CurrentContext && read->CurrentContext != ctx))
336       return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
337
338    /* simply require the configs to be equal */
339    if (draw->Config != ctx->Config || read->Config != ctx->Config)
340       return _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
341
342    switch (ctx->ClientAPI) {
343 #ifdef EGL_VERSION_1_4
344    /* OpenGL and OpenGL ES are conflicting */
345    case EGL_OPENGL_ES_API:
346       conflict_api = EGL_OPENGL_API;
347       break;
348    case EGL_OPENGL_API:
349       conflict_api = EGL_OPENGL_ES_API;
350       break;
351 #endif
352    default:
353       conflict_api = -1;
354       break;
355    }
356
357    if (conflict_api >= 0 && _eglGetAPIContext(conflict_api))
358       return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
359
360    return EGL_TRUE;
361 }
362
363
364 /**
365  * Bind the context to the current thread and given surfaces.  Return the
366  * previously bound context and the surfaces it bound to.  Each argument is
367  * both input and output.
368  */
369 EGLBoolean
370 _eglBindContext(_EGLContext **ctx, _EGLSurface **draw, _EGLSurface **read)
371 {
372    _EGLThreadInfo *t = _eglGetCurrentThread();
373    _EGLContext *newCtx = *ctx, *oldCtx;
374
375    if (!_eglCheckMakeCurrent(newCtx, *draw, *read))
376       return EGL_FALSE;
377
378    /* bind the new context */
379    oldCtx = _eglBindContextToThread(newCtx, t);
380
381    if (newCtx)
382       _eglBindContextToSurfaces(newCtx, draw, read);
383
384    /* unbind the old context from its binding surfaces */
385    if (oldCtx && oldCtx != newCtx) {
386       assert(!*draw && !*read);
387
388       *draw = oldCtx->DrawSurface;
389       *read = oldCtx->ReadSurface;
390       assert(*draw && *read);
391
392       _eglBindContextToSurfaces(NULL, draw, read);
393    }
394
395    *ctx = oldCtx;
396    /* draw and read have been updated in _eglBindContextToSurfaces */
397
398    return EGL_TRUE;
399 }
400
401
402 /**
403  * Just a placeholder/demo function.  Drivers should override this.
404  */
405 EGLBoolean
406 _eglMakeCurrent(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *draw,
407                 _EGLSurface *read, _EGLContext *ctx)
408 {
409    return EGL_FALSE;
410 }
411
412
413 /**
414  * This is defined by the EGL_MESA_copy_context extension.
415  */
416 EGLBoolean
417 _eglCopyContextMESA(_EGLDriver *drv, EGLDisplay dpy, EGLContext source,
418                     EGLContext dest, EGLint mask)
419 {
420    /* This function will always have to be overridden/implemented in the
421     * device driver.  If the driver is based on Mesa, use _mesa_copy_context().
422     */
423    return EGL_FALSE;
424 }