473 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			473 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
| /*
 | |
|  * Copyright © 2004 David Reveman
 | |
|  * 
 | |
|  * Permission to use, copy, modify, distribute, and sell this software
 | |
|  * and its documentation for any purpose is hereby granted without
 | |
|  * fee, provided that the above copyright notice appear in all copies
 | |
|  * and that both that copyright notice and this permission notice
 | |
|  * appear in supporting documentation, and that the names of
 | |
|  * David Reveman not be used in advertising or publicity pertaining to
 | |
|  * distribution of the software without specific, written prior permission.
 | |
|  * David Reveman makes no representations about the suitability of this
 | |
|  * software for any purpose. It is provided "as is" without express or
 | |
|  * implied warranty.
 | |
|  *
 | |
|  * DAVID REVEMAN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 
 | |
|  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
 | |
|  * NO EVENT SHALL DAVID REVEMAN BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 | |
|  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
 | |
|  * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 
 | |
|  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 | |
|  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | |
|  *
 | |
|  * Author: David Reveman <davidr@freedesktop.org>
 | |
|  */
 | |
| 
 | |
| #include "xgl.h"
 | |
| 
 | |
| /*
 | |
|  * This offscreen memory manager is horrible and needs some serious work.
 | |
|  *
 | |
|  * It's a recursive memory manager. It's quite fast but wastes huge
 | |
|  * amounts of memory. A simple scoring mechanism is used and pixmaps
 | |
|  * that blit to screen get high scores which makes a compositing
 | |
|  * manager run fast.
 | |
|  *
 | |
|  * NOTE: With GL_ARB_uber_buffer or GL_EXT_render_target we probably
 | |
|  * wont need this offscreen management at all.
 | |
|  */
 | |
| 
 | |
| static glitz_drawable_buffer_t _buffers[] = {
 | |
|     GLITZ_DRAWABLE_BUFFER_BACK_COLOR,
 | |
|     GLITZ_DRAWABLE_BUFFER_FRONT_COLOR
 | |
| };
 | |
| 
 | |
| #define MAX_LEVEL 6
 | |
| 
 | |
| static Bool
 | |
| xglOffscreenMoveIn (xglOffscreenAreaPtr pArea,
 | |
| 		    PixmapPtr		pPixmap)
 | |
| {
 | |
|     XGL_PIXMAP_PRIV (pPixmap);
 | |
| 
 | |
|     if (!xglSyncSurface (&pPixmap->drawable))
 | |
| 	FatalError (XGL_SW_FAILURE_STRING);
 | |
| 
 | |
|     pArea->pPixmapPriv = pPixmapPriv;
 | |
|     pArea->state       = xglOffscreenAreaOccupied;
 | |
|     
 | |
|     pPixmapPriv->pArea  = pArea;
 | |
|     pPixmapPriv->target = xglPixmapTargetIn;
 | |
| 
 | |
|     glitz_surface_attach (pPixmapPriv->surface,
 | |
| 			  pArea->pOffscreen->drawable,
 | |
| 			  pArea->pOffscreen->buffer,
 | |
| 			  pArea->x, pArea->y);
 | |
| 
 | |
|     XGL_INCREMENT_PIXMAP_SCORE (pPixmapPriv, 500);
 | |
| 
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| static void
 | |
| xglOffscreenMoveOut (xglOffscreenAreaPtr pArea)
 | |
| {
 | |
|     glitz_surface_detach (pArea->pPixmapPriv->surface);
 | |
| 
 | |
|     pArea->pPixmapPriv->pArea  = NULL;
 | |
|     pArea->pPixmapPriv->target = xglPixmapTargetOut;
 | |
|     pArea->pPixmapPriv	       = NULL;
 | |
|     pArea->state	       = xglOffscreenAreaAvailable;
 | |
| }
 | |
| 
 | |
| static xglOffscreenAreaPtr
 | |
| xglCreateOffscreenArea (xglOffscreenPtr pOffscreen,
 | |
| 			int	  	level,
 | |
| 			int		x,
 | |
| 			int		y)
 | |
| {
 | |
|     xglOffscreenAreaPtr pArea;
 | |
|     int			i;
 | |
|     
 | |
|     pArea = xalloc (sizeof (xglOffscreenAreaRec));
 | |
|     if (!pArea)
 | |
| 	return NULL;
 | |
| 
 | |
|     pArea->level	= level;
 | |
|     pArea->x		= x;
 | |
|     pArea->y		= y;
 | |
|     pArea->pOffscreen	= pOffscreen;
 | |
|     pArea->pPixmapPriv	= NULL;
 | |
|     pArea->state	= xglOffscreenAreaAvailable;
 | |
|     
 | |
|     for (i = 0; i < 4; i++)
 | |
| 	pArea->pArea[i] = NULL;
 | |
|     
 | |
|     return pArea;
 | |
| }
 | |
| 
 | |
| static void
 | |
| xglDestroyOffscreenArea (xglOffscreenAreaPtr pArea)
 | |
| {   
 | |
|     if (!pArea)
 | |
| 	return;
 | |
| 
 | |
|     if (pArea->pPixmapPriv)
 | |
|     {
 | |
| 	xglOffscreenMoveOut (pArea);
 | |
|     }
 | |
|     else
 | |
|     {
 | |
| 	int i;
 | |
| 	
 | |
| 	for (i = 0; i < 4; i++)
 | |
| 	    xglDestroyOffscreenArea (pArea->pArea[i]);
 | |
|     }
 | |
|     
 | |
|     xfree (pArea);
 | |
| }
 | |
| 
 | |
| static Bool
 | |
| xglOffscreenInit (xglOffscreenPtr	  pOffscreen,
 | |
| 		  glitz_drawable_t	  *drawable,
 | |
| 		  glitz_drawable_buffer_t buffer,
 | |
| 		  unsigned int		  width,
 | |
| 		  unsigned int		  height)
 | |
| {
 | |
|     pOffscreen->pArea = xglCreateOffscreenArea (NULL, 0, 0, 0);
 | |
|     if (!pOffscreen->pArea)
 | |
| 	return FALSE;
 | |
| 
 | |
|     glitz_drawable_reference (drawable);
 | |
| 
 | |
|     pOffscreen->drawable = drawable;
 | |
|     pOffscreen->format   = glitz_drawable_get_format (drawable);
 | |
|     pOffscreen->buffer   = buffer;
 | |
|     pOffscreen->width    = width;
 | |
|     pOffscreen->height   = height;
 | |
|     
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| static void
 | |
| xglOffscreenFini (xglOffscreenPtr pOffscreen)
 | |
| {
 | |
|     xglDestroyOffscreenArea (pOffscreen->pArea);
 | |
|     glitz_drawable_destroy (pOffscreen->drawable);
 | |
| }
 | |
| 
 | |
| static int
 | |
| xglOffscreenAreaGetTopScore (xglOffscreenAreaPtr pArea)
 | |
| {
 | |
|     int topScore;
 | |
|     
 | |
|     if (pArea->pPixmapPriv)
 | |
|     {
 | |
| 	topScore = pArea->pPixmapPriv->score;
 | |
| 	XGL_DECREMENT_PIXMAP_SCORE (pArea->pPixmapPriv, 5);
 | |
| 	
 | |
| 	return topScore;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
| 	int topScore, score, i;
 | |
| 	
 | |
| 	topScore = 0;
 | |
| 	for (i = 0; i < 4; i++)
 | |
| 	{
 | |
| 	    if (pArea->pArea[i])
 | |
| 	    {
 | |
| 		score = xglOffscreenAreaGetTopScore (pArea->pArea[i]);
 | |
| 		if (score > topScore)
 | |
| 		    topScore = score;
 | |
| 	    }
 | |
| 	}
 | |
| 	return topScore;
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| static Bool
 | |
| xglOffscreenFindArea (xglOffscreenAreaPtr pArea,
 | |
| 		      PixmapPtr		  pPixmap,
 | |
| 		      int		  level)
 | |
| {
 | |
|     if (pArea->level > level)
 | |
| 	return FALSE;
 | |
| 	
 | |
|     switch (pArea->state) {
 | |
|     case xglOffscreenAreaOccupied:
 | |
|     {
 | |
| 	XGL_PIXMAP_PRIV (pPixmap);
 | |
| 	
 | |
| 	if (pPixmapPriv->score < pArea->pPixmapPriv->score)
 | |
| 	{
 | |
| 	    XGL_DECREMENT_PIXMAP_SCORE (pArea->pPixmapPriv, 10);
 | |
| 	    
 | |
| 	    return FALSE;
 | |
| 	}
 | |
| 	
 | |
| 	xglOffscreenMoveOut (pArea);
 | |
|     }
 | |
|     /* fall-through */
 | |
|     case xglOffscreenAreaAvailable:
 | |
|     {
 | |
| 	if (pArea->level == level || pArea->level == MAX_LEVEL)
 | |
| 	{
 | |
| 	    if (xglOffscreenMoveIn (pArea, pPixmap))
 | |
| 		return TRUE;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 	    int dx[4], dy[4], i;
 | |
| 	    
 | |
| 	    dx[0] = dx[2] = dy[0] = dy[1] = 0;
 | |
| 	    dx[1] = dx[3] = pArea->pOffscreen->width  >> (pArea->level + 1);
 | |
| 	    dy[2] = dy[3] = pArea->pOffscreen->height >> (pArea->level + 1);
 | |
| 	    
 | |
| 	    for (i = 0; i < 4; i++)
 | |
| 	    {
 | |
| 		pArea->pArea[i] =
 | |
| 		    xglCreateOffscreenArea (pArea->pOffscreen,
 | |
| 					    pArea->level + 1,
 | |
| 					    pArea->x + dx[i],
 | |
| 					    pArea->y + dy[i]);
 | |
| 	    }
 | |
| 
 | |
| 	    pArea->state = xglOffscreenAreaDivided;
 | |
| 	    
 | |
| 	    if (xglOffscreenFindArea (pArea->pArea[0], pPixmap, level))
 | |
| 		return TRUE;
 | |
| 	}
 | |
|     } break;
 | |
|     case xglOffscreenAreaDivided:
 | |
|     {
 | |
| 	int i;
 | |
| 	
 | |
| 	if (pArea->level == level)
 | |
| 	{
 | |
| 	    int topScore;
 | |
| 
 | |
| 	    XGL_PIXMAP_PRIV (pPixmap);
 | |
| 
 | |
| 	    topScore = xglOffscreenAreaGetTopScore (pArea);
 | |
| 	    
 | |
| 	    if (pPixmapPriv->score >= topScore)
 | |
| 	    {
 | |
| 		/*
 | |
| 		 * Kick out old pixmaps
 | |
| 		 */
 | |
| 		for (i = 0; i < 4; i++)
 | |
| 		{
 | |
| 		    xglDestroyOffscreenArea (pArea->pArea[i]);
 | |
| 		    pArea->pArea[i] = NULL;
 | |
| 		}
 | |
| 
 | |
| 		if (xglOffscreenMoveIn (pArea, pPixmap))
 | |
| 		    return TRUE;
 | |
| 	    }
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 	    for (i = 0; i < 4; i++)
 | |
| 	    {
 | |
| 		if (xglOffscreenFindArea (pArea->pArea[i], pPixmap, level))
 | |
| 		    return TRUE;
 | |
| 	    }
 | |
| 	}
 | |
|     } break;
 | |
|     }
 | |
| 
 | |
|     return FALSE;
 | |
| }
 | |
| 
 | |
| Bool
 | |
| xglInitOffscreen (ScreenPtr	   pScreen,
 | |
| 		  xglScreenInfoPtr pScreenInfo)
 | |
| {
 | |
|     xglOffscreenPtr	    pOffscreen;
 | |
|     int			nOffscreen;
 | |
|     glitz_drawable_format_t *format;
 | |
| 
 | |
|     XGL_SCREEN_PRIV (pScreen);
 | |
| 
 | |
|     pScreenPriv->pOffscreen = NULL;
 | |
|     pScreenPriv->nOffscreen = 0;
 | |
| 
 | |
|     format = glitz_drawable_get_format (pScreenPriv->drawable);
 | |
| 
 | |
|     /*
 | |
|      * Use back buffer as offscreen area.
 | |
|      */
 | |
|     if (format->doublebuffer)
 | |
|     {
 | |
| 	pScreenPriv->pOffscreen =
 | |
| 	    xrealloc (pScreenPriv->pOffscreen,
 | |
| 		      sizeof (xglOffscreenRec) *
 | |
| 		      (pScreenPriv->nOffscreen + 1));
 | |
| 	if (pScreenPriv->pOffscreen)
 | |
| 	{
 | |
| 	    pOffscreen = &pScreenPriv->pOffscreen[pScreenPriv->nOffscreen];
 | |
| 	    
 | |
| 	    if (xglOffscreenInit (pOffscreen,
 | |
| 				  pScreenPriv->drawable,
 | |
| 				  GLITZ_DRAWABLE_BUFFER_BACK_COLOR,
 | |
| 				  pScreenInfo->width, pScreenInfo->height))
 | |
| 	    {
 | |
| 		pScreenPriv->nOffscreen++;
 | |
| 		ErrorF ("Initialized %dx%d back buffer offscreen area\n",
 | |
| 			pScreenInfo->width, pScreenInfo->height);
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     if (nxglPbufferVisuals)
 | |
|     {
 | |
| 	glitz_pbuffer_attributes_t attributes;
 | |
| 	unsigned long		   mask;
 | |
| 	glitz_drawable_t           *pbuffer;
 | |
| 	int			   i;
 | |
| 
 | |
| 	for (i = 0; i < nxglPbufferVisuals; i++)
 | |
| 	{
 | |
| 	    /*
 | |
| 	     * This can be a bit tricky. I've noticed that when some OpenGL
 | |
| 	     * drivers can't create an accelerated pbuffer of the size we're
 | |
| 	     * requesting they create a software one with the correct
 | |
| 	     * size, but that's not what we want. So if your OpenGL driver
 | |
| 	     * supports accelerated pbuffers but offscreen drawing is really
 | |
| 	     * slow, try decrementing these values.
 | |
| 	     */
 | |
| 	    attributes.width  = 2048;
 | |
| 	    attributes.height = 2048;
 | |
| 	    
 | |
| 	    mask = GLITZ_PBUFFER_WIDTH_MASK | GLITZ_PBUFFER_HEIGHT_MASK;
 | |
| 
 | |
| 	    pbuffer =
 | |
| 		glitz_create_pbuffer_drawable (pScreenPriv->drawable,
 | |
| 					       xglPbufferVisuals[i].format,
 | |
| 					       &attributes, mask);
 | |
| 
 | |
| 	    if (pbuffer)
 | |
| 	    {
 | |
| 		unsigned long width, height;
 | |
| 		int	      j;
 | |
| 		
 | |
| 		width  = glitz_drawable_get_width (pbuffer);
 | |
| 		height = glitz_drawable_get_height (pbuffer);
 | |
| 		
 | |
| 		j = 0;
 | |
| 
 | |
| 		/*
 | |
| 		 * No back buffer? only add front buffer.
 | |
| 		 */
 | |
| 		if (!xglPbufferVisuals[i].format->doublebuffer)
 | |
| 		    j++;
 | |
| 		
 | |
| 		while (j < 2)
 | |
| 		{
 | |
| 		    pScreenPriv->pOffscreen =
 | |
| 			xrealloc (pScreenPriv->pOffscreen,
 | |
| 				  sizeof (xglOffscreenRec) *
 | |
| 				  (pScreenPriv->nOffscreen + 1));
 | |
| 		    if (pScreenPriv->pOffscreen)
 | |
| 		    {
 | |
| 			pOffscreen =
 | |
| 			    &pScreenPriv->pOffscreen[pScreenPriv->nOffscreen];
 | |
| 
 | |
| 			if (xglOffscreenInit (pOffscreen,
 | |
| 					      pbuffer, _buffers[j],
 | |
| 					      width, height))
 | |
| 			{
 | |
| 			    pScreenPriv->nOffscreen++;
 | |
| 			    ErrorF ("Initialized %dx%d pbuffer offscreen "
 | |
| 				    "area\n", width, height);
 | |
| 			}
 | |
| 		    }
 | |
| 		    j++;
 | |
| 		}
 | |
| 		glitz_drawable_destroy (pbuffer);
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     pOffscreen = pScreenPriv->pOffscreen;
 | |
|     nOffscreen = pScreenPriv->nOffscreen;
 | |
| 
 | |
|     /*
 | |
|      * Update offscreen pointers in root offscreen areas
 | |
|      */
 | |
|     while (nOffscreen--)
 | |
|     {
 | |
| 	pOffscreen->pArea->pOffscreen = pOffscreen;
 | |
| 	pOffscreen++;
 | |
|     }
 | |
|     
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| void
 | |
| xglFiniOffscreen (ScreenPtr pScreen)
 | |
| {
 | |
|     XGL_SCREEN_PRIV (pScreen);
 | |
| 	
 | |
|     while (pScreenPriv->nOffscreen--)
 | |
| 	xglOffscreenFini (&pScreenPriv->pOffscreen[pScreenPriv->nOffscreen]);
 | |
|     
 | |
|     if (pScreenPriv->pOffscreen)
 | |
| 	xfree (pScreenPriv->pOffscreen);
 | |
| }
 | |
| 
 | |
| Bool
 | |
| xglFindOffscreenArea (ScreenPtr pScreen,
 | |
| 		      PixmapPtr	pPixmap)
 | |
| {
 | |
|     xglOffscreenPtr	 pOffscreen;
 | |
|     int			 nOffscreen;
 | |
|     glitz_color_format_t *pColor;
 | |
|     
 | |
|     XGL_SCREEN_PRIV (pScreen);
 | |
|     XGL_PIXMAP_PRIV (pPixmap);
 | |
| 
 | |
|     if (pPixmapPriv->score < 0)
 | |
| 	return FALSE;
 | |
| 
 | |
|     pColor = &pPixmapPriv->format->color;
 | |
| 
 | |
|     pOffscreen = pScreenPriv->pOffscreen;
 | |
|     nOffscreen = pScreenPriv->nOffscreen;
 | |
| 
 | |
|     while (nOffscreen--)
 | |
|     {
 | |
| 	int level;
 | |
| 
 | |
| 	if (pOffscreen->format->color.red_size   >= pColor->red_size &&
 | |
| 	    pOffscreen->format->color.green_size >= pColor->green_size &&
 | |
| 	    pOffscreen->format->color.blue_size  >= pColor->blue_size &&
 | |
| 	    pOffscreen->format->color.alpha_size >= pColor->alpha_size)
 | |
| 	{
 | |
| 
 | |
| 	    level = 0;
 | |
| 	    while ((pOffscreen->width  >> level) >= pPixmap->drawable.width &&
 | |
| 		   (pOffscreen->height >> level) >= pPixmap->drawable.height)
 | |
| 		level++;
 | |
| 	    
 | |
| 	    if (!level)
 | |
| 		continue;
 | |
| 
 | |
| 	    if (xglOffscreenFindArea (pOffscreen->pArea, pPixmap, level - 1))
 | |
| 		return TRUE;
 | |
| 	}
 | |
| 	pOffscreen++;
 | |
|     }
 | |
| 
 | |
|     return FALSE;
 | |
| }
 | |
| 
 | |
| void
 | |
| xglWithdrawOffscreenArea (xglOffscreenAreaPtr pArea)
 | |
| {
 | |
|     pArea->pPixmapPriv = NULL;
 | |
|     pArea->state       = xglOffscreenAreaAvailable;
 | |
| }
 |