/** * \file dri_util.c * DRI utility functions. * * This module acts as glue between GLX and the actual hardware driver. A DRI * driver doesn't really \e have to use any of this - it's optional. But, some * useful stuff is done here that otherwise would have to be duplicated in most * drivers. * * Basically, these utility functions take care of some of the dirty details of * screen initialization, context creation, context binding, DRM setup, etc. * * These functions are compiled into each DRI driver so libGL.so knows nothing * about them. */ #include #include #include #include #include #ifndef MAP_FAILED #define MAP_FAILED ((void *)-1) #endif #include "main/imports.h" #define None 0 #include "dri_util.h" #include "drm_sarea.h" #include "utils.h" #include "xmlpool.h" #include "../glsl/glsl_parser_extras.h" PUBLIC const char __dri2ConfigOptions[] = DRI_CONF_BEGIN DRI_CONF_SECTION_PERFORMANCE DRI_CONF_VBLANK_MODE(DRI_CONF_VBLANK_DEF_INTERVAL_1) DRI_CONF_SECTION_END DRI_CONF_END; static const uint __dri2NConfigOptions = 1; #ifndef GLX_OML_sync_control typedef GLboolean ( * PFNGLXGETMSCRATEOMLPROC) (__DRIdrawable *drawable, int32_t *numerator, int32_t *denominator); #endif static void dri_get_drawable(__DRIdrawable *pdp); static void dri_put_drawable(__DRIdrawable *pdp); GLint driIntersectArea( drm_clip_rect_t rect1, drm_clip_rect_t rect2 ) { if (rect2.x1 > rect1.x1) rect1.x1 = rect2.x1; if (rect2.x2 < rect1.x2) rect1.x2 = rect2.x2; if (rect2.y1 > rect1.y1) rect1.y1 = rect2.y1; if (rect2.y2 < rect1.y2) rect1.y2 = rect2.y2; if (rect1.x1 > rect1.x2 || rect1.y1 > rect1.y2) return 0; return (rect1.x2 - rect1.x1) * (rect1.y2 - rect1.y1); } /*****************************************************************/ /** \name Context (un)binding functions */ /*****************************************************************/ /*@{*/ /** * Unbind context. * * \param scrn the screen. * \param gc context. * * \return \c GL_TRUE on success, or \c GL_FALSE on failure. * * \internal * This function calls __DriverAPIRec::UnbindContext, and then decrements * __DRIdrawableRec::refcount which must be non-zero for a successful * return. * * While casting the opaque private pointers associated with the parameters * into their respective real types it also assures they are not \c NULL. */ static int driUnbindContext(__DRIcontext *pcp) { __DRIscreen *psp; __DRIdrawable *pdp; __DRIdrawable *prp; /* ** Assume error checking is done properly in glXMakeCurrent before ** calling driUnbindContext. */ if (pcp == NULL) return GL_FALSE; psp = pcp->driScreenPriv; pdp = pcp->driDrawablePriv; prp = pcp->driReadablePriv; /* already unbound */ if (!pdp && !prp) return GL_TRUE; /* Let driver unbind drawable from context */ (*psp->DriverAPI.UnbindContext)(pcp); assert(pdp); if (pdp->refcount == 0) { /* ERROR!!! */ return GL_FALSE; } dri_put_drawable(pdp); if (prp != pdp) { if (prp->refcount == 0) { /* ERROR!!! */ return GL_FALSE; } dri_put_drawable(prp); } /* XXX this is disabled so that if we call SwapBuffers on an unbound * window we can determine the last context bound to the window and * use that context's lock. (BrianP, 2-Dec-2000) */ pcp->driDrawablePriv = pcp->driReadablePriv = NULL; return GL_TRUE; } /** * This function takes both a read buffer and a draw buffer. This is needed * for \c glXMakeCurrentReadSGI or GLX 1.3's \c glXMakeContextCurrent * function. */ static int driBindContext(__DRIcontext *pcp, __DRIdrawable *pdp, __DRIdrawable *prp) { __DRIscreen *psp = NULL; /* ** Assume error checking is done properly in glXMakeCurrent before ** calling driUnbindContext. */ if (!pcp) return GL_FALSE; /* Bind the drawable to the context */ psp = pcp->driScreenPriv; pcp->driDrawablePriv = pdp; pcp->driReadablePriv = prp; if (pdp) { pdp->driContextPriv = pcp; dri_get_drawable(pdp); } if (prp && pdp != prp) { dri_get_drawable(prp); } /* Call device-specific MakeCurrent */ return (*psp->DriverAPI.MakeCurrent)(pcp, pdp, prp); } static __DRIdrawable * dri2CreateNewDrawable(__DRIscreen *screen, const __DRIconfig *config, void *loaderPrivate) { __DRIdrawable *pdraw; pdraw = malloc(sizeof *pdraw); if (!pdraw) return NULL; pdraw->driContextPriv = NULL; pdraw->loaderPrivate = loaderPrivate; pdraw->hHWDrawable = 0; pdraw->refcount = 1; pdraw->pStamp = NULL; pdraw->lastStamp = 0; pdraw->index = 0; pdraw->x = 0; pdraw->y = 0; pdraw->w = 0; pdraw->h = 0; pdraw->numClipRects = 0; pdraw->numBackClipRects = 0; pdraw->pClipRects = NULL; pdraw->pBackClipRects = NULL; pdraw->vblSeq = 0; pdraw->vblFlags = 0; pdraw->driScreenPriv = screen; if (!(*screen->DriverAPI.CreateBuffer)(screen, pdraw, &config->modes, 0)) { free(pdraw); return NULL; } pdraw->msc_base = 0; /* This special default value is replaced with the configured * default value when the drawable is first bound to a direct * rendering context. */ pdraw->swap_interval = (unsigned)-1; pdraw->pClipRects = &pdraw->dri2.clipRect; pdraw->pBackClipRects = &pdraw->dri2.clipRect; pdraw->pStamp = &pdraw->dri2.stamp; *pdraw->pStamp = pdraw->lastStamp + 1; return pdraw; } static __DRIbuffer * dri2AllocateBuffer(__DRIscreen *screen, unsigned int attachment, unsigned int format, int width, int height) { return (*screen->DriverAPI.AllocateBuffer)(screen, attachment, format, width, height); } static void dri2ReleaseBuffer(__DRIscreen *screen, __DRIbuffer *buffer) { (*screen->DriverAPI.ReleaseBuffer)(screen, buffer); } static int dri2ConfigQueryb(__DRIscreen *screen, const char *var, GLboolean *val) { if (!driCheckOption(&screen->optionCache, var, DRI_BOOL)) return -1; *val = driQueryOptionb(&screen->optionCache, var); return 0; } static int dri2ConfigQueryi(__DRIscreen *screen, const char *var, GLint *val) { if (!driCheckOption(&screen->optionCache, var, DRI_INT) && !driCheckOption(&screen->optionCache, var, DRI_ENUM)) return -1; *val = driQueryOptioni(&screen->optionCache, var); return 0; } static int dri2ConfigQueryf(__DRIscreen *screen, const char *var, GLfloat *val) { if (!driCheckOption(&screen->optionCache, var, DRI_FLOAT)) return -1; *val = driQueryOptionf(&screen->optionCache, var); return 0; } static void dri_get_drawable(__DRIdrawable *pdp) { pdp->refcount++; } static void dri_put_drawable(__DRIdrawable *pdp) { __DRIscreen *psp; if (pdp) { pdp->refcount--; if (pdp->refcount) return; psp = pdp->driScreenPriv; (*psp->DriverAPI.DestroyBuffer)(pdp); if (pdp->pClipRects && pdp->pClipRects != &pdp->dri2.clipRect) { free(pdp->pClipRects); pdp->pClipRects = NULL; } if (pdp->pBackClipRects && pdp->pClipRects != &pdp->dri2.clipRect) { free(pdp->pBackClipRects); pdp->pBackClipRects = NULL; } free(pdp); } } static void driDestroyDrawable(__DRIdrawable *pdp) { dri_put_drawable(pdp); } /*@}*/ /*****************************************************************/ /** \name Context handling functions */ /*****************************************************************/ /*@{*/ /** * Destroy the per-context private information. * * \internal * This function calls __DriverAPIRec::DestroyContext on \p contextPrivate, calls * drmDestroyContext(), and finally frees \p contextPrivate. */ static void driDestroyContext(__DRIcontext *pcp) { if (pcp) { (*pcp->driScreenPriv->DriverAPI.DestroyContext)(pcp); free(pcp); } } static unsigned int dri2GetAPIMask(__DRIscreen *screen) { return screen->api_mask; } static __DRIcontext * dri2CreateNewContextForAPI(__DRIscreen *screen, int api, const __DRIconfig *config, __DRIcontext *shared, void *data) { __DRIcontext *context; const struct gl_config *modes = (config != NULL) ? &config->modes : NULL; void *shareCtx = (shared != NULL) ? shared->driverPrivate : NULL; gl_api mesa_api; if (!(screen->api_mask & (1 << api))) return NULL; switch (api) { case __DRI_API_OPENGL: mesa_api = API_OPENGL; break; case __DRI_API_GLES: mesa_api = API_OPENGLES; break; case __DRI_API_GLES2: mesa_api = API_OPENGLES2; break; default: return NULL; } context = malloc(sizeof *context); if (!context) return NULL; context->driScreenPriv = screen; context->driDrawablePriv = NULL; context->loaderPrivate = data; if (!(*screen->DriverAPI.CreateContext)(mesa_api, modes, context, shareCtx) ) { free(context); return NULL; } return context; } static __DRIcontext * dri2CreateNewContext(__DRIscreen *screen, const __DRIconfig *config, __DRIcontext *shared, void *data) { return dri2CreateNewContextForAPI(screen, __DRI_API_OPENGL, config, shared, data); } static int driCopyContext(__DRIcontext *dest, __DRIcontext *src, unsigned long mask) { (void) dest; (void) src; (void) mask; return GL_FALSE; } /*@}*/ /*****************************************************************/ /** \name Screen handling functions */ /*****************************************************************/ /*@{*/ /** * Destroy the per-screen private information. * * \internal * This function calls __DriverAPIRec::DestroyScreen on \p screenPrivate, calls * drmClose(), and finally frees \p screenPrivate. */ static void driDestroyScreen(__DRIscreen *psp) { if (psp) { /* No interaction with the X-server is possible at this point. This * routine is called after XCloseDisplay, so there is no protocol * stream open to the X-server anymore. */ _mesa_destroy_shader_compiler(); if (psp->DriverAPI.DestroyScreen) (*psp->DriverAPI.DestroyScreen)(psp); if (!psp->dri2.enabled) { (void)drmUnmap((drmAddress)psp->pSAREA, SAREA_MAX); (void)drmUnmap((drmAddress)psp->pFB, psp->fbSize); (void)drmCloseOnce(psp->fd); } else { driDestroyOptionCache(&psp->optionCache); driDestroyOptionInfo(&psp->optionInfo); } free(psp); } } static void setupLoaderExtensions(__DRIscreen *psp, const __DRIextension **extensions) { int i; for (i = 0; extensions[i]; i++) { if (strcmp(extensions[i]->name, __DRI_GET_DRAWABLE_INFO) == 0) psp->getDrawableInfo = (__DRIgetDrawableInfoExtension *) extensions[i]; if (strcmp(extensions[i]->name, __DRI_DAMAGE) == 0) psp->damage = (__DRIdamageExtension *) extensions[i]; if (strcmp(extensions[i]->name, __DRI_SYSTEM_TIME) == 0) psp->systemTime = (__DRIsystemTimeExtension *) extensions[i]; if (strcmp(extensions[i]->name, __DRI_DRI2_LOADER) == 0) psp->dri2.loader = (__DRIdri2LoaderExtension *) extensions[i]; if (strcmp(extensions[i]->name, __DRI_IMAGE_LOOKUP) == 0) psp->dri2.image = (__DRIimageLookupExtension *) extensions[i]; if (strcmp(extensions[i]->name, __DRI_USE_INVALIDATE) == 0) psp->dri2.useInvalidate = (__DRIuseInvalidateExtension *) extensions[i]; } } /** * DRI2 */ static __DRIscreen * dri2CreateNewScreen(int scrn, int fd, const __DRIextension **extensions, const __DRIconfig ***driver_configs, void *data) { static const __DRIextension *emptyExtensionList[] = { NULL }; __DRIscreen *psp; drmVersionPtr version; if (driDriverAPI.InitScreen2 == NULL) return NULL; psp = calloc(1, sizeof(*psp)); if (!psp) return NULL; setupLoaderExtensions(psp, extensions); version = drmGetVersion(fd); if (version) { psp->drm_version.major = version->version_major; psp->drm_version.minor = version->version_minor; psp->drm_version.patch = version->version_patchlevel; drmFreeVersion(version); } psp->extensions = emptyExtensionList; psp->fd = fd; psp->myNum = scrn; psp->dri2.enabled = GL_TRUE; psp->DriverAPI = driDriverAPI; psp->api_mask = (1 << __DRI_API_OPENGL); *driver_configs = driDriverAPI.InitScreen2(psp); if (*driver_configs == NULL) { free(psp); return NULL; } psp->DriverAPI = driDriverAPI; psp->loaderPrivate = data; driParseOptionInfo(&psp->optionInfo, __dri2ConfigOptions, __dri2NConfigOptions); driParseConfigFiles(&psp->optionCache, &psp->optionInfo, psp->myNum, "dri2"); return psp; } static const __DRIextension **driGetExtensions(__DRIscreen *psp) { return psp->extensions; } /** Core interface */ const __DRIcoreExtension driCoreExtension = { { __DRI_CORE, __DRI_CORE_VERSION }, NULL, driDestroyScreen, driGetExtensions, driGetConfigAttrib, driIndexConfigAttrib, NULL, driDestroyDrawable, NULL, NULL, driCopyContext, driDestroyContext, driBindContext, driUnbindContext }; /** DRI2 interface */ const __DRIdri2Extension driDRI2Extension = { { __DRI_DRI2, __DRI_DRI2_VERSION }, dri2CreateNewScreen, dri2CreateNewDrawable, dri2CreateNewContext, dri2GetAPIMask, dri2CreateNewContextForAPI, dri2AllocateBuffer, dri2ReleaseBuffer }; const __DRI2configQueryExtension dri2ConfigQueryExtension = { { __DRI2_CONFIG_QUERY, __DRI2_CONFIG_QUERY_VERSION }, dri2ConfigQueryb, dri2ConfigQueryi, dri2ConfigQueryf, }; /** * Calculate amount of swap interval used between GLX buffer swaps. * * The usage value, on the range [0,max], is the fraction of total swap * interval time used between GLX buffer swaps is calculated. * * \f$p = t_d / (i * t_r)\f$ * * Where \f$t_d\f$ is the time since the last GLX buffer swap, \f$i\f$ is the * swap interval (as set by \c glXSwapIntervalSGI), and \f$t_r\f$ time * required for a single vertical refresh period (as returned by \c * glXGetMscRateOML). * * See the documentation for the GLX_MESA_swap_frame_usage extension for more * details. * * \param dPriv Pointer to the private drawable structure. * \return If less than a single swap interval time period was required * between GLX buffer swaps, a number greater than 0 and less than * 1.0 is returned. If exactly one swap interval time period is * required, 1.0 is returned, and if more than one is required then * a number greater than 1.0 will be returned. * * \sa glXSwapIntervalSGI glXGetMscRateOML * * \todo Instead of caching the \c glXGetMscRateOML function pointer, would it * be possible to cache the sync rate? */ float driCalculateSwapUsage( __DRIdrawable *dPriv, int64_t last_swap_ust, int64_t current_ust ) { int32_t n; int32_t d; int interval; float usage = 1.0; __DRIscreen *psp = dPriv->driScreenPriv; if ( (*psp->systemTime->getMSCRate)(dPriv, &n, &d, dPriv->loaderPrivate) ) { interval = (dPriv->swap_interval != 0) ? dPriv->swap_interval : 1; /* We want to calculate * (current_UST - last_swap_UST) / (interval * us_per_refresh). We get * current_UST by calling __glXGetUST. last_swap_UST is stored in * dPriv->swap_ust. interval has already been calculated. * * The only tricky part is us_per_refresh. us_per_refresh is * 1000000 / MSC_rate. We know the MSC_rate is n / d. We can flip it * around and say us_per_refresh = 1000000 * d / n. Since this goes in * the denominator of the final calculation, we calculate * (interval * 1000000 * d) and move n into the numerator. */ usage = (current_ust - last_swap_ust); usage *= n; usage /= (interval * d); usage /= 1000000.0; } return usage; } void dri2InvalidateDrawable(__DRIdrawable *drawable) { drawable->dri2.stamp++; } /*@}*/