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