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