1 /**************************************************************************
2 *
3 * Copyright 2009, VMware, Inc.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27 /*
28 * Author: Keith Whitwell <keithw@vmware.com>
29 * Author: Jakob Bornecrantz <wallbraker@gmail.com>
30 */
31
32 #include "dri_screen.h"
33 #include "dri_drawable.h"
34 #include "dri_context.h"
35 #include "frontend/drm_driver.h"
36
37 #include "pipe/p_context.h"
38 #include "pipe-loader/pipe_loader.h"
39 #include "state_tracker/st_context.h"
40
41 #include "util/u_cpu_detect.h"
42 #include "util/u_memory.h"
43 #include "util/u_debug.h"
44
45 struct dri_context *
dri_create_context(struct dri_screen * screen,gl_api api,const struct gl_config * visual,const struct __DriverContextConfig * ctx_config,unsigned * error,struct dri_context * sharedContextPrivate,void * loaderPrivate)46 dri_create_context(struct dri_screen *screen,
47 gl_api api, const struct gl_config *visual,
48 const struct __DriverContextConfig *ctx_config,
49 unsigned *error,
50 struct dri_context *sharedContextPrivate,
51 void *loaderPrivate)
52 {
53 struct dri_context *ctx = NULL;
54 struct st_context *st_share = NULL;
55 struct st_context_attribs attribs;
56 enum st_context_error ctx_err = 0;
57 unsigned allowed_flags = __DRI_CTX_FLAG_DEBUG |
58 __DRI_CTX_FLAG_FORWARD_COMPATIBLE;
59 unsigned allowed_attribs =
60 __DRIVER_CONTEXT_ATTRIB_PRIORITY |
61 __DRIVER_CONTEXT_ATTRIB_RELEASE_BEHAVIOR |
62 __DRIVER_CONTEXT_ATTRIB_NO_ERROR;
63 const __DRIbackgroundCallableExtension *backgroundCallable =
64 screen->dri2.backgroundCallable;
65 const struct driOptionCache *optionCache = &screen->dev->option_cache;
66
67 /* This is effectively doing error checking for GLX context creation (by both
68 * Mesa and the X server) when the driver doesn't support the robustness ext.
69 * EGL already checks, so it won't send us the flags if the ext isn't
70 * available.
71 */
72 if (screen->has_reset_status_query) {
73 allowed_flags |= __DRI_CTX_FLAG_ROBUST_BUFFER_ACCESS;
74 allowed_attribs |= __DRIVER_CONTEXT_ATTRIB_RESET_STRATEGY;
75 }
76
77 if (screen->has_protected_context)
78 allowed_attribs |= __DRIVER_CONTEXT_ATTRIB_PROTECTED;
79
80 if (ctx_config->flags & ~allowed_flags) {
81 *error = __DRI_CTX_ERROR_UNKNOWN_FLAG;
82 goto fail;
83 }
84
85 if (ctx_config->attribute_mask & ~allowed_attribs) {
86 *error = __DRI_CTX_ERROR_UNKNOWN_ATTRIBUTE;
87 goto fail;
88 }
89
90 memset(&attribs, 0, sizeof(attribs));
91 switch (api) {
92 case API_OPENGLES:
93 attribs.profile = API_OPENGLES;
94 break;
95 case API_OPENGLES2:
96 attribs.profile = API_OPENGLES2;
97 break;
98 case API_OPENGL_COMPAT:
99 case API_OPENGL_CORE:
100 if (driQueryOptionb(optionCache, "force_compat_profile")) {
101 attribs.profile = API_OPENGL_COMPAT;
102 } else {
103 attribs.profile = api == API_OPENGL_COMPAT ? API_OPENGL_COMPAT
104 : API_OPENGL_CORE;
105 }
106
107 attribs.major = ctx_config->major_version;
108 attribs.minor = ctx_config->minor_version;
109
110 if ((ctx_config->flags & __DRI_CTX_FLAG_FORWARD_COMPATIBLE) != 0)
111 attribs.flags |= ST_CONTEXT_FLAG_FORWARD_COMPATIBLE;
112 break;
113 default:
114 *error = __DRI_CTX_ERROR_BAD_API;
115 goto fail;
116 }
117
118 if ((ctx_config->flags & __DRI_CTX_FLAG_DEBUG) != 0)
119 attribs.flags |= ST_CONTEXT_FLAG_DEBUG;
120
121 if (ctx_config->flags & __DRI_CTX_FLAG_ROBUST_BUFFER_ACCESS)
122 attribs.context_flags |= PIPE_CONTEXT_ROBUST_BUFFER_ACCESS;
123
124 if (ctx_config->attribute_mask & __DRIVER_CONTEXT_ATTRIB_RESET_STRATEGY)
125 if (ctx_config->reset_strategy != __DRI_CTX_RESET_NO_NOTIFICATION)
126 attribs.context_flags |= PIPE_CONTEXT_LOSE_CONTEXT_ON_RESET;
127
128 if (ctx_config->attribute_mask & __DRIVER_CONTEXT_ATTRIB_NO_ERROR)
129 attribs.flags |= ctx_config->no_error ? ST_CONTEXT_FLAG_NO_ERROR : 0;
130
131 if (ctx_config->attribute_mask & __DRIVER_CONTEXT_ATTRIB_PRIORITY) {
132 switch (ctx_config->priority) {
133 case __DRI_CTX_PRIORITY_LOW:
134 attribs.context_flags |= PIPE_CONTEXT_LOW_PRIORITY;
135 break;
136 case __DRI_CTX_PRIORITY_HIGH:
137 attribs.context_flags |= PIPE_CONTEXT_HIGH_PRIORITY;
138 break;
139 case __DRI_CTX_PRIORITY_REALTIME:
140 attribs.context_flags |= PIPE_CONTEXT_REALTIME_PRIORITY;
141 break;
142 default:
143 break;
144 }
145 }
146
147 if ((ctx_config->attribute_mask & __DRIVER_CONTEXT_ATTRIB_RELEASE_BEHAVIOR)
148 && (ctx_config->release_behavior == __DRI_CTX_RELEASE_BEHAVIOR_NONE))
149 attribs.flags |= ST_CONTEXT_FLAG_RELEASE_NONE;
150
151 if (ctx_config->attribute_mask & __DRIVER_CONTEXT_ATTRIB_PROTECTED)
152 attribs.context_flags |= PIPE_CONTEXT_PROTECTED;
153
154 struct dri_context *share_ctx = NULL;
155 if (sharedContextPrivate) {
156 share_ctx = (struct dri_context *)sharedContextPrivate;
157 st_share = share_ctx->st;
158 }
159
160 ctx = CALLOC_STRUCT(dri_context);
161 if (ctx == NULL) {
162 *error = __DRI_CTX_ERROR_NO_MEMORY;
163 goto fail;
164 }
165
166 ctx->screen = screen;
167 ctx->loaderPrivate = loaderPrivate;
168
169 /* KHR_no_error is likely to crash, overflow memory, etc if an application
170 * has errors so don't enable it for setuid processes.
171 */
172 if (debug_get_bool_option("MESA_NO_ERROR", false) ||
173 driQueryOptionb(&screen->dev->option_cache, "mesa_no_error"))
174 #if !defined(_WIN32)
175 if (__normal_user())
176 #endif
177 attribs.flags |= ST_CONTEXT_FLAG_NO_ERROR;
178
179 attribs.options = screen->options;
180 dri_fill_st_visual(&attribs.visual, screen, visual);
181 ctx->st = st_api_create_context(&screen->base, &attribs, &ctx_err,
182 st_share);
183 if (ctx->st == NULL) {
184 switch (ctx_err) {
185 case ST_CONTEXT_SUCCESS:
186 *error = __DRI_CTX_ERROR_SUCCESS;
187 break;
188 case ST_CONTEXT_ERROR_NO_MEMORY:
189 *error = __DRI_CTX_ERROR_NO_MEMORY;
190 break;
191 case ST_CONTEXT_ERROR_BAD_VERSION:
192 *error = __DRI_CTX_ERROR_BAD_VERSION;
193 break;
194 }
195 goto fail;
196 }
197 ctx->st->frontend_context = (void *) ctx;
198
199 if (ctx->st->cso_context) {
200 ctx->pp = pp_init(ctx->st->pipe, screen->pp_enabled, ctx->st->cso_context,
201 ctx->st, st_context_invalidate_state);
202 ctx->hud = hud_create(ctx->st->cso_context,
203 share_ctx ? share_ctx->hud : NULL,
204 ctx->st, st_context_invalidate_state);
205 }
206
207 /* order of precedence (least to most):
208 * - driver setting
209 * - app setting
210 * - user setting
211 */
212 bool enable_glthread = driQueryOptionb(&screen->dev->option_cache, "mesa_glthread_driver");
213
214 /* always disable glthread by default if fewer than 5 "big" CPUs are active */
215 unsigned nr_big_cpus = util_get_cpu_caps()->nr_big_cpus;
216 if (util_get_cpu_caps()->nr_cpus < 4 || (nr_big_cpus && nr_big_cpus < 5))
217 enable_glthread = false;
218
219 int app_enable_glthread = driQueryOptioni(&screen->dev->option_cache, "mesa_glthread_app_profile");
220 if (app_enable_glthread != -1) {
221 /* if set (not -1), apply the app setting */
222 enable_glthread = app_enable_glthread == 1;
223 }
224 if (getenv("mesa_glthread")) {
225 /* only apply the env var if set */
226 bool user_enable_glthread = debug_get_bool_option("mesa_glthread", false);
227 if (user_enable_glthread != enable_glthread) {
228 /* print warning to mimic old behavior */
229 fprintf(stderr, "ATTENTION: default value of option mesa_glthread overridden by environment.\n");
230 }
231 enable_glthread = user_enable_glthread;
232 }
233 /* Do this last. */
234 if (enable_glthread) {
235 bool safe = true;
236
237 /* This is only needed by X11/DRI2, which can be unsafe. */
238 if (backgroundCallable &&
239 backgroundCallable->base.version >= 2 &&
240 backgroundCallable->isThreadSafe &&
241 !backgroundCallable->isThreadSafe(loaderPrivate))
242 safe = false;
243
244 if (safe)
245 _mesa_glthread_init(ctx->st->ctx);
246 }
247
248 *error = __DRI_CTX_ERROR_SUCCESS;
249 return ctx;
250
251 fail:
252 if (ctx && ctx->st)
253 st_destroy_context(ctx->st);
254
255 free(ctx);
256 return NULL;
257 }
258
259 void
dri_destroy_context(struct dri_context * ctx)260 dri_destroy_context(struct dri_context *ctx)
261 {
262 /* Wait for glthread to finish because we can't use pipe_context from
263 * multiple threads.
264 */
265 _mesa_glthread_finish(ctx->st->ctx);
266
267 if (ctx->hud) {
268 hud_destroy(ctx->hud, ctx->st->cso_context);
269 }
270
271 if (ctx->pp)
272 pp_free(ctx->pp);
273
274 /* No particular reason to wait for command completion before
275 * destroying a context, but we flush the context here
276 * to avoid having to add code elsewhere to cope with flushing a
277 * partially destroyed context.
278 */
279 st_context_flush(ctx->st, 0, NULL, NULL, NULL);
280 st_destroy_context(ctx->st);
281 free(ctx);
282 }
283
284 /* This is called inside MakeCurrent to unbind the context. */
285 bool
dri_unbind_context(struct dri_context * ctx)286 dri_unbind_context(struct dri_context *ctx)
287 {
288 /* dri_util.c ensures cPriv is not null */
289 struct st_context *st = ctx->st;
290
291 if (st == st_api_get_current()) {
292 _mesa_glthread_finish(st->ctx);
293
294 /* Record HUD queries for the duration the context was "current". */
295 if (ctx->hud)
296 hud_record_only(ctx->hud, st->pipe);
297
298 st_api_make_current(NULL, NULL, NULL);
299 }
300
301 if (ctx->draw || ctx->read) {
302 assert(ctx->draw);
303
304 dri_put_drawable(ctx->draw);
305
306 if (ctx->read != ctx->draw)
307 dri_put_drawable(ctx->read);
308
309 ctx->draw = NULL;
310 ctx->read = NULL;
311 }
312
313 return GL_TRUE;
314 }
315
316 bool
dri_make_current(struct dri_context * ctx,struct dri_drawable * draw,struct dri_drawable * read)317 dri_make_current(struct dri_context *ctx,
318 struct dri_drawable *draw,
319 struct dri_drawable *read)
320 {
321 /* dri_unbind_context() is always called before this, so drawables are
322 * always NULL here.
323 */
324 assert(!ctx->draw);
325 assert(!ctx->read);
326
327 if ((draw && !read) || (!draw && read))
328 return GL_FALSE; /* only both non-NULL or both NULL are allowed */
329
330 /* Wait for glthread to finish because we can't use st_context from
331 * multiple threads.
332 */
333 _mesa_glthread_finish(ctx->st->ctx);
334
335 /* There are 2 cases that can occur here. Either we bind drawables, or we
336 * bind NULL for configless and surfaceless contexts.
337 */
338 if (!draw && !read)
339 return st_api_make_current(ctx->st, NULL, NULL);
340
341 /* Bind drawables to the context */
342 ctx->draw = draw;
343 ctx->read = read;
344
345 dri_get_drawable(draw);
346 draw->texture_stamp = draw->lastStamp - 1;
347
348 if (draw != read) {
349 dri_get_drawable(read);
350 read->texture_stamp = read->lastStamp - 1;
351 }
352
353 st_api_make_current(ctx->st, &draw->base, &read->base);
354
355 /* This is ok to call here. If they are already init, it's a no-op. */
356 if (ctx->pp && draw->textures[ST_ATTACHMENT_BACK_LEFT])
357 pp_init_fbos(ctx->pp, draw->textures[ST_ATTACHMENT_BACK_LEFT]->width0,
358 draw->textures[ST_ATTACHMENT_BACK_LEFT]->height0);
359
360 return GL_TRUE;
361 }
362
363 struct dri_context *
dri_get_current(void)364 dri_get_current(void)
365 {
366 struct st_context *st = st_api_get_current();
367
368 return (struct dri_context *) st ? st->frontend_context : NULL;
369 }
370
371 /* vim: set sw=3 ts=8 sts=3 expandtab: */
372