egl: eglMakeCurrent should accept an uninitialized display.
[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 "egldriver.h"
8 #include "eglglobals.h"
9 #include "eglsurface.h"
10
11
12 /**
13 * Initialize the given _EGLContext object to defaults and/or the values
14 * in the attrib_list.
15 */
16 EGLBoolean
17 _eglInitContext(_EGLDriver *drv, _EGLContext *ctx,
18 _EGLConfig *conf, const EGLint *attrib_list)
19 {
20 EGLint i;
21 const EGLenum api = eglQueryAPI();
22
23 if (api == EGL_NONE) {
24 _eglError(EGL_BAD_MATCH, "eglCreateContext(no client API)");
25 return EGL_FALSE;
26 }
27
28 memset(ctx, 0, sizeof(_EGLContext));
29
30 ctx->ClientVersion = 1; /* the default, per EGL spec */
31
32 for (i = 0; attrib_list && attrib_list[i] != EGL_NONE; i++) {
33 switch (attrib_list[i]) {
34 case EGL_CONTEXT_CLIENT_VERSION:
35 i++;
36 ctx->ClientVersion = attrib_list[i];
37 break;
38 default:
39 _eglError(EGL_BAD_ATTRIBUTE, "_eglInitContext");
40 return EGL_FALSE;
41 }
42 }
43
44 ctx->Config = conf;
45 ctx->DrawSurface = EGL_NO_SURFACE;
46 ctx->ReadSurface = EGL_NO_SURFACE;
47 ctx->ClientAPI = api;
48 ctx->WindowRenderBuffer = EGL_NONE;
49
50 return EGL_TRUE;
51 }
52
53
54 /**
55 * Just a placeholder/demo function. Real driver will never use this!
56 */
57 _EGLContext *
58 _eglCreateContext(_EGLDriver *drv, _EGLDisplay *dpy, _EGLConfig *conf,
59 _EGLContext *share_list, const EGLint *attrib_list)
60 {
61 #if 0 /* example code */
62 _EGLContext *context;
63
64 context = (_EGLContext *) calloc(1, sizeof(_EGLContext));
65 if (!context)
66 return NULL;
67
68 if (!_eglInitContext(drv, context, conf, attrib_list)) {
69 free(context);
70 return NULL;
71 }
72
73 return context;
74 #endif
75 return NULL;
76 }
77
78
79 /**
80 * Default fallback routine - drivers should usually override this.
81 */
82 EGLBoolean
83 _eglDestroyContext(_EGLDriver *drv, _EGLDisplay *dpy, _EGLContext *ctx)
84 {
85 if (!_eglIsContextBound(ctx))
86 free(ctx);
87 return EGL_TRUE;
88 }
89
90
91 #ifdef EGL_VERSION_1_2
92 static EGLint
93 _eglQueryContextRenderBuffer(_EGLContext *ctx)
94 {
95 _EGLSurface *surf = ctx->DrawSurface;
96 EGLint rb;
97
98 if (!surf)
99 return EGL_NONE;
100 if (surf->Type == EGL_WINDOW_BIT && ctx->WindowRenderBuffer != EGL_NONE)
101 rb = ctx->WindowRenderBuffer;
102 else
103 rb = surf->RenderBuffer;
104 return rb;
105 }
106 #endif /* EGL_VERSION_1_2 */
107
108
109 EGLBoolean
110 _eglQueryContext(_EGLDriver *drv, _EGLDisplay *dpy, _EGLContext *c,
111 EGLint attribute, EGLint *value)
112 {
113 (void) drv;
114 (void) dpy;
115
116 if (!value)
117 return _eglError(EGL_BAD_PARAMETER, "eglQueryContext");
118
119 switch (attribute) {
120 case EGL_CONFIG_ID:
121 *value = GET_CONFIG_ATTRIB(c->Config, EGL_CONFIG_ID);
122 break;
123 case EGL_CONTEXT_CLIENT_VERSION:
124 *value = c->ClientVersion;
125 break;
126 #ifdef EGL_VERSION_1_2
127 case EGL_CONTEXT_CLIENT_TYPE:
128 *value = c->ClientAPI;
129 break;
130 case EGL_RENDER_BUFFER:
131 *value = _eglQueryContextRenderBuffer(c);
132 break;
133 #endif /* EGL_VERSION_1_2 */
134 default:
135 return _eglError(EGL_BAD_ATTRIBUTE, "eglQueryContext");
136 }
137
138 return EGL_TRUE;
139 }
140
141
142 /**
143 * Bind the context to the surfaces. Return the surfaces that are "orphaned".
144 * That is, when the context is not NULL, return the surfaces it previously
145 * bound to; when the context is NULL, the same surfaces are returned.
146 */
147 static void
148 _eglBindContextToSurfaces(_EGLContext *ctx,
149 _EGLSurface **draw, _EGLSurface **read)
150 {
151 _EGLSurface *newDraw = *draw, *newRead = *read;
152
153 if (newDraw->Binding)
154 newDraw->Binding->DrawSurface = NULL;
155 newDraw->Binding = ctx;
156
157 if (newRead->Binding)
158 newRead->Binding->ReadSurface = NULL;
159 newRead->Binding = ctx;
160
161 if (ctx) {
162 *draw = ctx->DrawSurface;
163 ctx->DrawSurface = newDraw;
164
165 *read = ctx->ReadSurface;
166 ctx->ReadSurface = newRead;
167 }
168 }
169
170
171 /**
172 * Bind the context to the thread and return the previous context.
173 *
174 * Note that the context may be NULL.
175 */
176 static _EGLContext *
177 _eglBindContextToThread(_EGLContext *ctx, _EGLThreadInfo *t)
178 {
179 EGLint apiIndex;
180 _EGLContext *oldCtx;
181
182 apiIndex = (ctx) ?
183 _eglConvertApiToIndex(ctx->ClientAPI) : t->CurrentAPIIndex;
184
185 oldCtx = t->CurrentContexts[apiIndex];
186 if (ctx == oldCtx)
187 return NULL;
188
189 if (oldCtx)
190 oldCtx->Binding = NULL;
191 if (ctx)
192 ctx->Binding = t;
193
194 t->CurrentContexts[apiIndex] = ctx;
195
196 return oldCtx;
197 }
198
199
200 /**
201 * Return true if the given context and surfaces can be made current.
202 */
203 static EGLBoolean
204 _eglCheckMakeCurrent(_EGLContext *ctx, _EGLSurface *draw, _EGLSurface *read)
205 {
206 _EGLThreadInfo *t = _eglGetCurrentThread();
207 EGLint conflict_api;
208
209 if (_eglIsCurrentThreadDummy())
210 return _eglError(EGL_BAD_ALLOC, "eglMakeCurrent");
211
212 /* this is easy */
213 if (!ctx) {
214 if (draw || read)
215 return _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
216 return EGL_TRUE;
217 }
218
219 /* ctx/draw/read must be all given */
220 if (draw == NULL || read == NULL)
221 return _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
222
223 /* context stealing from another thread is not allowed */
224 if (ctx->Binding && ctx->Binding != t)
225 return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
226
227 /*
228 * The spec says
229 *
230 * "If ctx is current to some other thread, or if either draw or read are
231 * bound to contexts in another thread, an EGL_BAD_ACCESS error is
232 * generated."
233 *
234 * But it also says
235 *
236 * "at most one context may be bound to a particular surface at a given
237 * time"
238 *
239 * The latter is more restrictive so we can check only the latter case.
240 */
241 if ((draw->Binding && draw->Binding != ctx) ||
242 (read->Binding && read->Binding != ctx))
243 return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
244
245 /* simply require the configs to be equal */
246 if (draw->Config != ctx->Config || read->Config != ctx->Config)
247 return _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
248
249 switch (ctx->ClientAPI) {
250 #ifdef EGL_VERSION_1_4
251 /* OpenGL and OpenGL ES are conflicting */
252 case EGL_OPENGL_ES_API:
253 conflict_api = EGL_OPENGL_API;
254 break;
255 case EGL_OPENGL_API:
256 conflict_api = EGL_OPENGL_ES_API;
257 break;
258 #endif
259 default:
260 conflict_api = -1;
261 break;
262 }
263
264 if (conflict_api >= 0 && _eglGetAPIContext(conflict_api))
265 return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
266
267 return EGL_TRUE;
268 }
269
270
271 /**
272 * Bind the context to the current thread and given surfaces. Return the
273 * previously bound context and the surfaces it bound to. Each argument is
274 * both input and output.
275 */
276 EGLBoolean
277 _eglBindContext(_EGLContext **ctx, _EGLSurface **draw, _EGLSurface **read)
278 {
279 _EGLThreadInfo *t = _eglGetCurrentThread();
280 _EGLContext *newCtx = *ctx, *oldCtx;
281
282 if (!_eglCheckMakeCurrent(newCtx, *draw, *read))
283 return EGL_FALSE;
284
285 /* bind the new context */
286 oldCtx = _eglBindContextToThread(newCtx, t);
287 *ctx = oldCtx;
288 if (newCtx)
289 _eglBindContextToSurfaces(newCtx, draw, read);
290
291 /* unbind the old context from its binding surfaces */
292 if (oldCtx) {
293 /*
294 * If the new context replaces some old context, the new one should not
295 * be current before the replacement and it should not be bound to any
296 * surface.
297 */
298 if (newCtx)
299 assert(!*draw && !*read);
300
301 *draw = oldCtx->DrawSurface;
302 *read = oldCtx->ReadSurface;
303 assert(*draw && *read);
304
305 _eglBindContextToSurfaces(NULL, draw, read);
306 }
307
308 return EGL_TRUE;
309 }
310
311
312 /**
313 * Drivers will typically call this to do the error checking and
314 * update the various flags.
315 * Then, the driver will do its device-dependent Make-Current stuff.
316 */
317 EGLBoolean
318 _eglMakeCurrent(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *draw,
319 _EGLSurface *read, _EGLContext *ctx)
320 {
321 if (!_eglBindContext(&ctx, &draw, &read))
322 return EGL_FALSE;
323
324 /* nothing we can do if the display is uninitialized */
325 if (dpy->Initialized) {
326 /* avoid double destroy */
327 if (read && read == draw)
328 read = NULL;
329
330 if (ctx && !_eglIsContextLinked(ctx))
331 drv->API.DestroyContext(drv, dpy, ctx);
332 if (draw && !_eglIsSurfaceLinked(draw))
333 drv->API.DestroySurface(drv, dpy, draw);
334 if (read && !_eglIsSurfaceLinked(read))
335 drv->API.DestroySurface(drv, dpy, read);
336 }
337
338 return EGL_TRUE;
339 }
340
341
342 /**
343 * This is defined by the EGL_MESA_copy_context extension.
344 */
345 EGLBoolean
346 _eglCopyContextMESA(_EGLDriver *drv, EGLDisplay dpy, EGLContext source,
347 EGLContext dest, EGLint mask)
348 {
349 /* This function will always have to be overridden/implemented in the
350 * device driver. If the driver is based on Mesa, use _mesa_copy_context().
351 */
352 return EGL_FALSE;
353 }