xserver-multidpi/hw/xgl/xgloffscreen.c
2004-11-05 13:26:07 +00:00

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