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;
|
|
}
|