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