/* Copyright (C) 2004-2006 Claudio Ciccani This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "nvidia.h" #include "nvidia_regs.h" #include "nvidia_accel.h" #include "nvidia_3d.h" static __inline__ u32 f2d( float f ) { union { float f; u32 d; } t; t.f = f; return t.d; } #define nv_setstate3d( state3d ) { \ if ((state3d)->modified) { \ nv_begin( SUBC_TEXTRIANGLE, TXTRI_COLOR_KEY, 7 ); \ nv_outr( (state3d)->colorkey ); \ nv_outr( (state3d)->offset ); \ nv_outr( (state3d)->format ); \ nv_outr( (state3d)->filter ); \ nv_outr( (state3d)->blend ); \ nv_outr( (state3d)->control ); \ nv_outr( (state3d)->fog ); \ \ (state3d)->modified = false; \ } \ } #define nv_putvertex( i, x, y, z, w, col, spc, s, t ) { \ nv_begin( SUBC_TEXTRIANGLE, TXTRI_VERTEX0+(i)*32, 8 ); \ nv_outr( f2d( x ) ); \ nv_outr( f2d( y ) ); \ nv_outr( f2d( z ) ); \ nv_outr( f2d( w ) ); \ nv_outr( col ); \ nv_outr( spc ); \ nv_outr( f2d( s ) ); \ nv_outr( f2d( t ) ); \ } #define nv_emit_vertices( i, v0, v1, v2, v3, v4, v5, v6, v7 ) { \ nv_begin( SUBC_TEXTRIANGLE, TXTRI_PRIMITIVE0+(i)*4, 1 ); \ nv_outr( ((v7) << 28) | ((v6) << 24) | \ ((v5) << 20) | ((v4) << 16) | \ ((v3) << 12) | ((v2) << 8) | \ ((v1) << 4) | (v0) ); \ } static void nv_load_texture( NVidiaDriverData *nvdrv, NVidiaDeviceData *nvdev ); #define M_TRANSFORM(x, y, retx, rety, m) { \ float _x, _y; \ _x = ((x) * (m)[0] + (y) * (m)[1] + (m)[2]) / 65536.f; \ _y = ((x) * (m)[3] + (y) * (m)[4] + (m)[5]) / 65536.f; \ retx = _x; \ rety = _y; \ } bool nvFillRectangle3D( void *drv, void *dev, DFBRectangle *rect ) { NVidiaDriverData *nvdrv = (NVidiaDriverData*) drv; NVidiaDeviceData *nvdev = (NVidiaDeviceData*) dev; float x1, y1; float x2, y2; x1 = rect->x; x2 = rect->x+rect->w; y1 = rect->y; y2 = rect->y+rect->h; nv_setstate3d( &nvdev->state3d[0] ); if (nvdev->matrix) { float x, y; M_TRANSFORM( x1, y1, x, y, nvdev->matrix ); nv_putvertex( 0, x, y, 0, 1, nvdev->color3d, 0, 0, 0 ); M_TRANSFORM( x2, y1, x, y, nvdev->matrix ); nv_putvertex( 1, x, y, 0, 1, nvdev->color3d, 0, 0, 0 ); M_TRANSFORM( x2, y2, x, y, nvdev->matrix ); nv_putvertex( 2, x, y, 0, 1, nvdev->color3d, 0, 0, 0 ); M_TRANSFORM( x1, y2, x, y, nvdev->matrix ); nv_putvertex( 3, x, y, 0, 1, nvdev->color3d, 0, 0, 0 ); } else { nv_putvertex( 0, x1, y1, 0, 1, nvdev->color3d, 0, 0, 0 ); nv_putvertex( 1, x2, y1, 0, 1, nvdev->color3d, 0, 0, 0 ); nv_putvertex( 2, x2, y2, 0, 1, nvdev->color3d, 0, 0, 0 ); nv_putvertex( 3, x1, y2, 0, 1, nvdev->color3d, 0, 0, 0 ); } nv_emit_vertices( 0, 0, 1, 2, 0, 2, 3, 0, 0 ); return true; } bool nvFillTriangle3D( void *drv, void *dev, DFBTriangle *tri ) { NVidiaDriverData *nvdrv = (NVidiaDriverData*) drv; NVidiaDeviceData *nvdev = (NVidiaDeviceData*) dev; float x1, y1; float x2, y2; float x3, y3; x1 = tri->x1; x2 = tri->x2; x3 = tri->x3; y1 = tri->y1; y2 = tri->y2; y3 = tri->y3; if (nvdev->matrix) { M_TRANSFORM( x1, y1, x1, y1, nvdev->matrix ); M_TRANSFORM( x2, y2, x2, y2, nvdev->matrix ); M_TRANSFORM( x3, y3, x3, y3, nvdev->matrix ); } nv_setstate3d( &nvdev->state3d[0] ); nv_putvertex( 0, x1, y1, 0, 1, nvdev->color3d, 0, 0, 0 ); nv_putvertex( 1, x2, y2, 0, 1, nvdev->color3d, 0, 0, 0 ); nv_putvertex( 2, x3, y3, 0, 1, nvdev->color3d, 0, 0, 0 ); nv_emit_vertices( 0, 0, 1, 2, 0, 0, 0, 0, 0 ); return true; } bool nvDrawRectangle3D( void *drv, void *dev, DFBRectangle *rect ) { NVidiaDriverData *nvdrv = (NVidiaDriverData*) drv; NVidiaDeviceData *nvdev = (NVidiaDeviceData*) dev; DFBRegion r[4]; int i; if (nvdev->matrix) { DFBRegion line; /* top */ line = (DFBRegion) { rect->x, rect->y, rect->x+rect->w, rect->y }; nvDrawLine3D( drv, dev, &line ); /* right */ line = (DFBRegion) { rect->x+rect->w, rect->y, rect->x+rect->w, rect->y+rect->h }; nvDrawLine3D( drv, dev, &line ); /* bottom */ line = (DFBRegion) { rect->x, rect->y+rect->h, rect->x+rect->w, rect->y+rect->h }; nvDrawLine3D( drv, dev, &line ); /* left */ line = (DFBRegion) { rect->x, rect->y, rect->x, rect->y+rect->h }; nvDrawLine3D( drv, dev, &line ); return true; } /* top */ r[0].x1 = rect->x; r[0].y1 = rect->y; r[0].x2 = rect->x + rect->w; r[0].y2 = rect->y + 1; /* right */ r[1].x1 = rect->x + rect->w - 1; r[1].y1 = rect->y + 1; r[1].x2 = rect->x + rect->w; r[1].y2 = rect->y + rect->h - 1; /* bottom */ r[2].x1 = rect->x; r[2].y1 = rect->y + rect->h - 1; r[2].x2 = rect->x + rect->w; r[2].y2 = rect->y + rect->h; /* left */ r[3].x1 = rect->x; r[3].y1 = rect->y + 1; r[3].x2 = rect->x + 1; r[3].y2 = rect->y + rect->h - 1; nv_setstate3d( &nvdev->state3d[0] ); for (i = 0; i < 4; i++) { nv_putvertex( 0, r[i].x1, r[i].y1, 0, 1, nvdev->color3d, 0, 0, 0 ); nv_putvertex( 1, r[i].x2, r[i].y1, 0, 1, nvdev->color3d, 0, 0, 0 ); nv_putvertex( 2, r[i].x2, r[i].y2, 0, 1, nvdev->color3d, 0, 0, 0 ); nv_putvertex( 3, r[i].x1, r[i].y2, 0, 1, nvdev->color3d, 0, 0, 0 ); nv_emit_vertices( 0, 0, 1, 2, 0, 2, 3, 0, 0 ); } return true; } bool nvDrawLine3D( void *drv, void *dev, DFBRegion *line ) { NVidiaDriverData *nvdrv = (NVidiaDriverData*) drv; NVidiaDeviceData *nvdev = (NVidiaDeviceData*) dev; float xinc, yinc; float x1, y1; float x2, y2; float dx, dy; x1 = line->x1; x2 = line->x2; y1 = line->y1; y2 = line->y2; if (nvdev->matrix) { M_TRANSFORM( x1, y1, x1, y1, nvdev->matrix ); M_TRANSFORM( x2, y2, x2, y2, nvdev->matrix ); dx = fabs(x2 - x1); dy = fabs(y2 - y1); } else { dx = abs(line->x2 - line->x1); dy = abs(line->y2 - line->y1); } if (dx > dy) { /* more horizontal */ xinc = 0.0; yinc = 0.5; } else { /* more vertical */ xinc = 0.5; yinc = 0.0; } nv_setstate3d( &nvdev->state3d[0] ); nv_putvertex( 0, x1 - xinc, y1 - yinc, 0, 1, nvdev->color3d, 0, 0, 0 ); nv_putvertex( 1, x1 + xinc, y1 + yinc, 0, 1, nvdev->color3d, 0, 0, 0 ); nv_putvertex( 2, x2 + xinc, y2 + yinc, 0, 1, nvdev->color3d, 0, 0, 0 ); nv_putvertex( 3, x2 - xinc, y2 - yinc, 0, 1, nvdev->color3d, 0, 0, 0 ); nv_emit_vertices( 0, 2, 0, 1, 3, 0, 2, 0, 0 ); return true; } bool nvTextureTriangles( void *drv, void *dev, DFBVertex *ve, int num, DFBTriangleFormation formation ) { NVidiaDriverData *nvdrv = (NVidiaDriverData*) drv; NVidiaDeviceData *nvdev = (NVidiaDeviceData*) dev; float s_scale; float t_scale; int i; /* load source texture into texture buffer */ nv_load_texture( nvdrv, nvdev ); s_scale = (float)nvdev->src_width / (float)(1 << ((nvdev->state3d[1].format >> 16) & 0xF)); t_scale = (float)nvdev->src_height / (float)(1 << ((nvdev->state3d[1].format >> 20) & 0xF)); for (i = 0; i < num; i++) { if (nvdev->matrix) M_TRANSFORM( ve[i].x, ve[i].y, ve[i].x, ve[i].y, nvdev->matrix ); ve[i].x += 0.5; ve[i].y += 0.5; ve[i].s *= s_scale; ve[i].t *= t_scale; } nv_setstate3d( &nvdev->state3d[1] ); switch (formation) { case DTTF_LIST: for (i = 0; i < num; i += 3) { nv_putvertex( 0, ve[i].x, ve[i].y, ve[i].z, ve[i].w, nvdev->color3d, 0, ve[i].s, ve[i].t ); nv_putvertex( 1, ve[i+1].x, ve[i+1].y, ve[i+1].z, ve[i+1].w, nvdev->color3d, 0, ve[i+1].s, ve[i+1].t ); nv_putvertex( 2, ve[i+2].x, ve[i+2].y, ve[i+2].z, ve[i+2].w, nvdev->color3d, 0, ve[i+2].s, ve[i+2].t ); nv_emit_vertices( 0, 0, 1, 2, 0, 0, 0, 0, 0 ); } break; case DTTF_STRIP: nv_putvertex( 0, ve[0].x, ve[0].y, ve[0].z, ve[0].w, nvdev->color3d, 0, ve[0].s, ve[0].t ); nv_putvertex( 1, ve[1].x, ve[1].y, ve[1].z, ve[1].w, nvdev->color3d, 0, ve[1].s, ve[1].t ); nv_putvertex( 2, ve[2].x, ve[2].y, ve[2].z, ve[2].w, nvdev->color3d, 0, ve[2].s, ve[2].t ); nv_emit_vertices( 0, 0, 1, 2, 0, 0, 0, 0, 0 ); for (i = 3; i < num; i++) { nv_putvertex( 0, ve[i-2].x, ve[i-2].y, ve[i-2].z, ve[i-2].w, nvdev->color3d, 0, ve[i-2].s, ve[i-2].t ); nv_putvertex( 1, ve[i-1].x, ve[i-1].y, ve[i-1].z, ve[i-1].w, nvdev->color3d, 0, ve[i-1].s, ve[i-1].t ); nv_putvertex( 2, ve[i].x, ve[i].y, ve[i].z, ve[i].w, nvdev->color3d, 0, ve[i].s, ve[i].t ); nv_emit_vertices( 0, 0, 1, 2, 0, 0, 0, 0, 0 ); } break; case DTTF_FAN: nv_putvertex( 0, ve[0].x, ve[0].y, ve[0].z, ve[0].w, nvdev->color3d, 0, ve[0].s, ve[0].t ); nv_putvertex( 1, ve[1].x, ve[1].y, ve[1].z, ve[1].w, nvdev->color3d, 0, ve[1].s, ve[1].t ); nv_putvertex( 2, ve[2].x, ve[2].y, ve[2].z, ve[2].w, nvdev->color3d, 0, ve[2].s, ve[2].t ); nv_emit_vertices( 0, 0, 1, 2, 0, 0, 0, 0, 0 ); for (i = 3; i < num; i++) { nv_putvertex( 0, ve[0].x, ve[0].y, ve[0].z, ve[0].w, nvdev->color3d, 0, ve[0].s, ve[0].t ); nv_putvertex( 1, ve[i-1].x, ve[i-1].y, ve[i-1].z, ve[i-1].w, nvdev->color3d, 0, ve[i-1].s, ve[i-1].t ); nv_putvertex( 2, ve[i].x, ve[i].y, ve[i].z, ve[i].w, nvdev->color3d, 0, ve[i].s, ve[i].t ); nv_emit_vertices( 0, 0, 1, 2, 0, 0, 0, 0, 0 ); } break; default: D_BUG( "unexpected triangle formation" ); return false; } return true; } /* * Surface to Texture conversion routines. */ #define VINC 0xAAAAAAAC #define VMASK 0x55555555 #define UINC 0x55555558 #define UMASK 0xAAAAAAAA static inline void a8_to_tex( u32 *dst, u8 *src, int pitch, int width, int height ) { u32 u, v; int i; for (v = 0; height--; v = (v + VINC) & VMASK) { for (i = 0, u = 0; i < width; i += 2, u = (u + UINC) & UMASK) { #ifdef WORDS_BIGENDIAN dst[(u|v)/4] = ((src[i+0] & 0xF0) << 24) | ((src[i+1] & 0xF0) << 8) | 0x0FFF0FFF; #else dst[(u|v)/4] = ((src[i+0] & 0xF0) << 8) | ((src[i+1] & 0xF0) << 24) | 0x0FFF0FFF; #endif } if (width & 1) { u = (u + UINC) & UMASK; dst[(u|v)/4] = ((src[width-1] & 0xF0) << 8) | 0x0FFF; } src += pitch; } } static inline void rgb16_to_tex( u32 *dst, u8 *src, int pitch, int width, int height ) { u32 u, v; int i; for (v = 0; height--; v = (v + VINC) & VMASK) { for (i = 0, u = 0; i < width/2; i++, u = (u + UINC) & UMASK) dst[(u|v)/4] = ((u32*) src)[i]; if (width & 1) { u = (u + UINC) & UMASK; dst[(u|v)/4] = ((u16*) src)[width-1]; } src += pitch; } } static inline void rgb32_to_tex( u32 *dst, u8 *src, int pitch, int width, int height ) { u32 u, v; int i; for (v = 0; height--; v = (v + VINC) & VMASK) { for (i = 0, u = 0; i < width; i += 2, u = (u + UINC) & UMASK) { register u32 pix0, pix1; pix0 = ((u32*) src)[i]; pix0 = RGB32_TO_RGB16( pix0 ); pix1 = ((u32*) src)[i+1]; pix1 = RGB32_TO_RGB16( pix1 ); #ifdef WORDS_BIGENDIAN dst[(u|v)/4] = (pix0 << 16) | pix1; #else dst[(u|v)/4] = pix0 | (pix1 << 16); #endif } if (width & 1) { u = (u + UINC) & UMASK; dst[(u|v)/4] = RGB32_TO_RGB16( ((u32*) src)[width-1] ); } src += pitch; } } static inline void argb_to_tex( u32 *dst, u8 *src, int pitch, int width, int height ) { u32 u, v; int i; for (v = 0; height--; v = (v + VINC) & VMASK) { for (i = 0, u = 0; i < width; i += 2, u = (u + UINC) & UMASK) { register u32 pix0, pix1; pix0 = ((u32*) src)[i]; pix0 = ARGB_TO_ARGB4444( pix0 ); pix1 = ((u32*) src)[i+1]; pix1 = ARGB_TO_ARGB4444( pix1 ); #ifdef WORDS_BIGENDIAN dst[(u|v)/4] = (pix0 << 16) | pix1; #else dst[(u|v)/4] = pix0 | (pix1 << 16); #endif } if (width & 1) { u = (u + UINC) & UMASK; dst[(u|v)/4] = ARGB_TO_ARGB4444( ((u32*) src)[width-1] ); } src += pitch; } } static void nv_load_texture( NVidiaDriverData *nvdrv, NVidiaDeviceData *nvdev ) { CoreSurfaceBuffer *buffer = nvdev->src_texture; u32 *dst; dst = dfb_gfxcard_memory_virtual( nvdrv->device, nvdev->buf_offset[1] ); #if 0 if (nvdev->src_interlaced) { if (surface->caps & DSCAPS_SEPARATED) { if (surface->field) field_offset = nvdev->src_height * src_pitch; } else { if (surface->field) field_offset = src_pitch; src_pitch *= 2; } } #endif switch (buffer->format) { case DSPF_A8: a8_to_tex( dst, nvdev->src_lock->addr, nvdev->src_lock->pitch, nvdev->src_width, nvdev->src_height ); break; case DSPF_ARGB1555: case DSPF_RGB16: rgb16_to_tex( dst, nvdev->src_lock->addr, nvdev->src_lock->pitch, nvdev->src_width, nvdev->src_height ); break; case DSPF_RGB32: rgb32_to_tex( dst, nvdev->src_lock->addr, nvdev->src_lock->pitch, nvdev->src_width, nvdev->src_height ); break; case DSPF_ARGB: argb_to_tex( dst, nvdev->src_lock->addr, nvdev->src_lock->pitch, nvdev->src_width, nvdev->src_height ); break; default: D_BUG( "unexpected pixelformat" ); break; } }