Add another layer on top of the simple 2d regions in intel_regions.[ch]

which keeps track of a whole, well-defined mipmap tree.  These are a
fixed layout on intel hardware and managing them is complicated in the
face of GL's TexImage function calls where data can arrive in any order,
making it difficult to guess a layout ahead of time.

Wrapping mipmap trees up in a struct and programming interface like this
reduces the burden elsewhere.
This commit is contained in:
Keith Whitwell 2006-01-24 16:38:43 +00:00
parent d4d7fdb43b
commit 3bca9c47f4
6 changed files with 488 additions and 98 deletions

View file

@ -10,6 +10,7 @@
/* The buffer manager context. Opaque.
*/
struct bufmgr;
struct bm_buffer_list;
struct bufmgr *bm_fake_intel_Attach( struct intel_context *intel );
@ -23,18 +24,13 @@ void bmInitPool( struct bufmgr *,
unsigned long high_offset,
void *virtual_base );
#define BM_MEM_SYS 0x1
#define BM_MEM_LOCAL 0x1
#define BM_MEM_AGP 0x2
#define BM_MEM_VRAM 0x4 /* not used */
#define BM_WRITE 0x100 /* not used */
#define BM_READ 0x200 /* not used */
/* Maximum number of buffers to pass to bmValidateBufferList:
*/
#define BM_VALIDATE_MAX 32
/* Flags for validate. If both NO_UPLOAD and NO_EVICT are specified,
* ValidateBuffers is essentially a query.
@ -76,34 +72,39 @@ void bmUnmapBuffer( struct bufmgr *,
/* To be called prior to emitting commands to hardware which reference
* these buffers.
*
* ClearBufferList() and AddBuffer() build up a list of buffers to be
* NewBufferList() and AddBuffer() build up a list of buffers to be
* validated. The buffer list provides information on where the
* buffers should be placed and whether their contents need to be
* preserved on copying. The offset data elements are return values
* from this function telling the driver exactly where the buffers are
* currently located.
*
* ValidateBuffers() performs the actual validation.
* ValidateBufferList() performs the actual validation and returns the
* buffer pools and offsets within the pools.
*
* ReleaseValidatedBuffers() must be called to set fences and other
* FenceBufferList() must be called to set fences and other
* housekeeping before unlocking after a successful call to
* ValidateBuffers().
*
* The buffer manager knows how to emit and test fences directly
* through the drm and without callbacks or whatever into the driver.
* ValidateBufferList(). The buffer manager knows how to emit and test
* fences directly through the drm and without callbacks to the
* driver.
*/
void bmClearBufferList( struct bufmgr * );
struct bm_buffer_list *bmNewBufferList( void );
void bmAddBuffer( struct bufmgr *bm,
void bmAddBuffer( struct bufmgr *,
struct bm_buffer_list *list,
unsigned buffer,
unsigned flags,
unsigned *pool_return,
unsigned *offset_return );
int bmValidateBufferList( struct bufmgr *,
struct bm_buffer_list *,
unsigned flags );
void bmReleaseValidatedBuffers( struct bufmgr * );
void bmFenceBufferList( struct bufmgr *,
struct bm_buffer_list * );
void bmFreeBufferList( struct bm_buffer_list * );
/* This functionality is used by the buffer manager, not really sure

View file

@ -18,8 +18,11 @@
struct _mesa_HashTable;
#define BM_MEM_LOCAL 0x1
#define BM_MEM_AGP 0x2
/* Maximum number of buffers to pass to bmValidateBufferList:
*/
#define BM_LIST_MAX 32
/* Wrapper around mm.c's mem_block, which understands that you must
* wait for fences to expire before memory can be freed. This is
@ -53,6 +56,16 @@ struct pool {
struct block freed;
};
/* List of buffers to validate:
*/
struct bm_buffer_list {
struct buffer *buffer[BM_LIST_MAX];
unsigned *offset_return[BM_LIST_MAX];
unsigned nr;
unsigned need_fence;
};
struct bufmgr {
struct intel_context *intel;
struct buffer buffer_list;
@ -61,14 +74,7 @@ struct bufmgr {
unsigned buf_nr; /* for generating ids */
/* List of buffers to validate:
*/
struct buffer *validated[BM_VALIDATE_MAX];
unsigned *offset_return[BM_VALIDATE_MAX];
unsigned nr_validated;
unsigned last_fence;
unsigned in_progress;
};
@ -194,7 +200,7 @@ static int move_buffers( struct bufmgr *bm,
int newMemType,
int flags )
{
struct block *newMem[BM_VALIDATE_MAX];
struct block *newMem[BM_LIST_MAX];
GLint i;
memset(newMem, 0, sizeof(newMem));
@ -476,28 +482,34 @@ void bm_fake_SetFixedBufferParams( struct bufmgr *bm
/* Build the list of buffers to validate:
*/
void bmClearBufferList( struct bufmgr *bm )
struct bm_buffer_list *bmNewBufferList( void )
{
assert(!bm->in_progress);
bm->nr_validated = 0;
struct bm_buffer_list *list = calloc(sizeof(*list), 1);
return list;
}
void bmAddBuffer( struct bufmgr *bm,
struct bm_buffer_list *list,
unsigned buffer,
unsigned flags,
unsigned *pool_return,
unsigned *offset_return )
{
assert(bm->nr_validated < BM_VALIDATE_MAX);
assert(list->nr < BM_LIST_MAX);
bm->validated[bm->nr_validated] = _mesa_HashLookup(bm->hash, buffer);
bm->offset_return[bm->nr_validated] = offset_return;
bm->nr_validated++;
list->buffer[list->nr] = _mesa_HashLookup(bm->hash, buffer);
list->offset_return[list->nr] = offset_return;
list->nr++;
if (pool_return)
*pool_return = 0;
}
void bmFreeBufferList( struct bm_buffer_list *list )
{
assert(!list->need_fence);
free(list);
}
@ -510,17 +522,18 @@ void bmAddBuffer( struct bufmgr *bm,
* buffers are currently located.
*/
int bmValidateBufferList( struct bufmgr *bm,
struct bm_buffer_list *list,
unsigned flags )
{
unsigned i;
unsigned total = 0;
if (bm->nr_validated > BM_VALIDATE_MAX)
if (list->nr > BM_LIST_MAX)
return 0;
for (i = 0; i < bm->nr_validated; i++) {
assert(!bm->validated[i]->mapped);
total += bm->validated[i]->size;
for (i = 0; i < list->nr; i++) {
assert(!list->buffer[i]->mapped);
total += list->buffer[i]->size;
}
/* Don't need to try allocation in this case:
@ -532,13 +545,13 @@ int bmValidateBufferList( struct bufmgr *bm,
* succeeds. This is a pretty poor strategy but really hard to do
* better without more infrastucture... Which is coming - hooray!
*/
while (!move_buffers(bm, bm->validated, bm->nr_validated, BM_MEM_AGP, flags)) {
while (!move_buffers(bm, list->buffer, list->nr, BM_MEM_AGP, flags)) {
if ((flags & BM_NO_EVICT) ||
!evict_lru(bm))
return 0;
}
bm->in_progress = 1;
list->need_fence = 1;
return 1;
}
@ -551,20 +564,21 @@ int bmValidateBufferList( struct bufmgr *bm,
* The buffer manager knows how to emit and test fences directly
* through the drm and without callbacks or whatever into the driver.
*/
void bmReleaseValidatedBuffers( struct bufmgr *bm )
void bmFenceBufferList( struct bufmgr *bm, struct bm_buffer_list *list )
{
unsigned i;
assert(bm->in_progress);
bm->in_progress = 0;
assert(list->need_fence);
list->need_fence = 0;
bm->last_fence = bmSetFence( bm );
/* Move all buffers to head of resident list and set their fences
*/
for (i = 0; i < bm->nr_validated; i++) {
assert(bm->validated[i]->block->buf == bm->validated[i]);
move_to_head(&bm->pool.lru, bm->validated[i]->block);
bm->validated[i]->block->fence = bm->last_fence;
for (i = 0; i < list->nr; i++) {
assert(list->buffer[i]->block->buf == list->buffer[i]);
move_to_head(&bm->pool.lru, list->buffer[i]->block);
list->buffer[i]->block->fence = bm->last_fence;
}
}

View file

@ -0,0 +1,218 @@
/**************************************************************************
*
* Copyright 2006 Tungsten Graphics, Inc., Cedar Park, Texas.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sub license, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
**************************************************************************/
#include "intel_context.h"
#include "intel_mipmap_tree.h"
#include "intel_regions.h"
#include "bufmgr.h"
static GLenum target_to_target( GLenum target )
{
switch (target) {
case GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB:
case GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB:
case GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB:
case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB:
case GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB:
case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB:
return GL_TEXTURE_CUBE_MAP_ARB;
default:
return target;
}
}
struct intel_mipmap_tree *intel_miptree_create( struct intel_context *intel,
GLenum target,
GLenum internal_format,
GLuint first_level,
GLuint last_level,
GLuint width0,
GLuint height0,
GLuint depth0,
GLuint cpp,
GLboolean compressed)
{
GLboolean ok;
struct intel_mipmap_tree *mt = calloc(sizeof(*mt), 1);
mt->target = target_to_target(target);
mt->internal_format = internal_format;
mt->first_level = first_level;
mt->last_level = last_level;
mt->width0 = width0;
mt->height0 = height0;
mt->depth0 = depth0;
mt->cpp = cpp;
mt->compressed = compressed;
switch (intel->intelScreen->deviceID) {
case PCI_CHIP_I945_G:
ok = i945_miptree_layout( mt );
break;
case PCI_CHIP_I915_G:
case PCI_CHIP_I915_GM:
ok = i915_miptree_layout( mt );
break;
default:
ok = 0;
break;
}
if (ok)
mt->region = intel_region_alloc( intel,
mt->cpp,
mt->pitch,
mt->total_height );
if (!mt->region) {
free(mt);
return NULL;
}
return mt;
}
struct intel_mipmap_tree *intel_miptree_reference( struct intel_mipmap_tree *mt )
{
mt->refcount++;
return mt;
}
void intel_miptree_release( struct intel_context *intel,
struct intel_mipmap_tree *mt )
{
if (--mt->refcount) {
intel_region_release(intel, mt->region);
free(mt);
}
}
/* Can the image be pulled into a unified mipmap tree. This mirrors
* the completeness test in a lot of ways.
*
* Not sure whether I want to pass gl_texture_image here.
*/
GLboolean intel_miptree_match_image( struct intel_mipmap_tree *mt,
struct gl_texture_image *image,
GLuint face,
GLuint level )
{
/* Images with borders are never pulled into mipmap trees.
*/
if (image->Border)
return GL_FALSE;
if (image->InternalFormat != mt->internal_format ||
image->IsCompressed != mt->compressed)
return GL_FALSE;
/* Test image dimensions against the base level image adjusted for
* minification. This will also catch images not present in the
* tree, changed targets, etc.
*/
if (image->Width != mt->offset[face][level].width ||
image->Height != mt->offset[face][level].height ||
image->Depth != mt->offset[face][level].depth)
return GL_FALSE;
return GL_TRUE;
}
GLubyte *intel_miptree_image_map(struct intel_context *intel,
struct intel_mipmap_tree *mt,
GLuint face,
GLuint level,
GLuint *stride)
{
GLubyte *img = intel_region_map(intel, mt->region);
if (stride)
*stride = mt->pitch * mt->cpp;
return img + (mt->offset[face][level].x +
mt->offset[face][level].y * mt->pitch) * mt->cpp;
}
void intel_miptree_image_unmap(struct intel_context *intel,
struct intel_mipmap_tree *mt)
{
intel_region_unmap(intel, mt->region);
}
/* Upload data for a particular image.
*
* TODO: 3D textures
*/
void intel_miptree_image_data(struct intel_context *intel,
struct intel_mipmap_tree *dst,
GLuint face,
GLuint level,
void *src, GLuint src_pitch )
{
intel_region_data(intel,
dst->region,
dst->offset[face][level].x,
dst->offset[face][level].y,
src,
src_pitch,
0, 0, /* source x,y */
dst->offset[face][level].width,
dst->offset[face][level].height);
}
/* Copy mipmap image between trees
*/
void intel_miptree_image_copy( struct intel_context *intel,
struct intel_mipmap_tree *dst,
GLuint face, GLuint level,
struct intel_mipmap_tree *src )
{
assert(src->offset[face][level].width ==
dst->offset[face][level].width);
assert(src->offset[face][level].height ==
dst->offset[face][level].height);
intel_region_copy(intel,
dst->region,
dst->offset[face][level].x,
dst->offset[face][level].y,
src->region,
src->offset[face][level].x,
src->offset[face][level].y,
src->offset[face][level].width,
src->offset[face][level].height);
}

View file

@ -0,0 +1,156 @@
/**************************************************************************
*
* Copyright 2006 Tungsten Graphics, Inc., Cedar Park, Texas.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sub license, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
**************************************************************************/
#ifndef INTEL_MIPMAP_TREE_H
#define INTEL_MIPMAP_TREE_H
#include "intel_regions.h"
/* A layer on top of the intel_regions code which adds:
*
* - Code to size and layout a region to hold a set of mipmaps.
* - Query to determine if a new image fits in an existing tree.
* - More refcounting
* - maybe able to remove refcounting from intel_region?
* - ?
*
* The fixed mipmap layout of intel hardware where one offset
* specifies the position of all images in a mipmap hierachy
* complicates the implementation of GL texture image commands,
* compared to hardware where each image is specified with an
* independent offset.
*
* In an ideal world, each texture object would be associated with a
* single bufmgr buffer or 2d intel_region, and all the images within
* the texture object would slot into the tree as they arrive. The
* reality can be a little messier, as images can arrive from the user
* with sizes that don't fit in the existing tree, or in an order
* where the tree layout cannot be guessed immediately.
*
* This structure encodes an idealized mipmap tree. The GL image
* commands build these where possible, otherwise store the images in
* temporary system buffers.
*/
struct intel_mipmap_offset {
GLuint x;
GLuint y;
GLuint width;
GLuint height;
GLuint depth; /* how will this work? */
};
struct intel_mipmap_tree {
/* Effectively the key:
*/
GLenum target;
GLenum internal_format;
GLuint first_level;
GLuint last_level;
GLuint width0, height0, depth0;
GLuint cpp;
GLboolean compressed;
/* Derived from the above:
*/
GLuint pitch;
GLuint depth_pitch; /* per-image on i945? */
GLuint total_height;
struct intel_mipmap_offset offset[MAX_FACES][MAX_TEXTURE_LEVELS];
/* The data is held here:
*/
struct intel_region *region;
/* These are also refcounted:
*/
GLuint refcount;
};
struct intel_mipmap_tree *intel_miptree_create( struct intel_context *intel,
GLenum target,
GLenum internal_format,
GLuint first_level,
GLuint last_level,
GLuint width0,
GLuint height0,
GLuint depth0,
GLuint cpp,
GLboolean compressed);
struct intel_mipmap_tree *intel_miptree_reference( struct intel_mipmap_tree * );
void intel_miptree_release( struct intel_context *intel,
struct intel_mipmap_tree *mt );
/* Check if an image fits an existing mipmap tree layout
*/
GLboolean intel_miptree_match_image( struct intel_mipmap_tree *mt,
struct gl_texture_image *image,
GLuint face,
GLuint level );
/* Return a pointer to an image within a tree. Return image stride as
* well.
*/
GLubyte *intel_miptree_image_map( struct intel_context *intel,
struct intel_mipmap_tree *mt,
GLuint face,
GLuint level,
GLuint *stride );
void intel_miptree_image_unmap( struct intel_context *intel,
struct intel_mipmap_tree *mt );
/* Upload an image into a tree
*/
void intel_miptree_image_data(struct intel_context *intel,
struct intel_mipmap_tree *dst,
GLuint face,
GLuint level,
void *src, GLuint src_pitch );
/* Copy an image between two trees
*/
void intel_miptree_image_copy( struct intel_context *intel,
struct intel_mipmap_tree *dst,
GLuint face, GLuint level,
struct intel_mipmap_tree *src );
/* i915_mipmap_tree.c:
*/
GLboolean i915_miptree_layout( struct intel_mipmap_tree *mt );
GLboolean i945_miptree_layout( struct intel_mipmap_tree *mt );
#endif

View file

@ -106,9 +106,9 @@ static void _mesa_copy_rect( GLubyte *dst,
GLuint width,
GLuint height,
GLubyte *src,
GLuint src_pitch,
GLuint src_x,
GLuint src_y,
GLuint src_pitch )
GLuint src_y )
{
GLuint i;
@ -195,24 +195,24 @@ static void _mesa_fill_rect( GLubyte *dst,
* Currently always memcpy.
*/
void intel_region_data(struct intel_context *intel,
struct intel_region *dest,
GLuint destx, GLuint desty,
struct intel_region *dst,
GLuint dstx, GLuint dsty,
void *src, GLuint src_pitch,
GLuint srcx, GLuint srcy,
GLuint width, GLuint height)
{
LOCK_HARDWARE(intel);
_mesa_copy_rect(intel_region_map(intel, dest),
dest->cpp,
dest->pitch,
destx, desty,
destx + width, desty + height,
_mesa_copy_rect(intel_region_map(intel, dst),
dst->cpp,
dst->pitch,
dstx, dsty,
width, height,
src,
srcx, srcy,
src_pitch);
src_pitch,
srcx, srcy);
intel_region_unmap(intel, dest);
intel_region_unmap(intel, dst);
UNLOCK_HARDWARE(intel);
@ -222,52 +222,51 @@ void intel_region_data(struct intel_context *intel,
* push buffers into AGP - will currently do so whenever possible.
*/
void intel_region_copy( struct intel_context *intel,
struct intel_region *dest,
GLuint destx, GLuint desty,
struct intel_region *dst,
GLuint dstx, GLuint dsty,
struct intel_region *src,
GLuint srcx, GLuint srcy,
GLuint width, GLuint height )
{
unsigned dst_offset;
unsigned src_offset;
struct bm_buffer_list *list = bmNewBufferList();
assert(src->cpp == dest->cpp);
assert(src->cpp == dst->cpp);
LOCK_HARDWARE(intel);
bmClearBufferList(intel->bm);
bmAddBuffer(intel->bm, dest->buffer, BM_WRITE, NULL, &dst_offset);
bmAddBuffer(intel->bm, src->buffer, BM_READ, NULL, &src_offset);
bmAddBuffer(intel->bm, list, dst->buffer, BM_WRITE, NULL, &dst_offset);
bmAddBuffer(intel->bm, list, src->buffer, BM_READ, NULL, &src_offset);
/* What I really want to do is query if both buffers are already
* uploaded:
*/
if (bmValidateBufferList(intel->bm, BM_NO_EVICT|BM_NO_UPLOAD)) {
if (bmValidateBufferList(intel->bm, list, BM_NO_EVICT|BM_NO_UPLOAD)) {
intelEmitCopyBlitLocked(intel,
dest->cpp,
src->pitch,
src_offset,
dest->pitch,
dst_offset,
srcx, srcy,
destx, desty,
width, height);
dst->cpp,
src->pitch, src_offset,
dst->pitch, dst_offset,
srcx, srcy,
dstx, dsty,
width, height);
bmReleaseValidatedBuffers(intel->bm);
bmFenceBufferList(intel->bm, list);
}
else {
_mesa_copy_rect(intel_region_map(intel, dest),
dest->cpp,
dest->pitch,
destx, desty,
_mesa_copy_rect(intel_region_map(intel, dst),
dst->cpp,
dst->pitch,
dstx, dsty,
width, height,
intel_region_map(intel, src),
srcx, srcy,
src->pitch);
intel_region_unmap(intel, dest);
intel_region_unmap(intel, dst);
intel_region_unmap(intel, src);
}
bmFreeBufferList(list);
UNLOCK_HARDWARE(intel);
}
@ -275,40 +274,40 @@ void intel_region_copy( struct intel_context *intel,
* push buffers into AGP - will currently do so whenever possible.
*/
void intel_region_fill( struct intel_context *intel,
struct intel_region *dest,
GLuint destx, GLuint desty,
struct intel_region *dst,
GLuint dstx, GLuint dsty,
GLuint width, GLuint height,
GLuint color )
{
unsigned dst_offset;
struct bm_buffer_list *list = bmNewBufferList();
LOCK_HARDWARE(intel);
bmAddBuffer(intel->bm, list, dst->buffer, BM_WRITE, NULL, &dst_offset);
bmClearBufferList(intel->bm);
bmAddBuffer(intel->bm, dest->buffer, BM_WRITE, NULL, &dst_offset);
if (bmValidateBufferList(intel->bm, BM_NO_EVICT)) {
if (bmValidateBufferList(intel->bm, list, BM_NO_EVICT)) {
intelEmitFillBlitLocked(intel,
dest->cpp,
dest->pitch,
dst->cpp,
dst->pitch,
dst_offset,
destx, desty,
dstx, dsty,
width, height,
color );
bmReleaseValidatedBuffers(intel->bm);
bmFenceBufferList(intel->bm, list);
}
else {
_mesa_fill_rect(intel_region_map(intel, dest),
dest->cpp,
dest->pitch,
destx, desty,
destx + width, desty + height,
_mesa_fill_rect(intel_region_map(intel, dst),
dst->cpp,
dst->pitch,
dstx, dsty,
width, height,
color);
intel_region_unmap(intel, dest);
intel_region_unmap(intel, dst);
}
bmFreeBufferList(list);
UNLOCK_HARDWARE(intel);
}

View file

@ -28,6 +28,9 @@
#ifndef INTEL_REGIONS_H
#define INTEL_REGIONS_H
#include "mtypes.h"
struct intel_context;
/* A layer on top of the bufmgr buffers that adds a few useful things:
*
@ -36,7 +39,6 @@
* - Buffer dimensions - pitch and height.
* - Blitter commands for copying 2D regions between buffers.
*/
struct intel_region {
GLuint buffer;
GLuint refcount;