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_memory.h"
42#include "util/debug.h"
43
44GLboolean
45dri_create_context(gl_api api, const struct gl_config * visual,
46                   __DRIcontext * cPriv,
47                   const struct __DriverContextConfig *ctx_config,
48                   unsigned *error,
49                   void *sharedContextPrivate)
50{
51   __DRIscreen *sPriv = cPriv->driScreenPriv;
52   struct dri_screen *screen = dri_screen(sPriv);
53   struct st_api *stapi = screen->st_api;
54   struct dri_context *ctx = NULL;
55   struct st_context_iface *st_share = NULL;
56   struct st_context_attribs attribs;
57   enum st_context_error ctx_err = 0;
58   unsigned allowed_flags = __DRI_CTX_FLAG_DEBUG |
59                            __DRI_CTX_FLAG_FORWARD_COMPATIBLE;
60   unsigned allowed_attribs =
61      __DRIVER_CONTEXT_ATTRIB_PRIORITY |
62      __DRIVER_CONTEXT_ATTRIB_RELEASE_BEHAVIOR |
63      __DRIVER_CONTEXT_ATTRIB_NO_ERROR;
64   const __DRIbackgroundCallableExtension *backgroundCallable =
65      screen->sPriv->dri2.backgroundCallable;
66   const struct driOptionCache *optionCache = &screen->dev->option_cache;
67
68   if (screen->has_reset_status_query) {
69      allowed_flags |= __DRI_CTX_FLAG_ROBUST_BUFFER_ACCESS;
70      allowed_attribs |= __DRIVER_CONTEXT_ATTRIB_RESET_STRATEGY;
71   }
72
73   if (ctx_config->flags & ~allowed_flags) {
74      *error = __DRI_CTX_ERROR_UNKNOWN_FLAG;
75      goto fail;
76   }
77
78   if (ctx_config->attribute_mask & ~allowed_attribs) {
79      *error = __DRI_CTX_ERROR_UNKNOWN_ATTRIBUTE;
80      goto fail;
81   }
82
83   memset(&attribs, 0, sizeof(attribs));
84   switch (api) {
85   case API_OPENGLES:
86      attribs.profile = ST_PROFILE_OPENGL_ES1;
87      break;
88   case API_OPENGLES2:
89      attribs.profile = ST_PROFILE_OPENGL_ES2;
90      break;
91   case API_OPENGL_COMPAT:
92   case API_OPENGL_CORE:
93      if (driQueryOptionb(optionCache, "force_compat_profile")) {
94         attribs.profile = ST_PROFILE_DEFAULT;
95      } else {
96         attribs.profile = api == API_OPENGL_COMPAT ? ST_PROFILE_DEFAULT
97                                                    : ST_PROFILE_OPENGL_CORE;
98      }
99
100      attribs.major = ctx_config->major_version;
101      attribs.minor = ctx_config->minor_version;
102
103      if ((ctx_config->flags & __DRI_CTX_FLAG_FORWARD_COMPATIBLE) != 0)
104	 attribs.flags |= ST_CONTEXT_FLAG_FORWARD_COMPATIBLE;
105      break;
106   default:
107      *error = __DRI_CTX_ERROR_BAD_API;
108      goto fail;
109   }
110
111   if ((ctx_config->flags & __DRI_CTX_FLAG_DEBUG) != 0)
112      attribs.flags |= ST_CONTEXT_FLAG_DEBUG;
113
114   if (ctx_config->flags & __DRI_CTX_FLAG_ROBUST_BUFFER_ACCESS)
115      attribs.flags |= ST_CONTEXT_FLAG_ROBUST_ACCESS;
116
117   if (ctx_config->attribute_mask & __DRIVER_CONTEXT_ATTRIB_RESET_STRATEGY)
118      if (ctx_config->reset_strategy != __DRI_CTX_RESET_NO_NOTIFICATION)
119         attribs.flags |= ST_CONTEXT_FLAG_RESET_NOTIFICATION_ENABLED;
120
121   if (ctx_config->attribute_mask & __DRIVER_CONTEXT_ATTRIB_NO_ERROR)
122      attribs.flags |= ctx_config->no_error ? ST_CONTEXT_FLAG_NO_ERROR : 0;
123
124   if (ctx_config->attribute_mask & __DRIVER_CONTEXT_ATTRIB_PRIORITY) {
125      switch (ctx_config->priority) {
126      case __DRI_CTX_PRIORITY_LOW:
127         attribs.flags |= ST_CONTEXT_FLAG_LOW_PRIORITY;
128         break;
129      case __DRI_CTX_PRIORITY_HIGH:
130         attribs.flags |= ST_CONTEXT_FLAG_HIGH_PRIORITY;
131         break;
132      default:
133         break;
134      }
135   }
136
137   if ((ctx_config->attribute_mask & __DRIVER_CONTEXT_ATTRIB_RELEASE_BEHAVIOR)
138       && (ctx_config->release_behavior == __DRI_CTX_RELEASE_BEHAVIOR_NONE))
139      attribs.flags |= ST_CONTEXT_FLAG_RELEASE_NONE;
140
141   struct dri_context *share_ctx = NULL;
142   if (sharedContextPrivate) {
143      share_ctx = (struct dri_context *)sharedContextPrivate;
144      st_share = share_ctx->st;
145   }
146
147   ctx = CALLOC_STRUCT(dri_context);
148   if (ctx == NULL) {
149      *error = __DRI_CTX_ERROR_NO_MEMORY;
150      goto fail;
151   }
152
153   cPriv->driverPrivate = ctx;
154   ctx->cPriv = cPriv;
155   ctx->sPriv = sPriv;
156
157   /* KHR_no_error is likely to crash, overflow memory, etc if an application
158    * has errors so don't enable it for setuid processes.
159    */
160   if (env_var_as_boolean("MESA_NO_ERROR", false) ||
161       driQueryOptionb(&screen->dev->option_cache, "mesa_no_error"))
162#if !defined(_WIN32)
163      if (geteuid() == getuid())
164#endif
165         attribs.flags |= ST_CONTEXT_FLAG_NO_ERROR;
166
167   attribs.options = screen->options;
168   dri_fill_st_visual(&attribs.visual, screen, visual);
169   ctx->st = stapi->create_context(stapi, &screen->base, &attribs, &ctx_err,
170				   st_share);
171   if (ctx->st == NULL) {
172      switch (ctx_err) {
173      case ST_CONTEXT_SUCCESS:
174	 *error = __DRI_CTX_ERROR_SUCCESS;
175	 break;
176      case ST_CONTEXT_ERROR_NO_MEMORY:
177	 *error = __DRI_CTX_ERROR_NO_MEMORY;
178	 break;
179      case ST_CONTEXT_ERROR_BAD_API:
180	 *error = __DRI_CTX_ERROR_BAD_API;
181	 break;
182      case ST_CONTEXT_ERROR_BAD_VERSION:
183	 *error = __DRI_CTX_ERROR_BAD_VERSION;
184	 break;
185      case ST_CONTEXT_ERROR_BAD_FLAG:
186	 *error = __DRI_CTX_ERROR_BAD_FLAG;
187	 break;
188      case ST_CONTEXT_ERROR_UNKNOWN_ATTRIBUTE:
189	 *error = __DRI_CTX_ERROR_UNKNOWN_ATTRIBUTE;
190	 break;
191      case ST_CONTEXT_ERROR_UNKNOWN_FLAG:
192	 *error = __DRI_CTX_ERROR_UNKNOWN_FLAG;
193	 break;
194      }
195      goto fail;
196   }
197   ctx->st->st_manager_private = (void *) ctx;
198   ctx->stapi = stapi;
199
200   if (ctx->st->cso_context) {
201      ctx->pp = pp_init(ctx->st->pipe, screen->pp_enabled, ctx->st->cso_context,
202                        ctx->st);
203      ctx->hud = hud_create(ctx->st->cso_context, ctx->st,
204                            share_ctx ? share_ctx->hud : NULL);
205   }
206
207   /* Do this last. */
208   if (ctx->st->start_thread &&
209         driQueryOptionb(&screen->dev->option_cache, "mesa_glthread")) {
210
211      if (backgroundCallable && backgroundCallable->base.version >= 2 &&
212            backgroundCallable->isThreadSafe) {
213
214         if (backgroundCallable->isThreadSafe(cPriv->loaderPrivate))
215            ctx->st->start_thread(ctx->st);
216         else
217            fprintf(stderr, "dri_create_context: glthread isn't thread safe "
218                  "- missing call XInitThreads\n");
219      } else {
220         fprintf(stderr, "dri_create_context: requested glthread but driver "
221               "is missing backgroundCallable V2 extension\n");
222      }
223   }
224
225   *error = __DRI_CTX_ERROR_SUCCESS;
226   return GL_TRUE;
227
228 fail:
229   if (ctx && ctx->st)
230      ctx->st->destroy(ctx->st);
231
232   free(ctx);
233   return GL_FALSE;
234}
235
236void
237dri_destroy_context(__DRIcontext * cPriv)
238{
239   struct dri_context *ctx = dri_context(cPriv);
240
241   if (ctx->hud) {
242      hud_destroy(ctx->hud, ctx->st->cso_context);
243   }
244
245   if (ctx->pp)
246      pp_free(ctx->pp);
247
248   /* No particular reason to wait for command completion before
249    * destroying a context, but we flush the context here
250    * to avoid having to add code elsewhere to cope with flushing a
251    * partially destroyed context.
252    */
253   ctx->st->flush(ctx->st, 0, NULL, NULL, NULL);
254   ctx->st->destroy(ctx->st);
255   free(ctx);
256}
257
258/* This is called inside MakeCurrent to unbind the context. */
259GLboolean
260dri_unbind_context(__DRIcontext * cPriv)
261{
262   /* dri_util.c ensures cPriv is not null */
263   struct dri_screen *screen = dri_screen(cPriv->driScreenPriv);
264   struct dri_context *ctx = dri_context(cPriv);
265   struct st_context_iface *st = ctx->st;
266   struct st_api *stapi = screen->st_api;
267
268   if (st == stapi->get_current(stapi)) {
269      if (st->thread_finish)
270         st->thread_finish(st);
271
272      /* Record HUD queries for the duration the context was "current". */
273      if (ctx->hud)
274         hud_record_only(ctx->hud, st->pipe);
275
276      stapi->make_current(stapi, NULL, NULL, NULL);
277   }
278   ctx->dPriv = NULL;
279   ctx->rPriv = NULL;
280
281   return GL_TRUE;
282}
283
284GLboolean
285dri_make_current(__DRIcontext * cPriv,
286		 __DRIdrawable * driDrawPriv,
287		 __DRIdrawable * driReadPriv)
288{
289   /* dri_util.c ensures cPriv is not null */
290   struct dri_context *ctx = dri_context(cPriv);
291   struct dri_drawable *draw = dri_drawable(driDrawPriv);
292   struct dri_drawable *read = dri_drawable(driReadPriv);
293
294   if (!draw && !read)
295      return ctx->stapi->make_current(ctx->stapi, ctx->st, NULL, NULL);
296   else if (!draw || !read)
297      return GL_FALSE;
298
299   if (ctx->dPriv != driDrawPriv) {
300      ctx->dPriv = driDrawPriv;
301      draw->texture_stamp = driDrawPriv->lastStamp - 1;
302   }
303   if (ctx->rPriv != driReadPriv) {
304      ctx->rPriv = driReadPriv;
305      read->texture_stamp = driReadPriv->lastStamp - 1;
306   }
307
308   ctx->stapi->make_current(ctx->stapi, ctx->st, &draw->base, &read->base);
309
310   /* This is ok to call here. If they are already init, it's a no-op. */
311   if (ctx->pp && draw->textures[ST_ATTACHMENT_BACK_LEFT])
312      pp_init_fbos(ctx->pp, draw->textures[ST_ATTACHMENT_BACK_LEFT]->width0,
313                   draw->textures[ST_ATTACHMENT_BACK_LEFT]->height0);
314
315   return GL_TRUE;
316}
317
318struct dri_context *
319dri_get_current(__DRIscreen *sPriv)
320{
321   struct dri_screen *screen = dri_screen(sPriv);
322   struct st_api *stapi = screen->st_api;
323   struct st_context_iface *st;
324
325   st = stapi->get_current(stapi);
326
327   return (struct dri_context *) st ? st->st_manager_private : NULL;
328}
329
330/* vim: set sw=3 ts=8 sts=3 expandtab: */
331