xref: /aosp_15_r20/external/mesa3d/src/gallium/frontends/dri/dri_context.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
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 <[email protected]>
29  * Author: Jakob Bornecrantz <[email protected]>
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       default:
140          break;
141       }
142    }
143 
144    if ((ctx_config->attribute_mask & __DRIVER_CONTEXT_ATTRIB_RELEASE_BEHAVIOR)
145        && (ctx_config->release_behavior == __DRI_CTX_RELEASE_BEHAVIOR_NONE))
146       attribs.flags |= ST_CONTEXT_FLAG_RELEASE_NONE;
147 
148    if (ctx_config->attribute_mask & __DRIVER_CONTEXT_ATTRIB_PROTECTED)
149       attribs.context_flags |= PIPE_CONTEXT_PROTECTED;
150 
151    struct dri_context *share_ctx = NULL;
152    if (sharedContextPrivate) {
153       share_ctx = (struct dri_context *)sharedContextPrivate;
154       st_share = share_ctx->st;
155    }
156 
157    ctx = CALLOC_STRUCT(dri_context);
158    if (ctx == NULL) {
159       *error = __DRI_CTX_ERROR_NO_MEMORY;
160       goto fail;
161    }
162 
163    ctx->screen = screen;
164    ctx->loaderPrivate = loaderPrivate;
165 
166    /* KHR_no_error is likely to crash, overflow memory, etc if an application
167     * has errors so don't enable it for setuid processes.
168     */
169    if (debug_get_bool_option("MESA_NO_ERROR", false) ||
170        driQueryOptionb(&screen->dev->option_cache, "mesa_no_error"))
171 #if !defined(_WIN32)
172       if (__normal_user())
173 #endif
174          attribs.flags |= ST_CONTEXT_FLAG_NO_ERROR;
175 
176    attribs.options = screen->options;
177    dri_fill_st_visual(&attribs.visual, screen, visual);
178    ctx->st = st_api_create_context(&screen->base, &attribs, &ctx_err,
179 				   st_share);
180    if (ctx->st == NULL) {
181       switch (ctx_err) {
182       case ST_CONTEXT_SUCCESS:
183 	 *error = __DRI_CTX_ERROR_SUCCESS;
184 	 break;
185       case ST_CONTEXT_ERROR_NO_MEMORY:
186 	 *error = __DRI_CTX_ERROR_NO_MEMORY;
187 	 break;
188       case ST_CONTEXT_ERROR_BAD_VERSION:
189 	 *error = __DRI_CTX_ERROR_BAD_VERSION;
190 	 break;
191       }
192       goto fail;
193    }
194    ctx->st->frontend_context = (void *) ctx;
195 
196    if (ctx->st->cso_context) {
197       ctx->pp = pp_init(ctx->st->pipe, screen->pp_enabled, ctx->st->cso_context,
198                         ctx->st, st_context_invalidate_state);
199       ctx->hud = hud_create(ctx->st->cso_context,
200                             share_ctx ? share_ctx->hud : NULL,
201                             ctx->st, st_context_invalidate_state);
202    }
203 
204    /* order of precedence (least to most):
205     * - driver setting
206     * - app setting
207     * - user setting
208     */
209    bool enable_glthread = driQueryOptionb(&screen->dev->option_cache, "mesa_glthread_driver");
210 
211    /* always disable glthread by default if fewer than 5 "big" CPUs are active */
212    unsigned nr_big_cpus = util_get_cpu_caps()->nr_big_cpus;
213    if (util_get_cpu_caps()->nr_cpus < 4 || (nr_big_cpus && nr_big_cpus < 5))
214       enable_glthread = false;
215 
216    int app_enable_glthread = driQueryOptioni(&screen->dev->option_cache, "mesa_glthread_app_profile");
217    if (app_enable_glthread != -1) {
218       /* if set (not -1), apply the app setting */
219       enable_glthread = app_enable_glthread == 1;
220    }
221    if (getenv("mesa_glthread")) {
222       /* only apply the env var if set */
223       bool user_enable_glthread = debug_get_bool_option("mesa_glthread", false);
224       if (user_enable_glthread != enable_glthread) {
225          /* print warning to mimic old behavior */
226          fprintf(stderr, "ATTENTION: default value of option mesa_glthread overridden by environment.\n");
227       }
228       enable_glthread = user_enable_glthread;
229    }
230    /* Do this last. */
231    if (enable_glthread) {
232       bool safe = true;
233 
234       /* This is only needed by X11/DRI2, which can be unsafe. */
235       if (backgroundCallable &&
236           backgroundCallable->base.version >= 2 &&
237           backgroundCallable->isThreadSafe &&
238           !backgroundCallable->isThreadSafe(loaderPrivate))
239          safe = false;
240 
241       if (safe)
242          _mesa_glthread_init(ctx->st->ctx);
243    }
244 
245    *error = __DRI_CTX_ERROR_SUCCESS;
246    return ctx;
247 
248  fail:
249    if (ctx && ctx->st)
250       st_destroy_context(ctx->st);
251 
252    free(ctx);
253    return NULL;
254 }
255 
256 void
dri_destroy_context(struct dri_context * ctx)257 dri_destroy_context(struct dri_context *ctx)
258 {
259    /* Wait for glthread to finish because we can't use pipe_context from
260     * multiple threads.
261     */
262    _mesa_glthread_finish(ctx->st->ctx);
263 
264    if (ctx->hud) {
265       hud_destroy(ctx->hud, ctx->st->cso_context);
266    }
267 
268    if (ctx->pp)
269       pp_free(ctx->pp);
270 
271    /* No particular reason to wait for command completion before
272     * destroying a context, but we flush the context here
273     * to avoid having to add code elsewhere to cope with flushing a
274     * partially destroyed context.
275     */
276    st_context_flush(ctx->st, 0, NULL, NULL, NULL);
277    st_destroy_context(ctx->st);
278    free(ctx);
279 }
280 
281 /* This is called inside MakeCurrent to unbind the context. */
282 bool
dri_unbind_context(struct dri_context * ctx)283 dri_unbind_context(struct dri_context *ctx)
284 {
285    /* dri_util.c ensures cPriv is not null */
286    struct st_context *st = ctx->st;
287 
288    if (st == st_api_get_current()) {
289       _mesa_glthread_finish(st->ctx);
290 
291       /* Record HUD queries for the duration the context was "current". */
292       if (ctx->hud)
293          hud_record_only(ctx->hud, st->pipe);
294 
295       st_api_make_current(NULL, NULL, NULL);
296    }
297 
298    if (ctx->draw || ctx->read) {
299       assert(ctx->draw);
300 
301       dri_put_drawable(ctx->draw);
302 
303       if (ctx->read != ctx->draw)
304           dri_put_drawable(ctx->read);
305 
306       ctx->draw = NULL;
307       ctx->read = NULL;
308    }
309 
310    return GL_TRUE;
311 }
312 
313 bool
dri_make_current(struct dri_context * ctx,struct dri_drawable * draw,struct dri_drawable * read)314 dri_make_current(struct dri_context *ctx,
315 		 struct dri_drawable *draw,
316 		 struct dri_drawable *read)
317 {
318    /* dri_unbind_context() is always called before this, so drawables are
319     * always NULL here.
320     */
321    assert(!ctx->draw);
322    assert(!ctx->read);
323 
324    if ((draw && !read) || (!draw && read))
325       return GL_FALSE; /* only both non-NULL or both NULL are allowed */
326 
327    /* Wait for glthread to finish because we can't use st_context from
328     * multiple threads.
329     */
330    _mesa_glthread_finish(ctx->st->ctx);
331 
332    /* There are 2 cases that can occur here. Either we bind drawables, or we
333     * bind NULL for configless and surfaceless contexts.
334     */
335    if (!draw && !read)
336       return st_api_make_current(ctx->st, NULL, NULL);
337 
338    /* Bind drawables to the context */
339    ctx->draw = draw;
340    ctx->read = read;
341 
342    dri_get_drawable(draw);
343    draw->texture_stamp = draw->lastStamp - 1;
344 
345    if (draw != read) {
346       dri_get_drawable(read);
347       read->texture_stamp = read->lastStamp - 1;
348    }
349 
350    st_api_make_current(ctx->st, &draw->base, &read->base);
351 
352    /* This is ok to call here. If they are already init, it's a no-op. */
353    if (ctx->pp && draw->textures[ST_ATTACHMENT_BACK_LEFT])
354       pp_init_fbos(ctx->pp, draw->textures[ST_ATTACHMENT_BACK_LEFT]->width0,
355                    draw->textures[ST_ATTACHMENT_BACK_LEFT]->height0);
356 
357    return GL_TRUE;
358 }
359 
360 struct dri_context *
dri_get_current(void)361 dri_get_current(void)
362 {
363    struct st_context *st = st_api_get_current();
364 
365    return (struct dri_context *) st ? st->frontend_context : NULL;
366 }
367 
368 /* vim: set sw=3 ts=8 sts=3 expandtab: */
369