/* Copyright (c) 2002-2012 Croteam Ltd. All rights reserved. */ #include "stdh.h" #include #include #include #include #include #include #include // asm shortcuts #define O offset #define Q qword ptr #define D dword ptr #define W word ptr #define B byte ptr // we need array for OpenGL mipmaps that are lower than N*1 or 1*N static ULONG _aulLastMipmaps[(INDEX)(1024*1.334)]; static CTexParams *_tpCurrent; extern INDEX GFX_iActiveTexUnit; // unpacks texture filering from one INDEX to two GLenums (and eventually re-adjust input INDEX) static void UnpackFilter_OGL( INDEX iFilter, GLenum &eMagFilter, GLenum &eMinFilter) { switch( iFilter) { case 110: case 10: eMagFilter=GL_NEAREST; eMinFilter=GL_NEAREST; break; case 111: case 11: eMagFilter=GL_NEAREST; eMinFilter=GL_NEAREST_MIPMAP_NEAREST; break; case 112: case 12: eMagFilter=GL_NEAREST; eMinFilter=GL_NEAREST_MIPMAP_LINEAR; break; case 220: case 20: eMagFilter=GL_LINEAR; eMinFilter=GL_LINEAR; break; case 221: case 21: eMagFilter=GL_LINEAR; eMinFilter=GL_LINEAR_MIPMAP_NEAREST; break; case 222: case 22: eMagFilter=GL_LINEAR; eMinFilter=GL_LINEAR_MIPMAP_LINEAR; break; case 120: eMagFilter=GL_NEAREST; eMinFilter=GL_LINEAR; break; case 121: eMagFilter=GL_NEAREST; eMinFilter=GL_LINEAR_MIPMAP_NEAREST; break; case 122: eMagFilter=GL_NEAREST; eMinFilter=GL_LINEAR_MIPMAP_LINEAR; break; case 210: eMagFilter=GL_LINEAR; eMinFilter=GL_NEAREST; break; case 211: eMagFilter=GL_LINEAR; eMinFilter=GL_NEAREST_MIPMAP_NEAREST; break; case 212: eMagFilter=GL_LINEAR; eMinFilter=GL_NEAREST_MIPMAP_LINEAR; break; default: ASSERTALWAYS( "Illegal OpenGL texture filtering mode."); break; } } // change texture filtering mode if needed extern void MimicTexParams_OGL( CTexParams &tpLocal) { ASSERT( &tpLocal!=NULL); _pfGfxProfile.StartTimer( CGfxProfile::PTI_TEXTUREPARAMS); // set texture filtering mode if required if( tpLocal.tp_iFilter != _tpGlobal[0].tp_iFilter) { // update OpenGL texture filters GLenum eMagFilter, eMinFilter; UnpackFilter_OGL( _tpGlobal[0].tp_iFilter, eMagFilter, eMinFilter); // adjust minimize filter in case of a single mipmap if( tpLocal.tp_bSingleMipmap) { if( eMinFilter==GL_NEAREST_MIPMAP_NEAREST || eMinFilter==GL_NEAREST_MIPMAP_LINEAR) eMinFilter = GL_NEAREST; else if( eMinFilter==GL_LINEAR_MIPMAP_NEAREST || eMinFilter==GL_LINEAR_MIPMAP_LINEAR) eMinFilter = GL_LINEAR; } // update texture filter pglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, eMagFilter); pglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, eMinFilter); tpLocal.tp_iFilter = _tpGlobal[0].tp_iFilter; OGL_CHECKERROR; } // set texture anisotropy degree if required and supported if( tpLocal.tp_iAnisotropy != _tpGlobal[0].tp_iAnisotropy) { tpLocal.tp_iAnisotropy = _tpGlobal[0].tp_iAnisotropy; if( _pGfx->gl_iMaxTextureAnisotropy>=2) { // only if allowed pglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, tpLocal.tp_iAnisotropy); } } // set texture clamping modes if changed if( tpLocal.tp_eWrapU!=_tpGlobal[GFX_iActiveTexUnit].tp_eWrapU || tpLocal.tp_eWrapV!=_tpGlobal[GFX_iActiveTexUnit].tp_eWrapV) { // prepare temp vars GLuint eWrapU = _tpGlobal[GFX_iActiveTexUnit].tp_eWrapU==GFX_REPEAT ? GL_REPEAT : GL_CLAMP; GLuint eWrapV = _tpGlobal[GFX_iActiveTexUnit].tp_eWrapV==GFX_REPEAT ? GL_REPEAT : GL_CLAMP; // eventually re-adjust clamping params in case of clamp_to_edge extension if( _pGfx->gl_ulFlags&GLF_EXT_EDGECLAMP) { if( eWrapU == GL_CLAMP) eWrapU = GL_CLAMP_TO_EDGE; if( eWrapV == GL_CLAMP) eWrapV = GL_CLAMP_TO_EDGE; } // set clamping params and update local texture clamping modes pglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, eWrapU); pglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, eWrapV); tpLocal.tp_eWrapU = _tpGlobal[GFX_iActiveTexUnit].tp_eWrapU; tpLocal.tp_eWrapV = _tpGlobal[GFX_iActiveTexUnit].tp_eWrapV; OGL_CHECKERROR; } // keep last texture params (for tex upload and stuff) _tpCurrent = &tpLocal; _pfGfxProfile.StopTimer( CGfxProfile::PTI_TEXTUREPARAMS); } // upload context for current texture to accelerator's memory // (returns format in which texture was really uploaded) extern void UploadTexture_OGL( ULONG *pulTexture, PIX pixSizeU, PIX pixSizeV, GLenum eInternalFormat, BOOL bUseSubImage) { // safeties ASSERT( pulTexture!=NULL); ASSERT( pixSizeU>0 && pixSizeV>0); _sfStats.StartTimer( CStatForm::STI_BINDTEXTURE); _pfGfxProfile.StartTimer( CGfxProfile::PTI_TEXTUREUPLOADING); // upload each original mip-map INDEX iMip=0; PIX pixOffset=0; while( pixSizeU>0 && pixSizeV>0) { // check that memory is readable ASSERT( pulTexture[pixOffset +pixSizeU*pixSizeV -1] != 0xDEADBEEF); // upload mipmap as fast as possible if( bUseSubImage) { pglTexSubImage2D( GL_TEXTURE_2D, iMip, 0, 0, pixSizeU, pixSizeV, GL_RGBA, GL_UNSIGNED_BYTE, pulTexture+pixOffset); } else { pglTexImage2D( GL_TEXTURE_2D, iMip, eInternalFormat, pixSizeU, pixSizeV, 0, GL_RGBA, GL_UNSIGNED_BYTE, pulTexture+pixOffset); } OGL_CHECKERROR; // advance to next mip-map pixOffset += pixSizeU*pixSizeV; pixSizeU >>=1; pixSizeV >>=1; iMip++; // end here if there is only one mip-map to upload if( _tpCurrent->tp_bSingleMipmap) break; } // see if we need to generate and upload additional mipmaps (those under 1*N or N*1) if( !_tpCurrent->tp_bSingleMipmap && pixSizeU!=pixSizeV) { // prepare variables PIX pixSize = Max(pixSizeU,pixSizeV); ASSERT( pixSize<=2048); ULONG *pulSrc = pulTexture+pixOffset-pixSize*2; ULONG *pulDst = _aulLastMipmaps; // loop thru mipmaps while( pixSizeU>0 || pixSizeV>0) { // make next mipmap if( pixSizeU==0) pixSizeU=1; if( pixSizeV==0) pixSizeV=1; pixSize = pixSizeU*pixSizeV; __asm { pxor mm0,mm0 mov esi,D [pulSrc] mov edi,D [pulDst] mov ecx,D [pixSize] pixLoop: movd mm1,D [esi+0] movd mm2,D [esi+4] punpcklbw mm1,mm0 punpcklbw mm2,mm0 paddw mm1,mm2 psrlw mm1,1 packuswb mm1,mm0 movd D [edi],mm1 add esi,4*2 add edi,4 dec ecx jnz pixLoop emms } // upload mipmap if( bUseSubImage) { pglTexSubImage2D( GL_TEXTURE_2D, iMip, 0, 0, pixSizeU, pixSizeV, GL_RGBA, GL_UNSIGNED_BYTE, pulDst); } else { pglTexImage2D( GL_TEXTURE_2D, iMip, eInternalFormat, pixSizeU, pixSizeV, 0, GL_RGBA, GL_UNSIGNED_BYTE, pulDst); } OGL_CHECKERROR; // advance to next mip-map pulSrc = pulDst; pulDst += pixSize; pixOffset += pixSize; pixSizeU >>=1; pixSizeV >>=1; iMip++; } } // all done _pfGfxProfile.IncrementCounter( CGfxProfile::PCI_TEXTUREUPLOADS, 1); _pfGfxProfile.IncrementCounter( CGfxProfile::PCI_TEXTUREUPLOADBYTES, pixOffset*4); _sfStats.IncrementCounter( CStatForm::SCI_TEXTUREUPLOADS, 1); _sfStats.IncrementCounter( CStatForm::SCI_TEXTUREUPLOADBYTES, pixOffset*4); _pfGfxProfile.StopTimer( CGfxProfile::PTI_TEXTUREUPLOADING); _sfStats.StopTimer( CStatForm::STI_BINDTEXTURE); } // returns bytes/pixels ratio for uploaded texture extern INDEX GetFormatPixRatio_OGL( GLenum eFormat) { switch( eFormat) { case GL_RGBA: case GL_RGBA8: return 4; case GL_RGB: case GL_RGB8: return 3; case GL_RGB5: case GL_RGB5_A1: case GL_RGB4: case GL_RGBA4: case GL_LUMINANCE_ALPHA: case GL_LUMINANCE8_ALPHA8: return 2; // compressed formats and single-channel formats default: return 1; } } // returns bytes/pixels ratio for uploaded texture extern INDEX GetTexturePixRatio_OGL( GLuint uiBindNo) { GLenum eInternalFormat; pglBindTexture( GL_TEXTURE_2D, uiBindNo); pglGetTexLevelParameteriv( GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, (GLint*)&eInternalFormat); OGL_CHECKERROR; return GetFormatPixRatio_OGL( eInternalFormat); } // return allowed dithering method extern INDEX AdjustDitheringType_OGL( GLenum eFormat, INDEX iDitheringType) { switch( eFormat) { // these formats don't need dithering case GL_RGB8: case GL_RGBA8: case GL_LUMINANCE8: case GL_LUMINANCE8_ALPHA8: return NONE; // these formats need reduced dithering case GL_RGB5: case GL_RGB5_A1: if( iDitheringType>7) return iDitheringType-3; // other formats need dithering as it is default: return iDitheringType; } }