diff options
author | Keith Whitwell <keith@tungstengraphics.com> | 2006-08-03 15:41:10 +0000 |
---|---|---|
committer | Keith Whitwell <keith@tungstengraphics.com> | 2006-08-03 15:41:10 +0000 |
commit | f9f3de8c316ac63c27a09b04482501b628943f50 (patch) | |
tree | 651889882d5e54927e47ffa4d882ccf322ace0c8 | |
parent | 3d0a073a71f8ce04387b1c4072af83daa93b5326 (diff) | |
download | mesa-f9f3de8c316ac63c27a09b04482501b628943f50.tar.gz |
first pass at texture uploads from pbo's with the blitter
-rw-r--r-- | src/mesa/drivers/dri/i915/intel_blit.c | 511 | ||||
-rw-r--r-- | src/mesa/drivers/dri/i915/intel_bufmgr.c | 454 | ||||
-rw-r--r-- | src/mesa/drivers/dri/i915/intel_tex_image.c | 560 |
3 files changed, 1525 insertions, 0 deletions
diff --git a/src/mesa/drivers/dri/i915/intel_blit.c b/src/mesa/drivers/dri/i915/intel_blit.c new file mode 100644 index 00000000000..83d3e183dad --- /dev/null +++ b/src/mesa/drivers/dri/i915/intel_blit.c @@ -0,0 +1,511 @@ +/************************************************************************** + * + * Copyright 2003 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 <stdio.h> +#include <errno.h> + +#include "mtypes.h" +#include "context.h" +#include "enums.h" + +#include "intel_batchbuffer.h" +#include "intel_blit.h" +#include "intel_buffers.h" +#include "intel_context.h" +#include "intel_fbo.h" +#include "intel_reg.h" +#include "intel_regions.h" + +#include "intel_bufmgr.h" + + +/** + * Copy the back color buffer to the front color buffer. + * Used for SwapBuffers(). + */ +void intelCopyBuffer( const __DRIdrawablePrivate *dPriv, + const drm_clip_rect_t *rect ) +{ + GET_CURRENT_CONTEXT(ctx); + struct intel_context *intel; + GLboolean missed_target; + int64_t ust; + + DBG("%s\n", __FUNCTION__); + + assert(dPriv); + + /* We need a rendering context in order to issue the blit cmd. + * Use the current context. + * XXX need to fix this someday. + */ + if (!ctx) { + _mesa_problem(NULL, "No current context in intelCopyBuffer()"); + return; + } + intel = (struct intel_context *) ctx; + + /* FIXME: Temporary fix for fence ageing. + * + */ + + if (!intel->last_swap_fence_retired) { + bmFinishFence(intel->bm, intel->last_swap_fence); + } + + + if (!rect) + { + driWaitForVBlank( dPriv, &intel->vbl_seq, intel->vblank_flags, & missed_target ); + } + + + /* The LOCK_HARDWARE is required for the cliprects. Buffer offsets + * should work regardless. + */ + LOCK_HARDWARE( intel ); + + if (intel->driDrawable && + intel->driDrawable->numClipRects) + { + const intelScreenPrivate *intelScreen = intel->intelScreen; + struct gl_framebuffer *fb + = (struct gl_framebuffer *) dPriv->driverPrivate; + const struct intel_region *frontRegion + = intel_get_rb_region(fb, BUFFER_FRONT_LEFT); + const struct intel_region *backRegion + = intel_get_rb_region(fb, BUFFER_BACK_LEFT); + const int nbox = dPriv->numClipRects; + const drm_clip_rect_t *pbox = dPriv->pClipRects; + const int pitch = frontRegion->pitch; + const int cpp = frontRegion->cpp; + int BR13, CMD; + int i; + + ASSERT(fb); + ASSERT(fb->Name == 0); /* Not a user-created FBO */ + ASSERT(frontRegion); + ASSERT(backRegion); + ASSERT(frontRegion->pitch == backRegion->pitch); + ASSERT(frontRegion->cpp == backRegion->cpp); + + if (cpp == 2) { + BR13 = (pitch * cpp) | (0xCC << 16) | (1<<24); + CMD = XY_SRC_COPY_BLT_CMD; + } + else { + BR13 = (pitch * cpp) | (0xCC << 16) | (1<<24) | (1<<25); + CMD = (XY_SRC_COPY_BLT_CMD | XY_SRC_COPY_BLT_WRITE_ALPHA | + XY_SRC_COPY_BLT_WRITE_RGB); + } + + for (i = 0 ; i < nbox; i++, pbox++) + { + drm_clip_rect_t box; + + if (pbox->x1 > pbox->x2 || + pbox->y1 > pbox->y2 || + pbox->x2 > intelScreen->width || + pbox->y2 > intelScreen->height) + continue; + + box = *pbox; + + if (rect) + { + if (rect->x1 > box.x1) + box.x1 = rect->x1; + if (rect->y1 > box.y1) + box.y1 = rect->y1; + if (rect->x2 < box.x2) + box.x2 = rect->x2; + if (rect->y2 < box.y2) + box.y2 = rect->y2; + + if (box.x1 > box.x2 || box.y1 > box.y2) + continue; + } + + BEGIN_BATCH(8, INTEL_BATCH_NO_CLIPRECTS); + OUT_BATCH( CMD ); + OUT_BATCH( BR13 ); + OUT_BATCH( (pbox->y1 << 16) | pbox->x1 ); + OUT_BATCH( (pbox->y2 << 16) | pbox->x2 ); + + if (intel->sarea->pf_current_page == 0) + OUT_RELOC( frontRegion->buffer, DRM_MM_TT|DRM_MM_WRITE, 0 ); + else + OUT_RELOC( backRegion->buffer, DRM_MM_TT|DRM_MM_WRITE, 0 ); + OUT_BATCH( (pbox->y1 << 16) | pbox->x1 ); + OUT_BATCH( BR13 & 0xffff ); + + if (intel->sarea->pf_current_page == 0) + OUT_RELOC( backRegion->buffer, DRM_MM_TT|DRM_MM_READ, 0 ); + else + OUT_RELOC( frontRegion->buffer, DRM_MM_TT|DRM_MM_READ, 0 ); + + ADVANCE_BATCH(); + } + + intel->last_swap_fence = intel_batchbuffer_flush( intel->batch ); + intel->last_swap_fence_retired = GL_FALSE; + } + UNLOCK_HARDWARE( intel ); + + if (!rect) + { + intel->swap_count++; + (*dri_interface->getUST)(&ust); + if (missed_target) { + intel->swap_missed_count++; + intel->swap_missed_ust = ust - intel->swap_ust; + } + + intel->swap_ust = ust; + } +} + + + + +void intelEmitFillBlit( struct intel_context *intel, + GLuint cpp, + GLshort dst_pitch, + GLuint dst_buffer, + GLuint dst_offset, + GLshort x, GLshort y, + GLshort w, GLshort h, + GLuint color ) +{ + GLuint BR13, CMD; + BATCH_LOCALS; + + dst_pitch *= cpp; + + switch(cpp) { + case 1: + case 2: + case 3: + BR13 = dst_pitch | (0xF0 << 16) | (1<<24); + CMD = XY_COLOR_BLT_CMD; + break; + case 4: + BR13 = dst_pitch | (0xF0 << 16) | (1<<24) | (1<<25); + CMD = (XY_COLOR_BLT_CMD | XY_COLOR_BLT_WRITE_ALPHA | + XY_COLOR_BLT_WRITE_RGB); + break; + default: + return; + } + + DBG("%s dst:buf(%d)/%d+%d %d,%d sz:%dx%d\n", + __FUNCTION__, + dst_buffer, dst_pitch, dst_offset, x, y, + w,h); + + + BEGIN_BATCH(6, INTEL_BATCH_NO_CLIPRECTS); + OUT_BATCH( CMD ); + OUT_BATCH( BR13 ); + OUT_BATCH( (y << 16) | x ); + OUT_BATCH( ((y+h) << 16) | (x+w) ); + OUT_RELOC( dst_buffer, DRM_MM_TT|DRM_MM_WRITE, dst_offset ); + OUT_BATCH( color ); + ADVANCE_BATCH(); +} + + +/* Copy BitBlt + */ +void intelEmitCopyBlit( struct intel_context *intel, + GLuint cpp, + GLshort src_pitch, + GLuint src_buffer, + GLuint src_offset, + GLshort dst_pitch, + GLuint dst_buffer, + GLuint dst_offset, + GLshort src_x, GLshort src_y, + GLshort dst_x, GLshort dst_y, + GLshort w, GLshort h ) +{ + GLuint CMD, BR13; + int dst_y2 = dst_y + h; + int dst_x2 = dst_x + w; + BATCH_LOCALS; + + + DBG("%s src:buf(%d)/%d+%d %d,%d dst:buf(%d)/%d+%d %d,%d sz:%dx%d\n", + __FUNCTION__, + src_buffer, src_pitch, src_offset, src_x, src_y, + dst_buffer, dst_pitch, dst_offset, dst_x, dst_y, + w,h); + + src_pitch *= cpp; + dst_pitch *= cpp; + + switch(cpp) { + case 1: + case 2: + case 3: + BR13 = (((GLint)dst_pitch)&0xffff) | (0xCC << 16) | (1<<24); + CMD = XY_SRC_COPY_BLT_CMD; + break; + case 4: + BR13 = (((GLint)dst_pitch)&0xffff) | (0xCC << 16) | (1<<24) | (1<<25); + CMD = (XY_SRC_COPY_BLT_CMD | XY_SRC_COPY_BLT_WRITE_ALPHA | + XY_SRC_COPY_BLT_WRITE_RGB); + break; + default: + return; + } + + if (dst_y2 < dst_y || + dst_x2 < dst_x) { + return; + } + + /* Initial y values don't seem to work with negative pitches. If + * we adjust the offsets manually (below), it seems to work fine. + * + * On the other hand, if we always adjust, the hardware doesn't + * know which blit directions to use, so overlapping copypixels get + * the wrong result. + */ + if ( dst_pitch > 0 && + src_pitch > 0) { + BEGIN_BATCH(8, INTEL_BATCH_NO_CLIPRECTS); + OUT_BATCH( CMD ); + OUT_BATCH( BR13 ); + OUT_BATCH( (dst_y << 16) | dst_x ); + OUT_BATCH( (dst_y2 << 16) | dst_x2 ); + OUT_RELOC( dst_buffer, DRM_MM_TT|DRM_MM_WRITE, dst_offset ); + OUT_BATCH( (src_y << 16) | src_x ); + OUT_BATCH( ((GLint)src_pitch&0xffff) ); + OUT_RELOC( src_buffer, DRM_MM_TT|DRM_MM_READ, src_offset ); + ADVANCE_BATCH(); + } + else { + BEGIN_BATCH(8, INTEL_BATCH_NO_CLIPRECTS); + OUT_BATCH( CMD ); + OUT_BATCH( BR13 ); + OUT_BATCH( (0 << 16) | dst_x ); + OUT_BATCH( (h << 16) | dst_x2 ); + OUT_RELOC( dst_buffer, DRM_MM_TT|DRM_MM_WRITE, dst_offset + dst_y * dst_pitch ); + OUT_BATCH( (0 << 16) | src_x ); + OUT_BATCH( ((GLint)src_pitch&0xffff) ); + OUT_RELOC( src_buffer, DRM_MM_TT|DRM_MM_READ, src_offset + src_y * src_pitch ); + ADVANCE_BATCH(); + } +} + + +/** + * Use blitting to clear the renderbuffers named by 'flags'. + * Note: we can't use the ctx->DrawBuffer->_ColorDrawBufferMask field + * since that might include software renderbuffers or renderbuffers + * which we're clearing with triangles. + * \param mask bitmask of BUFFER_BIT_* values indicating buffers to clear + */ +void intelClearWithBlit(GLcontext *ctx, GLbitfield mask, GLboolean all, + GLint cx, GLint cy, GLint cw, GLint ch) +{ + struct intel_context *intel = intel_context( ctx ); + GLuint clear_depth; + GLbitfield skipBuffers = 0; + BATCH_LOCALS; + + if (INTEL_DEBUG & DEBUG_DRI) + _mesa_printf("%s %x\n", __FUNCTION__, mask); + + /* + * Compute values for clearing the buffers. + */ + clear_depth = 0; + if (mask & BUFFER_BIT_DEPTH) { + clear_depth = (GLuint) (ctx->DrawBuffer->_DepthMax * ctx->Depth.Clear); + } + if (mask & BUFFER_BIT_STENCIL) { + clear_depth |= (ctx->Stencil.Clear & 0xff) << 24; + } + + /* If clearing both depth and stencil, skip BUFFER_BIT_STENCIL in + * the loop below. + */ + if ((mask & BUFFER_BIT_DEPTH) && (mask & BUFFER_BIT_STENCIL)) { + skipBuffers = BUFFER_BIT_STENCIL; + } + + /* XXX Move this flush/lock into the following conditional? */ + intelFlush( &intel->ctx ); + LOCK_HARDWARE( intel ); + + if (intel->numClipRects) + { + drm_clip_rect_t clear; + int i; + + /* Refresh the cx/y/w/h values as they may have been invalidated + * by a new window position or size picked up when we did + * LOCK_HARDWARE above. The values passed by mesa are not + * reliable. + */ + { + cx = ctx->DrawBuffer->_Xmin; + cy = ctx->DrawBuffer->_Ymin; + ch = ctx->DrawBuffer->_Ymax - ctx->DrawBuffer->_Ymin; + cw = ctx->DrawBuffer->_Xmax - ctx->DrawBuffer->_Xmin; + } + + if (intel->ctx.DrawBuffer->Name == 0) { + /* clearing a window */ + + /* flip top to bottom */ + clear.x1 = cx + intel->drawX; + clear.y1 = intel->driDrawable->y + intel->driDrawable->h - cy - ch; + clear.x2 = clear.x1 + cw; + clear.y2 = clear.y1 + ch; + + /* adjust for page flipping */ + if ( intel->sarea->pf_current_page == 1 ) { + const GLuint tmp = mask; + mask &= ~(BUFFER_BIT_FRONT_LEFT | BUFFER_BIT_BACK_LEFT); + if ( tmp & BUFFER_BIT_FRONT_LEFT ) mask |= BUFFER_BIT_BACK_LEFT; + if ( tmp & BUFFER_BIT_BACK_LEFT ) mask |= BUFFER_BIT_FRONT_LEFT; + } + } + else { + /* clearing FBO */ + ASSERT(intel->numClipRects == 1); + ASSERT(intel->pClipRects == &intel->fboRect); + clear.x1 = cx; + clear.y1 = intel->ctx.DrawBuffer->Height - cy - ch; + clear.x2 = clear.y1 + cw; + clear.y2 = clear.y1 + ch; + /* no change to mask */ + } + + for (i = 0 ; i < intel->numClipRects ; i++) + { + const drm_clip_rect_t *box = &intel->pClipRects[i]; + drm_clip_rect_t b; + GLuint buf; + GLuint clearMask = mask; /* use copy, since we modify it below */ + + if (!all) { + intel_intersect_cliprects(&b, &clear, box); + } else { + b = *box; + } + + if (0) + _mesa_printf("clear %d,%d..%d,%d, mask %x\n", + b.x1, b.y1, + b.x2, b.y2, + mask); + + /* Loop over all renderbuffers */ + for (buf = 0; buf < BUFFER_COUNT && clearMask; buf++) { + const GLbitfield bufBit = 1 << buf; + if ((clearMask & bufBit) && !(bufBit & skipBuffers)) { + /* OK, clear this renderbuffer */ + const struct intel_renderbuffer *irb + = intel_renderbuffer(ctx->DrawBuffer-> + Attachment[buf].Renderbuffer); + GLuint clearVal; + GLint pitch, cpp; + GLuint BR13, CMD; + + ASSERT(irb); + ASSERT(irb->region); + + pitch = irb->region->pitch; + cpp = irb->region->cpp; + + DBG("%s dst:buf(%d)/%d+%d %d,%d sz:%dx%d\n", + __FUNCTION__, + irb->region->buffer, (pitch * cpp), + irb->region->draw_offset, + b.x1, b.y1, + b.x2 - b.x1, b.y2 - b.y1); + + + /* Setup the blit command */ + if (cpp == 4) { + BR13 = (0xF0 << 16) | (pitch * cpp) | (1<<24) | (1<<25); + if (buf == BUFFER_DEPTH || buf == BUFFER_STENCIL) { + CMD = XY_COLOR_BLT_CMD; + if (clearMask & BUFFER_BIT_DEPTH) + CMD |= XY_COLOR_BLT_WRITE_RGB; + if (clearMask & BUFFER_BIT_STENCIL) + CMD |= XY_COLOR_BLT_WRITE_ALPHA; + } + else { + /* clearing RGBA */ + CMD = (XY_COLOR_BLT_CMD | + XY_COLOR_BLT_WRITE_ALPHA | + XY_COLOR_BLT_WRITE_RGB); + } + } + else { + ASSERT(cpp == 2 || cpp == 0); + BR13 = (0xF0 << 16) | (pitch * cpp) | (1<<24); + CMD = XY_COLOR_BLT_CMD; + } + + if (buf == BUFFER_DEPTH || buf == BUFFER_STENCIL) { + clearVal = clear_depth; + } + else { + clearVal = (cpp == 4) + ? intel->ClearColor8888 : intel->ClearColor565; + } + /* + _mesa_debug(ctx, "hardware blit clear buf %d rb id %d\n", + buf, irb->Base.Name); + */ + BEGIN_BATCH(6, INTEL_BATCH_NO_CLIPRECTS); + OUT_BATCH( CMD ); + OUT_BATCH( BR13 ); + OUT_BATCH( (b.y1 << 16) | b.x1 ); + OUT_BATCH( (b.y2 << 16) | b.x2 ); + OUT_RELOC( irb->region->buffer, DRM_MM_TT|DRM_MM_WRITE, + irb->region->draw_offset ); + OUT_BATCH( clearVal ); + ADVANCE_BATCH(); + clearMask &= ~bufBit; /* turn off bit, for faster loop exit */ + } + } + } + intel_batchbuffer_flush( intel->batch ); + } + + UNLOCK_HARDWARE( intel ); +} + + diff --git a/src/mesa/drivers/dri/i915/intel_bufmgr.c b/src/mesa/drivers/dri/i915/intel_bufmgr.c new file mode 100644 index 00000000000..c9efed9f431 --- /dev/null +++ b/src/mesa/drivers/dri/i915/intel_bufmgr.c @@ -0,0 +1,454 @@ +/************************************************************************** + * + * Copyright 2006 Tungsten Graphics, Inc., Steamboat Springs, CO. + * 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 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 + * THE COPYRIGHT HOLDERS, AUTHORS 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. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * + **************************************************************************/ + +#include "intel_bufmgr.h" + +#include "intel_context.h" +#include "intel_ioctl.h" + +#include "hash.h" +#include "simple_list.h" +#include "mm.h" +#include "imports.h" +#include "glthread.h" +#include <sys/ioctl.h> +#include <unistd.h> +#include <drm.h> + +struct _mesa_HashTable; + +/* The buffer manager is really part of the gl_shared_state struct. + * TODO: Organize for the bufmgr to be created/deleted with the shared + * state and stored within the DriverData of that struct. Currently + * there are no mesa callbacks for this. + */ + +#define BM_MAX 16 +static struct bufmgr +{ + _glthread_Mutex mutex; /**< for thread safety */ + int driFd; + int refcount; + struct _mesa_HashTable *hash; + + unsigned buf_nr; /* for generating ids */ + drmMMPool batchPool; + drmFence initFence; +} bufmgr_pool[BM_MAX]; + +static int nr_bms; + +#define LOCK(bm) _glthread_LOCK_MUTEX(bm->mutex) +#define UNLOCK(bm) _glthread_UNLOCK_MUTEX(bm->mutex) + +static void +bmError(int val, const char *file, const char *function, int line) +{ + _mesa_printf("Fatal video memory manager error \"%s\".\n" + "Check kernel logs or set the LIBGL_DEBUG\n" + "environment variable to \"verbose\" for more info.\n" + "Detected in file %s, line %d, function %s.\n", + strerror(-val), file, line, function); +#ifndef NDEBUG + exit(-1); +#else + abort(); +#endif +} + +#define BM_CKFATAL(val) \ + do{ \ + int tstVal = (val); \ + if (tstVal) \ + bmError(tstVal, __FILE__, __FUNCTION__, __LINE__); \ + } while(0); + +/*********************************************************************** + * Public functions + */ + +/* The initialization functions are skewed in the fake implementation. + * This call would be to attach to an existing manager, rather than to + * create a local one. + */ + +struct bufmgr * +bm_intel_Attach(struct intel_context *intel) +{ + GLuint i; + + for (i = 0; i < nr_bms; i++) + if (bufmgr_pool[i].driFd == intel->driFd) { + bufmgr_pool[i].refcount++; + _mesa_printf("retrieive old bufmgr for fd %d\n", + bufmgr_pool[i].driFd); + return &bufmgr_pool[i]; + } + + if (nr_bms < BM_MAX) { + struct bufmgr *bm = &bufmgr_pool[nr_bms++]; + + _mesa_printf("create new bufmgr for fd %d\n", intel->driFd); + bm->driFd = intel->driFd; + bm->hash = _mesa_NewHashTable(); + bm->refcount = 1; + _glthread_INIT_MUTEX(bm->mutex); + + drmGetLock(bm->driFd, intel->hHWContext, 0); + BM_CKFATAL(drmMMAllocBufferPool(bm->driFd, mmPoolRing, 0, + DRM_MM_TT | DRM_MM_NO_EVICT | + DRM_MM_READ | DRM_MM_EXE | + BM_BATCHBUFFER, 1024 * 1024, 4096, + &bm->batchPool)); + + + BM_CKFATAL(drmEmitFence(bm->driFd, 0, &bm->initFence)); + drmUnlock(bm->driFd, intel->hHWContext); + return bm; + } + + _mesa_printf("failed to create new bufmgr for fd %d\n", intel->driFd); + return NULL; +} + +void +bmGenBuffers(struct bufmgr *bm, unsigned n, unsigned *buffers, unsigned flags) +{ + LOCK(bm); + { + unsigned i; + unsigned bFlags = + (flags) ? flags : DRM_MM_TT | DRM_MM_VRAM | DRM_MM_SYSTEM; + + for (i = 0; i < n; i++) { + drmMMBuf *buf = calloc(sizeof(*buf), 1); + + BM_CKFATAL(drmMMInitBuffer(bm->driFd, bFlags, 12, buf)); + buf->client_priv = ++bm->buf_nr; + buffers[i] = buf->client_priv; + _mesa_HashInsert(bm->hash, buffers[i], buf); + } + } + UNLOCK(bm); +} + +void +bmSetShared(struct bufmgr *bm, unsigned buffer, unsigned flags, + unsigned long offset, void *virtual) +{ + LOCK(bm); + { + drmMMBuf *buf = _mesa_HashLookup(bm->hash, buffer); + + assert(buf); + + buf->flags = DRM_MM_NO_EVICT | DRM_MM_SHARED + | DRM_MM_WRITE | DRM_MM_READ; + buf->flags |= flags & DRM_MM_MEMTYPE_MASK; + buf->offset = offset; + buf->virtual = virtual; + BM_CKFATAL(drmMMAllocBuffer(bm->driFd, 0, NULL, 0, buf)); + } + UNLOCK(bm); +} + +void +bmDeleteBuffers(struct bufmgr *bm, unsigned n, unsigned *buffers) +{ + LOCK(bm); + { + unsigned i; + + for (i = 0; i < n; i++) { + drmMMBuf *buf = _mesa_HashLookup(bm->hash, buffers[i]); + + if (buf) { + BM_CKFATAL(drmMMFreeBuffer(bm->driFd, buf)); + + _mesa_HashRemove(bm->hash, buffers[i]); + } + } + } + UNLOCK(bm); +} + +/* If buffer size changes, free and reallocate. Otherwise update in + * place. + */ + +void +bmBufferData(struct bufmgr *bm, + unsigned buffer, unsigned size, const void *data, unsigned flags) +{ + LOCK(bm); + { + drmMMBuf *buf = (drmMMBuf *) _mesa_HashLookup(bm->hash, buffer); + + DBG("bmBufferData %d sz 0x%x data: %p\n", buffer, size, data); + + assert(buf); + assert(!buf->mapped); + assert(size); + + if (buf->flags & BM_BATCHBUFFER) { + BM_CKFATAL(drmMMFreeBuffer(bm->driFd, buf)); + BM_CKFATAL(drmMMAllocBuffer + (bm->driFd, size, &bm->batchPool, 1, buf)); + } else if (!(buf->flags & DRM_MM_SHARED)) { + + if (buf->block && (buf->size < size || drmBufIsBusy(bm->driFd, buf))) { + BM_CKFATAL(drmMMFreeBuffer(bm->driFd, buf)); + } + if (!buf->block) { + BM_CKFATAL(drmMMAllocBuffer(bm->driFd, size, NULL, 0, buf)); + } + + } + + if (data != NULL) { + + memcpy(drmMMMapBuffer(bm->driFd, buf), data, size); + drmMMUnmapBuffer(bm->driFd, buf); + + } + } + UNLOCK(bm); +} + +/* Update the buffer in place, in whatever space it is currently resident: + */ +void +bmBufferSubData(struct bufmgr *bm, + unsigned buffer, + unsigned offset, unsigned size, const void *data) +{ + LOCK(bm); + { + drmMMBuf *buf = (drmMMBuf *) _mesa_HashLookup(bm->hash, buffer); + + DBG("bmBufferSubdata %d offset 0x%x sz 0x%x\n", buffer, offset, size); + + assert(buf); + drmBufWaitBusy(bm->driFd, buf); + + if (size) { + memcpy((unsigned char *) drmMMMapBuffer(bm->driFd, buf) + offset, + data, size); + drmMMUnmapBuffer(bm->driFd, buf); + } + } + UNLOCK(bm); +} + +/* Extract data from the buffer: + */ +void +bmBufferGetSubData(struct bufmgr *bm, + unsigned buffer, + unsigned offset, unsigned size, void *data) +{ + LOCK(bm); + { + drmMMBuf *buf = (drmMMBuf *) _mesa_HashLookup(bm->hash, buffer); + + DBG("bmBufferSubdata %d offset 0x%x sz 0x%x\n", buffer, offset, size); + + assert(buf); + drmBufWaitBusy(bm->driFd, buf); + + if (size) { + memcpy(data, + (unsigned char *) drmMMMapBuffer(bm->driFd, buf) + offset, + size); + drmMMUnmapBuffer(bm->driFd, buf); + } + } + UNLOCK(bm); +} + +/* Return a pointer to whatever space the buffer is currently resident in: + */ +void * +bmMapBuffer(struct bufmgr *bm, unsigned buffer, unsigned flags) +{ + void *retval; + + LOCK(bm); + { + drmMMBuf *buf = (drmMMBuf *) _mesa_HashLookup(bm->hash, buffer); + + DBG("bmMapBuffer %d\n", buffer); + DBG("Map: Block is 0x%x\n", &buf->block); + + assert(buf); + /* assert(!buf->mapped); */ + retval = drmMMMapBuffer(bm->driFd, buf); + } + UNLOCK(bm); + + return retval; +} + +void +bmUnmapBuffer(struct bufmgr *bm, unsigned buffer) +{ + LOCK(bm); + { + drmMMBuf *buf = (drmMMBuf *) _mesa_HashLookup(bm->hash, buffer); + + if (!buf) + goto out; + + DBG("bmUnmapBuffer %d\n", buffer); + + drmMMUnmapBuffer(bm->driFd, buf); + } + out: + UNLOCK(bm); +} + +/* Build the list of buffers to validate. Note that the buffer list + * isn't a shared structure so we don't need mutexes when manipulating + * it. + * + * XXX: need refcounting for drmMMBuf structs so that they can't be + * deleted while on these lists. + */ +struct _drmMMBufList * +bmNewBufferList(void) +{ + return drmMMInitListHead(); +} + +int +bmAddBuffer(struct bufmgr *bm, + struct _drmMMBufList *list, + unsigned buffer, + unsigned flags, + unsigned *memtype_return, unsigned long *offset_return) +{ + drmMMBuf *buf = (drmMMBuf *) _mesa_HashLookup(bm->hash, buffer); + + assert(buf); + return drmMMBufListAdd(list, buf, 0, flags, memtype_return, offset_return); +} + +void +bmFreeBufferList(struct _drmMMBufList *list) +{ + drmMMFreeBufList(list); +} + +int +bmScanBufferList(struct bufmgr *bm, + struct _drmMMBufList *list, unsigned buffer) +{ + drmMMBuf *buf = (drmMMBuf *) _mesa_HashLookup(bm->hash, buffer); + + assert(buf); + return drmMMScanBufList(list, buf); +} + +/* To be called prior to emitting commands to hardware which reference + * these buffers. The buffer_usage list provides information on where + * the buffers should be placed and whether their contents need to be + * preserved on copying. The offset and pool data elements are return + * values from this function telling the driver exactly where the + * buffers are currently located. + */ + +int +bmValidateBufferList(struct bufmgr *bm, + struct _drmMMBufList *list, unsigned flags) +{ + BM_CKFATAL(drmMMValidateBuffers(bm->driFd, list)); + return 0; +} + +/* After commands are emitted but before unlocking, this must be + * called so that the buffer manager can correctly age the buffers. + * The buffer manager keeps track of the list of validated buffers, so + * already knows what to apply the fence to. + * + * The buffer manager knows how to emit and test fences directly + * through the drm and without callbacks or whatever into the driver. + */ +unsigned +bmFenceBufferList(struct bufmgr *bm, struct _drmMMBufList *list) +{ + drmFence fence; + + BM_CKFATAL(drmMMFenceBuffers(bm->driFd, list)); + BM_CKFATAL(drmEmitFence(bm->driFd, 0, &fence)); + + return fence.fenceSeq; +} + +/* This functionality is used by the buffer manager, not really sure + * if we need to be exposing it in this way, probably libdrm will + * offer equivalent calls. + * + * For now they can stay, but will likely change/move before final: + */ +unsigned +bmSetFence(struct bufmgr *bm) +{ + drmFence dFence; + + BM_CKFATAL(drmEmitFence(bm->driFd, 0, &dFence)); + + return dFence.fenceSeq; +} + +int +bmTestFence(struct bufmgr *bm, unsigned fence) +{ + drmFence dFence; + int retired; + + dFence.fenceType = 0; + dFence.fenceSeq = fence; + BM_CKFATAL(drmTestFence(bm->driFd, dFence, 0, &retired)); + return retired; +} + +void +bmFinishFence(struct bufmgr *bm, unsigned fence) +{ + drmFence dFence; + dFence.fenceType = 0; + dFence.fenceSeq = fence; + BM_CKFATAL(drmWaitFence(bm->driFd, dFence)); + bm->initFence = dFence; +} + +unsigned +bmInitFence(struct bufmgr *bm) +{ + return bm->initFence.fenceSeq; +} diff --git a/src/mesa/drivers/dri/i915/intel_tex_image.c b/src/mesa/drivers/dri/i915/intel_tex_image.c new file mode 100644 index 00000000000..ea52ec360a2 --- /dev/null +++ b/src/mesa/drivers/dri/i915/intel_tex_image.c @@ -0,0 +1,560 @@ + +#include <stdlib.h> +#include <stdio.h> + +#include "glheader.h" +#include "macros.h" +#include "mtypes.h" +#include "enums.h" +#include "colortab.h" +#include "convolve.h" +#include "context.h" +#include "simple_list.h" +#include "texcompress.h" +#include "texformat.h" +#include "texobj.h" +#include "texstore.h" + +#include "intel_context.h" +#include "intel_mipmap_tree.h" +#include "intel_buffer_objects.h" +#include "intel_tex.h" +#include "intel_ioctl.h" +#include "intel_blit.h" + + +/* Functions to store texture images. Where possible, mipmap_tree's + * will be created or further instantiated with image data, otherwise + * images will be stored in malloc'd memory. A validation step is + * required to pull those images into a mipmap tree, or otherwise + * decide a fallback is required. + */ + + +static int logbase2(int n) +{ + GLint i = 1; + GLint log2 = 0; + + while (n > i) { + i *= 2; + log2++; + } + + return log2; +} + + +/* Otherwise, store it in memory if (Border != 0) or (any dimension == + * 1). + * + * Otherwise, if max_level >= level >= min_level, create tree with + * space for textures from min_level down to max_level. + * + * Otherwise, create tree with space for textures from (level + * 0)..(1x1). Consider pruning this tree at a validation if the + * saving is worth it. + */ +static void guess_and_alloc_mipmap_tree( struct intel_context *intel, + struct intel_texture_object *intelObj, + struct intel_texture_image *intelImage ) +{ + GLuint firstLevel; + GLuint lastLevel; + GLuint width = intelImage->base.Width; + GLuint height = intelImage->base.Height; + GLuint depth = intelImage->base.Depth; + GLuint l2width, l2height, l2depth; + GLuint i; + + DBG("%s\n", __FUNCTION__); + + if (intelImage->base.Border) + return; + + if (intelImage->level > intelObj->base.BaseLevel && + (intelImage->base.Width == 1 || + (intelObj->base.Target != GL_TEXTURE_1D && + intelImage->base.Height == 1) || + (intelObj->base.Target == GL_TEXTURE_3D && + intelImage->base.Depth == 1))) + return; + + /* If this image disrespects BaseLevel, allocate from level zero. + * Usually BaseLevel == 0, so it's unlikely to happen. + */ + if (intelImage->level < intelObj->base.BaseLevel) + firstLevel = 0; + else + firstLevel = intelObj->base.BaseLevel; + + + /* Figure out image dimensions at start level. + */ + for (i = intelImage->level; i > firstLevel; i--) { + width <<= 1; + if (height != 1) height <<= 1; + if (depth != 1) depth <<= 1; + } + + /* Guess a reasonable value for lastLevel. This is probably going + * to be wrong fairly often and might mean that we have to look at + * resizable buffers, or require that buffers implement lazy + * pagetable arrangements. + */ + if ((intelObj->base.MinFilter == GL_NEAREST || + intelObj->base.MinFilter == GL_LINEAR) && + intelImage->level == firstLevel) { + lastLevel = firstLevel; + } + else { + l2width = logbase2(width); + l2height = logbase2(height); + l2depth = logbase2(depth); + lastLevel = firstLevel + MAX2(MAX2(l2width,l2height),l2depth); + } + + assert(!intelObj->mt); + intelObj->mt = intel_miptree_create( intel, + intelObj->base.Target, + intelImage->base.InternalFormat, + firstLevel, + lastLevel, + width, + height, + depth, + intelImage->base.TexFormat->TexelBytes, + intelImage->base.IsCompressed ); + + DBG("%s - success\n", __FUNCTION__); +} + + + + +static GLuint target_to_face( 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 ((GLuint) target - + (GLuint) GL_TEXTURE_CUBE_MAP_POSITIVE_X); + default: + return 0; + } +} + +/* There are actually quite a few combinations this will work for, + * more than what I've listed here. + */ +static GLboolean check_pbo_blit( GLint internalFormat, + GLenum format, GLenum type, + const struct gl_texture_format *mesa_format ) +{ + switch (internalFormat) { + case 4: + case GL_RGBA: + return (format == GL_BGRA && + type == GL_UNSIGNED_INT_8_8_8_8_REV && + mesa_format == &_mesa_texformat_argb8888); + case 3: + case GL_RGB: + return (format == GL_RGB && + type == GL_UNSIGNED_SHORT_5_6_5 && + mesa_format == &_mesa_texformat_rgb565); + case GL_YCBCR_MESA: + return (type == GL_UNSIGNED_SHORT_8_8_MESA || + type == GL_UNSIGNED_BYTE); + default: + return GL_FALSE; + } +} + + + +static GLboolean try_pbo_upload( struct intel_context *intel, + struct intel_texture_image *intelImage, + const struct gl_pixelstore_attrib *unpack, + GLint internalFormat, + GLint width, GLint height, + GLenum format, GLenum type, + const void *pixels) +{ + struct intel_buffer_object *intelObj = intel_buffer_object(unpack->BufferObj); + GLuint src_offset, src_stride; + GLuint dst_offset, dst_stride; + + if (!intelObj || + intel->ctx._ImageTransferState || + unpack->SkipPixels || + unpack->SkipRows) { + _mesa_printf("%s: failure 1\n", __FUNCTION__); + return GL_FALSE; + } + + if (!check_pbo_blit(internalFormat, format, type, intelImage->base.TexFormat)) { + _mesa_printf("%s - bad format for blit\n", __FUNCTION__); + return GL_FALSE; + } + + src_offset = (GLuint) pixels; + + if (unpack->RowLength > 0) + src_stride = unpack->RowLength; + else + src_stride = width; + + dst_offset = intel_miptree_image_offset(intelImage->mt, + intelImage->face, + intelImage->level); + + dst_stride = intelImage->mt->pitch; + + intelFlush( &intel->ctx ); + LOCK_HARDWARE( intel ); + + intelEmitCopyBlit( intel, + intelImage->mt->cpp, + src_stride, intel_bufferobj_buffer(intelObj), src_offset, + dst_stride, intelImage->mt->region->buffer, dst_offset, + 0, + 0, + 0, + 0, + width, + height ); + + intel_batchbuffer_flush( intel->batch ); + UNLOCK_HARDWARE( intel ); + + return GL_TRUE; +} + + + +static void intelTexImage(GLcontext *ctx, + GLint dims, + GLenum target, GLint level, + GLint internalFormat, + GLint width, GLint height, GLint depth, + GLint border, + GLenum format, GLenum type, const void *pixels, + const struct gl_pixelstore_attrib *unpack, + struct gl_texture_object *texObj, + struct gl_texture_image *texImage) +{ + struct intel_context *intel = intel_context(ctx); + struct intel_texture_object *intelObj = intel_texture_object(texObj); + struct intel_texture_image *intelImage = intel_texture_image(texImage); + GLint postConvWidth = width; + GLint postConvHeight = height; + GLint texelBytes, sizeInBytes; + GLuint dstRowStride; + + + DBG("%s target %s level %d %dx%dx%d border %d\n", __FUNCTION__, + _mesa_lookup_enum_by_nr(target), + level, + width, height, depth, border); + + intelFlush(ctx); + + intelImage->face = target_to_face( target ); + intelImage->level = level; + + if (ctx->_ImageTransferState & IMAGE_CONVOLUTION_BIT) { + _mesa_adjust_image_for_convolution(ctx, dims, &postConvWidth, + &postConvHeight); + } + + /* choose the texture format */ + texImage->TexFormat = intelChooseTextureFormat(ctx, internalFormat, + format, type); + + assert(texImage->TexFormat); + + switch (dims) { + case 1: + texImage->FetchTexelc = texImage->TexFormat->FetchTexel1D; + texImage->FetchTexelf = texImage->TexFormat->FetchTexel1Df; + break; + case 2: + texImage->FetchTexelc = texImage->TexFormat->FetchTexel2D; + texImage->FetchTexelf = texImage->TexFormat->FetchTexel2Df; + break; + case 3: + texImage->FetchTexelc = texImage->TexFormat->FetchTexel3D; + texImage->FetchTexelf = texImage->TexFormat->FetchTexel3Df; + break; + default: + assert(0); + break; + } + + texelBytes = texImage->TexFormat->TexelBytes; + + + /* Minimum pitch of 32 bytes */ + if (postConvWidth * texelBytes < 32) { + postConvWidth = 32 / texelBytes; + texImage->RowStride = postConvWidth; + } + + assert(texImage->RowStride == postConvWidth); + + /* Release the reference to a potentially orphaned buffer. + * Release any old malloced memory. + */ + if (intelImage->mt) { + intel_miptree_release(intel, &intelImage->mt); + assert(!texImage->Data); + } + else if (texImage->Data) { + free(texImage->Data); + } + + /* If this is the only texture image in the tree, could call + * bmBufferData with NULL data to free the old block and avoid + * waiting on any outstanding fences. + */ + if (intelObj->mt && + intelObj->mt->first_level == level && + intelObj->mt->last_level == level && + intelObj->mt->target != GL_TEXTURE_CUBE_MAP_ARB && + !intel_miptree_match_image(intelObj->mt, &intelImage->base, + intelImage->face, intelImage->level)) { + + DBG("release it\n"); + intel_miptree_release(intel, &intelObj->mt); + assert(!intelObj->mt); + } + + if (!intelObj->mt) { + guess_and_alloc_mipmap_tree(intel, intelObj, intelImage); + if (!intelObj->mt) { + if (INTEL_DEBUG & DEBUG_TEXTURE) + _mesa_printf("guess_and_alloc_mipmap_tree: failed\n"); + } + } + + + if (intelObj->mt && + intelObj->mt != intelImage->mt && + intel_miptree_match_image(intelObj->mt, &intelImage->base, + intelImage->face, intelImage->level)) { + + if (intelImage->mt) { + intel_miptree_release(intel, &intelImage->mt); + } + + intel_miptree_reference(&intelImage->mt, intelObj->mt); + assert(intelImage->mt); + } + + if (!intelImage->mt) { + if (INTEL_DEBUG & DEBUG_TEXTURE) + _mesa_printf("XXX: Image did not fit into tree - storing in local memory!\n"); + } + + + /* Attempt to use the blitter for PBO image uploads: + * + * Next step would be texturing directly from PBO's. + */ + if (dims <= 2 && + intelImage->mt && + intel_buffer_object(unpack->BufferObj)) { + + _mesa_printf("trying pbo upload\n"); + + if (try_pbo_upload(intel, intelImage, unpack, + internalFormat, + width, height, format, type, pixels)) { + _mesa_printf("pbo upload succeeded\n"); + return; + } + + _mesa_printf("pbo upload failed\n"); + } + + + + /* intelCopyTexImage calls this function with pixels == NULL, with + * the expectation that the mipmap tree will be set up but nothing + * more will be done. This is where those calls return: + */ + pixels = _mesa_validate_pbo_teximage(ctx, dims, width, height, 1, + format, type, + pixels, unpack, "glTexImage"); + if (!pixels) + return; + + + LOCK_HARDWARE(intel); + + if (intelImage->mt) { + texImage->Data = intel_miptree_image_map(intel, + intelImage->mt, + intelImage->face, + intelImage->level, + &dstRowStride, + intelImage->base.ImageOffsets); + } + else { + /* Allocate regular memory and store the image there temporarily. */ + if (texImage->IsCompressed) { + sizeInBytes = texImage->CompressedSize; + dstRowStride = _mesa_compressed_row_stride(texImage->InternalFormat,width); + assert(dims != 3); + } + else { + dstRowStride = postConvWidth * texelBytes; + sizeInBytes = depth * dstRowStride * postConvHeight; + } + + texImage->Data = malloc(sizeInBytes); + } + + if (INTEL_DEBUG & DEBUG_TEXTURE) + _mesa_printf("Upload image %dx%dx%d row_len %x " + "pitch %x\n", + width, height, depth, + width * texelBytes, dstRowStride); + + /* Copy data. Would like to know when it's ok for us to eg. use + * the blitter to copy. Or, use the hardware to do the format + * conversion and copy: + */ + if (!texImage->TexFormat->StoreImage(ctx, dims, + texImage->_BaseFormat, + texImage->TexFormat, + texImage->Data, + 0, 0, 0, /* dstX/Y/Zoffset */ + dstRowStride, + texImage->ImageOffsets, + width, height, depth, + format, type, pixels, unpack)) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage"); + } + + _mesa_unmap_teximage_pbo(ctx, unpack); + + if (intelImage->mt) { + intel_miptree_image_unmap(intel, intelImage->mt); + texImage->Data = NULL; + } + + UNLOCK_HARDWARE(intel); + +#if 0 + /* GL_SGIS_generate_mipmap -- this can be accelerated now. + */ + if (level == texObj->BaseLevel && + texObj->GenerateMipmap) { + intel_generate_mipmap(ctx, target, + &ctx->Texture.Unit[ctx->Texture.CurrentUnit], + texObj); + } +#endif +} + +void intelTexImage3D(GLcontext *ctx, + GLenum target, GLint level, + GLint internalFormat, + GLint width, GLint height, GLint depth, + GLint border, + GLenum format, GLenum type, const void *pixels, + const struct gl_pixelstore_attrib *unpack, + struct gl_texture_object *texObj, + struct gl_texture_image *texImage) +{ + intelTexImage( ctx, 3, target, level, + internalFormat, width, height, depth, border, + format, type, pixels, + unpack, texObj, texImage ); +} + + +void intelTexImage2D(GLcontext *ctx, + GLenum target, GLint level, + GLint internalFormat, + GLint width, GLint height, GLint border, + GLenum format, GLenum type, const void *pixels, + const struct gl_pixelstore_attrib *unpack, + struct gl_texture_object *texObj, + struct gl_texture_image *texImage) +{ + intelTexImage( ctx, 2, target, level, + internalFormat, width, height, 1, border, + format, type, pixels, + unpack, texObj, texImage ); +} + +void intelTexImage1D(GLcontext *ctx, + GLenum target, GLint level, + GLint internalFormat, + GLint width, GLint border, + GLenum format, GLenum type, const void *pixels, + const struct gl_pixelstore_attrib *unpack, + struct gl_texture_object *texObj, + struct gl_texture_image *texImage) +{ + intelTexImage( ctx, 1, target, level, + internalFormat, width, 1, 1, border, + format, type, pixels, + unpack, texObj, texImage ); +} + + + +/** + * Need to map texture image into memory before copying image data, + * then unmap it. + */ +void intelGetTexImage( GLcontext *ctx, GLenum target, GLint level, + GLenum format, GLenum type, GLvoid *pixels, + struct gl_texture_object *texObj, + struct gl_texture_image *texImage ) +{ + struct intel_context *intel = intel_context( ctx ); + struct intel_texture_image *intelImage = intel_texture_image(texImage); + + /* Map */ + if (intelImage->mt) { + /* Image is stored in hardware format in a buffer managed by the + * kernel. Need to explicitly map and unmap it. + */ + intelImage->base.Data = + intel_miptree_image_map(intel, + intelImage->mt, + intelImage->face, + intelImage->level, + &intelImage->base.RowStride, + intelImage->base.ImageOffsets); + } + else { + /* Otherwise, the image should actually be stored in + * intelImage->base.Data. This is pretty confusing for + * everybody, I'd much prefer to separate the two functions of + * texImage->Data - storage for texture images in main memory + * and access (ie mappings) of images. In other words, we'd + * create a new texImage->Map field and leave Data simply for + * storage. + */ + assert(intelImage->base.Data); + } + + _mesa_get_teximage(ctx, target, level, format, type, pixels, + texObj, texImage); + + /* Unmap */ + if (intelImage->mt) { + intel_miptree_image_unmap(intel, intelImage->mt); + intelImage->base.Data = NULL; + } +} + |