From 2fc9351ee768ce0c123ce1d9b210dc6958ab41b2 Mon Sep 17 00:00:00 2001 From: Vladimir Dergachev Date: Mon, 3 Jan 2005 05:44:20 +0000 Subject: Port texture allocation code from R200. Hook it up, so lesson06 displays red colored textures. --- src/mesa/drivers/dri/r300/Makefile | 4 + src/mesa/drivers/dri/r300/r300_cmdbuf.c | 63 ++ src/mesa/drivers/dri/r300/r300_context.c | 38 +- src/mesa/drivers/dri/r300/r300_context.h | 86 +- src/mesa/drivers/dri/r300/r300_ioctl.c | 189 ++++ src/mesa/drivers/dri/r300/r300_ioctl.h | 12 + src/mesa/drivers/dri/r300/r300_render.c | 28 +- src/mesa/drivers/dri/r300/r300_tex.c | 1055 ++++++++++++++++++++++ src/mesa/drivers/dri/r300/r300_tex.h | 51 ++ src/mesa/drivers/dri/r300/r300_texmem.c | 490 ++++++++++ src/mesa/drivers/dri/r300/r300_texstate.c | 1390 +++++++++++++++++++++++++++++ 11 files changed, 3393 insertions(+), 13 deletions(-) create mode 100644 src/mesa/drivers/dri/r300/r300_tex.c create mode 100644 src/mesa/drivers/dri/r300/r300_tex.h create mode 100644 src/mesa/drivers/dri/r300/r300_texmem.c create mode 100644 src/mesa/drivers/dri/r300/r300_texstate.c (limited to 'src/mesa/drivers/dri/r300') diff --git a/src/mesa/drivers/dri/r300/Makefile b/src/mesa/drivers/dri/r300/Makefile index ef25526425..cfe81760f2 100644 --- a/src/mesa/drivers/dri/r300/Makefile +++ b/src/mesa/drivers/dri/r300/Makefile @@ -33,6 +33,9 @@ DRIVER_SOURCES = \ r300_state.c \ r300_render.c \ r300_lib.c \ + r300_texmem.c \ + r300_tex.c \ + r300_texstate.c \ \ r200_context.c \ r200_ioctl.c \ @@ -52,6 +55,7 @@ DRIVER_SOURCES = \ r200_vtxfmt_sse.c \ r200_vtxfmt_x86.c + C_SOURCES = $(COMMON_SOURCES) $(DRIVER_SOURCES) X86_SOURCES = r200_vtxtmp_x86.S diff --git a/src/mesa/drivers/dri/r300/r300_cmdbuf.c b/src/mesa/drivers/dri/r300/r300_cmdbuf.c index 82ad315273..e2bb2709af 100644 --- a/src/mesa/drivers/dri/r300/r300_cmdbuf.c +++ b/src/mesa/drivers/dri/r300/r300_cmdbuf.c @@ -46,6 +46,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "radeon_ioctl.h" #include "r300_context.h" #include "r300_ioctl.h" +#include "radeon_reg.h" #include "r300_reg.h" #include "r300_cmdbuf.h" @@ -545,3 +546,65 @@ void r300DestroyCmdBuf(r300ContextPtr r300) } } +void r300EmitBlit(r300ContextPtr rmesa, + GLuint color_fmt, + GLuint src_pitch, + GLuint src_offset, + GLuint dst_pitch, + GLuint dst_offset, + GLint srcx, GLint srcy, + GLint dstx, GLint dsty, GLuint w, GLuint h) +{ + drm_radeon_cmd_header_t *cmd; + + if (RADEON_DEBUG & DEBUG_IOCTL) + fprintf(stderr, + "%s src %x/%x %d,%d dst: %x/%x %d,%d sz: %dx%d\n", + __FUNCTION__, src_pitch, src_offset, srcx, srcy, + dst_pitch, dst_offset, dstx, dsty, w, h); + + assert((src_pitch & 63) == 0); + assert((dst_pitch & 63) == 0); + assert((src_offset & 1023) == 0); + assert((dst_offset & 1023) == 0); + assert(w < (1 << 16)); + assert(h < (1 << 16)); + + cmd = + (drm_radeon_cmd_header_t *) r200AllocCmdBuf(rmesa, 8 * sizeof(int), + __FUNCTION__); + + cmd[0].header.cmd_type = RADEON_CMD_PACKET3; + cmd[1].i = R200_CP_CMD_BITBLT_MULTI | (5 << 16); + cmd[2].i = (RADEON_GMC_SRC_PITCH_OFFSET_CNTL | + RADEON_GMC_DST_PITCH_OFFSET_CNTL | + RADEON_GMC_BRUSH_NONE | + (color_fmt << 8) | + RADEON_GMC_SRC_DATATYPE_COLOR | + RADEON_ROP3_S | + RADEON_DP_SRC_SOURCE_MEMORY | + RADEON_GMC_CLR_CMP_CNTL_DIS | RADEON_GMC_WR_MSK_DIS); + + cmd[3].i = ((src_pitch / 64) << 22) | (src_offset >> 10); + cmd[4].i = ((dst_pitch / 64) << 22) | (dst_offset >> 10); + cmd[5].i = (srcx << 16) | srcy; + cmd[6].i = (dstx << 16) | dsty; /* dst */ + cmd[7].i = (w << 16) | h; +} + +void r300EmitWait(r300ContextPtr rmesa, GLuint flags) +{ + if (rmesa->radeon.dri.drmMinor >= 6) { + drm_radeon_cmd_header_t *cmd; + + assert(!(flags & ~(RADEON_WAIT_2D | RADEON_WAIT_3D))); + + cmd = + (drm_radeon_cmd_header_t *) r200AllocCmdBuf(rmesa, + 1 * sizeof(int), + __FUNCTION__); + cmd[0].i = 0; + cmd[0].wait.cmd_type = RADEON_CMD_WAIT; + cmd[0].wait.flags = flags; + } +} diff --git a/src/mesa/drivers/dri/r300/r300_context.c b/src/mesa/drivers/dri/r300/r300_context.c index 9831dc60a4..88a67ce57c 100644 --- a/src/mesa/drivers/dri/r300/r300_context.c +++ b/src/mesa/drivers/dri/r300/r300_context.c @@ -57,6 +57,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "r300_cmdbuf.h" #include "r300_state.h" #include "r300_ioctl.h" +#include "r300_tex.h" #include "vblank.h" #include "utils.h" @@ -147,7 +148,7 @@ GLboolean r300CreateContext(const __GLcontextModes * glVisual, struct dd_function_table functions; r300ContextPtr r300; GLcontext *ctx; - int tcl_mode; + int tcl_mode, i; assert(glVisual); assert(driContextPriv); @@ -171,7 +172,7 @@ GLboolean r300CreateContext(const __GLcontextModes * glVisual, _mesa_init_driver_functions(&functions); r300InitIoctlFuncs(&functions); r300InitStateFuncs(&functions); - //r200InitTextureFuncs(&functions); + r300InitTextureFuncs(&functions); if (!radeonInitContext(&r300->radeon, &functions, glVisual, driContextPriv, sharedContextPrivate)) { @@ -180,6 +181,35 @@ GLboolean r300CreateContext(const __GLcontextModes * glVisual, } /* Init r300 context data */ + r300->dma.buf0_address = r300->radeon.radeonScreen->buffers->list[0].address; + + (void)memset(r300->texture_heaps, 0, sizeof(r300->texture_heaps)); + make_empty_list(&r300->swapped); + + r300->nr_heaps = 1 /* screen->numTexHeaps */ ; + assert(r300->nr_heaps < R200_NR_TEX_HEAPS); + for (i = 0; i < r300->nr_heaps; i++) { + r300->texture_heaps[i] = driCreateTextureHeap(i, r300, + screen-> + texSize[i], 12, + RADEON_NR_TEX_REGIONS, + (drmTextureRegionPtr) + r300->radeon.sarea-> + tex_list[i], + &r300->radeon.sarea-> + tex_age[i], + &r300->swapped, + sizeof + (r300TexObj), + (destroy_texture_object_t + *) + r300DestroyTexObj); + } + r300->texture_depth = driQueryOptioni(&r300->radeon.optionCache, + "texture_depth"); + if (r300->texture_depth == DRI_CONF_TEXTURE_DEPTH_FB) + r300->texture_depth = (screen->cpp == 4) ? + DRI_CONF_TEXTURE_DEPTH_32 : DRI_CONF_TEXTURE_DEPTH_16; /* Set the maximum texture size small enough that we can guarentee that * all texture units can bind a maximal texture and have them both in @@ -239,8 +269,8 @@ GLboolean r300CreateContext(const __GLcontextModes * glVisual, #if 0 /* plug in a few more device driver functions */ /* XXX these should really go right after _mesa_init_driver_functions() */ - r200InitPixelFuncs(ctx); - r200InitSwtcl(ctx); + r300InitPixelFuncs(ctx); + r300InitSwtcl(ctx); #endif TNL_CONTEXT(ctx)->Driver.RunPipeline = _tnl_run_pipeline; diff --git a/src/mesa/drivers/dri/r300/r300_context.h b/src/mesa/drivers/dri/r300/r300_context.h index b5d821225a..54826ea801 100644 --- a/src/mesa/drivers/dri/r300/r300_context.h +++ b/src/mesa/drivers/dri/r300/r300_context.h @@ -66,6 +66,44 @@ static __inline__ uint32_t r300PackFloat32(float fl) return u.u; } + +/************ DMA BUFFERS **************/ + +/* Need refcounting on dma buffers: + */ +struct r300_dma_buffer { + int refcount; /* the number of retained regions in buf */ + drmBufPtr buf; +}; + +#define GET_START(rvb) (rmesa->radeon.radeonScreen->gart_buffer_offset + \ + (rvb)->address - rmesa->dma.buf0_address + \ + (rvb)->start) + +/* A retained region, eg vertices for indexed vertices. + */ +struct r300_dma_region { + struct r300_dma_buffer *buf; + char *address; /* == buf->address */ + int start, end, ptr; /* offsets from start of buf */ + int aos_start; + int aos_stride; + int aos_size; +}; + +struct r300_dma { + /* Active dma region. Allocations for vertices and retained + * regions come from here. Also used for emitting random vertices, + * these may be flushed by calling flush_current(); + */ + struct r300_dma_region current; + + void (*flush) (r300ContextPtr); + + char *buf0_address; /* start of buf[0], for index calcs */ + GLuint nr_released_bufs; /* flush after so many buffers released */ +}; + /* Texture related */ #define TEX_0 0x1 @@ -74,8 +112,8 @@ static __inline__ uint32_t r300PackFloat32(float fl) #define TEX_3 0x8 #define TEX_4 0x10 #define TEX_5 0x20 -#define TEX_6 0x20 -#define TEX_7 0x20 +#define TEX_6 0x40 +#define TEX_7 0x80 #define TEX_ALL 0xff typedef struct r300_tex_obj r300TexObj, *r300TexObjPtr; @@ -114,6 +152,17 @@ struct r300_tex_obj { GLboolean border_fallback; }; +struct r300_texture_env_state { + r300TexObjPtr texobj; + GLenum format; + GLenum envMode; +}; + +#define R300_MAX_TEXTURE_UNITS 6 + +struct r300_texture_state { + struct r300_texture_env_state unit[R300_MAX_TEXTURE_UNITS]; +}; /** * A block of hardware state. @@ -402,6 +451,7 @@ struct r300_depthbuffer_state { struct r300_state { struct r300_depthbuffer_state depth; + struct r300_texture_state texture; }; @@ -419,6 +469,38 @@ struct r300_context { int elt_count; /* size of the buffer for vertices */ int attrib_count; /* size of the buffer for vertex attributes.. Somehow it can be different ? */ + + /* Vertex buffers + */ + #if 0 /* we'll need it later, but not now */ + struct r300_ioctl ioctl; + #endif + struct r300_dma dma; + GLboolean save_on_next_unlock; + + /* Texture object bookkeeping + */ + unsigned nr_heaps; + driTexHeap *texture_heaps[R200_NR_TEX_HEAPS]; + driTextureObject swapped; + int texture_depth; + float initialMaxAnisotropy; + + /* Clientdata textures; + */ + GLuint prefer_gart_client_texturing; + + /* TCL stuff + */ + GLmatrix TexGenMatrix[R300_MAX_TEXTURE_UNITS]; + GLboolean recheck_texgen[R300_MAX_TEXTURE_UNITS]; + GLboolean TexGenNeedNormals[R300_MAX_TEXTURE_UNITS]; + GLuint TexMatEnabled; + GLuint TexMatCompSel; + GLuint TexGenEnabled; + GLuint TexGenInputs; + GLuint TexGenCompSel; + GLmatrix tmpmat; }; #define R300_CONTEXT(ctx) ((r300ContextPtr)(ctx->DriverCtx)) diff --git a/src/mesa/drivers/dri/r300/r300_ioctl.c b/src/mesa/drivers/dri/r300/r300_ioctl.c index 63a7d540e4..ffadaff38f 100644 --- a/src/mesa/drivers/dri/r300/r300_ioctl.c +++ b/src/mesa/drivers/dri/r300/r300_ioctl.c @@ -282,6 +282,195 @@ void r300Flush(GLcontext * ctx) r300FlushCmdBuf(r300, __FUNCTION__); } +void r300RefillCurrentDmaRegion(r300ContextPtr rmesa) +{ + struct r200_dma_buffer *dmabuf; + int fd = rmesa->radeon.dri.fd; + int index = 0; + int size = 0; + drmDMAReq dma; + int ret; + + if (RADEON_DEBUG & (DEBUG_IOCTL | DEBUG_DMA)) + fprintf(stderr, "%s\n", __FUNCTION__); + + if (rmesa->dma.flush) { + rmesa->dma.flush(rmesa); + } + + if (rmesa->dma.current.buf) + r300ReleaseDmaRegion(rmesa, &rmesa->dma.current, __FUNCTION__); + + if (rmesa->dma.nr_released_bufs > 4) + r300FlushCmdBuf(rmesa, __FUNCTION__); + + dma.context = rmesa->radeon.dri.hwContext; + dma.send_count = 0; + dma.send_list = NULL; + dma.send_sizes = NULL; + dma.flags = 0; + dma.request_count = 1; + dma.request_size = RADEON_BUFFER_SIZE; + dma.request_list = &index; + dma.request_sizes = &size; + dma.granted_count = 0; + + LOCK_HARDWARE(&rmesa->radeon); /* no need to validate */ + + while (1) { + ret = drmDMA(fd, &dma); + if (ret == 0) + break; + + if (rmesa->dma.nr_released_bufs) { + r200FlushCmdBufLocked(rmesa, __FUNCTION__); + } + + if (rmesa->radeon.do_usleeps) { + UNLOCK_HARDWARE(&rmesa->radeon); + DO_USLEEP(1); + LOCK_HARDWARE(&rmesa->radeon); + } + } + + UNLOCK_HARDWARE(&rmesa->radeon); + + if (RADEON_DEBUG & DEBUG_DMA) + fprintf(stderr, "Allocated buffer %d\n", index); + + dmabuf = CALLOC_STRUCT(r300_dma_buffer); + dmabuf->buf = &rmesa->radeon.radeonScreen->buffers->list[index]; + dmabuf->refcount = 1; + + rmesa->dma.current.buf = dmabuf; + rmesa->dma.current.address = dmabuf->buf->address; + rmesa->dma.current.end = dmabuf->buf->total; + rmesa->dma.current.start = 0; + rmesa->dma.current.ptr = 0; +} + +void r300ReleaseDmaRegion(r300ContextPtr rmesa, + struct r300_dma_region *region, const char *caller) +{ + if (RADEON_DEBUG & DEBUG_IOCTL) + fprintf(stderr, "%s from %s\n", __FUNCTION__, caller); + + if (!region->buf) + return; + + if (rmesa->dma.flush) + rmesa->dma.flush(rmesa); + + if (--region->buf->refcount == 0) { + drm_radeon_cmd_header_t *cmd; + + if (RADEON_DEBUG & (DEBUG_IOCTL | DEBUG_DMA)) + fprintf(stderr, "%s -- DISCARD BUF %d\n", __FUNCTION__, + region->buf->buf->idx); + + cmd = + (drm_radeon_cmd_header_t *) r300AllocCmdBuf(rmesa, + sizeof(*cmd), + __FUNCTION__); + cmd->dma.cmd_type = RADEON_CMD_DMA_DISCARD; + cmd->dma.buf_idx = region->buf->buf->idx; + FREE(region->buf); + rmesa->dma.nr_released_bufs++; + } + + region->buf = 0; + region->start = 0; +} + +/* Allocates a region from rmesa->dma.current. If there isn't enough + * space in current, grab a new buffer (and discard what was left of current) + */ +void r300AllocDmaRegion(r300ContextPtr rmesa, + struct r300_dma_region *region, + int bytes, int alignment) +{ + if (RADEON_DEBUG & DEBUG_IOCTL) + fprintf(stderr, "%s %d\n", __FUNCTION__, bytes); + + if (rmesa->dma.flush) + rmesa->dma.flush(rmesa); + + if (region->buf) + r300ReleaseDmaRegion(rmesa, region, __FUNCTION__); + + alignment--; + rmesa->dma.current.start = rmesa->dma.current.ptr = + (rmesa->dma.current.ptr + alignment) & ~alignment; + + if (rmesa->dma.current.ptr + bytes > rmesa->dma.current.end) + r300RefillCurrentDmaRegion(rmesa); + + region->start = rmesa->dma.current.start; + region->ptr = rmesa->dma.current.start; + region->end = rmesa->dma.current.start + bytes; + region->address = rmesa->dma.current.address; + region->buf = rmesa->dma.current.buf; + region->buf->refcount++; + + rmesa->dma.current.ptr += bytes; /* bug - if alignment > 7 */ + rmesa->dma.current.start = + rmesa->dma.current.ptr = (rmesa->dma.current.ptr + 0x7) & ~0x7; + + assert(rmesa->dma.current.ptr <= rmesa->dma.current.end); +} + +/* Called via glXGetMemoryOffsetMESA() */ +GLuint r300GetMemoryOffsetMESA(__DRInativeDisplay * dpy, int scrn, + const GLvoid * pointer) +{ + GET_CURRENT_CONTEXT(ctx); + r300ContextPtr rmesa; + GLuint card_offset; + + if (!ctx || !(rmesa = R300_CONTEXT(ctx))) { + fprintf(stderr, "%s: no context\n", __FUNCTION__); + return ~0; + } + + if (!r300IsGartMemory(rmesa, pointer, 0)) + return ~0; + + if (rmesa->radeon.dri.drmMinor < 6) + return ~0; + + card_offset = r300GartOffsetFromVirtual(rmesa, pointer); + + return card_offset - rmesa->radeon.radeonScreen->gart_base; +} + +GLboolean r300IsGartMemory(r300ContextPtr rmesa, const GLvoid * pointer, + GLint size) +{ + int offset = + (char *)pointer - (char *)rmesa->radeon.radeonScreen->gartTextures.map; + int valid = (size >= 0 && offset >= 0 + && offset + size < rmesa->radeon.radeonScreen->gartTextures.size); + + if (RADEON_DEBUG & DEBUG_IOCTL) + fprintf(stderr, "r300IsGartMemory( %p ) : %d\n", pointer, + valid); + + return valid; +} + +GLuint r300GartOffsetFromVirtual(r300ContextPtr rmesa, const GLvoid * pointer) +{ + int offset = + (char *)pointer - (char *)rmesa->radeon.radeonScreen->gartTextures.map; + + fprintf(stderr, "offset=%08x\n", offset); + + if (offset < 0 || offset > rmesa->radeon.radeonScreen->gartTextures.size) + return ~0; + else + return rmesa->radeon.radeonScreen->gart_texture_offset + offset; +} + void r300InitIoctlFuncs(struct dd_function_table *functions) { functions->Clear = r300Clear; diff --git a/src/mesa/drivers/dri/r300/r300_ioctl.h b/src/mesa/drivers/dri/r300/r300_ioctl.h index 26ca07cce2..9a2ece5c70 100644 --- a/src/mesa/drivers/dri/r300/r300_ioctl.h +++ b/src/mesa/drivers/dri/r300/r300_ioctl.h @@ -36,6 +36,18 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifndef __R300_IOCTL_H__ #define __R300_IOCTL_H__ +#include "r300_context.h" +#include "radeon_drm.h" + +extern GLuint r300GetMemoryOffsetMESA(__DRInativeDisplay * dpy, int scrn, + const GLvoid * pointer); + +extern GLboolean r300IsGartMemory(r300ContextPtr rmesa, const GLvoid * pointer, + GLint size); + +extern GLuint r300GartOffsetFromVirtual(r300ContextPtr rmesa, + const GLvoid * pointer); + extern void r300Flush(GLcontext * ctx); extern void r300InitIoctlFuncs(struct dd_function_table *functions); diff --git a/src/mesa/drivers/dri/r300/r300_render.c b/src/mesa/drivers/dri/r300/r300_render.c index dbc18a834d..c16a960a5d 100644 --- a/src/mesa/drivers/dri/r300/r300_render.c +++ b/src/mesa/drivers/dri/r300/r300_render.c @@ -458,12 +458,14 @@ static void r300_render_tex_primitive(r300ContextPtr rmesa, type=r300_get_primitive_type(rmesa, ctx, start, end, prim); + #if 1 fprintf(stderr,"ObjPtr: size=%d stride=%d\n", VB->ObjPtr->size, VB->ObjPtr->stride); fprintf(stderr,"ColorPtr[0]: size=%d stride=%d\n", VB->ColorPtr[0]->size, VB->ColorPtr[0]->stride); fprintf(stderr,"TexCoordPtr[0]: size=%d stride=%d\n", VB->TexCoordPtr[0]->size, VB->TexCoordPtr[0]->stride); + #endif if(type<0)return; @@ -508,16 +510,21 @@ static GLboolean r300_run_tex_render(GLcontext *ctx, AOS_DATA vb_arrays[3]; /* Only do 2d textures */ struct gl_texture_object *to=ctx->Texture.Unit[0].Current2D; - radeonScreenPtr rsp=rmesa->radeon.radeonScreen; + r300TexObjPtr t=to->DriverData; LOCAL_VARS + /* Update texture state - needs to be done only when actually changed.. + All the time for now.. */ + r300UpdateTextureState(ctx); + /* Flush state - make sure command buffer is nice and large */ r300Flush(ctx); //fprintf(stderr, "You can enable texture drawing in %s:%s \n", __FILE__, __FUNCTION__); //return GL_TRUE; - + + if (RADEON_DEBUG == DEBUG_PRIMS) fprintf(stderr, "%s\n", __FUNCTION__); @@ -549,8 +556,6 @@ static GLboolean r300_run_tex_render(GLcontext *ctx, vb_arrays[2].ncomponents=4; vb_arrays[2].reg=REG_TEX0; - /* Fill texture with some random data */ - for(i=0;i<100000;i++)((int *)(rsp->gartTextures.map))[i]=rand(); /* needed before starting 3d operation .. */ reg_start(R300_RB3D_DSTCACHE_CTLSTAT,0); @@ -578,8 +583,16 @@ static GLboolean r300_run_tex_render(GLcontext *ctx, SINGLE_TEXTURE_PIPELINE.vertex_shader.unknown2.body.f[2]=1.0; SINGLE_TEXTURE_PIPELINE.vertex_shader.unknown2.body.f[3]=0.0; - /* Put it in the beginning of texture memory */ - SINGLE_TEXTURE_PIPELINE.texture_unit[0].offset=rsp->gartTextures.handle; + /* Use actual texture offset */ + + SINGLE_TEXTURE_PIPELINE.texture_unit[0].offset=rmesa->radeon.radeonScreen->fbLocation+t->offset; + #if 0 + SINGLE_TEXTURE_PIPELINE.texture_unit[0].format=t->format; + SINGLE_TEXTURE_PIPELINE.texture_unit[0].size=t->size; + #endif + SINGLE_TEXTURE_PIPELINE.texture_unit[0].filter=t->filter; + SINGLE_TEXTURE_PIPELINE.texture_unit[0].unknown1=t->pitch; /* Unknown 1 is pitch ! */ + SINGLE_TEXTURE_PIPELINE.texture_unit[0].filter=t->filter; /* Upload texture, a hack, really we can do a lot better */ @@ -644,6 +657,7 @@ reg_start(R300_RS_INTERP_0,7); reg_start(0x4f18,0); e32(0x00000003); +// exit(-1); fprintf(stderr, "\n"); return GL_FALSE; } @@ -671,7 +685,7 @@ static GLboolean r300_run_render(GLcontext *ctx, if(ctx->Texture.Unit[0].Enabled) return r300_run_tex_render(ctx, stage); else - return r300_run_flat_render(ctx, stage); + return r300_run_vb_flat_render(ctx, stage); #else return GL_TRUE; #endif diff --git a/src/mesa/drivers/dri/r300/r300_tex.c b/src/mesa/drivers/dri/r300/r300_tex.c new file mode 100644 index 0000000000..bdbdc0b890 --- /dev/null +++ b/src/mesa/drivers/dri/r300/r300_tex.c @@ -0,0 +1,1055 @@ +/* $XFree86: xc/lib/GL/mesa/src/drv/r300/r300_tex.c,v 1.2 2002/11/05 17:46:08 tsi Exp $ */ +/* +Copyright (C) The Weather Channel, Inc. 2002. All Rights Reserved. + +The Weather Channel (TM) funded Tungsten Graphics to develop the +initial release of the Radeon 8500 driver under the XFree86 license. +This notice must be preserved. + +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, sublicense, 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 NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT OWNER(S) 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. +*/ + +/* + * Authors: + * Keith Whitwell + */ + +#include "glheader.h" +#include "imports.h" +#include "colormac.h" +#include "context.h" +#include "enums.h" +#include "image.h" +#include "simple_list.h" +#include "texformat.h" +#include "texstore.h" +#include "texmem.h" +#include "teximage.h" +#include "texobj.h" + +#include "r300_context.h" +#include "r300_state.h" +#include "r300_ioctl.h" +//#include "r300_swtcl.h" +#include "r300_tex.h" + +#include "xmlpool.h" + +/** + * Set the texture wrap modes. + * + * \param t Texture object whose wrap modes are to be set + * \param swrap Wrap mode for the \a s texture coordinate + * \param twrap Wrap mode for the \a t texture coordinate + */ + +static void r300SetTexWrap(r300TexObjPtr t, GLenum swrap, GLenum twrap, + GLenum rwrap) +{ + GLboolean is_clamp = GL_FALSE; + GLboolean is_clamp_to_border = GL_FALSE; + + t->filter &= + ~(R200_CLAMP_S_MASK | R200_CLAMP_T_MASK | R200_BORDER_MODE_D3D); + + switch (swrap) { + case GL_REPEAT: + t->filter |= R200_CLAMP_S_WRAP; + break; + case GL_CLAMP: + t->filter |= R200_CLAMP_S_CLAMP_GL; + is_clamp = GL_TRUE; + break; + case GL_CLAMP_TO_EDGE: + t->filter |= R200_CLAMP_S_CLAMP_LAST; + break; + case GL_CLAMP_TO_BORDER: + t->filter |= R200_CLAMP_S_CLAMP_GL; + is_clamp_to_border = GL_TRUE; + break; + case GL_MIRRORED_REPEAT: + t->filter |= R200_CLAMP_S_MIRROR; + break; + case GL_MIRROR_CLAMP_EXT: + t->filter |= R200_CLAMP_S_MIRROR_CLAMP_GL; + is_clamp = GL_TRUE; + break; + case GL_MIRROR_CLAMP_TO_EDGE_EXT: + t->filter |= R200_CLAMP_S_MIRROR_CLAMP_LAST; + break; + case GL_MIRROR_CLAMP_TO_BORDER_EXT: + t->filter |= R200_CLAMP_S_MIRROR_CLAMP_GL; + is_clamp_to_border = GL_TRUE; + break; + default: + _mesa_problem(NULL, "bad S wrap mode in %s", __FUNCTION__); + } + + switch (twrap) { + case GL_REPEAT: + t->filter |= R200_CLAMP_T_WRAP; + break; + case GL_CLAMP: + t->filter |= R200_CLAMP_T_CLAMP_GL; + is_clamp = GL_TRUE; + break; + case GL_CLAMP_TO_EDGE: + t->filter |= R200_CLAMP_T_CLAMP_LAST; + break; + case GL_CLAMP_TO_BORDER: + t->filter |= R200_CLAMP_T_CLAMP_GL | R200_BORDER_MODE_D3D; + is_clamp_to_border = GL_TRUE; + break; + case GL_MIRRORED_REPEAT: + t->filter |= R200_CLAMP_T_MIRROR; + break; + case GL_MIRROR_CLAMP_EXT: + t->filter |= R200_CLAMP_T_MIRROR_CLAMP_GL; + is_clamp = GL_TRUE; + break; + case GL_MIRROR_CLAMP_TO_EDGE_EXT: + t->filter |= R200_CLAMP_T_MIRROR_CLAMP_LAST; + break; + case GL_MIRROR_CLAMP_TO_BORDER_EXT: + t->filter |= R200_CLAMP_T_MIRROR_CLAMP_GL; + is_clamp_to_border = GL_TRUE; + break; + default: + _mesa_problem(NULL, "bad T wrap mode in %s", __FUNCTION__); + } + + #if 0 /* Which field is this ? */ + t->pp_txformat_x &= ~R200_CLAMP_Q_MASK; + + switch (rwrap) { + case GL_REPEAT: + t->pp_txformat_x |= R200_CLAMP_Q_WRAP; + break; + case GL_CLAMP: + t->pp_txformat_x |= R200_CLAMP_Q_CLAMP_GL; + is_clamp = GL_TRUE; + break; + case GL_CLAMP_TO_EDGE: + t->pp_txformat_x |= R200_CLAMP_Q_CLAMP_LAST; + break; + case GL_CLAMP_TO_BORDER: + t->pp_txformat_x |= R200_CLAMP_Q_CLAMP_GL; + is_clamp_to_border = GL_TRUE; + break; + case GL_MIRRORED_REPEAT: + t->pp_txformat_x |= R200_CLAMP_Q_MIRROR; + break; + case GL_MIRROR_CLAMP_EXT: + t->pp_txformat_x |= R200_CLAMP_Q_MIRROR_CLAMP_GL; + is_clamp = GL_TRUE; + break; + case GL_MIRROR_CLAMP_TO_EDGE_EXT: + t->pp_txformat_x |= R200_CLAMP_Q_MIRROR_CLAMP_LAST; + break; + case GL_MIRROR_CLAMP_TO_BORDER_EXT: + t->pp_txformat_x |= R200_CLAMP_Q_MIRROR_CLAMP_GL; + is_clamp_to_border = GL_TRUE; + break; + default: + _mesa_problem(NULL, "bad R wrap mode in %s", __FUNCTION__); + } + #endif + + if (is_clamp_to_border) { + t->filter |= R200_BORDER_MODE_D3D; + } + + t->border_fallback = (is_clamp && is_clamp_to_border); +} + +static void r300SetTexMaxAnisotropy(r300TexObjPtr t, GLfloat max) +{ + t->filter &= ~R200_MAX_ANISO_MASK; + + if (max == 1.0) { + t->filter |= R200_MAX_ANISO_1_TO_1; + } else if (max <= 2.0) { + t->filter |= R200_MAX_ANISO_2_TO_1; + } else if (max <= 4.0) { + t->filter |= R200_MAX_ANISO_4_TO_1; + } else if (max <= 8.0) { + t->filter |= R200_MAX_ANISO_8_TO_1; + } else { + t->filter |= R200_MAX_ANISO_16_TO_1; + } +} + +/** + * Set the texture magnification and minification modes. + * + * \param t Texture whose filter modes are to be set + * \param minf Texture minification mode + * \param magf Texture magnification mode + */ + +static void r300SetTexFilter(r300TexObjPtr t, GLenum minf, GLenum magf) +{ + GLuint anisotropy = (t->filter & R200_MAX_ANISO_MASK); + + t->filter &= ~(R200_MIN_FILTER_MASK | R200_MAG_FILTER_MASK); + #if 0 + t->pp_txformat_x &= ~R200_VOLUME_FILTER_MASK; + #endif + + if (anisotropy == R200_MAX_ANISO_1_TO_1) { + switch (minf) { + case GL_NEAREST: + t->filter |= R200_MIN_FILTER_NEAREST; + break; + case GL_LINEAR: + t->filter |= R200_MIN_FILTER_LINEAR; + break; + case GL_NEAREST_MIPMAP_NEAREST: + t->filter |= R200_MIN_FILTER_NEAREST_MIP_NEAREST; + break; + case GL_NEAREST_MIPMAP_LINEAR: + t->filter |= R200_MIN_FILTER_LINEAR_MIP_NEAREST; + break; + case GL_LINEAR_MIPMAP_NEAREST: + t->filter |= R200_MIN_FILTER_NEAREST_MIP_LINEAR; + break; + case GL_LINEAR_MIPMAP_LINEAR: + t->filter |= R200_MIN_FILTER_LINEAR_MIP_LINEAR; + break; + } + } else { + switch (minf) { + case GL_NEAREST: + t->filter |= R200_MIN_FILTER_ANISO_NEAREST; + break; + case GL_LINEAR: + t->filter |= R200_MIN_FILTER_ANISO_LINEAR; + break; + case GL_NEAREST_MIPMAP_NEAREST: + case GL_LINEAR_MIPMAP_NEAREST: + t->filter |= + R200_MIN_FILTER_ANISO_NEAREST_MIP_NEAREST; + break; + case GL_NEAREST_MIPMAP_LINEAR: + case GL_LINEAR_MIPMAP_LINEAR: + t->filter |= + R200_MIN_FILTER_ANISO_NEAREST_MIP_LINEAR; + break; + } + } + + /* Note we don't have 3D mipmaps so only use the mag filter setting + * to set the 3D texture filter mode. + */ + switch (magf) { + case GL_NEAREST: + t->filter |= R200_MAG_FILTER_NEAREST; + #if 0 + t->pp_txformat_x |= R200_VOLUME_FILTER_NEAREST; + #endif + break; + case GL_LINEAR: + t->filter |= R200_MAG_FILTER_LINEAR; + #if 0 + t->pp_txformat_x |= R200_VOLUME_FILTER_LINEAR; + #endif + break; + } +} + +static void r300SetTexBorderColor(r300TexObjPtr t, GLubyte c[4]) +{ + #if 0 + t->pp_border_color = radeonPackColor(4, c[0], c[1], c[2], c[3]); + #endif +} + +/** + * Allocate space for and load the mesa images into the texture memory block. + * This will happen before drawing with a new texture, or drawing with a + * texture after it was swapped out or teximaged again. + */ + +static r300TexObjPtr r300AllocTexObj(struct gl_texture_object *texObj) +{ + r300TexObjPtr t; + + t = CALLOC_STRUCT(r300_tex_obj); + texObj->DriverData = t; + if (t != NULL) { + if (RADEON_DEBUG & DEBUG_TEXTURE) { + fprintf(stderr, "%s( %p, %p )\n", __FUNCTION__, + (void *)texObj, (void *)t); + } + + /* Initialize non-image-dependent parts of the state: + */ + t->base.tObj = texObj; + t->border_fallback = GL_FALSE; + + make_empty_list(&t->base); + + r300SetTexWrap(t, texObj->WrapS, texObj->WrapT, texObj->WrapR); + r300SetTexMaxAnisotropy(t, texObj->MaxAnisotropy); + r300SetTexFilter(t, texObj->MinFilter, texObj->MagFilter); + r300SetTexBorderColor(t, texObj->_BorderChan); + } + + return t; +} + +static const struct gl_texture_format *r300ChooseTextureFormat(GLcontext * ctx, + GLint + internalFormat, + GLenum format, + GLenum type) +{ + r300ContextPtr rmesa = R300_CONTEXT(ctx); + const GLboolean do32bpt = + (rmesa->texture_depth == DRI_CONF_TEXTURE_DEPTH_32); + const GLboolean force16bpt = + (rmesa->texture_depth == DRI_CONF_TEXTURE_DEPTH_FORCE_16); + (void)format; + + switch (internalFormat) { + case 4: + case GL_RGBA: + case GL_COMPRESSED_RGBA: + switch (type) { + case GL_UNSIGNED_INT_10_10_10_2: + case GL_UNSIGNED_INT_2_10_10_10_REV: + return do32bpt ? _dri_texformat_argb8888 : + _dri_texformat_argb1555; + case GL_UNSIGNED_SHORT_4_4_4_4: + case GL_UNSIGNED_SHORT_4_4_4_4_REV: + return _dri_texformat_argb4444; + case GL_UNSIGNED_SHORT_5_5_5_1: + case GL_UNSIGNED_SHORT_1_5_5_5_REV: + return _dri_texformat_argb1555; + default: + return do32bpt ? _dri_texformat_rgba8888 : + _dri_texformat_argb4444; + } + + case 3: + case GL_RGB: + case GL_COMPRESSED_RGB: + switch (type) { + case GL_UNSIGNED_SHORT_4_4_4_4: + case GL_UNSIGNED_SHORT_4_4_4_4_REV: + return _dri_texformat_argb4444; + case GL_UNSIGNED_SHORT_5_5_5_1: + case GL_UNSIGNED_SHORT_1_5_5_5_REV: + return _dri_texformat_argb1555; + case GL_UNSIGNED_SHORT_5_6_5: + case GL_UNSIGNED_SHORT_5_6_5_REV: + return _dri_texformat_rgb565; + default: + return do32bpt ? _dri_texformat_rgba8888 : + _dri_texformat_rgb565; + } + + case GL_RGBA8: + case GL_RGB10_A2: + case GL_RGBA12: + case GL_RGBA16: + return !force16bpt ? + _dri_texformat_rgba8888 : _dri_texformat_argb4444; + + case GL_RGBA4: + case GL_RGBA2: + return _dri_texformat_argb4444; + + case GL_RGB5_A1: + return _dri_texformat_argb1555; + + case GL_RGB8: + case GL_RGB10: + case GL_RGB12: + case GL_RGB16: + return !force16bpt ? _dri_texformat_rgba8888 : + _dri_texformat_rgb565; + + case GL_RGB5: + case GL_RGB4: + case GL_R3_G3_B2: + return _dri_texformat_rgb565; + + case GL_ALPHA: + case GL_ALPHA4: + case GL_ALPHA8: + case GL_ALPHA12: + case GL_ALPHA16: + case GL_COMPRESSED_ALPHA: + return _dri_texformat_a8; + + case 1: + case GL_LUMINANCE: + case GL_LUMINANCE4: + case GL_LUMINANCE8: + case GL_LUMINANCE12: + case GL_LUMINANCE16: + case GL_COMPRESSED_LUMINANCE: + return _dri_texformat_l8; + + case 2: + case GL_LUMINANCE_ALPHA: + case GL_LUMINANCE4_ALPHA4: + case GL_LUMINANCE6_ALPHA2: + case GL_LUMINANCE8_ALPHA8: + case GL_LUMINANCE12_ALPHA4: + case GL_LUMINANCE12_ALPHA12: + case GL_LUMINANCE16_ALPHA16: + case GL_COMPRESSED_LUMINANCE_ALPHA: + return _dri_texformat_al88; + + case GL_INTENSITY: + case GL_INTENSITY4: + case GL_INTENSITY8: + case GL_INTENSITY12: + case GL_INTENSITY16: + case GL_COMPRESSED_INTENSITY: + return _dri_texformat_i8; + + case GL_YCBCR_MESA: + if (type == GL_UNSIGNED_SHORT_8_8_APPLE || + type == GL_UNSIGNED_BYTE) + return &_mesa_texformat_ycbcr; + else + return &_mesa_texformat_ycbcr_rev; + + default: + _mesa_problem(ctx, + "unexpected internalFormat 0x%x in r300ChooseTextureFormat", + (int)internalFormat); + return NULL; + } + + return NULL; /* never get here */ +} + +static GLboolean +r300ValidateClientStorage(GLcontext * ctx, GLenum target, + GLint internalFormat, + GLint srcWidth, GLint srcHeight, + GLenum format, GLenum type, const void *pixels, + const struct gl_pixelstore_attrib *packing, + struct gl_texture_object *texObj, + struct gl_texture_image *texImage) +{ + r300ContextPtr rmesa = R300_CONTEXT(ctx); + + if (0) + fprintf(stderr, "intformat %s format %s type %s\n", + _mesa_lookup_enum_by_nr(internalFormat), + _mesa_lookup_enum_by_nr(format), + _mesa_lookup_enum_by_nr(type)); + + if (!ctx->Unpack.ClientStorage) + return 0; + + if (ctx->_ImageTransferState || + texImage->IsCompressed || texObj->GenerateMipmap) + return 0; + + /* This list is incomplete, may be different on ppc??? + */ + switch (internalFormat) { + case GL_RGBA: + if (format == GL_BGRA && type == GL_UNSIGNED_INT_8_8_8_8_REV) { + texImage->TexFormat = _dri_texformat_argb8888; + } else + return 0; + break; + + case GL_RGB: + if (format == GL_RGB && type == GL_UNSIGNED_SHORT_5_6_5) { + texImage->TexFormat = _dri_texformat_rgb565; + } else + return 0; + break; + + case GL_YCBCR_MESA: + if (format == GL_YCBCR_MESA && + type == GL_UNSIGNED_SHORT_8_8_REV_APPLE) { + texImage->TexFormat = &_mesa_texformat_ycbcr_rev; + } else if (format == GL_YCBCR_MESA && + (type == GL_UNSIGNED_SHORT_8_8_APPLE || + type == GL_UNSIGNED_BYTE)) { + texImage->TexFormat = &_mesa_texformat_ycbcr; + } else + return 0; + break; + + default: + return 0; + } + + /* Could deal with these packing issues, but currently don't: + */ + if (packing->SkipPixels || + packing->SkipRows || packing->SwapBytes || packing->LsbFirst) { + return 0; + } + + { + GLint srcRowStride = _mesa_image_row_stride(packing, srcWidth, + format, type); + + if (0) + fprintf(stderr, "%s: srcRowStride %d/%x\n", + __FUNCTION__, srcRowStride, srcRowStride); + + /* Could check this later in upload, pitch restrictions could be + * relaxed, but would need to store the image pitch somewhere, + * as packing details might change before image is uploaded: + */ + if (!r300IsGartMemory(rmesa, pixels, srcHeight * srcRowStride) + || (srcRowStride & 63)) + return 0; + + /* Have validated that _mesa_transfer_teximage would be a straight + * memcpy at this point. NOTE: future calls to TexSubImage will + * overwrite the client data. This is explicitly mentioned in the + * extension spec. + */ + texImage->Data = (void *)pixels; + texImage->IsClientData = GL_TRUE; + texImage->RowStride = + srcRowStride / texImage->TexFormat->TexelBytes; + + return 1; + } +} + +static void r300TexImage1D(GLcontext * ctx, GLenum target, GLint level, + GLint internalFormat, + GLint width, GLint border, + GLenum format, GLenum type, const GLvoid * pixels, + const struct gl_pixelstore_attrib *packing, + struct gl_texture_object *texObj, + struct gl_texture_image *texImage) +{ + driTextureObject *t = (driTextureObject *) texObj->DriverData; + + if (t) { + driSwapOutTextureObject(t); + } else { + t = (driTextureObject *) r300AllocTexObj(texObj); + if (!t) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage1D"); + return; + } + } + + /* Note, this will call ChooseTextureFormat */ + _mesa_store_teximage1d(ctx, target, level, internalFormat, + width, border, format, type, pixels, + &ctx->Unpack, texObj, texImage); + + t->dirty_images[0] |= (1 << level); +} + +static void r300TexSubImage1D(GLcontext * ctx, GLenum target, GLint level, + GLint xoffset, + GLsizei width, + GLenum format, GLenum type, + const GLvoid * pixels, + const struct gl_pixelstore_attrib *packing, + struct gl_texture_object *texObj, + struct gl_texture_image *texImage) +{ + driTextureObject *t = (driTextureObject *) texObj->DriverData; + + assert(t); /* this _should_ be true */ + if (t) { + driSwapOutTextureObject(t); + } else { + t = (driTextureObject *) r300AllocTexObj(texObj); + if (!t) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexSubImage1D"); + return; + } + } + + _mesa_store_texsubimage1d(ctx, target, level, xoffset, width, + format, type, pixels, packing, texObj, + texImage); + + t->dirty_images[0] |= (1 << level); +} + +static void r300TexImage2D(GLcontext * ctx, GLenum target, GLint level, + GLint internalFormat, + GLint width, GLint height, GLint border, + GLenum format, GLenum type, const GLvoid * pixels, + const struct gl_pixelstore_attrib *packing, + struct gl_texture_object *texObj, + struct gl_texture_image *texImage) +{ + driTextureObject *t = (driTextureObject *) texObj->DriverData; + GLuint face; + + /* which cube face or ordinary 2D image */ + switch (target) { + case GL_TEXTURE_CUBE_MAP_POSITIVE_X: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: + case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: + case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: + face = + (GLuint) target - (GLuint) GL_TEXTURE_CUBE_MAP_POSITIVE_X; + ASSERT(face < 6); + break; + default: + face = 0; + } + + if (t != NULL) { + driSwapOutTextureObject(t); + } else { + t = (driTextureObject *) r300AllocTexObj(texObj); + if (!t) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage2D"); + return; + } + } + + texImage->IsClientData = GL_FALSE; + + if (r300ValidateClientStorage(ctx, target, + internalFormat, + width, height, + format, type, pixels, + packing, texObj, texImage)) { + if (RADEON_DEBUG & DEBUG_TEXTURE) + fprintf(stderr, "%s: Using client storage\n", + __FUNCTION__); + } else { + if (RADEON_DEBUG & DEBUG_TEXTURE) + fprintf(stderr, "%s: Using normal storage\n", + __FUNCTION__); + + /* Normal path: copy (to cached memory) and eventually upload + * via another copy to GART memory and then a blit... Could + * eliminate one copy by going straight to (permanent) GART. + * + * Note, this will call r300ChooseTextureFormat. + */ + _mesa_store_teximage2d(ctx, target, level, internalFormat, + width, height, border, format, type, + pixels, &ctx->Unpack, texObj, texImage); + + t->dirty_images[face] |= (1 << level); + } +} + +static void r300TexSubImage2D(GLcontext * ctx, GLenum target, GLint level, + GLint xoffset, GLint yoffset, + GLsizei width, GLsizei height, + GLenum format, GLenum type, + const GLvoid * pixels, + const struct gl_pixelstore_attrib *packing, + struct gl_texture_object *texObj, + struct gl_texture_image *texImage) +{ + driTextureObject *t = (driTextureObject *) texObj->DriverData; + GLuint face; + + /* which cube face or ordinary 2D image */ + switch (target) { + case GL_TEXTURE_CUBE_MAP_POSITIVE_X: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: + case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: + case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: + face = + (GLuint) target - (GLuint) GL_TEXTURE_CUBE_MAP_POSITIVE_X; + ASSERT(face < 6); + break; + default: + face = 0; + } + + assert(t); /* this _should_ be true */ + if (t) { + driSwapOutTextureObject(t); + } else { + t = (driTextureObject *) r300AllocTexObj(texObj); + if (!t) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexSubImage2D"); + return; + } + } + + _mesa_store_texsubimage2d(ctx, target, level, xoffset, yoffset, width, + height, format, type, pixels, packing, texObj, + texImage); + + t->dirty_images[face] |= (1 << level); +} + +#if ENABLE_HW_3D_TEXTURE +static void r300TexImage3D(GLcontext * ctx, GLenum target, GLint level, + GLint internalFormat, + GLint width, GLint height, GLint depth, + GLint border, + GLenum format, GLenum type, const GLvoid * pixels, + const struct gl_pixelstore_attrib *packing, + struct gl_texture_object *texObj, + struct gl_texture_image *texImage) +{ + driTextureObject *t = (driTextureObject *) texObj->DriverData; + + if (t) { + driSwapOutTextureObject(t); + } else { + t = (driTextureObject *) r300AllocTexObj(texObj); + if (!t) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage3D"); + return; + } + } + + texImage->IsClientData = GL_FALSE; + +#if 0 + if (r300ValidateClientStorage(ctx, target, + internalFormat, + width, height, + format, type, pixels, + packing, texObj, texImage)) { + if (RADEON_DEBUG & DEBUG_TEXTURE) + fprintf(stderr, "%s: Using client storage\n", + __FUNCTION__); + } else +#endif + { + if (RADEON_DEBUG & DEBUG_TEXTURE) + fprintf(stderr, "%s: Using normal storage\n", + __FUNCTION__); + + /* Normal path: copy (to cached memory) and eventually upload + * via another copy to GART memory and then a blit... Could + * eliminate one copy by going straight to (permanent) GART. + * + * Note, this will call r300ChooseTextureFormat. + */ + _mesa_store_teximage3d(ctx, target, level, internalFormat, + width, height, depth, border, + format, type, pixels, + &ctx->Unpack, texObj, texImage); + + t->dirty_images[0] |= (1 << level); + } +} +#endif + +#if ENABLE_HW_3D_TEXTURE +static void +r300TexSubImage3D(GLcontext * ctx, GLenum target, GLint level, + GLint xoffset, GLint yoffset, GLint zoffset, + GLsizei width, GLsizei height, GLsizei depth, + GLenum format, GLenum type, + const GLvoid * pixels, + const struct gl_pixelstore_attrib *packing, + struct gl_texture_object *texObj, + struct gl_texture_image *texImage) +{ + driTextureObject *t = (driTextureObject *) texObj->DriverData; + +/* fprintf(stderr, "%s\n", __FUNCTION__); */ + + assert(t); /* this _should_ be true */ + if (t) { + driSwapOutTextureObject(t); + } else { + t = (driTextureObject *) r300AllocTexObj(texObj); + if (!t) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexSubImage3D"); + return; + } + texObj->DriverData = t; + } + + _mesa_store_texsubimage3d(ctx, target, level, xoffset, yoffset, zoffset, + width, height, depth, + format, type, pixels, packing, texObj, + texImage); + + t->dirty_images[0] |= (1 << level); +} +#endif + +static void r300TexEnv(GLcontext * ctx, GLenum target, + GLenum pname, const GLfloat * param) +{ + r300ContextPtr rmesa = R300_CONTEXT(ctx); + GLuint unit = ctx->Texture.CurrentUnit; + struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit]; + + if (RADEON_DEBUG & DEBUG_STATE) { + fprintf(stderr, "%s( %s )\n", + __FUNCTION__, _mesa_lookup_enum_by_nr(pname)); + } + + fprintf(stderr, "%s:%s I am broken - Fixme !\n", __FILE__, __FUNCTION__); + + #if 0 + /* This is incorrect: Need to maintain this data for each of + * GL_TEXTURE_{123}D, GL_TEXTURE_RECTANGLE_NV, etc, and switch + * between them according to _ReallyEnabled. + */ + switch (pname) { + case GL_TEXTURE_ENV_COLOR:{ + GLubyte c[4]; + GLuint envColor; + UNCLAMPED_FLOAT_TO_RGBA_CHAN(c, texUnit->EnvColor); + envColor = radeonPackColor(4, c[0], c[1], c[2], c[3]); + if (rmesa->hw.tf.cmd[TF_TFACTOR_0 + unit] != envColor) { + R200_STATECHANGE(rmesa, tf); + rmesa->hw.tf.cmd[TF_TFACTOR_0 + unit] = + envColor; + } + break; + } + + case GL_TEXTURE_LOD_BIAS_EXT:{ + GLfloat bias, min; + GLuint b; + const int fixed_one = 0x8000000; + + /* The R200's LOD bias is a signed 2's complement value with a + * range of -16.0 <= bias < 16.0. + * + * NOTE: Add a small bias to the bias for conform mipsel.c test. + */ + bias = *param + .01; + min = + driQueryOptionb(&rmesa->radeon.optionCache, + "no_neg_lod_bias") ? 0.0 : -16.0; + bias = CLAMP(bias, min, 16.0); + b = (int)(bias * fixed_one) & R200_LOD_BIAS_MASK; + + if ((rmesa->hw.tex[unit]. + cmd[TEX_PP_TXFORMAT_X] & R200_LOD_BIAS_MASK) != + b) { + R200_STATECHANGE(rmesa, tex[unit]); + rmesa->hw.tex[unit].cmd[TEX_PP_TXFORMAT_X] &= + ~R200_LOD_BIAS_MASK; + rmesa->hw.tex[unit].cmd[TEX_PP_TXFORMAT_X] |= b; + } + break; + } + + default: + return; + } + #endif +} + +/** + * Changes variables and flags for a state update, which will happen at the + * next UpdateTextureState + */ + +static void r300TexParameter(GLcontext * ctx, GLenum target, + struct gl_texture_object *texObj, + GLenum pname, const GLfloat * params) +{ + r300TexObjPtr t = (r300TexObjPtr) texObj->DriverData; + + if (RADEON_DEBUG & (DEBUG_STATE | DEBUG_TEXTURE)) { + fprintf(stderr, "%s( %s )\n", __FUNCTION__, + _mesa_lookup_enum_by_nr(pname)); + } + + switch (pname) { + case GL_TEXTURE_MIN_FILTER: + case GL_TEXTURE_MAG_FILTER: + case GL_TEXTURE_MAX_ANISOTROPY_EXT: + r300SetTexMaxAnisotropy(t, texObj->MaxAnisotropy); + r300SetTexFilter(t, texObj->MinFilter, texObj->MagFilter); + break; + + case GL_TEXTURE_WRAP_S: + case GL_TEXTURE_WRAP_T: + case GL_TEXTURE_WRAP_R: + r300SetTexWrap(t, texObj->WrapS, texObj->WrapT, texObj->WrapR); + break; + + case GL_TEXTURE_BORDER_COLOR: + r300SetTexBorderColor(t, texObj->_BorderChan); + break; + + case GL_TEXTURE_BASE_LEVEL: + case GL_TEXTURE_MAX_LEVEL: + case GL_TEXTURE_MIN_LOD: + case GL_TEXTURE_MAX_LOD: + /* This isn't the most efficient solution but there doesn't appear to + * be a nice alternative. Since there's no LOD clamping, + * we just have to rely on loading the right subset of mipmap levels + * to simulate a clamped LOD. + */ + driSwapOutTextureObject((driTextureObject *) t); + break; + + default: + return; + } + + /* Mark this texobj as dirty (one bit per tex unit) + */ + t->dirty_state = TEX_ALL; +} + +static void r300BindTexture(GLcontext * ctx, GLenum target, + struct gl_texture_object *texObj) +{ + if (RADEON_DEBUG & (DEBUG_STATE | DEBUG_TEXTURE)) { + fprintf(stderr, "%s( %p ) unit=%d\n", __FUNCTION__, + (void *)texObj, ctx->Texture.CurrentUnit); + } + + if ((target == GL_TEXTURE_1D) + || (target == GL_TEXTURE_2D) +#if ENABLE_HW_3D_TEXTURE + || (target == GL_TEXTURE_3D) +#endif + || (target == GL_TEXTURE_CUBE_MAP) + || (target == GL_TEXTURE_RECTANGLE_NV)) { + assert(texObj->DriverData != NULL); + } +} + +static void r300DeleteTexture(GLcontext * ctx, struct gl_texture_object *texObj) +{ + r300ContextPtr rmesa = R300_CONTEXT(ctx); + driTextureObject *t = (driTextureObject *) texObj->DriverData; + + if (RADEON_DEBUG & (DEBUG_STATE | DEBUG_TEXTURE)) { + fprintf(stderr, "%s( %p (target = %s) )\n", __FUNCTION__, + (void *)texObj, + _mesa_lookup_enum_by_nr(texObj->Target)); + } + + if (t != NULL) { + if (rmesa) { + R300_FIREVERTICES(rmesa); + } + + driDestroyTextureObject(t); + } + /* Free mipmap images and the texture object itself */ + _mesa_delete_texture_object(ctx, texObj); +} + +/* Need: + * - Same GEN_MODE for all active bits + * - Same EyePlane/ObjPlane for all active bits when using Eye/Obj + * - STRQ presumably all supported (matrix means incoming R values + * can end up in STQ, this has implications for vertex support, + * presumably ok if maos is used, though?) + * + * Basically impossible to do this on the fly - just collect some + * basic info & do the checks from ValidateState(). + */ +static void r300TexGen(GLcontext * ctx, + GLenum coord, GLenum pname, const GLfloat * params) +{ + r300ContextPtr rmesa = R300_CONTEXT(ctx); + GLuint unit = ctx->Texture.CurrentUnit; + #if 0 /* Disable this for now - looks like we will be recalculating everything + anyway */ + rmesa->recheck_texgen[unit] = GL_TRUE; + #endif +} + +/** + * Allocate a new texture object. + * Called via ctx->Driver.NewTextureObject. + * Note: this function will be called during context creation to + * allocate the default texture objects. + * Note: we could use containment here to 'derive' the driver-specific + * texture object from the core mesa gl_texture_object. Not done at this time. + * Fixup MaxAnisotropy according to user preference. + */ +static struct gl_texture_object *r300NewTextureObject(GLcontext * ctx, + GLuint name, + GLenum target) +{ + r300ContextPtr rmesa = R300_CONTEXT(ctx); + struct gl_texture_object *obj; + obj = _mesa_new_texture_object(ctx, name, target); + if (!obj) + return NULL; + obj->MaxAnisotropy = rmesa->initialMaxAnisotropy; + r300AllocTexObj(obj); + return obj; +} + +void r300InitTextureFuncs(struct dd_function_table *functions) +{ + /* Note: we only plug in the functions we implement in the driver + * since _mesa_init_driver_functions() was already called. + */ + functions->ChooseTextureFormat = r300ChooseTextureFormat; + functions->TexImage1D = r300TexImage1D; + functions->TexImage2D = r300TexImage2D; +#if ENABLE_HW_3D_TEXTURE + functions->TexImage3D = r300TexImage3D; +#else + functions->TexImage3D = _mesa_store_teximage3d; +#endif + functions->TexSubImage1D = r300TexSubImage1D; + functions->TexSubImage2D = r300TexSubImage2D; +#if ENABLE_HW_3D_TEXTURE + functions->TexSubImage3D = r300TexSubImage3D; +#else + functions->TexSubImage3D = _mesa_store_texsubimage3d; +#endif + functions->NewTextureObject = r300NewTextureObject; + functions->BindTexture = r300BindTexture; + functions->DeleteTexture = r300DeleteTexture; + functions->IsTextureResident = driIsTextureResident; + + functions->TexEnv = r300TexEnv; + functions->TexParameter = r300TexParameter; + functions->TexGen = r300TexGen; + + driInitTextureFormats(); + +#if 000 + /* moved or obsolete code */ + r300ContextPtr rmesa = R300_CONTEXT(ctx); + driInitTextureObjects(ctx, &rmesa->swapped, + DRI_TEXMGR_DO_TEXTURE_1D + | DRI_TEXMGR_DO_TEXTURE_2D); + + /* Hack: r300NewTextureObject is not yet installed when the + * default textures are created. Therefore set MaxAnisotropy of the + * default 2D texture now. */ + ctx->Shared->Default2D->MaxAnisotropy = + driQueryOptionf(&rmesa->optionCache, "def_max_anisotropy"); +#endif +} diff --git a/src/mesa/drivers/dri/r300/r300_tex.h b/src/mesa/drivers/dri/r300/r300_tex.h new file mode 100644 index 0000000000..a18ff0e2ce --- /dev/null +++ b/src/mesa/drivers/dri/r300/r300_tex.h @@ -0,0 +1,51 @@ +/* $XFree86: xc/lib/GL/mesa/src/drv/r300/r300_tex.h,v 1.1 2002/10/30 12:51:53 alanh Exp $ */ +/* +Copyright (C) The Weather Channel, Inc. 2002. All Rights Reserved. + +The Weather Channel (TM) funded Tungsten Graphics to develop the +initial release of the Radeon 8500 driver under the XFree86 license. +This notice must be preserved. + +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, sublicense, 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 NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT OWNER(S) 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. + +**************************************************************************/ + +/* + * Authors: + * Keith Whitwell + */ + +#ifndef __r300_TEX_H__ +#define __r300_TEX_H__ + +#ifdef GLX_DIRECT_RENDERING + +extern void r300UpdateTextureState(GLcontext * ctx); + +extern int r300UploadTexImages(r300ContextPtr rmesa, r300TexObjPtr t, + GLuint face); + +extern void r300DestroyTexObj(r300ContextPtr rmesa, r300TexObjPtr t); + +extern void r300InitTextureFuncs(struct dd_function_table *functions); + +#endif +#endif /* __r300_TEX_H__ */ diff --git a/src/mesa/drivers/dri/r300/r300_texmem.c b/src/mesa/drivers/dri/r300/r300_texmem.c new file mode 100644 index 0000000000..78c077fa40 --- /dev/null +++ b/src/mesa/drivers/dri/r300/r300_texmem.c @@ -0,0 +1,490 @@ +/* $XFree86: xc/lib/GL/mesa/src/drv/r300/r300_texmem.c,v 1.5 2002/12/17 00:32:56 dawes Exp $ */ +/************************************************************************** + +Copyright (C) Tungsten Graphics 2002. All Rights Reserved. +The Weather Channel, Inc. funded Tungsten Graphics to develop the +initial release of the Radeon 8500 driver under the XFree86 +license. This notice must be preserved. + +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 on 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 ATI, VA LINUX SYSTEMS AND/OR THEIR +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. + +**************************************************************************/ + +/* + * Authors: + * Kevin E. Martin + * Gareth Hughes + * + */ + +#include + +#include "glheader.h" +#include "imports.h" +#include "context.h" +#include "colormac.h" +#include "macros.h" +#include "simple_list.h" +#include "radeon_reg.h" /* gets definition for usleep */ +#include "r300_context.h" +#include "r300_state.h" +#include "radeon_ioctl.h" +/* +#include "r300_swtcl.h" +*/ +#include "r300_tex.h" + +#include /* for usleep() */ + +/** + * Destroy any device-dependent state associated with the texture. This may + * include NULLing out hardware state that points to the texture. + */ +void r300DestroyTexObj(r300ContextPtr rmesa, r300TexObjPtr t) +{ + if (RADEON_DEBUG & DEBUG_TEXTURE) { + fprintf(stderr, "%s( %p, %p )\n", __FUNCTION__, + (void *)t, (void *)t->base.tObj); + } + + if (rmesa != NULL) { + unsigned i; + + for (i = 0; i < rmesa->radeon.glCtx->Const.MaxTextureUnits; i++) { + if (t == rmesa->state.texture.unit[i].texobj) { + rmesa->state.texture.unit[i].texobj = NULL; + /* This code below is meant to shorten state + pushed to the hardware by not programming + unneeded units. + + This does not appear to be worthwhile on R300 */ + #if 0 + remove_from_list(&rmesa->hw.tex[i]); + make_empty_list(&rmesa->hw.tex[i]); + remove_from_list(&rmesa->hw.cube[i]); + make_empty_list(&rmesa->hw.cube[i]); + #endif + } + } + } +} + +/* ------------------------------------------------------------ + * Texture image conversions + */ + +static void r300UploadGARTClientSubImage(r300ContextPtr rmesa, + r300TexObjPtr t, + struct gl_texture_image *texImage, + GLint hwlevel, + GLint x, GLint y, + GLint width, GLint height) +{ + const struct gl_texture_format *texFormat = texImage->TexFormat; + GLuint srcPitch, dstPitch; + int blit_format; + int srcOffset; + + /* + * XXX it appears that we always upload the full image, not a subimage. + * I.e. x==0, y==0, width=texWidth, height=texWidth. If this is ever + * changed, the src pitch will have to change. + */ + switch (texFormat->TexelBytes) { + case 1: + blit_format = R200_CP_COLOR_FORMAT_CI8; + srcPitch = t->image[0][0].width * texFormat->TexelBytes; + dstPitch = t->image[0][0].width * texFormat->TexelBytes; + break; + case 2: + blit_format = R200_CP_COLOR_FORMAT_RGB565; + srcPitch = t->image[0][0].width * texFormat->TexelBytes; + dstPitch = t->image[0][0].width * texFormat->TexelBytes; + break; + case 4: + blit_format = R200_CP_COLOR_FORMAT_ARGB8888; + srcPitch = t->image[0][0].width * texFormat->TexelBytes; + dstPitch = t->image[0][0].width * texFormat->TexelBytes; + break; + default: + return; + } + + t->image[0][hwlevel].data = texImage->Data; + srcOffset = r300GartOffsetFromVirtual(rmesa, texImage->Data); + + assert(srcOffset != ~0); + + /* Don't currently need to cope with small pitches? + */ + width = texImage->Width; + height = texImage->Height; + + r300EmitWait(rmesa, RADEON_WAIT_3D); + + r300EmitBlit(rmesa, blit_format, + srcPitch, + srcOffset, + dstPitch, + t->bufAddr, + x, + y, + t->image[0][hwlevel].x + x, + t->image[0][hwlevel].y + y, width, height); + + r300EmitWait(rmesa, RADEON_WAIT_2D); +} + +static void r300UploadRectSubImage(r300ContextPtr rmesa, + r300TexObjPtr t, + struct gl_texture_image *texImage, + GLint x, GLint y, GLint width, GLint height) +{ + const struct gl_texture_format *texFormat = texImage->TexFormat; + int blit_format, dstPitch, done; + + switch (texFormat->TexelBytes) { + case 1: + blit_format = R200_CP_COLOR_FORMAT_CI8; + break; + case 2: + blit_format = R200_CP_COLOR_FORMAT_RGB565; + break; + case 4: + blit_format = R200_CP_COLOR_FORMAT_ARGB8888; + break; + default: + return; + } + + t->image[0][0].data = texImage->Data; + + /* Currently don't need to cope with small pitches. + */ + width = texImage->Width; + height = texImage->Height; + dstPitch = t->pitch + 32; + + if (rmesa->prefer_gart_client_texturing && texImage->IsClientData) { + /* In this case, could also use GART texturing. This is + * currently disabled, but has been tested & works. + */ + t->offset = + r300GartOffsetFromVirtual(rmesa, texImage->Data); + t->pitch = + texImage->RowStride * texFormat->TexelBytes - 32; + + if (RADEON_DEBUG & DEBUG_TEXTURE) + fprintf(stderr, + "Using GART texturing for rectangular client texture\n"); + + /* Release FB memory allocated for this image: + */ + /* FIXME This may not be correct as driSwapOutTextureObject sets + * FIXME dirty_images. It may be fine, though. + */ + if (t->base.memBlock) { + driSwapOutTextureObject((driTextureObject *) t); + } + } else if (texImage->IsClientData) { + /* Data already in GART memory, with usable pitch. + */ + GLuint srcPitch; + srcPitch = texImage->RowStride * texFormat->TexelBytes; + r300EmitBlit(rmesa, + blit_format, + srcPitch, + r300GartOffsetFromVirtual(rmesa, texImage->Data), + dstPitch, t->bufAddr, 0, 0, 0, 0, width, height); + } else { + /* Data not in GART memory, or bad pitch. + */ + for (done = 0; done < height;) { + struct r300_dma_region region; + int lines = + MIN2(height - done, RADEON_BUFFER_SIZE / dstPitch); + int src_pitch; + char *tex; + + src_pitch = texImage->RowStride * texFormat->TexelBytes; + + tex = (char *)texImage->Data + done * src_pitch; + + memset(®ion, 0, sizeof(region)); + r300AllocDmaRegion(rmesa, ®ion, lines * dstPitch, + 1024); + + /* Copy texdata to dma: + */ + if (0) + fprintf(stderr, + "%s: src_pitch %d dst_pitch %d\n", + __FUNCTION__, src_pitch, dstPitch); + + if (src_pitch == dstPitch) { + memcpy(region.address + region.start, tex, + lines * src_pitch); + } else { + char *buf = region.address + region.start; + int i; + for (i = 0; i < lines; i++) { + memcpy(buf, tex, src_pitch); + buf += dstPitch; + tex += src_pitch; + } + } + + r300EmitWait(rmesa, RADEON_WAIT_3D); + + /* Blit to framebuffer + */ + r300EmitBlit(rmesa, + blit_format, + dstPitch, GET_START(®ion), + dstPitch, t->bufAddr, + 0, 0, 0, done, width, lines); + + r300EmitWait(rmesa, RADEON_WAIT_2D); + + r300ReleaseDmaRegion(rmesa, ®ion, __FUNCTION__); + done += lines; + } + } +} + +/** + * Upload the texture image associated with texture \a t at the specified + * level at the address relative to \a start. + */ +static void uploadSubImage(r300ContextPtr rmesa, r300TexObjPtr t, + GLint hwlevel, + GLint x, GLint y, GLint width, GLint height, + GLuint face) +{ + struct gl_texture_image *texImage = NULL; + GLuint offset; + GLint imageWidth, imageHeight; + GLint ret; + drm_radeon_texture_t tex; + drm_radeon_tex_image_t tmp; + const int level = hwlevel + t->base.firstLevel; + + if (RADEON_DEBUG & DEBUG_TEXTURE) { + fprintf(stderr, + "%s( %p, %p ) level/width/height/face = %d/%d/%d/%u\n", + __FUNCTION__, (void *)t, (void *)t->base.tObj, level, + width, height, face); + } + + ASSERT(face < 6); + + /* Ensure we have a valid texture to upload */ + if ((hwlevel < 0) || (hwlevel >= RADEON_MAX_TEXTURE_LEVELS)) { + _mesa_problem(NULL, "bad texture level in %s", __FUNCTION__); + return; + } + + texImage = t->base.tObj->Image[face][level]; + + if (!texImage) { + if (RADEON_DEBUG & DEBUG_TEXTURE) + fprintf(stderr, "%s: texImage %d is NULL!\n", + __FUNCTION__, level); + return; + } + if (!texImage->Data) { + if (RADEON_DEBUG & DEBUG_TEXTURE) + fprintf(stderr, "%s: image data is NULL!\n", + __FUNCTION__); + return; + } + + if (t->base.tObj->Target == GL_TEXTURE_RECTANGLE_NV) { + assert(level == 0); + assert(hwlevel == 0); + if (RADEON_DEBUG & DEBUG_TEXTURE) + fprintf(stderr, "%s: image data is rectangular\n", + __FUNCTION__); + r300UploadRectSubImage(rmesa, t, texImage, x, y, width, height); + return; + } else if (texImage->IsClientData) { + if (RADEON_DEBUG & DEBUG_TEXTURE) + fprintf(stderr, + "%s: image data is in GART client storage\n", + __FUNCTION__); + r300UploadGARTClientSubImage(rmesa, t, texImage, hwlevel, x, y, + width, height); + return; + } else if (RADEON_DEBUG & DEBUG_TEXTURE) + fprintf(stderr, "%s: image data is in normal memory\n", + __FUNCTION__); + + imageWidth = texImage->Width; + imageHeight = texImage->Height; + + offset = t->bufAddr; + + if (RADEON_DEBUG & (DEBUG_TEXTURE | DEBUG_IOCTL)) { + GLint imageX = 0; + GLint imageY = 0; + GLint blitX = t->image[face][hwlevel].x; + GLint blitY = t->image[face][hwlevel].y; + GLint blitWidth = t->image[face][hwlevel].width; + GLint blitHeight = t->image[face][hwlevel].height; + fprintf(stderr, " upload image: %d,%d at %d,%d\n", + imageWidth, imageHeight, imageX, imageY); + fprintf(stderr, " upload blit: %d,%d at %d,%d\n", + blitWidth, blitHeight, blitX, blitY); + fprintf(stderr, " blit ofs: 0x%07x level: %d/%d\n", + (GLuint) offset, hwlevel, level); + } + + t->image[face][hwlevel].data = texImage->Data; + + /* Init the DRM_RADEON_TEXTURE command / drm_radeon_texture_t struct. + * NOTE: we're always use a 1KB-wide blit and I8 texture format. + * We used to use 1, 2 and 4-byte texels and used to use the texture + * width to dictate the blit width - but that won't work for compressed + * textures. (Brian) + */ + tex.offset = offset; + tex.pitch = BLIT_WIDTH_BYTES / 64; + tex.format = R200_TXFORMAT_I8; /* any 1-byte texel format */ + if (texImage->TexFormat->TexelBytes) { + tex.width = imageWidth * texImage->TexFormat->TexelBytes; /* in bytes */ + tex.height = imageHeight; + } else { + tex.width = imageWidth; /* compressed */ + tex.height = imageHeight; + if (tex.height < 4) + tex.height = 4; + } + tex.image = &tmp; + + /* copy (x,y,width,height,data) */ + memcpy(&tmp, &t->image[face][hwlevel], sizeof(tmp)); + + LOCK_HARDWARE(&rmesa->radeon); + do { + ret = drmCommandWriteRead(rmesa->radeon.dri.fd, DRM_RADEON_TEXTURE, + &tex, sizeof(drm_radeon_texture_t)); + if (ret) { + if (RADEON_DEBUG & DEBUG_IOCTL) + fprintf(stderr, + "DRM_RADEON_TEXTURE: again!\n"); + usleep(1); + } + } while (ret && errno == EAGAIN); + + UNLOCK_HARDWARE(&rmesa->radeon); + + if (ret) { + fprintf(stderr, "DRM_RADEON_TEXTURE: return = %d\n", ret); + fprintf(stderr, " offset=0x%08x\n", offset); + fprintf(stderr, " image width=%d height=%d\n", + imageWidth, imageHeight); + fprintf(stderr, " blit width=%d height=%d data=%p\n", + t->image[face][hwlevel].width, + t->image[face][hwlevel].height, + t->image[face][hwlevel].data); + exit(1); + } +} + +/** + * Upload the texture images associated with texture \a t. This might + * require the allocation of texture memory. + * + * \param rmesa Context pointer + * \param t Texture to be uploaded + * \param face Cube map face to be uploaded. Zero for non-cube maps. + */ + +int r300UploadTexImages(r300ContextPtr rmesa, r300TexObjPtr t, GLuint face) +{ + const int numLevels = t->base.lastLevel - t->base.firstLevel + 1; + + if (RADEON_DEBUG & (DEBUG_TEXTURE | DEBUG_IOCTL)) { + fprintf(stderr, "%s( %p, %p ) sz=%d lvls=%d-%d\n", __FUNCTION__, + (void *)rmesa->radeon.glCtx, (void *)t->base.tObj, + t->base.totalSize, t->base.firstLevel, + t->base.lastLevel); + } + + if (!t || t->base.totalSize == 0) + return 0; + + if (RADEON_DEBUG & DEBUG_SYNC) { + fprintf(stderr, "%s: Syncing\n", __FUNCTION__); + radeonFinish(rmesa->radeon.glCtx); + } + + LOCK_HARDWARE(&rmesa->radeon); + + if (t->base.memBlock == NULL) { + int heap; + + heap = driAllocateTexture(rmesa->texture_heaps, rmesa->nr_heaps, + (driTextureObject *) t); + if (heap == -1) { + UNLOCK_HARDWARE(&rmesa->radeon); + return -1; + } + + /* Set the base offset of the texture image */ + t->bufAddr = rmesa->radeon.radeonScreen->texOffset[heap] + + t->base.memBlock->ofs; + t->offset = t->bufAddr; + + /* Mark this texobj as dirty on all units: + */ + t->dirty_state = TEX_ALL; + } + + /* Let the world know we've used this memory recently. + */ + driUpdateTextureLRU((driTextureObject *) t); + UNLOCK_HARDWARE(&rmesa->radeon); + + /* Upload any images that are new */ + if (t->base.dirty_images[face]) { + int i; + for (i = 0; i < numLevels; i++) { + if ((t->base. + dirty_images[face] & (1 << + (i + t->base.firstLevel))) != + 0) { + uploadSubImage(rmesa, t, i, 0, 0, + t->image[face][i].width, + t->image[face][i].height, face); + } + } + t->base.dirty_images[face] = 0; + } + + if (RADEON_DEBUG & DEBUG_SYNC) { + fprintf(stderr, "%s: Syncing\n", __FUNCTION__); + radeonFinish(rmesa->radeon.glCtx); + } + + return 0; +} diff --git a/src/mesa/drivers/dri/r300/r300_texstate.c b/src/mesa/drivers/dri/r300/r300_texstate.c new file mode 100644 index 0000000000..6962bb3ddb --- /dev/null +++ b/src/mesa/drivers/dri/r300/r300_texstate.c @@ -0,0 +1,1390 @@ +/* $XFree86: xc/lib/GL/mesa/src/drv/r300/r300_texstate.c,v 1.3 2003/02/15 22:18:47 dawes Exp $ */ +/* +Copyright (C) The Weather Channel, Inc. 2002. All Rights Reserved. + +The Weather Channel (TM) funded Tungsten Graphics to develop the +initial release of the Radeon 8500 driver under the XFree86 license. +This notice must be preserved. + +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, sublicense, 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 NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT OWNER(S) 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. + +**************************************************************************/ + +/* + * Authors: + * Keith Whitwell + */ + +#include "glheader.h" +#include "imports.h" +#include "context.h" +#include "macros.h" +#include "texformat.h" +#include "enums.h" + +#include "r300_context.h" +#include "r300_state.h" +#include "radeon_ioctl.h" +//#include "r300_swtcl.h" +#include "r300_tex.h" +//#include "r300_tcl.h" + +#define R200_TXFORMAT_A8 R200_TXFORMAT_I8 +#define R200_TXFORMAT_L8 R200_TXFORMAT_I8 +#define R200_TXFORMAT_AL88 R200_TXFORMAT_AI88 +#define R200_TXFORMAT_YCBCR R200_TXFORMAT_YVYU422 +#define R200_TXFORMAT_YCBCR_REV R200_TXFORMAT_VYUY422 + +#define _COLOR(f) \ + [ MESA_FORMAT_ ## f ] = { R200_TXFORMAT_ ## f, 0 } +#define _COLOR_REV(f) \ + [ MESA_FORMAT_ ## f ## _REV ] = { R200_TXFORMAT_ ## f, 0 } +#define _ALPHA(f) \ + [ MESA_FORMAT_ ## f ] = { R200_TXFORMAT_ ## f | R200_TXFORMAT_ALPHA_IN_MAP, 0 } +#define _ALPHA_REV(f) \ + [ MESA_FORMAT_ ## f ## _REV ] = { R200_TXFORMAT_ ## f | R200_TXFORMAT_ALPHA_IN_MAP, 0 } +#define _YUV(f) \ + [ MESA_FORMAT_ ## f ] = { R200_TXFORMAT_ ## f, R200_YUV_TO_RGB } +#define _INVALID(f) \ + [ MESA_FORMAT_ ## f ] = { 0xffffffff, 0 } +#define VALID_FORMAT(f) ( ((f) <= MESA_FORMAT_YCBCR_REV) \ + && (tx_table[f].format != 0xffffffff) ) + +static const struct { + GLuint format, filter; +} tx_table[] = { +_ALPHA(RGBA8888), + _ALPHA_REV(RGBA8888), + _ALPHA(ARGB8888), + _ALPHA_REV(ARGB8888), + _INVALID(RGB888), + _COLOR(RGB565), + _COLOR_REV(RGB565), + _ALPHA(ARGB4444), + _ALPHA_REV(ARGB4444), + _ALPHA(ARGB1555), + _ALPHA_REV(ARGB1555), + _ALPHA(AL88), + _ALPHA_REV(AL88), + _ALPHA(A8), + _COLOR(L8), + _ALPHA(I8), _INVALID(CI8), _YUV(YCBCR), _YUV(YCBCR_REV),}; + +#undef _COLOR +#undef _ALPHA +#undef _INVALID + +/** + * This function computes the number of bytes of storage needed for + * the given texture object (all mipmap levels, all cube faces). + * The \c image[face][level].x/y/width/height parameters for upload/blitting + * are computed here. \c filter, \c format, etc. will be set here + * too. + * + * \param rmesa Context pointer + * \param tObj GL texture object whose images are to be posted to + * hardware state. + */ +static void r300SetTexImages(r300ContextPtr rmesa, + struct gl_texture_object *tObj) +{ + r300TexObjPtr t = (r300TexObjPtr) tObj->DriverData; + const struct gl_texture_image *baseImage = + tObj->Image[0][tObj->BaseLevel]; + GLint curOffset; + GLint i; + GLint numLevels; + GLint log2Width, log2Height, log2Depth; + + /* Set the hardware texture format + */ + + t->format &= ~(R200_TXFORMAT_FORMAT_MASK | + R200_TXFORMAT_ALPHA_IN_MAP); + t->filter &= ~R200_YUV_TO_RGB; + + if (VALID_FORMAT(baseImage->TexFormat->MesaFormat)) { + t->format |= + tx_table[baseImage->TexFormat->MesaFormat].format; + t->filter |= + tx_table[baseImage->TexFormat->MesaFormat].filter; + } else { + _mesa_problem(NULL, "unexpected texture format in %s", + __FUNCTION__); + return; + } + + /* Compute which mipmap levels we really want to send to the hardware. + */ + + driCalculateTextureFirstLastLevel((driTextureObject *) t); + log2Width = tObj->Image[0][t->base.firstLevel]->WidthLog2; + log2Height = tObj->Image[0][t->base.firstLevel]->HeightLog2; + log2Depth = tObj->Image[0][t->base.firstLevel]->DepthLog2; + + numLevels = t->base.lastLevel - t->base.firstLevel + 1; + + assert(numLevels <= RADEON_MAX_TEXTURE_LEVELS); + + /* Calculate mipmap offsets and dimensions for blitting (uploading) + * The idea is that we lay out the mipmap levels within a block of + * memory organized as a rectangle of width BLIT_WIDTH_BYTES. + */ + curOffset = 0; + + for (i = 0; i < numLevels; i++) { + const struct gl_texture_image *texImage; + GLuint size; + + texImage = tObj->Image[0][i + t->base.firstLevel]; + if (!texImage) + break; + + /* find image size in bytes */ + if (texImage->IsCompressed) { + size = texImage->CompressedSize; + } else if (tObj->Target == GL_TEXTURE_RECTANGLE_NV) { + size = + ((texImage->Width * + texImage->TexFormat->TexelBytes + 63) + & ~63) * texImage->Height; + } else { + int w = + texImage->Width * texImage->TexFormat->TexelBytes; + if (w < 32) + w = 32; + size = w * texImage->Height * texImage->Depth; + } + assert(size > 0); + + /* Align to 32-byte offset. It is faster to do this unconditionally + * (no branch penalty). + */ + + curOffset = (curOffset + 0x1f) & ~0x1f; + + t->image[0][i].x = curOffset % BLIT_WIDTH_BYTES; + t->image[0][i].y = curOffset / BLIT_WIDTH_BYTES; + t->image[0][i].width = MIN2(size, BLIT_WIDTH_BYTES); + t->image[0][i].height = size / t->image[0][i].width; + +#if 0 + /* for debugging only and only applicable to non-rectangle targets */ + assert(size % t->image[0][i].width == 0); + assert(t->image[0][i].x == 0 + || (size < BLIT_WIDTH_BYTES + && t->image[0][i].height == 1)); +#endif + + if (0) + fprintf(stderr, + "level %d: %dx%d x=%d y=%d w=%d h=%d size=%d at %d\n", + i, texImage->Width, texImage->Height, + t->image[0][i].x, t->image[0][i].y, + t->image[0][i].width, t->image[0][i].height, + size, curOffset); + + curOffset += size; + + } + + /* Align the total size of texture memory block. + */ + t->base.totalSize = + (curOffset + RADEON_OFFSET_MASK) & ~RADEON_OFFSET_MASK; + + /* Setup remaining cube face blits, if needed */ + if (tObj->Target == GL_TEXTURE_CUBE_MAP) { + /* Round totalSize up to multiple of BLIT_WIDTH_BYTES */ + const GLuint faceSize = + (t->base.totalSize + BLIT_WIDTH_BYTES - 1) + & ~(BLIT_WIDTH_BYTES - 1); + const GLuint lines = faceSize / BLIT_WIDTH_BYTES; + GLuint face; + /* reuse face 0 x/y/width/height - just adjust y */ + for (face = 1; face < 6; face++) { + for (i = 0; i < numLevels; i++) { + t->image[face][i].x = t->image[0][i].x; + t->image[face][i].y = + t->image[0][i].y + face * lines; + t->image[face][i].width = t->image[0][i].width; + t->image[face][i].height = + t->image[0][i].height; + } + } + t->base.totalSize = 6 * faceSize; /* total texmem needed */ + } + + /* Hardware state: + */ + t->filter &= ~R200_MAX_MIP_LEVEL_MASK; + t->filter |= (numLevels - 1) << R200_MAX_MIP_LEVEL_SHIFT; + + t->format &= ~(R200_TXFORMAT_WIDTH_MASK | + R200_TXFORMAT_HEIGHT_MASK | + R200_TXFORMAT_CUBIC_MAP_ENABLE | + R200_TXFORMAT_F5_WIDTH_MASK | + R200_TXFORMAT_F5_HEIGHT_MASK); + t->format |= ((log2Width << R200_TXFORMAT_WIDTH_SHIFT) | + (log2Height << R200_TXFORMAT_HEIGHT_SHIFT)); + + #if 0 + t->format_x &= ~(R200_DEPTH_LOG2_MASK | R200_TEXCOORD_MASK); + if (tObj->Target == GL_TEXTURE_3D) { + t->format_x |= (log2Depth << R200_DEPTH_LOG2_SHIFT); + t->format_x |= R200_TEXCOORD_VOLUME; + } else if (tObj->Target == GL_TEXTURE_CUBE_MAP) { + ASSERT(log2Width == log2height); + t->format |= ((log2Width << R200_TXFORMAT_F5_WIDTH_SHIFT) | + (log2Height << R200_TXFORMAT_F5_HEIGHT_SHIFT) + | (R200_TXFORMAT_CUBIC_MAP_ENABLE)); + t->format_x |= R200_TEXCOORD_CUBIC_ENV; + t->pp_cubic_faces = ((log2Width << R200_FACE_WIDTH_1_SHIFT) | + (log2Height << R200_FACE_HEIGHT_1_SHIFT) | + (log2Width << R200_FACE_WIDTH_2_SHIFT) | + (log2Height << R200_FACE_HEIGHT_2_SHIFT) | + (log2Width << R200_FACE_WIDTH_3_SHIFT) | + (log2Height << R200_FACE_HEIGHT_3_SHIFT) | + (log2Width << R200_FACE_WIDTH_4_SHIFT) | + (log2Height << R200_FACE_HEIGHT_4_SHIFT)); + } + #endif + + t->size = (((tObj->Image[0][t->base.firstLevel]->Width - 1) << 0) | + ((tObj->Image[0][t->base.firstLevel]->Height - + 1) << 16)); + + /* Only need to round to nearest 32 for textures, but the blitter + * requires 64-byte aligned pitches, and we may/may not need the + * blitter. NPOT only! + */ + if (baseImage->IsCompressed) + t->pitch = + (tObj->Image[0][t->base.firstLevel]->Width + 63) & ~(63); + else + t->pitch = + ((tObj->Image[0][t->base.firstLevel]->Width * + baseImage->TexFormat->TexelBytes) + 63) & ~(63); + t->pitch -= 32; + + t->dirty_state = TEX_ALL; + + /* FYI: r300UploadTexImages( rmesa, t ) used to be called here */ +} + +/* ================================================================ + * Texture combine functions + */ + +/* GL_ARB_texture_env_combine support + */ + +/* The color tables have combine functions for GL_SRC_COLOR, + * GL_ONE_MINUS_SRC_COLOR, GL_SRC_ALPHA and GL_ONE_MINUS_SRC_ALPHA. + */ +static GLuint r300_register_color[][R200_MAX_TEXTURE_UNITS] = { + { + R200_TXC_ARG_A_R0_COLOR, + R200_TXC_ARG_A_R1_COLOR, + R200_TXC_ARG_A_R2_COLOR, + R200_TXC_ARG_A_R3_COLOR, + R200_TXC_ARG_A_R4_COLOR, + R200_TXC_ARG_A_R5_COLOR}, + { + R200_TXC_ARG_A_R0_COLOR | R200_TXC_COMP_ARG_A, + R200_TXC_ARG_A_R1_COLOR | R200_TXC_COMP_ARG_A, + R200_TXC_ARG_A_R2_COLOR | R200_TXC_COMP_ARG_A, + R200_TXC_ARG_A_R3_COLOR | R200_TXC_COMP_ARG_A, + R200_TXC_ARG_A_R4_COLOR | R200_TXC_COMP_ARG_A, + R200_TXC_ARG_A_R5_COLOR | R200_TXC_COMP_ARG_A}, + { + R200_TXC_ARG_A_R0_ALPHA, + R200_TXC_ARG_A_R1_ALPHA, + R200_TXC_ARG_A_R2_ALPHA, + R200_TXC_ARG_A_R3_ALPHA, + R200_TXC_ARG_A_R4_ALPHA, + R200_TXC_ARG_A_R5_ALPHA}, + { + R200_TXC_ARG_A_R0_ALPHA | R200_TXC_COMP_ARG_A, + R200_TXC_ARG_A_R1_ALPHA | R200_TXC_COMP_ARG_A, + R200_TXC_ARG_A_R2_ALPHA | R200_TXC_COMP_ARG_A, + R200_TXC_ARG_A_R3_ALPHA | R200_TXC_COMP_ARG_A, + R200_TXC_ARG_A_R4_ALPHA | R200_TXC_COMP_ARG_A, + R200_TXC_ARG_A_R5_ALPHA | R200_TXC_COMP_ARG_A}, +}; + +static GLuint r300_tfactor_color[] = { + R200_TXC_ARG_A_TFACTOR_COLOR, + R200_TXC_ARG_A_TFACTOR_COLOR | R200_TXC_COMP_ARG_A, + R200_TXC_ARG_A_TFACTOR_ALPHA, + R200_TXC_ARG_A_TFACTOR_ALPHA | R200_TXC_COMP_ARG_A +}; + +static GLuint r300_primary_color[] = { + R200_TXC_ARG_A_DIFFUSE_COLOR, + R200_TXC_ARG_A_DIFFUSE_COLOR | R200_TXC_COMP_ARG_A, + R200_TXC_ARG_A_DIFFUSE_ALPHA, + R200_TXC_ARG_A_DIFFUSE_ALPHA | R200_TXC_COMP_ARG_A +}; + +/* GL_ZERO table - indices 0-3 + * GL_ONE table - indices 1-4 + */ +static GLuint r300_zero_color[] = { + R200_TXC_ARG_A_ZERO, + R200_TXC_ARG_A_ZERO | R200_TXC_COMP_ARG_A, + R200_TXC_ARG_A_ZERO, + R200_TXC_ARG_A_ZERO | R200_TXC_COMP_ARG_A, + R200_TXC_ARG_A_ZERO +}; + +/* The alpha tables only have GL_SRC_ALPHA and GL_ONE_MINUS_SRC_ALPHA. + */ +static GLuint r300_register_alpha[][R200_MAX_TEXTURE_UNITS] = { + { + R200_TXA_ARG_A_R0_ALPHA, + R200_TXA_ARG_A_R1_ALPHA, + R200_TXA_ARG_A_R2_ALPHA, + R200_TXA_ARG_A_R3_ALPHA, + R200_TXA_ARG_A_R4_ALPHA, + R200_TXA_ARG_A_R5_ALPHA}, + { + R200_TXA_ARG_A_R0_ALPHA | R200_TXA_COMP_ARG_A, + R200_TXA_ARG_A_R1_ALPHA | R200_TXA_COMP_ARG_A, + R200_TXA_ARG_A_R2_ALPHA | R200_TXA_COMP_ARG_A, + R200_TXA_ARG_A_R3_ALPHA | R200_TXA_COMP_ARG_A, + R200_TXA_ARG_A_R4_ALPHA | R200_TXA_COMP_ARG_A, + R200_TXA_ARG_A_R5_ALPHA | R200_TXA_COMP_ARG_A}, +}; + +static GLuint r300_tfactor_alpha[] = { + R200_TXA_ARG_A_TFACTOR_ALPHA, + R200_TXA_ARG_A_TFACTOR_ALPHA | R200_TXA_COMP_ARG_A +}; + +static GLuint r300_primary_alpha[] = { + R200_TXA_ARG_A_DIFFUSE_ALPHA, + R200_TXA_ARG_A_DIFFUSE_ALPHA | R200_TXA_COMP_ARG_A +}; + +/* GL_ZERO table - indices 0-1 + * GL_ONE table - indices 1-2 + */ +static GLuint r300_zero_alpha[] = { + R200_TXA_ARG_A_ZERO, + R200_TXA_ARG_A_ZERO | R200_TXA_COMP_ARG_A, + R200_TXA_ARG_A_ZERO, +}; + +/* Extract the arg from slot A, shift it into the correct argument slot + * and set the corresponding complement bit. + */ +#define R200_COLOR_ARG( n, arg ) \ +do { \ + color_combine |= \ + ((color_arg[n] & R200_TXC_ARG_A_MASK) \ + << R200_TXC_ARG_##arg##_SHIFT); \ + color_combine |= \ + ((color_arg[n] >> R200_TXC_COMP_ARG_A_SHIFT) \ + << R200_TXC_COMP_ARG_##arg##_SHIFT); \ +} while (0) + +#define R200_ALPHA_ARG( n, arg ) \ +do { \ + alpha_combine |= \ + ((alpha_arg[n] & R200_TXA_ARG_A_MASK) \ + << R200_TXA_ARG_##arg##_SHIFT); \ + alpha_combine |= \ + ((alpha_arg[n] >> R200_TXA_COMP_ARG_A_SHIFT) \ + << R200_TXA_COMP_ARG_##arg##_SHIFT); \ +} while (0) + +/* ================================================================ + * Texture unit state management + */ + +static GLboolean r300UpdateTextureEnv(GLcontext * ctx, int unit) +{ + r300ContextPtr rmesa = R300_CONTEXT(ctx); + const struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit]; + GLuint color_combine, alpha_combine; + + #if 0 /* disable for now.. */ + GLuint color_scale = rmesa->hw.pix[unit].cmd[PIX_PP_TXCBLEND2] & + ~(R200_TXC_SCALE_MASK); + GLuint alpha_scale = rmesa->hw.pix[unit].cmd[PIX_PP_TXABLEND2] & + ~(R200_TXA_DOT_ALPHA | R200_TXA_SCALE_MASK); + + /* texUnit->_Current can be NULL if and only if the texture unit is + * not actually enabled. + */ + assert((texUnit->_ReallyEnabled == 0) + || (texUnit->_Current != NULL)); + + if (RADEON_DEBUG & DEBUG_TEXTURE) { + fprintf(stderr, "%s( %p, %d )\n", __FUNCTION__, (void *)ctx, + unit); + } + + /* Set the texture environment state. Isn't this nice and clean? + * The chip will automagically set the texture alpha to 0xff when + * the texture format does not include an alpha component. This + * reduces the amount of special-casing we have to do, alpha-only + * textures being a notable exception. + */ + /* Don't cache these results. + */ + rmesa->state.texture.unit[unit].format = 0; + rmesa->state.texture.unit[unit].envMode = 0; + + if (!texUnit->_ReallyEnabled) { + if (unit == 0) { + color_combine = + R200_TXC_ARG_A_ZERO | R200_TXC_ARG_B_ZERO | + R200_TXC_ARG_C_DIFFUSE_COLOR | R200_TXC_OP_MADD; + alpha_combine = + R200_TXA_ARG_A_ZERO | R200_TXA_ARG_B_ZERO | + R200_TXA_ARG_C_DIFFUSE_ALPHA | R200_TXA_OP_MADD; + } else { + color_combine = + R200_TXC_ARG_A_ZERO | R200_TXC_ARG_B_ZERO | + R200_TXC_ARG_C_R0_COLOR | R200_TXC_OP_MADD; + alpha_combine = + R200_TXA_ARG_A_ZERO | R200_TXA_ARG_B_ZERO | + R200_TXA_ARG_C_R0_ALPHA | R200_TXA_OP_MADD; + } + } else { + GLuint color_arg[3], alpha_arg[3]; + GLuint i; + const GLuint numColorArgs = + texUnit->_CurrentCombine->_NumArgsRGB; + const GLuint numAlphaArgs = texUnit->_CurrentCombine->_NumArgsA; + GLuint RGBshift = texUnit->_CurrentCombine->ScaleShiftRGB; + GLuint Ashift = texUnit->_CurrentCombine->ScaleShiftA; + + /* Step 1: + * Extract the color and alpha combine function arguments. + */ + for (i = 0; i < numColorArgs; i++) { + const GLint op = + texUnit->_CurrentCombine->OperandRGB[i] - + GL_SRC_COLOR; + assert(op >= 0); + assert(op <= 3); + switch (texUnit->_CurrentCombine->SourceRGB[i]) { + case GL_TEXTURE: + color_arg[i] = r300_register_color[op][unit]; + break; + case GL_CONSTANT: + color_arg[i] = r300_tfactor_color[op]; + break; + case GL_PRIMARY_COLOR: + color_arg[i] = r300_primary_color[op]; + break; + case GL_PREVIOUS: + if (unit == 0) + color_arg[i] = r300_primary_color[op]; + else + color_arg[i] = + r300_register_color[op][0]; + break; + case GL_ZERO: + color_arg[i] = r300_zero_color[op]; + break; + case GL_ONE: + color_arg[i] = r300_zero_color[op + 1]; + break; + default: + return GL_FALSE; + } + } + + for (i = 0; i < numAlphaArgs; i++) { + const GLint op = + texUnit->_CurrentCombine->OperandA[i] - + GL_SRC_ALPHA; + assert(op >= 0); + assert(op <= 1); + switch (texUnit->_CurrentCombine->SourceA[i]) { + case GL_TEXTURE: + alpha_arg[i] = r300_register_alpha[op][unit]; + break; + case GL_CONSTANT: + alpha_arg[i] = r300_tfactor_alpha[op]; + break; + case GL_PRIMARY_COLOR: + alpha_arg[i] = r300_primary_alpha[op]; + break; + case GL_PREVIOUS: + if (unit == 0) + alpha_arg[i] = r300_primary_alpha[op]; + else + alpha_arg[i] = + r300_register_alpha[op][0]; + break; + case GL_ZERO: + alpha_arg[i] = r300_zero_alpha[op]; + break; + case GL_ONE: + alpha_arg[i] = r300_zero_alpha[op + 1]; + break; + default: + return GL_FALSE; + } + } + + /* Step 2: + * Build up the color and alpha combine functions. + */ + switch (texUnit->_CurrentCombine->ModeRGB) { + case GL_REPLACE: + color_combine = (R200_TXC_ARG_A_ZERO | + R200_TXC_ARG_B_ZERO | + R200_TXC_OP_MADD); + R200_COLOR_ARG(0, C); + break; + case GL_MODULATE: + color_combine = (R200_TXC_ARG_C_ZERO | + R200_TXC_OP_MADD); + R200_COLOR_ARG(0, A); + R200_COLOR_ARG(1, B); + break; + case GL_ADD: + color_combine = (R200_TXC_ARG_B_ZERO | + R200_TXC_COMP_ARG_B | + R200_TXC_OP_MADD); + R200_COLOR_ARG(0, A); + R200_COLOR_ARG(1, C); + break; + case GL_ADD_SIGNED: + color_combine = (R200_TXC_ARG_B_ZERO | R200_TXC_COMP_ARG_B | R200_TXC_BIAS_ARG_C | /* new */ + R200_TXC_OP_MADD); /* was ADDSIGNED */ + R200_COLOR_ARG(0, A); + R200_COLOR_ARG(1, C); + break; + case GL_SUBTRACT: + color_combine = (R200_TXC_ARG_B_ZERO | + R200_TXC_COMP_ARG_B | + R200_TXC_NEG_ARG_C | R200_TXC_OP_MADD); + R200_COLOR_ARG(0, A); + R200_COLOR_ARG(1, C); + break; + case GL_INTERPOLATE: + color_combine = (R200_TXC_OP_LERP); + R200_COLOR_ARG(0, B); + R200_COLOR_ARG(1, A); + R200_COLOR_ARG(2, C); + break; + + case GL_DOT3_RGB_EXT: + case GL_DOT3_RGBA_EXT: + /* The EXT version of the DOT3 extension does not support the + * scale factor, but the ARB version (and the version in OpenGL + * 1.3) does. + */ + RGBshift = 0; + /* FALLTHROUGH */ + + case GL_DOT3_RGB: + case GL_DOT3_RGBA: + /* DOT3 works differently on R200 than on R100. On R100, just + * setting the DOT3 mode did everything for you. On R200, the + * driver has to enable the biasing and scale in the inputs to + * put them in the proper [-1,1] range. This is what the 4x and + * the -0.5 in the DOT3 spec do. The post-scale is then set + * normally. + */ + + color_combine = (R200_TXC_ARG_C_ZERO | + R200_TXC_OP_DOT3 | + R200_TXC_BIAS_ARG_A | + R200_TXC_BIAS_ARG_B | + R200_TXC_SCALE_ARG_A | + R200_TXC_SCALE_ARG_B); + R200_COLOR_ARG(0, A); + R200_COLOR_ARG(1, B); + break; + + case GL_MODULATE_ADD_ATI: + color_combine = (R200_TXC_OP_MADD); + R200_COLOR_ARG(0, A); + R200_COLOR_ARG(1, C); + R200_COLOR_ARG(2, B); + break; + case GL_MODULATE_SIGNED_ADD_ATI: + color_combine = (R200_TXC_BIAS_ARG_C | /* new */ + R200_TXC_OP_MADD); /* was ADDSIGNED */ + R200_COLOR_ARG(0, A); + R200_COLOR_ARG(1, C); + R200_COLOR_ARG(2, B); + break; + case GL_MODULATE_SUBTRACT_ATI: + color_combine = (R200_TXC_NEG_ARG_C | R200_TXC_OP_MADD); + R200_COLOR_ARG(0, A); + R200_COLOR_ARG(1, C); + R200_COLOR_ARG(2, B); + break; + default: + return GL_FALSE; + } + + switch (texUnit->_CurrentCombine->ModeA) { + case GL_REPLACE: + alpha_combine = (R200_TXA_ARG_A_ZERO | + R200_TXA_ARG_B_ZERO | + R200_TXA_OP_MADD); + R200_ALPHA_ARG(0, C); + break; + case GL_MODULATE: + alpha_combine = (R200_TXA_ARG_C_ZERO | + R200_TXA_OP_MADD); + R200_ALPHA_ARG(0, A); + R200_ALPHA_ARG(1, B); + break; + case GL_ADD: + alpha_combine = (R200_TXA_ARG_B_ZERO | + R200_TXA_COMP_ARG_B | + R200_TXA_OP_MADD); + R200_ALPHA_ARG(0, A); + R200_ALPHA_ARG(1, C); + break; + case GL_ADD_SIGNED: + alpha_combine = (R200_TXA_ARG_B_ZERO | R200_TXA_COMP_ARG_B | R200_TXA_BIAS_ARG_C | /* new */ + R200_TXA_OP_MADD); /* was ADDSIGNED */ + R200_ALPHA_ARG(0, A); + R200_ALPHA_ARG(1, C); + break; + case GL_SUBTRACT: + alpha_combine = (R200_TXA_ARG_B_ZERO | + R200_TXA_COMP_ARG_B | + R200_TXA_NEG_ARG_C | R200_TXA_OP_MADD); + R200_ALPHA_ARG(0, A); + R200_ALPHA_ARG(1, C); + break; + case GL_INTERPOLATE: + alpha_combine = (R200_TXA_OP_LERP); + R200_ALPHA_ARG(0, B); + R200_ALPHA_ARG(1, A); + R200_ALPHA_ARG(2, C); + break; + + case GL_MODULATE_ADD_ATI: + alpha_combine = (R200_TXA_OP_MADD); + R200_ALPHA_ARG(0, A); + R200_ALPHA_ARG(1, C); + R200_ALPHA_ARG(2, B); + break; + case GL_MODULATE_SIGNED_ADD_ATI: + alpha_combine = (R200_TXA_BIAS_ARG_C | /* new */ + R200_TXA_OP_MADD); /* was ADDSIGNED */ + R200_ALPHA_ARG(0, A); + R200_ALPHA_ARG(1, C); + R200_ALPHA_ARG(2, B); + break; + case GL_MODULATE_SUBTRACT_ATI: + alpha_combine = (R200_TXA_NEG_ARG_C | R200_TXA_OP_MADD); + R200_ALPHA_ARG(0, A); + R200_ALPHA_ARG(1, C); + R200_ALPHA_ARG(2, B); + break; + default: + return GL_FALSE; + } + + if ((texUnit->_CurrentCombine->ModeRGB == GL_DOT3_RGBA_EXT) + || (texUnit->_CurrentCombine->ModeRGB == GL_DOT3_RGBA)) { + alpha_scale |= R200_TXA_DOT_ALPHA; + Ashift = RGBshift; + } + + /* Step 3: + * Apply the scale factor. + */ + color_scale |= (RGBshift << R200_TXC_SCALE_SHIFT); + alpha_scale |= (Ashift << R200_TXA_SCALE_SHIFT); + + /* All done! + */ + } + + if (rmesa->hw.pix[unit].cmd[PIX_PP_TXCBLEND] != color_combine || + rmesa->hw.pix[unit].cmd[PIX_PP_TXABLEND] != alpha_combine || + rmesa->hw.pix[unit].cmd[PIX_PP_TXCBLEND2] != color_scale || + rmesa->hw.pix[unit].cmd[PIX_PP_TXABLEND2] != alpha_scale) { + R300_STATECHANGE(rmesa, pix[unit]); + rmesa->hw.pix[unit].cmd[PIX_PP_TXCBLEND] = color_combine; + rmesa->hw.pix[unit].cmd[PIX_PP_TXABLEND] = alpha_combine; + rmesa->hw.pix[unit].cmd[PIX_PP_TXCBLEND2] = color_scale; + rmesa->hw.pix[unit].cmd[PIX_PP_TXABLEND2] = alpha_scale; + } + + #endif + + return GL_TRUE; +} + +#define TEXOBJ_TXFILTER_MASK (R200_MAX_MIP_LEVEL_MASK | \ + R200_MIN_FILTER_MASK | \ + R200_MAG_FILTER_MASK | \ + R200_MAX_ANISO_MASK | \ + R200_YUV_TO_RGB | \ + R200_YUV_TEMPERATURE_MASK | \ + R200_CLAMP_S_MASK | \ + R200_CLAMP_T_MASK | \ + R200_BORDER_MODE_D3D ) + +#define TEXOBJ_TXFORMAT_MASK (R200_TXFORMAT_WIDTH_MASK | \ + R200_TXFORMAT_HEIGHT_MASK | \ + R200_TXFORMAT_FORMAT_MASK | \ + R200_TXFORMAT_F5_WIDTH_MASK | \ + R200_TXFORMAT_F5_HEIGHT_MASK | \ + R200_TXFORMAT_ALPHA_IN_MAP | \ + R200_TXFORMAT_CUBIC_MAP_ENABLE | \ + R200_TXFORMAT_NON_POWER2) + +#define TEXOBJ_TXFORMAT_X_MASK (R200_DEPTH_LOG2_MASK | \ + R200_TEXCOORD_MASK | \ + R200_CLAMP_Q_MASK | \ + R200_VOLUME_FILTER_MASK) + +static void import_tex_obj_state(r300ContextPtr rmesa, + int unit, r300TexObjPtr texobj) +{ + #if 0 /* needs fixing.. or should be done elsewhere */ + GLuint *cmd = R300_DB_STATE(tex[unit]); + + cmd[TEX_PP_TXFILTER] &= ~TEXOBJ_TXFILTER_MASK; + cmd[TEX_PP_TXFILTER] |= texobj->filter & TEXOBJ_TXFILTER_MASK; + cmd[TEX_PP_TXFORMAT] &= ~TEXOBJ_TXFORMAT_MASK; + cmd[TEX_PP_TXFORMAT] |= texobj->format & TEXOBJ_TXFORMAT_MASK; + cmd[TEX_PP_TXFORMAT_X] &= ~TEXOBJ_TXFORMAT_X_MASK; + cmd[TEX_PP_TXFORMAT_X] |= + texobj->format_x & TEXOBJ_TXFORMAT_X_MASK; + cmd[TEX_PP_TXSIZE] = texobj->size; /* NPOT only! */ + cmd[TEX_PP_TXPITCH] = texobj->pitch; /* NPOT only! */ + cmd[TEX_PP_TXOFFSET] = texobj->pp_txoffset; + cmd[TEX_PP_BORDER_COLOR] = texobj->pp_border_color; + R200_DB_STATECHANGE(rmesa, &rmesa->hw.tex[unit]); + + if (texobj->base.tObj->Target == GL_TEXTURE_CUBE_MAP) { + GLuint *cube_cmd = R200_DB_STATE(cube[unit]); + GLuint bytesPerFace = texobj->base.totalSize / 6; + ASSERT(texobj->totalSize % 6 == 0); + cube_cmd[CUBE_PP_CUBIC_FACES] = texobj->pp_cubic_faces; + cube_cmd[CUBE_PP_CUBIC_OFFSET_F1] = + texobj->pp_txoffset + 1 * bytesPerFace; + cube_cmd[CUBE_PP_CUBIC_OFFSET_F2] = + texobj->pp_txoffset + 2 * bytesPerFace; + cube_cmd[CUBE_PP_CUBIC_OFFSET_F3] = + texobj->pp_txoffset + 3 * bytesPerFace; + cube_cmd[CUBE_PP_CUBIC_OFFSET_F4] = + texobj->pp_txoffset + 4 * bytesPerFace; + cube_cmd[CUBE_PP_CUBIC_OFFSET_F5] = + texobj->pp_txoffset + 5 * bytesPerFace; + R200_DB_STATECHANGE(rmesa, &rmesa->hw.cube[unit]); + } + + texobj->dirty_state &= ~(1 << unit); + #endif +} + +static void set_texgen_matrix(r300ContextPtr rmesa, + GLuint unit, + const GLfloat * s_plane, + const GLfloat * t_plane, const GLfloat * r_plane) +{ + static const GLfloat scale_identity[4] = { 1, 1, 1, 1 }; + + if (!TEST_EQ_4V(s_plane, scale_identity) || + !TEST_EQ_4V(t_plane, scale_identity) || + !TEST_EQ_4V(r_plane, scale_identity)) { + rmesa->TexGenEnabled |= R200_TEXMAT_0_ENABLE << unit; + rmesa->TexGenMatrix[unit].m[0] = s_plane[0]; + rmesa->TexGenMatrix[unit].m[4] = s_plane[1]; + rmesa->TexGenMatrix[unit].m[8] = s_plane[2]; + rmesa->TexGenMatrix[unit].m[12] = s_plane[3]; + + rmesa->TexGenMatrix[unit].m[1] = t_plane[0]; + rmesa->TexGenMatrix[unit].m[5] = t_plane[1]; + rmesa->TexGenMatrix[unit].m[9] = t_plane[2]; + rmesa->TexGenMatrix[unit].m[13] = t_plane[3]; + + /* NOTE: r_plane goes in the 4th row, not 3rd! */ + rmesa->TexGenMatrix[unit].m[3] = r_plane[0]; + rmesa->TexGenMatrix[unit].m[7] = r_plane[1]; + rmesa->TexGenMatrix[unit].m[11] = r_plane[2]; + rmesa->TexGenMatrix[unit].m[15] = r_plane[3]; + + //rmesa->NewGLState |= _NEW_TEXTURE_MATRIX; + } +} + +/* Need this special matrix to get correct reflection map coords */ +static void set_texgen_reflection_matrix(r300ContextPtr rmesa, GLuint unit) +{ + static const GLfloat m[16] = { + -1, 0, 0, 0, + 0, -1, 0, 0, + 0, 0, 0, -1, + 0, 0, -1, 0 + }; + _math_matrix_loadf(&(rmesa->TexGenMatrix[unit]), m); + _math_matrix_analyse(&(rmesa->TexGenMatrix[unit])); + rmesa->TexGenEnabled |= R200_TEXMAT_0_ENABLE << unit; +} + +/* Need this special matrix to get correct normal map coords */ +static void set_texgen_normal_map_matrix(r300ContextPtr rmesa, GLuint unit) +{ + static const GLfloat m[16] = { + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 0, 1, + 0, 0, 1, 0 + }; + _math_matrix_loadf(&(rmesa->TexGenMatrix[unit]), m); + _math_matrix_analyse(&(rmesa->TexGenMatrix[unit])); + rmesa->TexGenEnabled |= R200_TEXMAT_0_ENABLE << unit; +} + +/* Ignoring the Q texcoord for now. + * + * Returns GL_FALSE if fallback required. + */ +static GLboolean r300_validate_texgen(GLcontext * ctx, GLuint unit) +{ + r300ContextPtr rmesa = R300_CONTEXT(ctx); + const struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit]; + GLuint inputshift = R200_TEXGEN_0_INPUT_SHIFT + unit * 4; + GLuint tmp = rmesa->TexGenEnabled; + + rmesa->TexGenCompSel &= ~(R200_OUTPUT_TEX_0 << unit); + rmesa->TexGenEnabled &= ~(R200_TEXGEN_TEXMAT_0_ENABLE << unit); + rmesa->TexGenEnabled &= ~(R200_TEXMAT_0_ENABLE << unit); + rmesa->TexGenInputs &= ~(R200_TEXGEN_INPUT_MASK << inputshift); + rmesa->TexGenNeedNormals[unit] = 0; + + if (0) + fprintf(stderr, "%s unit %d\n", __FUNCTION__, unit); + + if ((texUnit->TexGenEnabled & (S_BIT | T_BIT | R_BIT)) == 0) { + /* Disabled, no fallback: + */ + rmesa->TexGenInputs |= + (R200_TEXGEN_INPUT_TEXCOORD_0 + unit) << inputshift; + return GL_TRUE; + } else if (texUnit->TexGenEnabled & Q_BIT) { + /* Very easy to do this, in fact would remove a fallback case + * elsewhere, but I haven't done it yet... Fallback: + */ + /*fprintf(stderr, "fallback Q_BIT\n"); */ + return GL_FALSE; + } else if (texUnit->TexGenEnabled == (S_BIT | T_BIT) && + texUnit->GenModeS == texUnit->GenModeT) { + /* OK */ + rmesa->TexGenEnabled |= R200_TEXGEN_TEXMAT_0_ENABLE << unit; + /* continue */ + } else if (texUnit->TexGenEnabled == (S_BIT | T_BIT | R_BIT) && + texUnit->GenModeS == texUnit->GenModeT && + texUnit->GenModeT == texUnit->GenModeR) { + /* OK */ + rmesa->TexGenEnabled |= R200_TEXGEN_TEXMAT_0_ENABLE << unit; + /* continue */ + } else { + /* Mixed modes, fallback: + */ + /* fprintf(stderr, "fallback mixed texgen\n"); */ + return GL_FALSE; + } + + rmesa->TexGenEnabled |= R200_TEXGEN_TEXMAT_0_ENABLE << unit; + + switch (texUnit->GenModeS) { + case GL_OBJECT_LINEAR: + rmesa->TexGenInputs |= R200_TEXGEN_INPUT_OBJ << inputshift; + set_texgen_matrix(rmesa, unit, + texUnit->ObjectPlaneS, + texUnit->ObjectPlaneT, texUnit->ObjectPlaneR); + break; + + case GL_EYE_LINEAR: + rmesa->TexGenInputs |= R200_TEXGEN_INPUT_EYE << inputshift; + set_texgen_matrix(rmesa, unit, + texUnit->EyePlaneS, + texUnit->EyePlaneT, texUnit->EyePlaneR); + break; + + case GL_REFLECTION_MAP_NV: + rmesa->TexGenNeedNormals[unit] = GL_TRUE; + rmesa->TexGenInputs |= + R200_TEXGEN_INPUT_EYE_REFLECT << inputshift; + set_texgen_reflection_matrix(rmesa, unit); + break; + + case GL_NORMAL_MAP_NV: + rmesa->TexGenNeedNormals[unit] = GL_TRUE; + rmesa->TexGenInputs |= + R200_TEXGEN_INPUT_EYE_NORMAL << inputshift; + set_texgen_normal_map_matrix(rmesa, unit); + break; + + case GL_SPHERE_MAP: + rmesa->TexGenNeedNormals[unit] = GL_TRUE; + rmesa->TexGenInputs |= R200_TEXGEN_INPUT_SPHERE << inputshift; + break; + + default: + /* Unsupported mode, fallback: + */ + /* fprintf(stderr, "fallback unsupported texgen\n"); */ + return GL_FALSE; + } + + rmesa->TexGenCompSel |= R200_OUTPUT_TEX_0 << unit; + + if (tmp != rmesa->TexGenEnabled) { + //rmesa->NewGLState |= _NEW_TEXTURE_MATRIX; + } + + return GL_TRUE; +} + +static void disable_tex(GLcontext * ctx, int unit) +{ + r300ContextPtr rmesa = R300_CONTEXT(ctx); + + #if 0 /* This needs to be redone.. or done elsewhere */ + if (rmesa->hw.ctx.cmd[CTX_PP_CNTL] & (R200_TEX_0_ENABLE << unit)) { + /* Texture unit disabled */ + if (rmesa->state.texture.unit[unit].texobj != NULL) { + /* The old texture is no longer bound to this texture unit. + * Mark it as such. + */ + + rmesa->state.texture.unit[unit].texobj->base.bound &= + ~(1UL << unit); + rmesa->state.texture.unit[unit].texobj = NULL; + } + + R300_STATECHANGE(rmesa, ctx); + rmesa->hw.ctx.cmd[CTX_PP_CNTL] &= ~((R200_TEX_0_ENABLE | + R200_TEX_BLEND_0_ENABLE) << + unit); + rmesa->hw.ctx.cmd[CTX_PP_CNTL] |= R200_TEX_BLEND_0_ENABLE; + + R300_STATECHANGE(rmesa, tcl); + rmesa->hw.vtx.cmd[VTX_TCL_OUTPUT_VTXFMT_1] &= + ~(7 << (unit * 3)); + + if (rmesa->radeon.TclFallback & (RADEON_TCL_FALLBACK_TEXGEN_0 << unit)) { + TCL_FALLBACK(ctx, (RADEON_TCL_FALLBACK_TEXGEN_0 << unit), + GL_FALSE); + } + + /* Actually want to keep all units less than max active texture + * enabled, right? Fix this for >2 texunits. + */ + /* FIXME: What should happen here if r300UpdateTextureEnv fails? */ + if (unit == 0) + r300UpdateTextureEnv(ctx, unit); + + { + GLuint inputshift = + R200_TEXGEN_0_INPUT_SHIFT + unit * 4; + GLuint tmp = rmesa->TexGenEnabled; + + rmesa->TexGenEnabled &= + ~(R200_TEXGEN_TEXMAT_0_ENABLE << unit); + rmesa->TexGenEnabled &= ~(R200_TEXMAT_0_ENABLE << unit); + rmesa->TexGenEnabled &= + ~(R200_TEXGEN_INPUT_MASK << inputshift); + rmesa->TexGenNeedNormals[unit] = 0; + rmesa->TexGenCompSel &= ~(R200_OUTPUT_TEX_0 << unit); + rmesa->TexGenInputs &= + ~(R200_TEXGEN_INPUT_MASK << inputshift); + + if (tmp != rmesa->TexGenEnabled) { + rmesa->recheck_texgen[unit] = GL_TRUE; + rmesa->NewGLState |= _NEW_TEXTURE_MATRIX; + } + } + } + #endif +} + +static GLboolean enable_tex_2d(GLcontext * ctx, int unit) +{ + r300ContextPtr rmesa = R300_CONTEXT(ctx); + struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit]; + struct gl_texture_object *tObj = texUnit->_Current; + r300TexObjPtr t = (r300TexObjPtr) tObj->DriverData; + + /* Need to load the 2d images associated with this unit. + */ + if (t->format & R200_TXFORMAT_NON_POWER2) { + t->format &= ~R200_TXFORMAT_NON_POWER2; + t->base.dirty_images[0] = ~0; + } + + ASSERT(tObj->Target == GL_TEXTURE_2D || tObj->Target == GL_TEXTURE_1D); + + if (t->base.dirty_images[0]) { + R300_FIREVERTICES(rmesa); + r300SetTexImages(rmesa, tObj); + r300UploadTexImages(rmesa, (r300TexObjPtr) tObj->DriverData, 0); + if (!t->base.memBlock) + return GL_FALSE; + } + + return GL_TRUE; +} + +#if ENABLE_HW_3D_TEXTURE +static GLboolean enable_tex_3d(GLcontext * ctx, int unit) +{ + r300ContextPtr rmesa = R300_CONTEXT(ctx); + struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit]; + struct gl_texture_object *tObj = texUnit->_Current; + r300TexObjPtr t = (r300TexObjPtr) tObj->DriverData; + + /* Need to load the 3d images associated with this unit. + */ + if (t->format & R200_TXFORMAT_NON_POWER2) { + t->format &= ~R200_TXFORMAT_NON_POWER2; + t->base.dirty_images[0] = ~0; + } + + ASSERT(tObj->Target == GL_TEXTURE_3D); + + /* R100 & R200 do not support mipmaps for 3D textures. + */ + if ((tObj->MinFilter != GL_NEAREST) && (tObj->MinFilter != GL_LINEAR)) { + return GL_FALSE; + } + + if (t->base.dirty_images[0]) { + R300_FIREVERTICES(rmesa); + r300SetTexImages(rmesa, tObj); + r300UploadTexImages(rmesa, (r300TexObjPtr) tObj->DriverData, 0); + if (!t->base.memBlock) + return GL_FALSE; + } + + return GL_TRUE; +} +#endif + +static GLboolean enable_tex_cube(GLcontext * ctx, int unit) +{ + r300ContextPtr rmesa = R300_CONTEXT(ctx); + struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit]; + struct gl_texture_object *tObj = texUnit->_Current; + r300TexObjPtr t = (r300TexObjPtr) tObj->DriverData; + GLuint face; + + /* Need to load the 2d images associated with this unit. + */ + if (t->format & R200_TXFORMAT_NON_POWER2) { + t->format &= ~R200_TXFORMAT_NON_POWER2; + for (face = 0; face < 6; face++) + t->base.dirty_images[face] = ~0; + } + + ASSERT(tObj->Target == GL_TEXTURE_CUBE_MAP); + + if (t->base.dirty_images[0] || t->base.dirty_images[1] || + t->base.dirty_images[2] || t->base.dirty_images[3] || + t->base.dirty_images[4] || t->base.dirty_images[5]) { + /* flush */ + R300_FIREVERTICES(rmesa); + /* layout memory space, once for all faces */ + r300SetTexImages(rmesa, tObj); + } + + /* upload (per face) */ + for (face = 0; face < 6; face++) { + if (t->base.dirty_images[face]) { + r300UploadTexImages(rmesa, + (r300TexObjPtr) tObj->DriverData, + face); + } + } + + if (!t->base.memBlock) { + /* texmem alloc failed, use s/w fallback */ + return GL_FALSE; + } + + return GL_TRUE; +} + +static GLboolean enable_tex_rect(GLcontext * ctx, int unit) +{ + r300ContextPtr rmesa = R300_CONTEXT(ctx); + struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit]; + struct gl_texture_object *tObj = texUnit->_Current; + r300TexObjPtr t = (r300TexObjPtr) tObj->DriverData; + + if (!(t->format & R200_TXFORMAT_NON_POWER2)) { + t->format |= R200_TXFORMAT_NON_POWER2; + t->base.dirty_images[0] = ~0; + } + + ASSERT(tObj->Target == GL_TEXTURE_RECTANGLE_NV); + + if (t->base.dirty_images[0]) { + R300_FIREVERTICES(rmesa); + r300SetTexImages(rmesa, tObj); + r300UploadTexImages(rmesa, (r300TexObjPtr) tObj->DriverData, 0); + if (!t->base.memBlock && !rmesa->prefer_gart_client_texturing) + return GL_FALSE; + } + + return GL_TRUE; +} + +static GLboolean update_tex_common(GLcontext * ctx, int unit) +{ + r300ContextPtr rmesa = R300_CONTEXT(ctx); + struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit]; + struct gl_texture_object *tObj = texUnit->_Current; + r300TexObjPtr t = (r300TexObjPtr) tObj->DriverData; + GLenum format; + + /* Fallback if there's a texture border */ + if (tObj->Image[0][tObj->BaseLevel]->Border > 0) + return GL_FALSE; + + /* Update state if this is a different texture object to last + * time. + */ + if (rmesa->state.texture.unit[unit].texobj != t) { + if (rmesa->state.texture.unit[unit].texobj != NULL) { + /* The old texture is no longer bound to this texture unit. + * Mark it as such. + */ + + rmesa->state.texture.unit[unit].texobj->base.bound &= + ~(1UL << unit); + } + + rmesa->state.texture.unit[unit].texobj = t; + t->base.bound |= (1UL << unit); + t->dirty_state |= 1 << unit; + driUpdateTextureLRU((driTextureObject *) t); /* XXX: should be locked! */ + } + + #if 0 /* do elsewhere ? */ + /* Newly enabled? + */ + if (1 + || !(rmesa->hw.ctx. + cmd[CTX_PP_CNTL] & (R200_TEX_0_ENABLE << unit))) { + R300_STATECHANGE(rmesa, ctx); + rmesa->hw.ctx.cmd[CTX_PP_CNTL] |= (R200_TEX_0_ENABLE | + R200_TEX_BLEND_0_ENABLE) << + unit; + + R300_STATECHANGE(rmesa, vtx); + rmesa->hw.vtx.cmd[VTX_TCL_OUTPUT_VTXFMT_1] |= 4 << (unit * 3); + + rmesa->recheck_texgen[unit] = GL_TRUE; + } + + if (t->dirty_state & (1 << unit)) { + import_tex_obj_state(rmesa, unit, t); + } + + if (rmesa->recheck_texgen[unit]) { + GLboolean fallback = !r300_validate_texgen(ctx, unit); + TCL_FALLBACK(ctx, (RADEON_TCL_FALLBACK_TEXGEN_0 << unit), + fallback); + rmesa->recheck_texgen[unit] = 0; + rmesa->NewGLState |= _NEW_TEXTURE_MATRIX; + } + + format = tObj->Image[0][tObj->BaseLevel]->Format; + if (rmesa->state.texture.unit[unit].format != format || + rmesa->state.texture.unit[unit].envMode != texUnit->EnvMode) { + rmesa->state.texture.unit[unit].format = format; + rmesa->state.texture.unit[unit].envMode = texUnit->EnvMode; + if (!r300UpdateTextureEnv(ctx, unit)) { + return GL_FALSE; + } + } + #endif + + FALLBACK(&rmesa->radeon, RADEON_FALLBACK_BORDER_MODE, t->border_fallback); + return !t->border_fallback; +} + +static GLboolean r300UpdateTextureUnit(GLcontext * ctx, int unit) +{ + struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit]; + + if (texUnit->_ReallyEnabled & (TEXTURE_RECT_BIT)) { + return (enable_tex_rect(ctx, unit) && + update_tex_common(ctx, unit)); + } else if (texUnit->_ReallyEnabled & (TEXTURE_1D_BIT | TEXTURE_2D_BIT)) { + return (enable_tex_2d(ctx, unit) && + update_tex_common(ctx, unit)); + } +#if ENABLE_HW_3D_TEXTURE + else if (texUnit->_ReallyEnabled & (TEXTURE_3D_BIT)) { + return (enable_tex_3d(ctx, unit) && + update_tex_common(ctx, unit)); + } +#endif + else if (texUnit->_ReallyEnabled & (TEXTURE_CUBE_BIT)) { + return (enable_tex_cube(ctx, unit) && + update_tex_common(ctx, unit)); + } else if (texUnit->_ReallyEnabled) { + return GL_FALSE; + } else { + disable_tex(ctx, unit); + return GL_TRUE; + } +} + +void r300UpdateTextureState(GLcontext * ctx) +{ + r300ContextPtr rmesa = R300_CONTEXT(ctx); + GLboolean ok; + GLuint dbg; + int i; + + ok = (r300UpdateTextureUnit(ctx, 0) && + r300UpdateTextureUnit(ctx, 1) && + r300UpdateTextureUnit(ctx, 2) && + r300UpdateTextureUnit(ctx, 3) && + r300UpdateTextureUnit(ctx, 4) && + r300UpdateTextureUnit(ctx, 5) && + r300UpdateTextureUnit(ctx, 6) && + r300UpdateTextureUnit(ctx, 7) + ); + + FALLBACK(&rmesa->radeon, RADEON_FALLBACK_TEXTURE, !ok); + + /* This needs correction, or just be done elsewhere + if (rmesa->radeon.TclFallback) + r300ChooseVertexState(ctx); + */ + + #if 0 /* Workaround - disable.. */ + if (GET_CHIP(rmesa->radeon.radeonScreen) == RADEON_CHIP_REAL_R200) { + /* + * T0 hang workaround ------------- + * not needed for r300 derivatives? + */ + if ((rmesa->hw.ctx.cmd[CTX_PP_CNTL] & R200_TEX_ENABLE_MASK) == + R200_TEX_0_ENABLE + && (rmesa->hw.tex[0]. + cmd[TEX_PP_TXFILTER] & R200_MIN_FILTER_MASK) > + R200_MIN_FILTER_LINEAR) { + + R300_STATECHANGE(rmesa, ctx); + R300_STATECHANGE(rmesa, tex[1]); + rmesa->hw.ctx.cmd[CTX_PP_CNTL] |= R200_TEX_1_ENABLE; + rmesa->hw.tex[1].cmd[TEX_PP_TXFORMAT] &= + ~TEXOBJ_TXFORMAT_MASK; + rmesa->hw.tex[1].cmd[TEX_PP_TXFORMAT] |= 0x08000000; + } else { + if ((rmesa->hw.ctx.cmd[CTX_PP_CNTL] & R200_TEX_1_ENABLE) + && (rmesa->hw.tex[1]. + cmd[TEX_PP_TXFORMAT] & 0x08000000)) { + R300_STATECHANGE(rmesa, tex[1]); + rmesa->hw.tex[1].cmd[TEX_PP_TXFORMAT] &= + ~0x08000000; + } + } + + /* maybe needs to be done pairwise due to 2 parallel (physical) tex units ? + looks like that's not the case, if 8500/9100 owners don't complain remove this... + for ( i = 0; i < ctx->Const.MaxTextureUnits; i += 2) { + if (((rmesa->hw.ctx.cmd[CTX_PP_CNTL] & ((R200_TEX_0_ENABLE | + R200_TEX_1_ENABLE ) << i)) == (R200_TEX_0_ENABLE << i)) && + ((rmesa->hw.tex[i].cmd[TEX_PP_TXFILTER] & R200_MIN_FILTER_MASK) > + R200_MIN_FILTER_LINEAR)) { + R300_STATECHANGE(rmesa, ctx); + R300_STATECHANGE(rmesa, tex[i+1]); + rmesa->hw.ctx.cmd[CTX_PP_CNTL] |= (R200_TEX_1_ENABLE << i); + rmesa->hw.tex[i+1].cmd[TEX_PP_TXFORMAT] &= ~TEXOBJ_TXFORMAT_MASK; + rmesa->hw.tex[i+1].cmd[TEX_PP_TXFORMAT] |= 0x08000000; + } + else { + if ((rmesa->hw.ctx.cmd[CTX_PP_CNTL] & (R200_TEX_1_ENABLE << i)) && + (rmesa->hw.tex[i+1].cmd[TEX_PP_TXFORMAT] & 0x08000000)) { + R300_STATECHANGE(rmesa, tex[i+1]); + rmesa->hw.tex[i+1].cmd[TEX_PP_TXFORMAT] &= ~0x08000000; + } + } + } */ + + /* + * Texture cache LRU hang workaround ------------- + * not needed for r300 derivatives? + */ + dbg = 0x0; + + if (((rmesa->hw.ctx.cmd[CTX_PP_CNTL] & (R200_TEX_0_ENABLE)) && + ((((rmesa->hw.tex[0]. + cmd[TEX_PP_TXFILTER] & R200_MIN_FILTER_MASK)) & 0x04) + == 0)) + || ((rmesa->hw.ctx.cmd[CTX_PP_CNTL] & R200_TEX_2_ENABLE) + && + ((((rmesa->hw.tex[2]. + cmd[TEX_PP_TXFILTER] & R200_MIN_FILTER_MASK)) & + 0x04) == 0)) + || ((rmesa->hw.ctx.cmd[CTX_PP_CNTL] & R200_TEX_4_ENABLE) + && + ((((rmesa->hw.tex[4]. + cmd[TEX_PP_TXFILTER] & R200_MIN_FILTER_MASK)) & + 0x04) == 0))) { + dbg |= 0x02; + } + + if (((rmesa->hw.ctx.cmd[CTX_PP_CNTL] & (R200_TEX_1_ENABLE)) && + ((((rmesa->hw.tex[1]. + cmd[TEX_PP_TXFILTER] & R200_MIN_FILTER_MASK)) & 0x04) + == 0)) + || ((rmesa->hw.ctx.cmd[CTX_PP_CNTL] & R200_TEX_3_ENABLE) + && + ((((rmesa->hw.tex[3]. + cmd[TEX_PP_TXFILTER] & R200_MIN_FILTER_MASK)) & + 0x04) == 0)) + || ((rmesa->hw.ctx.cmd[CTX_PP_CNTL] & R200_TEX_5_ENABLE) + && + ((((rmesa->hw.tex[5]. + cmd[TEX_PP_TXFILTER] & R200_MIN_FILTER_MASK)) & + 0x04) == 0))) { + dbg |= 0x04; + } + + if (dbg != rmesa->hw.tam.cmd[TAM_DEBUG3]) { + R300_STATECHANGE(rmesa, tam); + rmesa->hw.tam.cmd[TAM_DEBUG3] = dbg; + if (0) + printf("TEXCACHE LRU HANG WORKAROUND %x\n", + dbg); + } + } + #endif +} -- cgit v1.2.3