Merge branch 'mesa_7_7_branch'
[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 surface as the draw or read surface. Return the
144 * previous surface that the context is bound to.
145 *
146 * Note that the context may be NULL.
147 */
148 static _EGLSurface *
149 _eglBindContextToSurface(_EGLContext *ctx, _EGLSurface *surf, EGLint readdraw)
150 {
151 _EGLSurface **attachment, *oldSurf;
152
153 if (!ctx) {
154 surf->Binding = NULL;
155 return NULL;
156 }
157
158 attachment = (readdraw == EGL_DRAW) ?
159 &ctx->DrawSurface : &ctx->ReadSurface;
160 oldSurf = *attachment;
161
162 if (oldSurf == surf)
163 return NULL;
164
165 if (oldSurf)
166 oldSurf->Binding = NULL;
167 surf->Binding = ctx;
168 *attachment = surf;
169
170 return oldSurf;
171 }
172
173
174 /**
175 * Bind the context to the thread and return the previous context.
176 *
177 * Note that the context may be NULL.
178 */
179 static _EGLContext *
180 _eglBindContextToThread(_EGLContext *ctx, _EGLThreadInfo *t)
181 {
182 EGLint apiIndex;
183 _EGLContext *oldCtx;
184
185 apiIndex = (ctx) ?
186 _eglConvertApiToIndex(ctx->ClientAPI) : t->CurrentAPIIndex;
187
188 oldCtx = t->CurrentContexts[apiIndex];
189 if (ctx == oldCtx)
190 return NULL;
191
192 if (oldCtx)
193 oldCtx->Binding = NULL;
194 if (ctx)
195 ctx->Binding = t;
196
197 t->CurrentContexts[apiIndex] = ctx;
198
199 return oldCtx;
200 }
201
202
203 /**
204 * Return true if the given context and surfaces can be made current.
205 */
206 static EGLBoolean
207 _eglCheckMakeCurrent(_EGLContext *ctx, _EGLSurface *draw, _EGLSurface *read)
208 {
209 _EGLThreadInfo *t = _eglGetCurrentThread();
210 EGLint conflict_api;
211
212 if (_eglIsCurrentThreadDummy())
213 return _eglError(EGL_BAD_ALLOC, "eglMakeCurrent");
214
215 /* this is easy */
216 if (!ctx) {
217 if (draw || read)
218 return _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
219 return EGL_TRUE;
220 }
221
222 /* ctx/draw/read must be all given */
223 if (draw == NULL || read == NULL)
224 return _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
225
226 /* context stealing from another thread is not allowed */
227 if (ctx->Binding && ctx->Binding != t)
228 return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
229
230 /*
231 * The spec says
232 *
233 * "If ctx is current to some other thread, or if either draw or read are
234 * bound to contexts in another thread, an EGL_BAD_ACCESS error is
235 * generated."
236 *
237 * But it also says
238 *
239 * "at most one context may be bound to a particular surface at a given
240 * time"
241 *
242 * The latter is more restrictive so we can check only the latter case.
243 */
244 if ((draw->Binding && draw->Binding != ctx) ||
245 (read->Binding && read->Binding != ctx))
246 return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
247
248 /* simply require the configs to be equal */
249 if (draw->Config != ctx->Config || read->Config != ctx->Config)
250 return _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
251
252 switch (ctx->ClientAPI) {
253 #ifdef EGL_VERSION_1_4
254 /* OpenGL and OpenGL ES are conflicting */
255 case EGL_OPENGL_ES_API:
256 conflict_api = EGL_OPENGL_API;
257 break;
258 case EGL_OPENGL_API:
259 conflict_api = EGL_OPENGL_ES_API;
260 break;
261 #endif
262 default:
263 conflict_api = -1;
264 break;
265 }
266
267 if (conflict_api >= 0 && _eglGetAPIContext(conflict_api))
268 return _eglError(EGL_BAD_ACCESS, "eglMakeCurrent");
269
270 return EGL_TRUE;
271 }
272
273
274 /**
275 * Drivers will typically call this to do the error checking and
276 * update the various flags.
277 * Then, the driver will do its device-dependent Make-Current stuff.
278 */
279 EGLBoolean
280 _eglMakeCurrent(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *draw,
281 _EGLSurface *read, _EGLContext *ctx)
282 {
283 _EGLThreadInfo *t = _eglGetCurrentThread();
284 _EGLSurface *oldDraw = NULL, *oldRead = NULL;
285 _EGLContext *oldCtx;
286
287 if (!_eglCheckMakeCurrent(ctx, draw, read))
288 return EGL_FALSE;
289
290 oldCtx = _eglBindContextToThread(ctx, t);
291
292 if (ctx) {
293 oldDraw = _eglBindContextToSurface(ctx, draw, EGL_DRAW);
294 oldRead = _eglBindContextToSurface(ctx, read, EGL_READ);
295 }
296 else if (oldCtx) {
297 /* unbind the old context from its binding surfaces */
298 oldDraw = oldCtx->DrawSurface;
299 oldRead = oldCtx->ReadSurface;
300
301 if (oldDraw)
302 _eglBindContextToSurface(NULL, oldDraw, EGL_DRAW);
303 if (oldRead && oldRead != oldDraw)
304 _eglBindContextToSurface(NULL, oldRead, EGL_READ);
305 }
306
307 /* avoid double destroy */
308 if (oldRead && oldRead == oldDraw)
309 oldRead = NULL;
310
311 if (oldCtx && !_eglIsContextLinked(oldCtx))
312 drv->API.DestroyContext(drv, dpy, oldCtx);
313 if (oldDraw && !_eglIsSurfaceLinked(oldDraw))
314 drv->API.DestroySurface(drv, dpy, oldDraw);
315 if (oldRead && !_eglIsSurfaceLinked(oldRead))
316 drv->API.DestroySurface(drv, dpy, oldRead);
317
318 return EGL_TRUE;
319 }
320
321
322 /**
323 * This is defined by the EGL_MESA_copy_context extension.
324 */
325 EGLBoolean
326 _eglCopyContextMESA(_EGLDriver *drv, EGLDisplay dpy, EGLContext source,
327 EGLContext dest, EGLint mask)
328 {
329 /* This function will always have to be overridden/implemented in the
330 * device driver. If the driver is based on Mesa, use _mesa_copy_context().
331 */
332 return EGL_FALSE;
333 }