/************************************************************************** * * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. * All Rights Reserved. * **************************************************************************/ #include "glheader.h" #include "enums.h" #include "mtypes.h" #include "macros.h" #include "utils.h" #include "intel_screen.h" #include "intel_batchbuffer.h" #include "intel_ioctl.h" #include "i830_context.h" #include "i830_reg.h" /* A large amount of state doesn't need to be uploaded. */ #define ACTIVE (I830_UPLOAD_TEXBLEND(0) | \ I830_UPLOAD_STIPPLE | \ I830_UPLOAD_CTX | \ I830_UPLOAD_BUFFERS | \ I830_UPLOAD_TEX(0)) #define SET_STATE( i830, STATE ) \ do { \ i830->current->emitted &= ~ACTIVE; \ i830->current = &i830->STATE; \ i830->current->emitted &= ~ACTIVE; \ } while (0) /* Operations where the 3D engine is decoupled temporarily from the * current GL state and used for other purposes than simply rendering * incoming triangles. */ static void set_initial_state( i830ContextPtr i830 ) { memcpy(&i830->meta, &i830->initial, sizeof(i830->meta) ); i830->meta.active = ACTIVE; i830->meta.emitted = 0; } static void set_no_depth_stencil_write( i830ContextPtr i830 ) { /* ctx->Driver.Enable( ctx, GL_STENCIL_TEST, GL_FALSE ) */ i830->meta.Ctx[I830_CTXREG_ENABLES_1] &= ~ENABLE_STENCIL_TEST; i830->meta.Ctx[I830_CTXREG_ENABLES_2] &= ~ENABLE_STENCIL_WRITE; i830->meta.Ctx[I830_CTXREG_ENABLES_1] |= DISABLE_STENCIL_TEST; i830->meta.Ctx[I830_CTXREG_ENABLES_2] |= DISABLE_STENCIL_WRITE; /* ctx->Driver.Enable( ctx, GL_DEPTH_TEST, GL_FALSE ) */ i830->meta.Ctx[I830_CTXREG_ENABLES_1] &= ~ENABLE_DIS_DEPTH_TEST_MASK; i830->meta.Ctx[I830_CTXREG_ENABLES_2] &= ~ENABLE_DIS_DEPTH_WRITE_MASK; i830->meta.Ctx[I830_CTXREG_ENABLES_1] |= DISABLE_DEPTH_TEST; i830->meta.Ctx[I830_CTXREG_ENABLES_2] |= DISABLE_DEPTH_WRITE; i830->meta.emitted &= ~I830_UPLOAD_CTX; } /* Set stencil unit to replace always with the reference value. */ static void set_stencil_replace( i830ContextPtr i830, GLuint s_mask, GLuint s_clear) { /* ctx->Driver.Enable( ctx, GL_STENCIL_TEST, GL_TRUE ) */ i830->meta.Ctx[I830_CTXREG_ENABLES_1] |= ENABLE_STENCIL_TEST; i830->meta.Ctx[I830_CTXREG_ENABLES_2] |= ENABLE_STENCIL_WRITE; /* ctx->Driver.Enable( ctx, GL_DEPTH_TEST, GL_FALSE ) */ i830->meta.Ctx[I830_CTXREG_ENABLES_1] &= ~ENABLE_DIS_DEPTH_TEST_MASK; i830->meta.Ctx[I830_CTXREG_ENABLES_2] &= ~ENABLE_DIS_DEPTH_WRITE_MASK; i830->meta.Ctx[I830_CTXREG_ENABLES_1] |= DISABLE_DEPTH_TEST; i830->meta.Ctx[I830_CTXREG_ENABLES_2] |= DISABLE_DEPTH_WRITE; /* ctx->Driver.StencilMask( ctx, s_mask ) */ i830->meta.Ctx[I830_CTXREG_STATE4] &= ~MODE4_ENABLE_STENCIL_WRITE_MASK; i830->meta.Ctx[I830_CTXREG_STATE4] |= (ENABLE_STENCIL_WRITE_MASK | STENCIL_WRITE_MASK((s_mask&0xff))); /* ctx->Driver.StencilOp( ctx, GL_REPLACE, GL_REPLACE, GL_REPLACE ) */ i830->meta.Ctx[I830_CTXREG_STENCILTST] &= ~(STENCIL_OPS_MASK); i830->meta.Ctx[I830_CTXREG_STENCILTST] |= (ENABLE_STENCIL_PARMS | STENCIL_FAIL_OP(STENCILOP_REPLACE) | STENCIL_PASS_DEPTH_FAIL_OP(STENCILOP_REPLACE) | STENCIL_PASS_DEPTH_PASS_OP(STENCILOP_REPLACE)); /* ctx->Driver.StencilFunc( ctx, GL_ALWAYS, s_clear, ~0 ) */ i830->meta.Ctx[I830_CTXREG_STATE4] &= ~MODE4_ENABLE_STENCIL_TEST_MASK; i830->meta.Ctx[I830_CTXREG_STATE4] |= (ENABLE_STENCIL_TEST_MASK | STENCIL_TEST_MASK(0xff)); i830->meta.Ctx[I830_CTXREG_STENCILTST] &= ~(STENCIL_REF_VALUE_MASK | ENABLE_STENCIL_TEST_FUNC_MASK); i830->meta.Ctx[I830_CTXREG_STENCILTST] |= (ENABLE_STENCIL_REF_VALUE | ENABLE_STENCIL_TEST_FUNC | STENCIL_REF_VALUE((s_clear&0xff)) | STENCIL_TEST_FUNC(COMPAREFUNC_ALWAYS)); i830->meta.emitted &= ~I830_UPLOAD_CTX; } static void set_color_mask( i830ContextPtr i830, GLboolean state ) { const GLuint mask = ((1 << WRITEMASK_RED_SHIFT) | (1 << WRITEMASK_GREEN_SHIFT) | (1 << WRITEMASK_BLUE_SHIFT) | (1 << WRITEMASK_ALPHA_SHIFT)); if (state) { i830->meta.Ctx[I830_CTXREG_ENABLES_2] &= ~mask; i830->meta.Ctx[I830_CTXREG_ENABLES_2] |= (i830->state.Ctx[I830_CTXREG_ENABLES_2] & mask); } else i830->meta.Ctx[I830_CTXREG_ENABLES_2] |= mask; i830->meta.emitted &= ~I830_UPLOAD_CTX; } /* Installs a one-stage passthrough texture blend pipeline. Is there * more that can be done to turn off texturing? */ static void set_no_texture( i830ContextPtr i830 ) { i830->meta.TexBlendWordsUsed[0] = i830SetBlend_GL1_2( i830, 0, GL_NONE, 0, TEXBLENDARG_TEXEL0, i830->meta.TexBlend[0], 0 ); i830->meta.TexBlend[0][0] |= TEXOP_LAST_STAGE; i830->meta.emitted &= ~I830_UPLOAD_TEXBLEND(0); } /* Set up a single element blend stage for 'replace' texturing with no * funny ops. */ static void enable_texture_blend_replace( i830ContextPtr i830, GLenum format ) { i830->meta.TexBlendWordsUsed[0] = i830SetBlend_GL1_2( i830, 0, GL_REPLACE, format, TEXBLENDARG_TEXEL0, i830->meta.TexBlend[0], 0 ); i830->meta.TexBlend[0][0] |= TEXOP_LAST_STAGE; i830->meta.emitted &= ~I830_UPLOAD_TEXBLEND(0); /* fprintf(stderr, "%s: TexBlendWordsUsed[0]: %d\n", */ /* __FUNCTION__, i830->meta.TexBlendWordsUsed[0]); */ } /* Set up an arbitary piece of memory as a rectangular texture * (including the front or back buffer). */ static void set_tex_rect_source( i830ContextPtr i830, GLuint offset, GLuint width, GLuint height, GLuint pitch, GLuint textureFormat ) { GLint numLevels = 1; GLuint *setup = i830->meta.Tex[0]; pitch *= i830->intel.intelScreen->cpp; /* fprintf(stderr, "%s: offset: %x w: %d h: %d pitch %d format %x\n", */ /* __FUNCTION__, offset, width, height, pitch, textureFormat ); */ setup[I830_TEXREG_TM0LI] = (_3DSTATE_LOAD_STATE_IMMEDIATE_2 | (LOAD_TEXTURE_MAP0 << 0) | 4); setup[I830_TEXREG_TM0S0] = (TM0S0_USE_FENCE | offset); setup[I830_TEXREG_TM0S1] = (((height - 1) << TM0S1_HEIGHT_SHIFT) | ((width - 1) << TM0S1_WIDTH_SHIFT) | textureFormat); setup[I830_TEXREG_TM0S2] = ((((pitch / 4) - 1) << TM0S2_PITCH_SHIFT)); setup[I830_TEXREG_TM0S3] &= ~TM0S3_MAX_MIP_MASK; setup[I830_TEXREG_TM0S3] &= ~TM0S3_MIN_MIP_MASK; setup[I830_TEXREG_TM0S3] |= ((numLevels - 1)*4) << TM0S3_MIN_MIP_SHIFT; setup[I830_TEXREG_MCS] = (_3DSTATE_MAP_COORD_SET_CMD | MAP_UNIT(0) | ENABLE_TEXCOORD_PARAMS | TEXCOORDS_ARE_IN_TEXELUNITS | TEXCOORDTYPE_CARTESIAN | ENABLE_ADDR_V_CNTL | TEXCOORD_ADDR_V_MODE(TEXCOORDMODE_WRAP) | ENABLE_ADDR_U_CNTL | TEXCOORD_ADDR_U_MODE(TEXCOORDMODE_WRAP)); i830->meta.emitted &= ~I830_UPLOAD_TEX(0); } /* Select between front and back draw buffers. */ static void set_draw_offset( i830ContextPtr i830, GLuint offset ) { i830->meta.Buffer[I830_DESTREG_CBUFADDR2] = offset; i830->meta.emitted &= ~I830_UPLOAD_BUFFERS; } /* Setup an arbitary draw format, useful for targeting * texture or agp memory. */ static void set_draw_format( i830ContextPtr i830, GLuint format, GLuint depth_format) { i830->meta.Buffer[I830_DESTREG_DV1] = (DSTORG_HORT_BIAS(0x8) | /* .5 */ DSTORG_VERT_BIAS(0x8) | /* .5 */ format | DEPTH_IS_Z | depth_format); } static void set_vertex_format( i830ContextPtr i830 ) { i830->meta.Ctx[I830_CTXREG_VF] = (_3DSTATE_VFT0_CMD | VFT0_TEX_COUNT(1) | VFT0_DIFFUSE | VFT0_SPEC | VFT0_XYZW); i830->meta.Ctx[I830_CTXREG_VF2] = (_3DSTATE_VFT1_CMD | VFT1_TEX0_FMT(TEXCOORDFMT_2D) | VFT1_TEX1_FMT(TEXCOORDFMT_2D) | VFT1_TEX2_FMT(TEXCOORDFMT_2D) | VFT1_TEX3_FMT(TEXCOORDFMT_2D)); i830->meta.emitted &= ~I830_UPLOAD_CTX; } static void draw_quad(i830ContextPtr i830, GLfloat x0, GLfloat x1, GLfloat y0, GLfloat y1, GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha, GLfloat s0, GLfloat s1, GLfloat t0, GLfloat t1 ) { GLuint vertex_size = 8; GLuint *vb = intelEmitInlinePrimitiveLocked( &i830->intel, PRIM3D_TRIFAN, 4*vertex_size, vertex_size ); intelVertex tmp; int i; /* fprintf(stderr, "%s: %f,%f-%f,%f 0x%x%x%x%x %f,%f-%f,%f\n", */ /* __FUNCTION__, */ /* x0,y0,x1,y1,red,green,blue,alpha,s0,t0,s1,t1); */ /* initial vertex, left bottom */ tmp.v.x = x0; tmp.v.y = y0; tmp.v.z = 1.0; tmp.v.w = 1.0; tmp.v.color.red = red; tmp.v.color.green = green; tmp.v.color.blue = blue; tmp.v.color.alpha = alpha; tmp.v.specular.red = 0; tmp.v.specular.green = 0; tmp.v.specular.blue = 0; tmp.v.specular.alpha = 0; tmp.v.u0 = s0; tmp.v.v0 = t0; for (i = 0 ; i < 8 ; i++) vb[i] = tmp.ui[i]; /* right bottom */ vb += 8; tmp.v.x = x1; tmp.v.u0 = s1; for (i = 0 ; i < 8 ; i++) vb[i] = tmp.ui[i]; /* right top */ vb += 8; tmp.v.y = y1; tmp.v.v0 = t1; for (i = 0 ; i < 8 ; i++) vb[i] = tmp.ui[i]; /* left top */ vb += 8; tmp.v.x = x0; tmp.v.u0 = s0; for (i = 0 ; i < 8 ; i++) vb[i] = tmp.ui[i]; /* fprintf(stderr, "%s: DV1: %x\n", */ /* __FUNCTION__, i830->meta.Buffer[I830_DESTREG_DV1]); */ } void i830ClearWithTris(intelContextPtr intel, GLbitfield mask, GLboolean all, GLint cx, GLint cy, GLint cw, GLint ch) { i830ContextPtr i830 = I830_CONTEXT( intel ); __DRIdrawablePrivate *dPriv = intel->driDrawable; intelScreenPrivate *screen = intel->intelScreen; int x0, y0, x1, y1; SET_STATE( i830, meta ); set_initial_state( i830 ); set_no_texture( i830 ); set_vertex_format( i830 ); LOCK_HARDWARE(intel); if(!all) { x0 = cx; y0 = cy; x1 = x0 + cw; y1 = y0 + ch; } else { x0 = 0; y0 = 0; x1 = x0 + dPriv->w; y1 = y0 + dPriv->h; } /* Don't do any clipping to screen - these are window coordinates. * The active cliprects will be applied as for any other geometry. */ if(mask & DD_FRONT_LEFT_BIT) { set_no_depth_stencil_write( i830 ); set_color_mask( i830, GL_TRUE ); set_draw_offset( i830, screen->frontOffset ); draw_quad(i830, x0, x1, y0, y1, intel->clear_red, intel->clear_green, intel->clear_blue, intel->clear_alpha, 0, 0, 0, 0); } if(mask & DD_BACK_LEFT_BIT) { set_no_depth_stencil_write( i830 ); set_color_mask( i830, GL_TRUE ); set_draw_offset( i830, screen->backOffset ); draw_quad(i830, x0, x1, y0, y1, intel->clear_red, intel->clear_green, intel->clear_blue, intel->clear_alpha, 0, 0, 0, 0); } if(mask & DD_STENCIL_BIT) { set_stencil_replace( i830, intel->ctx.Stencil.WriteMask[0], intel->ctx.Stencil.Clear); set_color_mask( i830, GL_FALSE ); set_draw_offset( i830, screen->frontOffset ); draw_quad( i830, x0, x1, y0, y1, 0, 0, 0, 0, 0, 0, 0, 0 ); } UNLOCK_HARDWARE(intel); SET_STATE( i830, state ); } GLboolean i830TryTextureReadPixels( GLcontext *ctx, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, const struct gl_pixelstore_attrib *pack, GLvoid *pixels ) { i830ContextPtr i830 = I830_CONTEXT(ctx); intelContextPtr intel = INTEL_CONTEXT(ctx); intelScreenPrivate *screen = i830->intel.intelScreen; GLint pitch = pack->RowLength ? pack->RowLength : width; __DRIdrawablePrivate *dPriv = i830->intel.driDrawable; int textureFormat; GLenum glTextureFormat; int src_offset = i830->meta.Buffer[I830_DESTREG_CBUFADDR2]; int destOffset = intelAgpOffsetFromVirtual( &i830->intel, pixels); int destFormat, depthFormat, destPitch; drm_clip_rect_t tmp; if (INTEL_DEBUG & DEBUG_PIXEL) fprintf(stderr, "%s\n", __FUNCTION__); if ( ctx->_ImageTransferState || pack->SwapBytes || pack->LsbFirst || !pack->Invert) { fprintf(stderr, "%s: check_color failed\n", __FUNCTION__); return GL_FALSE; } switch (screen->fbFormat) { case DV_PF_565: textureFormat = MAPSURF_16BIT | MT_16BIT_RGB565; glTextureFormat = GL_RGB; break; case DV_PF_555: textureFormat = MAPSURF_16BIT | MT_16BIT_ARGB1555; glTextureFormat = GL_RGBA; break; case DV_PF_8888: textureFormat = MAPSURF_32BIT | MT_32BIT_ARGB8888; glTextureFormat = GL_RGBA; break; default: fprintf(stderr, "%s: textureFormat failed %x\n", __FUNCTION__, screen->fbFormat); return GL_FALSE; } switch (type) { case GL_UNSIGNED_SHORT_5_6_5: if (format != GL_RGB) return GL_FALSE; destFormat = COLR_BUF_RGB565; depthFormat = DEPTH_FRMT_16_FIXED; destPitch = pitch * 2; break; case GL_UNSIGNED_INT_8_8_8_8_REV: if (format != GL_BGRA) return GL_FALSE; destFormat = COLR_BUF_ARGB8888; depthFormat = DEPTH_FRMT_24_FIXED_8_OTHER; destPitch = pitch * 4; break; default: fprintf(stderr, "%s: destFormat failed %s\n", __FUNCTION__, _mesa_lookup_enum_by_nr(type)); return GL_FALSE; } destFormat |= (0x02<<24); /* fprintf(stderr, "type: %s destFormat: %x\n", */ /* _mesa_lookup_enum_by_nr(type), */ /* destFormat); */ intelFlush( ctx ); SET_STATE( i830, meta ); set_initial_state( i830 ); set_no_depth_stencil_write( i830 ); LOCK_HARDWARE( intel ); { intelWaitForIdle( intel ); /* required by GL */ if (!driClipRectToFramebuffer(ctx->ReadBuffer, &x, &y, &width, &height)) { UNLOCK_HARDWARE( intel ); SET_STATE(i830, state); fprintf(stderr, "%s: cliprect failed\n", __FUNCTION__); return GL_TRUE; } #if 0 /* FIXME -- Just emit the correct state */ if (i830SetParam(i830->driFd, I830_SETPARAM_CBUFFER_PITCH, destPitch) != 0) { UNLOCK_HARDWARE( intel ); SET_STATE(i830, state); fprintf(stderr, "%s: setparam failed\n", __FUNCTION__); return GL_FALSE; } #endif y = dPriv->h - y - height; x += dPriv->x; y += dPriv->y; /* Set the frontbuffer up as a large rectangular texture. */ set_tex_rect_source( i830, src_offset, screen->width, screen->height, screen->frontPitch, textureFormat ); enable_texture_blend_replace( i830, glTextureFormat ); /* Set the 3d engine to draw into the agp memory */ set_draw_offset( i830, destOffset ); set_draw_format( i830, destFormat, depthFormat ); /* Draw a single quad, no cliprects: */ i830->intel.numClipRects = 1; i830->intel.pClipRects = &tmp; i830->intel.pClipRects[0].x1 = 0; i830->intel.pClipRects[0].y1 = 0; i830->intel.pClipRects[0].x2 = width; i830->intel.pClipRects[0].y2 = height; draw_quad( i830, 0, width, 0, height, 0, 255, 0, 0, x, x+width, y, y+height ); intelWindowMoved( intel ); } UNLOCK_HARDWARE( intel ); intelFinish( ctx ); /* required by GL */ SET_STATE( i830, state ); return GL_TRUE; } GLboolean i830TryTextureDrawPixels( GLcontext *ctx, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, const struct gl_pixelstore_attrib *unpack, const GLvoid *pixels ) { intelContextPtr intel = INTEL_CONTEXT(ctx); i830ContextPtr i830 = I830_CONTEXT(ctx); GLint pitch = unpack->RowLength ? unpack->RowLength : width; __DRIdrawablePrivate *dPriv = intel->driDrawable; int textureFormat; GLenum glTextureFormat; int dst_offset = i830->meta.Buffer[I830_DESTREG_CBUFADDR2]; int src_offset = intelAgpOffsetFromVirtual( intel, pixels ); if (INTEL_DEBUG & DEBUG_PIXEL) fprintf(stderr, "%s\n", __FUNCTION__); /* Todo -- upload images that aren't in agp space, then texture * from them. */ if ( !intelIsAgpMemory( intel, pixels, pitch*height ) ) { fprintf(stderr, "%s: intelIsAgpMemory failed\n", __FUNCTION__); return GL_FALSE; } /* Todo -- don't want to clobber all the drawing state like we do * for readpixels -- most of this state can be handled just fine. */ if ( ctx->_ImageTransferState || unpack->SwapBytes || unpack->LsbFirst || ctx->Color.AlphaEnabled || ctx->Depth.Test || ctx->Fog.Enabled || ctx->Scissor.Enabled || ctx->Stencil.Enabled || !ctx->Color.ColorMask[0] || !ctx->Color.ColorMask[1] || !ctx->Color.ColorMask[2] || !ctx->Color.ColorMask[3] || ctx->Color.ColorLogicOpEnabled || ctx->Texture._EnabledUnits || ctx->Depth.OcclusionTest) { fprintf(stderr, "%s: other tests failed\n", __FUNCTION__); return GL_FALSE; } /* Todo -- remove these restrictions: */ if (ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != -1.0F) return GL_FALSE; switch (type) { case GL_UNSIGNED_SHORT_1_5_5_5_REV: if (format != GL_BGRA) return GL_FALSE; textureFormat = MAPSURF_16BIT | MT_16BIT_ARGB1555; glTextureFormat = GL_RGBA; break; case GL_UNSIGNED_SHORT_5_6_5: if (format != GL_RGB) return GL_FALSE; textureFormat = MAPSURF_16BIT | MT_16BIT_RGB565; glTextureFormat = GL_RGB; break; case GL_UNSIGNED_SHORT_8_8_MESA: if (format != GL_YCBCR_MESA) return GL_FALSE; textureFormat = (MAPSURF_422 | MT_422_YCRCB_SWAPY /* | TM0S1_COLORSPACE_CONVERSION */ ); glTextureFormat = GL_YCBCR_MESA; break; case GL_UNSIGNED_SHORT_8_8_REV_MESA: if (format != GL_YCBCR_MESA) return GL_FALSE; textureFormat = (MAPSURF_422 | MT_422_YCRCB_NORMAL /* | TM0S1_COLORSPACE_CONVERSION */ ); glTextureFormat = GL_YCBCR_MESA; break; case GL_UNSIGNED_INT_8_8_8_8_REV: if (format != GL_BGRA) return GL_FALSE; textureFormat = MAPSURF_32BIT | MT_32BIT_ARGB8888; glTextureFormat = GL_RGBA; break; default: fprintf(stderr, "%s: destFormat failed\n", __FUNCTION__); return GL_FALSE; } intelFlush( ctx ); SET_STATE( i830, meta ); LOCK_HARDWARE( intel ); { intelWaitForIdle( intel ); /* required by GL */ y -= height; /* cope with pixel zoom */ if (!driClipRectToFramebuffer(ctx->ReadBuffer, &x, &y, &width, &height)) { UNLOCK_HARDWARE( intel ); SET_STATE(i830, state); fprintf(stderr, "%s: cliprect failed\n", __FUNCTION__); return GL_TRUE; } y = dPriv->h - y - height; set_initial_state( i830 ); /* Set the pixel image up as a rectangular texture. */ set_tex_rect_source( i830, src_offset, width, height, pitch, /* XXXX!!!! -- /2 sometimes */ textureFormat ); enable_texture_blend_replace( i830, glTextureFormat ); /* Draw to the current draw buffer: */ set_draw_offset( i830, dst_offset ); /* Draw a quad, use regular cliprects */ /* fprintf(stderr, "x: %d y: %d width %d height %d\n", x, y, width, height); */ draw_quad( i830, x, x+width, y, y+height, 0, 255, 0, 0, 0, width, 0, height ); intelWindowMoved( intel ); } UNLOCK_HARDWARE( intel ); intelFinish( ctx ); /* required by GL */ SET_STATE(i830, state); return GL_TRUE; }