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