diff options
Diffstat (limited to 'indra/llrender')
36 files changed, 13660 insertions, 1974 deletions
diff --git a/indra/llrender/CMakeLists.txt b/indra/llrender/CMakeLists.txt index 9938a0198b..5c13df9f81 100644 --- a/indra/llrender/CMakeLists.txt +++ b/indra/llrender/CMakeLists.txt @@ -3,12 +3,16 @@ project(llrender) include(00-Common) +include(FindOpenGL) include(FreeType) include(LLCommon) include(LLImage) include(LLMath) include(LLRender) +include(LLVFS) include(LLWindow) +include(LLXML) +include(LLVFS) include_directories( ${FREETYPE_INCLUDE_DIRS} @@ -16,31 +20,49 @@ include_directories( ${LLIMAGE_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} ${LLRENDER_INCLUDE_DIRS} + ${LLVFS_INCLUDE_DIRS} ${LLWINDOW_INCLUDE_DIRS} + ${LLXML_INCLUDE_DIRS} + ${LLVFS_INCLUDE_DIRS} ) set(llrender_SOURCE_FILES - llfont.cpp + llcubemap.cpp + llfontfreetype.cpp llfontgl.cpp + llfontbitmapcache.cpp + llfontregistry.cpp llgldbg.cpp - llglimmediate.cpp + llglslshader.cpp llimagegl.cpp - llrendertarget.cpp + llpostprocess.cpp + llrendersphere.cpp + llshadermgr.cpp + lltexture.cpp llvertexbuffer.cpp - llvertexprogramgl.cpp ) set(llrender_HEADER_FILES CMakeLists.txt + llcubemap.h llfontgl.h - llfont.h + llfontfreetype.h + llfontbitmapcache.h + llfontregistry.h + llgl.h llgldbg.h - llglimmediate.h + llglheaders.h + llglslshader.h + llglstates.h + llgltypes.h llimagegl.h - llrendertarget.h + llpostprocess.h + llrender.h + llrendersphere.h + llshadermgr.h + lltexture.h llvertexbuffer.h - llvertexprogramgl.h ) set_source_files_properties(${llrender_HEADER_FILES} @@ -48,4 +70,33 @@ set_source_files_properties(${llrender_HEADER_FILES} list(APPEND llrender_SOURCE_FILES ${llrender_HEADER_FILES}) +if (SERVER AND NOT WINDOWS AND NOT DARWIN) + copy_server_sources( + llgl + llrender + ) + + + set_source_files_properties( + ${server_SOURCE_FILES} + PROPERTIES + COMPILE_FLAGS "-DLL_MESA=1 -DLL_MESA_HEADLESS=1" + ) + add_library (llrenderheadless + ${llrender_SOURCE_FILES} + ${server_SOURCE_FILES} + ) +else (SERVER AND NOT WINDOWS AND NOT DARWIN) + list(APPEND llrender_SOURCE_FILES + llgl.cpp + llrender.cpp + llrendertarget.cpp + ) +endif (SERVER AND NOT WINDOWS AND NOT DARWIN) add_library (llrender ${llrender_SOURCE_FILES}) +# Libraries on which this library depends, needed for Linux builds +# Sort by high-level to low-level +target_link_libraries(llrender + llimage + ${FREETYPE_LIBRARIES} + ${OPENGL_LIBRARIES}) diff --git a/indra/llrender/llcubemap.cpp b/indra/llrender/llcubemap.cpp new file mode 100644 index 0000000000..fb22d7f1f5 --- /dev/null +++ b/indra/llrender/llcubemap.cpp @@ -0,0 +1,496 @@ +/** + * @file llcubemap.cpp + * @brief LLCubeMap class implementation + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * 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; + * version 2.1 of the License only. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#include "linden_common.h" + +#include "llworkerthread.h" + +#include "llcubemap.h" + +#include "v4coloru.h" +#include "v3math.h" +#include "v3dmath.h" +#include "m3math.h" +#include "m4math.h" + +#include "llrender.h" + +#include "llglheaders.h" + +const F32 epsilon = 1e-7f; +const U16 RESOLUTION = 64; + +#if LL_DARWIN +// mipmap generation on cubemap textures seems to be broken on the Mac for at least some cards. +// Since the cubemap is small (64x64 per face) and doesn't have any fine detail, turning off mipmaps is a usable workaround. +const BOOL use_cube_mipmaps = FALSE; +#else +const BOOL use_cube_mipmaps = FALSE; //current build works best without cube mipmaps +#endif + +bool LLCubeMap::sUseCubeMaps = true; + +LLCubeMap::LLCubeMap() + : mTextureStage(0), + mTextureCoordStage(0), + mMatrixStage(0) +{ + mTargets[0] = GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB; + mTargets[1] = GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB; + mTargets[2] = GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB; + mTargets[3] = GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB; + mTargets[4] = GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB; + mTargets[5] = GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB; +} + +LLCubeMap::~LLCubeMap() +{ +} + +void LLCubeMap::initGL() +{ + llassert(gGLManager.mInited); + + if (gGLManager.mHasCubeMap && LLCubeMap::sUseCubeMaps) + { + // Not initialized, do stuff. + if (mImages[0].isNull()) + { + U32 texname = 0; + + LLImageGL::generateTextures(1, &texname); + + for (int i = 0; i < 6; i++) + { + mImages[i] = new LLImageGL(64, 64, 4, (use_cube_mipmaps? TRUE : FALSE)); + mImages[i]->setTarget(mTargets[i], LLTexUnit::TT_CUBE_MAP); + mRawImages[i] = new LLImageRaw(64, 64, 4); + mImages[i]->createGLTexture(0, mRawImages[i], texname); + + gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_CUBE_MAP, texname); + mImages[i]->setAddressMode(LLTexUnit::TAM_CLAMP); + stop_glerror(); + } + gGL.getTexUnit(0)->disable(); + } + disable(); + } + else + { + llwarns << "Using cube map without extension!" << llendl; + } +} + +void LLCubeMap::initRawData(const std::vector<LLPointer<LLImageRaw> >& rawimages) +{ + bool flip_x[6] = { false, true, false, false, true, false }; + bool flip_y[6] = { true, true, true, false, true, true }; + bool transpose[6] = { false, false, false, false, true, true }; + + // Yes, I know that this is inefficient! - djs 08/08/02 + for (int i = 0; i < 6; i++) + { + const U8 *sd = rawimages[i]->getData(); + U8 *td = mRawImages[i]->getData(); + + S32 offset = 0; + S32 sx, sy, so; + for (int y = 0; y < 64; y++) + { + for (int x = 0; x < 64; x++) + { + sx = x; + sy = y; + if (flip_y[i]) + { + sy = 63 - y; + } + if (flip_x[i]) + { + sx = 63 - x; + } + if (transpose[i]) + { + S32 temp = sx; + sx = sy; + sy = temp; + } + + so = 64*sy + sx; + so *= 4; + *(td + offset++) = *(sd + so++); + *(td + offset++) = *(sd + so++); + *(td + offset++) = *(sd + so++); + *(td + offset++) = *(sd + so++); + } + } + } +} + +void LLCubeMap::initGLData() +{ + for (int i = 0; i < 6; i++) + { + mImages[i]->setSubImage(mRawImages[i], 0, 0, 64, 64); + } +} + +void LLCubeMap::init(const std::vector<LLPointer<LLImageRaw> >& rawimages) +{ + if (!gGLManager.mIsDisabled) + { + initGL(); + initRawData(rawimages); + initGLData(); + } +} + +GLuint LLCubeMap::getGLName() +{ + return mImages[0]->getTexName(); +} + +void LLCubeMap::bind() +{ + gGL.getTexUnit(mTextureStage)->bind(this); +} + +void LLCubeMap::enable(S32 stage) +{ + enableTexture(stage); + enableTextureCoords(stage); +} + +void LLCubeMap::enableTexture(S32 stage) +{ + mTextureStage = stage; + if (gGLManager.mHasCubeMap && stage >= 0 && LLCubeMap::sUseCubeMaps) + { + gGL.getTexUnit(stage)->enable(LLTexUnit::TT_CUBE_MAP); + } +} + +void LLCubeMap::enableTextureCoords(S32 stage) +{ + mTextureCoordStage = stage; + if (gGLManager.mHasCubeMap && stage >= 0 && LLCubeMap::sUseCubeMaps) + { + if (stage > 0) + { + gGL.getTexUnit(stage)->activate(); + } + + glEnable(GL_TEXTURE_GEN_R); + glEnable(GL_TEXTURE_GEN_S); + glEnable(GL_TEXTURE_GEN_T); + + glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP); + glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP); + glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP); + + if (stage > 0) + { + gGL.getTexUnit(0)->activate(); + } + } +} + +void LLCubeMap::disable(void) +{ + disableTexture(); + disableTextureCoords(); +} + +void LLCubeMap::disableTexture(void) +{ + if (gGLManager.mHasCubeMap && mTextureStage >= 0 && LLCubeMap::sUseCubeMaps) + { + gGL.getTexUnit(mTextureStage)->disable(); + if (mTextureStage == 0) + { + gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); + } + } +} + +void LLCubeMap::disableTextureCoords(void) +{ + if (gGLManager.mHasCubeMap && mTextureCoordStage >= 0 && LLCubeMap::sUseCubeMaps) + { + if (mTextureCoordStage > 0) + { + gGL.getTexUnit(mTextureCoordStage)->activate(); + } + glDisable(GL_TEXTURE_GEN_S); + glDisable(GL_TEXTURE_GEN_T); + glDisable(GL_TEXTURE_GEN_R); + if (mTextureCoordStage > 0) + { + gGL.getTexUnit(0)->activate(); + } + } +} + +void LLCubeMap::setMatrix(S32 stage) +{ + mMatrixStage = stage; + + if (mMatrixStage < 0) return; + + if (stage > 0) + { + gGL.getTexUnit(stage)->activate(); + } + + LLVector3 x(LLVector3d(gGLModelView+0)); + LLVector3 y(LLVector3d(gGLModelView+4)); + LLVector3 z(LLVector3d(gGLModelView+8)); + + LLMatrix3 mat3; + mat3.setRows(x,y,z); + LLMatrix4 trans(mat3); + trans.transpose(); + + glMatrixMode(GL_TEXTURE); + glPushMatrix(); + glLoadMatrixf((F32 *)trans.mMatrix); + glMatrixMode(GL_MODELVIEW); + + if (stage > 0) + { + gGL.getTexUnit(0)->activate(); + } +} + +void LLCubeMap::restoreMatrix() +{ + if (mMatrixStage < 0) return; + + if (mMatrixStage > 0) + { + gGL.getTexUnit(mMatrixStage)->activate(); + } + glMatrixMode(GL_TEXTURE); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + + if (mMatrixStage > 0) + { + gGL.getTexUnit(0)->activate(); + } +} + +void LLCubeMap::setReflection (void) +{ + gGL.getTexUnit(mTextureStage)->bindManual(LLTexUnit::TT_CUBE_MAP, getGLName()); + mImages[0]->setFilteringOption(LLTexUnit::TFO_ANISOTROPIC); + mImages[0]->setAddressMode(LLTexUnit::TAM_CLAMP); +} + +LLVector3 LLCubeMap::map(U8 side, U16 v_val, U16 h_val) const +{ + LLVector3 dir; + + const U8 curr_coef = side >> 1; // 0/1 = X axis, 2/3 = Y, 4/5 = Z + const S8 side_dir = (((side & 1) << 1) - 1); // even = -1, odd = 1 + const U8 i_coef = (curr_coef + 1) % 3; + const U8 j_coef = (i_coef + 1) % 3; + + dir.mV[curr_coef] = side_dir; + + switch (side) + { + case 0: // negative X + dir.mV[i_coef] = -F32((v_val<<1) + 1) / RESOLUTION + 1; + dir.mV[j_coef] = F32((h_val<<1) + 1) / RESOLUTION - 1; + break; + case 1: // positive X + dir.mV[i_coef] = -F32((v_val<<1) + 1) / RESOLUTION + 1; + dir.mV[j_coef] = -F32((h_val<<1) + 1) / RESOLUTION + 1; + break; + case 2: // negative Y + dir.mV[i_coef] = -F32((v_val<<1) + 1) / RESOLUTION + 1; + dir.mV[j_coef] = F32((h_val<<1) + 1) / RESOLUTION - 1; + break; + case 3: // positive Y + dir.mV[i_coef] = F32((v_val<<1) + 1) / RESOLUTION - 1; + dir.mV[j_coef] = F32((h_val<<1) + 1) / RESOLUTION - 1; + break; + case 4: // negative Z + dir.mV[i_coef] = -F32((h_val<<1) + 1) / RESOLUTION + 1; + dir.mV[j_coef] = -F32((v_val<<1) + 1) / RESOLUTION + 1; + break; + case 5: // positive Z + dir.mV[i_coef] = -F32((h_val<<1) + 1) / RESOLUTION + 1; + dir.mV[j_coef] = F32((v_val<<1) + 1) / RESOLUTION - 1; + break; + default: + dir.mV[i_coef] = F32((v_val<<1) + 1) / RESOLUTION - 1; + dir.mV[j_coef] = F32((h_val<<1) + 1) / RESOLUTION - 1; + } + + dir.normVec(); + return dir; +} + + +BOOL LLCubeMap::project(F32& v_val, F32& h_val, BOOL& outside, + U8 side, const LLVector3& dir) const +{ + const U8 curr_coef = side >> 1; // 0/1 = X axis, 2/3 = Y, 4/5 = Z + const S8 side_dir = (((side & 1) << 1) - 1); // even = -1, odd = 1 + const U8 i_coef = (curr_coef + 1) % 3; + const U8 j_coef = (i_coef + 1) % 3; + + outside = TRUE; + if (side_dir * dir.mV[curr_coef] < 0) + return FALSE; + + LLVector3 ray; + + F32 norm_val = fabs(dir.mV[curr_coef]); + + if (norm_val < epsilon) + norm_val = 1e-5f; + + ray.mV[curr_coef] = side_dir; + ray.mV[i_coef] = dir.mV[i_coef] / norm_val; + ray.mV[j_coef] = dir.mV[j_coef] / norm_val; + + + const F32 i_val = (ray.mV[i_coef] + 1) * 0.5f * RESOLUTION; + const F32 j_val = (ray.mV[j_coef] + 1) * 0.5f * RESOLUTION; + + switch (side) + { + case 0: // negative X + v_val = RESOLUTION - i_val; + h_val = j_val; + break; + case 1: // positive X + v_val = RESOLUTION - i_val; + h_val = RESOLUTION - j_val; + break; + case 2: // negative Y + v_val = RESOLUTION - i_val; + h_val = j_val; + break; + case 3: // positive Y + v_val = i_val; + h_val = j_val; + break; + case 4: // negative Z + v_val = RESOLUTION - j_val; + h_val = RESOLUTION - i_val; + break; + case 5: // positive Z + v_val = RESOLUTION - j_val; + h_val = i_val; + break; + default: + v_val = i_val; + h_val = j_val; + } + + outside = ((v_val < 0) || (v_val > RESOLUTION) || + (h_val < 0) || (h_val > RESOLUTION)); + + return TRUE; +} + +BOOL LLCubeMap::project(F32& v_min, F32& v_max, F32& h_min, F32& h_max, + U8 side, LLVector3 dir[4]) const +{ + v_min = h_min = RESOLUTION; + v_max = h_max = 0; + + BOOL fully_outside = TRUE; + for (U8 vtx = 0; vtx < 4; ++vtx) + { + F32 v_val, h_val; + BOOL outside; + BOOL consider = project(v_val, h_val, outside, side, dir[vtx]); + if (!outside) + fully_outside = FALSE; + if (consider) + { + if (v_val < v_min) v_min = v_val; + if (v_val > v_max) v_max = v_val; + if (h_val < h_min) h_min = h_val; + if (h_val > h_max) h_max = h_val; + } + } + + v_min = llmax(0.0f, v_min); + v_max = llmin(RESOLUTION - epsilon, v_max); + h_min = llmax(0.0f, h_min); + h_max = llmin(RESOLUTION - epsilon, h_max); + + return !fully_outside; +} + + +void LLCubeMap::paintIn(LLVector3 dir[4], const LLColor4U& col) +{ + F32 v_min, v_max, h_min, h_max; + LLVector3 center = dir[0] + dir[1] + dir[2] + dir[3]; + center.normVec(); + + for (U8 side = 0; side < 6; ++side) + { + if (!project(v_min, v_max, h_min, h_max, side, dir)) + continue; + + U8 *td = mRawImages[side]->getData(); + + U16 v_minu = (U16) v_min; + U16 v_maxu = (U16) (ceil(v_max) + 0.5); + U16 h_minu = (U16) h_min; + U16 h_maxu = (U16) (ceil(h_max) + 0.5); + + for (U16 v = v_minu; v < v_maxu; ++v) + for (U16 h = h_minu; h < h_maxu; ++h) + //for (U16 v = 0; v < RESOLUTION; ++v) + // for (U16 h = 0; h < RESOLUTION; ++h) + { + const LLVector3 ray = map(side, v, h); + if (ray * center > 0.999) + { + const U32 offset = (RESOLUTION * v + h) * 4; + for (U8 cc = 0; cc < 3; ++cc) + td[offset + cc] = U8((td[offset + cc] + col.mV[cc]) * 0.5); + } + } + mImages[side]->setSubImage(mRawImages[side], 0, 0, 64, 64); + } +} + +void LLCubeMap::destroyGL() +{ + for (S32 i = 0; i < 6; i++) + { + mImages[i] = NULL; + } +} diff --git a/indra/llrender/llcubemap.h b/indra/llrender/llcubemap.h new file mode 100644 index 0000000000..ee2c41e026 --- /dev/null +++ b/indra/llrender/llcubemap.h @@ -0,0 +1,86 @@ +/** + * @file llcubemap.h + * @brief LLCubeMap class definition + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * 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; + * version 2.1 of the License only. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLCUBEMAP_H +#define LL_LLCUBEMAP_H + +#include "llgl.h" + +#include <vector> + +class LLVector3; + +// Environment map hack! +class LLCubeMap : public LLRefCount +{ +public: + LLCubeMap(); + void init(const std::vector<LLPointer<LLImageRaw> >& rawimages); + void initGL(); + void initRawData(const std::vector<LLPointer<LLImageRaw> >& rawimages); + void initGLData(); + + void bind(); + void enable(S32 stage); + + void enableTexture(S32 stage); + void enableTextureCoords(S32 stage); + S32 getStage(void) { return mTextureStage; } + + void disable(void); + void disableTexture(void); + void disableTextureCoords(void); + void setMatrix(S32 stage); + void restoreMatrix(); + void setReflection (void); + + void finishPaint(); + + GLuint getGLName(); + + LLVector3 map(U8 side, U16 v_val, U16 h_val) const; + BOOL project(F32& v_val, F32& h_val, BOOL& outside, + U8 side, const LLVector3& dir) const; + BOOL project(F32& v_min, F32& v_max, F32& h_min, F32& h_max, + U8 side, LLVector3 dir[4]) const; + void paintIn(LLVector3 dir[4], const LLColor4U& col); + void destroyGL(); + +public: + static bool sUseCubeMaps; + +protected: + friend class LLTexUnit; + ~LLCubeMap(); + LLGLenum mTargets[6]; + LLPointer<LLImageGL> mImages[6]; + LLPointer<LLImageRaw> mRawImages[6]; + S32 mTextureStage; + S32 mTextureCoordStage; + S32 mMatrixStage; +}; + +#endif diff --git a/indra/llrender/llfontbitmapcache.cpp b/indra/llrender/llfontbitmapcache.cpp new file mode 100644 index 0000000000..c985f6b959 --- /dev/null +++ b/indra/llrender/llfontbitmapcache.cpp @@ -0,0 +1,163 @@ +/** + * @file llfontbitmapcache.cpp + * @brief Storage for previously rendered glyphs. + * + * $LicenseInfo:firstyear=2008&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * 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; + * version 2.1 of the License only. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llgl.h" +#include "llfontbitmapcache.h" + +LLFontBitmapCache::LLFontBitmapCache(): + mNumComponents(0), + mBitmapWidth(0), + mBitmapHeight(0), + mBitmapNum(-1), + mMaxCharWidth(0), + mMaxCharHeight(0), + mCurrentOffsetX(1), + mCurrentOffsetY(1) +{ +} + +LLFontBitmapCache::~LLFontBitmapCache() +{ +} + +void LLFontBitmapCache::init(S32 num_components, + S32 max_char_width, + S32 max_char_height) +{ + reset(); + + mNumComponents = num_components; + mMaxCharWidth = max_char_width; + mMaxCharHeight = max_char_height; +} + +LLImageRaw *LLFontBitmapCache::getImageRaw(U32 bitmap_num) const +{ + if (bitmap_num >= mImageRawVec.size()) + return NULL; + + return mImageRawVec[bitmap_num]; +} + +LLImageGL *LLFontBitmapCache::getImageGL(U32 bitmap_num) const +{ + if (bitmap_num >= mImageGLVec.size()) + return NULL; + + return mImageGLVec[bitmap_num]; +} + + +BOOL LLFontBitmapCache::nextOpenPos(S32 width, S32 &pos_x, S32 &pos_y, S32& bitmap_num) +{ + if ((mBitmapNum<0) || (mCurrentOffsetX + width + 1) > mBitmapWidth) + { + if ((mBitmapNum<0) || (mCurrentOffsetY + 2*mMaxCharHeight + 2) > mBitmapHeight) + { + // We're out of space in the current image, or no image + // has been allocated yet. Make a new one. + mImageRawVec.push_back(new LLImageRaw); + mBitmapNum = mImageRawVec.size()-1; + LLImageRaw *image_raw = getImageRaw(mBitmapNum); + + // Make corresponding GL image. + mImageGLVec.push_back(new LLImageGL(FALSE)); + LLImageGL *image_gl = getImageGL(mBitmapNum); + + S32 image_width = mMaxCharWidth * 20; + S32 pow_iw = 2; + while (pow_iw < image_width) + { + pow_iw *= 2; + } + image_width = pow_iw; + image_width = llmin(512, image_width); // Don't make bigger than 512x512, ever. + S32 image_height = image_width; + + image_raw->resize(image_width, image_height, mNumComponents); + + mBitmapWidth = image_width; + mBitmapHeight = image_height; + + switch (mNumComponents) + { + case 1: + image_raw->clear(); + break; + case 2: + image_raw->clear(255, 0); + break; + } + + // Start at beginning of the new image. + mCurrentOffsetX = 1; + mCurrentOffsetY = 1; + + // Attach corresponding GL texture. + image_gl->createGLTexture(0, image_raw); + gGL.getTexUnit(0)->bind(image_gl); + image_gl->setFilteringOption(LLTexUnit::TFO_POINT); // was setMipFilterNearest(TRUE, TRUE); + } + else + { + // Move to next row in current image. + mCurrentOffsetX = 1; + mCurrentOffsetY += mMaxCharHeight + 1; + } + } + + pos_x = mCurrentOffsetX; + pos_y = mCurrentOffsetY; + bitmap_num = mBitmapNum; + + mCurrentOffsetX += width + 1; + + return TRUE; +} + +void LLFontBitmapCache::destroyGL() +{ + for (std::vector<LLPointer<LLImageGL> >::iterator it = mImageGLVec.begin(); + it != mImageGLVec.end(); ++it) + { + (*it)->destroyGLTexture(); + } +} + +void LLFontBitmapCache::reset() +{ + mImageRawVec.clear(); + mImageGLVec.clear(); + + mBitmapWidth = 0; + mBitmapHeight = 0; + mBitmapNum = -1; + mCurrentOffsetX = 1; + mCurrentOffsetY = 1; +} + diff --git a/indra/llrender/llfontbitmapcache.h b/indra/llrender/llfontbitmapcache.h new file mode 100644 index 0000000000..c93b0c7320 --- /dev/null +++ b/indra/llrender/llfontbitmapcache.h @@ -0,0 +1,72 @@ +/** + * @file llfontbitmapcache.h + * @brief Storage for previously rendered glyphs. + * + * $LicenseInfo:firstyear=2008&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * 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; + * version 2.1 of the License only. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLFONTBITMAPCACHE_H +#define LL_LLFONTBITMAPCACHE_H + +#include <vector> + +// Maintain a collection of bitmaps containing rendered glyphs. +// Generalizes the single-bitmap logic from LLFontFreetype and LLFontGL. +class LLFontBitmapCache: public LLRefCount +{ +public: + LLFontBitmapCache(); + ~LLFontBitmapCache(); + + // Need to call this once, before caching any glyphs. + void init(S32 num_components, + S32 max_char_width, + S32 max_char_height); + + void reset(); + + BOOL nextOpenPos(S32 width, S32 &posX, S32 &posY, S32 &bitmapNum); + + void destroyGL(); + + LLImageRaw *getImageRaw(U32 bitmapNum = 0) const; + LLImageGL *getImageGL(U32 bitmapNum = 0) const; + + S32 getMaxCharWidth() const { return mMaxCharWidth; } + S32 getNumComponents() const { return mNumComponents; } + S32 getBitmapWidth() const { return mBitmapWidth; } + S32 getBitmapHeight() const { return mBitmapHeight; } + +private: + S32 mNumComponents; + S32 mBitmapWidth; + S32 mBitmapHeight; + S32 mBitmapNum; + S32 mMaxCharWidth; + S32 mMaxCharHeight; + S32 mCurrentOffsetX; + S32 mCurrentOffsetY; + std::vector<LLPointer<LLImageRaw> > mImageRawVec; + std::vector<LLPointer<LLImageGL> > mImageGLVec; +}; + +#endif //LL_LLFONTBITMAPCACHE_H diff --git a/indra/llrender/llfontfreetype.cpp b/indra/llrender/llfontfreetype.cpp new file mode 100644 index 0000000000..b84e696e2d --- /dev/null +++ b/indra/llrender/llfontfreetype.cpp @@ -0,0 +1,592 @@ +/** + * @file llfontfreetype.cpp + * @brief Freetype font library wrapper + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * 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; + * version 2.1 of the License only. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llfontfreetype.h" +#include "llfontgl.h" + +// Freetype stuff +#include <ft2build.h> + +// For some reason, this won't work if it's not wrapped in the ifdef +#ifdef FT_FREETYPE_H +#include FT_FREETYPE_H +#endif + +#include "llerror.h" +#include "llimage.h" +//#include "llimagej2c.h" +#include "llmath.h" // Linden math +#include "llstring.h" +//#include "imdebug.h" +#include "llfontbitmapcache.h" +#include "llgl.h" + +FT_Render_Mode gFontRenderMode = FT_RENDER_MODE_NORMAL; + +LLFontManager *gFontManagerp = NULL; + +FT_Library gFTLibrary = NULL; + +//static +void LLFontManager::initClass() +{ + gFontManagerp = new LLFontManager; +} + +//static +void LLFontManager::cleanupClass() +{ + delete gFontManagerp; + gFontManagerp = NULL; +} + +LLFontManager::LLFontManager() +{ + int error; + error = FT_Init_FreeType(&gFTLibrary); + if (error) + { + // Clean up freetype libs. + llerrs << "Freetype initialization failure!" << llendl; + FT_Done_FreeType(gFTLibrary); + } +} + +LLFontManager::~LLFontManager() +{ + FT_Done_FreeType(gFTLibrary); +} + + +LLFontGlyphInfo::LLFontGlyphInfo(U32 index) +: mGlyphIndex(index), + mWidth(0), // In pixels + mHeight(0), // In pixels + mXAdvance(0.f), // In pixels + mYAdvance(0.f), // In pixels + mXBitmapOffset(0), // Offset to the origin in the bitmap + mYBitmapOffset(0), // Offset to the origin in the bitmap + mXBearing(0), // Distance from baseline to left in pixels + mYBearing(0), // Distance from baseline to top in pixels + mBitmapNum(0) // Which bitmap in the bitmap cache contains this glyph +{ +} + +LLFontFreetype::LLFontFreetype() +: mFontBitmapCachep(new LLFontBitmapCache), + mValid(FALSE), + mAscender(0.f), + mDescender(0.f), + mLineHeight(0.f), + mIsFallback(FALSE), + mFTFace(NULL), + mRenderGlyphCount(0), + mAddGlyphCount(0), + mStyle(0), + mPointSize(0) +{ +} + + +LLFontFreetype::~LLFontFreetype() +{ + // Clean up freetype libs. + if (mFTFace) + FT_Done_Face(mFTFace); + mFTFace = NULL; + + // Delete glyph info + std::for_each(mCharGlyphInfoMap.begin(), mCharGlyphInfoMap.end(), DeletePairedPointer()); + + // mFontBitmapCachep will be cleaned up by LLPointer destructor. + // mFallbackFonts cleaned up by LLPointer destructor +} + +BOOL LLFontFreetype::loadFace(const std::string& filename, F32 point_size, F32 vert_dpi, F32 horz_dpi, S32 components, BOOL is_fallback) +{ + // Don't leak face objects. This is also needed to deal with + // changed font file names. + if (mFTFace) + { + FT_Done_Face(mFTFace); + mFTFace = NULL; + } + + int error; + + error = FT_New_Face( gFTLibrary, + filename.c_str(), + 0, + &mFTFace ); + + if (error) + { + return FALSE; + } + + mIsFallback = is_fallback; + F32 pixels_per_em = (point_size / 72.f)*vert_dpi; // Size in inches * dpi + + error = FT_Set_Char_Size(mFTFace, /* handle to face object */ + 0, /* char_width in 1/64th of points */ + (S32)(point_size*64), /* char_height in 1/64th of points */ + (U32)horz_dpi, /* horizontal device resolution */ + (U32)vert_dpi); /* vertical device resolution */ + + if (error) + { + // Clean up freetype libs. + FT_Done_Face(mFTFace); + mFTFace = NULL; + return FALSE; + } + + F32 y_max, y_min, x_max, x_min; + F32 ems_per_unit = 1.f/ mFTFace->units_per_EM; + F32 pixels_per_unit = pixels_per_em * ems_per_unit; + + // Get size of bbox in pixels + y_max = mFTFace->bbox.yMax * pixels_per_unit; + y_min = mFTFace->bbox.yMin * pixels_per_unit; + x_max = mFTFace->bbox.xMax * pixels_per_unit; + x_min = mFTFace->bbox.xMin * pixels_per_unit; + mAscender = mFTFace->ascender * pixels_per_unit; + mDescender = -mFTFace->descender * pixels_per_unit; + mLineHeight = mFTFace->height * pixels_per_unit; + + S32 max_char_width = llround(0.5f + (x_max - x_min)); + S32 max_char_height = llround(0.5f + (y_max - y_min)); + + mFontBitmapCachep->init(components, max_char_width, max_char_height); + + if (!mFTFace->charmap) + { + //llinfos << " no unicode encoding, set whatever encoding there is..." << llendl; + FT_Set_Charmap(mFTFace, mFTFace->charmaps[0]); + } + + if (!mIsFallback) + { + // Add the default glyph + addGlyphFromFont(this, 0, 0); + } + + mName = filename; + mPointSize = point_size; + + mStyle = LLFontGL::NORMAL; + if(mFTFace->style_flags & FT_STYLE_FLAG_BOLD) + { + mStyle |= LLFontGL::BOLD; + mStyle &= ~LLFontGL::NORMAL; + } + + if(mFTFace->style_flags & FT_STYLE_FLAG_ITALIC) + { + mStyle |= LLFontGL::ITALIC; + mStyle &= ~LLFontGL::NORMAL; + } + + return TRUE; +} + +void LLFontFreetype::setFallbackFonts(const font_vector_t &font) +{ + mFallbackFonts = font; +} + +const LLFontFreetype::font_vector_t &LLFontFreetype::getFallbackFonts() const +{ + return mFallbackFonts; +} + +F32 LLFontFreetype::getLineHeight() const +{ + return mLineHeight; +} + +F32 LLFontFreetype::getAscenderHeight() const +{ + return mAscender; +} + +F32 LLFontFreetype::getDescenderHeight() const +{ + return mDescender; +} + +F32 LLFontFreetype::getXAdvance(llwchar wch) const +{ + if (mFTFace == NULL) + return 0.0; + + // Return existing info only if it is current + LLFontGlyphInfo* gi = getGlyphInfo(wch); + if (gi) + { + return gi->mXAdvance; + } + else + { + char_glyph_info_map_t::iterator found_it = mCharGlyphInfoMap.find((llwchar)0); + if (found_it != mCharGlyphInfoMap.end()) + { + return found_it->second->mXAdvance; + } + } + + // Last ditch fallback - no glyphs defined at all. + return (F32)mFontBitmapCachep->getMaxCharWidth(); +} + +F32 LLFontFreetype::getXAdvance(const LLFontGlyphInfo* glyph) const +{ + if (mFTFace == NULL) + return 0.0; + + return glyph->mXAdvance; +} + +F32 LLFontFreetype::getXKerning(llwchar char_left, llwchar char_right) const +{ + if (mFTFace == NULL) + return 0.0; + + //llassert(!mIsFallback); + LLFontGlyphInfo* left_glyph_info = getGlyphInfo(char_left);; + U32 left_glyph = left_glyph_info ? left_glyph_info->mGlyphIndex : 0; + // Kern this puppy. + LLFontGlyphInfo* right_glyph_info = getGlyphInfo(char_right); + U32 right_glyph = right_glyph_info ? right_glyph_info->mGlyphIndex : 0; + + FT_Vector delta; + + llverify(!FT_Get_Kerning(mFTFace, left_glyph, right_glyph, ft_kerning_unfitted, &delta)); + + return delta.x*(1.f/64.f); +} + +F32 LLFontFreetype::getXKerning(const LLFontGlyphInfo* left_glyph_info, const LLFontGlyphInfo* right_glyph_info) const +{ + if (mFTFace == NULL) + return 0.0; + + U32 left_glyph = left_glyph_info ? left_glyph_info->mGlyphIndex : 0; + U32 right_glyph = right_glyph_info ? right_glyph_info->mGlyphIndex : 0; + + FT_Vector delta; + + llverify(!FT_Get_Kerning(mFTFace, left_glyph, right_glyph, ft_kerning_unfitted, &delta)); + + return delta.x*(1.f/64.f); +} + +BOOL LLFontFreetype::hasGlyph(llwchar wch) const +{ + llassert(!mIsFallback); + return(mCharGlyphInfoMap.find(wch) != mCharGlyphInfoMap.end()); +} + +LLFontGlyphInfo* LLFontFreetype::addGlyph(llwchar wch) const +{ + if (mFTFace == NULL) + return FALSE; + + llassert(!mIsFallback); + //lldebugs << "Adding new glyph for " << wch << " to font" << llendl; + + FT_UInt glyph_index; + + // Initialize char to glyph map + glyph_index = FT_Get_Char_Index(mFTFace, wch); + if (glyph_index == 0) + { + //llinfos << "Trying to add glyph from fallback font!" << llendl; + font_vector_t::const_iterator iter; + for(iter = mFallbackFonts.begin(); iter != mFallbackFonts.end(); iter++) + { + glyph_index = FT_Get_Char_Index((*iter)->mFTFace, wch); + if (glyph_index) + { + return addGlyphFromFont(*iter, wch, glyph_index); + } + } + } + + char_glyph_info_map_t::iterator iter = mCharGlyphInfoMap.find(wch); + if (iter == mCharGlyphInfoMap.end()) + { + return addGlyphFromFont(this, wch, glyph_index); + } + return NULL; +} + +LLFontGlyphInfo* LLFontFreetype::addGlyphFromFont(const LLFontFreetype *fontp, llwchar wch, U32 glyph_index) const +{ + if (mFTFace == NULL) + return NULL; + + llassert(!mIsFallback); + fontp->renderGlyph(glyph_index); + S32 width = fontp->mFTFace->glyph->bitmap.width; + S32 height = fontp->mFTFace->glyph->bitmap.rows; + + S32 pos_x, pos_y; + S32 bitmap_num; + mFontBitmapCachep->nextOpenPos(width, pos_x, pos_y, bitmap_num); + mAddGlyphCount++; + + LLFontGlyphInfo* gi = new LLFontGlyphInfo(glyph_index); + gi->mXBitmapOffset = pos_x; + gi->mYBitmapOffset = pos_y; + gi->mBitmapNum = bitmap_num; + gi->mWidth = width; + gi->mHeight = height; + gi->mXBearing = fontp->mFTFace->glyph->bitmap_left; + gi->mYBearing = fontp->mFTFace->glyph->bitmap_top; + // Convert these from 26.6 units to float pixels. + gi->mXAdvance = fontp->mFTFace->glyph->advance.x / 64.f; + gi->mYAdvance = fontp->mFTFace->glyph->advance.y / 64.f; + + insertGlyphInfo(wch, gi); + + llassert(fontp->mFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO + || fontp->mFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY); + + if (fontp->mFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO + || fontp->mFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) + { + U8 *buffer_data = fontp->mFTFace->glyph->bitmap.buffer; + S32 buffer_row_stride = fontp->mFTFace->glyph->bitmap.pitch; + U8 *tmp_graydata = NULL; + + if (fontp->mFTFace->glyph->bitmap.pixel_mode + == FT_PIXEL_MODE_MONO) + { + // need to expand 1-bit bitmap to 8-bit graymap. + tmp_graydata = new U8[width * height]; + S32 xpos, ypos; + for (ypos = 0; ypos < height; ++ypos) + { + S32 bm_row_offset = buffer_row_stride * ypos; + for (xpos = 0; xpos < width; ++xpos) + { + U32 bm_col_offsetbyte = xpos / 8; + U32 bm_col_offsetbit = 7 - (xpos % 8); + U32 bit = + !!(buffer_data[bm_row_offset + + bm_col_offsetbyte + ] & (1 << bm_col_offsetbit) ); + tmp_graydata[width*ypos + xpos] = + 255 * bit; + } + } + // use newly-built graymap. + buffer_data = tmp_graydata; + buffer_row_stride = width; + } + + switch (mFontBitmapCachep->getNumComponents()) + { + case 1: + mFontBitmapCachep->getImageRaw(bitmap_num)->setSubImage(pos_x, + pos_y, + width, + height, + buffer_data, + buffer_row_stride, + TRUE); + break; + case 2: + setSubImageLuminanceAlpha(pos_x, + pos_y, + bitmap_num, + width, + height, + buffer_data, + buffer_row_stride); + break; + default: + break; + } + + if (tmp_graydata) + delete[] tmp_graydata; + } else { + // we don't know how to handle this pixel format from FreeType; + // omit it from the font-image. + } + + LLImageGL *image_gl = mFontBitmapCachep->getImageGL(bitmap_num); + LLImageRaw *image_raw = mFontBitmapCachep->getImageRaw(bitmap_num); + image_gl->setSubImage(image_raw, 0, 0, image_gl->getWidth(), image_gl->getHeight()); + + return gi; +} + +LLFontGlyphInfo* LLFontFreetype::getGlyphInfo(llwchar wch) const +{ + char_glyph_info_map_t::iterator iter = mCharGlyphInfoMap.find(wch); + if (iter != mCharGlyphInfoMap.end()) + { + return iter->second; + } + else + { + // this glyph doesn't yet exist, so render it and return the result + return addGlyph(wch); + } +} + +void LLFontFreetype::insertGlyphInfo(llwchar wch, LLFontGlyphInfo* gi) const +{ + char_glyph_info_map_t::iterator iter = mCharGlyphInfoMap.find(wch); + if (iter != mCharGlyphInfoMap.end()) + { + delete iter->second; + iter->second = gi; + } + else + { + mCharGlyphInfoMap[wch] = gi; + } +} + +void LLFontFreetype::renderGlyph(U32 glyph_index) const +{ + if (mFTFace == NULL) + return; + + int error = FT_Load_Glyph(mFTFace, glyph_index, FT_LOAD_DEFAULT ); + llassert(!error); + + error = FT_Render_Glyph(mFTFace->glyph, gFontRenderMode); + + mRenderGlyphCount++; + + llassert(!error); +} + +void LLFontFreetype::reset(F32 vert_dpi, F32 horz_dpi) +{ + resetBitmapCache(); + loadFace(mName, mPointSize, vert_dpi ,horz_dpi, mFontBitmapCachep->getNumComponents(), mIsFallback); + if (!mIsFallback) + { + // This is the head of the list - need to rebuild ourself and all fallbacks. + if (mFallbackFonts.empty()) + { + llwarns << "LLFontGL::reset(), no fallback fonts present" << llendl; + } + else + { + for(font_vector_t::iterator it = mFallbackFonts.begin(); + it != mFallbackFonts.end(); + ++it) + { + (*it)->reset(vert_dpi, horz_dpi); + } + } + } +} + +void LLFontFreetype::resetBitmapCache() +{ + for_each(mCharGlyphInfoMap.begin(), mCharGlyphInfoMap.end(), DeletePairedPointer()); + mCharGlyphInfoMap.clear(); + mFontBitmapCachep->reset(); + + // Adding default glyph is skipped for fallback fonts here as well as in loadFace(). + // This if was added as fix for EXT-4971. + if(!mIsFallback) + { + // Add the empty glyph + addGlyphFromFont(this, 0, 0); + } +} + +void LLFontFreetype::destroyGL() +{ + mFontBitmapCachep->destroyGL(); +} + +const std::string &LLFontFreetype::getName() const +{ + return mName; +} + +const LLPointer<LLFontBitmapCache> LLFontFreetype::getFontBitmapCache() const +{ + return mFontBitmapCachep; +} + +void LLFontFreetype::setStyle(U8 style) +{ + mStyle = style; +} + +U8 LLFontFreetype::getStyle() const +{ + return mStyle; +} + +void LLFontFreetype::setSubImageLuminanceAlpha(U32 x, U32 y, U32 bitmap_num, U32 width, U32 height, U8 *data, S32 stride) const +{ + LLImageRaw *image_raw = mFontBitmapCachep->getImageRaw(bitmap_num); + + llassert(!mIsFallback); + llassert(image_raw && (image_raw->getComponents() == 2)); + + + U8 *target = image_raw->getData(); + + if (!data) + { + return; + } + + if (0 == stride) + stride = width; + + U32 i, j; + U32 to_offset; + U32 from_offset; + U32 target_width = image_raw->getWidth(); + for (i = 0; i < height; i++) + { + to_offset = (y + i)*target_width + x; + from_offset = (height - 1 - i)*stride; + for (j = 0; j < width; j++) + { + *(target + to_offset*2 + 1) = *(data + from_offset); + to_offset++; + from_offset++; + } + } +} + diff --git a/indra/llrender/llfontfreetype.h b/indra/llrender/llfontfreetype.h new file mode 100644 index 0000000000..f1b23f22d5 --- /dev/null +++ b/indra/llrender/llfontfreetype.h @@ -0,0 +1,176 @@ +/** + * @file llfontfreetype.h + * @brief Font library wrapper + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * 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; + * version 2.1 of the License only. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLFONTFREETYPE_H +#define LL_LLFONTFREETYPE_H + +#include <boost/unordered_map.hpp> +#include "llpointer.h" +#include "llstl.h" + +#include "llimagegl.h" +#include "llfontbitmapcache.h" + +// Hack. FT_Face is just a typedef for a pointer to a struct, +// but there's no simple forward declarations file for FreeType, +// and the main include file is 200K. +// We'll forward declare the struct here. JC +struct FT_FaceRec_; +typedef struct FT_FaceRec_* LLFT_Face; + +class LLFontManager +{ +public: + static void initClass(); + static void cleanupClass(); + +private: + LLFontManager(); + ~LLFontManager(); +}; + +struct LLFontGlyphInfo +{ + LLFontGlyphInfo(U32 index); + + U32 mGlyphIndex; + + // Metrics + S32 mWidth; // In pixels + S32 mHeight; // In pixels + F32 mXAdvance; // In pixels + F32 mYAdvance; // In pixels + + // Information for actually rendering + S32 mXBitmapOffset; // Offset to the origin in the bitmap + S32 mYBitmapOffset; // Offset to the origin in the bitmap + S32 mXBearing; // Distance from baseline to left in pixels + S32 mYBearing; // Distance from baseline to top in pixels + S32 mBitmapNum; // Which bitmap in the bitmap cache contains this glyph +}; + +extern LLFontManager *gFontManagerp; + +class LLFontFreetype : public LLRefCount +{ +public: + LLFontFreetype(); + ~LLFontFreetype(); + + // is_fallback should be true for fallback fonts that aren't used + // to render directly (Unicode backup, primarily) + BOOL loadFace(const std::string& filename, F32 point_size, F32 vert_dpi, F32 horz_dpi, S32 components, BOOL is_fallback); + + typedef std::vector<LLPointer<LLFontFreetype> > font_vector_t; + + void setFallbackFonts(const font_vector_t &font); + const font_vector_t &getFallbackFonts() const; + + // Global font metrics - in units of pixels + F32 getLineHeight() const; + F32 getAscenderHeight() const; + F32 getDescenderHeight() const; + + +// For a lowercase "g": +// +// ------------------------------ +// ^ ^ +// | | +// xxx x |Ascender +// x x v | +// --------- xxxx-------------- Baseline +// ^ x | +// | Descender x | +// v xxxx |LineHeight +// ----------------------- | +// v +// ------------------------------ + + enum + { + FIRST_CHAR = 32, + NUM_CHARS = 127 - 32, + LAST_CHAR_BASIC = 127, + + // Need full 8-bit ascii range for spanish + NUM_CHARS_FULL = 255 - 32, + LAST_CHAR_FULL = 255 + }; + + F32 getXAdvance(llwchar wc) const; + F32 getXAdvance(const LLFontGlyphInfo* glyph) const; + F32 getXKerning(llwchar char_left, llwchar char_right) const; // Get the kerning between the two characters + F32 getXKerning(const LLFontGlyphInfo* left_glyph_info, const LLFontGlyphInfo* right_glyph_info) const; // Get the kerning between the two characters + + LLFontGlyphInfo* getGlyphInfo(llwchar wch) const; + + void reset(F32 vert_dpi, F32 horz_dpi); + + void destroyGL(); + + const std::string& getName() const; + + const LLPointer<LLFontBitmapCache> getFontBitmapCache() const; + + void setStyle(U8 style); + U8 getStyle() const; + +private: + void resetBitmapCache(); + void setSubImageLuminanceAlpha(U32 x, U32 y, U32 bitmap_num, U32 width, U32 height, U8 *data, S32 stride = 0) const; + BOOL hasGlyph(llwchar wch) const; // Has a glyph for this character + LLFontGlyphInfo* addGlyph(llwchar wch) const; // Add a new character to the font if necessary + LLFontGlyphInfo* addGlyphFromFont(const LLFontFreetype *fontp, llwchar wch, U32 glyph_index) const; // Add a glyph from this font to the other (returns the glyph_index, 0 if not found) + void renderGlyph(U32 glyph_index) const; + void insertGlyphInfo(llwchar wch, LLFontGlyphInfo* gi) const; + + std::string mName; + + U8 mStyle; + + F32 mPointSize; + F32 mAscender; + F32 mDescender; + F32 mLineHeight; + + LLFT_Face mFTFace; + + BOOL mIsFallback; + font_vector_t mFallbackFonts; // A list of fallback fonts to look for glyphs in (for Unicode chars) + + BOOL mValid; + + typedef boost::unordered_map<llwchar, LLFontGlyphInfo*> char_glyph_info_map_t; + mutable char_glyph_info_map_t mCharGlyphInfoMap; // Information about glyph location in bitmap + + mutable LLPointer<LLFontBitmapCache> mFontBitmapCachep; + + mutable S32 mRenderGlyphCount; + mutable S32 mAddGlyphCount; +}; + +#endif // LL_FONTFREETYPE_H diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index 3a408b5550..386bb987f9 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -2,43 +2,46 @@ * @file llfontgl.cpp * @brief Wrapper around FreeType * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2007, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlife.com/developers/opensource/gplv2 + * Copyright (C) 2010, Linden Research, Inc. + * + * 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; + * version 2.1 of the License only. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at http://secondlife.com/developers/opensource/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #include "linden_common.h" -#include <boost/tokenizer.hpp> - -#include "llfont.h" #include "llfontgl.h" + +// Linden library includes +#include "llfontfreetype.h" +#include "llfontbitmapcache.h" +#include "llfontregistry.h" #include "llgl.h" -#include "llglimmediate.h" -#include "v4color.h" +#include "llimagegl.h" +#include "llrender.h" #include "llstl.h" +#include "v4color.h" +#include "lltexture.h" +#include "lldir.h" + +// Third party library includes +#include <boost/tokenizer.hpp> const S32 BOLD_OFFSET = 1; @@ -48,603 +51,144 @@ F32 LLFontGL::sHorizDPI = 96.f; F32 LLFontGL::sScaleX = 1.f; F32 LLFontGL::sScaleY = 1.f; BOOL LLFontGL::sDisplayFont = TRUE ; -LLString LLFontGL::sAppDir; - -LLFontGL* LLFontGL::sMonospace = NULL; -LLFontGL* LLFontGL::sSansSerifSmall = NULL; -LLFontGL* LLFontGL::sSansSerif = NULL; -LLFontGL* LLFontGL::sSansSerifBig = NULL; -LLFontGL* LLFontGL::sSansSerifHuge = NULL; -LLFontGL* LLFontGL::sSansSerifBold = NULL; -LLFontList* LLFontGL::sMonospaceFallback = NULL; -LLFontList* LLFontGL::sSSFallback = NULL; -LLFontList* LLFontGL::sSSSmallFallback = NULL; -LLFontList* LLFontGL::sSSBigFallback = NULL; -LLFontList* LLFontGL::sSSHugeFallback = NULL; -LLFontList* LLFontGL::sSSBoldFallback = NULL; +std::string LLFontGL::sAppDir; + LLColor4 LLFontGL::sShadowColor(0.f, 0.f, 0.f, 1.f); +LLFontRegistry* LLFontGL::sFontRegistry = NULL; LLCoordFont LLFontGL::sCurOrigin; std::vector<LLCoordFont> LLFontGL::sOriginStack; -LLFontGL*& gExtCharFont = LLFontGL::sSansSerif; - const F32 EXT_X_BEARING = 1.f; const F32 EXT_Y_BEARING = 0.f; const F32 EXT_KERNING = 1.f; const F32 PIXEL_BORDER_THRESHOLD = 0.0001f; const F32 PIXEL_CORRECTION_DISTANCE = 0.01f; -const F32 PAD_AMT = 0.5f; +const F32 PAD_UVY = 0.5f; // half of vertical padding between glyphs in the glyph texture const F32 DROP_SHADOW_SOFT_STRENGTH = 0.3f; -F32 llfont_round_x(F32 x) +static F32 llfont_round_x(F32 x) { //return llfloor((x-LLFontGL::sCurOrigin.mX)/LLFontGL::sScaleX+0.5f)*LLFontGL::sScaleX+LLFontGL::sCurOrigin.mX; //return llfloor(x/LLFontGL::sScaleX+0.5f)*LLFontGL::sScaleY; return x; } -F32 llfont_round_y(F32 y) +static F32 llfont_round_y(F32 y) { //return llfloor((y-LLFontGL::sCurOrigin.mY)/LLFontGL::sScaleY+0.5f)*LLFontGL::sScaleY+LLFontGL::sCurOrigin.mY; //return llfloor(y+0.5f); return y; } -// static -U8 LLFontGL::getStyleFromString(const LLString &style) -{ - S32 ret = 0; - if (style.find("NORMAL") != style.npos) - { - ret |= NORMAL; - } - if (style.find("BOLD") != style.npos) - { - ret |= BOLD; - } - if (style.find("ITALIC") != style.npos) - { - ret |= ITALIC; - } - if (style.find("UNDERLINE") != style.npos) - { - ret |= UNDERLINE; - } - if (style.find("SHADOW") != style.npos) - { - ret |= DROP_SHADOW; - } - if (style.find("SOFT_SHADOW") != style.npos) - { - ret |= DROP_SHADOW_SOFT; - } - return ret; -} - LLFontGL::LLFontGL() - : LLFont() { - init(); - clearEmbeddedChars(); -} - -LLFontGL::LLFontGL(const LLFontGL &source) -{ - llerrs << "Not implemented!" << llendl; } LLFontGL::~LLFontGL() { - mImageGLp = NULL; - mRawImageGLp = NULL; - clearEmbeddedChars(); -} - -void LLFontGL::init() -{ - if (mImageGLp.isNull()) - { - mImageGLp = new LLImageGL(FALSE); - //RN: use nearest mipmap filtering to obviate the need to do pixel-accurate positioning - mImageGLp->bind(); - // we allow bilinear filtering to get sub-pixel positioning for drop shadows - //mImageGLp->setMipFilterNearest(TRUE, TRUE); - } - if (mRawImageGLp.isNull()) - { - mRawImageGLp = new LLImageRaw; // Note LLFontGL owns the image, not LLFont. - } - setRawImage( mRawImageGLp ); } void LLFontGL::reset() { - init(); - resetBitmap(); + mFontFreetype->reset(sVertDPI, sHorizDPI); } -// static -LLString LLFontGL::getFontPathSystem() -{ - LLString system_path; - - // Try to figure out where the system's font files are stored. - char *system_root = NULL; -#if LL_WINDOWS - system_root = getenv("SystemRoot"); /* Flawfinder: ignore */ - if (!system_root) - { - llwarns << "SystemRoot not found, attempting to load fonts from default path." << llendl; - } -#endif - - if (system_root) - { - system_path = llformat("%s/fonts/", system_root); - } - else - { -#if LL_WINDOWS - // HACK for windows 98/Me - system_path = "/WINDOWS/FONTS/"; -#elif LL_DARWIN - // HACK for Mac OS X - system_path = "/System/Library/Fonts/"; -#endif - } - return system_path; -} - - -// static -LLString LLFontGL::getFontPathLocal() -{ - LLString local_path; - - // Backup files if we can't load from system fonts directory. - // We could store this in an end-user writable directory to allow - // end users to switch fonts. - if (LLFontGL::sAppDir.length()) - { - // use specified application dir to look for fonts - local_path = LLFontGL::sAppDir + "/fonts/"; - } - else - { - // assume working directory is executable directory - local_path = "./fonts/"; - } - return local_path; -} - -//static -bool LLFontGL::loadFaceFallback(LLFontList *fontlistp, const LLString& fontname, const F32 point_size) +void LLFontGL::destroyGL() { - LLString local_path = getFontPathLocal(); - LLString sys_path = getFontPathSystem(); - - // The fontname string may contain multiple font file names separated by semicolons. - // Break it apart and try loading each one, in order. - typedef boost::tokenizer<boost::char_separator<char> > tokenizer; - boost::char_separator<char> sep(";"); - tokenizer tokens(fontname, sep); - tokenizer::iterator token_iter; - - for(token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter) - { - LLFont *fontp = new LLFont(); - LLString font_path = local_path + *token_iter; - if (!fontp->loadFace(font_path, point_size, sVertDPI, sHorizDPI, 2, TRUE)) - { - font_path = sys_path + *token_iter; - if (!fontp->loadFace(font_path, point_size, sVertDPI, sHorizDPI, 2, TRUE)) - { - LL_INFOS_ONCE("ViewerImages") << "Couldn't load font " << *token_iter << LL_ENDL; - delete fontp; - fontp = NULL; - } - } - - if(fontp) - { - fontlistp->addAtEnd(fontp); - } - } - - // We want to return true if at least one fallback font loaded correctly. - return (fontlistp->size() > 0); + mFontFreetype->destroyGL(); } -//static -bool LLFontGL::loadFace(LLFontGL *fontp, const LLString& fontname, const F32 point_size, LLFontList *fallback_fontp) +BOOL LLFontGL::loadFace(const std::string& filename, F32 point_size, F32 vert_dpi, F32 horz_dpi, S32 components, BOOL is_fallback) { - LLString local_path = getFontPathLocal(); - LLString font_path = local_path + fontname; - if (!fontp->loadFace(font_path, point_size, sVertDPI, sHorizDPI, 2, FALSE)) + if(mFontFreetype == reinterpret_cast<LLFontFreetype*>(NULL)) { - LLString sys_path = getFontPathSystem(); - font_path = sys_path + fontname; - if (!fontp->loadFace(font_path, point_size, sVertDPI, sHorizDPI, 2, FALSE)) - { - LL_WARNS("ViewerImages") << "Couldn't load font " << fontname << LL_ENDL; - return false; - } + mFontFreetype = new LLFontFreetype; } - fontp->setFallbackFont(fallback_fontp); - return true; + return mFontFreetype->loadFace(filename, point_size, vert_dpi, horz_dpi, components, is_fallback); } +static LLFastTimer::DeclareTimer FTM_RENDER_FONTS("Fonts"); -// static -BOOL LLFontGL::initDefaultFonts(F32 screen_dpi, F32 x_scale, F32 y_scale, - const LLString& monospace_file, F32 monospace_size, - const LLString& sansserif_file, - const LLString& sanserif_fallback_file, F32 ss_fallback_scale, - F32 small_size, F32 medium_size, F32 big_size, F32 huge_size, - const LLString& sansserif_bold_file, F32 bold_size, - const LLString& app_dir) +S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, const LLRect& rect, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, + ShadowType shadow, S32 max_chars, F32* right_x, BOOL use_ellipses) const { - BOOL failed = FALSE; - sVertDPI = (F32)llfloor(screen_dpi * y_scale); - sHorizDPI = (F32)llfloor(screen_dpi * x_scale); - sScaleX = x_scale; - sScaleY = y_scale; - sAppDir = app_dir; - - // - // Monospace font - // - - if (!sMonospace) - { - sMonospace = new LLFontGL(); - } - else - { - sMonospace->reset(); - } - - if (sMonospaceFallback) - { - delete sMonospaceFallback; - } - sMonospaceFallback = new LLFontList(); - if (!loadFaceFallback( - sMonospaceFallback, - sanserif_fallback_file, - monospace_size * ss_fallback_scale)) - { - delete sMonospaceFallback; - sMonospaceFallback = NULL; - } - - failed |= !loadFace(sMonospace, monospace_file, monospace_size, sMonospaceFallback); - - // - // Sans-serif fonts - // - if(!sSansSerifHuge) - { - sSansSerifHuge = new LLFontGL(); - } - else - { - sSansSerifHuge->reset(); - } - - if (sSSHugeFallback) - { - delete sSSHugeFallback; - } - sSSHugeFallback = new LLFontList(); - if (!loadFaceFallback( - sSSHugeFallback, - sanserif_fallback_file, - huge_size*ss_fallback_scale)) - { - delete sSSHugeFallback; - sSSHugeFallback = NULL; - } - - failed |= !loadFace(sSansSerifHuge, sansserif_file, huge_size, sSSHugeFallback); - - - if(!sSansSerifBig) - { - sSansSerifBig = new LLFontGL(); - } - else - { - sSansSerifBig->reset(); - } - - if (sSSBigFallback) - { - delete sSSBigFallback; - } - sSSBigFallback = new LLFontList(); - if (!loadFaceFallback( - sSSBigFallback, - sanserif_fallback_file, - big_size*ss_fallback_scale)) - { - delete sSSBigFallback; - sSSBigFallback = NULL; - } - - failed |= !loadFace(sSansSerifBig, sansserif_file, big_size, sSSBigFallback); - - - if(!sSansSerif) - { - sSansSerif = new LLFontGL(); - } - else - { - sSansSerif->reset(); - } - - if (sSSFallback) - { - delete sSSFallback; - } - sSSFallback = new LLFontList(); - if (!loadFaceFallback( - sSSFallback, - sanserif_fallback_file, - medium_size*ss_fallback_scale)) - { - delete sSSFallback; - sSSFallback = NULL; - } - failed |= !loadFace(sSansSerif, sansserif_file, medium_size, sSSFallback); - - - if(!sSansSerifSmall) - { - sSansSerifSmall = new LLFontGL(); - } - else - { - sSansSerifSmall->reset(); - } + F32 x = rect.mLeft; + F32 y = 0.f; - if(sSSSmallFallback) + switch(valign) { - delete sSSSmallFallback; - } - sSSSmallFallback = new LLFontList(); - if (!loadFaceFallback( - sSSSmallFallback, - sanserif_fallback_file, - small_size*ss_fallback_scale)) - { - delete sSSSmallFallback; - sSSSmallFallback = NULL; - } - failed |= !loadFace(sSansSerifSmall, sansserif_file, small_size, sSSSmallFallback); - - - // - // Sans-serif bold - // - if(!sSansSerifBold) - { - sSansSerifBold = new LLFontGL(); - } - else - { - sSansSerifBold->reset(); - } - - if (sSSBoldFallback) - { - delete sSSBoldFallback; - } - sSSBoldFallback = new LLFontList(); - if (!loadFaceFallback( - sSSBoldFallback, - sanserif_fallback_file, - medium_size*ss_fallback_scale)) - { - delete sSSBoldFallback; - sSSBoldFallback = NULL; - } - failed |= !loadFace(sSansSerifBold, sansserif_bold_file, medium_size, sSSBoldFallback); - - return !failed; -} - - - -// static -void LLFontGL::destroyDefaultFonts() -{ - delete sMonospace; - sMonospace = NULL; - - delete sSansSerifHuge; - sSansSerifHuge = NULL; - - delete sSansSerifBig; - sSansSerifBig = NULL; - - delete sSansSerif; - sSansSerif = NULL; - - delete sSansSerifSmall; - sSansSerifSmall = NULL; - - delete sSansSerifBold; - sSansSerifBold = NULL; - - delete sMonospaceFallback; - sMonospaceFallback = NULL; - - delete sSSHugeFallback; - sSSHugeFallback = NULL; - - delete sSSBigFallback; - sSSBigFallback = NULL; - - delete sSSFallback; - sSSFallback = NULL; - - delete sSSSmallFallback; - sSSSmallFallback = NULL; - - delete sSSBoldFallback; - sSSBoldFallback = NULL; -} - -//static -void LLFontGL::destroyGL() -{ - if (!sMonospace) - { - // Already all destroyed. - return; - } - sMonospace->mImageGLp->destroyGLTexture(); - sSansSerifHuge->mImageGLp->destroyGLTexture(); - sSansSerifSmall->mImageGLp->destroyGLTexture(); - sSansSerif->mImageGLp->destroyGLTexture(); - sSansSerifBig->mImageGLp->destroyGLTexture(); - sSansSerifBold->mImageGLp->destroyGLTexture(); -} - - - -LLFontGL &LLFontGL::operator=(const LLFontGL &source) -{ - llerrs << "Not implemented" << llendl; - return *this; -} - -BOOL LLFontGL::loadFace(const std::string& filename, - const F32 point_size, const F32 vert_dpi, const F32 horz_dpi, - const S32 components, BOOL is_fallback) -{ - if (!LLFont::loadFace(filename, point_size, vert_dpi, horz_dpi, components, is_fallback)) - { - return FALSE; - } - mImageGLp->createGLTexture(0, mRawImageGLp); - mImageGLp->bind(); - mImageGLp->setMipFilterNearest(TRUE, TRUE); - return TRUE; -} - -BOOL LLFontGL::addChar(const llwchar wch) -{ - if (!LLFont::addChar(wch)) - { - return FALSE; + case TOP: + y = rect.mTop; + break; + case VCENTER: + y = rect.getCenterY(); + break; + case BASELINE: + case BOTTOM: + y = rect.mBottom; + break; + default: + y = rect.mBottom; + break; } - - stop_glerror(); - mImageGLp->setSubImage(mRawImageGLp, 0, 0, mImageGLp->getWidth(), mImageGLp->getHeight()); - mImageGLp->bind(); - mImageGLp->setMipFilterNearest(TRUE, TRUE); - stop_glerror(); - return TRUE; + return render(wstr, begin_offset, x, y, color, halign, valign, style, shadow, max_chars, rect.getWidth(), right_x, use_ellipses); } -S32 LLFontGL::renderUTF8(const LLString &text, const S32 offset, - const F32 x, const F32 y, - const LLColor4 &color, - const HAlign halign, const VAlign valign, - U8 style, - const S32 max_chars, const S32 max_pixels, - F32* right_x, - BOOL use_ellipses) const +S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, + ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, BOOL use_ellipses) const { - LLWString wstr = utf8str_to_wstring(text); - return render(wstr, offset, x, y, color, halign, valign, style, max_chars, max_pixels, right_x, use_ellipses); -} + LLFastTimer _(FTM_RENDER_FONTS); -S32 LLFontGL::render(const LLWString &wstr, - const S32 begin_offset, - const F32 x, const F32 y, - const LLColor4 &color, - const HAlign halign, const VAlign valign, - U8 style, - const S32 max_chars, S32 max_pixels, - F32* right_x, - BOOL use_embedded, - BOOL use_ellipses) const -{ if(!sDisplayFont) //do not display texts { return wstr.length() ; } - LLGLEnable tex(GL_TEXTURE_2D); - if (wstr.empty()) { return 0; } + gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); + S32 scaled_max_pixels = max_pixels == S32_MAX ? S32_MAX : llceil((F32)max_pixels * sScaleX); - // HACK for better bolding - if (style & BOLD) - { - if (this == LLFontGL::sSansSerif) - { - return LLFontGL::sSansSerifBold->render( - wstr, begin_offset, - x, y, - color, - halign, valign, - (style & ~BOLD), - max_chars, max_pixels, - right_x, use_embedded); - } - } + // determine which style flags need to be added programmatically by stripping off the + // style bits that are drawn by the underlying Freetype font + U8 style_to_add = (style | mFontDescriptor.getStyle()) & ~mFontFreetype->getStyle(); F32 drop_shadow_strength = 0.f; - if (style & (DROP_SHADOW | DROP_SHADOW_SOFT)) + if (shadow != NO_SHADOW) { F32 luminance; color.calcHSL(NULL, NULL, &luminance); drop_shadow_strength = clamp_rescale(luminance, 0.35f, 0.6f, 0.f, 1.f); if (luminance < 0.35f) { - style = style & ~(DROP_SHADOW | DROP_SHADOW_SOFT); + shadow = NO_SHADOW; } } - gGL.pushMatrix(); - glLoadIdentity(); - gGL.translatef(floorf(sCurOrigin.mX*sScaleX), floorf(sCurOrigin.mY*sScaleY), sCurOrigin.mZ); - //glScalef(sScaleX, sScaleY, 1.0f); + gGL.pushUIMatrix(); + + gGL.loadUIIdentity(); - // avoid half pixels - // RN: if we're going to this trouble, might as well snap to nearest pixel all the time - // but the plan is to get rid of this so that fonts "just work" - //F32 half_pixel_distance = llabs(fmodf(sCurOrigin.mX * sScaleX, 1.f) - 0.5f); - //if (half_pixel_distance < PIXEL_BORDER_THRESHOLD) - //{ - gGL.translatef(PIXEL_CORRECTION_DISTANCE*sScaleX, 0.f, 0.f); - //} - - // this code would just snap to pixel grid, although it seems to introduce more jitter - //F32 pixel_offset_x = llround(sCurOrigin.mX * sScaleX) - (sCurOrigin.mX * sScaleX); - //F32 pixel_offset_y = llround(sCurOrigin.mY * sScaleY) - (sCurOrigin.mY * sScaleY); - //gGL.translatef(-pixel_offset_x, -pixel_offset_y, 0.f); - - // scale back to native pixel size - //glScalef(1.f / sScaleX, 1.f / sScaleY, 1.f); - //glScaled(1.0 / (F64) sScaleX, 1.0 / (F64) sScaleY, 1.0f); - LLFastTimer t(LLFastTimer::FTM_RENDER_FONTS); - - gGL.color4fv( color.mV ); + //gGL.translateUI(floorf(sCurOrigin.mX*sScaleX), floorf(sCurOrigin.mY*sScaleY), sCurOrigin.mZ); + + // this code snaps the text origin to a pixel grid to start with + //F32 pixel_offset_x = llround((F32)sCurOrigin.mX) - (sCurOrigin.mX); + //F32 pixel_offset_y = llround((F32)sCurOrigin.mY) - (sCurOrigin.mY); + //gGL.translateUI(-pixel_offset_x, -pixel_offset_y, 0.f); + + LLVector2 origin(floorf(sCurOrigin.mX*sScaleX), floorf(sCurOrigin.mY*sScaleY)); + // snap the text origin to a pixel grid to start with + origin.mV[VX] -= llround((F32)sCurOrigin.mX) - (sCurOrigin.mX); + origin.mV[VY] -= llround((F32)sCurOrigin.mY) - (sCurOrigin.mY); + S32 chars_drawn = 0; S32 i; @@ -661,26 +205,23 @@ S32 LLFontGL::render(const LLWString &wstr, F32 cur_x, cur_y, cur_render_x, cur_render_y; - // Bind the font texture - - mImageGLp->bind(0); - - gGL.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Not guaranteed to be set correctly + // Not guaranteed to be set correctly + gGL.setSceneBlendType(LLRender::BT_ALPHA); - cur_x = ((F32)x * sScaleX); - cur_y = ((F32)y * sScaleY); + cur_x = ((F32)x * sScaleX) + origin.mV[VX]; + cur_y = ((F32)y * sScaleY) + origin.mV[VY]; // Offset y by vertical alignment. switch (valign) { case TOP: - cur_y -= mAscender; + cur_y -= mFontFreetype->getAscenderHeight(); break; case BOTTOM: - cur_y += mDescender; + cur_y += mFontFreetype->getDescenderHeight(); break; case VCENTER: - cur_y -= ((mAscender - mDescender)/2.f); + cur_y -= (mFontFreetype->getAscenderHeight() - mFontFreetype->getDescenderHeight()) / 2.f; break; case BASELINE: // Baseline, do nothing. @@ -694,214 +235,217 @@ S32 LLFontGL::render(const LLWString &wstr, case LEFT: break; case RIGHT: - cur_x -= llmin(scaled_max_pixels, llround(getWidthF32(wstr.c_str(), 0, length) * sScaleX)); + cur_x -= llmin(scaled_max_pixels, llround(getWidthF32(wstr.c_str(), begin_offset, length) * sScaleX)); break; case HCENTER: - cur_x -= llmin(scaled_max_pixels, llround(getWidthF32(wstr.c_str(), 0, length) * sScaleX)) / 2; + cur_x -= llmin(scaled_max_pixels, llround(getWidthF32(wstr.c_str(), begin_offset, length) * sScaleX)) / 2; break; default: break; } - // Round properly. - //cur_render_y = (F32)llfloor(cur_y/sScaleY + 0.5f)*sScaleY; - //cur_render_x = (F32)llfloor(cur_x/sScaleX + 0.5f)*sScaleX; - cur_render_y = cur_y; cur_render_x = cur_x; - F32 start_x = cur_x; + F32 start_x = llround(cur_x); + + const LLFontBitmapCache* font_bitmap_cache = mFontFreetype->getFontBitmapCache(); - F32 inv_width = 1.f / mImageGLp->getWidth(); - F32 inv_height = 1.f / mImageGLp->getHeight(); + F32 inv_width = 1.f / font_bitmap_cache->getBitmapWidth(); + F32 inv_height = 1.f / font_bitmap_cache->getBitmapHeight(); - const S32 LAST_CHARACTER = LLFont::LAST_CHAR_FULL; + const S32 LAST_CHARACTER = LLFontFreetype::LAST_CHAR_FULL; BOOL draw_ellipses = FALSE; - if (use_ellipses && halign == LEFT) + if (use_ellipses) { // check for too long of a string - if (getWidthF32(wstr.c_str(), 0, max_chars) * sScaleX > scaled_max_pixels) + S32 string_width = llround(getWidthF32(wstr.c_str(), begin_offset, max_chars) * sScaleX); + if (string_width > scaled_max_pixels) { // use four dots for ellipsis width to generate padding - const LLWString dots(utf8str_to_wstring(LLString("...."))); + const LLWString dots(utf8str_to_wstring(std::string("...."))); scaled_max_pixels = llmax(0, scaled_max_pixels - llround(getWidthF32(dots.c_str()))); draw_ellipses = TRUE; } } + const LLFontGlyphInfo* next_glyph = NULL; + + const S32 GLYPH_BATCH_SIZE = 30; + LLVector3 vertices[GLYPH_BATCH_SIZE * 4]; + LLVector2 uvs[GLYPH_BATCH_SIZE * 4]; + LLColor4U colors[GLYPH_BATCH_SIZE * 4]; + + LLColor4U text_color(color); + + S32 bitmap_num = -1; + S32 glyph_count = 0; for (i = begin_offset; i < begin_offset + length; i++) { llwchar wch = wstr[i]; - // Handle embedded characters first, if they're enabled. - // Embedded characters are a hack for notecards - const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL; - if (ext_data) + const LLFontGlyphInfo* fgi = next_glyph; + next_glyph = NULL; + if(!fgi) { - LLImageGL* ext_image = ext_data->mImage; - const LLWString& label = ext_data->mLabel; - - F32 ext_height = (F32)ext_image->getHeight() * sScaleY; - - F32 ext_width = (F32)ext_image->getWidth() * sScaleX; - F32 ext_advance = (EXT_X_BEARING * sScaleX) + ext_width; - - if (!label.empty()) - { - ext_advance += (EXT_X_BEARING + gExtCharFont->getWidthF32( label.c_str() )) * sScaleX; - } + fgi = mFontFreetype->getGlyphInfo(wch); + } + if (!fgi) + { + llerrs << "Missing Glyph Info" << llendl; + break; + } + // Per-glyph bitmap texture. + S32 next_bitmap_num = fgi->mBitmapNum; + if (next_bitmap_num != bitmap_num) + { + bitmap_num = next_bitmap_num; + LLImageGL *font_image = font_bitmap_cache->getImageGL(bitmap_num); + gGL.getTexUnit(0)->bind(font_image); + } + + if ((start_x + scaled_max_pixels) < (cur_x + fgi->mXBearing + fgi->mWidth)) + { + // Not enough room for this character. + break; + } - if (start_x + scaled_max_pixels < cur_x + ext_advance) + // Draw the text at the appropriate location + //Specify vertices and texture coordinates + LLRectf uv_rect((fgi->mXBitmapOffset) * inv_width, + (fgi->mYBitmapOffset + fgi->mHeight + PAD_UVY) * inv_height, + (fgi->mXBitmapOffset + fgi->mWidth) * inv_width, + (fgi->mYBitmapOffset - PAD_UVY) * inv_height); + // snap glyph origin to whole screen pixel + LLRectf screen_rect(llround(cur_render_x + (F32)fgi->mXBearing), + llround(cur_render_y + (F32)fgi->mYBearing), + llround(cur_render_x + (F32)fgi->mXBearing) + (F32)fgi->mWidth, + llround(cur_render_y + (F32)fgi->mYBearing) - (F32)fgi->mHeight); + + if (glyph_count >= GLYPH_BATCH_SIZE) + { + gGL.begin(LLRender::QUADS); { - // Not enough room for this character. - break; + gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4); } + gGL.end(); - ext_image->bind(); - const F32 ext_x = cur_render_x + (EXT_X_BEARING * sScaleX); - const F32 ext_y = cur_render_y + (EXT_Y_BEARING * sScaleY + mAscender - mLineHeight); + glyph_count = 0; + } - LLRectf uv_rect(0.f, 1.f, 1.f, 0.f); - LLRectf screen_rect(ext_x, ext_y + ext_height, ext_x + ext_width, ext_y); - drawGlyph(screen_rect, uv_rect, LLColor4::white, style, drop_shadow_strength); + drawGlyph(glyph_count, vertices, uvs, colors, screen_rect, uv_rect, text_color, style_to_add, shadow, drop_shadow_strength); - if (!label.empty()) - { - gGL.pushMatrix(); - //glLoadIdentity(); - //gGL.translatef(sCurOrigin.mX, sCurOrigin.mY, 0.0f); - //glScalef(sScaleX, sScaleY, 1.f); - gExtCharFont->render(label, 0, - /*llfloor*/((ext_x + (F32)ext_image->getWidth() + EXT_X_BEARING) / sScaleX), - /*llfloor*/(cur_y / sScaleY), - color, - halign, BASELINE, NORMAL, S32_MAX, S32_MAX, NULL, - TRUE ); - gGL.popMatrix(); - } + chars_drawn++; + cur_x += fgi->mXAdvance; + cur_y += fgi->mYAdvance; - gGL.color4fv(color.mV); - - chars_drawn++; - cur_x += ext_advance; - if (((i + 1) < length) && wstr[i+1]) - { - cur_x += EXT_KERNING * sScaleX; - } - cur_render_x = cur_x; - - // Bind the font texture - mImageGLp->bind(); - } - else + llwchar next_char = wstr[i+1]; + if (next_char && (next_char < LAST_CHARACTER)) { - if (!hasGlyph(wch)) - { - (const_cast<LLFontGL*>(this))->addChar(wch); - } - - const LLFontGlyphInfo* fgi= getGlyphInfo(wch); - if (!fgi) - { - llerrs << "Missing Glyph Info" << llendl; - break; - } - if ((start_x + scaled_max_pixels) < (cur_x + fgi->mXBearing + fgi->mWidth)) - { - // Not enough room for this character. - break; - } + // Kern this puppy. + next_glyph = mFontFreetype->getGlyphInfo(next_char); + cur_x += mFontFreetype->getXKerning(fgi, next_glyph); + } - // Draw the text at the appropriate location - //Specify vertices and texture coordinates - LLRectf uv_rect((fgi->mXBitmapOffset - PAD_AMT) * inv_width, - (fgi->mYBitmapOffset + fgi->mHeight + PAD_AMT) * inv_height, - (fgi->mXBitmapOffset + fgi->mWidth + PAD_AMT) * inv_width, - (fgi->mYBitmapOffset - PAD_AMT) * inv_height); - LLRectf screen_rect(cur_render_x + (F32)fgi->mXBearing - PAD_AMT, - cur_render_y + (F32)fgi->mYBearing + PAD_AMT, - cur_render_x + (F32)fgi->mXBearing + (F32)fgi->mWidth + PAD_AMT, - cur_render_y + (F32)fgi->mYBearing - (F32)fgi->mHeight - PAD_AMT); - - drawGlyph(screen_rect, uv_rect, color, style, drop_shadow_strength); - - chars_drawn++; - cur_x += fgi->mXAdvance; - cur_y += fgi->mYAdvance; - - llwchar next_char = wstr[i+1]; - if (next_char && (next_char < LAST_CHARACTER)) - { - // Kern this puppy. - if (!hasGlyph(next_char)) - { - (const_cast<LLFontGL*>(this))->addChar(next_char); - } - cur_x += getXKerning(wch, next_char); - } + // Round after kerning. + // Must do this to cur_x, not just to cur_render_x, otherwise you + // will squish sub-pixel kerned characters too close together. + // For example, "CCCCC" looks bad. + cur_x = (F32)llround(cur_x); + //cur_y = (F32)llround(cur_y); - // Round after kerning. - // Must do this to cur_x, not just to cur_render_x, otherwise you - // will squish sub-pixel kerned characters too close together. - // For example, "CCCCC" looks bad. - cur_x = (F32)llfloor(cur_x + 0.5f); - //cur_y = (F32)llfloor(cur_y + 0.5f); + cur_render_x = cur_x; + cur_render_y = cur_y; + } - cur_render_x = cur_x; - cur_render_y = cur_y; - } + gGL.begin(LLRender::QUADS); + { + gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4); } + gGL.end(); + if (right_x) { - *right_x = cur_x / sScaleX; + *right_x = (cur_x - origin.mV[VX]) / sScaleX; } - if (style & UNDERLINE) + //FIXME: add underline as glyph? + if (style_to_add & UNDERLINE) { - LLGLSNoTexture no_texture; - gGL.begin(LLVertexBuffer::LINES); - gGL.vertex2f(start_x, cur_y - (mDescender)); - gGL.vertex2f(cur_x, cur_y - (mDescender)); + F32 descender = mFontFreetype->getDescenderHeight(); + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + gGL.begin(LLRender::LINES); + gGL.vertex2f(start_x, cur_y - (descender)); + gGL.vertex2f(cur_x, cur_y - (descender)); gGL.end(); } - // *FIX: get this working in all alignment cases, etc. if (draw_ellipses) { + // recursively render ellipses at end of string // we've already reserved enough room - gGL.pushMatrix(); - //glLoadIdentity(); - //gGL.translatef(sCurOrigin.mX, sCurOrigin.mY, 0.0f); - //glScalef(sScaleX, sScaleY, 1.f); - renderUTF8("...", + gGL.pushUIMatrix(); + renderUTF8(std::string("..."), 0, - cur_x / sScaleX, (F32)y, + (cur_x - origin.mV[VX]) / sScaleX, (F32)y, color, LEFT, valign, - style, + style_to_add, + shadow, S32_MAX, max_pixels, right_x, FALSE); - gGL.popMatrix(); + gGL.popUIMatrix(); } - gGL.popMatrix(); + gGL.popUIMatrix(); return chars_drawn; } - -LLImageGL *LLFontGL::getImageGL() const +S32 LLFontGL::render(const LLWString &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color) const +{ + return render(text, begin_offset, x, y, color, LEFT, BASELINE, NORMAL, NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE); +} + +S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, BOOL use_ellipses) const +{ + return render(utf8str_to_wstring(text), begin_offset, x, y, color, halign, valign, style, shadow, max_chars, max_pixels, right_x, use_ellipses); +} + +S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y, const LLColor4 &color) const { - return mImageGLp; + return renderUTF8(text, begin_offset, (F32)x, (F32)y, color, LEFT, BASELINE, NORMAL, NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE); } -S32 LLFontGL::getWidth(const LLString& utf8text) const +S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, ShadowType shadow) const +{ + return renderUTF8(text, begin_offset, (F32)x, (F32)y, color, halign, valign, style, shadow, S32_MAX, S32_MAX, NULL, FALSE); +} + +// font metrics - override for LLFontFreetype that returns units of virtual pixels +F32 LLFontGL::getLineHeight() const +{ + return (F32)llround(mFontFreetype->getLineHeight() / sScaleY); +} + +F32 LLFontGL::getAscenderHeight() const +{ + return (F32)llround(mFontFreetype->getAscenderHeight() / sScaleY); +} + +F32 LLFontGL::getDescenderHeight() const +{ + return (F32)llround(mFontFreetype->getDescenderHeight() / sScaleY); +} + +S32 LLFontGL::getWidth(const std::string& utf8text) const { LLWString wtext = utf8str_to_wstring(utf8text); return getWidth(wtext.c_str(), 0, S32_MAX); @@ -912,19 +456,19 @@ S32 LLFontGL::getWidth(const llwchar* wchars) const return getWidth(wchars, 0, S32_MAX); } -S32 LLFontGL::getWidth(const LLString& utf8text, const S32 begin_offset, const S32 max_chars) const +S32 LLFontGL::getWidth(const std::string& utf8text, S32 begin_offset, S32 max_chars) const { LLWString wtext = utf8str_to_wstring(utf8text); return getWidth(wtext.c_str(), begin_offset, max_chars); } -S32 LLFontGL::getWidth(const llwchar* wchars, const S32 begin_offset, const S32 max_chars, BOOL use_embedded) const +S32 LLFontGL::getWidth(const llwchar* wchars, S32 begin_offset, S32 max_chars) const { - F32 width = getWidthF32(wchars, begin_offset, max_chars, use_embedded); + F32 width = getWidthF32(wchars, begin_offset, max_chars); return llround(width); } -F32 LLFontGL::getWidthF32(const LLString& utf8text) const +F32 LLFontGL::getWidthF32(const std::string& utf8text) const { LLWString wtext = utf8str_to_wstring(utf8text); return getWidthF32(wtext.c_str(), 0, S32_MAX); @@ -935,62 +479,65 @@ F32 LLFontGL::getWidthF32(const llwchar* wchars) const return getWidthF32(wchars, 0, S32_MAX); } -F32 LLFontGL::getWidthF32(const LLString& utf8text, const S32 begin_offset, const S32 max_chars ) const +F32 LLFontGL::getWidthF32(const std::string& utf8text, S32 begin_offset, S32 max_chars ) const { LLWString wtext = utf8str_to_wstring(utf8text); return getWidthF32(wtext.c_str(), begin_offset, max_chars); } -F32 LLFontGL::getWidthF32(const llwchar* wchars, const S32 begin_offset, const S32 max_chars, BOOL use_embedded) const +F32 LLFontGL::getWidthF32(const llwchar* wchars, S32 begin_offset, S32 max_chars) const { - const S32 LAST_CHARACTER = LLFont::LAST_CHAR_FULL; + const S32 LAST_CHARACTER = LLFontFreetype::LAST_CHAR_FULL; F32 cur_x = 0; const S32 max_index = begin_offset + max_chars; - for (S32 i = begin_offset; i < max_index; i++) + + const LLFontGlyphInfo* next_glyph = NULL; + + F32 width_padding = 0.f; + for (S32 i = begin_offset; i < max_index && wchars[i] != 0; i++) { - const llwchar wch = wchars[i]; - if (wch == 0) + llwchar wch = wchars[i]; + + const LLFontGlyphInfo* fgi = next_glyph; + next_glyph = NULL; + if(!fgi) { - break; // done + fgi = mFontFreetype->getGlyphInfo(wch); } - const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL; - if (ext_data) - { - // Handle crappy embedded hack - cur_x += getEmbeddedCharAdvance(ext_data); - if( ((i+1) < max_chars) && (i+1 < max_index)) - { - cur_x += EXT_KERNING * sScaleX; - } - } - else - { - cur_x += getXAdvance(wch); - llwchar next_char = wchars[i+1]; + F32 advance = mFontFreetype->getXAdvance(fgi); - if (((i + 1) < max_chars) - && next_char - && (next_char < LAST_CHARACTER)) - { - // Kern this puppy. - cur_x += getXKerning(wch, next_char); - } + // for the last character we want to measure the greater of its width and xadvance values + // so keep track of the difference between these values for the each character we measure + // so we can fix things up at the end + width_padding = llmax( 0.f, // always use positive padding amount + width_padding - advance, // previous padding left over after advance of current character + (F32)(fgi->mWidth + fgi->mXBearing) - advance); // difference between width of this character and advance to next character + + cur_x += advance; + llwchar next_char = wchars[i+1]; + + if (((i + 1) < begin_offset + max_chars) + && next_char + && (next_char < LAST_CHARACTER)) + { + // Kern this puppy. + next_glyph = mFontFreetype->getGlyphInfo(next_char); + cur_x += mFontFreetype->getXKerning(fgi, next_glyph); } // Round after kerning. - cur_x = (F32)llfloor(cur_x + 0.5f); + cur_x = (F32)llround(cur_x); } + // add in extra pixels for last character's width past its xadvance + cur_x += width_padding; + return cur_x / sScaleX; } - - // Returns the max number of complete characters from text (up to max_chars) that can be drawn in max_pixels -S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_chars, - BOOL end_on_word_boundary, const BOOL use_embedded, - F32* drawn_pixels) const +S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_chars, EWordWrapStyle end_on_word_boundary) const { if (!wchars || !wchars[0] || max_chars == 0) { @@ -1007,7 +554,11 @@ S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_ch S32 start_of_last_word = 0; BOOL in_word = FALSE; - F32 scaled_max_pixels = (F32)llceil(max_pixels * sScaleX); + // avoid S32 overflow when max_pixels == S32_MAX by staying in floating point + F32 scaled_max_pixels = ceil(max_pixels * sScaleX); + F32 width_padding = 0.f; + + LLFontGlyphInfo* next_glyph = NULL; S32 i; for (i=0; (i < max_chars); i++) @@ -1020,85 +571,92 @@ S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_ch break; } - const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL; - if (ext_data) + if (in_word) { - if (in_word) - { - in_word = FALSE; - } - else + if (iswspace(wch)) { - start_of_last_word = i; - } - cur_x += getEmbeddedCharAdvance(ext_data); - - if (scaled_max_pixels < cur_x) - { - clip = TRUE; - break; - } - - if (((i+1) < max_chars) && wchars[i+1]) - { - cur_x += EXT_KERNING * sScaleX; - } - - if( scaled_max_pixels < cur_x ) - { - clip = TRUE; - break; - } - } - else - { - if (in_word) - { - if (iswspace(wch)) + if(wch !=(0x00A0)) { in_word = FALSE; } } - else + if (iswindividual(wch)) { - start_of_last_word = i; - if (!iswspace(wch)) + if (iswpunct(wchars[i+1])) + { + in_word=TRUE; + } + else { - in_word = TRUE; + in_word=FALSE; + start_of_last_word = i; } } - - cur_x += getXAdvance(wch); - - if (scaled_max_pixels < cur_x) + } + else + { + start_of_last_word = i; + if (!iswspace(wch)||!iswindividual(wch)) { - clip = TRUE; - break; + in_word = TRUE; } + } + + LLFontGlyphInfo* fgi = next_glyph; + next_glyph = NULL; + if(!fgi) + { + fgi = mFontFreetype->getGlyphInfo(wch); + } - if (((i+1) < max_chars) && wchars[i+1]) - { - // Kern this puppy. - cur_x += getXKerning(wch, wchars[i+1]); - } + // account for glyphs that run beyond the starting point for the next glyphs + width_padding = llmax( 0.f, // always use positive padding amount + width_padding - fgi->mXAdvance, // previous padding left over after advance of current character + (F32)(fgi->mWidth + fgi->mXBearing) - fgi->mXAdvance); // difference between width of this character and advance to next character + + cur_x += fgi->mXAdvance; + + // clip if current character runs past scaled_max_pixels (using width_padding) + if (scaled_max_pixels < cur_x + width_padding) + { + clip = TRUE; + break; + } + + if (((i+1) < max_chars) && wchars[i+1]) + { + // Kern this puppy. + next_glyph = mFontFreetype->getGlyphInfo(wchars[i+1]); + cur_x += mFontFreetype->getXKerning(fgi, next_glyph); } + // Round after kerning. - cur_x = (F32)llfloor(cur_x + 0.5f); + cur_x = llround(cur_x); drawn_x = cur_x; } - if( clip && end_on_word_boundary && (start_of_last_word != 0) ) - { - i = start_of_last_word; - } - if (drawn_pixels) + if( clip ) { - *drawn_pixels = drawn_x; + switch (end_on_word_boundary) + { + case ONLY_WORD_BOUNDARIES: + i = start_of_last_word; + break; + case WORD_BOUNDARY_IF_POSSIBLE: + if (start_of_last_word != 0) + { + i = start_of_last_word; + } + break; + default: + case ANYWHERE: + // do nothing + break; + } } return i; } - S32 LLFontGL::firstDrawableChar(const llwchar* wchars, F32 max_pixels, S32 text_len, S32 start_pos, S32 max_chars) const { if (!wchars || !wchars[0] || max_chars == 0) @@ -1116,64 +674,51 @@ S32 LLFontGL::firstDrawableChar(const llwchar* wchars, F32 max_pixels, S32 text_ { llwchar wch = wchars[i]; - const embedded_data_t* ext_data = getEmbeddedCharData(wch); - if (ext_data) - { - F32 char_width = getEmbeddedCharAdvance(ext_data); - - if( scaled_max_pixels < (total_width + char_width) ) - { - break; - } - - total_width += char_width; - - drawable_chars++; - if( max_chars >= 0 && drawable_chars >= max_chars ) - { - break; - } + const LLFontGlyphInfo* fgi= mFontFreetype->getGlyphInfo(wch); - if ( i > 0 ) - { - total_width += EXT_KERNING * sScaleX; - } + // last character uses character width, since the whole character needs to be visible + // other characters just use advance + F32 width = (i == start) + ? (F32)(fgi->mWidth + fgi->mXBearing) // use actual width for last character + : fgi->mXAdvance; // use advance for all other characters - // Round after kerning. - total_width = (F32)llfloor(total_width + 0.5f); - } - else + if( scaled_max_pixels < (total_width + width) ) { - F32 char_width = getXAdvance(wch); - if( scaled_max_pixels < (total_width + char_width) ) - { - break; - } - - total_width += char_width; + break; + } - drawable_chars++; - if( max_chars >= 0 && drawable_chars >= max_chars ) - { - break; - } + total_width += width; + drawable_chars++; - if ( i > 0 ) - { - // Kerning - total_width += getXKerning(wchars[i-1], wch); - } + if( max_chars >= 0 && drawable_chars >= max_chars ) + { + break; + } - // Round after kerning. - total_width = (F32)llfloor(total_width + 0.5f); + if ( i > 0 ) + { + // kerning + total_width += mFontFreetype->getXKerning(wchars[i-1], wch); } + + // Round after kerning. + total_width = llround(total_width); } - return text_len - drawable_chars; + if (drawable_chars == 0) + { + return start_pos; // just draw last character + } + else + { + // if only 1 character is drawable, we want to return start_pos as the first character to draw + // if 2 are drawable, return start_pos and character before start_pos, etc. + return start_pos + 1 - drawable_chars; + } + } - -S32 LLFontGL::charFromPixelOffset(const llwchar* wchars, const S32 begin_offset, F32 target_x, F32 max_pixels, S32 max_chars, BOOL round, BOOL use_embedded) const +S32 LLFontGL::charFromPixelOffset(const llwchar* wchars, S32 begin_offset, F32 target_x, F32 max_pixels, S32 max_chars, BOOL round) const { if (!wchars || !wchars[0] || max_chars == 0) { @@ -1181,327 +726,202 @@ S32 LLFontGL::charFromPixelOffset(const llwchar* wchars, const S32 begin_offset, } F32 cur_x = 0; - S32 pos = 0; target_x *= sScaleX; // max_chars is S32_MAX by default, so make sure we don't get overflow - const S32 max_index = begin_offset + llmin(S32_MAX - begin_offset, max_chars); + const S32 max_index = begin_offset + llmin(S32_MAX - begin_offset, max_chars - 1); F32 scaled_max_pixels = max_pixels * sScaleX; + + const LLFontGlyphInfo* next_glyph = NULL; - for (S32 i = begin_offset; (i < max_index); i++) + S32 pos; + for (pos = begin_offset; pos < max_index; pos++) { - llwchar wch = wchars[i]; + llwchar wch = wchars[pos]; if (!wch) { break; // done } - const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL; - if (ext_data) + + const LLFontGlyphInfo* glyph = next_glyph; + next_glyph = NULL; + if(!glyph) { - F32 ext_advance = getEmbeddedCharAdvance(ext_data); - - if (round) - { - // Note: if the mouse is on the left half of the character, the pick is to the character's left - // If it's on the right half, the pick is to the right. - if (target_x < cur_x + ext_advance/2) - { - break; - } - } - else - { - if (target_x < cur_x + ext_advance) - { - break; - } - } + glyph = mFontFreetype->getGlyphInfo(wch); + } + + F32 char_width = mFontFreetype->getXAdvance(glyph); - if (scaled_max_pixels < cur_x + ext_advance) + if (round) + { + // Note: if the mouse is on the left half of the character, the pick is to the character's left + // If it's on the right half, the pick is to the right. + if (target_x < cur_x + char_width*0.5f) { break; } - - pos++; - cur_x += ext_advance; - - if (((i + 1) < max_index) - && (wchars[(i + 1)])) - { - cur_x += EXT_KERNING * sScaleX; - } - // Round after kerning. - cur_x = (F32)llfloor(cur_x + 0.5f); } - else + else if (target_x < cur_x + char_width) { - F32 char_width = getXAdvance(wch); + break; + } - if (round) - { - // Note: if the mouse is on the left half of the character, the pick is to the character's left - // If it's on the right half, the pick is to the right. - if (target_x < cur_x + char_width*0.5f) - { - break; - } - } - else if (target_x < cur_x + char_width) - { - break; - } + if (scaled_max_pixels < cur_x + char_width) + { + break; + } - if (scaled_max_pixels < cur_x + char_width) - { - break; - } + cur_x += char_width; - pos++; - cur_x += char_width; + if (((pos + 1) < max_index) + && (wchars[(pos + 1)])) + { + // Kern this puppy. + next_glyph = mFontFreetype->getGlyphInfo(wchars[pos + 1]); + cur_x += mFontFreetype->getXKerning(glyph, next_glyph); + } - if (((i + 1) < max_index) - && (wchars[(i + 1)])) - { - llwchar next_char = wchars[i + 1]; - // Kern this puppy. - cur_x += getXKerning(wch, next_char); - } - // Round after kerning. - cur_x = (F32)llfloor(cur_x + 0.5f); - } + // Round after kerning. + cur_x = llround(cur_x); } - return pos; + return llmin(max_chars, pos - begin_offset); } - -const LLFontGL::embedded_data_t* LLFontGL::getEmbeddedCharData(const llwchar wch) const +const LLFontDescriptor& LLFontGL::getFontDesc() const { - // Handle crappy embedded hack - embedded_map_t::const_iterator iter = mEmbeddedChars.find(wch); - if (iter != mEmbeddedChars.end()) - { - return iter->second; - } - return NULL; + return mFontDescriptor; } - -F32 LLFontGL::getEmbeddedCharAdvance(const embedded_data_t* ext_data) const +// static +void LLFontGL::initClass(F32 screen_dpi, F32 x_scale, F32 y_scale, const std::string& app_dir, const std::vector<std::string>& xui_paths, bool create_gl_textures) { - const LLWString& label = ext_data->mLabel; - LLImageGL* ext_image = ext_data->mImage; + sVertDPI = (F32)llfloor(screen_dpi * y_scale); + sHorizDPI = (F32)llfloor(screen_dpi * x_scale); + sScaleX = x_scale; + sScaleY = y_scale; + sAppDir = app_dir; - F32 ext_width = (F32)ext_image->getWidth(); - if( !label.empty() ) + // Font registry init + if (!sFontRegistry) { - ext_width += (EXT_X_BEARING + gExtCharFont->getWidthF32(label.c_str())) * sScaleX; + sFontRegistry = new LLFontRegistry(xui_paths, create_gl_textures); + sFontRegistry->parseFontInfo("fonts.xml"); } - - return (EXT_X_BEARING * sScaleX) + ext_width; -} - - -void LLFontGL::clearEmbeddedChars() -{ - for_each(mEmbeddedChars.begin(), mEmbeddedChars.end(), DeletePairedPointer()); - mEmbeddedChars.clear(); -} - -void LLFontGL::addEmbeddedChar( llwchar wc, LLImageGL* image, const LLString& label ) -{ - LLWString wlabel = utf8str_to_wstring(label); - addEmbeddedChar(wc, image, wlabel); -} - -void LLFontGL::addEmbeddedChar( llwchar wc, LLImageGL* image, const LLWString& wlabel ) -{ - embedded_data_t* ext_data = new embedded_data_t(image, wlabel); - mEmbeddedChars[wc] = ext_data; -} - -void LLFontGL::removeEmbeddedChar( llwchar wc ) -{ - embedded_map_t::iterator iter = mEmbeddedChars.find(wc); - if (iter != mEmbeddedChars.end()) + else { - delete iter->second; - mEmbeddedChars.erase(wc); + sFontRegistry->reset(); } } - -void LLFontGL::renderQuad(const LLRectf& screen_rect, const LLRectf& uv_rect, F32 slant_amt) const +// Force standard fonts to get generated up front. +// This is primarily for error detection purposes. +// Don't do this during initClass because it can be slow and we want to get +// the viewer window on screen first. JC +// static +bool LLFontGL::loadDefaultFonts() { - gGL.texCoord2f(uv_rect.mRight, uv_rect.mTop); - gGL.vertex2f(llfont_round_x(screen_rect.mRight), - llfont_round_y(screen_rect.mTop)); - - gGL.texCoord2f(uv_rect.mLeft, uv_rect.mTop); - gGL.vertex2f(llfont_round_x(screen_rect.mLeft), - llfont_round_y(screen_rect.mTop)); - - gGL.texCoord2f(uv_rect.mLeft, uv_rect.mBottom); - gGL.vertex2f(llfont_round_x(screen_rect.mLeft + slant_amt), - llfont_round_y(screen_rect.mBottom)); - - gGL.texCoord2f(uv_rect.mRight, uv_rect.mBottom); - gGL.vertex2f(llfont_round_x(screen_rect.mRight + slant_amt), - llfont_round_y(screen_rect.mBottom)); + bool succ = true; + succ &= (NULL != getFontSansSerifSmall()); + succ &= (NULL != getFontSansSerif()); + succ &= (NULL != getFontSansSerifBig()); + succ &= (NULL != getFontSansSerifHuge()); + succ &= (NULL != getFontSansSerifBold()); + succ &= (NULL != getFontMonospace()); + succ &= (NULL != getFontExtChar()); + return succ; } -void LLFontGL::drawGlyph(const LLRectf& screen_rect, const LLRectf& uv_rect, const LLColor4& color, U8 style, F32 drop_shadow_strength) const +// static +void LLFontGL::destroyDefaultFonts() { - F32 slant_offset; - slant_offset = ((style & ITALIC) ? ( -mAscender * 0.2f) : 0.f); + // Remove the actual fonts. + delete sFontRegistry; + sFontRegistry = NULL; +} - gGL.begin(LLVertexBuffer::QUADS); +//static +void LLFontGL::destroyAllGL() +{ + if (sFontRegistry) { - //FIXME: bold and drop shadow are mutually exclusive only for convenience - //Allow both when we need them. - if (style & BOLD) - { - gGL.color4fv(color.mV); - for (S32 pass = 0; pass < 2; pass++) - { - LLRectf screen_rect_offset = screen_rect; - - screen_rect_offset.translate((F32)(pass * BOLD_OFFSET), 0.f); - renderQuad(screen_rect_offset, uv_rect, slant_offset); - } - } - else if (style & DROP_SHADOW_SOFT) - { - LLColor4 shadow_color = LLFontGL::sShadowColor; - shadow_color.mV[VALPHA] = color.mV[VALPHA] * drop_shadow_strength * DROP_SHADOW_SOFT_STRENGTH; - gGL.color4fv(shadow_color.mV); - for (S32 pass = 0; pass < 5; pass++) - { - LLRectf screen_rect_offset = screen_rect; - - switch(pass) - { - case 0: - screen_rect_offset.translate(-1.f, -1.f); - break; - case 1: - screen_rect_offset.translate(1.f, -1.f); - break; - case 2: - screen_rect_offset.translate(1.f, 1.f); - break; - case 3: - screen_rect_offset.translate(-1.f, 1.f); - break; - case 4: - screen_rect_offset.translate(0, -2.f); - break; - } - - renderQuad(screen_rect_offset, uv_rect, slant_offset); - } - gGL.color4fv(color.mV); - renderQuad(screen_rect, uv_rect, slant_offset); - } - else if (style & DROP_SHADOW) - { - LLColor4 shadow_color = LLFontGL::sShadowColor; - shadow_color.mV[VALPHA] = color.mV[VALPHA] * drop_shadow_strength; - gGL.color4fv(shadow_color.mV); - LLRectf screen_rect_shadow = screen_rect; - screen_rect_shadow.translate(1.f, -1.f); - renderQuad(screen_rect_shadow, uv_rect, slant_offset); - gGL.color4fv(color.mV); - renderQuad(screen_rect, uv_rect, slant_offset); - } - else // normal rendering - { - gGL.color4fv(color.mV); - renderQuad(screen_rect, uv_rect, slant_offset); - } - + sFontRegistry->destroyGL(); } - gGL.end(); } // static -LLString LLFontGL::nameFromFont(const LLFontGL* fontp) +U8 LLFontGL::getStyleFromString(const std::string &style) { - if (fontp == sSansSerifHuge) - { - return LLString("SansSerifHuge"); - } - else if (fontp == sSansSerifSmall) - { - return LLString("SansSerifSmall"); - } - else if (fontp == sSansSerif) - { - return LLString("SansSerif"); - } - else if (fontp == sSansSerifBig) + S32 ret = 0; + if (style.find("NORMAL") != style.npos) { - return LLString("SansSerifBig"); + ret |= NORMAL; } - else if (fontp == sSansSerifBold) + if (style.find("BOLD") != style.npos) { - return LLString("SansSerifBold"); + ret |= BOLD; } - else if (fontp == sMonospace) + if (style.find("ITALIC") != style.npos) { - return LLString("Monospace"); + ret |= ITALIC; } - else + if (style.find("UNDERLINE") != style.npos) { - return LLString(); + ret |= UNDERLINE; } + return ret; } // static -LLFontGL* LLFontGL::fontFromName(const LLString& font_name) +std::string LLFontGL::getStringFromStyle(U8 style) { - LLFontGL* gl_font = NULL; - if (font_name == "SansSerifHuge") - { - gl_font = LLFontGL::sSansSerifHuge; - } - else if (font_name == "SansSerifSmall") - { - gl_font = LLFontGL::sSansSerifSmall; - } - else if (font_name == "SansSerif") + std::string style_string; + if (style & NORMAL) { - gl_font = LLFontGL::sSansSerif; + style_string += "|NORMAL"; } - else if (font_name == "SansSerifBig") + if (style & BOLD) { - gl_font = LLFontGL::sSansSerifBig; + style_string += "|BOLD"; } - else if (font_name == "SansSerifBold") + if (style & ITALIC) { - gl_font = LLFontGL::sSansSerifBold; + style_string += "|ITALIC"; } - else if (font_name == "Monospace") + if (style & UNDERLINE) { - gl_font = LLFontGL::sMonospace; + style_string += "|UNDERLINE"; } - return gl_font; + return style_string; +} + +// static +std::string LLFontGL::nameFromFont(const LLFontGL* fontp) +{ + return fontp->mFontDescriptor.getName(); +} + + +// static +std::string LLFontGL::sizeFromFont(const LLFontGL* fontp) +{ + return fontp->mFontDescriptor.getSize(); } // static -LLString LLFontGL::nameFromHAlign(LLFontGL::HAlign align) +std::string LLFontGL::nameFromHAlign(LLFontGL::HAlign align) { - if (align == LEFT) return LLString("left"); - else if (align == RIGHT) return LLString("right"); - else if (align == HCENTER) return LLString("center"); - else return LLString(); + if (align == LEFT) return std::string("left"); + else if (align == RIGHT) return std::string("right"); + else if (align == HCENTER) return std::string("center"); + else return std::string(); } // static -LLFontGL::HAlign LLFontGL::hAlignFromName(const LLString& name) +LLFontGL::HAlign LLFontGL::hAlignFromName(const std::string& name) { LLFontGL::HAlign gl_hfont_align = LLFontGL::LEFT; if (name == "left") @@ -1521,17 +941,17 @@ LLFontGL::HAlign LLFontGL::hAlignFromName(const LLString& name) } // static -LLString LLFontGL::nameFromVAlign(LLFontGL::VAlign align) +std::string LLFontGL::nameFromVAlign(LLFontGL::VAlign align) { - if (align == TOP) return LLString("top"); - else if (align == VCENTER) return LLString("center"); - else if (align == BASELINE) return LLString("baseline"); - else if (align == BOTTOM) return LLString("bottom"); - else return LLString(); + if (align == TOP) return std::string("top"); + else if (align == VCENTER) return std::string("center"); + else if (align == BASELINE) return std::string("baseline"); + else if (align == BOTTOM) return std::string("bottom"); + else return std::string(); } // static -LLFontGL::VAlign LLFontGL::vAlignFromName(const LLString& name) +LLFontGL::VAlign LLFontGL::vAlignFromName(const std::string& name) { LLFontGL::VAlign gl_vfont_align = LLFontGL::BASELINE; if (name == "top") @@ -1553,3 +973,244 @@ LLFontGL::VAlign LLFontGL::vAlignFromName(const LLString& name) //else leave baseline return gl_vfont_align; } + +//static +LLFontGL* LLFontGL::getFontMonospace() +{ + return getFont(LLFontDescriptor("Monospace","Monospace",0)); +} + +//static +LLFontGL* LLFontGL::getFontSansSerifSmall() +{ + return getFont(LLFontDescriptor("SansSerif","Small",0)); +} + +//static +LLFontGL* LLFontGL::getFontSansSerif() +{ + return getFont(LLFontDescriptor("SansSerif","Medium",0)); +} + +//static +LLFontGL* LLFontGL::getFontSansSerifBig() +{ + return getFont(LLFontDescriptor("SansSerif","Large",0)); +} + +//static +LLFontGL* LLFontGL::getFontSansSerifHuge() +{ + return getFont(LLFontDescriptor("SansSerif","Huge",0)); +} + +//static +LLFontGL* LLFontGL::getFontSansSerifBold() +{ + return getFont(LLFontDescriptor("SansSerif","Medium",BOLD)); +} + +//static +LLFontGL* LLFontGL::getFontExtChar() +{ + return getFontSansSerif(); +} + +//static +LLFontGL* LLFontGL::getFont(const LLFontDescriptor& desc) +{ + return sFontRegistry->getFont(desc); +} + +//static +LLFontGL* LLFontGL::getFontByName(const std::string& name) +{ + // check for most common fonts first + if (name == "SANSSERIF") + { + return getFontSansSerif(); + } + else if (name == "SANSSERIF_SMALL") + { + return getFontSansSerifSmall(); + } + else if (name == "SANSSERIF_BIG") + { + return getFontSansSerifBig(); + } + else if (name == "SMALL" || name == "OCRA") + { + // *BUG: Should this be "MONOSPACE"? Do we use "OCRA" anymore? + // Does "SMALL" mean "SERIF"? + return getFontMonospace(); + } + else + { + return NULL; + } +} + +//static +LLFontGL* LLFontGL::getFontDefault() +{ + return getFontSansSerif(); // Fallback to sans serif as default font +} + + +// static +std::string LLFontGL::getFontPathSystem() +{ + std::string system_path; + + // Try to figure out where the system's font files are stored. + char *system_root = NULL; +#if LL_WINDOWS + system_root = getenv("SystemRoot"); /* Flawfinder: ignore */ + if (!system_root) + { + llwarns << "SystemRoot not found, attempting to load fonts from default path." << llendl; + } +#endif + + if (system_root) + { + system_path = llformat("%s/fonts/", system_root); + } + else + { +#if LL_WINDOWS + // HACK for windows 98/Me + system_path = "/WINDOWS/FONTS/"; +#elif LL_DARWIN + // HACK for Mac OS X + system_path = "/System/Library/Fonts/"; +#endif + } + return system_path; +} + + +// static +std::string LLFontGL::getFontPathLocal() +{ + std::string local_path; + + // Backup files if we can't load from system fonts directory. + // We could store this in an end-user writable directory to allow + // end users to switch fonts. + if (LLFontGL::sAppDir.length()) + { + // use specified application dir to look for fonts + local_path = LLFontGL::sAppDir + "/fonts/"; + } + else + { + // assume working directory is executable directory + local_path = "./fonts/"; + } + return local_path; +} + +LLFontGL::LLFontGL(const LLFontGL &source) +{ + llerrs << "Not implemented!" << llendl; +} + +LLFontGL &LLFontGL::operator=(const LLFontGL &source) +{ + llerrs << "Not implemented" << llendl; + return *this; +} + +void LLFontGL::renderQuad(LLVector3* vertex_out, LLVector2* uv_out, LLColor4U* colors_out, const LLRectf& screen_rect, const LLRectf& uv_rect, const LLColor4U& color, F32 slant_amt) const +{ + S32 index = 0; + + vertex_out[index] = LLVector3(llfont_round_x(screen_rect.mRight), llfont_round_y(screen_rect.mTop), 0.f); + uv_out[index] = LLVector2(uv_rect.mRight, uv_rect.mTop); + colors_out[index] = color; + index++; + + vertex_out[index] = LLVector3(llfont_round_x(screen_rect.mLeft), llfont_round_y(screen_rect.mTop), 0.f); + uv_out[index] = LLVector2(uv_rect.mLeft, uv_rect.mTop); + colors_out[index] = color; + index++; + + vertex_out[index] = LLVector3(llfont_round_x(screen_rect.mLeft), llfont_round_y(screen_rect.mBottom), 0.f); + uv_out[index] = LLVector2(uv_rect.mLeft, uv_rect.mBottom); + colors_out[index] = color; + index++; + + vertex_out[index] = LLVector3(llfont_round_x(screen_rect.mRight), llfont_round_y(screen_rect.mBottom), 0.f); + uv_out[index] = LLVector2(uv_rect.mRight, uv_rect.mBottom); + colors_out[index] = color; +} + +void LLFontGL::drawGlyph(S32& glyph_count, LLVector3* vertex_out, LLVector2* uv_out, LLColor4U* colors_out, const LLRectf& screen_rect, const LLRectf& uv_rect, const LLColor4U& color, U8 style, ShadowType shadow, F32 drop_shadow_strength) const +{ + F32 slant_offset; + slant_offset = ((style & ITALIC) ? ( -mFontFreetype->getAscenderHeight() * 0.2f) : 0.f); + + //FIXME: bold and drop shadow are mutually exclusive only for convenience + //Allow both when we need them. + if (style & BOLD) + { + for (S32 pass = 0; pass < 2; pass++) + { + LLRectf screen_rect_offset = screen_rect; + + screen_rect_offset.translate((F32)(pass * BOLD_OFFSET), 0.f); + renderQuad(&vertex_out[glyph_count * 4], &uv_out[glyph_count * 4], &colors_out[glyph_count * 4], screen_rect_offset, uv_rect, color, slant_offset); + glyph_count++; + } + } + else if (shadow == DROP_SHADOW_SOFT) + { + LLColor4U shadow_color = LLFontGL::sShadowColor; + shadow_color.mV[VALPHA] = U8(color.mV[VALPHA] * drop_shadow_strength * DROP_SHADOW_SOFT_STRENGTH); + for (S32 pass = 0; pass < 5; pass++) + { + LLRectf screen_rect_offset = screen_rect; + + switch(pass) + { + case 0: + screen_rect_offset.translate(-1.f, -1.f); + break; + case 1: + screen_rect_offset.translate(1.f, -1.f); + break; + case 2: + screen_rect_offset.translate(1.f, 1.f); + break; + case 3: + screen_rect_offset.translate(-1.f, 1.f); + break; + case 4: + screen_rect_offset.translate(0, -2.f); + break; + } + + renderQuad(&vertex_out[glyph_count * 4], &uv_out[glyph_count * 4], &colors_out[glyph_count * 4], screen_rect_offset, uv_rect, shadow_color, slant_offset); + glyph_count++; + } + renderQuad(&vertex_out[glyph_count * 4], &uv_out[glyph_count * 4], &colors_out[glyph_count * 4], screen_rect, uv_rect, color, slant_offset); + glyph_count++; + } + else if (shadow == DROP_SHADOW) + { + LLColor4U shadow_color = LLFontGL::sShadowColor; + shadow_color.mV[VALPHA] = U8(color.mV[VALPHA] * drop_shadow_strength); + LLRectf screen_rect_shadow = screen_rect; + screen_rect_shadow.translate(1.f, -1.f); + renderQuad(&vertex_out[glyph_count * 4], &uv_out[glyph_count * 4], &colors_out[glyph_count * 4], screen_rect_shadow, uv_rect, shadow_color, slant_offset); + glyph_count++; + renderQuad(&vertex_out[glyph_count * 4], &uv_out[glyph_count * 4], &colors_out[glyph_count * 4], screen_rect, uv_rect, color, slant_offset); + glyph_count++; + } + else // normal rendering + { + renderQuad(&vertex_out[glyph_count * 4], &uv_out[glyph_count * 4], &colors_out[glyph_count * 4], screen_rect, uv_rect, color, slant_offset); + glyph_count++; + } +} diff --git a/indra/llrender/llfontgl.h b/indra/llrender/llfontgl.h index e49a9fc296..dc8d848ed2 100644 --- a/indra/llrender/llfontgl.h +++ b/indra/llrender/llfontgl.h @@ -3,45 +3,47 @@ * @author Doug Soo * @brief Wrapper around FreeType * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2007, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlife.com/developers/opensource/gplv2 + * Copyright (C) 2010, Linden Research, Inc. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at http://secondlife.com/developers/opensource/flossexception + * 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; + * version 2.1 of the License only. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #ifndef LL_LLFONTGL_H #define LL_LLFONTGL_H -#include "llfont.h" -#include "llimagegl.h" -#include "v2math.h" #include "llcoord.h" +#include "llfontregistry.h" +#include "llimagegl.h" +#include "llpointer.h" #include "llrect.h" +#include "v2math.h" class LLColor4; +// Key used to request a font. +class LLFontDescriptor; +class LLFontFreetype; -class LLFontGL : public LLFont +// Structure used to store previously requested fonts. +class LLFontRegistry; + +class LLFontGL { public: enum HAlign @@ -64,203 +66,156 @@ public: enum StyleFlags { // text style to render. May be combined (these are bit flags) - NORMAL = 0, - BOLD = 1, - ITALIC = 2, - UNDERLINE = 4, - DROP_SHADOW = 8, - DROP_SHADOW_SOFT = 16 + NORMAL = 0x00, + BOLD = 0x01, + ITALIC = 0x02, + UNDERLINE = 0x04 + }; + + enum ShadowType + { + NO_SHADOW, + DROP_SHADOW, + DROP_SHADOW_SOFT }; - - // Takes a string with potentially several flags, i.e. "NORMAL|BOLD|ITALIC" - static U8 getStyleFromString(const LLString &style); LLFontGL(); - LLFontGL(const LLFontGL &source); ~LLFontGL(); - void init(); // Internal init, or reinitialization + void reset(); // Reset a font after GL cleanup. ONLY works on an already loaded font. - LLFontGL &operator=(const LLFontGL &source); + void destroyGL(); - static BOOL initDefaultFonts(F32 screen_dpi, F32 x_scale, F32 y_scale, - const LLString& monospace_file, F32 monospace_size, - const LLString& sansserif_file, - const LLString& sansserif_fallback_file, F32 ss_fallback_scale, - F32 small_size, F32 medium_size, F32 large_size, F32 huge_size, - const LLString& sansserif_bold_file, F32 bold_size, - const LLString& app_dir = LLString::null); + BOOL loadFace(const std::string& filename, F32 point_size, const F32 vert_dpi, const F32 horz_dpi, const S32 components, BOOL is_fallback); - static void destroyDefaultFonts(); - static void destroyGL(); + S32 render(const LLWString &text, S32 begin_offset, + const LLRect& rect, + const LLColor4 &color, + HAlign halign = LEFT, VAlign valign = BASELINE, + U8 style = NORMAL, ShadowType shadow = NO_SHADOW, + S32 max_chars = S32_MAX, + F32* right_x=NULL, + BOOL use_ellipses = FALSE) const; - static bool loadFaceFallback(LLFontList *fontp, const LLString& fontname, const F32 point_size); - static bool loadFace(LLFontGL *fontp, const LLString& fontname, const F32 point_size, LLFontList *fallback_fontp); - /* virtual*/ BOOL loadFace(const std::string& filename, - const F32 point_size, const F32 vert_dpi, const F32 horz_dpi, - const S32 components, BOOL is_fallback); + S32 render(const LLWString &text, S32 begin_offset, + F32 x, F32 y, + const LLColor4 &color, + HAlign halign = LEFT, VAlign valign = BASELINE, + U8 style = NORMAL, ShadowType shadow = NO_SHADOW, + S32 max_chars = S32_MAX, S32 max_pixels = S32_MAX, + F32* right_x=NULL, + BOOL use_ellipses = FALSE) const; + S32 render(const LLWString &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color) const; - S32 renderUTF8(const LLString &text, const S32 begin_offset, - S32 x, S32 y, - const LLColor4 &color) const - { - return renderUTF8(text, begin_offset, (F32)x, (F32)y, color, - LEFT, BASELINE, NORMAL, - S32_MAX, S32_MAX, NULL, FALSE); - } - - S32 renderUTF8(const LLString &text, const S32 begin_offset, - S32 x, S32 y, - const LLColor4 &color, - HAlign halign, VAlign valign, U8 style = NORMAL) const - { - return renderUTF8(text, begin_offset, (F32)x, (F32)y, color, - halign, valign, style, - S32_MAX, S32_MAX, NULL, FALSE); - } - // renderUTF8 does a conversion, so is slower! - S32 renderUTF8(const LLString &text, - S32 begin_offset, - F32 x, F32 y, - const LLColor4 &color, - HAlign halign, - VAlign valign, - U8 style, - S32 max_chars, - S32 max_pixels, - F32* right_x, - BOOL use_ellipses) const; - - S32 render(const LLWString &text, const S32 begin_offset, - F32 x, F32 y, - const LLColor4 &color) const - { - return render(text, begin_offset, x, y, color, - LEFT, BASELINE, NORMAL, - S32_MAX, S32_MAX, NULL, FALSE, FALSE); - } - + S32 renderUTF8(const std::string &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, BOOL use_ellipses) const; + S32 renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y, const LLColor4 &color) const; + S32 renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style = NORMAL, ShadowType shadow = NO_SHADOW) const; - S32 render(const LLWString &text, - S32 begin_offset, - F32 x, F32 y, - const LLColor4 &color, - HAlign halign = LEFT, - VAlign valign = BASELINE, - U8 style = NORMAL, - S32 max_chars = S32_MAX, - S32 max_pixels = S32_MAX, - F32* right_x=NULL, - BOOL use_embedded = FALSE, - BOOL use_ellipses = FALSE) const; - - // font metrics - override for LLFont that returns units of virtual pixels - /*virtual*/ F32 getLineHeight() const { return (F32)llround(mLineHeight / sScaleY); } - /*virtual*/ F32 getAscenderHeight() const { return (F32)llround(mAscender / sScaleY); } - /*virtual*/ F32 getDescenderHeight() const { return (F32)llround(mDescender / sScaleY); } - - virtual S32 getWidth(const LLString& utf8text) const; - virtual S32 getWidth(const llwchar* wchars) const; - virtual S32 getWidth(const LLString& utf8text, const S32 offset, const S32 max_chars ) const; - virtual S32 getWidth(const llwchar* wchars, const S32 offset, const S32 max_chars, BOOL use_embedded = FALSE) const; + // font metrics - override for LLFontFreetype that returns units of virtual pixels + F32 getLineHeight() const; + F32 getAscenderHeight() const; + F32 getDescenderHeight() const; + + S32 getWidth(const std::string& utf8text) const; + S32 getWidth(const llwchar* wchars) const; + S32 getWidth(const std::string& utf8text, S32 offset, S32 max_chars ) const; + S32 getWidth(const llwchar* wchars, S32 offset, S32 max_chars) const; - virtual F32 getWidthF32(const LLString& utf8text) const; - virtual F32 getWidthF32(const llwchar* wchars) const; - virtual F32 getWidthF32(const LLString& text, const S32 offset, const S32 max_chars ) const; - virtual F32 getWidthF32(const llwchar* wchars, const S32 offset, const S32 max_chars, BOOL use_embedded = FALSE ) const; + F32 getWidthF32(const std::string& utf8text) const; + F32 getWidthF32(const llwchar* wchars) const; + F32 getWidthF32(const std::string& text, S32 offset, S32 max_chars ) const; + F32 getWidthF32(const llwchar* wchars, S32 offset, S32 max_chars) const; // The following are called often, frequently with large buffers, so do not use a string interface // Returns the max number of complete characters from text (up to max_chars) that can be drawn in max_pixels - virtual S32 maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_chars = S32_MAX, - BOOL end_on_word_boundary = FALSE, const BOOL use_embedded = FALSE, - F32* drawn_pixels = NULL) const; + typedef enum e_word_wrap_style + { + ONLY_WORD_BOUNDARIES, + WORD_BOUNDARY_IF_POSSIBLE, + ANYWHERE + } EWordWrapStyle ; + S32 maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_chars = S32_MAX, EWordWrapStyle end_on_word_boundary = ANYWHERE) const; // Returns the index of the first complete characters from text that can be drawn in max_pixels - // starting on the right side (at character start_pos). - virtual S32 firstDrawableChar(const llwchar* wchars, F32 max_pixels, S32 text_len, S32 start_pos=S32_MAX, S32 max_chars = S32_MAX) const; + // given that the character at start_pos should be the last character (or as close to last as possible). + S32 firstDrawableChar(const llwchar* wchars, F32 max_pixels, S32 text_len, S32 start_pos=S32_MAX, S32 max_chars = S32_MAX) const; // Returns the index of the character closest to pixel position x (ignoring text to the right of max_pixels and max_chars) - virtual S32 charFromPixelOffset(const llwchar* wchars, const S32 char_offset, - F32 x, F32 max_pixels=F32_MAX, S32 max_chars = S32_MAX, - BOOL round = TRUE, BOOL use_embedded = FALSE) const; + S32 charFromPixelOffset(const llwchar* wchars, S32 char_offset, F32 x, F32 max_pixels=F32_MAX, S32 max_chars = S32_MAX, BOOL round = TRUE) const; + const LLFontDescriptor& getFontDesc() const; - LLImageGL *getImageGL() const; - void addEmbeddedChar( llwchar wc, LLImageGL* image, const LLString& label); - void addEmbeddedChar( llwchar wc, LLImageGL* image, const LLWString& label); - void removeEmbeddedChar( llwchar wc ); + static void initClass(F32 screen_dpi, F32 x_scale, F32 y_scale, const std::string& app_dir, const std::vector<std::string>& xui_paths, bool create_gl_textures = true); - static LLString nameFromFont(const LLFontGL* fontp); - static LLFontGL* fontFromName(const LLString& name); + // Load sans-serif, sans-serif-small, etc. + // Slow, requires multiple seconds to load fonts. + static bool loadDefaultFonts(); + static void destroyDefaultFonts(); + static void destroyAllGL(); - static LLString nameFromHAlign(LLFontGL::HAlign align); - static LLFontGL::HAlign hAlignFromName(const LLString& name); + // Takes a string with potentially several flags, i.e. "NORMAL|BOLD|ITALIC" + static U8 getStyleFromString(const std::string &style); + static std::string getStringFromStyle(U8 style); - static LLString nameFromVAlign(LLFontGL::VAlign align); - static LLFontGL::VAlign vAlignFromName(const LLString& name); + static std::string nameFromFont(const LLFontGL* fontp); + static std::string sizeFromFont(const LLFontGL* fontp); - static void setFontDisplay(BOOL flag) { sDisplayFont = flag ; } + static std::string nameFromHAlign(LLFontGL::HAlign align); + static LLFontGL::HAlign hAlignFromName(const std::string& name); -protected: - struct embedded_data_t - { - embedded_data_t(LLImageGL* image, const LLWString& label) : mImage(image), mLabel(label) {} - LLPointer<LLImageGL> mImage; - LLWString mLabel; - }; - const embedded_data_t* getEmbeddedCharData(const llwchar wch) const; - F32 getEmbeddedCharAdvance(const embedded_data_t* ext_data) const; - void clearEmbeddedChars(); - void renderQuad(const LLRectf& screen_rect, const LLRectf& uv_rect, F32 slant_amt) const; - void drawGlyph(const LLRectf& screen_rect, const LLRectf& uv_rect, const LLColor4& color, U8 style, F32 drop_shadow_fade) const; + static std::string nameFromVAlign(LLFontGL::VAlign align); + static LLFontGL::VAlign vAlignFromName(const std::string& name); -public: - static F32 sVertDPI; - static F32 sHorizDPI; - static F32 sScaleX; - static F32 sScaleY; - static BOOL sDisplayFont ; - static LLString sAppDir; // For loading fonts + static void setFontDisplay(BOOL flag) { sDisplayFont = flag; } - static LLFontGL* sMonospace; // medium - static LLFontList* sMonospaceFallback; - - static LLFontGL* sSansSerifSmall; // small - static LLFontList* sSSSmallFallback; - static LLFontGL* sSansSerif; // medium - static LLFontList* sSSFallback; - static LLFontGL* sSansSerifBig; // large - static LLFontList* sSSBigFallback; - static LLFontGL* sSansSerifHuge; // very large - static LLFontList* sSSHugeFallback; + static LLFontGL* getFontMonospace(); + static LLFontGL* getFontSansSerifSmall(); + static LLFontGL* getFontSansSerif(); + static LLFontGL* getFontSansSerifBig(); + static LLFontGL* getFontSansSerifHuge(); + static LLFontGL* getFontSansSerifBold(); + static LLFontGL* getFontExtChar(); + static LLFontGL* getFont(const LLFontDescriptor& desc); + // Use with legacy names like "SANSSERIF_SMALL" or "OCRA" + static LLFontGL* getFontByName(const std::string& name); + static LLFontGL* getFontDefault(); // default fallback font + + static std::string getFontPathLocal(); + static std::string getFontPathSystem(); - static LLFontGL* sSansSerifBold; // medium, bolded - static LLFontList* sSSBoldFallback; + static LLCoordFont sCurOrigin; + static std::vector<LLCoordFont> sOriginStack; static LLColor4 sShadowColor; + static F32 sVertDPI; + static F32 sHorizDPI; + static F32 sScaleX; + static F32 sScaleY; + static BOOL sDisplayFont ; + static std::string sAppDir; // For loading fonts + +private: + friend class LLFontRegistry; friend class LLTextBillboard; friend class LLHUDText; -protected: - /*virtual*/ BOOL addChar(const llwchar wch); - static LLString getFontPathLocal(); - static LLString getFontPathSystem(); + LLFontGL(const LLFontGL &source); + LLFontGL &operator=(const LLFontGL &source); + + LLFontDescriptor mFontDescriptor; + LLPointer<LLFontFreetype> mFontFreetype; -protected: - LLPointer<LLImageRaw> mRawImageGLp; - LLPointer<LLImageGL> mImageGLp; - typedef std::map<llwchar,embedded_data_t*> embedded_map_t; - embedded_map_t mEmbeddedChars; - -public: - static LLCoordFont sCurOrigin; - static std::vector<LLCoordFont> sOriginStack; + void renderQuad(LLVector3* vertex_out, LLVector2* uv_out, LLColor4U* colors_out, const LLRectf& screen_rect, const LLRectf& uv_rect, const LLColor4U& color, F32 slant_amt) const; + void drawGlyph(S32& glyph_count, LLVector3* vertex_out, LLVector2* uv_out, LLColor4U* colors_out, const LLRectf& screen_rect, const LLRectf& uv_rect, const LLColor4U& color, U8 style, ShadowType shadow, F32 drop_shadow_fade) const; + + // Registry holds all instantiated fonts. + static LLFontRegistry* sFontRegistry; }; #endif diff --git a/indra/llrender/llfontregistry.cpp b/indra/llrender/llfontregistry.cpp new file mode 100644 index 0000000000..4d22eba3d9 --- /dev/null +++ b/indra/llrender/llfontregistry.cpp @@ -0,0 +1,670 @@ +/** + * @file llfontregistry.cpp + * @author Brad Payne + * @brief Storage for fonts. + * + * $LicenseInfo:firstyear=2008&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * 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; + * version 2.1 of the License only. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "llgl.h" +#include "llfontfreetype.h" +#include "llfontgl.h" +#include "llfontregistry.h" +#include <boost/tokenizer.hpp> +#include "llcontrol.h" +#include "lldir.h" +#include "llwindow.h" + +extern LLControlGroup gSavedSettings; + +using std::string; +using std::map; + +bool fontDescInitFromXML(LLXMLNodePtr node, LLFontDescriptor& desc); + +LLFontDescriptor::LLFontDescriptor(): + mStyle(0) +{ +} + +LLFontDescriptor::LLFontDescriptor(const std::string& name, + const std::string& size, + const U8 style, + const string_vec_t& file_names): + mName(name), + mSize(size), + mStyle(style), + mFileNames(file_names) +{ +} + +LLFontDescriptor::LLFontDescriptor(const std::string& name, + const std::string& size, + const U8 style): + mName(name), + mSize(size), + mStyle(style) +{ +} + + +bool LLFontDescriptor::operator<(const LLFontDescriptor& b) const +{ + if (mName < b.mName) + return true; + else if (mName > b.mName) + return false; + + if (mStyle < b.mStyle) + return true; + else if (mStyle > b.mStyle) + return false; + + if (mSize < b.mSize) + return true; + else + return false; +} + +static const std::string s_template_string("TEMPLATE"); + +bool LLFontDescriptor::isTemplate() const +{ + return getSize() == s_template_string; +} + +// Look for substring match and remove substring if matched. +bool removeSubString(std::string& str, const std::string& substr) +{ + size_t pos = str.find(substr); + if (pos != string::npos) + { + str.erase(pos, substr.size()); + return true; + } + return false; +} + +// Check for substring match without modifying the source string. +bool findSubString(std::string& str, const std::string& substr) +{ + size_t pos = str.find(substr); + if (pos != string::npos) + { + return true; + } + return false; +} + + +// Normal form is +// - raw name +// - bold, italic style info reflected in both style and font name. +// - other style info removed. +// - size info moved to mSize, defaults to Medium +// For example, +// - "SansSerifHuge" would normalize to { "SansSerif", "Huge", 0 } +// - "SansSerifBold" would normalize to { "SansSerifBold", "Medium", BOLD } +LLFontDescriptor LLFontDescriptor::normalize() const +{ + std::string new_name(mName); + std::string new_size(mSize); + U8 new_style(mStyle); + + // Only care about style to extent it can be picked up by font. + new_style &= (LLFontGL::BOLD | LLFontGL::ITALIC); + + // All these transformations are to support old-style font specifications. + if (removeSubString(new_name,"Small")) + new_size = "Small"; + if (removeSubString(new_name,"Big")) + new_size = "Large"; + if (removeSubString(new_name,"Medium")) + new_size = "Medium"; + if (removeSubString(new_name,"Large")) + new_size = "Large"; + if (removeSubString(new_name,"Huge")) + new_size = "Huge"; + + // HACK - Monospace is the only one we don't remove, so + // name "Monospace" doesn't get taken down to "" + // For other fonts, there's no ambiguity between font name and size specifier. + if (new_size != s_template_string && new_size.empty() && findSubString(new_name,"Monospace")) + new_size = "Monospace"; + if (new_size.empty()) + new_size = "Medium"; + + if (removeSubString(new_name,"Bold")) + new_style |= LLFontGL::BOLD; + + if (removeSubString(new_name,"Italic")) + new_style |= LLFontGL::ITALIC; + + return LLFontDescriptor(new_name,new_size,new_style,getFileNames()); +} + +LLFontRegistry::LLFontRegistry(const string_vec_t& xui_paths, + bool create_gl_textures) +: mCreateGLTextures(create_gl_textures) +{ + // Propagate this down from LLUICtrlFactory so LLRender doesn't + // need an upstream dependency on LLUI. + mXUIPaths = xui_paths; + + // This is potentially a slow directory traversal, so we want to + // cache the result. + mUltimateFallbackList = LLWindow::getDynamicFallbackFontList(); +} + +LLFontRegistry::~LLFontRegistry() +{ + clear(); +} + +bool LLFontRegistry::parseFontInfo(const std::string& xml_filename) +{ + bool success = false; // Succeed if we find at least one XUI file + const string_vec_t& xml_paths = mXUIPaths; + for (string_vec_t::const_iterator path_it = xml_paths.begin(); + path_it != xml_paths.end(); + ++path_it) + { + + LLXMLNodePtr root; + std::string full_filename = gDirUtilp->findSkinnedFilename(*path_it, xml_filename); + bool parsed_file = LLXMLNode::parseFile(full_filename, root, NULL); + + if (!parsed_file) + continue; + + if ( root.isNull() || ! root->hasName( "fonts" ) ) + { + llwarns << "Bad font info file: " + << full_filename << llendl; + continue; + } + + std::string root_name; + root->getAttributeString("name",root_name); + if (root->hasName("fonts")) + { + // Expect a collection of children consisting of "font" or "font_size" entries + bool init_succ = initFromXML(root); + success = success || init_succ; + } + } + //if (success) + // dump(); + + return success; +} + +std::string currentOsName() +{ +#if LL_WINDOWS + return "Windows"; +#elif LL_DARWIN + return "Mac"; +#elif LL_SDL + return "Linux"; +#else + return ""; +#endif +} + +bool fontDescInitFromXML(LLXMLNodePtr node, LLFontDescriptor& desc) +{ + if (node->hasName("font")) + { + std::string attr_name; + if (node->getAttributeString("name",attr_name)) + { + desc.setName(attr_name); + } + + std::string attr_style; + if (node->getAttributeString("font_style",attr_style)) + { + desc.setStyle(LLFontGL::getStyleFromString(attr_style)); + } + + desc.setSize(s_template_string); + } + + LLXMLNodePtr child; + for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling()) + { + std::string child_name; + child->getAttributeString("name",child_name); + if (child->hasName("file")) + { + std::string font_file_name = child->getTextContents(); + desc.getFileNames().push_back(font_file_name); + } + else if (child->hasName("os")) + { + if (child_name == currentOsName()) + { + fontDescInitFromXML(child, desc); + } + } + } + return true; +} + +bool LLFontRegistry::initFromXML(LLXMLNodePtr node) +{ + LLXMLNodePtr child; + + for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling()) + { + std::string child_name; + child->getAttributeString("name",child_name); + if (child->hasName("font")) + { + LLFontDescriptor desc; + bool font_succ = fontDescInitFromXML(child, desc); + LLFontDescriptor norm_desc = desc.normalize(); + if (font_succ) + { + // if this is the first time we've seen this font name, + // create a new template map entry for it. + const LLFontDescriptor *match_desc = getMatchingFontDesc(desc); + if (match_desc == NULL) + { + // Create a new entry (with no corresponding font). + mFontMap[norm_desc] = NULL; + } + // otherwise, find the existing entry and combine data. + else + { + // Prepend files from desc. + // A little roundabout because the map key is const, + // so we have to fetch it, make a new map key, and + // replace the old entry. + string_vec_t match_file_names = match_desc->getFileNames(); + match_file_names.insert(match_file_names.begin(), + desc.getFileNames().begin(), + desc.getFileNames().end()); + LLFontDescriptor new_desc = *match_desc; + new_desc.getFileNames() = match_file_names; + mFontMap.erase(*match_desc); + mFontMap[new_desc] = NULL; + } + } + } + else if (child->hasName("font_size")) + { + std::string size_name; + F32 size_value; + if (child->getAttributeString("name",size_name) && + child->getAttributeF32("size",size_value)) + { + mFontSizes[size_name] = size_value; + } + + } + } + return true; +} + +bool LLFontRegistry::nameToSize(const std::string& size_name, F32& size) +{ + font_size_map_t::iterator it = mFontSizes.find(size_name); + if (it != mFontSizes.end()) + { + size = it->second; + return true; + } + return false; +} + + +LLFontGL *LLFontRegistry::createFont(const LLFontDescriptor& desc) +{ + // Name should hold a font name recognized as a setting; the value + // of the setting should be a list of font files. + // Size should be a recognized string value + // Style should be a set of flags including any implied by the font name. + + // First decipher the requested size. + LLFontDescriptor norm_desc = desc.normalize(); + F32 point_size; + bool found_size = nameToSize(norm_desc.getSize(),point_size); + if (!found_size) + { + llwarns << "createFont unrecognized size " << norm_desc.getSize() << llendl; + return NULL; + } + llinfos << "createFont " << norm_desc.getName() << " size " << norm_desc.getSize() << " style " << ((S32) norm_desc.getStyle()) << llendl; + F32 fallback_scale = 1.0; + + // Find corresponding font template (based on same descriptor with no size specified) + LLFontDescriptor template_desc(norm_desc); + template_desc.setSize(s_template_string); + const LLFontDescriptor *match_desc = getClosestFontTemplate(template_desc); + if (!match_desc) + { + llwarns << "createFont failed, no template found for " + << norm_desc.getName() << " style [" << ((S32)norm_desc.getStyle()) << "]" << llendl; + return NULL; + } + + // See whether this best-match font has already been instantiated in the requested size. + LLFontDescriptor nearest_exact_desc = *match_desc; + nearest_exact_desc.setSize(norm_desc.getSize()); + font_reg_map_t::iterator it = mFontMap.find(nearest_exact_desc); + // If we fail to find a font in the fonts directory, it->second might be NULL. + // We shouldn't construcnt a font with a NULL mFontFreetype. + // This may not be the best solution, but it at least prevents a crash. + if (it != mFontMap.end() && it->second != NULL) + { + llinfos << "-- matching font exists: " << nearest_exact_desc.getName() << " size " << nearest_exact_desc.getSize() << " style " << ((S32) nearest_exact_desc.getStyle()) << llendl; + + // copying underlying Freetype font, and storing in LLFontGL with requested font descriptor + LLFontGL *font = new LLFontGL; + font->mFontDescriptor = desc; + font->mFontFreetype = it->second->mFontFreetype; + mFontMap[desc] = font; + + return font; + } + + // Build list of font names to look for. + // Files specified for this font come first, followed by those from the default descriptor. + string_vec_t file_names = match_desc->getFileNames(); + string_vec_t default_file_names; + LLFontDescriptor default_desc("default",s_template_string,0); + const LLFontDescriptor *match_default_desc = getMatchingFontDesc(default_desc); + if (match_default_desc) + { + file_names.insert(file_names.end(), + match_default_desc->getFileNames().begin(), + match_default_desc->getFileNames().end()); + } + + // Add ultimate fallback list - generated dynamically on linux, + // null elsewhere. + file_names.insert(file_names.end(), + getUltimateFallbackList().begin(), + getUltimateFallbackList().end()); + + // Load fonts based on names. + if (file_names.empty()) + { + llwarns << "createFont failed, no file names specified" << llendl; + return NULL; + } + + LLFontFreetype::font_vector_t fontlist; + LLFontGL *result = NULL; + + // Snarf all fonts we can into fontlist. First will get pulled + // off the list and become the "head" font, set to non-fallback. + // Rest will consitute the fallback list. + BOOL is_first_found = TRUE; + + std::string local_path = LLFontGL::getFontPathLocal(); + std::string sys_path = LLFontGL::getFontPathSystem(); + + // The fontname string may contain multiple font file names separated by semicolons. + // Break it apart and try loading each one, in order. + for(string_vec_t::iterator file_name_it = file_names.begin(); + file_name_it != file_names.end(); + ++file_name_it) + { + LLFontGL *fontp = new LLFontGL; + std::string font_path = local_path + *file_name_it; + // *HACK: Fallback fonts don't render, so we can use that to suppress + // creation of OpenGL textures for test apps. JC + BOOL is_fallback = !is_first_found || !mCreateGLTextures; + F32 extra_scale = (is_fallback)?fallback_scale:1.0; + if (!fontp->loadFace(font_path, extra_scale * point_size, + LLFontGL::sVertDPI, LLFontGL::sHorizDPI, 2, is_fallback)) + { + font_path = sys_path + *file_name_it; + + if (!fontp->loadFace(font_path, extra_scale * point_size, + LLFontGL::sVertDPI, LLFontGL::sHorizDPI, 2, is_fallback)) + { + LL_INFOS_ONCE("LLFontRegistry") << "Couldn't load font " << *file_name_it << LL_ENDL; + delete fontp; + fontp = NULL; + } + } + + if(fontp) + { + if (is_first_found) + { + result = fontp; + is_first_found = false; + } + else + { + fontlist.push_back(fontp->mFontFreetype); + delete fontp; + fontp = NULL; + } + } + } + + if (result && !fontlist.empty()) + { + result->mFontFreetype->setFallbackFonts(fontlist); + } + + if (result) + { + result->mFontDescriptor = desc; + } + else + { + llwarns << "createFont failed in some way" << llendl; + } + + mFontMap[desc] = result; + return result; +} + +void LLFontRegistry::reset() +{ + for (font_reg_map_t::iterator it = mFontMap.begin(); + it != mFontMap.end(); + ++it) + { + // Reset the corresponding font but preserve the entry. + if (it->second) + it->second->reset(); + } +} + +void LLFontRegistry::clear() +{ + for (font_reg_map_t::iterator it = mFontMap.begin(); + it != mFontMap.end(); + ++it) + { + LLFontGL *fontp = it->second; + delete fontp; + } + mFontMap.clear(); +} + +void LLFontRegistry::destroyGL() +{ + for (font_reg_map_t::iterator it = mFontMap.begin(); + it != mFontMap.end(); + ++it) + { + // Reset the corresponding font but preserve the entry. + if (it->second) + it->second->destroyGL(); + } +} + +LLFontGL *LLFontRegistry::getFont(const LLFontDescriptor& desc) +{ + font_reg_map_t::iterator it = mFontMap.find(desc); + if (it != mFontMap.end()) + return it->second; + else + { + LLFontGL *fontp = createFont(desc); + if (!fontp) + { + llwarns << "getFont failed, name " << desc.getName() + <<" style=[" << ((S32) desc.getStyle()) << "]" + << " size=[" << desc.getSize() << "]" << llendl; + } + return fontp; + } +} + +const LLFontDescriptor *LLFontRegistry::getMatchingFontDesc(const LLFontDescriptor& desc) +{ + LLFontDescriptor norm_desc = desc.normalize(); + + font_reg_map_t::iterator it = mFontMap.find(norm_desc); + if (it != mFontMap.end()) + return &(it->first); + else + return NULL; +} + +static U32 bitCount(U8 c) +{ + U32 count = 0; + if (c & 1) + count++; + if (c & 2) + count++; + if (c & 4) + count++; + if (c & 8) + count++; + if (c & 16) + count++; + if (c & 32) + count++; + if (c & 64) + count++; + if (c & 128) + count++; + return count; +} + +// Find nearest match for the requested descriptor. +const LLFontDescriptor *LLFontRegistry::getClosestFontTemplate(const LLFontDescriptor& desc) +{ + const LLFontDescriptor *exact_match_desc = getMatchingFontDesc(desc); + if (exact_match_desc) + { + return exact_match_desc; + } + + LLFontDescriptor norm_desc = desc.normalize(); + + const LLFontDescriptor *best_match_desc = NULL; + for (font_reg_map_t::iterator it = mFontMap.begin(); + it != mFontMap.end(); + ++it) + { + const LLFontDescriptor* curr_desc = &(it->first); + + // Ignore if not a template. + if (!curr_desc->isTemplate()) + continue; + + // Ignore if font name is wrong. + if (curr_desc->getName() != norm_desc.getName()) + continue; + + // Reject font if it matches any bits we don't want + if (curr_desc->getStyle() & ~norm_desc.getStyle()) + { + continue; + } + + // Take if it's the first plausible candidate we've found. + if (!best_match_desc) + { + best_match_desc = curr_desc; + continue; + } + + // Take if it matches more bits than anything before. + U8 best_style_match_bits = + norm_desc.getStyle() & best_match_desc->getStyle(); + U8 curr_style_match_bits = + norm_desc.getStyle() & curr_desc->getStyle(); + if (bitCount(curr_style_match_bits) > bitCount(best_style_match_bits)) + { + best_match_desc = curr_desc; + continue; + } + + // Tie-breaker: take if it matches bold. + if (curr_style_match_bits & LLFontGL::BOLD) // Bold is requested and this descriptor matches it. + { + best_match_desc = curr_desc; + continue; + } + } + + // Nothing matched. + return best_match_desc; +} + +void LLFontRegistry::dump() +{ + llinfos << "LLFontRegistry dump: " << llendl; + for (font_size_map_t::iterator size_it = mFontSizes.begin(); + size_it != mFontSizes.end(); + ++size_it) + { + llinfos << "Size: " << size_it->first << " => " << size_it->second << llendl; + } + for (font_reg_map_t::iterator font_it = mFontMap.begin(); + font_it != mFontMap.end(); + ++font_it) + { + const LLFontDescriptor& desc = font_it->first; + llinfos << "Font: name=" << desc.getName() + << " style=[" << ((S32)desc.getStyle()) << "]" + << " size=[" << desc.getSize() << "]" + << " fileNames=" + << llendl; + for (string_vec_t::const_iterator file_it=desc.getFileNames().begin(); + file_it != desc.getFileNames().end(); + ++file_it) + { + llinfos << " file: " << *file_it <<llendl; + } + } +} + +const string_vec_t& LLFontRegistry::getUltimateFallbackList() const +{ + return mUltimateFallbackList; +} diff --git a/indra/llrender/llfontregistry.h b/indra/llrender/llfontregistry.h new file mode 100644 index 0000000000..8b06191c56 --- /dev/null +++ b/indra/llrender/llfontregistry.h @@ -0,0 +1,112 @@ +/** + * @file llfontregistry.h + * @author Brad Payne + * @brief Storage for fonts. + * + * $LicenseInfo:firstyear=2008&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * 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; + * version 2.1 of the License only. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLFONTREGISTRY_H +#define LL_LLFONTREGISTRY_H + +#include "llxmlnode.h" + +class LLFontGL; + +typedef std::vector<std::string> string_vec_t; + +class LLFontDescriptor +{ +public: + LLFontDescriptor(); + LLFontDescriptor(const std::string& name, const std::string& size, const U8 style); + LLFontDescriptor(const std::string& name, const std::string& size, const U8 style, const string_vec_t& file_names); + LLFontDescriptor normalize() const; + + bool operator<(const LLFontDescriptor& b) const; + + bool isTemplate() const; + + const std::string& getName() const { return mName; } + void setName(const std::string& name) { mName = name; } + const std::string& getSize() const { return mSize; } + void setSize(const std::string& size) { mSize = size; } + const std::vector<std::string>& getFileNames() const { return mFileNames; } + std::vector<std::string>& getFileNames() { return mFileNames; } + const U8 getStyle() const { return mStyle; } + void setStyle(U8 style) { mStyle = style; } + +private: + std::string mName; + std::string mSize; + string_vec_t mFileNames; + U8 mStyle; +}; + +class LLFontRegistry +{ +public: + // create_gl_textures - set to false for test apps with no OpenGL window, + // such as llui_libtest + LLFontRegistry(const string_vec_t& xui_paths, + bool create_gl_textures); + ~LLFontRegistry(); + + // Load standard font info from XML file(s). + bool parseFontInfo(const std::string& xml_filename); + bool initFromXML(LLXMLNodePtr node); + + // Clear cached glyphs for all fonts. + void reset(); + + // Destroy all fonts. + void clear(); + + // GL cleanup + void destroyGL(); + + LLFontGL *getFont(const LLFontDescriptor& desc); + const LLFontDescriptor *getMatchingFontDesc(const LLFontDescriptor& desc); + const LLFontDescriptor *getClosestFontTemplate(const LLFontDescriptor& desc); + + bool nameToSize(const std::string& size_name, F32& size); + + void dump(); + + const string_vec_t& getUltimateFallbackList() const; + +private: + LLFontGL *createFont(const LLFontDescriptor& desc); + typedef std::map<LLFontDescriptor,LLFontGL*> font_reg_map_t; + typedef std::map<std::string,F32> font_size_map_t; + + // Given a descriptor, look up specific font instantiation. + font_reg_map_t mFontMap; + // Given a size name, look up the point size. + font_size_map_t mFontSizes; + + string_vec_t mUltimateFallbackList; + string_vec_t mXUIPaths; + bool mCreateGLTextures; +}; + +#endif // LL_LLFONTREGISTRY_H diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp new file mode 100644 index 0000000000..c0edd92bc1 --- /dev/null +++ b/indra/llrender/llgl.cpp @@ -0,0 +1,2059 @@ +/** + * @file llgl.cpp + * @brief LLGL implementation + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * 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; + * version 2.1 of the License only. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +// This file sets some global GL parameters, and implements some +// useful functions for GL operations. + +#define GLH_EXT_SINGLE_FILE + +#include "linden_common.h" + +#include "boost/tokenizer.hpp" + +#include "llsys.h" + +#include "llgl.h" +#include "llrender.h" + +#include "llerror.h" +#include "llerrorcontrol.h" +#include "llquaternion.h" +#include "llmath.h" +#include "m4math.h" +#include "llstring.h" +#include "llmemtype.h" +#include "llstacktrace.h" + +#include "llglheaders.h" + +#ifdef _DEBUG +//#define GL_STATE_VERIFY +#endif + + +BOOL gDebugSession = FALSE; +BOOL gDebugGL = FALSE; +BOOL gClothRipple = FALSE; +BOOL gNoRender = FALSE; +BOOL gGLActive = FALSE; + + +std::ofstream gFailLog; + +void ll_init_fail_log(std::string filename) +{ + gFailLog.open(filename.c_str()); +} + + +void ll_fail(std::string msg) +{ + + if (gDebugSession) + { + std::vector<std::string> lines; + + gFailLog << LLError::utcTime() << " " << msg << std::endl; + + gFailLog << "Stack Trace:" << std::endl; + + ll_get_stack_trace(lines); + + for(size_t i = 0; i < lines.size(); ++i) + { + gFailLog << lines[i] << std::endl; + } + + gFailLog << "End of Stack Trace." << std::endl << std::endl; + + gFailLog.flush(); + } +}; + +void ll_close_fail_log() +{ + gFailLog.close(); +} + +LLMatrix4 gGLObliqueProjectionInverse; + +#define LL_GL_NAME_POOLING 0 + +LLGLNamePool::pool_list_t LLGLNamePool::sInstances; +std::list<LLGLUpdate*> LLGLUpdate::sGLQ; + +#if (LL_WINDOWS || LL_LINUX || LL_SOLARIS) && !LL_MESA_HEADLESS +// ATI prototypes +// vertex blending prototypes +PFNGLWEIGHTPOINTERARBPROC glWeightPointerARB = NULL; +PFNGLVERTEXBLENDARBPROC glVertexBlendARB = NULL; +PFNGLWEIGHTFVARBPROC glWeightfvARB = NULL; + +// Vertex buffer object prototypes +PFNGLBINDBUFFERARBPROC glBindBufferARB = NULL; +PFNGLDELETEBUFFERSARBPROC glDeleteBuffersARB = NULL; +PFNGLGENBUFFERSARBPROC glGenBuffersARB = NULL; +PFNGLISBUFFERARBPROC glIsBufferARB = NULL; +PFNGLBUFFERDATAARBPROC glBufferDataARB = NULL; +PFNGLBUFFERSUBDATAARBPROC glBufferSubDataARB = NULL; +PFNGLGETBUFFERSUBDATAARBPROC glGetBufferSubDataARB = NULL; +PFNGLMAPBUFFERARBPROC glMapBufferARB = NULL; +PFNGLUNMAPBUFFERARBPROC glUnmapBufferARB = NULL; +PFNGLGETBUFFERPARAMETERIVARBPROC glGetBufferParameterivARB = NULL; +PFNGLGETBUFFERPOINTERVARBPROC glGetBufferPointervARB = NULL; + +// vertex object prototypes +PFNGLNEWOBJECTBUFFERATIPROC glNewObjectBufferATI = NULL; +PFNGLISOBJECTBUFFERATIPROC glIsObjectBufferATI = NULL; +PFNGLUPDATEOBJECTBUFFERATIPROC glUpdateObjectBufferATI = NULL; +PFNGLGETOBJECTBUFFERFVATIPROC glGetObjectBufferfvATI = NULL; +PFNGLGETOBJECTBUFFERIVATIPROC glGetObjectBufferivATI = NULL; +PFNGLFREEOBJECTBUFFERATIPROC glFreeObjectBufferATI = NULL; +PFNGLARRAYOBJECTATIPROC glArrayObjectATI = NULL; +PFNGLVERTEXATTRIBARRAYOBJECTATIPROC glVertexAttribArrayObjectATI = NULL; +PFNGLGETARRAYOBJECTFVATIPROC glGetArrayObjectfvATI = NULL; +PFNGLGETARRAYOBJECTIVATIPROC glGetArrayObjectivATI = NULL; +PFNGLVARIANTARRAYOBJECTATIPROC glVariantObjectArrayATI = NULL; +PFNGLGETVARIANTARRAYOBJECTFVATIPROC glGetVariantArrayObjectfvATI = NULL; +PFNGLGETVARIANTARRAYOBJECTIVATIPROC glGetVariantArrayObjectivATI = NULL; + +// GL_ARB_occlusion_query +PFNGLGENQUERIESARBPROC glGenQueriesARB = NULL; +PFNGLDELETEQUERIESARBPROC glDeleteQueriesARB = NULL; +PFNGLISQUERYARBPROC glIsQueryARB = NULL; +PFNGLBEGINQUERYARBPROC glBeginQueryARB = NULL; +PFNGLENDQUERYARBPROC glEndQueryARB = NULL; +PFNGLGETQUERYIVARBPROC glGetQueryivARB = NULL; +PFNGLGETQUERYOBJECTIVARBPROC glGetQueryObjectivARB = NULL; +PFNGLGETQUERYOBJECTUIVARBPROC glGetQueryObjectuivARB = NULL; + +// GL_ARB_point_parameters +PFNGLPOINTPARAMETERFARBPROC glPointParameterfARB = NULL; +PFNGLPOINTPARAMETERFVARBPROC glPointParameterfvARB = NULL; + +// GL_EXT_framebuffer_object +PFNGLISRENDERBUFFEREXTPROC glIsRenderbufferEXT = NULL; +PFNGLBINDRENDERBUFFEREXTPROC glBindRenderbufferEXT = NULL; +PFNGLDELETERENDERBUFFERSEXTPROC glDeleteRenderbuffersEXT = NULL; +PFNGLGENRENDERBUFFERSEXTPROC glGenRenderbuffersEXT = NULL; +PFNGLRENDERBUFFERSTORAGEEXTPROC glRenderbufferStorageEXT = NULL; +PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC glGetRenderbufferParameterivEXT = NULL; +PFNGLISFRAMEBUFFEREXTPROC glIsFramebufferEXT = NULL; +PFNGLBINDFRAMEBUFFEREXTPROC glBindFramebufferEXT = NULL; +PFNGLDELETEFRAMEBUFFERSEXTPROC glDeleteFramebuffersEXT = NULL; +PFNGLGENFRAMEBUFFERSEXTPROC glGenFramebuffersEXT = NULL; +PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC glCheckFramebufferStatusEXT = NULL; +PFNGLFRAMEBUFFERTEXTURE1DEXTPROC glFramebufferTexture1DEXT = NULL; +PFNGLFRAMEBUFFERTEXTURE2DEXTPROC glFramebufferTexture2DEXT = NULL; +PFNGLFRAMEBUFFERTEXTURE3DEXTPROC glFramebufferTexture3DEXT = NULL; +PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC glFramebufferRenderbufferEXT = NULL; +PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC glGetFramebufferAttachmentParameterivEXT = NULL; +PFNGLGENERATEMIPMAPEXTPROC glGenerateMipmapEXT = NULL; + +// GL_EXT_framebuffer_multisample +PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glRenderbufferStorageMultisampleEXT = NULL; + +// GL_EXT_framebuffer_blit +PFNGLBLITFRAMEBUFFEREXTPROC glBlitFramebufferEXT = NULL; + +// GL_EXT_blend_func_separate +PFNGLBLENDFUNCSEPARATEEXTPROC glBlendFuncSeparateEXT = NULL; + +// GL_ARB_draw_buffers +PFNGLDRAWBUFFERSARBPROC glDrawBuffersARB = NULL; + +//shader object prototypes +PFNGLDELETEOBJECTARBPROC glDeleteObjectARB = NULL; +PFNGLGETHANDLEARBPROC glGetHandleARB = NULL; +PFNGLDETACHOBJECTARBPROC glDetachObjectARB = NULL; +PFNGLCREATESHADEROBJECTARBPROC glCreateShaderObjectARB = NULL; +PFNGLSHADERSOURCEARBPROC glShaderSourceARB = NULL; +PFNGLCOMPILESHADERARBPROC glCompileShaderARB = NULL; +PFNGLCREATEPROGRAMOBJECTARBPROC glCreateProgramObjectARB = NULL; +PFNGLATTACHOBJECTARBPROC glAttachObjectARB = NULL; +PFNGLLINKPROGRAMARBPROC glLinkProgramARB = NULL; +PFNGLUSEPROGRAMOBJECTARBPROC glUseProgramObjectARB = NULL; +PFNGLVALIDATEPROGRAMARBPROC glValidateProgramARB = NULL; +PFNGLUNIFORM1FARBPROC glUniform1fARB = NULL; +PFNGLUNIFORM2FARBPROC glUniform2fARB = NULL; +PFNGLUNIFORM3FARBPROC glUniform3fARB = NULL; +PFNGLUNIFORM4FARBPROC glUniform4fARB = NULL; +PFNGLUNIFORM1IARBPROC glUniform1iARB = NULL; +PFNGLUNIFORM2IARBPROC glUniform2iARB = NULL; +PFNGLUNIFORM3IARBPROC glUniform3iARB = NULL; +PFNGLUNIFORM4IARBPROC glUniform4iARB = NULL; +PFNGLUNIFORM1FVARBPROC glUniform1fvARB = NULL; +PFNGLUNIFORM2FVARBPROC glUniform2fvARB = NULL; +PFNGLUNIFORM3FVARBPROC glUniform3fvARB = NULL; +PFNGLUNIFORM4FVARBPROC glUniform4fvARB = NULL; +PFNGLUNIFORM1IVARBPROC glUniform1ivARB = NULL; +PFNGLUNIFORM2IVARBPROC glUniform2ivARB = NULL; +PFNGLUNIFORM3IVARBPROC glUniform3ivARB = NULL; +PFNGLUNIFORM4IVARBPROC glUniform4ivARB = NULL; +PFNGLUNIFORMMATRIX2FVARBPROC glUniformMatrix2fvARB = NULL; +PFNGLUNIFORMMATRIX3FVARBPROC glUniformMatrix3fvARB = NULL; +PFNGLUNIFORMMATRIX4FVARBPROC glUniformMatrix4fvARB = NULL; +PFNGLGETOBJECTPARAMETERFVARBPROC glGetObjectParameterfvARB = NULL; +PFNGLGETOBJECTPARAMETERIVARBPROC glGetObjectParameterivARB = NULL; +PFNGLGETINFOLOGARBPROC glGetInfoLogARB = NULL; +PFNGLGETATTACHEDOBJECTSARBPROC glGetAttachedObjectsARB = NULL; +PFNGLGETUNIFORMLOCATIONARBPROC glGetUniformLocationARB = NULL; +PFNGLGETACTIVEUNIFORMARBPROC glGetActiveUniformARB = NULL; +PFNGLGETUNIFORMFVARBPROC glGetUniformfvARB = NULL; +PFNGLGETUNIFORMIVARBPROC glGetUniformivARB = NULL; +PFNGLGETSHADERSOURCEARBPROC glGetShaderSourceARB = NULL; + +// vertex shader prototypes +#if LL_LINUX || LL_SOLARIS +PFNGLVERTEXATTRIB1DARBPROC glVertexAttrib1dARB = NULL; +PFNGLVERTEXATTRIB1DVARBPROC glVertexAttrib1dvARB = NULL; +PFNGLVERTEXATTRIB1FARBPROC glVertexAttrib1fARB = NULL; +PFNGLVERTEXATTRIB1FVARBPROC glVertexAttrib1fvARB = NULL; +PFNGLVERTEXATTRIB1SARBPROC glVertexAttrib1sARB = NULL; +PFNGLVERTEXATTRIB1SVARBPROC glVertexAttrib1svARB = NULL; +PFNGLVERTEXATTRIB2DARBPROC glVertexAttrib2dARB = NULL; +PFNGLVERTEXATTRIB2DVARBPROC glVertexAttrib2dvARB = NULL; +PFNGLVERTEXATTRIB2FARBPROC glVertexAttrib2fARB = NULL; +PFNGLVERTEXATTRIB2FVARBPROC glVertexAttrib2fvARB = NULL; +PFNGLVERTEXATTRIB2SARBPROC glVertexAttrib2sARB = NULL; +PFNGLVERTEXATTRIB2SVARBPROC glVertexAttrib2svARB = NULL; +PFNGLVERTEXATTRIB3DARBPROC glVertexAttrib3dARB = NULL; +PFNGLVERTEXATTRIB3DVARBPROC glVertexAttrib3dvARB = NULL; +PFNGLVERTEXATTRIB3FARBPROC glVertexAttrib3fARB = NULL; +PFNGLVERTEXATTRIB3FVARBPROC glVertexAttrib3fvARB = NULL; +PFNGLVERTEXATTRIB3SARBPROC glVertexAttrib3sARB = NULL; +PFNGLVERTEXATTRIB3SVARBPROC glVertexAttrib3svARB = NULL; +#endif // LL_LINUX || LL_SOLARIS +PFNGLVERTEXATTRIB4NBVARBPROC glVertexAttrib4nbvARB = NULL; +PFNGLVERTEXATTRIB4NIVARBPROC glVertexAttrib4nivARB = NULL; +PFNGLVERTEXATTRIB4NSVARBPROC glVertexAttrib4nsvARB = NULL; +PFNGLVERTEXATTRIB4NUBARBPROC glVertexAttrib4nubARB = NULL; +PFNGLVERTEXATTRIB4NUBVARBPROC glVertexAttrib4nubvARB = NULL; +PFNGLVERTEXATTRIB4NUIVARBPROC glVertexAttrib4nuivARB = NULL; +PFNGLVERTEXATTRIB4NUSVARBPROC glVertexAttrib4nusvARB = NULL; +#if LL_LINUX || LL_SOLARIS +PFNGLVERTEXATTRIB4BVARBPROC glVertexAttrib4bvARB = NULL; +PFNGLVERTEXATTRIB4DARBPROC glVertexAttrib4dARB = NULL; +PFNGLVERTEXATTRIB4DVARBPROC glVertexAttrib4dvARB = NULL; +PFNGLVERTEXATTRIB4FARBPROC glVertexAttrib4fARB = NULL; +PFNGLVERTEXATTRIB4FVARBPROC glVertexAttrib4fvARB = NULL; +PFNGLVERTEXATTRIB4IVARBPROC glVertexAttrib4ivARB = NULL; +PFNGLVERTEXATTRIB4SARBPROC glVertexAttrib4sARB = NULL; +PFNGLVERTEXATTRIB4SVARBPROC glVertexAttrib4svARB = NULL; +PFNGLVERTEXATTRIB4UBVARBPROC glVertexAttrib4ubvARB = NULL; +PFNGLVERTEXATTRIB4UIVARBPROC glVertexAttrib4uivARB = NULL; +PFNGLVERTEXATTRIB4USVARBPROC glVertexAttrib4usvARB = NULL; +PFNGLVERTEXATTRIBPOINTERARBPROC glVertexAttribPointerARB = NULL; +PFNGLENABLEVERTEXATTRIBARRAYARBPROC glEnableVertexAttribArrayARB = NULL; +PFNGLDISABLEVERTEXATTRIBARRAYARBPROC glDisableVertexAttribArrayARB = NULL; +PFNGLPROGRAMSTRINGARBPROC glProgramStringARB = NULL; +PFNGLBINDPROGRAMARBPROC glBindProgramARB = NULL; +PFNGLDELETEPROGRAMSARBPROC glDeleteProgramsARB = NULL; +PFNGLGENPROGRAMSARBPROC glGenProgramsARB = NULL; +PFNGLPROGRAMENVPARAMETER4DARBPROC glProgramEnvParameter4dARB = NULL; +PFNGLPROGRAMENVPARAMETER4DVARBPROC glProgramEnvParameter4dvARB = NULL; +PFNGLPROGRAMENVPARAMETER4FARBPROC glProgramEnvParameter4fARB = NULL; +PFNGLPROGRAMENVPARAMETER4FVARBPROC glProgramEnvParameter4fvARB = NULL; +PFNGLPROGRAMLOCALPARAMETER4DARBPROC glProgramLocalParameter4dARB = NULL; +PFNGLPROGRAMLOCALPARAMETER4DVARBPROC glProgramLocalParameter4dvARB = NULL; +PFNGLPROGRAMLOCALPARAMETER4FARBPROC glProgramLocalParameter4fARB = NULL; +PFNGLPROGRAMLOCALPARAMETER4FVARBPROC glProgramLocalParameter4fvARB = NULL; +PFNGLGETPROGRAMENVPARAMETERDVARBPROC glGetProgramEnvParameterdvARB = NULL; +PFNGLGETPROGRAMENVPARAMETERFVARBPROC glGetProgramEnvParameterfvARB = NULL; +PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC glGetProgramLocalParameterdvARB = NULL; +PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC glGetProgramLocalParameterfvARB = NULL; +PFNGLGETPROGRAMIVARBPROC glGetProgramivARB = NULL; +PFNGLGETPROGRAMSTRINGARBPROC glGetProgramStringARB = NULL; +PFNGLGETVERTEXATTRIBDVARBPROC glGetVertexAttribdvARB = NULL; +PFNGLGETVERTEXATTRIBFVARBPROC glGetVertexAttribfvARB = NULL; +PFNGLGETVERTEXATTRIBIVARBPROC glGetVertexAttribivARB = NULL; +PFNGLGETVERTEXATTRIBPOINTERVARBPROC glGetVertexAttribPointervARB = NULL; +PFNGLISPROGRAMARBPROC glIsProgramARB = NULL; +#endif // LL_LINUX || LL_SOLARIS +PFNGLBINDATTRIBLOCATIONARBPROC glBindAttribLocationARB = NULL; +PFNGLGETACTIVEATTRIBARBPROC glGetActiveAttribARB = NULL; +PFNGLGETATTRIBLOCATIONARBPROC glGetAttribLocationARB = NULL; + +#if LL_WINDOWS +PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL; +#endif + +#if LL_LINUX_NV_GL_HEADERS +// linux nvidia headers. these define these differently to mesa's. ugh. +PFNGLACTIVETEXTUREARBPROC glActiveTextureARB = NULL; +PFNGLCLIENTACTIVETEXTUREARBPROC glClientActiveTextureARB = NULL; +PFNGLDRAWRANGEELEMENTSPROC glDrawRangeElements = NULL; +#endif // LL_LINUX_NV_GL_HEADERS +#endif + +LLGLManager gGLManager; + +LLGLManager::LLGLManager() : + mInited(FALSE), + mIsDisabled(FALSE), + + mHasMultitexture(FALSE), + mNumTextureUnits(1), + mHasMipMapGeneration(FALSE), + mHasCompressedTextures(FALSE), + mHasFramebufferObject(FALSE), + mHasFramebufferMultisample(FALSE), + mHasBlendFuncSeparate(FALSE), + + mHasVertexBufferObject(FALSE), + mHasPBuffer(FALSE), + mHasShaderObjects(FALSE), + mHasVertexShader(FALSE), + mHasFragmentShader(FALSE), + mHasOcclusionQuery(FALSE), + mHasPointParameters(FALSE), + mHasDrawBuffers(FALSE), + mHasTextureRectangle(FALSE), + + mHasAnisotropic(FALSE), + mHasARBEnvCombine(FALSE), + mHasCubeMap(FALSE), + + mIsATI(FALSE), + mIsNVIDIA(FALSE), + mIsIntel(FALSE), + mIsGF2or4MX(FALSE), + mIsGF3(FALSE), + mIsGFFX(FALSE), + mATIOffsetVerticalLines(FALSE), + mATIOldDriver(FALSE), + + mHasRequirements(TRUE), + + mHasSeparateSpecularColor(FALSE), + + mDebugGPU(FALSE), + + mDriverVersionMajor(1), + mDriverVersionMinor(0), + mDriverVersionRelease(0), + mGLVersion(1.0f), + + mVRAM(0), + mGLMaxVertexRange(0), + mGLMaxIndexRange(0) +{ +} + +//--------------------------------------------------------------------- +// Global initialization for GL +//--------------------------------------------------------------------- +void LLGLManager::initWGL() +{ + mHasPBuffer = FALSE; +#if LL_WINDOWS && !LL_MESA_HEADLESS + if (!glh_init_extensions("WGL_ARB_pixel_format")) + { + LL_WARNS("RenderInit") << "No ARB pixel format extensions" << LL_ENDL; + } + + if (ExtensionExists("WGL_EXT_swap_control", gGLHExts.mSysExts)) + { + GLH_EXT_NAME(wglSwapIntervalEXT) = (PFNWGLSWAPINTERVALEXTPROC)GLH_EXT_GET_PROC_ADDRESS("wglSwapIntervalEXT"); + } + + if( !glh_init_extensions("WGL_ARB_pbuffer") ) + { + LL_WARNS("RenderInit") << "No ARB WGL PBuffer extensions" << LL_ENDL; + } + + if( !glh_init_extensions("WGL_ARB_render_texture") ) + { + LL_WARNS("RenderInit") << "No ARB WGL render texture extensions" << LL_ENDL; + } + + mHasPBuffer = ExtensionExists("WGL_ARB_pbuffer", gGLHExts.mSysExts) && + ExtensionExists("WGL_ARB_render_texture", gGLHExts.mSysExts) && + ExtensionExists("WGL_ARB_pixel_format", gGLHExts.mSysExts); +#endif +} + +// return false if unable (or unwilling due to old drivers) to init GL +bool LLGLManager::initGL() +{ + if (mInited) + { + LL_ERRS("RenderInit") << "Calling init on LLGLManager after already initialized!" << LL_ENDL; + } + + GLint alpha_bits; + glGetIntegerv( GL_ALPHA_BITS, &alpha_bits ); + if( 8 != alpha_bits ) + { + LL_WARNS("RenderInit") << "Frame buffer has less than 8 bits of alpha. Avatar texture compositing will fail." << LL_ENDL; + } + + // Extract video card strings and convert to upper case to + // work around driver-to-driver variation in capitalization. + mGLVendor = std::string((const char *)glGetString(GL_VENDOR)); + LLStringUtil::toUpper(mGLVendor); + + mGLRenderer = std::string((const char *)glGetString(GL_RENDERER)); + LLStringUtil::toUpper(mGLRenderer); + + parse_gl_version( &mDriverVersionMajor, + &mDriverVersionMinor, + &mDriverVersionRelease, + &mDriverVersionVendorString ); + + mGLVersion = mDriverVersionMajor + mDriverVersionMinor * .1f; + + // Trailing space necessary to keep "nVidia Corpor_ati_on" cards + // from being recognized as ATI. + if (mGLVendor.substr(0,4) == "ATI ") + { + mGLVendorShort = "ATI"; + BOOL mobile = FALSE; + if (mGLRenderer.find("MOBILITY") != std::string::npos) + { + mobile = TRUE; + } + mIsATI = TRUE; + +#if LL_WINDOWS && !LL_MESA_HEADLESS + if (mDriverVersionRelease < 3842) + { + mATIOffsetVerticalLines = TRUE; + } +#endif // LL_WINDOWS + +#if (LL_WINDOWS || LL_LINUX) && !LL_MESA_HEADLESS + // release 7277 is a point at which we verify that ATI OpenGL + // drivers get pretty stable with SL, ~Catalyst 8.2, + // for both Win32 and Linux. + if (mDriverVersionRelease < 7277 && + mDriverVersionRelease != 0) // 0 == Undetectable driver version - these get to pretend to be new ATI drivers, though that decision may be revisited. + { + mATIOldDriver = TRUE; + } +#endif // (LL_WINDOWS || LL_LINUX) && !LL_MESA_HEADLESS + } + else if (mGLVendor.find("NVIDIA ") != std::string::npos) + { + mGLVendorShort = "NVIDIA"; + mIsNVIDIA = TRUE; + if ( mGLRenderer.find("GEFORCE4 MX") != std::string::npos + || mGLRenderer.find("GEFORCE2") != std::string::npos + || mGLRenderer.find("GEFORCE 2") != std::string::npos + || mGLRenderer.find("GEFORCE4 460 GO") != std::string::npos + || mGLRenderer.find("GEFORCE4 440 GO") != std::string::npos + || mGLRenderer.find("GEFORCE4 420 GO") != std::string::npos) + { + mIsGF2or4MX = TRUE; + } + else if (mGLRenderer.find("GEFORCE FX") != std::string::npos + || mGLRenderer.find("QUADRO FX") != std::string::npos + || mGLRenderer.find("NV34") != std::string::npos) + { + mIsGFFX = TRUE; + } + else if(mGLRenderer.find("GEFORCE3") != std::string::npos) + { + mIsGF3 = TRUE; + } + + } + else if (mGLVendor.find("INTEL") != std::string::npos +#if LL_LINUX + // The Mesa-based drivers put this in the Renderer string, + // not the Vendor string. + || mGLRenderer.find("INTEL") != std::string::npos +#endif //LL_LINUX + ) + { + mGLVendorShort = "INTEL"; + mIsIntel = TRUE; + } + else + { + mGLVendorShort = "MISC"; + } + + // This is called here because it depends on the setting of mIsGF2or4MX, and sets up mHasMultitexture. + initExtensions(); + + if (mHasMultitexture) + { + GLint num_tex_units; + glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &num_tex_units); + mNumTextureUnits = llmin(num_tex_units, (GLint)MAX_GL_TEXTURE_UNITS); + if (mIsIntel) + { + mNumTextureUnits = llmin(mNumTextureUnits, 2); + } + } + else + { + mHasRequirements = FALSE; + + // We don't support cards that don't support the GL_ARB_multitexture extension + LL_WARNS("RenderInit") << "GL Drivers do not support GL_ARB_multitexture" << LL_ENDL; + return false; + } + + setToDebugGPU(); + + initGLStates(); + return true; +} + +void LLGLManager::setToDebugGPU() +{ + //"MOBILE INTEL(R) 965 EXPRESS CHIP", + if (mGLRenderer.find("INTEL") != std::string::npos && mGLRenderer.find("965") != std::string::npos) + { + mDebugGPU = TRUE ; + } + + return ; +} + +void LLGLManager::getGLInfo(LLSD& info) +{ + info["GLInfo"]["GLVendor"] = std::string((const char *)glGetString(GL_VENDOR)); + info["GLInfo"]["GLRenderer"] = std::string((const char *)glGetString(GL_RENDERER)); + info["GLInfo"]["GLVersion"] = std::string((const char *)glGetString(GL_VERSION)); + +#if !LL_MESA_HEADLESS + std::string all_exts = ll_safe_string((const char *)gGLHExts.mSysExts); + boost::char_separator<char> sep(" "); + boost::tokenizer<boost::char_separator<char> > tok(all_exts, sep); + for(boost::tokenizer<boost::char_separator<char> >::iterator i = tok.begin(); i != tok.end(); ++i) + { + info["GLInfo"]["GLExtensions"].append(*i); + } +#endif +} + +std::string LLGLManager::getGLInfoString() +{ + std::string info_str; + std::string all_exts, line; + + info_str += std::string("GL_VENDOR ") + ll_safe_string((const char *)glGetString(GL_VENDOR)) + std::string("\n"); + info_str += std::string("GL_RENDERER ") + ll_safe_string((const char *)glGetString(GL_RENDERER)) + std::string("\n"); + info_str += std::string("GL_VERSION ") + ll_safe_string((const char *)glGetString(GL_VERSION)) + std::string("\n"); + +#if !LL_MESA_HEADLESS + all_exts = (const char *)gGLHExts.mSysExts; + LLStringUtil::replaceChar(all_exts, ' ', '\n'); + info_str += std::string("GL_EXTENSIONS:\n") + all_exts + std::string("\n"); +#endif + + return info_str; +} + +void LLGLManager::printGLInfoString() +{ + std::string info_str; + std::string all_exts, line; + + LL_INFOS("RenderInit") << "GL_VENDOR: " << ((const char *)glGetString(GL_VENDOR)) << LL_ENDL; + LL_INFOS("RenderInit") << "GL_RENDERER: " << ((const char *)glGetString(GL_RENDERER)) << LL_ENDL; + LL_INFOS("RenderInit") << "GL_VERSION: " << ((const char *)glGetString(GL_VERSION)) << LL_ENDL; + +#if !LL_MESA_HEADLESS + all_exts = std::string(gGLHExts.mSysExts); + LLStringUtil::replaceChar(all_exts, ' ', '\n'); + LL_DEBUGS("RenderInit") << "GL_EXTENSIONS:\n" << all_exts << LL_ENDL; +#endif +} + +std::string LLGLManager::getRawGLString() +{ + std::string gl_string; + gl_string = ll_safe_string((char*)glGetString(GL_VENDOR)) + " " + ll_safe_string((char*)glGetString(GL_RENDERER)); + return gl_string; +} + +void LLGLManager::shutdownGL() +{ + if (mInited) + { + glFinish(); + stop_glerror(); + mInited = FALSE; + } +} + +// these are used to turn software blending on. They appear in the Debug/Avatar menu +// presence of vertex skinning/blending or vertex programs will set these to FALSE by default. + +void LLGLManager::initExtensions() +{ +#if LL_MESA_HEADLESS +# if GL_ARB_multitexture + mHasMultitexture = TRUE; +# else + mHasMultitexture = FALSE; +# endif +# if GL_ARB_texture_env_combine + mHasARBEnvCombine = TRUE; +# else + mHasARBEnvCombine = FALSE; +# endif +# if GL_ARB_texture_compression + mHasCompressedTextures = TRUE; +# else + mHasCompressedTextures = FALSE; +# endif +# if GL_ARB_vertex_buffer_object + mHasVertexBufferObject = TRUE; +# else + mHasVertexBufferObject = FALSE; +# endif +# if GL_EXT_framebuffer_object + mHasFramebufferObject = TRUE; +# else + mHasFramebufferObject = FALSE; +# endif +# if GL_EXT_framebuffer_multisample + mHasFramebufferMultisample = TRUE; +# else + mHasFramebufferMultisample = FALSE; +# endif +# if GL_ARB_draw_buffers + mHasDrawBuffers = TRUE; +#else + mHasDrawBuffers = FALSE; +# endif +# if GL_EXT_blend_func_separate + mHasBlendFuncSeparate = TRUE; +#else + mHasBlendFuncSeparate = FALSE; +# endif + mHasMipMapGeneration = FALSE; + mHasSeparateSpecularColor = FALSE; + mHasAnisotropic = FALSE; + mHasCubeMap = FALSE; + mHasOcclusionQuery = FALSE; + mHasPointParameters = FALSE; + mHasShaderObjects = FALSE; + mHasVertexShader = FALSE; + mHasFragmentShader = FALSE; + mHasTextureRectangle = FALSE; +#else // LL_MESA_HEADLESS + mHasMultitexture = glh_init_extensions("GL_ARB_multitexture"); + mHasMipMapGeneration = glh_init_extensions("GL_SGIS_generate_mipmap"); + mHasSeparateSpecularColor = glh_init_extensions("GL_EXT_separate_specular_color"); + mHasAnisotropic = glh_init_extensions("GL_EXT_texture_filter_anisotropic"); + glh_init_extensions("GL_ARB_texture_cube_map"); + mHasCubeMap = ExtensionExists("GL_ARB_texture_cube_map", gGLHExts.mSysExts); + mHasARBEnvCombine = ExtensionExists("GL_ARB_texture_env_combine", gGLHExts.mSysExts); + mHasCompressedTextures = glh_init_extensions("GL_ARB_texture_compression"); + mHasOcclusionQuery = ExtensionExists("GL_ARB_occlusion_query", gGLHExts.mSysExts); + mHasVertexBufferObject = ExtensionExists("GL_ARB_vertex_buffer_object", gGLHExts.mSysExts); + // mask out FBO support when packed_depth_stencil isn't there 'cause we need it for LLRenderTarget -Brad + mHasFramebufferObject = ExtensionExists("GL_EXT_framebuffer_object", gGLHExts.mSysExts) + && ExtensionExists("GL_EXT_packed_depth_stencil", gGLHExts.mSysExts); + mHasFramebufferMultisample = mHasFramebufferObject && ExtensionExists("GL_EXT_framebuffer_multisample", gGLHExts.mSysExts); + mHasDrawBuffers = ExtensionExists("GL_ARB_draw_buffers", gGLHExts.mSysExts); + mHasBlendFuncSeparate = ExtensionExists("GL_EXT_blend_func_separate", gGLHExts.mSysExts); + mHasTextureRectangle = ExtensionExists("GL_ARB_texture_rectangle", gGLHExts.mSysExts); +#if !LL_DARWIN + mHasPointParameters = !mIsATI && ExtensionExists("GL_ARB_point_parameters", gGLHExts.mSysExts); +#endif + mHasShaderObjects = ExtensionExists("GL_ARB_shader_objects", gGLHExts.mSysExts) && ExtensionExists("GL_ARB_shading_language_100", gGLHExts.mSysExts); + mHasVertexShader = ExtensionExists("GL_ARB_vertex_program", gGLHExts.mSysExts) && ExtensionExists("GL_ARB_vertex_shader", gGLHExts.mSysExts) + && ExtensionExists("GL_ARB_shading_language_100", gGLHExts.mSysExts); + mHasFragmentShader = ExtensionExists("GL_ARB_fragment_shader", gGLHExts.mSysExts) && ExtensionExists("GL_ARB_shading_language_100", gGLHExts.mSysExts); +#endif + +#if LL_LINUX || LL_SOLARIS + llinfos << "initExtensions() checking shell variables to adjust features..." << llendl; + // Our extension support for the Linux Client is very young with some + // potential driver gotchas, so offer a semi-secret way to turn it off. + if (getenv("LL_GL_NOEXT")) + { + //mHasMultitexture = FALSE; // NEEDED! + mHasARBEnvCombine = FALSE; + mHasCompressedTextures = FALSE; + mHasVertexBufferObject = FALSE; + mHasFramebufferObject = FALSE; + mHasFramebufferMultisample = FALSE; + mHasDrawBuffers = FALSE; + mHasBlendFuncSeparate = FALSE; + mHasMipMapGeneration = FALSE; + mHasSeparateSpecularColor = FALSE; + mHasAnisotropic = FALSE; + mHasCubeMap = FALSE; + mHasOcclusionQuery = FALSE; + mHasPointParameters = FALSE; + mHasShaderObjects = FALSE; + mHasVertexShader = FALSE; + mHasFragmentShader = FALSE; + LL_WARNS("RenderInit") << "GL extension support DISABLED via LL_GL_NOEXT" << LL_ENDL; + } + else if (getenv("LL_GL_BASICEXT")) /* Flawfinder: ignore */ + { + // This switch attempts to turn off all support for exotic + // extensions which I believe correspond to fatal driver + // bug reports. This should be the default until we get a + // proper blacklist/whitelist on Linux. + mHasMipMapGeneration = FALSE; + mHasAnisotropic = FALSE; + //mHasCubeMap = FALSE; // apparently fatal on Intel 915 & similar + //mHasOcclusionQuery = FALSE; // source of many ATI system hangs + mHasShaderObjects = FALSE; + mHasVertexShader = FALSE; + mHasFragmentShader = FALSE; + mHasBlendFuncSeparate = FALSE; + LL_WARNS("RenderInit") << "GL extension support forced to SIMPLE level via LL_GL_BASICEXT" << LL_ENDL; + } + if (getenv("LL_GL_BLACKLIST")) /* Flawfinder: ignore */ + { + // This lets advanced troubleshooters disable specific + // GL extensions to isolate problems with their hardware. + // SL-28126 + const char *const blacklist = getenv("LL_GL_BLACKLIST"); /* Flawfinder: ignore */ + LL_WARNS("RenderInit") << "GL extension support partially disabled via LL_GL_BLACKLIST: " << blacklist << LL_ENDL; + if (strchr(blacklist,'a')) mHasARBEnvCombine = FALSE; + if (strchr(blacklist,'b')) mHasCompressedTextures = FALSE; + if (strchr(blacklist,'c')) mHasVertexBufferObject = FALSE; + if (strchr(blacklist,'d')) mHasMipMapGeneration = FALSE;//S +// if (strchr(blacklist,'f')) mHasNVVertexArrayRange = FALSE;//S +// if (strchr(blacklist,'g')) mHasNVFence = FALSE;//S + if (strchr(blacklist,'h')) mHasSeparateSpecularColor = FALSE; + if (strchr(blacklist,'i')) mHasAnisotropic = FALSE;//S + if (strchr(blacklist,'j')) mHasCubeMap = FALSE;//S +// if (strchr(blacklist,'k')) mHasATIVAO = FALSE;//S + if (strchr(blacklist,'l')) mHasOcclusionQuery = FALSE; + if (strchr(blacklist,'m')) mHasShaderObjects = FALSE;//S + if (strchr(blacklist,'n')) mHasVertexShader = FALSE;//S + if (strchr(blacklist,'o')) mHasFragmentShader = FALSE;//S + if (strchr(blacklist,'p')) mHasPointParameters = FALSE;//S + if (strchr(blacklist,'q')) mHasFramebufferObject = FALSE;//S + if (strchr(blacklist,'r')) mHasDrawBuffers = FALSE;//S + if (strchr(blacklist,'s')) mHasFramebufferMultisample = FALSE; + if (strchr(blacklist,'t')) mHasTextureRectangle = FALSE; + if (strchr(blacklist,'u')) mHasBlendFuncSeparate = FALSE;//S + + } +#endif // LL_LINUX || LL_SOLARIS + + if (!mHasMultitexture) + { + LL_INFOS("RenderInit") << "Couldn't initialize multitexturing" << LL_ENDL; + } + if (!mHasMipMapGeneration) + { + LL_INFOS("RenderInit") << "Couldn't initialize mipmap generation" << LL_ENDL; + } + if (!mHasARBEnvCombine) + { + LL_INFOS("RenderInit") << "Couldn't initialize GL_ARB_texture_env_combine" << LL_ENDL; + } + if (!mHasSeparateSpecularColor) + { + LL_INFOS("RenderInit") << "Couldn't initialize separate specular color" << LL_ENDL; + } + if (!mHasAnisotropic) + { + LL_INFOS("RenderInit") << "Couldn't initialize anisotropic filtering" << LL_ENDL; + } + if (!mHasCompressedTextures) + { + LL_INFOS("RenderInit") << "Couldn't initialize GL_ARB_texture_compression" << LL_ENDL; + } + if (!mHasOcclusionQuery) + { + LL_INFOS("RenderInit") << "Couldn't initialize GL_ARB_occlusion_query" << LL_ENDL; + } + if (!mHasPointParameters) + { + LL_INFOS("RenderInit") << "Couldn't initialize GL_ARB_point_parameters" << LL_ENDL; + } + if (!mHasShaderObjects) + { + LL_INFOS("RenderInit") << "Couldn't initialize GL_ARB_shader_objects" << LL_ENDL; + } + if (!mHasVertexShader) + { + LL_INFOS("RenderInit") << "Couldn't initialize GL_ARB_vertex_shader" << LL_ENDL; + } + if (!mHasFragmentShader) + { + LL_INFOS("RenderInit") << "Couldn't initialize GL_ARB_fragment_shader" << LL_ENDL; + } + if (!mHasBlendFuncSeparate) + { + LL_INFOS("RenderInit") << "Couldn't initialize GL_EXT_blend_func_separate" << LL_ENDL; + } + if (!mHasDrawBuffers) + { + LL_INFOS("RenderInit") << "Couldn't initialize GL_ARB_draw_buffers" << LL_ENDL; + } + + // Disable certain things due to known bugs + if (mIsIntel && mHasMipMapGeneration) + { + LL_INFOS("RenderInit") << "Disabling mip-map generation for Intel GPUs" << LL_ENDL; + mHasMipMapGeneration = FALSE; + } + if (mIsATI && mHasMipMapGeneration) + { + LL_INFOS("RenderInit") << "Disabling mip-map generation for ATI GPUs (performance opt)" << LL_ENDL; + mHasMipMapGeneration = FALSE; + } + + // Misc + glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, (GLint*) &mGLMaxVertexRange); + glGetIntegerv(GL_MAX_ELEMENTS_INDICES, (GLint*) &mGLMaxIndexRange); + +#if (LL_WINDOWS || LL_LINUX || LL_SOLARIS) && !LL_MESA_HEADLESS + LL_DEBUGS("RenderInit") << "GL Probe: Getting symbols" << LL_ENDL; + if (mHasVertexBufferObject) + { + glBindBufferARB = (PFNGLBINDBUFFERARBPROC)GLH_EXT_GET_PROC_ADDRESS("glBindBufferARB"); + if (glBindBufferARB) + { + glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC)GLH_EXT_GET_PROC_ADDRESS("glDeleteBuffersARB"); + glGenBuffersARB = (PFNGLGENBUFFERSARBPROC)GLH_EXT_GET_PROC_ADDRESS("glGenBuffersARB"); + glIsBufferARB = (PFNGLISBUFFERARBPROC)GLH_EXT_GET_PROC_ADDRESS("glIsBufferARB"); + glBufferDataARB = (PFNGLBUFFERDATAARBPROC)GLH_EXT_GET_PROC_ADDRESS("glBufferDataARB"); + glBufferSubDataARB = (PFNGLBUFFERSUBDATAARBPROC)GLH_EXT_GET_PROC_ADDRESS("glBufferSubDataARB"); + glGetBufferSubDataARB = (PFNGLGETBUFFERSUBDATAARBPROC)GLH_EXT_GET_PROC_ADDRESS("glGetBufferSubDataARB"); + glMapBufferARB = (PFNGLMAPBUFFERARBPROC)GLH_EXT_GET_PROC_ADDRESS("glMapBufferARB"); + glUnmapBufferARB = (PFNGLUNMAPBUFFERARBPROC)GLH_EXT_GET_PROC_ADDRESS("glUnmapBufferARB"); + glGetBufferParameterivARB = (PFNGLGETBUFFERPARAMETERIVARBPROC)GLH_EXT_GET_PROC_ADDRESS("glGetBufferParameterivARB"); + glGetBufferPointervARB = (PFNGLGETBUFFERPOINTERVARBPROC)GLH_EXT_GET_PROC_ADDRESS("glGetBufferPointervARB"); + } + else + { + mHasVertexBufferObject = FALSE; + } + } + if (mHasFramebufferObject) + { + llinfos << "initExtensions() FramebufferObject-related procs..." << llendl; + glIsRenderbufferEXT = (PFNGLISRENDERBUFFEREXTPROC) GLH_EXT_GET_PROC_ADDRESS("glIsRenderbufferEXT"); + glBindRenderbufferEXT = (PFNGLBINDRENDERBUFFEREXTPROC) GLH_EXT_GET_PROC_ADDRESS("glBindRenderbufferEXT"); + glDeleteRenderbuffersEXT = (PFNGLDELETERENDERBUFFERSEXTPROC) GLH_EXT_GET_PROC_ADDRESS("glDeleteRenderbuffersEXT"); + glGenRenderbuffersEXT = (PFNGLGENRENDERBUFFERSEXTPROC) GLH_EXT_GET_PROC_ADDRESS("glGenRenderbuffersEXT"); + glRenderbufferStorageEXT = (PFNGLRENDERBUFFERSTORAGEEXTPROC) GLH_EXT_GET_PROC_ADDRESS("glRenderbufferStorageEXT"); + glGetRenderbufferParameterivEXT = (PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC) GLH_EXT_GET_PROC_ADDRESS("glGetRenderbufferParameterivEXT"); + glIsFramebufferEXT = (PFNGLISFRAMEBUFFEREXTPROC) GLH_EXT_GET_PROC_ADDRESS("glIsFramebufferEXT"); + glBindFramebufferEXT = (PFNGLBINDFRAMEBUFFEREXTPROC) GLH_EXT_GET_PROC_ADDRESS("glBindFramebufferEXT"); + glDeleteFramebuffersEXT = (PFNGLDELETEFRAMEBUFFERSEXTPROC) GLH_EXT_GET_PROC_ADDRESS("glDeleteFramebuffersEXT"); + glGenFramebuffersEXT = (PFNGLGENFRAMEBUFFERSEXTPROC) GLH_EXT_GET_PROC_ADDRESS("glGenFramebuffersEXT"); + glCheckFramebufferStatusEXT = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) GLH_EXT_GET_PROC_ADDRESS("glCheckFramebufferStatusEXT"); + glFramebufferTexture1DEXT = (PFNGLFRAMEBUFFERTEXTURE1DEXTPROC) GLH_EXT_GET_PROC_ADDRESS("glFramebufferTexture1DEXT"); + glFramebufferTexture2DEXT = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) GLH_EXT_GET_PROC_ADDRESS("glFramebufferTexture2DEXT"); + glFramebufferTexture3DEXT = (PFNGLFRAMEBUFFERTEXTURE3DEXTPROC) GLH_EXT_GET_PROC_ADDRESS("glFramebufferTexture3DEXT"); + glFramebufferRenderbufferEXT = (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) GLH_EXT_GET_PROC_ADDRESS("glFramebufferRenderbufferEXT"); + glGetFramebufferAttachmentParameterivEXT = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) GLH_EXT_GET_PROC_ADDRESS("glGetFramebufferAttachmentParameterivEXT"); + glGenerateMipmapEXT = (PFNGLGENERATEMIPMAPEXTPROC) GLH_EXT_GET_PROC_ADDRESS("glGenerateMipmapEXT"); + } + if (mHasFramebufferMultisample) + { + glRenderbufferStorageMultisampleEXT = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC) GLH_EXT_GET_PROC_ADDRESS("glRenderbufferStorageMultisampleEXT"); + glBlitFramebufferEXT = (PFNGLBLITFRAMEBUFFEREXTPROC) GLH_EXT_GET_PROC_ADDRESS("glBlitFramebufferEXT"); + } + if (mHasDrawBuffers) + { + glDrawBuffersARB = (PFNGLDRAWBUFFERSARBPROC) GLH_EXT_GET_PROC_ADDRESS("glDrawBuffersARB"); + } + if (mHasBlendFuncSeparate) + { + glBlendFuncSeparateEXT = (PFNGLBLENDFUNCSEPARATEEXTPROC) GLH_EXT_GET_PROC_ADDRESS("glBlendFuncSeparateEXT"); + } +#if (!LL_LINUX && !LL_SOLARIS) || LL_LINUX_NV_GL_HEADERS + // This is expected to be a static symbol on Linux GL implementations, except if we use the nvidia headers - bah + glDrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawRangeElements"); + if (!glDrawRangeElements) + { + mGLMaxVertexRange = 0; + mGLMaxIndexRange = 0; + } +#endif // !LL_LINUX || LL_LINUX_NV_GL_HEADERS +#if LL_LINUX_NV_GL_HEADERS + // nvidia headers are critically different from mesa-esque + glActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC)GLH_EXT_GET_PROC_ADDRESS("glActiveTextureARB"); + glClientActiveTextureARB = (PFNGLCLIENTACTIVETEXTUREARBPROC)GLH_EXT_GET_PROC_ADDRESS("glClientActiveTextureARB"); +#endif // LL_LINUX_NV_GL_HEADERS + + if (mHasOcclusionQuery) + { + llinfos << "initExtensions() OcclusionQuery-related procs..." << llendl; + glGenQueriesARB = (PFNGLGENQUERIESARBPROC)GLH_EXT_GET_PROC_ADDRESS("glGenQueriesARB"); + glDeleteQueriesARB = (PFNGLDELETEQUERIESARBPROC)GLH_EXT_GET_PROC_ADDRESS("glDeleteQueriesARB"); + glIsQueryARB = (PFNGLISQUERYARBPROC)GLH_EXT_GET_PROC_ADDRESS("glIsQueryARB"); + glBeginQueryARB = (PFNGLBEGINQUERYARBPROC)GLH_EXT_GET_PROC_ADDRESS("glBeginQueryARB"); + glEndQueryARB = (PFNGLENDQUERYARBPROC)GLH_EXT_GET_PROC_ADDRESS("glEndQueryARB"); + glGetQueryivARB = (PFNGLGETQUERYIVARBPROC)GLH_EXT_GET_PROC_ADDRESS("glGetQueryivARB"); + glGetQueryObjectivARB = (PFNGLGETQUERYOBJECTIVARBPROC)GLH_EXT_GET_PROC_ADDRESS("glGetQueryObjectivARB"); + glGetQueryObjectuivARB = (PFNGLGETQUERYOBJECTUIVARBPROC)GLH_EXT_GET_PROC_ADDRESS("glGetQueryObjectuivARB"); + } + if (mHasPointParameters) + { + llinfos << "initExtensions() PointParameters-related procs..." << llendl; + glPointParameterfARB = (PFNGLPOINTPARAMETERFARBPROC)GLH_EXT_GET_PROC_ADDRESS("glPointParameterfARB"); + glPointParameterfvARB = (PFNGLPOINTPARAMETERFVARBPROC)GLH_EXT_GET_PROC_ADDRESS("glPointParameterfvARB"); + } + if (mHasShaderObjects) + { + glDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC) GLH_EXT_GET_PROC_ADDRESS("glDeleteObjectARB"); + glGetHandleARB = (PFNGLGETHANDLEARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetHandleARB"); + glDetachObjectARB = (PFNGLDETACHOBJECTARBPROC) GLH_EXT_GET_PROC_ADDRESS("glDetachObjectARB"); + glCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC) GLH_EXT_GET_PROC_ADDRESS("glCreateShaderObjectARB"); + glShaderSourceARB = (PFNGLSHADERSOURCEARBPROC) GLH_EXT_GET_PROC_ADDRESS("glShaderSourceARB"); + glCompileShaderARB = (PFNGLCOMPILESHADERARBPROC) GLH_EXT_GET_PROC_ADDRESS("glCompileShaderARB"); + glCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC) GLH_EXT_GET_PROC_ADDRESS("glCreateProgramObjectARB"); + glAttachObjectARB = (PFNGLATTACHOBJECTARBPROC) GLH_EXT_GET_PROC_ADDRESS("glAttachObjectARB"); + glLinkProgramARB = (PFNGLLINKPROGRAMARBPROC) GLH_EXT_GET_PROC_ADDRESS("glLinkProgramARB"); + glUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUseProgramObjectARB"); + glValidateProgramARB = (PFNGLVALIDATEPROGRAMARBPROC) GLH_EXT_GET_PROC_ADDRESS("glValidateProgramARB"); + glUniform1fARB = (PFNGLUNIFORM1FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform1fARB"); + glUniform2fARB = (PFNGLUNIFORM2FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform2fARB"); + glUniform3fARB = (PFNGLUNIFORM3FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform3fARB"); + glUniform4fARB = (PFNGLUNIFORM4FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform4fARB"); + glUniform1iARB = (PFNGLUNIFORM1IARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform1iARB"); + glUniform2iARB = (PFNGLUNIFORM2IARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform2iARB"); + glUniform3iARB = (PFNGLUNIFORM3IARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform3iARB"); + glUniform4iARB = (PFNGLUNIFORM4IARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform4iARB"); + glUniform1fvARB = (PFNGLUNIFORM1FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform1fvARB"); + glUniform2fvARB = (PFNGLUNIFORM2FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform2fvARB"); + glUniform3fvARB = (PFNGLUNIFORM3FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform3fvARB"); + glUniform4fvARB = (PFNGLUNIFORM4FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform4fvARB"); + glUniform1ivARB = (PFNGLUNIFORM1IVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform1ivARB"); + glUniform2ivARB = (PFNGLUNIFORM2IVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform2ivARB"); + glUniform3ivARB = (PFNGLUNIFORM3IVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform3ivARB"); + glUniform4ivARB = (PFNGLUNIFORM4IVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform4ivARB"); + glUniformMatrix2fvARB = (PFNGLUNIFORMMATRIX2FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix2fvARB"); + glUniformMatrix3fvARB = (PFNGLUNIFORMMATRIX3FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix3fvARB"); + glUniformMatrix4fvARB = (PFNGLUNIFORMMATRIX4FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix4fvARB"); + glGetObjectParameterfvARB = (PFNGLGETOBJECTPARAMETERFVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetObjectParameterfvARB"); + glGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetObjectParameterivARB"); + glGetInfoLogARB = (PFNGLGETINFOLOGARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetInfoLogARB"); + glGetAttachedObjectsARB = (PFNGLGETATTACHEDOBJECTSARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetAttachedObjectsARB"); + glGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetUniformLocationARB"); + glGetActiveUniformARB = (PFNGLGETACTIVEUNIFORMARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetActiveUniformARB"); + glGetUniformfvARB = (PFNGLGETUNIFORMFVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetUniformfvARB"); + glGetUniformivARB = (PFNGLGETUNIFORMIVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetUniformivARB"); + glGetShaderSourceARB = (PFNGLGETSHADERSOURCEARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetShaderSourceARB"); + } + if (mHasVertexShader) + { + llinfos << "initExtensions() VertexShader-related procs..." << llendl; + glGetAttribLocationARB = (PFNGLGETATTRIBLOCATIONARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetAttribLocationARB"); + glBindAttribLocationARB = (PFNGLBINDATTRIBLOCATIONARBPROC) GLH_EXT_GET_PROC_ADDRESS("glBindAttribLocationARB"); + glGetActiveAttribARB = (PFNGLGETACTIVEATTRIBARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetActiveAttribARB"); + glVertexAttrib1dARB = (PFNGLVERTEXATTRIB1DARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1dARB"); + glVertexAttrib1dvARB = (PFNGLVERTEXATTRIB1DVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1dvARB"); + glVertexAttrib1fARB = (PFNGLVERTEXATTRIB1FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1fARB"); + glVertexAttrib1fvARB = (PFNGLVERTEXATTRIB1FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1fvARB"); + glVertexAttrib1sARB = (PFNGLVERTEXATTRIB1SARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1sARB"); + glVertexAttrib1svARB = (PFNGLVERTEXATTRIB1SVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1svARB"); + glVertexAttrib2dARB = (PFNGLVERTEXATTRIB2DARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2dARB"); + glVertexAttrib2dvARB = (PFNGLVERTEXATTRIB2DVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2dvARB"); + glVertexAttrib2fARB = (PFNGLVERTEXATTRIB2FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2fARB"); + glVertexAttrib2fvARB = (PFNGLVERTEXATTRIB2FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2fvARB"); + glVertexAttrib2sARB = (PFNGLVERTEXATTRIB2SARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2sARB"); + glVertexAttrib2svARB = (PFNGLVERTEXATTRIB2SVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2svARB"); + glVertexAttrib3dARB = (PFNGLVERTEXATTRIB3DARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3dARB"); + glVertexAttrib3dvARB = (PFNGLVERTEXATTRIB3DVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3dvARB"); + glVertexAttrib3fARB = (PFNGLVERTEXATTRIB3FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3fARB"); + glVertexAttrib3fvARB = (PFNGLVERTEXATTRIB3FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3fvARB"); + glVertexAttrib3sARB = (PFNGLVERTEXATTRIB3SARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3sARB"); + glVertexAttrib3svARB = (PFNGLVERTEXATTRIB3SVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3svARB"); + glVertexAttrib4nbvARB = (PFNGLVERTEXATTRIB4NBVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4nbvARB"); + glVertexAttrib4nivARB = (PFNGLVERTEXATTRIB4NIVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4nivARB"); + glVertexAttrib4nsvARB = (PFNGLVERTEXATTRIB4NSVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4nsvARB"); + glVertexAttrib4nubARB = (PFNGLVERTEXATTRIB4NUBARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4nubARB"); + glVertexAttrib4nubvARB = (PFNGLVERTEXATTRIB4NUBVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4nubvARB"); + glVertexAttrib4nuivARB = (PFNGLVERTEXATTRIB4NUIVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4nuivARB"); + glVertexAttrib4nusvARB = (PFNGLVERTEXATTRIB4NUSVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4nusvARB"); + glVertexAttrib4bvARB = (PFNGLVERTEXATTRIB4BVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4bvARB"); + glVertexAttrib4dARB = (PFNGLVERTEXATTRIB4DARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4dARB"); + glVertexAttrib4dvARB = (PFNGLVERTEXATTRIB4DVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4dvARB"); + glVertexAttrib4fARB = (PFNGLVERTEXATTRIB4FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4fARB"); + glVertexAttrib4fvARB = (PFNGLVERTEXATTRIB4FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4fvARB"); + glVertexAttrib4ivARB = (PFNGLVERTEXATTRIB4IVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4ivARB"); + glVertexAttrib4sARB = (PFNGLVERTEXATTRIB4SARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4sARB"); + glVertexAttrib4svARB = (PFNGLVERTEXATTRIB4SVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4svARB"); + glVertexAttrib4ubvARB = (PFNGLVERTEXATTRIB4UBVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4ubvARB"); + glVertexAttrib4uivARB = (PFNGLVERTEXATTRIB4UIVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4uivARB"); + glVertexAttrib4usvARB = (PFNGLVERTEXATTRIB4USVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4usvARB"); + glVertexAttribPointerARB = (PFNGLVERTEXATTRIBPOINTERARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttribPointerARB"); + glEnableVertexAttribArrayARB = (PFNGLENABLEVERTEXATTRIBARRAYARBPROC) GLH_EXT_GET_PROC_ADDRESS("glEnableVertexAttribArrayARB"); + glDisableVertexAttribArrayARB = (PFNGLDISABLEVERTEXATTRIBARRAYARBPROC) GLH_EXT_GET_PROC_ADDRESS("glDisableVertexAttribArrayARB"); + glProgramStringARB = (PFNGLPROGRAMSTRINGARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramStringARB"); + glBindProgramARB = (PFNGLBINDPROGRAMARBPROC) GLH_EXT_GET_PROC_ADDRESS("glBindProgramARB"); + glDeleteProgramsARB = (PFNGLDELETEPROGRAMSARBPROC) GLH_EXT_GET_PROC_ADDRESS("glDeleteProgramsARB"); + glGenProgramsARB = (PFNGLGENPROGRAMSARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGenProgramsARB"); + glProgramEnvParameter4dARB = (PFNGLPROGRAMENVPARAMETER4DARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramEnvParameter4dARB"); + glProgramEnvParameter4dvARB = (PFNGLPROGRAMENVPARAMETER4DVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramEnvParameter4dvARB"); + glProgramEnvParameter4fARB = (PFNGLPROGRAMENVPARAMETER4FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramEnvParameter4fARB"); + glProgramEnvParameter4fvARB = (PFNGLPROGRAMENVPARAMETER4FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramEnvParameter4fvARB"); + glProgramLocalParameter4dARB = (PFNGLPROGRAMLOCALPARAMETER4DARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramLocalParameter4dARB"); + glProgramLocalParameter4dvARB = (PFNGLPROGRAMLOCALPARAMETER4DVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramLocalParameter4dvARB"); + glProgramLocalParameter4fARB = (PFNGLPROGRAMLOCALPARAMETER4FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramLocalParameter4fARB"); + glProgramLocalParameter4fvARB = (PFNGLPROGRAMLOCALPARAMETER4FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramLocalParameter4fvARB"); + glGetProgramEnvParameterdvARB = (PFNGLGETPROGRAMENVPARAMETERDVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetProgramEnvParameterdvARB"); + glGetProgramEnvParameterfvARB = (PFNGLGETPROGRAMENVPARAMETERFVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetProgramEnvParameterfvARB"); + glGetProgramLocalParameterdvARB = (PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetProgramLocalParameterdvARB"); + glGetProgramLocalParameterfvARB = (PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetProgramLocalParameterfvARB"); + glGetProgramivARB = (PFNGLGETPROGRAMIVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetProgramivARB"); + glGetProgramStringARB = (PFNGLGETPROGRAMSTRINGARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetProgramStringARB"); + glGetVertexAttribdvARB = (PFNGLGETVERTEXATTRIBDVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetVertexAttribdvARB"); + glGetVertexAttribfvARB = (PFNGLGETVERTEXATTRIBFVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetVertexAttribfvARB"); + glGetVertexAttribivARB = (PFNGLGETVERTEXATTRIBIVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetVertexAttribivARB"); + glGetVertexAttribPointervARB = (PFNGLGETVERTEXATTRIBPOINTERVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glgetVertexAttribPointervARB"); + glIsProgramARB = (PFNGLISPROGRAMARBPROC) GLH_EXT_GET_PROC_ADDRESS("glIsProgramARB"); + } + LL_DEBUGS("RenderInit") << "GL Probe: Got symbols" << LL_ENDL; +#endif + + mInited = TRUE; +} + +void rotate_quat(LLQuaternion& rotation) +{ + F32 angle_radians, x, y, z; + rotation.getAngleAxis(&angle_radians, &x, &y, &z); + glRotatef(angle_radians * RAD_TO_DEG, x, y, z); +} + +void flush_glerror() +{ + glGetError(); +} + +void do_assert_glerror() +{ + if (LL_UNLIKELY(!gGLManager.mInited)) + { + LL_ERRS("RenderInit") << "GL not initialized" << LL_ENDL; + } + // Create or update texture to be used with this data + GLenum error; + error = glGetError(); + BOOL quit = FALSE; + while (LL_UNLIKELY(error)) + { + quit = TRUE; + GLubyte const * gl_error_msg = gluErrorString(error); + if (NULL != gl_error_msg) + { + LL_WARNS("RenderState") << "GL Error:" << error<< LL_ENDL; + LL_WARNS("RenderState") << "GL Error String:" << gl_error_msg << LL_ENDL; + + if (gDebugSession) + { + gFailLog << "GL Error:" << gl_error_msg << std::endl; + } + } + else + { + // gluErrorString returns NULL for some extensions' error codes. + // you'll probably have to grep for the number in glext.h. + LL_WARNS("RenderState") << "GL Error: UNKNOWN 0x" << std::hex << error << std::dec << LL_ENDL; + + if (gDebugSession) + { + gFailLog << "GL Error: UNKNOWN 0x" << std::hex << error << std::dec << std::endl; + } + } + error = glGetError(); + } + + if (quit) + { + if (gDebugSession) + { + ll_fail("assert_glerror failed"); + } + else + { + llerrs << "One or more unhandled GL errors." << llendl; + } + } +} + +void assert_glerror() +{ + if (!gGLActive) + { + //llwarns << "GL used while not active!" << llendl; + + if (gDebugSession) + { + //ll_fail("GL used while not active"); + } + } + + if (!gNoRender && gDebugGL) + { + do_assert_glerror(); + } +} + + +void clear_glerror() +{ + // Create or update texture to be used with this data + GLenum error; + error = glGetError(); +} + +/////////////////////////////////////////////////////////////// +// +// LLGLState +// + +// Static members +boost::unordered_map<LLGLenum, LLGLboolean> LLGLState::sStateMap; + +GLboolean LLGLDepthTest::sDepthEnabled = GL_FALSE; // OpenGL default +GLenum LLGLDepthTest::sDepthFunc = GL_LESS; // OpenGL default +GLboolean LLGLDepthTest::sWriteEnabled = GL_TRUE; // OpenGL default + +//static +void LLGLState::initClass() +{ + sStateMap[GL_DITHER] = GL_TRUE; + // sStateMap[GL_TEXTURE_2D] = GL_TRUE; + + //make sure multisample defaults to disabled + sStateMap[GL_MULTISAMPLE_ARB] = GL_FALSE; + glDisable(GL_MULTISAMPLE_ARB); + + sStateMap[GL_MULTISAMPLE_ARB] = GL_FALSE; + glDisable(GL_MULTISAMPLE_ARB); + + glEnableClientState(GL_VERTEX_ARRAY); +} + +//static +void LLGLState::restoreGL() +{ + sStateMap.clear(); + initClass(); +} + +//static +// Really shouldn't be needed, but seems we sometimes do. +void LLGLState::resetTextureStates() +{ + gGL.flush(); + GLint maxTextureUnits; + + glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &maxTextureUnits); + for (S32 j = maxTextureUnits-1; j >=0; j--) + { + gGL.getTexUnit(j)->activate(); + glClientActiveTextureARB(GL_TEXTURE0_ARB+j); + j == 0 ? gGL.getTexUnit(j)->enable(LLTexUnit::TT_TEXTURE) : gGL.getTexUnit(j)->disable(); + } +} + +void LLGLState::dumpStates() +{ + LL_INFOS("RenderState") << "GL States:" << LL_ENDL; + for (boost::unordered_map<LLGLenum, LLGLboolean>::iterator iter = sStateMap.begin(); + iter != sStateMap.end(); ++iter) + { + LL_INFOS("RenderState") << llformat(" 0x%04x : %s",(S32)iter->first,iter->second?"TRUE":"FALSE") << LL_ENDL; + } +} + +void LLGLState::checkStates(const std::string& msg) +{ + if (!gDebugGL) + { + return; + } + + stop_glerror(); + + GLint src; + GLint dst; + glGetIntegerv(GL_BLEND_SRC, &src); + glGetIntegerv(GL_BLEND_DST, &dst); + + BOOL error = FALSE; + + if (src != GL_SRC_ALPHA || dst != GL_ONE_MINUS_SRC_ALPHA) + { + if (gDebugSession) + { + gFailLog << "Blend function corrupted: " << std::hex << src << " " << std::hex << dst << " " << msg << std::dec << std::endl; + error = TRUE; + } + else + { + LL_GL_ERRS << "Blend function corrupted: " << std::hex << src << " " << std::hex << dst << " " << msg << std::dec << LL_ENDL; + } + } + + for (boost::unordered_map<LLGLenum, LLGLboolean>::iterator iter = sStateMap.begin(); + iter != sStateMap.end(); ++iter) + { + LLGLenum state = iter->first; + LLGLboolean cur_state = iter->second; + LLGLboolean gl_state = glIsEnabled(state); + if(cur_state != gl_state) + { + dumpStates(); + if (gDebugSession) + { + gFailLog << llformat("LLGLState error. State: 0x%04x",state) << std::endl; + error = TRUE; + } + else + { + LL_GL_ERRS << llformat("LLGLState error. State: 0x%04x",state) << LL_ENDL; + } + } + } + + if (error) + { + ll_fail("LLGLState::checkStates failed."); + } + stop_glerror(); +} + +void LLGLState::checkTextureChannels(const std::string& msg) +{ + if (!gDebugGL) + { + return; + } + + stop_glerror(); + + GLint activeTexture; + glGetIntegerv(GL_ACTIVE_TEXTURE_ARB, &activeTexture); + stop_glerror(); + + BOOL error = FALSE; + + if (activeTexture == GL_TEXTURE0_ARB) + { + GLint tex_env_mode = 0; + + glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &tex_env_mode); + stop_glerror(); + + if (tex_env_mode != GL_MODULATE) + { + error = TRUE; + LL_WARNS("RenderState") << "GL_TEXTURE_ENV_MODE invalid: " << std::hex << tex_env_mode << std::dec << LL_ENDL; + if (gDebugSession) + { + gFailLog << "GL_TEXTURE_ENV_MODE invalid: " << std::hex << tex_env_mode << std::dec << std::endl; + } + } + } + + GLint maxTextureUnits = 0; + glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &maxTextureUnits); + stop_glerror(); + + static const char* label[] = + { + "GL_TEXTURE_2D", + "GL_TEXTURE_COORD_ARRAY", + "GL_TEXTURE_1D", + "GL_TEXTURE_CUBE_MAP_ARB", + "GL_TEXTURE_GEN_S", + "GL_TEXTURE_GEN_T", + "GL_TEXTURE_GEN_Q", + "GL_TEXTURE_GEN_R", + "GL_TEXTURE_RECTANGLE_ARB" + }; + + static GLint value[] = + { + GL_TEXTURE_2D, + GL_TEXTURE_COORD_ARRAY, + GL_TEXTURE_1D, + GL_TEXTURE_CUBE_MAP_ARB, + GL_TEXTURE_GEN_S, + GL_TEXTURE_GEN_T, + GL_TEXTURE_GEN_Q, + GL_TEXTURE_GEN_R, + GL_TEXTURE_RECTANGLE_ARB + }; + + GLint stackDepth = 0; + + glh::matrix4f mat; + glh::matrix4f identity; + identity.identity(); + + for (GLint i = 1; i < maxTextureUnits; i++) + { + gGL.getTexUnit(i)->activate(); + glClientActiveTextureARB(GL_TEXTURE0_ARB+i); + stop_glerror(); + glGetIntegerv(GL_TEXTURE_STACK_DEPTH, &stackDepth); + stop_glerror(); + + if (stackDepth != 1) + { + error = TRUE; + LL_WARNS("RenderState") << "Texture matrix stack corrupted." << LL_ENDL; + + if (gDebugSession) + { + gFailLog << "Texture matrix stack corrupted." << std::endl; + } + } + + glGetFloatv(GL_TEXTURE_MATRIX, (GLfloat*) mat.m); + stop_glerror(); + + if (mat != identity) + { + error = TRUE; + LL_WARNS("RenderState") << "Texture matrix in channel " << i << " corrupt." << LL_ENDL; + if (gDebugSession) + { + gFailLog << "Texture matrix in channel " << i << " corrupt." << std::endl; + } + } + + + for (S32 j = (i == 0 ? 1 : 0); + j < (gGLManager.mHasTextureRectangle ? 9 : 8); j++) + { + if (glIsEnabled(value[j])) + { + error = TRUE; + LL_WARNS("RenderState") << "Texture channel " << i << " still has " << label[j] << " enabled." << LL_ENDL; + if (gDebugSession) + { + gFailLog << "Texture channel " << i << " still has " << label[j] << " enabled." << std::endl; + } + } + stop_glerror(); + } + + glGetFloatv(GL_TEXTURE_MATRIX, mat.m); + stop_glerror(); + + if (mat != identity) + { + error = TRUE; + LL_WARNS("RenderState") << "Texture matrix " << i << " is not identity." << LL_ENDL; + if (gDebugSession) + { + gFailLog << "Texture matrix " << i << " is not identity." << std::endl; + } + } + } + + gGL.getTexUnit(0)->activate(); + glClientActiveTextureARB(GL_TEXTURE0_ARB); + stop_glerror(); + + if (error) + { + if (gDebugSession) + { + ll_fail("LLGLState::checkTextureChannels failed."); + } + else + { + LL_GL_ERRS << "GL texture state corruption detected. " << msg << LL_ENDL; + } + } +} + +void LLGLState::checkClientArrays(const std::string& msg, U32 data_mask) +{ + if (!gDebugGL) + { + return; + } + + stop_glerror(); + BOOL error = FALSE; + + GLint active_texture; + glGetIntegerv(GL_CLIENT_ACTIVE_TEXTURE_ARB, &active_texture); + + if (active_texture != GL_TEXTURE0_ARB) + { + llwarns << "Client active texture corrupted: " << active_texture << llendl; + if (gDebugSession) + { + gFailLog << "Client active texture corrupted: " << active_texture << std::endl; + } + error = TRUE; + } + + glGetIntegerv(GL_ACTIVE_TEXTURE_ARB, &active_texture); + if (active_texture != GL_TEXTURE0_ARB) + { + llwarns << "Active texture corrupted: " << active_texture << llendl; + if (gDebugSession) + { + gFailLog << "Active texture corrupted: " << active_texture << std::endl; + } + error = TRUE; + } + + static const char* label[] = + { + "GL_VERTEX_ARRAY", + "GL_NORMAL_ARRAY", + "GL_COLOR_ARRAY", + "GL_TEXTURE_COORD_ARRAY" + }; + + static GLint value[] = + { + GL_VERTEX_ARRAY, + GL_NORMAL_ARRAY, + GL_COLOR_ARRAY, + GL_TEXTURE_COORD_ARRAY + }; + + U32 mask[] = + { //copied from llvertexbuffer.h + 0x0001, //MAP_VERTEX, + 0x0002, //MAP_NORMAL, + 0x0010, //MAP_COLOR, + 0x0004, //MAP_TEXCOORD + }; + + + for (S32 j = 0; j < 4; j++) + { + if (glIsEnabled(value[j])) + { + if (!(mask[j] & data_mask)) + { + error = TRUE; + LL_WARNS("RenderState") << "GL still has " << label[j] << " enabled." << LL_ENDL; + if (gDebugSession) + { + gFailLog << "GL still has " << label[j] << " enabled." << std::endl; + } + } + } + else + { + if (mask[j] & data_mask) + { + error = TRUE; + LL_WARNS("RenderState") << "GL does not have " << label[j] << " enabled." << LL_ENDL; + if (gDebugSession) + { + gFailLog << "GL does not have " << label[j] << " enabled." << std::endl; + } + } + } + } + + glClientActiveTextureARB(GL_TEXTURE1_ARB); + gGL.getTexUnit(1)->activate(); + if (glIsEnabled(GL_TEXTURE_COORD_ARRAY)) + { + if (!(data_mask & 0x0008)) + { + error = TRUE; + LL_WARNS("RenderState") << "GL still has GL_TEXTURE_COORD_ARRAY enabled on channel 1." << LL_ENDL; + if (gDebugSession) + { + gFailLog << "GL still has GL_TEXTURE_COORD_ARRAY enabled on channel 1." << std::endl; + } + } + } + else + { + if (data_mask & 0x0008) + { + error = TRUE; + LL_WARNS("RenderState") << "GL does not have GL_TEXTURE_COORD_ARRAY enabled on channel 1." << LL_ENDL; + if (gDebugSession) + { + gFailLog << "GL does not have GL_TEXTURE_COORD_ARRAY enabled on channel 1." << std::endl; + } + } + } + + if (glIsEnabled(GL_TEXTURE_2D)) + { + if (!(data_mask & 0x0008)) + { + error = TRUE; + LL_WARNS("RenderState") << "GL still has GL_TEXTURE_2D enabled on channel 1." << LL_ENDL; + if (gDebugSession) + { + gFailLog << "GL still has GL_TEXTURE_2D enabled on channel 1." << std::endl; + } + } + } + else + { + if (data_mask & 0x0008) + { + error = TRUE; + LL_WARNS("RenderState") << "GL does not have GL_TEXTURE_2D enabled on channel 1." << LL_ENDL; + if (gDebugSession) + { + gFailLog << "GL does not have GL_TEXTURE_2D enabled on channel 1." << std::endl; + } + } + } + + glClientActiveTextureARB(GL_TEXTURE0_ARB); + gGL.getTexUnit(0)->activate(); + + if (gGLManager.mHasVertexShader) + { //make sure vertex attribs are all disabled + GLint count; + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS_ARB, &count); + for (GLint i = 0; i < count; i++) + { + GLint enabled; + glGetVertexAttribivARB((GLuint) i, GL_VERTEX_ATTRIB_ARRAY_ENABLED_ARB, &enabled); + if (enabled) + { + error = TRUE; + LL_WARNS("RenderState") << "GL still has vertex attrib array " << i << " enabled." << LL_ENDL; + if (gDebugSession) + { + gFailLog << "GL still has vertex attrib array " << i << " enabled." << std::endl; + } + } + } + } + + if (error) + { + if (gDebugSession) + { + ll_fail("LLGLState::checkClientArrays failed."); + } + else + { + LL_GL_ERRS << "GL client array corruption detected. " << msg << LL_ENDL; + } + } +} + +/////////////////////////////////////////////////////////////////////// + +LLGLState::LLGLState(LLGLenum state, S32 enabled) : + mState(state), mWasEnabled(FALSE), mIsEnabled(FALSE) +{ + stop_glerror(); + if (state) + { + mWasEnabled = sStateMap[state]; + llassert(mWasEnabled == glIsEnabled(state)); + setEnabled(enabled); + stop_glerror(); + } +} + +void LLGLState::setEnabled(S32 enabled) +{ + if (!mState) + { + return; + } + if (enabled == CURRENT_STATE) + { + enabled = sStateMap[mState] == GL_TRUE ? TRUE : FALSE; + } + else if (enabled == TRUE && sStateMap[mState] != GL_TRUE) + { + gGL.flush(); + glEnable(mState); + sStateMap[mState] = GL_TRUE; + } + else if (enabled == FALSE && sStateMap[mState] != GL_FALSE) + { + gGL.flush(); + glDisable(mState); + sStateMap[mState] = GL_FALSE; + } + mIsEnabled = enabled; +} + +LLGLState::~LLGLState() +{ + stop_glerror(); + if (mState) + { + if (gDebugGL) + { + if (!gDebugSession) + { + llassert_always(sStateMap[mState] == glIsEnabled(mState)); + } + else + { + if (sStateMap[mState] != glIsEnabled(mState)) + { + ll_fail("GL enabled state does not match expected"); + } + } + } + + if (mIsEnabled != mWasEnabled) + { + gGL.flush(); + if (mWasEnabled) + { + glEnable(mState); + sStateMap[mState] = GL_TRUE; + } + else + { + glDisable(mState); + sStateMap[mState] = GL_FALSE; + } + } + } + stop_glerror(); +} + +//////////////////////////////////////////////////////////////////////////////// + +void LLGLManager::initGLStates() +{ + //gl states moved to classes in llglstates.h + LLGLState::initClass(); +} + +//////////////////////////////////////////////////////////////////////////////// + +void enable_vertex_weighting(const S32 index) +{ +#if GL_ARB_vertex_program + if (index > 0) glEnableVertexAttribArrayARB(index); // vertex weights +#endif +} + +void disable_vertex_weighting(const S32 index) +{ +#if GL_ARB_vertex_program + if (index > 0) glDisableVertexAttribArrayARB(index); // vertex weights +#endif +} + +void enable_binormals(const S32 index) +{ +#if GL_ARB_vertex_program + if (index > 0) + { + glEnableVertexAttribArrayARB(index); // binormals + } +#endif +} + +void disable_binormals(const S32 index) +{ +#if GL_ARB_vertex_program + if (index > 0) + { + glDisableVertexAttribArrayARB(index); // binormals + } +#endif +} + + +void enable_cloth_weights(const S32 index) +{ +#if GL_ARB_vertex_program + if (index > 0) glEnableVertexAttribArrayARB(index); +#endif +} + +void disable_cloth_weights(const S32 index) +{ +#if GL_ARB_vertex_program + if (index > 0) glDisableVertexAttribArrayARB(index); +#endif +} + +void set_vertex_weights(const S32 index, const U32 stride, const F32 *weights) +{ +#if GL_ARB_vertex_program + if (index > 0) glVertexAttribPointerARB(index, 1, GL_FLOAT, FALSE, stride, weights); + stop_glerror(); +#endif +} + +void set_vertex_clothing_weights(const S32 index, const U32 stride, const LLVector4 *weights) +{ +#if GL_ARB_vertex_program + if (index > 0) glVertexAttribPointerARB(index, 4, GL_FLOAT, TRUE, stride, weights); + stop_glerror(); +#endif +} + +void set_binormals(const S32 index, const U32 stride,const LLVector3 *binormals) +{ +#if GL_ARB_vertex_program + if (index > 0) glVertexAttribPointerARB(index, 3, GL_FLOAT, FALSE, stride, binormals); + stop_glerror(); +#endif +} + +void parse_gl_version( S32* major, S32* minor, S32* release, std::string* vendor_specific ) +{ + // GL_VERSION returns a null-terminated string with the format: + // <major>.<minor>[.<release>] [<vendor specific>] + + const char* version = (const char*) glGetString(GL_VERSION); + *major = 0; + *minor = 0; + *release = 0; + vendor_specific->assign(""); + + if( !version ) + { + return; + } + + std::string ver_copy( version ); + S32 len = (S32)strlen( version ); /* Flawfinder: ignore */ + S32 i = 0; + S32 start; + // Find the major version + start = i; + for( ; i < len; i++ ) + { + if( '.' == version[i] ) + { + break; + } + } + std::string major_str = ver_copy.substr(start,i-start); + LLStringUtil::convertToS32(major_str, *major); + + if( '.' == version[i] ) + { + i++; + } + + // Find the minor version + start = i; + for( ; i < len; i++ ) + { + if( ('.' == version[i]) || isspace(version[i]) ) + { + break; + } + } + std::string minor_str = ver_copy.substr(start,i-start); + LLStringUtil::convertToS32(minor_str, *minor); + + // Find the release number (optional) + if( '.' == version[i] ) + { + i++; + + start = i; + for( ; i < len; i++ ) + { + if( isspace(version[i]) ) + { + break; + } + } + + std::string release_str = ver_copy.substr(start,i-start); + LLStringUtil::convertToS32(release_str, *release); + } + + // Skip over any white space + while( version[i] && isspace( version[i] ) ) + { + i++; + } + + // Copy the vendor-specific string (optional) + if( version[i] ) + { + vendor_specific->assign( version + i ); + } +} + +LLGLUserClipPlane::LLGLUserClipPlane(const LLPlane& p, const glh::matrix4f& modelview, const glh::matrix4f& projection) +{ + mModelview = modelview; + mProjection = projection; + + setPlane(p.mV[0], p.mV[1], p.mV[2], p.mV[3]); +} + +void LLGLUserClipPlane::setPlane(F32 a, F32 b, F32 c, F32 d) +{ + glh::matrix4f& P = mProjection; + glh::matrix4f& M = mModelview; + + glh::matrix4f invtrans_MVP = (P * M).inverse().transpose(); + glh::vec4f oplane(a,b,c,d); + glh::vec4f cplane; + invtrans_MVP.mult_matrix_vec(oplane, cplane); + + cplane /= fabs(cplane[2]); // normalize such that depth is not scaled + cplane[3] -= 1; + + if(cplane[2] < 0) + cplane *= -1; + + glh::matrix4f suffix; + suffix.set_row(2, cplane); + glh::matrix4f newP = suffix * P; + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadMatrixf(newP.m); + gGLObliqueProjectionInverse = LLMatrix4(newP.inverse().transpose().m); + glMatrixMode(GL_MODELVIEW); +} + +LLGLUserClipPlane::~LLGLUserClipPlane() +{ + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); +} + +LLGLNamePool::LLGLNamePool() +{ +} + +void LLGLNamePool::registerPool(LLGLNamePool* pool) +{ + pool_list_t::iterator iter = std::find(sInstances.begin(), sInstances.end(), pool); + if (iter == sInstances.end()) + { + sInstances.push_back(pool); + } +} + +LLGLNamePool::~LLGLNamePool() +{ + pool_list_t::iterator iter = std::find(sInstances.begin(), sInstances.end(), this); + if (iter != sInstances.end()) + { + sInstances.erase(iter); + } +} + +void LLGLNamePool::upkeep() +{ + std::sort(mNameList.begin(), mNameList.end(), CompareUsed()); +} + +void LLGLNamePool::cleanup() +{ + for (name_list_t::iterator iter = mNameList.begin(); iter != mNameList.end(); ++iter) + { + releaseName(iter->name); + } + + mNameList.clear(); +} + +GLuint LLGLNamePool::allocate() +{ +#if LL_GL_NAME_POOLING + for (name_list_t::iterator iter = mNameList.begin(); iter != mNameList.end(); ++iter) + { + if (!iter->used) + { + iter->used = TRUE; + return iter->name; + } + } + + NameEntry entry; + entry.name = allocateName(); + entry.used = TRUE; + mNameList.push_back(entry); + + return entry.name; +#else + return allocateName(); +#endif +} + +void LLGLNamePool::release(GLuint name) +{ +#if LL_GL_NAME_POOLING + for (name_list_t::iterator iter = mNameList.begin(); iter != mNameList.end(); ++iter) + { + if (iter->name == name) + { + if (iter->used) + { + iter->used = FALSE; + return; + } + else + { + llerrs << "Attempted to release a pooled name that is not in use!" << llendl; + } + } + } + llerrs << "Attempted to release a non pooled name!" << llendl; +#else + releaseName(name); +#endif +} + +//static +void LLGLNamePool::upkeepPools() +{ + LLMemType mt(LLMemType::MTYPE_UPKEEP_POOLS); + for (pool_list_t::iterator iter = sInstances.begin(); iter != sInstances.end(); ++iter) + { + LLGLNamePool* pool = *iter; + pool->upkeep(); + } +} + +//static +void LLGLNamePool::cleanupPools() +{ + for (pool_list_t::iterator iter = sInstances.begin(); iter != sInstances.end(); ++iter) + { + LLGLNamePool* pool = *iter; + pool->cleanup(); + } +} + +LLGLDepthTest::LLGLDepthTest(GLboolean depth_enabled, GLboolean write_enabled, GLenum depth_func) +: mPrevDepthEnabled(sDepthEnabled), mPrevDepthFunc(sDepthFunc), mPrevWriteEnabled(sWriteEnabled) +{ + stop_glerror(); + + checkState(); + + if (!depth_enabled) + { // always disable depth writes if depth testing is disabled + // GL spec defines this as a requirement, but some implementations allow depth writes with testing disabled + // The proper way to write to depth buffer with testing disabled is to enable testing and use a depth_func of GL_ALWAYS + write_enabled = FALSE; + } + + if (depth_enabled != sDepthEnabled) + { + gGL.flush(); + if (depth_enabled) glEnable(GL_DEPTH_TEST); + else glDisable(GL_DEPTH_TEST); + sDepthEnabled = depth_enabled; + } + if (depth_func != sDepthFunc) + { + gGL.flush(); + glDepthFunc(depth_func); + sDepthFunc = depth_func; + } + if (write_enabled != sWriteEnabled) + { + gGL.flush(); + glDepthMask(write_enabled); + sWriteEnabled = write_enabled; + } +} + +LLGLDepthTest::~LLGLDepthTest() +{ + checkState(); + if (sDepthEnabled != mPrevDepthEnabled ) + { + gGL.flush(); + if (mPrevDepthEnabled) glEnable(GL_DEPTH_TEST); + else glDisable(GL_DEPTH_TEST); + sDepthEnabled = mPrevDepthEnabled; + } + if (sDepthFunc != mPrevDepthFunc) + { + gGL.flush(); + glDepthFunc(mPrevDepthFunc); + sDepthFunc = mPrevDepthFunc; + } + if (sWriteEnabled != mPrevWriteEnabled ) + { + gGL.flush(); + glDepthMask(mPrevWriteEnabled); + sWriteEnabled = mPrevWriteEnabled; + } +} + +void LLGLDepthTest::checkState() +{ + if (gDebugGL) + { + GLint func = 0; + GLboolean mask = FALSE; + + glGetIntegerv(GL_DEPTH_FUNC, &func); + glGetBooleanv(GL_DEPTH_WRITEMASK, &mask); + + if (glIsEnabled(GL_DEPTH_TEST) != sDepthEnabled || + sWriteEnabled != mask || + sDepthFunc != func) + { + if (gDebugSession) + { + gFailLog << "Unexpected depth testing state." << std::endl; + } + else + { + LL_GL_ERRS << "Unexpected depth testing state." << LL_ENDL; + } + } + } +} + +LLGLClampToFarClip::LLGLClampToFarClip(glh::matrix4f P) +{ + for (U32 i = 0; i < 4; i++) + { + P.element(2, i) = P.element(3, i) * 0.99999f; + } + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadMatrixf(P.m); + glMatrixMode(GL_MODELVIEW); +} + +LLGLClampToFarClip::~LLGLClampToFarClip() +{ + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); +} + diff --git a/indra/llrender/llgl.h b/indra/llrender/llgl.h new file mode 100644 index 0000000000..5e8965c06a --- /dev/null +++ b/indra/llrender/llgl.h @@ -0,0 +1,419 @@ +/** + * @file llgl.h + * @brief LLGL definition + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * 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; + * version 2.1 of the License only. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLGL_H +#define LL_LLGL_H + +// This file contains various stuff for handling gl extensions and other gl related stuff. + +#include <string> +#include <boost/unordered_map.hpp> +#include <list> + +#include "llerror.h" +#include "v4color.h" +#include "llstring.h" +#include "stdtypes.h" +#include "v4math.h" +#include "llplane.h" +#include "llgltypes.h" + +#include "llglheaders.h" +#include "glh/glh_linear.h" + +extern BOOL gDebugGL; +extern BOOL gDebugSession; +extern std::ofstream gFailLog; + +#define LL_GL_ERRS LL_ERRS("RenderState") + +void ll_init_fail_log(std::string filename); + +void ll_fail(std::string msg); + +void ll_close_fail_log(); + +class LLSD; + +// Manage GL extensions... +class LLGLManager +{ +public: + LLGLManager(); + + bool initGL(); + void shutdownGL(); + + void initWGL(); // Initializes stupid WGL extensions + + std::string getRawGLString(); // For sending to simulator + + BOOL mInited; + BOOL mIsDisabled; + + // Extensions used by everyone + BOOL mHasMultitexture; + S32 mNumTextureUnits; + BOOL mHasMipMapGeneration; + BOOL mHasCompressedTextures; + BOOL mHasFramebufferObject; + BOOL mHasFramebufferMultisample; + BOOL mHasBlendFuncSeparate; + + // ARB Extensions + BOOL mHasVertexBufferObject; + BOOL mHasPBuffer; + BOOL mHasShaderObjects; + BOOL mHasVertexShader; + BOOL mHasFragmentShader; + BOOL mHasOcclusionQuery; + BOOL mHasPointParameters; + BOOL mHasDrawBuffers; + BOOL mHasTextureRectangle; + + // Other extensions. + BOOL mHasAnisotropic; + BOOL mHasARBEnvCombine; + BOOL mHasCubeMap; + + // Vendor-specific extensions + BOOL mIsATI; + BOOL mIsNVIDIA; + BOOL mIsIntel; + BOOL mIsGF2or4MX; + BOOL mIsGF3; + BOOL mIsGFFX; + BOOL mATIOffsetVerticalLines; + BOOL mATIOldDriver; + + // Whether this version of GL is good enough for SL to use + BOOL mHasRequirements; + + // Misc extensions + BOOL mHasSeparateSpecularColor; + + //whether this GPU is in the debug list. + BOOL mDebugGPU; + + S32 mDriverVersionMajor; + S32 mDriverVersionMinor; + S32 mDriverVersionRelease; + F32 mGLVersion; // e.g = 1.4 + std::string mDriverVersionVendorString; + + S32 mVRAM; // VRAM in MB + S32 mGLMaxVertexRange; + S32 mGLMaxIndexRange; + + void getPixelFormat(); // Get the best pixel format + + std::string getGLInfoString(); + void printGLInfoString(); + void getGLInfo(LLSD& info); + + // In ALL CAPS + std::string mGLVendor; + std::string mGLVendorShort; + + // In ALL CAPS + std::string mGLRenderer; + +private: + void initExtensions(); + void initGLStates(); + void initGLImages(); + void setToDebugGPU(); +}; + +extern LLGLManager gGLManager; + +class LLQuaternion; +class LLMatrix4; + +void rotate_quat(LLQuaternion& rotation); + +void flush_glerror(); // Flush GL errors when we know we're handling them correctly. + +void assert_glerror(); + +void clear_glerror(); + +//#if LL_DEBUG +# define stop_glerror() assert_glerror() +# define llglassertok() assert_glerror() +//#else +//# define stop_glerror() +//# define llglassertok() +//#endif + +#define llglassertok_always() assert_glerror() + +//////////////////////// +// +// Note: U32's are GLEnum's... +// + +// This is a class for GL state management + +/* + GL STATE MANAGEMENT DESCRIPTION + + LLGLState and its two subclasses, LLGLEnable and LLGLDisable, manage the current + enable/disable states of the GL to prevent redundant setting of state within a + render path or the accidental corruption of what state the next path expects. + + Essentially, wherever you would call glEnable set a state and then + subsequently reset it by calling glDisable (or vice versa), make an instance of + LLGLEnable with the state you want to set, and assume it will be restored to its + original state when that instance of LLGLEnable is destroyed. It is good practice + to exploit stack frame controls for optimal setting/unsetting and readability of + code. In llglstates.h, there are a collection of helper classes that define groups + of enables/disables that can cause multiple states to be set with the creation of + one instance. + + Sample usage: + + //disable lighting for rendering hud objects + //INCORRECT USAGE + LLGLEnable lighting(GL_LIGHTING); + renderHUD(); + LLGLDisable lighting(GL_LIGHTING); + + //CORRECT USAGE + { + LLGLEnable lighting(GL_LIGHTING); + renderHUD(); + } + + If a state is to be set on a conditional, the following mechanism + is useful: + + { + LLGLEnable lighting(light_hud ? GL_LIGHTING : 0); + renderHUD(); + } + + A LLGLState initialized with a parameter of 0 does nothing. + + LLGLState works by maintaining a map of the current GL states, and ignoring redundant + enables/disables. If a redundant call is attempted, it becomes a noop, otherwise, + it is set in the constructor and reset in the destructor. + + For debugging GL state corruption, running with debug enabled will trigger asserts + if the existing GL state does not match the expected GL state. + +*/ +class LLGLState +{ +public: + static void initClass(); + static void restoreGL(); + + static void resetTextureStates(); + static void dumpStates(); + static void checkStates(const std::string& msg = ""); + static void checkTextureChannels(const std::string& msg = ""); + static void checkClientArrays(const std::string& msg = "", U32 data_mask = 0x0001); + +protected: + static boost::unordered_map<LLGLenum, LLGLboolean> sStateMap; + +public: + enum { CURRENT_STATE = -2 }; + LLGLState(LLGLenum state, S32 enabled = CURRENT_STATE); + ~LLGLState(); + void setEnabled(S32 enabled); + void enable() { setEnabled(TRUE); } + void disable() { setEnabled(FALSE); } +protected: + LLGLenum mState; + BOOL mWasEnabled; + BOOL mIsEnabled; +}; + +// New LLGLState class wrappers that don't depend on actual GL flags. +class LLGLEnableBlending : public LLGLState +{ +public: + LLGLEnableBlending(bool enable); +}; + +class LLGLEnableAlphaReject : public LLGLState +{ +public: + LLGLEnableAlphaReject(bool enable); +}; + +/// TODO: Being deprecated. +class LLGLEnable : public LLGLState +{ +public: + LLGLEnable(LLGLenum state) : LLGLState(state, TRUE) {} +}; + +/// TODO: Being deprecated. +class LLGLDisable : public LLGLState +{ +public: + LLGLDisable(LLGLenum state) : LLGLState(state, FALSE) {} +}; + +/* + Store and modify projection matrix to create an oblique + projection that clips to the specified plane. Oblique + projections alter values in the depth buffer, so this + class should not be used mid-renderpass. + + Restores projection matrix on destruction. + GL_MODELVIEW_MATRIX is active whenever program execution + leaves this class. + Does not stack. + Caches inverse of projection matrix used in gGLObliqueProjectionInverse +*/ +class LLGLUserClipPlane +{ +public: + + LLGLUserClipPlane(const LLPlane& plane, const glh::matrix4f& modelview, const glh::matrix4f& projection); + ~LLGLUserClipPlane(); + + void setPlane(F32 a, F32 b, F32 c, F32 d); + +private: + glh::matrix4f mProjection; + glh::matrix4f mModelview; +}; + +/* + Modify and load projection matrix to push depth values to far clip plane. + + Restores projection matrix on destruction. + GL_MODELVIEW_MATRIX is active whenever program execution + leaves this class. + Does not stack. +*/ +class LLGLClampToFarClip +{ +public: + LLGLClampToFarClip(glh::matrix4f projection); + ~LLGLClampToFarClip(); +}; + +/* + Generic pooling scheme for things which use GL names (used for occlusion queries and vertex buffer objects). + Prevents thrashing of GL name caches by avoiding calls to glGenFoo and glDeleteFoo. +*/ +class LLGLNamePool +{ +public: + struct NameEntry + { + GLuint name; + BOOL used; + }; + + struct CompareUsed + { + bool operator()(const NameEntry& lhs, const NameEntry& rhs) + { + return lhs.used < rhs.used; //FALSE entries first + } + }; + + typedef std::vector<NameEntry> name_list_t; + name_list_t mNameList; + + LLGLNamePool(); + virtual ~LLGLNamePool(); + + void upkeep(); + void cleanup(); + + GLuint allocate(); + void release(GLuint name); + + static void registerPool(LLGLNamePool* pool); + static void upkeepPools(); + static void cleanupPools(); + +protected: + typedef std::vector<LLGLNamePool*> pool_list_t; + static pool_list_t sInstances; + + virtual GLuint allocateName() = 0; + virtual void releaseName(GLuint name) = 0; +}; + +/* + Interface for objects that need periodic GL updates applied to them. + Used to synchronize GL updates with GL thread. +*/ +class LLGLUpdate +{ +public: + + static std::list<LLGLUpdate*> sGLQ; + + BOOL mInQ; + LLGLUpdate() + : mInQ(FALSE) + { + } + virtual ~LLGLUpdate() + { + if (mInQ) + { + std::list<LLGLUpdate*>::iterator iter = std::find(sGLQ.begin(), sGLQ.end(), this); + if (iter != sGLQ.end()) + { + sGLQ.erase(iter); + } + } + } + virtual void updateGL() = 0; +}; + +extern LLMatrix4 gGLObliqueProjectionInverse; + +#include "llglstates.h" + +void init_glstates(); +void enable_vertex_weighting(const S32 index); +void disable_vertex_weighting(const S32 index); +void enable_binormals(const S32 index); +void disable_binormals(const S32 index); +void enable_cloth_weights(const S32 index); +void disable_cloth_weights(const S32 index); +void set_vertex_weights(const S32 index, const U32 stride, const F32 *weights); +void set_vertex_clothing_weights(const S32 index, const U32 stride, const LLVector4 *weights); +void set_binormals(const S32 index, const U32 stride, const LLVector3 *binormals); +void parse_gl_version( S32* major, S32* minor, S32* release, std::string* vendor_specific ); + +extern BOOL gClothRipple; +extern BOOL gNoRender; +extern BOOL gGLActive; + +#endif // LL_LLGL_H diff --git a/indra/llrender/llgldbg.cpp b/indra/llrender/llgldbg.cpp index 704cda9a23..4b68194db3 100644 --- a/indra/llrender/llgldbg.cpp +++ b/indra/llrender/llgldbg.cpp @@ -2,30 +2,25 @@ * @file llgldbg.cpp * @brief Definitions for OpenGL debugging support * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2007, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlife.com/developers/opensource/gplv2 + * Copyright (C) 2010, Linden Research, Inc. + * + * 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; + * version 2.1 of the License only. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at http://secondlife.com/developers/opensource/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llrender/llgldbg.h b/indra/llrender/llgldbg.h index 8f390054a6..963579cb82 100644 --- a/indra/llrender/llgldbg.h +++ b/indra/llrender/llgldbg.h @@ -2,30 +2,25 @@ * @file llgldbg.h * @brief Definitions for OpenGL debugging support * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2007, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlife.com/developers/opensource/gplv2 + * Copyright (C) 2010, Linden Research, Inc. + * + * 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; + * version 2.1 of the License only. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at http://secondlife.com/developers/opensource/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llrender/llglheaders.h b/indra/llrender/llglheaders.h new file mode 100644 index 0000000000..5a34b46d0c --- /dev/null +++ b/indra/llrender/llglheaders.h @@ -0,0 +1,833 @@ +/** + * @file llglheaders.h + * @brief LLGL definitions + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * 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; + * version 2.1 of the License only. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLGLHEADERS_H +#define LL_LLGLHEADERS_H + +#if LL_SOLARIS +# if defined(__sparc) +# define I_NEED_OS2_H // avoiding BOOL conflicts +# endif +# include "GL/gl.h" +# if defined(__sparc) +# undef I_NEED_OS2_H +# ifdef BOOL +# undef BOOL // now get rid of Xmd.h crap +# endif +# endif +# include "GL/glx.h" +# define GL_GLEXT_PROTOTYPES 1 +# include "GL/glext.h" +# include "GL/glu.h" +# include "GL/glx.h" +# define GLX_GLXEXT_PROTOTYPES 1 +# include "GL/glxext.h" +//# define GLH_EXT_GET_PROC_ADDRESS(p) glXGetProcAddressARB((const GLubyte*)(p)) +# define GLH_EXT_GET_PROC_ADDRESS(p) glXGetProcAddress((const GLubyte*)(p)) + +// The __APPLE__ kludge is to make glh_extensions.h not symbol-clash horribly +// This header is distributed with SL. You'll find it in linden/libraries/include/GL/ +# define __APPLE__ +# include "GL/glh_extensions.h" +# undef __APPLE__ + + +// GL_ARB_vertex_buffer_object +extern PFNGLBINDBUFFERARBPROC glBindBufferARB; +extern PFNGLDELETEBUFFERSARBPROC glDeleteBuffersARB; +extern PFNGLGENBUFFERSARBPROC glGenBuffersARB; +extern PFNGLISBUFFERARBPROC glIsBufferARB; +extern PFNGLBUFFERDATAARBPROC glBufferDataARB; +extern PFNGLBUFFERSUBDATAARBPROC glBufferSubDataARB; +extern PFNGLGETBUFFERSUBDATAARBPROC glGetBufferSubDataARB; +extern PFNGLMAPBUFFERARBPROC glMapBufferARB; +extern PFNGLUNMAPBUFFERARBPROC glUnmapBufferARB; +extern PFNGLGETBUFFERPARAMETERIVARBPROC glGetBufferParameterivARB; +extern PFNGLGETBUFFERPOINTERVARBPROC glGetBufferPointervARB; + +// GL_ATI_vertex_array_object +extern PFNGLNEWOBJECTBUFFERATIPROC glNewObjectBufferATI; +extern PFNGLISOBJECTBUFFERATIPROC glIsObjectBufferATI; +extern PFNGLUPDATEOBJECTBUFFERATIPROC glUpdateObjectBufferATI; +extern PFNGLGETOBJECTBUFFERFVATIPROC glGetObjectBufferfvATI; +extern PFNGLGETOBJECTBUFFERIVATIPROC glGetObjectBufferivATI; +extern PFNGLFREEOBJECTBUFFERATIPROC glFreeObjectBufferATI; +extern PFNGLARRAYOBJECTATIPROC glArrayObjectATI; +extern PFNGLVERTEXATTRIBARRAYOBJECTATIPROC glVertexAttribArrayObjectATI; +extern PFNGLGETARRAYOBJECTFVATIPROC glGetArrayObjectfvATI; +extern PFNGLGETARRAYOBJECTIVATIPROC glGetArrayObjectivATI; +extern PFNGLVARIANTARRAYOBJECTATIPROC glVariantObjectArrayATI; +extern PFNGLGETVARIANTARRAYOBJECTFVATIPROC glGetVariantArrayObjectfvATI; +extern PFNGLGETVARIANTARRAYOBJECTIVATIPROC glGetVariantArrayObjectivATI; + +// GL_ARB_occlusion_query +extern PFNGLGENQUERIESARBPROC glGenQueriesARB; +extern PFNGLDELETEQUERIESARBPROC glDeleteQueriesARB; +extern PFNGLISQUERYARBPROC glIsQueryARB; +extern PFNGLBEGINQUERYARBPROC glBeginQueryARB; +extern PFNGLENDQUERYARBPROC glEndQueryARB; +extern PFNGLGETQUERYIVARBPROC glGetQueryivARB; +extern PFNGLGETQUERYOBJECTIVARBPROC glGetQueryObjectivARB; +extern PFNGLGETQUERYOBJECTUIVARBPROC glGetQueryObjectuivARB; + +// GL_ARB_point_parameters +extern PFNGLPOINTPARAMETERFARBPROC glPointParameterfARB; +extern PFNGLPOINTPARAMETERFVARBPROC glPointParameterfvARB; + +// GL_ARB_shader_objects +extern PFNGLDELETEOBJECTARBPROC glDeleteObjectARB; +extern PFNGLGETHANDLEARBPROC glGetHandleARB; +extern PFNGLDETACHOBJECTARBPROC glDetachObjectARB; +extern PFNGLCREATESHADEROBJECTARBPROC glCreateShaderObjectARB; +extern PFNGLSHADERSOURCEARBPROC glShaderSourceARB; +extern PFNGLCOMPILESHADERARBPROC glCompileShaderARB; +extern PFNGLCREATEPROGRAMOBJECTARBPROC glCreateProgramObjectARB; +extern PFNGLATTACHOBJECTARBPROC glAttachObjectARB; +extern PFNGLLINKPROGRAMARBPROC glLinkProgramARB; +extern PFNGLUSEPROGRAMOBJECTARBPROC glUseProgramObjectARB; +extern PFNGLVALIDATEPROGRAMARBPROC glValidateProgramARB; +extern PFNGLUNIFORM1FARBPROC glUniform1fARB; +extern PFNGLUNIFORM2FARBPROC glUniform2fARB; +extern PFNGLUNIFORM3FARBPROC glUniform3fARB; +extern PFNGLUNIFORM4FARBPROC glUniform4fARB; +extern PFNGLUNIFORM1IARBPROC glUniform1iARB; +extern PFNGLUNIFORM2IARBPROC glUniform2iARB; +extern PFNGLUNIFORM3IARBPROC glUniform3iARB; +extern PFNGLUNIFORM4IARBPROC glUniform4iARB; +extern PFNGLUNIFORM1FVARBPROC glUniform1fvARB; +extern PFNGLUNIFORM2FVARBPROC glUniform2fvARB; +extern PFNGLUNIFORM3FVARBPROC glUniform3fvARB; +extern PFNGLUNIFORM4FVARBPROC glUniform4fvARB; +extern PFNGLUNIFORM1IVARBPROC glUniform1ivARB; +extern PFNGLUNIFORM2IVARBPROC glUniform2ivARB; +extern PFNGLUNIFORM3IVARBPROC glUniform3ivARB; +extern PFNGLUNIFORM4IVARBPROC glUniform4ivARB; +extern PFNGLUNIFORMMATRIX2FVARBPROC glUniformMatrix2fvARB; +extern PFNGLUNIFORMMATRIX3FVARBPROC glUniformMatrix3fvARB; +extern PFNGLUNIFORMMATRIX4FVARBPROC glUniformMatrix4fvARB; +extern PFNGLGETOBJECTPARAMETERFVARBPROC glGetObjectParameterfvARB; +extern PFNGLGETOBJECTPARAMETERIVARBPROC glGetObjectParameterivARB; +extern PFNGLGETINFOLOGARBPROC glGetInfoLogARB; +extern PFNGLGETATTACHEDOBJECTSARBPROC glGetAttachedObjectsARB; +extern PFNGLGETUNIFORMLOCATIONARBPROC glGetUniformLocationARB; +extern PFNGLGETACTIVEUNIFORMARBPROC glGetActiveUniformARB; +extern PFNGLGETUNIFORMFVARBPROC glGetUniformfvARB; +extern PFNGLGETUNIFORMIVARBPROC glGetUniformivARB; +extern PFNGLGETSHADERSOURCEARBPROC glGetShaderSourceARB; + +// GL_ARB_vertex_shader +extern PFNGLVERTEXATTRIB1DARBPROC glVertexAttrib1dARB; +extern PFNGLVERTEXATTRIB1DVARBPROC glVertexAttrib1dvARB; +extern PFNGLVERTEXATTRIB1FARBPROC glVertexAttrib1fARB; +extern PFNGLVERTEXATTRIB1FVARBPROC glVertexAttrib1fvARB; +extern PFNGLVERTEXATTRIB1SARBPROC glVertexAttrib1sARB; +extern PFNGLVERTEXATTRIB1SVARBPROC glVertexAttrib1svARB; +extern PFNGLVERTEXATTRIB2DARBPROC glVertexAttrib2dARB; +extern PFNGLVERTEXATTRIB2DVARBPROC glVertexAttrib2dvARB; +extern PFNGLVERTEXATTRIB2FARBPROC glVertexAttrib2fARB; +extern PFNGLVERTEXATTRIB2FVARBPROC glVertexAttrib2fvARB; +extern PFNGLVERTEXATTRIB2SARBPROC glVertexAttrib2sARB; +extern PFNGLVERTEXATTRIB2SVARBPROC glVertexAttrib2svARB; +extern PFNGLVERTEXATTRIB3DARBPROC glVertexAttrib3dARB; +extern PFNGLVERTEXATTRIB3DVARBPROC glVertexAttrib3dvARB; +extern PFNGLVERTEXATTRIB3FARBPROC glVertexAttrib3fARB; +extern PFNGLVERTEXATTRIB3FVARBPROC glVertexAttrib3fvARB; +extern PFNGLVERTEXATTRIB3SARBPROC glVertexAttrib3sARB; +extern PFNGLVERTEXATTRIB3SVARBPROC glVertexAttrib3svARB; +extern PFNGLVERTEXATTRIB4NBVARBPROC glVertexAttrib4nbvARB; +extern PFNGLVERTEXATTRIB4NIVARBPROC glVertexAttrib4nivARB; +extern PFNGLVERTEXATTRIB4NSVARBPROC glVertexAttrib4nsvARB; +extern PFNGLVERTEXATTRIB4NUBARBPROC glVertexAttrib4nubARB; +extern PFNGLVERTEXATTRIB4NUBVARBPROC glVertexAttrib4nubvARB; +extern PFNGLVERTEXATTRIB4NUIVARBPROC glVertexAttrib4nuivARB; +extern PFNGLVERTEXATTRIB4NUSVARBPROC glVertexAttrib4nusvARB; +extern PFNGLVERTEXATTRIB4BVARBPROC glVertexAttrib4bvARB; +extern PFNGLVERTEXATTRIB4DARBPROC glVertexAttrib4dARB; +extern PFNGLVERTEXATTRIB4DVARBPROC glVertexAttrib4dvARB; +extern PFNGLVERTEXATTRIB4FARBPROC glVertexAttrib4fARB; +extern PFNGLVERTEXATTRIB4FVARBPROC glVertexAttrib4fvARB; +extern PFNGLVERTEXATTRIB4IVARBPROC glVertexAttrib4ivARB; +extern PFNGLVERTEXATTRIB4SARBPROC glVertexAttrib4sARB; +extern PFNGLVERTEXATTRIB4SVARBPROC glVertexAttrib4svARB; +extern PFNGLVERTEXATTRIB4UBVARBPROC glVertexAttrib4ubvARB; +extern PFNGLVERTEXATTRIB4UIVARBPROC glVertexAttrib4uivARB; +extern PFNGLVERTEXATTRIB4USVARBPROC glVertexAttrib4usvARB; +extern PFNGLVERTEXATTRIBPOINTERARBPROC glVertexAttribPointerARB; +extern PFNGLENABLEVERTEXATTRIBARRAYARBPROC glEnableVertexAttribArrayARB; +extern PFNGLDISABLEVERTEXATTRIBARRAYARBPROC glDisableVertexAttribArrayARB; +extern PFNGLPROGRAMSTRINGARBPROC glProgramStringARB; +extern PFNGLBINDPROGRAMARBPROC glBindProgramARB; +extern PFNGLDELETEPROGRAMSARBPROC glDeleteProgramsARB; +extern PFNGLGENPROGRAMSARBPROC glGenProgramsARB; +extern PFNGLPROGRAMENVPARAMETER4DARBPROC glProgramEnvParameter4dARB; +extern PFNGLPROGRAMENVPARAMETER4DVARBPROC glProgramEnvParameter4dvARB; +extern PFNGLPROGRAMENVPARAMETER4FARBPROC glProgramEnvParameter4fARB; +extern PFNGLPROGRAMENVPARAMETER4FVARBPROC glProgramEnvParameter4fvARB; +extern PFNGLPROGRAMLOCALPARAMETER4DARBPROC glProgramLocalParameter4dARB; +extern PFNGLPROGRAMLOCALPARAMETER4DVARBPROC glProgramLocalParameter4dvARB; +extern PFNGLPROGRAMLOCALPARAMETER4FARBPROC glProgramLocalParameter4fARB; +extern PFNGLPROGRAMLOCALPARAMETER4FVARBPROC glProgramLocalParameter4fvARB; +extern PFNGLGETPROGRAMENVPARAMETERDVARBPROC glGetProgramEnvParameterdvARB; +extern PFNGLGETPROGRAMENVPARAMETERFVARBPROC glGetProgramEnvParameterfvARB; +extern PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC glGetProgramLocalParameterdvARB; +extern PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC glGetProgramLocalParameterfvARB; +extern PFNGLGETPROGRAMIVARBPROC glGetProgramivARB; +extern PFNGLGETPROGRAMSTRINGARBPROC glGetProgramStringARB; +extern PFNGLGETVERTEXATTRIBDVARBPROC glGetVertexAttribdvARB; +extern PFNGLGETVERTEXATTRIBFVARBPROC glGetVertexAttribfvARB; +extern PFNGLGETVERTEXATTRIBIVARBPROC glGetVertexAttribivARB; +extern PFNGLGETVERTEXATTRIBPOINTERVARBPROC glGetVertexAttribPointervARB; +extern PFNGLISPROGRAMARBPROC glIsProgramARB; +extern PFNGLBINDATTRIBLOCATIONARBPROC glBindAttribLocationARB; +extern PFNGLGETACTIVEATTRIBARBPROC glGetActiveAttribARB; +extern PFNGLGETATTRIBLOCATIONARBPROC glGetAttribLocationARB; + +extern PFNGLCOMPRESSEDTEXIMAGE2DARBPROC glCompressedTexImage2DARB; +extern PFNGLGETCOMPRESSEDTEXIMAGEARBPROC glGetCompressedTexImageARB; + +extern PFNGLCOLORTABLEEXTPROC glColorTableEXT; + +//GL_EXT_blend_func_separate +extern PFNGLBLENDFUNCSEPARATEEXTPROC glBlendFuncSeparateEXT; + +//GL_EXT_framebuffer_object +extern PFNGLISRENDERBUFFEREXTPROC glIsRenderbufferEXT; +extern PFNGLBINDRENDERBUFFEREXTPROC glBindRenderbufferEXT; +extern PFNGLDELETERENDERBUFFERSEXTPROC glDeleteRenderbuffersEXT; +extern PFNGLGENRENDERBUFFERSEXTPROC glGenRenderbuffersEXT; +extern PFNGLRENDERBUFFERSTORAGEEXTPROC glRenderbufferStorageEXT; +extern PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC glGetRenderbufferParameterivEXT; +extern PFNGLISFRAMEBUFFEREXTPROC glIsFramebufferEXT; +extern PFNGLBINDFRAMEBUFFEREXTPROC glBindFramebufferEXT; +extern PFNGLDELETEFRAMEBUFFERSEXTPROC glDeleteFramebuffersEXT; +extern PFNGLGENFRAMEBUFFERSEXTPROC glGenFramebuffersEXT; +extern PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC glCheckFramebufferStatusEXT; +extern PFNGLFRAMEBUFFERTEXTURE1DEXTPROC glFramebufferTexture1DEXT; +extern PFNGLFRAMEBUFFERTEXTURE2DEXTPROC glFramebufferTexture2DEXT; +extern PFNGLFRAMEBUFFERTEXTURE3DEXTPROC glFramebufferTexture3DEXT; +extern PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC glFramebufferRenderbufferEXT; +extern PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC glGetFramebufferAttachmentParameterivEXT; +extern PFNGLGENERATEMIPMAPEXTPROC glGenerateMipmapEXT; + +#elif LL_MESA +//---------------------------------------------------------------------------- +// MESA headers +// quotes so we get libraries/.../GL/ version +#define GL_GLEXT_PROTOTYPES 1 +#include "GL/gl.h" +#include "GL/glext.h" +#include "GL/glu.h" + +// The __APPLE__ kludge is to make glh_extensions.h not symbol-clash horribly +# define __APPLE__ +# include "GL/glh_extensions.h" +# undef __APPLE__ + +#elif LL_LINUX +//---------------------------------------------------------------------------- +// LL_LINUX + +//---------------------------------------------------------------------------- +// Linux, MESA headers, but not necessarily assuming MESA runtime. +// quotes so we get libraries/.../GL/ version +#include "GL/gl.h" +#include "GL/glext.h" +#include "GL/glu.h" + + +#if LL_LINUX && !LL_MESA_HEADLESS +// The __APPLE__ kludge is to make glh_extensions.h not symbol-clash horribly +# define __APPLE__ +# include "GL/glh_extensions.h" +# undef __APPLE__ + +/* Although SDL very likely ends up calling glXGetProcAddress() itself, + if we use SDL_GL_GetProcAddress() then we get bogus addresses back on + some systems. Weird. */ +/*# include "SDL/SDL.h" + # define GLH_EXT_GET_PROC_ADDRESS(p) SDL_GL_GetProcAddress(p) */ +#define GLX_GLXEXT_PROTOTYPES 1 +# include "GL/glx.h" +# include "GL/glxext.h" +// Use glXGetProcAddressARB instead of glXGetProcAddress - the ARB symbol +// is considered 'legacy' but works on more machines. +# define GLH_EXT_GET_PROC_ADDRESS(p) glXGetProcAddressARB((const GLubyte*)(p)) +#endif // LL_LINUX && !LL_MESA_HEADLESS + +#if LL_LINUX && defined(WINGDIAPI) +// WINGDIAPI gets set if we are using the linux nvidia gl.h header which needs +// the functions below setting up. +# define LL_LINUX_NV_GL_HEADERS 1 +#else +# define LL_LINUX_NV_GL_HEADERS 0 +#endif // LL_LINUX && defined(WINGDIAPI) + + +#if LL_LINUX_NV_GL_HEADERS +// Missing functions when using nvidia headers: +extern PFNGLACTIVETEXTUREARBPROC glActiveTextureARB; +extern PFNGLCLIENTACTIVETEXTUREARBPROC glClientActiveTextureARB; +extern PFNGLDRAWRANGEELEMENTSPROC glDrawRangeElements; +#endif // LL_LINUX_NV_GL_HEADERS + +// GL_ARB_vertex_buffer_object +extern PFNGLBINDBUFFERARBPROC glBindBufferARB; +extern PFNGLDELETEBUFFERSARBPROC glDeleteBuffersARB; +extern PFNGLGENBUFFERSARBPROC glGenBuffersARB; +extern PFNGLISBUFFERARBPROC glIsBufferARB; +extern PFNGLBUFFERDATAARBPROC glBufferDataARB; +extern PFNGLBUFFERSUBDATAARBPROC glBufferSubDataARB; +extern PFNGLGETBUFFERSUBDATAARBPROC glGetBufferSubDataARB; +extern PFNGLMAPBUFFERARBPROC glMapBufferARB; +extern PFNGLUNMAPBUFFERARBPROC glUnmapBufferARB; +extern PFNGLGETBUFFERPARAMETERIVARBPROC glGetBufferParameterivARB; +extern PFNGLGETBUFFERPOINTERVARBPROC glGetBufferPointervARB; + +// GL_ATI_vertex_array_object +extern PFNGLNEWOBJECTBUFFERATIPROC glNewObjectBufferATI; +extern PFNGLISOBJECTBUFFERATIPROC glIsObjectBufferATI; +extern PFNGLUPDATEOBJECTBUFFERATIPROC glUpdateObjectBufferATI; +extern PFNGLGETOBJECTBUFFERFVATIPROC glGetObjectBufferfvATI; +extern PFNGLGETOBJECTBUFFERIVATIPROC glGetObjectBufferivATI; +extern PFNGLFREEOBJECTBUFFERATIPROC glFreeObjectBufferATI; +extern PFNGLARRAYOBJECTATIPROC glArrayObjectATI; +extern PFNGLVERTEXATTRIBARRAYOBJECTATIPROC glVertexAttribArrayObjectATI; +extern PFNGLGETARRAYOBJECTFVATIPROC glGetArrayObjectfvATI; +extern PFNGLGETARRAYOBJECTIVATIPROC glGetArrayObjectivATI; +extern PFNGLVARIANTARRAYOBJECTATIPROC glVariantObjectArrayATI; +extern PFNGLGETVARIANTARRAYOBJECTFVATIPROC glGetVariantArrayObjectfvATI; +extern PFNGLGETVARIANTARRAYOBJECTIVATIPROC glGetVariantArrayObjectivATI; + +// GL_ARB_occlusion_query +extern PFNGLGENQUERIESARBPROC glGenQueriesARB; +extern PFNGLDELETEQUERIESARBPROC glDeleteQueriesARB; +extern PFNGLISQUERYARBPROC glIsQueryARB; +extern PFNGLBEGINQUERYARBPROC glBeginQueryARB; +extern PFNGLENDQUERYARBPROC glEndQueryARB; +extern PFNGLGETQUERYIVARBPROC glGetQueryivARB; +extern PFNGLGETQUERYOBJECTIVARBPROC glGetQueryObjectivARB; +extern PFNGLGETQUERYOBJECTUIVARBPROC glGetQueryObjectuivARB; + +// GL_ARB_point_parameters +extern PFNGLPOINTPARAMETERFARBPROC glPointParameterfARB; +extern PFNGLPOINTPARAMETERFVARBPROC glPointParameterfvARB; + +// GL_ARB_shader_objects +extern PFNGLDELETEOBJECTARBPROC glDeleteObjectARB; +extern PFNGLGETHANDLEARBPROC glGetHandleARB; +extern PFNGLDETACHOBJECTARBPROC glDetachObjectARB; +extern PFNGLCREATESHADEROBJECTARBPROC glCreateShaderObjectARB; +extern PFNGLSHADERSOURCEARBPROC glShaderSourceARB; +extern PFNGLCOMPILESHADERARBPROC glCompileShaderARB; +extern PFNGLCREATEPROGRAMOBJECTARBPROC glCreateProgramObjectARB; +extern PFNGLATTACHOBJECTARBPROC glAttachObjectARB; +extern PFNGLLINKPROGRAMARBPROC glLinkProgramARB; +extern PFNGLUSEPROGRAMOBJECTARBPROC glUseProgramObjectARB; +extern PFNGLVALIDATEPROGRAMARBPROC glValidateProgramARB; +extern PFNGLUNIFORM1FARBPROC glUniform1fARB; +extern PFNGLUNIFORM2FARBPROC glUniform2fARB; +extern PFNGLUNIFORM3FARBPROC glUniform3fARB; +extern PFNGLUNIFORM4FARBPROC glUniform4fARB; +extern PFNGLUNIFORM1IARBPROC glUniform1iARB; +extern PFNGLUNIFORM2IARBPROC glUniform2iARB; +extern PFNGLUNIFORM3IARBPROC glUniform3iARB; +extern PFNGLUNIFORM4IARBPROC glUniform4iARB; +extern PFNGLUNIFORM1FVARBPROC glUniform1fvARB; +extern PFNGLUNIFORM2FVARBPROC glUniform2fvARB; +extern PFNGLUNIFORM3FVARBPROC glUniform3fvARB; +extern PFNGLUNIFORM4FVARBPROC glUniform4fvARB; +extern PFNGLUNIFORM1IVARBPROC glUniform1ivARB; +extern PFNGLUNIFORM2IVARBPROC glUniform2ivARB; +extern PFNGLUNIFORM3IVARBPROC glUniform3ivARB; +extern PFNGLUNIFORM4IVARBPROC glUniform4ivARB; +extern PFNGLUNIFORMMATRIX2FVARBPROC glUniformMatrix2fvARB; +extern PFNGLUNIFORMMATRIX3FVARBPROC glUniformMatrix3fvARB; +extern PFNGLUNIFORMMATRIX4FVARBPROC glUniformMatrix4fvARB; +extern PFNGLGETOBJECTPARAMETERFVARBPROC glGetObjectParameterfvARB; +extern PFNGLGETOBJECTPARAMETERIVARBPROC glGetObjectParameterivARB; +extern PFNGLGETINFOLOGARBPROC glGetInfoLogARB; +extern PFNGLGETATTACHEDOBJECTSARBPROC glGetAttachedObjectsARB; +extern PFNGLGETUNIFORMLOCATIONARBPROC glGetUniformLocationARB; +extern PFNGLGETACTIVEUNIFORMARBPROC glGetActiveUniformARB; +extern PFNGLGETUNIFORMFVARBPROC glGetUniformfvARB; +extern PFNGLGETUNIFORMIVARBPROC glGetUniformivARB; +extern PFNGLGETSHADERSOURCEARBPROC glGetShaderSourceARB; + +// GL_ARB_vertex_shader +extern PFNGLVERTEXATTRIB1DARBPROC glVertexAttrib1dARB; +extern PFNGLVERTEXATTRIB1DVARBPROC glVertexAttrib1dvARB; +extern PFNGLVERTEXATTRIB1FARBPROC glVertexAttrib1fARB; +extern PFNGLVERTEXATTRIB1FVARBPROC glVertexAttrib1fvARB; +extern PFNGLVERTEXATTRIB1SARBPROC glVertexAttrib1sARB; +extern PFNGLVERTEXATTRIB1SVARBPROC glVertexAttrib1svARB; +extern PFNGLVERTEXATTRIB2DARBPROC glVertexAttrib2dARB; +extern PFNGLVERTEXATTRIB2DVARBPROC glVertexAttrib2dvARB; +extern PFNGLVERTEXATTRIB2FARBPROC glVertexAttrib2fARB; +extern PFNGLVERTEXATTRIB2FVARBPROC glVertexAttrib2fvARB; +extern PFNGLVERTEXATTRIB2SARBPROC glVertexAttrib2sARB; +extern PFNGLVERTEXATTRIB2SVARBPROC glVertexAttrib2svARB; +extern PFNGLVERTEXATTRIB3DARBPROC glVertexAttrib3dARB; +extern PFNGLVERTEXATTRIB3DVARBPROC glVertexAttrib3dvARB; +extern PFNGLVERTEXATTRIB3FARBPROC glVertexAttrib3fARB; +extern PFNGLVERTEXATTRIB3FVARBPROC glVertexAttrib3fvARB; +extern PFNGLVERTEXATTRIB3SARBPROC glVertexAttrib3sARB; +extern PFNGLVERTEXATTRIB3SVARBPROC glVertexAttrib3svARB; +extern PFNGLVERTEXATTRIB4NBVARBPROC glVertexAttrib4nbvARB; +extern PFNGLVERTEXATTRIB4NIVARBPROC glVertexAttrib4nivARB; +extern PFNGLVERTEXATTRIB4NSVARBPROC glVertexAttrib4nsvARB; +extern PFNGLVERTEXATTRIB4NUBARBPROC glVertexAttrib4nubARB; +extern PFNGLVERTEXATTRIB4NUBVARBPROC glVertexAttrib4nubvARB; +extern PFNGLVERTEXATTRIB4NUIVARBPROC glVertexAttrib4nuivARB; +extern PFNGLVERTEXATTRIB4NUSVARBPROC glVertexAttrib4nusvARB; +extern PFNGLVERTEXATTRIB4BVARBPROC glVertexAttrib4bvARB; +extern PFNGLVERTEXATTRIB4DARBPROC glVertexAttrib4dARB; +extern PFNGLVERTEXATTRIB4DVARBPROC glVertexAttrib4dvARB; +extern PFNGLVERTEXATTRIB4FARBPROC glVertexAttrib4fARB; +extern PFNGLVERTEXATTRIB4FVARBPROC glVertexAttrib4fvARB; +extern PFNGLVERTEXATTRIB4IVARBPROC glVertexAttrib4ivARB; +extern PFNGLVERTEXATTRIB4SARBPROC glVertexAttrib4sARB; +extern PFNGLVERTEXATTRIB4SVARBPROC glVertexAttrib4svARB; +extern PFNGLVERTEXATTRIB4UBVARBPROC glVertexAttrib4ubvARB; +extern PFNGLVERTEXATTRIB4UIVARBPROC glVertexAttrib4uivARB; +extern PFNGLVERTEXATTRIB4USVARBPROC glVertexAttrib4usvARB; +extern PFNGLVERTEXATTRIBPOINTERARBPROC glVertexAttribPointerARB; +extern PFNGLENABLEVERTEXATTRIBARRAYARBPROC glEnableVertexAttribArrayARB; +extern PFNGLDISABLEVERTEXATTRIBARRAYARBPROC glDisableVertexAttribArrayARB; +extern PFNGLPROGRAMSTRINGARBPROC glProgramStringARB; +extern PFNGLBINDPROGRAMARBPROC glBindProgramARB; +extern PFNGLDELETEPROGRAMSARBPROC glDeleteProgramsARB; +extern PFNGLGENPROGRAMSARBPROC glGenProgramsARB; +extern PFNGLPROGRAMENVPARAMETER4DARBPROC glProgramEnvParameter4dARB; +extern PFNGLPROGRAMENVPARAMETER4DVARBPROC glProgramEnvParameter4dvARB; +extern PFNGLPROGRAMENVPARAMETER4FARBPROC glProgramEnvParameter4fARB; +extern PFNGLPROGRAMENVPARAMETER4FVARBPROC glProgramEnvParameter4fvARB; +extern PFNGLPROGRAMLOCALPARAMETER4DARBPROC glProgramLocalParameter4dARB; +extern PFNGLPROGRAMLOCALPARAMETER4DVARBPROC glProgramLocalParameter4dvARB; +extern PFNGLPROGRAMLOCALPARAMETER4FARBPROC glProgramLocalParameter4fARB; +extern PFNGLPROGRAMLOCALPARAMETER4FVARBPROC glProgramLocalParameter4fvARB; +extern PFNGLGETPROGRAMENVPARAMETERDVARBPROC glGetProgramEnvParameterdvARB; +extern PFNGLGETPROGRAMENVPARAMETERFVARBPROC glGetProgramEnvParameterfvARB; +extern PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC glGetProgramLocalParameterdvARB; +extern PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC glGetProgramLocalParameterfvARB; +extern PFNGLGETPROGRAMIVARBPROC glGetProgramivARB; +extern PFNGLGETPROGRAMSTRINGARBPROC glGetProgramStringARB; +extern PFNGLGETVERTEXATTRIBDVARBPROC glGetVertexAttribdvARB; +extern PFNGLGETVERTEXATTRIBFVARBPROC glGetVertexAttribfvARB; +extern PFNGLGETVERTEXATTRIBIVARBPROC glGetVertexAttribivARB; +extern PFNGLGETVERTEXATTRIBPOINTERVARBPROC glGetVertexAttribPointervARB; +extern PFNGLISPROGRAMARBPROC glIsProgramARB; +extern PFNGLBINDATTRIBLOCATIONARBPROC glBindAttribLocationARB; +extern PFNGLGETACTIVEATTRIBARBPROC glGetActiveAttribARB; +extern PFNGLGETATTRIBLOCATIONARBPROC glGetAttribLocationARB; + +extern PFNGLCOMPRESSEDTEXIMAGE2DARBPROC glCompressedTexImage2DARB; +extern PFNGLGETCOMPRESSEDTEXIMAGEARBPROC glGetCompressedTexImageARB; + +//GL_EXT_blend_func_separate +extern PFNGLBLENDFUNCSEPARATEEXTPROC glBlendFuncSeparateEXT; + +//GL_EXT_framebuffer_object +extern PFNGLISRENDERBUFFEREXTPROC glIsRenderbufferEXT; +extern PFNGLBINDRENDERBUFFEREXTPROC glBindRenderbufferEXT; +extern PFNGLDELETERENDERBUFFERSEXTPROC glDeleteRenderbuffersEXT; +extern PFNGLGENRENDERBUFFERSEXTPROC glGenRenderbuffersEXT; +extern PFNGLRENDERBUFFERSTORAGEEXTPROC glRenderbufferStorageEXT; +extern PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC glGetRenderbufferParameterivEXT; +extern PFNGLISFRAMEBUFFEREXTPROC glIsFramebufferEXT; +extern PFNGLBINDFRAMEBUFFEREXTPROC glBindFramebufferEXT; +extern PFNGLDELETEFRAMEBUFFERSEXTPROC glDeleteFramebuffersEXT; +extern PFNGLGENFRAMEBUFFERSEXTPROC glGenFramebuffersEXT; +extern PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC glCheckFramebufferStatusEXT; +extern PFNGLFRAMEBUFFERTEXTURE1DEXTPROC glFramebufferTexture1DEXT; +extern PFNGLFRAMEBUFFERTEXTURE2DEXTPROC glFramebufferTexture2DEXT; +extern PFNGLFRAMEBUFFERTEXTURE3DEXTPROC glFramebufferTexture3DEXT; +extern PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC glFramebufferRenderbufferEXT; +extern PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC glGetFramebufferAttachmentParameterivEXT; +extern PFNGLGENERATEMIPMAPEXTPROC glGenerateMipmapEXT; + +// GL_EXT_framebuffer_multisample +extern PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glRenderbufferStorageMultisampleEXT; + +// GL_EXT_framebuffer_blit +extern PFNGLBLITFRAMEBUFFEREXTPROC glBlitFramebufferEXT; + +//GL_ARB_draw_buffers +extern PFNGLDRAWBUFFERSARBPROC glDrawBuffersARB; + + +#elif LL_WINDOWS +//---------------------------------------------------------------------------- +// LL_WINDOWS + +// windows gl headers depend on things like APIENTRY, so include windows. +#define WIN32_LEAN_AND_MEAN +#include <winsock2.h> +#include <windows.h> + +//---------------------------------------------------------------------------- +#include <GL/gl.h> +#include <GL/glu.h> + +// quotes so we get libraries/.../GL/ version +#include "GL/glext.h" +#include "GL/glh_extensions.h" + + +// GL_ARB_vertex_buffer_object +extern PFNGLBINDBUFFERARBPROC glBindBufferARB; +extern PFNGLDELETEBUFFERSARBPROC glDeleteBuffersARB; +extern PFNGLGENBUFFERSARBPROC glGenBuffersARB; +extern PFNGLISBUFFERARBPROC glIsBufferARB; +extern PFNGLBUFFERDATAARBPROC glBufferDataARB; +extern PFNGLBUFFERSUBDATAARBPROC glBufferSubDataARB; +extern PFNGLGETBUFFERSUBDATAARBPROC glGetBufferSubDataARB; +extern PFNGLMAPBUFFERARBPROC glMapBufferARB; +extern PFNGLUNMAPBUFFERARBPROC glUnmapBufferARB; +extern PFNGLGETBUFFERPARAMETERIVARBPROC glGetBufferParameterivARB; +extern PFNGLGETBUFFERPOINTERVARBPROC glGetBufferPointervARB; + +// GL_ATI_vertex_array_object +extern PFNGLNEWOBJECTBUFFERATIPROC glNewObjectBufferATI; +extern PFNGLISOBJECTBUFFERATIPROC glIsObjectBufferATI; +extern PFNGLUPDATEOBJECTBUFFERATIPROC glUpdateObjectBufferATI; +extern PFNGLGETOBJECTBUFFERFVATIPROC glGetObjectBufferfvATI; +extern PFNGLGETOBJECTBUFFERIVATIPROC glGetObjectBufferivATI; +extern PFNGLFREEOBJECTBUFFERATIPROC glFreeObjectBufferATI; +extern PFNGLARRAYOBJECTATIPROC glArrayObjectATI; +extern PFNGLVERTEXATTRIBARRAYOBJECTATIPROC glVertexAttribArrayObjectATI; +extern PFNGLGETARRAYOBJECTFVATIPROC glGetArrayObjectfvATI; +extern PFNGLGETARRAYOBJECTIVATIPROC glGetArrayObjectivATI; +extern PFNGLVARIANTARRAYOBJECTATIPROC glVariantObjectArrayATI; +extern PFNGLGETVARIANTARRAYOBJECTFVATIPROC glGetVariantArrayObjectfvATI; +extern PFNGLGETVARIANTARRAYOBJECTIVATIPROC glGetVariantArrayObjectivATI; + +extern PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT; + +// GL_ARB_occlusion_query +extern PFNGLGENQUERIESARBPROC glGenQueriesARB; +extern PFNGLDELETEQUERIESARBPROC glDeleteQueriesARB; +extern PFNGLISQUERYARBPROC glIsQueryARB; +extern PFNGLBEGINQUERYARBPROC glBeginQueryARB; +extern PFNGLENDQUERYARBPROC glEndQueryARB; +extern PFNGLGETQUERYIVARBPROC glGetQueryivARB; +extern PFNGLGETQUERYOBJECTIVARBPROC glGetQueryObjectivARB; +extern PFNGLGETQUERYOBJECTUIVARBPROC glGetQueryObjectuivARB; + +// GL_ARB_point_parameters +extern PFNGLPOINTPARAMETERFARBPROC glPointParameterfARB; +extern PFNGLPOINTPARAMETERFVARBPROC glPointParameterfvARB; + +// GL_ARB_shader_objects +extern PFNGLDELETEOBJECTARBPROC glDeleteObjectARB; +extern PFNGLGETHANDLEARBPROC glGetHandleARB; +extern PFNGLDETACHOBJECTARBPROC glDetachObjectARB; +extern PFNGLCREATESHADEROBJECTARBPROC glCreateShaderObjectARB; +extern PFNGLSHADERSOURCEARBPROC glShaderSourceARB; +extern PFNGLCOMPILESHADERARBPROC glCompileShaderARB; +extern PFNGLCREATEPROGRAMOBJECTARBPROC glCreateProgramObjectARB; +extern PFNGLATTACHOBJECTARBPROC glAttachObjectARB; +extern PFNGLLINKPROGRAMARBPROC glLinkProgramARB; +extern PFNGLUSEPROGRAMOBJECTARBPROC glUseProgramObjectARB; +extern PFNGLVALIDATEPROGRAMARBPROC glValidateProgramARB; +extern PFNGLUNIFORM1FARBPROC glUniform1fARB; +extern PFNGLUNIFORM2FARBPROC glUniform2fARB; +extern PFNGLUNIFORM3FARBPROC glUniform3fARB; +extern PFNGLUNIFORM4FARBPROC glUniform4fARB; +extern PFNGLUNIFORM1IARBPROC glUniform1iARB; +extern PFNGLUNIFORM2IARBPROC glUniform2iARB; +extern PFNGLUNIFORM3IARBPROC glUniform3iARB; +extern PFNGLUNIFORM4IARBPROC glUniform4iARB; +extern PFNGLUNIFORM1FVARBPROC glUniform1fvARB; +extern PFNGLUNIFORM2FVARBPROC glUniform2fvARB; +extern PFNGLUNIFORM3FVARBPROC glUniform3fvARB; +extern PFNGLUNIFORM4FVARBPROC glUniform4fvARB; +extern PFNGLUNIFORM1IVARBPROC glUniform1ivARB; +extern PFNGLUNIFORM2IVARBPROC glUniform2ivARB; +extern PFNGLUNIFORM3IVARBPROC glUniform3ivARB; +extern PFNGLUNIFORM4IVARBPROC glUniform4ivARB; +extern PFNGLUNIFORMMATRIX2FVARBPROC glUniformMatrix2fvARB; +extern PFNGLUNIFORMMATRIX3FVARBPROC glUniformMatrix3fvARB; +extern PFNGLUNIFORMMATRIX4FVARBPROC glUniformMatrix4fvARB; +extern PFNGLGETOBJECTPARAMETERFVARBPROC glGetObjectParameterfvARB; +extern PFNGLGETOBJECTPARAMETERIVARBPROC glGetObjectParameterivARB; +extern PFNGLGETINFOLOGARBPROC glGetInfoLogARB; +extern PFNGLGETATTACHEDOBJECTSARBPROC glGetAttachedObjectsARB; +extern PFNGLGETUNIFORMLOCATIONARBPROC glGetUniformLocationARB; +extern PFNGLGETACTIVEUNIFORMARBPROC glGetActiveUniformARB; +extern PFNGLGETUNIFORMFVARBPROC glGetUniformfvARB; +extern PFNGLGETUNIFORMIVARBPROC glGetUniformivARB; +extern PFNGLGETSHADERSOURCEARBPROC glGetShaderSourceARB; + +// GL_ARB_vertex_shader +extern PFNGLVERTEXATTRIB1DARBPROC glVertexAttrib1dARB; +extern PFNGLVERTEXATTRIB1DVARBPROC glVertexAttrib1dvARB; +extern PFNGLVERTEXATTRIB1FARBPROC glVertexAttrib1fARB; +extern PFNGLVERTEXATTRIB1FVARBPROC glVertexAttrib1fvARB; +extern PFNGLVERTEXATTRIB1SARBPROC glVertexAttrib1sARB; +extern PFNGLVERTEXATTRIB1SVARBPROC glVertexAttrib1svARB; +extern PFNGLVERTEXATTRIB2DARBPROC glVertexAttrib2dARB; +extern PFNGLVERTEXATTRIB2DVARBPROC glVertexAttrib2dvARB; +extern PFNGLVERTEXATTRIB2FARBPROC glVertexAttrib2fARB; +extern PFNGLVERTEXATTRIB2FVARBPROC glVertexAttrib2fvARB; +extern PFNGLVERTEXATTRIB2SARBPROC glVertexAttrib2sARB; +extern PFNGLVERTEXATTRIB2SVARBPROC glVertexAttrib2svARB; +extern PFNGLVERTEXATTRIB3DARBPROC glVertexAttrib3dARB; +extern PFNGLVERTEXATTRIB3DVARBPROC glVertexAttrib3dvARB; +extern PFNGLVERTEXATTRIB3FARBPROC glVertexAttrib3fARB; +extern PFNGLVERTEXATTRIB3FVARBPROC glVertexAttrib3fvARB; +extern PFNGLVERTEXATTRIB3SARBPROC glVertexAttrib3sARB; +extern PFNGLVERTEXATTRIB3SVARBPROC glVertexAttrib3svARB; +extern PFNGLVERTEXATTRIB4NBVARBPROC glVertexAttrib4nbvARB; +extern PFNGLVERTEXATTRIB4NIVARBPROC glVertexAttrib4nivARB; +extern PFNGLVERTEXATTRIB4NSVARBPROC glVertexAttrib4nsvARB; +extern PFNGLVERTEXATTRIB4NUBARBPROC glVertexAttrib4nubARB; +extern PFNGLVERTEXATTRIB4NUBVARBPROC glVertexAttrib4nubvARB; +extern PFNGLVERTEXATTRIB4NUIVARBPROC glVertexAttrib4nuivARB; +extern PFNGLVERTEXATTRIB4NUSVARBPROC glVertexAttrib4nusvARB; +extern PFNGLVERTEXATTRIB4BVARBPROC glVertexAttrib4bvARB; +extern PFNGLVERTEXATTRIB4DARBPROC glVertexAttrib4dARB; +extern PFNGLVERTEXATTRIB4DVARBPROC glVertexAttrib4dvARB; +extern PFNGLVERTEXATTRIB4FARBPROC glVertexAttrib4fARB; +extern PFNGLVERTEXATTRIB4FVARBPROC glVertexAttrib4fvARB; +extern PFNGLVERTEXATTRIB4IVARBPROC glVertexAttrib4ivARB; +extern PFNGLVERTEXATTRIB4SARBPROC glVertexAttrib4sARB; +extern PFNGLVERTEXATTRIB4SVARBPROC glVertexAttrib4svARB; +extern PFNGLVERTEXATTRIB4UBVARBPROC glVertexAttrib4ubvARB; +extern PFNGLVERTEXATTRIB4UIVARBPROC glVertexAttrib4uivARB; +extern PFNGLVERTEXATTRIB4USVARBPROC glVertexAttrib4usvARB; +extern PFNGLVERTEXATTRIBPOINTERARBPROC glVertexAttribPointerARB; +extern PFNGLENABLEVERTEXATTRIBARRAYARBPROC glEnableVertexAttribArrayARB; +extern PFNGLDISABLEVERTEXATTRIBARRAYARBPROC glDisableVertexAttribArrayARB; +extern PFNGLPROGRAMSTRINGARBPROC glProgramStringARB; +extern PFNGLBINDPROGRAMARBPROC glBindProgramARB; +extern PFNGLDELETEPROGRAMSARBPROC glDeleteProgramsARB; +extern PFNGLGENPROGRAMSARBPROC glGenProgramsARB; +extern PFNGLPROGRAMENVPARAMETER4DARBPROC glProgramEnvParameter4dARB; +extern PFNGLPROGRAMENVPARAMETER4DVARBPROC glProgramEnvParameter4dvARB; +extern PFNGLPROGRAMENVPARAMETER4FARBPROC glProgramEnvParameter4fARB; +extern PFNGLPROGRAMENVPARAMETER4FVARBPROC glProgramEnvParameter4fvARB; +extern PFNGLPROGRAMLOCALPARAMETER4DARBPROC glProgramLocalParameter4dARB; +extern PFNGLPROGRAMLOCALPARAMETER4DVARBPROC glProgramLocalParameter4dvARB; +extern PFNGLPROGRAMLOCALPARAMETER4FARBPROC glProgramLocalParameter4fARB; +extern PFNGLPROGRAMLOCALPARAMETER4FVARBPROC glProgramLocalParameter4fvARB; +extern PFNGLGETPROGRAMENVPARAMETERDVARBPROC glGetProgramEnvParameterdvARB; +extern PFNGLGETPROGRAMENVPARAMETERFVARBPROC glGetProgramEnvParameterfvARB; +extern PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC glGetProgramLocalParameterdvARB; +extern PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC glGetProgramLocalParameterfvARB; +extern PFNGLGETPROGRAMIVARBPROC glGetProgramivARB; +extern PFNGLGETPROGRAMSTRINGARBPROC glGetProgramStringARB; +extern PFNGLGETVERTEXATTRIBDVARBPROC glGetVertexAttribdvARB; +extern PFNGLGETVERTEXATTRIBFVARBPROC glGetVertexAttribfvARB; +extern PFNGLGETVERTEXATTRIBIVARBPROC glGetVertexAttribivARB; +extern PFNGLGETVERTEXATTRIBPOINTERVARBPROC glGetVertexAttribPointervARB; +extern PFNGLISPROGRAMARBPROC glIsProgramARB; +extern PFNGLBINDATTRIBLOCATIONARBPROC glBindAttribLocationARB; +extern PFNGLGETACTIVEATTRIBARBPROC glGetActiveAttribARB; +extern PFNGLGETATTRIBLOCATIONARBPROC glGetAttribLocationARB; + +//GL_EXT_blend_func_separate +extern PFNGLBLENDFUNCSEPARATEEXTPROC glBlendFuncSeparateEXT; + +//GL_EXT_framebuffer_object +extern PFNGLISRENDERBUFFEREXTPROC glIsRenderbufferEXT; +extern PFNGLBINDRENDERBUFFEREXTPROC glBindRenderbufferEXT; +extern PFNGLDELETERENDERBUFFERSEXTPROC glDeleteRenderbuffersEXT; +extern PFNGLGENRENDERBUFFERSEXTPROC glGenRenderbuffersEXT; +extern PFNGLRENDERBUFFERSTORAGEEXTPROC glRenderbufferStorageEXT; +extern PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC glGetRenderbufferParameterivEXT; +extern PFNGLISFRAMEBUFFEREXTPROC glIsFramebufferEXT; +extern PFNGLBINDFRAMEBUFFEREXTPROC glBindFramebufferEXT; +extern PFNGLDELETEFRAMEBUFFERSEXTPROC glDeleteFramebuffersEXT; +extern PFNGLGENFRAMEBUFFERSEXTPROC glGenFramebuffersEXT; +extern PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC glCheckFramebufferStatusEXT; +extern PFNGLFRAMEBUFFERTEXTURE1DEXTPROC glFramebufferTexture1DEXT; +extern PFNGLFRAMEBUFFERTEXTURE2DEXTPROC glFramebufferTexture2DEXT; +extern PFNGLFRAMEBUFFERTEXTURE3DEXTPROC glFramebufferTexture3DEXT; +extern PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC glFramebufferRenderbufferEXT; +extern PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC glGetFramebufferAttachmentParameterivEXT; +extern PFNGLGENERATEMIPMAPEXTPROC glGenerateMipmapEXT; + +// GL_EXT_framebuffer_multisample +extern PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glRenderbufferStorageMultisampleEXT; + +// GL_EXT_framebuffer_blit +extern PFNGLBLITFRAMEBUFFEREXTPROC glBlitFramebufferEXT; + +//GL_ARB_draw_buffers +extern PFNGLDRAWBUFFERSARBPROC glDrawBuffersARB; + + +#elif LL_DARWIN +//---------------------------------------------------------------------------- +// LL_DARWIN + +#include <OpenGL/gl.h> +#include <OpenGL/glu.h> + +#define GL_EXT_separate_specular_color 1 +#include <OpenGL/glext.h> + +#include "GL/glh_extensions.h" + +// These symbols don't exist on 10.3.9, so they have to be declared weak. Redeclaring them here fixes the problem. +// Note that they also must not be called on 10.3.9. This should be taken care of by a runtime check for the existence of the GL extension. +#include <AvailabilityMacros.h> + +//GL_EXT_blend_func_separate +extern void glBlendFuncSeparateEXT(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + +// GL_EXT_framebuffer_object +extern GLboolean glIsRenderbufferEXT(GLuint renderbuffer) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +extern void glBindRenderbufferEXT(GLenum target, GLuint renderbuffer) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +extern void glDeleteRenderbuffersEXT(GLsizei n, const GLuint *renderbuffers) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +extern void glGenRenderbuffersEXT(GLsizei n, GLuint *renderbuffers) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +extern void glRenderbufferStorageEXT(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +extern void glGetRenderbufferParameterivEXT(GLenum target, GLenum pname, GLint *params) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +extern GLboolean glIsFramebufferEXT(GLuint framebuffer) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +extern void glBindFramebufferEXT(GLenum target, GLuint framebuffer) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +extern void glDeleteFramebuffersEXT(GLsizei n, const GLuint *framebuffers) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +extern void glGenFramebuffersEXT(GLsizei n, GLuint *framebuffers) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +extern GLenum glCheckFramebufferStatusEXT(GLenum target) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +extern void glFramebufferTexture1DEXT(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +extern void glFramebufferTexture2DEXT(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +extern void glFramebufferTexture3DEXT(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +extern void glFramebufferRenderbufferEXT(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +extern void glGetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment, GLenum pname, GLint *params) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +extern void glGenerateMipmapEXT(GLenum target) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + +// GL_ARB_draw_buffers +extern void glDrawBuffersARB(GLsizei n, const GLenum* bufs) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + +#ifdef __cplusplus +extern "C" { +#endif +// +// Define vertex buffer object headers on Mac +// +#ifndef GL_ARB_vertex_buffer_object +#define GL_BUFFER_SIZE_ARB 0x8764 +#define GL_BUFFER_USAGE_ARB 0x8765 +#define GL_ARRAY_BUFFER_ARB 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER_ARB 0x8893 +#define GL_ARRAY_BUFFER_BINDING_ARB 0x8894 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB 0x8895 +#define GL_VERTEX_ARRAY_BUFFER_BINDING_ARB 0x8896 +#define GL_NORMAL_ARRAY_BUFFER_BINDING_ARB 0x8897 +#define GL_COLOR_ARRAY_BUFFER_BINDING_ARB 0x8898 +#define GL_INDEX_ARRAY_BUFFER_BINDING_ARB 0x8899 +#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB 0x889A +#define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB 0x889B +#define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB 0x889C +#define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB 0x889D +#define GL_WEIGHT_ARRAY_BUFFER_BINDING_ARB 0x889E +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB 0x889F +#define GL_READ_ONLY_ARB 0x88B8 +#define GL_WRITE_ONLY_ARB 0x88B9 +#define GL_READ_WRITE_ARB 0x88BA +#define GL_BUFFER_ACCESS_ARB 0x88BB +#define GL_BUFFER_MAPPED_ARB 0x88BC +#define GL_BUFFER_MAP_POINTER_ARB 0x88BD +#define GL_STREAM_DRAW_ARB 0x88E0 +#define GL_STREAM_READ_ARB 0x88E1 +#define GL_STREAM_COPY_ARB 0x88E2 +#define GL_STATIC_DRAW_ARB 0x88E4 +#define GL_STATIC_READ_ARB 0x88E5 +#define GL_STATIC_COPY_ARB 0x88E6 +#define GL_DYNAMIC_DRAW_ARB 0x88E8 +#define GL_DYNAMIC_READ_ARB 0x88E9 +#define GL_DYNAMIC_COPY_ARB 0x88EA +#endif + + + +#ifndef GL_ARB_vertex_buffer_object +/* GL types for handling large vertex buffer objects */ +typedef intptr_t GLintptrARB; +typedef intptr_t GLsizeiptrARB; +#endif + + +#ifndef GL_ARB_vertex_buffer_object +#define GL_ARB_vertex_buffer_object 1 +#ifdef GL_GLEXT_FUNCTION_POINTERS +typedef void (* glBindBufferARBProcPtr) (GLenum target, GLuint buffer); +typedef void (* glDeleteBufferARBProcPtr) (GLsizei n, const GLuint *buffers); +typedef void (* glGenBuffersARBProcPtr) (GLsizei n, GLuint *buffers); +typedef GLboolean (* glIsBufferARBProcPtr) (GLuint buffer); +typedef void (* glBufferDataARBProcPtr) (GLenum target, GLsizeiptrARB size, const GLvoid *data, GLenum usage); +typedef void (* glBufferSubDataARBProcPtr) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, const GLvoid *data); +typedef void (* glGetBufferSubDataARBProcPtr) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, GLvoid *data); +typedef GLvoid* (* glMapBufferARBProcPtr) (GLenum target, GLenum access); /* Flawfinder: ignore */ +typedef GLboolean (* glUnmapBufferARBProcPtr) (GLenum target); +typedef void (* glGetBufferParameterivARBProcPtr) (GLenum target, GLenum pname, GLint *params); +typedef void (* glGetBufferPointervARBProcPtr) (GLenum target, GLenum pname, GLvoid* *params); +#else +extern void glBindBufferARB (GLenum, GLuint); +extern void glDeleteBuffersARB (GLsizei, const GLuint *); +extern void glGenBuffersARB (GLsizei, GLuint *); +extern GLboolean glIsBufferARB (GLuint); +extern void glBufferDataARB (GLenum, GLsizeiptrARB, const GLvoid *, GLenum); +extern void glBufferSubDataARB (GLenum, GLintptrARB, GLsizeiptrARB, const GLvoid *); +extern void glGetBufferSubDataARB (GLenum, GLintptrARB, GLsizeiptrARB, GLvoid *); +extern GLvoid* glMapBufferARB (GLenum, GLenum); +extern GLboolean glUnmapBufferARB (GLenum); +extern void glGetBufferParameterivARB (GLenum, GLenum, GLint *); +extern void glGetBufferPointervARB (GLenum, GLenum, GLvoid* *); +#endif /* GL_GLEXT_FUNCTION_POINTERS */ +#endif + +// May be needed for DARWIN... +// #ifndef GL_ARB_compressed_tex_image +// #define GL_ARB_compressed_tex_image 1 +// #ifdef GL_GLEXT_FUNCTION_POINTERS +// typedef void (* glCompressedTexImage1D) (GLenum, GLint, GLenum, GLsizei, GLint, GLsizei, const GLvoid*); +// typedef void (* glCompressedTexImage2D) (GLenum, GLint, GLenum, GLsizei, GLsizei, GLint, GLsizei, const GLvoid*); +// typedef void (* glCompressedTexImage3D) (GLenum, GLint, GLenum, GLsizei, GLsizei, GLsizei, GLint, GLsizei, const GLvoid*); +// typedef void (* glCompressedTexSubImage1D) (GLenum, GLint, GLint, GLsizei, GLenum, GLsizei, const GLvoid*); +// typedef void (* glCompressedTexSubImage2D) (GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLsizei, const GLvoid*); +// typedef void (* glCompressedTexSubImage3D) (GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLenum, GLsizei, const GLvoid*); +// typedef void (* glGetCompressedTexImage) (GLenum, GLint, GLvoid*); +// #else +// extern void glCompressedTexImage1D (GLenum, GLint, GLenum, GLsizei, GLint, GLsizei, const GLvoid*); +// extern void glCompressedTexImage2D (GLenum, GLint, GLenum, GLsizei, GLsizei, GLint, GLsizei, const GLvoid*); +// extern void glCompressedTexImage3D (GLenum, GLint, GLenum, GLsizei, GLsizei, GLsizei, GLint, GLsizei, const GLvoid*); +// extern void glCompressedTexSubImage1D (GLenum, GLint, GLint, GLsizei, GLenum, GLsizei, const GLvoid*); +// extern void glCompressedTexSubImage2D (GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLsizei, const GLvoid*); +// extern void glCompressedTexSubImage3D (GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLenum, GLsizei, const GLvoid*); +// extern void glGetCompressedTexImage (GLenum, GLint, GLvoid*); +// #endif /* GL_GLEXT_FUNCTION_POINTERS */ +// #endif + +#ifdef __cplusplus +} +#endif + +#include <AGL/gl.h> + +#endif // LL_MESA / LL_WINDOWS / LL_DARWIN + + +#endif // LL_LLGLHEADERS_H diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp new file mode 100644 index 0000000000..16534fa9a5 --- /dev/null +++ b/indra/llrender/llglslshader.cpp @@ -0,0 +1,904 @@ +/** + * @file llglslshader.cpp + * @brief GLSL helper functions and state. + * + * $LicenseInfo:firstyear=2005&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * 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; + * version 2.1 of the License only. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llglslshader.h" + +#include "llshadermgr.h" +#include "llfile.h" +#include "llrender.h" + +#if LL_DARWIN +#include "OpenGL/OpenGL.h" +#endif + +#ifdef LL_RELEASE_FOR_DOWNLOAD +#define UNIFORM_ERRS LL_WARNS_ONCE("Shader") +#else +#define UNIFORM_ERRS LL_ERRS("Shader") +#endif + +// Lots of STL stuff in here, using namespace std to keep things more readable +using std::vector; +using std::pair; +using std::make_pair; +using std::string; + +BOOL shouldChange(const LLVector4& v1, const LLVector4& v2) +{ + return v1 != v2; +} + +LLShaderFeatures::LLShaderFeatures() +: calculatesLighting(false), isShiny(false), isFullbright(false), hasWaterFog(false), +hasTransport(false), hasSkinning(false), hasAtmospherics(false), isSpecular(false), +hasGamma(false), hasLighting(false), calculatesAtmospherics(false) +{ +} + +//=============================== +// LLGLSL Shader implementation +//=============================== +LLGLSLShader::LLGLSLShader() + : mProgramObject(0), mActiveTextureChannels(0), mShaderLevel(0), mShaderGroup(SG_DEFAULT), mUniformsDirty(FALSE) +{ +} + +void LLGLSLShader::unload() +{ + stop_glerror(); + mAttribute.clear(); + mTexture.clear(); + mUniform.clear(); + mShaderFiles.clear(); + + if (mProgramObject) + { + GLhandleARB obj[1024]; + GLsizei count; + + glGetAttachedObjectsARB(mProgramObject, 1024, &count, obj); + for (GLsizei i = 0; i < count; i++) + { + glDeleteObjectARB(obj[i]); + } + + glDeleteObjectARB(mProgramObject); + + mProgramObject = 0; + } + + //hack to make apple not complain + glGetError(); + + stop_glerror(); +} + +BOOL LLGLSLShader::createShader(vector<string> * attributes, + vector<string> * uniforms) +{ + llassert_always(!mShaderFiles.empty()); + BOOL success = TRUE; + + // Create program + mProgramObject = glCreateProgramObjectARB(); + + // Attach existing objects + if (!LLShaderMgr::instance()->attachShaderFeatures(this)) + { + return FALSE; + } + + vector< pair<string,GLenum> >::iterator fileIter = mShaderFiles.begin(); + for ( ; fileIter != mShaderFiles.end(); fileIter++ ) + { + GLhandleARB shaderhandle = LLShaderMgr::instance()->loadShaderFile((*fileIter).first, mShaderLevel, (*fileIter).second); + LL_DEBUGS("ShaderLoading") << "SHADER FILE: " << (*fileIter).first << " mShaderLevel=" << mShaderLevel << LL_ENDL; + if (mShaderLevel > 0) + { + attachObject(shaderhandle); + } + else + { + success = FALSE; + } + } + + // Map attributes and uniforms + if (success) + { + success = mapAttributes(attributes); + } + if (success) + { + success = mapUniforms(uniforms); + } + if( !success ) + { + LL_WARNS("ShaderLoading") << "Failed to link shader: " << mName << LL_ENDL; + + // Try again using a lower shader level; + if (mShaderLevel > 0) + { + LL_WARNS("ShaderLoading") << "Failed to link using shader level " << mShaderLevel << " trying again using shader level " << (mShaderLevel - 1) << LL_ENDL; + mShaderLevel--; + return createShader(attributes,uniforms); + } + } + return success; +} + +BOOL LLGLSLShader::attachObject(std::string object) +{ + if (LLShaderMgr::instance()->mShaderObjects.count(object) > 0) + { + stop_glerror(); + glAttachObjectARB(mProgramObject, LLShaderMgr::instance()->mShaderObjects[object]); + stop_glerror(); + return TRUE; + } + else + { + LL_WARNS("ShaderLoading") << "Attempting to attach shader object that hasn't been compiled: " << object << LL_ENDL; + return FALSE; + } +} + +void LLGLSLShader::attachObject(GLhandleARB object) +{ + if (object != 0) + { + stop_glerror(); + glAttachObjectARB(mProgramObject, object); + stop_glerror(); + } + else + { + LL_WARNS("ShaderLoading") << "Attempting to attach non existing shader object. " << LL_ENDL; + } +} + +void LLGLSLShader::attachObjects(GLhandleARB* objects, S32 count) +{ + for (S32 i = 0; i < count; i++) + { + attachObject(objects[i]); + } +} + +BOOL LLGLSLShader::mapAttributes(const vector<string> * attributes) +{ + //link the program + BOOL res = link(); + + mAttribute.clear(); + U32 numAttributes = (attributes == NULL) ? 0 : attributes->size(); + mAttribute.resize(LLShaderMgr::instance()->mReservedAttribs.size() + numAttributes, -1); + + if (res) + { //read back channel locations + + //read back reserved channels first + for (U32 i = 0; i < LLShaderMgr::instance()->mReservedAttribs.size(); i++) + { + const char* name = LLShaderMgr::instance()->mReservedAttribs[i].c_str(); + S32 index = glGetAttribLocationARB(mProgramObject, (const GLcharARB *)name); + if (index != -1) + { + mAttribute[i] = index; + LL_DEBUGS("ShaderLoading") << "Attribute " << name << " assigned to channel " << index << LL_ENDL; + } + } + if (attributes != NULL) + { + for (U32 i = 0; i < numAttributes; i++) + { + const char* name = (*attributes)[i].c_str(); + S32 index = glGetAttribLocationARB(mProgramObject, name); + if (index != -1) + { + mAttribute[LLShaderMgr::instance()->mReservedAttribs.size() + i] = index; + LL_DEBUGS("ShaderLoading") << "Attribute " << name << " assigned to channel " << index << LL_ENDL; + } + } + } + + return TRUE; + } + + return FALSE; +} + +void LLGLSLShader::mapUniform(GLint index, const vector<string> * uniforms) +{ + if (index == -1) + { + return; + } + + GLenum type; + GLsizei length; + GLint size; + char name[1024]; /* Flawfinder: ignore */ + name[0] = 0; + + glGetActiveUniformARB(mProgramObject, index, 1024, &length, &size, &type, (GLcharARB *)name); + S32 location = glGetUniformLocationARB(mProgramObject, name); + if (location != -1) + { + //chop off "[0]" so we can always access the first element + //of an array by the array name + char* is_array = strstr(name, "[0]"); + if (is_array) + { + is_array[0] = 0; + } + + mUniformMap[name] = location; + LL_DEBUGS("ShaderLoading") << "Uniform " << name << " is at location " << location << LL_ENDL; + + //find the index of this uniform + for (S32 i = 0; i < (S32) LLShaderMgr::instance()->mReservedUniforms.size(); i++) + { + if ( (mUniform[i] == -1) + && (LLShaderMgr::instance()->mReservedUniforms[i].compare(0, length, name, LLShaderMgr::instance()->mReservedUniforms[i].length()) == 0)) + { + //found it + mUniform[i] = location; + mTexture[i] = mapUniformTextureChannel(location, type); + return; + } + } + + if (uniforms != NULL) + { + for (U32 i = 0; i < uniforms->size(); i++) + { + if ( (mUniform[i+LLShaderMgr::instance()->mReservedUniforms.size()] == -1) + && ((*uniforms)[i].compare(0, length, name, (*uniforms)[i].length()) == 0)) + { + //found it + mUniform[i+LLShaderMgr::instance()->mReservedUniforms.size()] = location; + mTexture[i+LLShaderMgr::instance()->mReservedUniforms.size()] = mapUniformTextureChannel(location, type); + return; + } + } + } + } + } + +GLint LLGLSLShader::mapUniformTextureChannel(GLint location, GLenum type) +{ + if (type >= GL_SAMPLER_1D_ARB && type <= GL_SAMPLER_2D_RECT_SHADOW_ARB) + { //this here is a texture + glUniform1iARB(location, mActiveTextureChannels); + LL_DEBUGS("ShaderLoading") << "Assigned to texture channel " << mActiveTextureChannels << LL_ENDL; + return mActiveTextureChannels++; + } + return -1; +} + +BOOL LLGLSLShader::mapUniforms(const vector<string> * uniforms) +{ + BOOL res = TRUE; + + mActiveTextureChannels = 0; + mUniform.clear(); + mUniformMap.clear(); + mTexture.clear(); + mValue.clear(); + //initialize arrays + U32 numUniforms = (uniforms == NULL) ? 0 : uniforms->size(); + mUniform.resize(numUniforms + LLShaderMgr::instance()->mReservedUniforms.size(), -1); + mTexture.resize(numUniforms + LLShaderMgr::instance()->mReservedUniforms.size(), -1); + + bind(); + + //get the number of active uniforms + GLint activeCount; + glGetObjectParameterivARB(mProgramObject, GL_OBJECT_ACTIVE_UNIFORMS_ARB, &activeCount); + + for (S32 i = 0; i < activeCount; i++) + { + mapUniform(i, uniforms); + } + + unbind(); + + return res; +} + +BOOL LLGLSLShader::link(BOOL suppress_errors) +{ + return LLShaderMgr::instance()->linkProgramObject(mProgramObject, suppress_errors); +} + +void LLGLSLShader::bind() +{ + if (gGLManager.mHasShaderObjects) + { + glUseProgramObjectARB(mProgramObject); + + if (mUniformsDirty) + { + LLShaderMgr::instance()->updateShaderUniforms(this); + mUniformsDirty = FALSE; + } + } +} + +void LLGLSLShader::unbind() +{ + if (gGLManager.mHasShaderObjects) + { + stop_glerror(); + if (gGLManager.mIsNVIDIA) + { + for (U32 i = 0; i < mAttribute.size(); ++i) + { + vertexAttrib4f(i, 0,0,0,1); + stop_glerror(); + } + } + glUseProgramObjectARB(0); + stop_glerror(); + } +} + +void LLGLSLShader::bindNoShader(void) +{ + glUseProgramObjectARB(0); +} + +S32 LLGLSLShader::enableTexture(S32 uniform, LLTexUnit::eTextureType mode) +{ + if (uniform < 0 || uniform >= (S32)mTexture.size()) + { + UNIFORM_ERRS << "Uniform out of range: " << uniform << LL_ENDL; + return -1; + } + S32 index = mTexture[uniform]; + if (index != -1) + { + gGL.getTexUnit(index)->activate(); + gGL.getTexUnit(index)->enable(mode); + } + return index; +} + +S32 LLGLSLShader::disableTexture(S32 uniform, LLTexUnit::eTextureType mode) +{ + if (uniform < 0 || uniform >= (S32)mTexture.size()) + { + UNIFORM_ERRS << "Uniform out of range: " << uniform << LL_ENDL; + return -1; + } + S32 index = mTexture[uniform]; + if (index != -1 && gGL.getTexUnit(index)->getCurrType() != LLTexUnit::TT_NONE) + { + if (gDebugGL && gGL.getTexUnit(index)->getCurrType() != mode) + { + if (gDebugSession) + { + gFailLog << "Texture channel " << index << " texture type corrupted." << std::endl; + ll_fail("LLGLSLShader::disableTexture failed"); + } + else + { + llerrs << "Texture channel " << index << " texture type corrupted." << llendl; + } + } + gGL.getTexUnit(index)->disable(); + } + return index; +} + +void LLGLSLShader::uniform1i(U32 index, GLint x) +{ + if (mProgramObject > 0) + { + if (mUniform.size() <= index) + { + UNIFORM_ERRS << "Uniform index out of bounds." << LL_ENDL; + return; + } + + if (mUniform[index] >= 0) + { + std::map<GLint, LLVector4>::iterator iter = mValue.find(mUniform[index]); + if (iter == mValue.end() || iter->second.mV[0] != x) + { + glUniform1iARB(mUniform[index], x); + mValue[mUniform[index]] = LLVector4(x,0.f,0.f,0.f); + } + } + } +} + +void LLGLSLShader::uniform1f(U32 index, GLfloat x) +{ + if (mProgramObject > 0) + { + if (mUniform.size() <= index) + { + UNIFORM_ERRS << "Uniform index out of bounds." << LL_ENDL; + return; + } + + if (mUniform[index] >= 0) + { + std::map<GLint, LLVector4>::iterator iter = mValue.find(mUniform[index]); + if (iter == mValue.end() || iter->second.mV[0] != x) + { + glUniform1fARB(mUniform[index], x); + mValue[mUniform[index]] = LLVector4(x,0.f,0.f,0.f); + } + } + } +} + +void LLGLSLShader::uniform2f(U32 index, GLfloat x, GLfloat y) +{ + if (mProgramObject > 0) + { + if (mUniform.size() <= index) + { + UNIFORM_ERRS << "Uniform index out of bounds." << LL_ENDL; + return; + } + + if (mUniform[index] >= 0) + { + std::map<GLint, LLVector4>::iterator iter = mValue.find(mUniform[index]); + LLVector4 vec(x,y,0.f,0.f); + if (iter == mValue.end() || shouldChange(iter->second,vec)) + { + glUniform2fARB(mUniform[index], x, y); + mValue[mUniform[index]] = vec; + } + } + } +} + +void LLGLSLShader::uniform3f(U32 index, GLfloat x, GLfloat y, GLfloat z) +{ + if (mProgramObject > 0) + { + if (mUniform.size() <= index) + { + UNIFORM_ERRS << "Uniform index out of bounds." << LL_ENDL; + return; + } + + if (mUniform[index] >= 0) + { + std::map<GLint, LLVector4>::iterator iter = mValue.find(mUniform[index]); + LLVector4 vec(x,y,z,0.f); + if (iter == mValue.end() || shouldChange(iter->second,vec)) + { + glUniform3fARB(mUniform[index], x, y, z); + mValue[mUniform[index]] = vec; + } + } + } +} + +void LLGLSLShader::uniform4f(U32 index, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ + if (mProgramObject > 0) + { + if (mUniform.size() <= index) + { + UNIFORM_ERRS << "Uniform index out of bounds." << LL_ENDL; + return; + } + + if (mUniform[index] >= 0) + { + std::map<GLint, LLVector4>::iterator iter = mValue.find(mUniform[index]); + LLVector4 vec(x,y,z,w); + if (iter == mValue.end() || shouldChange(iter->second,vec)) + { + glUniform4fARB(mUniform[index], x, y, z, w); + mValue[mUniform[index]] = vec; + } + } + } +} + +void LLGLSLShader::uniform1iv(U32 index, U32 count, const GLint* v) +{ + if (mProgramObject > 0) + { + if (mUniform.size() <= index) + { + UNIFORM_ERRS << "Uniform index out of bounds." << LL_ENDL; + return; + } + + if (mUniform[index] >= 0) + { + std::map<GLint, LLVector4>::iterator iter = mValue.find(mUniform[index]); + LLVector4 vec(v[0],0.f,0.f,0.f); + if (iter == mValue.end() || shouldChange(iter->second,vec) || count != 1) + { + glUniform1ivARB(mUniform[index], count, v); + mValue[mUniform[index]] = vec; + } + } + } +} + +void LLGLSLShader::uniform1fv(U32 index, U32 count, const GLfloat* v) +{ + if (mProgramObject > 0) + { + if (mUniform.size() <= index) + { + UNIFORM_ERRS << "Uniform index out of bounds." << LL_ENDL; + return; + } + + if (mUniform[index] >= 0) + { + std::map<GLint, LLVector4>::iterator iter = mValue.find(mUniform[index]); + LLVector4 vec(v[0],0.f,0.f,0.f); + if (iter == mValue.end() || shouldChange(iter->second,vec) || count != 1) + { + glUniform1fvARB(mUniform[index], count, v); + mValue[mUniform[index]] = vec; + } + } + } +} + +void LLGLSLShader::uniform2fv(U32 index, U32 count, const GLfloat* v) +{ + if (mProgramObject > 0) + { + if (mUniform.size() <= index) + { + UNIFORM_ERRS << "Uniform index out of bounds." << LL_ENDL; + return; + } + + if (mUniform[index] >= 0) + { + std::map<GLint, LLVector4>::iterator iter = mValue.find(mUniform[index]); + LLVector4 vec(v[0],v[1],0.f,0.f); + if (iter == mValue.end() || shouldChange(iter->second,vec) || count != 1) + { + glUniform2fvARB(mUniform[index], count, v); + mValue[mUniform[index]] = vec; + } + } + } +} + +void LLGLSLShader::uniform3fv(U32 index, U32 count, const GLfloat* v) +{ + if (mProgramObject > 0) + { + if (mUniform.size() <= index) + { + UNIFORM_ERRS << "Uniform index out of bounds." << LL_ENDL; + return; + } + + if (mUniform[index] >= 0) + { + std::map<GLint, LLVector4>::iterator iter = mValue.find(mUniform[index]); + LLVector4 vec(v[0],v[1],v[2],0.f); + if (iter == mValue.end() || shouldChange(iter->second,vec) || count != 1) + { + glUniform3fvARB(mUniform[index], count, v); + mValue[mUniform[index]] = vec; + } + } + } +} + +void LLGLSLShader::uniform4fv(U32 index, U32 count, const GLfloat* v) +{ + if (mProgramObject > 0) + { + if (mUniform.size() <= index) + { + UNIFORM_ERRS << "Uniform index out of bounds." << LL_ENDL; + return; + } + + if (mUniform[index] >= 0) + { + std::map<GLint, LLVector4>::iterator iter = mValue.find(mUniform[index]); + LLVector4 vec(v[0],v[1],v[2],v[3]); + if (iter == mValue.end() || shouldChange(iter->second,vec) || count != 1) + { + glUniform4fvARB(mUniform[index], count, v); + mValue[mUniform[index]] = vec; + } + } + } +} + +void LLGLSLShader::uniformMatrix2fv(U32 index, U32 count, GLboolean transpose, const GLfloat *v) +{ + if (mProgramObject > 0) + { + if (mUniform.size() <= index) + { + UNIFORM_ERRS << "Uniform index out of bounds." << LL_ENDL; + return; + } + + if (mUniform[index] >= 0) + { + glUniformMatrix2fvARB(mUniform[index], count, transpose, v); + } + } +} + +void LLGLSLShader::uniformMatrix3fv(U32 index, U32 count, GLboolean transpose, const GLfloat *v) +{ + if (mProgramObject > 0) + { + if (mUniform.size() <= index) + { + UNIFORM_ERRS << "Uniform index out of bounds." << LL_ENDL; + return; + } + + if (mUniform[index] >= 0) + { + glUniformMatrix3fvARB(mUniform[index], count, transpose, v); + } + } +} + +void LLGLSLShader::uniformMatrix4fv(U32 index, U32 count, GLboolean transpose, const GLfloat *v) +{ + if (mProgramObject > 0) + { + if (mUniform.size() <= index) + { + UNIFORM_ERRS << "Uniform index out of bounds." << LL_ENDL; + return; + } + + if (mUniform[index] >= 0) + { + glUniformMatrix4fvARB(mUniform[index], count, transpose, v); + } + } +} + +GLint LLGLSLShader::getUniformLocation(const string& uniform) +{ + if (mProgramObject > 0) + { + std::map<string, GLint>::iterator iter = mUniformMap.find(uniform); + if (iter != mUniformMap.end()) + { + llassert(iter->second == glGetUniformLocationARB(mProgramObject, uniform.c_str())); + return iter->second; + } + } + + return -1; +} + +void LLGLSLShader::uniform1i(const string& uniform, GLint v) +{ + GLint location = getUniformLocation(uniform); + + if (location >= 0) + { + std::map<GLint, LLVector4>::iterator iter = mValue.find(location); + LLVector4 vec(v,0.f,0.f,0.f); + if (iter == mValue.end() || shouldChange(iter->second,vec)) + { + glUniform1iARB(location, v); + mValue[location] = vec; + } + } +} + +void LLGLSLShader::uniform1f(const string& uniform, GLfloat v) +{ + GLint location = getUniformLocation(uniform); + + if (location >= 0) + { + std::map<GLint, LLVector4>::iterator iter = mValue.find(location); + LLVector4 vec(v,0.f,0.f,0.f); + if (iter == mValue.end() || shouldChange(iter->second,vec)) + { + glUniform1fARB(location, v); + mValue[location] = vec; + } + } +} + +void LLGLSLShader::uniform2f(const string& uniform, GLfloat x, GLfloat y) +{ + GLint location = getUniformLocation(uniform); + + if (location >= 0) + { + std::map<GLint, LLVector4>::iterator iter = mValue.find(location); + LLVector4 vec(x,y,0.f,0.f); + if (iter == mValue.end() || shouldChange(iter->second,vec)) + { + glUniform2fARB(location, x,y); + mValue[location] = vec; + } + } + +} + +void LLGLSLShader::uniform3f(const string& uniform, GLfloat x, GLfloat y, GLfloat z) +{ + GLint location = getUniformLocation(uniform); + + if (location >= 0) + { + std::map<GLint, LLVector4>::iterator iter = mValue.find(location); + LLVector4 vec(x,y,z,0.f); + if (iter == mValue.end() || shouldChange(iter->second,vec)) + { + glUniform3fARB(location, x,y,z); + mValue[location] = vec; + } + } +} + +void LLGLSLShader::uniform4f(const string& uniform, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ + GLint location = getUniformLocation(uniform); + + if (location >= 0) + { + std::map<GLint, LLVector4>::iterator iter = mValue.find(location); + LLVector4 vec(x,y,z,w); + if (iter == mValue.end() || shouldChange(iter->second,vec)) + { + glUniform4fARB(location, x,y,z,w); + mValue[location] = vec; + } + } +} + +void LLGLSLShader::uniform1fv(const string& uniform, U32 count, const GLfloat* v) +{ + GLint location = getUniformLocation(uniform); + + if (location >= 0) + { + std::map<GLint, LLVector4>::iterator iter = mValue.find(location); + LLVector4 vec(v[0],0.f,0.f,0.f); + if (iter == mValue.end() || shouldChange(iter->second,vec) || count != 1) + { + glUniform1fvARB(location, count, v); + mValue[location] = vec; + } + } +} + +void LLGLSLShader::uniform2fv(const string& uniform, U32 count, const GLfloat* v) +{ + GLint location = getUniformLocation(uniform); + + if (location >= 0) + { + std::map<GLint, LLVector4>::iterator iter = mValue.find(location); + LLVector4 vec(v[0],v[1],0.f,0.f); + if (iter == mValue.end() || shouldChange(iter->second,vec) || count != 1) + { + glUniform2fvARB(location, count, v); + mValue[location] = vec; + } + } +} + +void LLGLSLShader::uniform3fv(const string& uniform, U32 count, const GLfloat* v) +{ + GLint location = getUniformLocation(uniform); + + if (location >= 0) + { + std::map<GLint, LLVector4>::iterator iter = mValue.find(location); + LLVector4 vec(v[0],v[1],v[2],0.f); + if (iter == mValue.end() || shouldChange(iter->second,vec) || count != 1) + { + glUniform3fvARB(location, count, v); + mValue[location] = vec; + } + } +} + +void LLGLSLShader::uniform4fv(const string& uniform, U32 count, const GLfloat* v) +{ + GLint location = getUniformLocation(uniform); + + if (location >= 0) + { + LLVector4 vec(v); + std::map<GLint, LLVector4>::iterator iter = mValue.find(location); + if (iter == mValue.end() || shouldChange(iter->second,vec) || count != 1) + { + glUniform4fvARB(location, count, v); + mValue[location] = vec; + } + } +} + +void LLGLSLShader::uniformMatrix2fv(const string& uniform, U32 count, GLboolean transpose, const GLfloat* v) +{ + GLint location = getUniformLocation(uniform); + + if (location >= 0) + { + glUniformMatrix2fvARB(location, count, transpose, v); + } +} + +void LLGLSLShader::uniformMatrix3fv(const string& uniform, U32 count, GLboolean transpose, const GLfloat* v) +{ + GLint location = getUniformLocation(uniform); + + if (location >= 0) + { + glUniformMatrix3fvARB(location, count, transpose, v); + } +} + +void LLGLSLShader::uniformMatrix4fv(const string& uniform, U32 count, GLboolean transpose, const GLfloat* v) +{ + GLint location = getUniformLocation(uniform); + + if (location >= 0) + { + glUniformMatrix4fvARB(location, count, transpose, v); + } +} + + +void LLGLSLShader::vertexAttrib4f(U32 index, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ + if (mAttribute[index] > 0) + { + glVertexAttrib4fARB(mAttribute[index], x, y, z, w); + } +} + +void LLGLSLShader::vertexAttrib4fv(U32 index, GLfloat* v) +{ + if (mAttribute[index] > 0) + { + glVertexAttrib4fvARB(mAttribute[index], v); + } +} diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h new file mode 100644 index 0000000000..c11bd50716 --- /dev/null +++ b/indra/llrender/llglslshader.h @@ -0,0 +1,139 @@ +/** + * @file llglslshader.h + * @brief GLSL shader wrappers + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * 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; + * version 2.1 of the License only. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLGLSLSHADER_H +#define LL_LLGLSLSHADER_H + +#include "llgl.h" +#include "llrender.h" + +class LLShaderFeatures +{ +public: + bool calculatesLighting; + bool calculatesAtmospherics; + bool hasLighting; // implies no transport (it's possible to have neither though) + bool isShiny; + bool isFullbright; // implies no lighting + bool isSpecular; + bool hasWaterFog; // implies no gamma + bool hasTransport; // implies no lighting (it's possible to have neither though) + bool hasSkinning; + bool hasAtmospherics; + bool hasGamma; + + // char numLights; + + LLShaderFeatures(); +}; + +class LLGLSLShader +{ +public: + + enum + { + SG_DEFAULT = 0, + SG_SKY, + SG_WATER + }; + + LLGLSLShader(); + + void unload(); + BOOL createShader(std::vector<std::string> * attributes, + std::vector<std::string> * uniforms); + BOOL attachObject(std::string object); + void attachObject(GLhandleARB object); + void attachObjects(GLhandleARB* objects = NULL, S32 count = 0); + BOOL mapAttributes(const std::vector<std::string> * attributes); + BOOL mapUniforms(const std::vector<std::string> * uniforms); + void mapUniform(GLint index, const std::vector<std::string> * uniforms); + void uniform1i(U32 index, GLint i); + void uniform1f(U32 index, GLfloat v); + void uniform2f(U32 index, GLfloat x, GLfloat y); + void uniform3f(U32 index, GLfloat x, GLfloat y, GLfloat z); + void uniform4f(U32 index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + void uniform1iv(U32 index, U32 count, const GLint* i); + void uniform1fv(U32 index, U32 count, const GLfloat* v); + void uniform2fv(U32 index, U32 count, const GLfloat* v); + void uniform3fv(U32 index, U32 count, const GLfloat* v); + void uniform4fv(U32 index, U32 count, const GLfloat* v); + void uniform1i(const std::string& uniform, GLint i); + void uniform1f(const std::string& uniform, GLfloat v); + void uniform2f(const std::string& uniform, GLfloat x, GLfloat y); + void uniform3f(const std::string& uniform, GLfloat x, GLfloat y, GLfloat z); + void uniform4f(const std::string& uniform, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + void uniform1iv(const std::string& uniform, U32 count, const GLint* i); + void uniform1fv(const std::string& uniform, U32 count, const GLfloat* v); + void uniform2fv(const std::string& uniform, U32 count, const GLfloat* v); + void uniform3fv(const std::string& uniform, U32 count, const GLfloat* v); + void uniform4fv(const std::string& uniform, U32 count, const GLfloat* v); + void uniformMatrix2fv(U32 index, U32 count, GLboolean transpose, const GLfloat *v); + void uniformMatrix3fv(U32 index, U32 count, GLboolean transpose, const GLfloat *v); + void uniformMatrix4fv(U32 index, U32 count, GLboolean transpose, const GLfloat *v); + void uniformMatrix2fv(const std::string& uniform, U32 count, GLboolean transpose, const GLfloat *v); + void uniformMatrix3fv(const std::string& uniform, U32 count, GLboolean transpose, const GLfloat *v); + void uniformMatrix4fv(const std::string& uniform, U32 count, GLboolean transpose, const GLfloat *v); + + void vertexAttrib4f(U32 index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + void vertexAttrib4fv(U32 index, GLfloat* v); + + GLint getUniformLocation(const std::string& uniform); + + GLint mapUniformTextureChannel(GLint location, GLenum type); + + + //enable/disable texture channel for specified uniform + //if given texture uniform is active in the shader, + //the corresponding channel will be active upon return + //returns channel texture is enabled in from [0-MAX) + S32 enableTexture(S32 uniform, LLTexUnit::eTextureType mode = LLTexUnit::TT_TEXTURE); + S32 disableTexture(S32 uniform, LLTexUnit::eTextureType mode = LLTexUnit::TT_TEXTURE); + + BOOL link(BOOL suppress_errors = FALSE); + void bind(); + void unbind(); + + // Unbinds any previously bound shader by explicitly binding no shader. + static void bindNoShader(void); + + GLhandleARB mProgramObject; + std::vector<GLint> mAttribute; //lookup table of attribute enum to attribute channel + std::vector<GLint> mUniform; //lookup table of uniform enum to uniform location + std::map<std::string, GLint> mUniformMap; //lookup map of uniform name to uniform location + std::map<GLint, LLVector4> mValue; //lookup map of uniform location to last known value + std::vector<GLint> mTexture; + S32 mActiveTextureChannels; + S32 mShaderLevel; + S32 mShaderGroup; + BOOL mUniformsDirty; + LLShaderFeatures mFeatures; + std::vector< std::pair< std::string, GLenum > > mShaderFiles; + std::string mName; +}; + +#endif diff --git a/indra/llrender/llglstates.h b/indra/llrender/llglstates.h new file mode 100644 index 0000000000..d5a29dcd0c --- /dev/null +++ b/indra/llrender/llglstates.h @@ -0,0 +1,281 @@ +/** + * @file llglstates.h + * @brief LLGL states definitions + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * 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; + * version 2.1 of the License only. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +//THIS HEADER SHOULD ONLY BE INCLUDED FROM llgl.h +#ifndef LL_LLGLSTATES_H +#define LL_LLGLSTATES_H + +#include "llimagegl.h" + +//---------------------------------------------------------------------------- + +class LLGLDepthTest +{ + // Enabled by default +public: + LLGLDepthTest(GLboolean depth_enabled, GLboolean write_enabled = GL_TRUE, GLenum depth_func = GL_LEQUAL); + + ~LLGLDepthTest(); + + void checkState(); + + GLboolean mPrevDepthEnabled; + GLenum mPrevDepthFunc; + GLboolean mPrevWriteEnabled; +private: + static GLboolean sDepthEnabled; // defaults to GL_FALSE + static GLenum sDepthFunc; // defaults to GL_LESS + static GLboolean sWriteEnabled; // defaults to GL_TRUE +}; + +//---------------------------------------------------------------------------- + +class LLGLSDefault +{ +protected: + LLGLEnable mColorMaterial; + LLGLDisable mAlphaTest, mBlend, mCullFace, mDither, mFog, + mLineSmooth, mLineStipple, mNormalize, mPolygonSmooth, + mTextureGenQ, mTextureGenR, mTextureGenS, mTextureGenT, + mGLMultisample; +public: + LLGLSDefault() + : + // Enable + mColorMaterial(GL_COLOR_MATERIAL), + // Disable + mAlphaTest(GL_ALPHA_TEST), + mBlend(GL_BLEND), + mCullFace(GL_CULL_FACE), + mDither(GL_DITHER), + mFog(GL_FOG), + mLineSmooth(GL_LINE_SMOOTH), + mLineStipple(GL_LINE_STIPPLE), + mNormalize(GL_NORMALIZE), + mPolygonSmooth(GL_POLYGON_SMOOTH), + mTextureGenQ(GL_TEXTURE_GEN_Q), + mTextureGenR(GL_TEXTURE_GEN_R), + mTextureGenS(GL_TEXTURE_GEN_S), + mTextureGenT(GL_TEXTURE_GEN_T), + mGLMultisample(GL_MULTISAMPLE_ARB) + { } +}; + +class LLGLSObjectSelect +{ +protected: + LLGLDisable mBlend, mFog, mAlphaTest; + LLGLEnable mCullFace; +public: + LLGLSObjectSelect() + : mBlend(GL_BLEND), mFog(GL_FOG), + mAlphaTest(GL_ALPHA_TEST), + mCullFace(GL_CULL_FACE) + { } +}; + +class LLGLSObjectSelectAlpha +{ +protected: + LLGLEnable mAlphaTest; +public: + LLGLSObjectSelectAlpha() + : mAlphaTest(GL_ALPHA_TEST) + {} +}; + +//---------------------------------------------------------------------------- + +class LLGLSUIDefault +{ +protected: + LLGLEnable mBlend, mAlphaTest; + LLGLDisable mCullFace; + LLGLDepthTest mDepthTest; +public: + LLGLSUIDefault() + : mBlend(GL_BLEND), mAlphaTest(GL_ALPHA_TEST), + mCullFace(GL_CULL_FACE), + mDepthTest(GL_FALSE, GL_TRUE, GL_LEQUAL) + {} +}; + +class LLGLSNoAlphaTest // : public LLGLSUIDefault +{ +protected: + LLGLDisable mAlphaTest; +public: + LLGLSNoAlphaTest() + : mAlphaTest(GL_ALPHA_TEST) + {} +}; + +//---------------------------------------------------------------------------- + +class LLGLSFog +{ +protected: + LLGLEnable mFog; +public: + LLGLSFog() + : mFog(GL_FOG) + {} +}; + +class LLGLSNoFog +{ +protected: + LLGLDisable mFog; +public: + LLGLSNoFog() + : mFog(GL_FOG) + {} +}; + +//---------------------------------------------------------------------------- + +class LLGLSPipeline +{ +protected: + LLGLEnable mCullFace; + LLGLDepthTest mDepthTest; +public: + LLGLSPipeline() + : mCullFace(GL_CULL_FACE), + mDepthTest(GL_TRUE, GL_TRUE, GL_LEQUAL) + { } +}; + +class LLGLSPipelineAlpha // : public LLGLSPipeline +{ +protected: + LLGLEnable mBlend, mAlphaTest; +public: + LLGLSPipelineAlpha() + : mBlend(GL_BLEND), + mAlphaTest(GL_ALPHA_TEST) + { } +}; + +class LLGLSPipelineEmbossBump +{ +protected: + LLGLDisable mFog; +public: + LLGLSPipelineEmbossBump() + : mFog(GL_FOG) + { } +}; + +class LLGLSPipelineSelection +{ +protected: + LLGLDisable mCullFace; +public: + LLGLSPipelineSelection() + : mCullFace(GL_CULL_FACE) + {} +}; + +class LLGLSPipelineAvatar +{ +protected: + LLGLEnable mNormalize; +public: + LLGLSPipelineAvatar() + : mNormalize(GL_NORMALIZE) + {} +}; + +class LLGLSPipelineSkyBox +{ +protected: + LLGLDisable mAlphaTest, mCullFace, mFog; +public: + LLGLSPipelineSkyBox() + : mAlphaTest(GL_ALPHA_TEST), mCullFace(GL_CULL_FACE), mFog(GL_FOG) + { } +}; + +class LLGLSTracker +{ +protected: + LLGLEnable mCullFace, mBlend, mAlphaTest; +public: + LLGLSTracker() : + mCullFace(GL_CULL_FACE), + mBlend(GL_BLEND), + mAlphaTest(GL_ALPHA_TEST) + + { } +}; + +//---------------------------------------------------------------------------- + +class LLGLSSpecular +{ +public: + LLGLSSpecular(const LLColor4& color, F32 shininess) + { + if (shininess > 0.0f) + { + glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, color.mV); + S32 shiny = (S32)(shininess*128.f); + shiny = llclamp(shiny,0,128); + glMateriali(GL_FRONT_AND_BACK, GL_SHININESS, shiny); + } + } + ~LLGLSSpecular() + { + glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, LLColor4(0.f,0.f,0.f,0.f).mV); + glMateriali(GL_FRONT_AND_BACK, GL_SHININESS, 0); + } +}; + +//---------------------------------------------------------------------------- + + +class LLGLSBlendFunc : public LLGLSPipeline { +protected: + GLint mSavedSrc, mSavedDst; + LLGLEnable mBlend; + +public: + LLGLSBlendFunc(GLenum srcFunc, GLenum dstFunc) : + mBlend(GL_BLEND) + { + glGetIntegerv(GL_BLEND_SRC, &mSavedSrc); + glGetIntegerv(GL_BLEND_DST, &mSavedDst); + glBlendFunc(srcFunc, dstFunc); + } + + ~LLGLSBlendFunc(void) { + glBlendFunc(mSavedSrc, mSavedDst); + } +}; + + +#endif diff --git a/indra/llrender/llgltypes.h b/indra/llrender/llgltypes.h new file mode 100644 index 0000000000..6c217ef727 --- /dev/null +++ b/indra/llrender/llgltypes.h @@ -0,0 +1,39 @@ +/** + * @file llgltypes.h + * @brief LLGL definition + * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * 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; + * version 2.1 of the License only. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LLGLTYPES_H +#define LLGLTYPES_H + +#define MAX_GL_TEXTURE_UNITS 16 + +typedef U32 LLGLenum; +typedef U32 LLGLuint; +typedef S32 LLGLint; +typedef F32 LLGLfloat; +typedef F64 LLGLdouble; +typedef U8 LLGLboolean; + +#endif diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index 55fa48f437..9d037f2565 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -2,30 +2,25 @@ * @file llimagegl.cpp * @brief Generic GL image handler * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2007, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlife.com/developers/opensource/gplv2 + * Copyright (C) 2010, Linden Research, Inc. + * + * 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; + * version 2.1 of the License only. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at http://secondlife.com/developers/opensource/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -41,11 +36,8 @@ #include "llmath.h" #include "llgl.h" -#include "llglimmediate.h" - - +#include "llrender.h" //---------------------------------------------------------------------------- - const F32 MIN_TEXTURE_LIFETIME = 10.f; //statics @@ -53,17 +45,178 @@ LLGLuint LLImageGL::sCurrentBoundTextures[MAX_GL_TEXTURE_UNITS] = { 0 }; U32 LLImageGL::sUniqueCount = 0; U32 LLImageGL::sBindCount = 0; -S32 LLImageGL::sGlobalTextureMemory = 0; -S32 LLImageGL::sBoundTextureMemory = 0; +S32 LLImageGL::sGlobalTextureMemoryInBytes = 0; +S32 LLImageGL::sBoundTextureMemoryInBytes = 0; S32 LLImageGL::sCurBoundTextureMemory = 0; S32 LLImageGL::sCount = 0; +std::list<U32> LLImageGL::sDeadTextureList; BOOL LLImageGL::sGlobalUseAnisotropic = FALSE; F32 LLImageGL::sLastFrameTime = 0.f; +BOOL LLImageGL::sAllowReadBackRaw = FALSE ; +LLImageGL* LLImageGL::sDefaultGLTexture = NULL ; std::set<LLImageGL*> LLImageGL::sImageList; +//**************************************************************************************************** +//The below for texture auditing use only +//**************************************************************************************************** +//----------------------- +//debug use +BOOL gAuditTexture = FALSE ; +#define MAX_TEXTURE_LOG_SIZE 22 //2048 * 2048 +std::vector<S32> LLImageGL::sTextureLoadedCounter(MAX_TEXTURE_LOG_SIZE + 1) ; +std::vector<S32> LLImageGL::sTextureBoundCounter(MAX_TEXTURE_LOG_SIZE + 1) ; +std::vector<S32> LLImageGL::sTextureCurBoundCounter(MAX_TEXTURE_LOG_SIZE + 1) ; +S32 LLImageGL::sCurTexSizeBar = -1 ; +S32 LLImageGL::sCurTexPickSize = -1 ; +LLPointer<LLImageGL> LLImageGL::sHighlightTexturep = NULL; +S32 LLImageGL::sMaxCatagories = 1 ; + +std::vector<S32> LLImageGL::sTextureMemByCategory; +std::vector<S32> LLImageGL::sTextureMemByCategoryBound ; +std::vector<S32> LLImageGL::sTextureCurMemByCategoryBound ; +//------------------------ +//**************************************************************************************************** +//End for texture auditing use only +//**************************************************************************************************** + +//************************************************************************************** +//below are functions for debug use +//do not delete them even though they are not currently being used. +void check_all_images() +{ + for (std::set<LLImageGL*>::iterator iter = LLImageGL::sImageList.begin(); + iter != LLImageGL::sImageList.end(); iter++) + { + LLImageGL* glimage = *iter; + if (glimage->getTexName() && glimage->isGLTextureCreated()) + { + gGL.getTexUnit(0)->bind(glimage) ; + glimage->checkTexSize() ; + gGL.getTexUnit(0)->unbind(glimage->getTarget()) ; + } + } +} + +void LLImageGL::checkTexSize(bool forced) const +{ + if ((forced || gDebugGL) && mTarget == GL_TEXTURE_2D) + { + { + //check viewport + GLint vp[4] ; + glGetIntegerv(GL_VIEWPORT, vp) ; + llcallstacks << "viewport: " << vp[0] << " : " << vp[1] << " : " << vp[2] << " : " << vp[3] << llcallstacksendl ; + } + + GLint texname; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &texname); + BOOL error = FALSE; + if (texname != mTexName) + { + llinfos << "Bound: " << texname << " Should bind: " << mTexName << " Default: " << LLImageGL::sDefaultGLTexture->getTexName() << llendl; + + error = TRUE; + if (gDebugSession) + { + gFailLog << "Invalid texture bound!" << std::endl; + } + else + { + llerrs << "Invalid texture bound!" << llendl; + } + } + stop_glerror() ; + LLGLint x = 0, y = 0 ; + glGetTexLevelParameteriv(mTarget, 0, GL_TEXTURE_WIDTH, (GLint*)&x); + glGetTexLevelParameteriv(mTarget, 0, GL_TEXTURE_HEIGHT, (GLint*)&y) ; + stop_glerror() ; + llcallstacks << "w: " << x << " h: " << y << llcallstacksendl ; + + if(!x || !y) + { + return ; + } + if(x != (mWidth >> mCurrentDiscardLevel) || y != (mHeight >> mCurrentDiscardLevel)) + { + error = TRUE; + if (gDebugSession) + { + gFailLog << "wrong texture size and discard level!" << + mWidth << " Height: " << mHeight << " Current Level: " << (S32)mCurrentDiscardLevel << std::endl; + } + else + { + llerrs << "wrong texture size and discard level: width: " << + mWidth << " Height: " << mHeight << " Current Level: " << (S32)mCurrentDiscardLevel << llendl ; + } + } + + if (error) + { + ll_fail("LLImageGL::checkTexSize failed."); + } + } +} +//end of debug functions +//************************************************************************************** + //---------------------------------------------------------------------------- +BOOL is_little_endian() +{ + S32 a = 0x12345678; + U8 *c = (U8*)(&a); + + return (*c == 0x78) ; +} +//static +void LLImageGL::initClass(S32 num_catagories) +{ + sMaxCatagories = num_catagories ; + + sTextureMemByCategory.resize(sMaxCatagories); + sTextureMemByCategoryBound.resize(sMaxCatagories) ; + sTextureCurMemByCategoryBound.resize(sMaxCatagories) ; +} + +//static +void LLImageGL::cleanupClass() +{ + sTextureMemByCategory.clear() ; + sTextureMemByCategoryBound.clear() ; + sTextureCurMemByCategoryBound.clear() ; +} + +//static +void LLImageGL::setHighlightTexture(S32 category) +{ + const S32 dim = 128; + sHighlightTexturep = new LLImageGL() ; + LLPointer<LLImageRaw> image_raw = new LLImageRaw(dim,dim,3); + U8* data = image_raw->getData(); + for (S32 i = 0; i<dim; i++) + { + for (S32 j = 0; j<dim; j++) + { + const S32 border = 2; + if (i<border || j<border || i>=(dim-border) || j>=(dim-border)) + { + *data++ = 0xff; + *data++ = 0xff; + *data++ = 0xff; + } + else + { + *data++ = 0xff; + *data++ = 0xff; + *data++ = 0x00; + } + } + } + sHighlightTexturep->createGLTexture(0, image_raw, 0, TRUE, category); + image_raw = NULL; +} //static S32 LLImageGL::dataFormatBits(S32 dataformat) @@ -125,60 +278,37 @@ S32 LLImageGL::dataFormatComponents(S32 dataformat) //---------------------------------------------------------------------------- // static -void LLImageGL::bindExternalTexture(LLGLuint gl_name, S32 stage, LLGLenum bind_target ) +void LLImageGL::updateStats(F32 current_time) { - gGL.flush(); - if (stage > 0) - { - glActiveTextureARB(GL_TEXTURE0_ARB + stage); - } - glBindTexture(bind_target, gl_name); - sCurrentBoundTextures[stage] = gl_name; - if (stage > 0) - { - glActiveTextureARB(GL_TEXTURE0_ARB); - } -} + sLastFrameTime = current_time; + sBoundTextureMemoryInBytes = sCurBoundTextureMemory; + sCurBoundTextureMemory = 0; -// static -void LLImageGL::unbindTexture(S32 stage, LLGLenum bind_target) -{ - // LLGLSLShader can return -1 - if (stage >= 0) + if(gAuditTexture) { - gGL.flush(); - if (stage > 0) + for(U32 i = 0 ; i < sTextureCurBoundCounter.size() ; i++) { - glActiveTextureARB(GL_TEXTURE0_ARB + stage); - glBindTexture(GL_TEXTURE_2D, 0); - glActiveTextureARB(GL_TEXTURE0_ARB); + sTextureBoundCounter[i] = sTextureCurBoundCounter[i] ; + sTextureCurBoundCounter[i] = 0 ; } - else + for(U32 i = 0 ; i < sTextureCurMemByCategoryBound.size() ; i++) { - glBindTexture(GL_TEXTURE_2D, 0); + sTextureMemByCategoryBound[i] = sTextureCurMemByCategoryBound[i] ; + sTextureCurMemByCategoryBound[i] = 0 ; } - sCurrentBoundTextures[stage] = 0; } } -// static (duplicated for speed and to avoid GL_TEXTURE_2D default argument which requires GL header dependency) -void LLImageGL::unbindTexture(S32 stage) -{ - unbindTexture(stage, GL_TEXTURE_2D); -} - -// static -void LLImageGL::updateStats(F32 current_time) -{ - sLastFrameTime = current_time; - sBoundTextureMemory = sCurBoundTextureMemory; - sCurBoundTextureMemory = 0; -} - //static -S32 LLImageGL::updateBoundTexMem(const S32 delta) +S32 LLImageGL::updateBoundTexMem(const S32 mem, const S32 ncomponents, S32 category) { - LLImageGL::sCurBoundTextureMemory += delta; + if(gAuditTexture && ncomponents > 0 && category > -1) + { + sTextureCurBoundCounter[getTextureCounterIndex(mem / ncomponents)]++ ; + sTextureCurMemByCategoryBound[category] += mem ; + } + + LLImageGL::sCurBoundTextureMemory += mem ; return LLImageGL::sCurBoundTextureMemory; } @@ -189,23 +319,30 @@ void LLImageGL::destroyGL(BOOL save_state) { for (S32 stage = 0; stage < gGLManager.mNumTextureUnits; stage++) { - LLImageGL::unbindTexture(stage, GL_TEXTURE_2D); + gGL.getTexUnit(stage)->unbind(LLTexUnit::TT_TEXTURE); } + + sAllowReadBackRaw = true ; for (std::set<LLImageGL*>::iterator iter = sImageList.begin(); iter != sImageList.end(); iter++) { LLImageGL* glimage = *iter; - if (glimage->mTexName && glimage->mComponents) + if (glimage->mTexName) { - if (save_state) + if (save_state && glimage->isGLTextureCreated() && glimage->mComponents) { glimage->mSaveData = new LLImageRaw; - glimage->readBackRaw(glimage->mCurrentDiscardLevel, glimage->mSaveData, false); + if(!glimage->readBackRaw(glimage->mCurrentDiscardLevel, glimage->mSaveData, false)) //necessary, keep it. + { + glimage->mSaveData = NULL ; + } } + glimage->destroyGLTexture(); stop_glerror(); } } + sAllowReadBackRaw = false ; } //static @@ -215,11 +352,15 @@ void LLImageGL::restoreGL() iter != sImageList.end(); iter++) { LLImageGL* glimage = *iter; - if (glimage->mSaveData.notNull() && glimage->mSaveData->getComponents()) + if(glimage->getTexName()) { - if (glimage->getComponents()) + llerrs << "tex name is not 0." << llendl ; + } + if (glimage->mSaveData.notNull()) + { + if (glimage->getComponents() && glimage->mSaveData->getComponents()) { - glimage->createGLTexture(glimage->mCurrentDiscardLevel, glimage->mSaveData); + glimage->createGLTexture(glimage->mCurrentDiscardLevel, glimage->mSaveData, 0, TRUE, glimage->getCategory()); stop_glerror(); } glimage->mSaveData = NULL; // deletes data @@ -229,6 +370,7 @@ void LLImageGL::restoreGL() //---------------------------------------------------------------------------- +//for server side use only. //static BOOL LLImageGL::create(LLPointer<LLImageGL>& dest, BOOL usemipmaps) { @@ -236,12 +378,14 @@ BOOL LLImageGL::create(LLPointer<LLImageGL>& dest, BOOL usemipmaps) return TRUE; } +//for server side use only. BOOL LLImageGL::create(LLPointer<LLImageGL>& dest, U32 width, U32 height, U8 components, BOOL usemipmaps) { dest = new LLImageGL(width, height, components, usemipmaps); return TRUE; } +//for server side use only. BOOL LLImageGL::create(LLPointer<LLImageGL>& dest, const LLImageRaw* imageraw, BOOL usemipmaps) { dest = new LLImageGL(imageraw, usemipmaps); @@ -276,6 +420,7 @@ LLImageGL::LLImageGL(const LLImageRaw* imageraw, BOOL usemipmaps) setSize(0, 0, 0); sImageList.insert(this); sCount++; + createGLTexture(0, imageraw); } @@ -283,41 +428,65 @@ LLImageGL::~LLImageGL() { LLImageGL::cleanup(); sImageList.erase(this); + delete [] mPickMask; + mPickMask = NULL; sCount--; } void LLImageGL::init(BOOL usemipmaps) { -#ifdef DEBUG_MISS - mMissed = FALSE; -#endif + // keep these members in the same order as declared in llimagehl.h + // so that it is obvious by visual inspection if we forgot to + // init a field. + + mTextureMemory = 0; + mLastBindTime = 0.f; - mTextureMemory = 0; - mLastBindTime = 0.f; - - mTarget = GL_TEXTURE_2D; - mBindTarget = GL_TEXTURE_2D; - mUseMipMaps = usemipmaps; - mHasMipMaps = FALSE; - mAutoGenMips = FALSE; - mTexName = 0; - mIsResident = 0; - mClampS = FALSE; - mClampT = FALSE; - mMipFilterNearest = FALSE; - mWidth = 0; - mHeight = 0; - mComponents = 0; + mPickMask = NULL; + mPickMaskWidth = 0; + mPickMaskHeight = 0; + mUseMipMaps = usemipmaps; + mHasExplicitFormat = FALSE; + mAutoGenMips = FALSE; + + mIsMask = FALSE; + mNeedsAlphaAndPickMask = TRUE ; + mAlphaStride = 0 ; + mAlphaOffset = 0 ; + + mGLTextureCreated = FALSE ; + mTexName = 0; + mWidth = 0; + mHeight = 0; + mCurrentDiscardLevel = -1; + + mDiscardLevelInAtlas = -1 ; + mTexelsInAtlas = 0 ; + mTexelsInGLTexture = 0 ; + mTarget = GL_TEXTURE_2D; + mBindTarget = LLTexUnit::TT_TEXTURE; + mHasMipMaps = false; + + mIsResident = 0; + + mComponents = 0; mMaxDiscardLevel = MAX_DISCARD_LEVEL; - mCurrentDiscardLevel = -1; - mDontDiscard = FALSE; + + mTexOptionsDirty = true; + mAddressMode = LLTexUnit::TAM_WRAP; + mFilterOption = LLTexUnit::TFO_ANISOTROPIC; mFormatInternal = -1; mFormatPrimary = (LLGLenum) 0; mFormatType = GL_UNSIGNED_BYTE; mFormatSwapBytes = FALSE; - mHasExplicitFormat = FALSE; + +#ifdef DEBUG_MISS + mMissed = FALSE; +#endif + + mCategory = -1; } void LLImageGL::cleanup() @@ -331,17 +500,19 @@ void LLImageGL::cleanup() //---------------------------------------------------------------------------- +//this function is used to check the size of a texture image. +//so dim should be a positive number static bool check_power_of_two(S32 dim) { - while(dim > 1) + if(dim < 0) { - if (dim & 1) - { - return false; - } - dim >>= 1; + return false ; } - return true; + if(!dim)//0 is a power-of-two number + { + return true ; + } + return !(dim & (dim - 1)) ; } //static @@ -357,7 +528,7 @@ void LLImageGL::setSize(S32 width, S32 height, S32 ncomponents) // Check if dimensions are a power of two! if (!checkSize(width,height)) { - llerrs << llformat("Texture has non power of two dimention: %dx%d",width,height) << llendl; + llerrs << llformat("Texture has non power of two dimension: %dx%d",width,height) << llendl; } if (mTexName) @@ -365,7 +536,12 @@ void LLImageGL::setSize(S32 width, S32 height, S32 ncomponents) // llwarns << "Setting Size of LLImageGL with existing mTexName = " << mTexName << llendl; destroyGLTexture(); } - + + // pickmask validity depends on old image size, delete it + delete [] mPickMask; + mPickMask = NULL; + mPickMaskWidth = mPickMaskHeight = 0; + mWidth = width; mHeight = height; mComponents = ncomponents; @@ -415,79 +591,35 @@ void LLImageGL::dump() } //---------------------------------------------------------------------------- - -BOOL LLImageGL::bindTextureInternal(const S32 stage) const +void LLImageGL::forceUpdateBindStats(void) const { - if (gGLManager.mIsDisabled) - { - llwarns << "Trying to bind a texture while GL is disabled!" << llendl; - } - - - if (sCurrentBoundTextures[stage] && sCurrentBoundTextures[stage] == mTexName) - { - // already set! - return TRUE; - } + mLastBindTime = sLastFrameTime; +} +BOOL LLImageGL::updateBindStats(S32 tex_mem) const +{ if (mTexName != 0) { #ifdef DEBUG_MISS mMissed = ! getIsResident(TRUE); #endif - - gGL.flush(); - if (stage > 0) - { - glActiveTextureARB(GL_TEXTURE0_ARB + stage); - } - - glBindTexture(mBindTarget, mTexName); - sCurrentBoundTextures[stage] = mTexName; sBindCount++; - - if (stage > 0) - { - glActiveTextureARB(GL_TEXTURE0_ARB); - } - if (mLastBindTime != sLastFrameTime) { // we haven't accounted for this texture yet this frame sUniqueCount++; - updateBoundTexMem(mTextureMemory); + updateBoundTexMem(tex_mem, mComponents, mCategory); mLastBindTime = sLastFrameTime; + + return TRUE ; } - - return TRUE; - } - else - { - gGL.flush(); - if (stage > 0) - { - glActiveTextureARB(GL_TEXTURE0_ARB+stage); - } - glBindTexture(mBindTarget, 0); - if (stage > 0) - { - glActiveTextureARB(GL_TEXTURE0_ARB+stage); - } - sCurrentBoundTextures[stage] = 0; - return FALSE; } + return FALSE ; } -//virtual -BOOL LLImageGL::bind(const S32 stage) const +F32 LLImageGL::getTimePassedSinceLastBound() { - if (stage == -1) - { - return FALSE; - } - BOOL res = bindTextureInternal(stage); - //llassert(res); - return res; + return sLastFrameTime - mLastBindTime ; } void LLImageGL::setExplicitFormat( LLGLint internal_format, LLGLenum primary_format, LLGLenum type_format, BOOL swap_bytes ) @@ -502,6 +634,8 @@ void LLImageGL::setExplicitFormat( LLGLint internal_format, LLGLenum primary_for else mFormatType = type_format; mFormatSwapBytes = swap_bytes; + + calcAlphaChannelOffsetAndStride() ; } //---------------------------------------------------------------------------- @@ -517,28 +651,23 @@ void LLImageGL::setImage(const LLImageRaw* imageraw) void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips) { -// LLFastTimer t1(LLFastTimer::FTM_TEMP1); - bool is_compressed = false; if (mFormatPrimary >= GL_COMPRESSED_RGBA_S3TC_DXT1_EXT && mFormatPrimary <= GL_COMPRESSED_RGBA_S3TC_DXT5_EXT) { is_compressed = true; } - { -// LLFastTimer t2(LLFastTimer::FTM_TEMP2); - llverify(bindTextureInternal(0)); - } + llverify(gGL.getTexUnit(0)->bind(this)); if (mUseMipMaps) { -// LLFastTimer t2(LLFastTimer::FTM_TEMP3); if (data_hasmips) { // NOTE: data_in points to largest image; smaller images // are stored BEFORE the largest image for (S32 d=mCurrentDiscardLevel; d<=mMaxDiscardLevel; d++) { + S32 w = getWidth(d); S32 h = getHeight(d); S32 gl_level = d-mCurrentDiscardLevel; @@ -548,14 +677,13 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips) } if (is_compressed) { -// LLFastTimer t2(LLFastTimer::FTM_TEMP4); - S32 tex_size = dataFormatBytes(mFormatPrimary, w, h); + S32 tex_size = dataFormatBytes(mFormatPrimary, w, h); glCompressedTexImage2DARB(mTarget, gl_level, mFormatPrimary, w, h, 0, tex_size, (GLvoid *)data_in); stop_glerror(); } else { -// LLFastTimer t2(LLFastTimer::FTM_TEMP4); +// LLFastTimer t2(FTM_TEMP4); if(mFormatSwapBytes) { @@ -563,8 +691,13 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips) stop_glerror(); } - glTexImage2D(mTarget, gl_level, mFormatInternal, w, h, 0, mFormatPrimary, GL_UNSIGNED_BYTE, (GLvoid*)data_in); - + LLImageGL::setManualImage(mTarget, gl_level, mFormatInternal, w, h, mFormatPrimary, GL_UNSIGNED_BYTE, (GLvoid*)data_in); + if (gl_level == 0) + { + analyzeAlpha(data_in, w, h); + } + updatePickMask(w, h, data_in); + if(mFormatSwapBytes) { glPixelStorei(GL_UNPACK_SWAP_BYTES, 0); @@ -580,10 +713,10 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips) { if (mAutoGenMips) { - glTexParameteri(mBindTarget, GL_GENERATE_MIPMAP_SGIS, TRUE); + glTexParameteri(LLTexUnit::getInternalType(mBindTarget), GL_GENERATE_MIPMAP_SGIS, TRUE); stop_glerror(); { -// LLFastTimer t2(LLFastTimer::FTM_TEMP4); +// LLFastTimer t2(FTM_TEMP4); if(mFormatSwapBytes) { @@ -591,12 +724,18 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips) stop_glerror(); } - glTexImage2D(mTarget, 0, mFormatInternal, - getWidth(mCurrentDiscardLevel), getHeight(mCurrentDiscardLevel), 0, + S32 w = getWidth(mCurrentDiscardLevel); + S32 h = getHeight(mCurrentDiscardLevel); + + LLImageGL::setManualImage(mTarget, 0, mFormatInternal, + w, h, mFormatPrimary, mFormatType, data_in); + analyzeAlpha(data_in, w, h); stop_glerror(); + updatePickMask(w, h, data_in); + if(mFormatSwapBytes) { glPixelStorei(GL_UNPACK_SWAP_BYTES, 0); @@ -637,15 +776,23 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips) } llassert(w > 0 && h > 0 && cur_mip_data); { -// LLFastTimer t1(LLFastTimer::FTM_TEMP4); +// LLFastTimer t1(FTM_TEMP4); if(mFormatSwapBytes) { glPixelStorei(GL_UNPACK_SWAP_BYTES, 1); stop_glerror(); } - glTexImage2D(mTarget, m, mFormatInternal, w, h, 0, mFormatPrimary, mFormatType, cur_mip_data); + LLImageGL::setManualImage(mTarget, m, mFormatInternal, w, h, mFormatPrimary, mFormatType, cur_mip_data); + if (m == 0) + { + analyzeAlpha(data_in, w, h); + } stop_glerror(); + if (m == 0) + { + updatePickMask(w, h, cur_mip_data); + } if(mFormatSwapBytes) { @@ -673,11 +820,10 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips) { llerrs << "Compressed Image has mipmaps but data does not (can not auto generate compressed mips)" << llendl; } - mHasMipMaps = TRUE; + mHasMipMaps = true; } else { -// LLFastTimer t2(LLFastTimer::FTM_TEMP5); S32 w = getWidth(); S32 h = getHeight(); if (is_compressed) @@ -694,8 +840,12 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips) stop_glerror(); } - glTexImage2D(mTarget, 0, mFormatInternal, w, h, 0, + LLImageGL::setManualImage(mTarget, 0, mFormatInternal, w, h, mFormatPrimary, mFormatType, (GLvoid *)data_in); + analyzeAlpha(data_in, w, h); + + updatePickMask(w, h, data_in); + stop_glerror(); if(mFormatSwapBytes) @@ -705,12 +855,99 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips) } } - mHasMipMaps = FALSE; + mHasMipMaps = false; + } + stop_glerror(); + mGLTextureCreated = true; +} + +BOOL LLImageGL::preAddToAtlas(S32 discard_level, const LLImageRaw* raw_image) +{ + if (gGLManager.mIsDisabled) + { + llwarns << "Trying to create a texture while GL is disabled!" << llendl; + return FALSE; + } + llassert(gGLManager.mInited); + stop_glerror(); + + if (discard_level < 0) + { + llassert(mCurrentDiscardLevel >= 0); + discard_level = mCurrentDiscardLevel; + } + discard_level = llclamp(discard_level, 0, (S32)mMaxDiscardLevel); + + // Actual image width/height = raw image width/height * 2^discard_level + S32 w = raw_image->getWidth() << discard_level; + S32 h = raw_image->getHeight() << discard_level; + + // setSize may call destroyGLTexture if the size does not match + setSize(w, h, raw_image->getComponents()); + + if( !mHasExplicitFormat ) + { + switch (mComponents) + { + case 1: + // Use luminance alpha (for fonts) + mFormatInternal = GL_LUMINANCE8; + mFormatPrimary = GL_LUMINANCE; + mFormatType = GL_UNSIGNED_BYTE; + break; + case 2: + // Use luminance alpha (for fonts) + mFormatInternal = GL_LUMINANCE8_ALPHA8; + mFormatPrimary = GL_LUMINANCE_ALPHA; + mFormatType = GL_UNSIGNED_BYTE; + break; + case 3: + mFormatInternal = GL_RGB8; + mFormatPrimary = GL_RGB; + mFormatType = GL_UNSIGNED_BYTE; + break; + case 4: + mFormatInternal = GL_RGBA8; + mFormatPrimary = GL_RGBA; + mFormatType = GL_UNSIGNED_BYTE; + break; + default: + llerrs << "Bad number of components for texture: " << (U32)getComponents() << llendl; + } } + + mCurrentDiscardLevel = discard_level; + mDiscardLevelInAtlas = discard_level; + mTexelsInAtlas = raw_image->getWidth() * raw_image->getHeight() ; + mLastBindTime = sLastFrameTime; + mGLTextureCreated = false ; + + glPixelStorei(GL_UNPACK_ROW_LENGTH, raw_image->getWidth()); stop_glerror(); + + if(mFormatSwapBytes) + { + glPixelStorei(GL_UNPACK_SWAP_BYTES, 1); + stop_glerror(); + } + + return TRUE ; } -BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height) +void LLImageGL::postAddToAtlas() +{ + if(mFormatSwapBytes) + { + glPixelStorei(GL_UNPACK_SWAP_BYTES, 0); + stop_glerror(); + } + + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + gGL.getTexUnit(0)->setTextureFilteringOption(mFilterOption); + stop_glerror(); +} + +BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height, BOOL force_fast_update) { if (!width || !height) { @@ -727,7 +964,8 @@ BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S3 return FALSE; } - if (x_pos == 0 && y_pos == 0 && width == getWidth() && height == getHeight() && data_width == width && data_height == height) + // HACK: allow the caller to explicitly force the fast path (i.e. using glTexSubImage2D here instead of calling setImage) even when updating the full texture. + if (!force_fast_update && x_pos == 0 && y_pos == 0 && width == getWidth() && height == getHeight() && data_width == width && data_height == height) { setImage(datap, FALSE); } @@ -781,14 +1019,13 @@ BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S3 datap += (y_pos * data_width + x_pos) * getComponents(); // Update the GL texture - BOOL res = bindTextureInternal(0); + BOOL res = gGL.getTexUnit(0)->bindManual(mBindTarget, mTexName); if (!res) llerrs << "LLImageGL::setSubImage(): bindTexture failed" << llendl; stop_glerror(); - LLGLEnable tex( GL_TEXTURE_2D ); - glTexSubImage2D(mTarget, 0, x_pos, y_pos, width, height, mFormatPrimary, mFormatType, datap); + gGL.getTexUnit(0)->disable(); stop_glerror(); if(mFormatSwapBytes) @@ -799,22 +1036,31 @@ BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S3 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); stop_glerror(); + mGLTextureCreated = true; } - return TRUE; } -BOOL LLImageGL::setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height) +BOOL LLImageGL::setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height, BOOL force_fast_update) { - return setSubImage(imageraw->getData(), imageraw->getWidth(), imageraw->getHeight(), x_pos, y_pos, width, height); + return setSubImage(imageraw->getData(), imageraw->getWidth(), imageraw->getHeight(), x_pos, y_pos, width, height, force_fast_update); } // Copy sub image from frame buffer BOOL LLImageGL::setSubImageFromFrameBuffer(S32 fb_x, S32 fb_y, S32 x_pos, S32 y_pos, S32 width, S32 height) { - if (bindTextureInternal(0)) + if (gGL.getTexUnit(0)->bind(this, false, true)) { + if(gGLManager.mDebugGPU) + { + llinfos << "Calling glCopyTexSubImage2D(...)" << llendl ; + checkTexSize(true) ; + llcallstacks << fb_x << " : " << fb_y << " : " << x_pos << " : " << y_pos << " : " << width << " : " << height << + " : " << (S32)mComponents << llcallstacksendl ; + } + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, fb_x, fb_y, x_pos, y_pos, width, height); + mGLTextureCreated = true; stop_glerror(); return TRUE; } @@ -824,14 +1070,68 @@ BOOL LLImageGL::setSubImageFromFrameBuffer(S32 fb_x, S32 fb_y, S32 x_pos, S32 y_ } } -BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename/*=0*/) +// static +void LLImageGL::generateTextures(S32 numTextures, U32 *textures) +{ + glGenTextures(numTextures, (GLuint*)textures); +} + +// static +void LLImageGL::deleteTextures(S32 numTextures, U32 *textures) +{ + for (S32 i = 0; i < numTextures; i++) + { + sDeadTextureList.push_back(textures[i]); + } +} + +// static +void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 width, S32 height, U32 pixformat, U32 pixtype, const void *pixels) +{ + glTexImage2D(target, miplevel, intformat, width, height, 0, pixformat, pixtype, pixels); + stop_glerror(); +} + +//create an empty GL texture: just create a texture name +//the texture is assiciate with some image by calling glTexImage outside LLImageGL +BOOL LLImageGL::createGLTexture() +{ + if (gGLManager.mIsDisabled) + { + llwarns << "Trying to create a texture while GL is disabled!" << llendl; + return FALSE; + } + + mGLTextureCreated = false ; //do not save this texture when gl is destroyed. + + llassert(gGLManager.mInited); + stop_glerror(); + + if(mTexName) + { + glDeleteTextures(1, (reinterpret_cast<GLuint*>(&mTexName))) ; + } + + glGenTextures(1, (GLuint*)&mTexName); + stop_glerror(); + if (!mTexName) + { + llerrs << "LLImageGL::createGLTexture failed to make an empty texture" << llendl; + } + + return TRUE ; +} + +BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename/*=0*/, BOOL to_create, S32 category) { if (gGLManager.mIsDisabled) { llwarns << "Trying to create a texture while GL is disabled!" << llendl; return FALSE; } - llassert(gGLManager.mInited || gNoRender); + + mGLTextureCreated = false ; + llassert(gGLManager.mInited); stop_glerror(); if (discard_level < 0) @@ -842,8 +1142,10 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S discard_level = llclamp(discard_level, 0, (S32)mMaxDiscardLevel); // Actual image width/height = raw image width/height * 2^discard_level - S32 w = imageraw->getWidth() << discard_level; - S32 h = imageraw->getHeight() << discard_level; + S32 raw_w = imageraw->getWidth() ; + S32 raw_h = imageraw->getHeight() ; + S32 w = raw_w << discard_level; + S32 h = raw_h << discard_level; // setSize may call destroyGLTexture if the size does not match setSize(w, h, imageraw->getComponents()); @@ -877,8 +1179,19 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S default: llerrs << "Bad number of components for texture: " << (U32)getComponents() << llendl; } + + calcAlphaChannelOffsetAndStride() ; } + if(!to_create) //not create a gl texture + { + destroyGLTexture(); + mCurrentDiscardLevel = discard_level; + mLastBindTime = sLastFrameTime; + return TRUE ; + } + + setCategory(category) ; const U8* rawdata = imageraw->getData(); return createGLTexture(discard_level, rawdata, FALSE, usename); } @@ -901,7 +1214,7 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_ return TRUE; } - GLuint old_name = mTexName; + U32 old_name = mTexName; // S32 old_discard = mCurrentDiscardLevel; if (usename != 0) @@ -910,13 +1223,12 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_ } else { - glGenTextures(1, (GLuint*)&mTexName); + LLImageGL::generateTextures(1, &mTexName); stop_glerror(); { -// LLFastTimer t1(LLFastTimer::FTM_TEMP6); - llverify(bindTextureInternal(0)); - glTexParameteri(mBindTarget, GL_TEXTURE_BASE_LEVEL, 0); - glTexParameteri(mBindTarget, GL_TEXTURE_MAX_LEVEL, mMaxDiscardLevel-discard_level); + llverify(gGL.getTexUnit(0)->bind(this)); + glTexParameteri(LLTexUnit::getInternalType(mBindTarget), GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(LLTexUnit::getInternalType(mBindTarget), GL_TEXTURE_MAX_LEVEL, mMaxDiscardLevel-discard_level); } } if (!mTexName) @@ -936,115 +1248,69 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_ #endif } - mCurrentDiscardLevel = discard_level; + mCurrentDiscardLevel = discard_level; setImage(data_in, data_hasmips); - setClamp(mClampS, mClampT); - setMipFilterNearest(mMipFilterNearest); - + // Set texture options to our defaults. + gGL.getTexUnit(0)->setHasMipMaps(mHasMipMaps); + gGL.getTexUnit(0)->setTextureAddressMode(mAddressMode); + gGL.getTexUnit(0)->setTextureFilteringOption(mFilterOption); + // things will break if we don't unbind after creation - unbindTexture(0, mBindTarget); + gGL.getTexUnit(0)->unbind(mBindTarget); stop_glerror(); if (old_name != 0) { - sGlobalTextureMemory -= mTextureMemory; - glDeleteTextures(1, &old_name); - stop_glerror(); - } + sGlobalTextureMemoryInBytes -= mTextureMemory; - mTextureMemory = getMipBytes(discard_level); - sGlobalTextureMemory += mTextureMemory; - - // mark this as bound at this point, so we don't throw it out immediately - mLastBindTime = sLastFrameTime; + if(gAuditTexture) + { + decTextureCounter(mTextureMemory, mComponents, mCategory) ; + } - return TRUE; -} + LLImageGL::deleteTextures(1, &old_name); -BOOL LLImageGL::setDiscardLevel(S32 discard_level) -{ - llassert(discard_level >= 0); - llassert(mCurrentDiscardLevel >= 0); + stop_glerror(); + } - discard_level = llclamp(discard_level, 0, (S32)mMaxDiscardLevel); + mTextureMemory = getMipBytes(discard_level); + sGlobalTextureMemoryInBytes += mTextureMemory; + mTexelsInGLTexture = getWidth() * getHeight() ; - if (mDontDiscard) + if(gAuditTexture) { - // don't discard! - return FALSE; + incTextureCounter(mTextureMemory, mComponents, mCategory) ; } - else if (discard_level == mCurrentDiscardLevel) - { - // nothing to do - return FALSE; - } - else if (discard_level < mCurrentDiscardLevel) - { - // larger image - dump(); - llerrs << "LLImageGL::setDiscardLevel() called with larger discard level; use createGLTexture()" << llendl; - return FALSE; - } - else if (mUseMipMaps) - { - LLPointer<LLImageRaw> imageraw = new LLImageRaw; - while(discard_level > mCurrentDiscardLevel) - { - if (readBackRaw(discard_level, imageraw, false)) - { - break; - } - discard_level--; - } - if (discard_level == mCurrentDiscardLevel) - { - // unable to increase the discard level - return FALSE; - } - return createGLTexture(discard_level, imageraw); - } - else - { -#if !LL_LINUX && !LL_SOLARIS - // *FIX: This should not be skipped for the linux client. - llerrs << "LLImageGL::setDiscardLevel() called on image without mipmaps" << llendl; -#endif - return FALSE; - } -} - -BOOL LLImageGL::isValidForSculpt(S32 discard_level, S32 image_width, S32 image_height, S32 ncomponents) -{ - assert_glerror(); - S32 gl_discard = discard_level - mCurrentDiscardLevel; - LLGLint glwidth = 0; - glGetTexLevelParameteriv(mTarget, gl_discard, GL_TEXTURE_WIDTH, (GLint*)&glwidth); - LLGLint glheight = 0; - glGetTexLevelParameteriv(mTarget, gl_discard, GL_TEXTURE_HEIGHT, (GLint*)&glheight); - LLGLint glcomponents = 0 ; - glGetTexLevelParameteriv(mTarget, gl_discard, GL_TEXTURE_INTERNAL_FORMAT, (GLint*)&glcomponents); - assert_glerror(); - - return glwidth >= image_width && glheight >= image_height && (GL_RGB8 == glcomponents || GL_RGBA8 == glcomponents) ; + // mark this as bound at this point, so we don't throw it out immediately + mLastBindTime = sLastFrameTime; + return TRUE; } -BOOL LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compressed_ok) +BOOL LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compressed_ok) const { + llassert_always(sAllowReadBackRaw) ; + //llerrs << "should not call this function!" << llendl ; + if (discard_level < 0) { discard_level = mCurrentDiscardLevel; } - if (mTexName == 0 || discard_level < mCurrentDiscardLevel) + if (mTexName == 0 || discard_level < mCurrentDiscardLevel || discard_level > mMaxDiscardLevel ) { return FALSE; } S32 gl_discard = discard_level - mCurrentDiscardLevel; - llverify(bindTextureInternal(0)); + //explicitly unbind texture + gGL.getTexUnit(0)->unbind(mBindTarget); + llverify(gGL.getTexUnit(0)->bindManual(mBindTarget, mTexName)); + + //debug code, leave it there commented. + //checkTexSize() ; LLGLint glwidth = 0; glGetTexLevelParameteriv(mTarget, gl_discard, GL_TEXTURE_WIDTH, (GLint*)&glwidth); @@ -1053,7 +1319,7 @@ BOOL LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compre // No mip data smaller than current discard level return FALSE; } - + S32 width = getWidth(discard_level); S32 height = getHeight(discard_level); S32 ncomponents = getComponents(); @@ -1061,6 +1327,13 @@ BOOL LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compre { return FALSE; } + if(width < glwidth) + { + llwarns << "texture size is smaller than it should be." << llendl ; + llwarns << "width: " << width << " glwidth: " << glwidth << " mWidth: " << mWidth << + " mCurrentDiscardLevel: " << (S32)mCurrentDiscardLevel << " discard_level: " << (S32)discard_level << llendl ; + return FALSE ; + } if (width <= 0 || width > 2048 || height <= 0 || height > 2048 || ncomponents < 1 || ncomponents > 4) { @@ -1122,91 +1395,84 @@ BOOL LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compre return FALSE ; } //----------------------------------------------------------------------------------------------- - + return TRUE ; } -void LLImageGL::destroyGLTexture() +void LLImageGL::deleteDeadTextures() { - stop_glerror(); - - if (mTexName != 0) + while (!sDeadTextureList.empty()) { + GLuint tex = sDeadTextureList.front(); + sDeadTextureList.pop_front(); for (int i = 0; i < gGLManager.mNumTextureUnits; i++) { - if (sCurrentBoundTextures[i] == mTexName) + if (sCurrentBoundTextures[i] == tex) { - unbindTexture(i, GL_TEXTURE_2D); + gGL.getTexUnit(i)->unbind(LLTexUnit::TT_TEXTURE); stop_glerror(); } } - - sGlobalTextureMemory -= mTextureMemory; - mTextureMemory = 0; - - glDeleteTextures(1, (GLuint*)&mTexName); - mTexName = 0; - + + glDeleteTextures(1, &tex); stop_glerror(); } } + +void LLImageGL::destroyGLTexture() +{ + if (mTexName != 0) + { + if(mTextureMemory) + { + if(gAuditTexture) + { + decTextureCounter(mTextureMemory, mComponents, mCategory) ; + } + sGlobalTextureMemoryInBytes -= mTextureMemory; + mTextureMemory = 0; + } + + LLImageGL::deleteTextures(1, &mTexName); + mTexName = 0; + mCurrentDiscardLevel = -1 ; //invalidate mCurrentDiscardLevel. + mGLTextureCreated = FALSE ; + } +} + + //---------------------------------------------------------------------------- -void LLImageGL::setClamp(BOOL clamps, BOOL clampt) +void LLImageGL::setAddressMode(LLTexUnit::eTextureAddressMode mode) { - mClampS = clamps; - mClampT = clampt; - if (mTexName != 0) + if (mAddressMode != mode) { - glTexParameteri(mBindTarget, GL_TEXTURE_WRAP_S, clamps ? GL_CLAMP_TO_EDGE : GL_REPEAT); - glTexParameteri(mBindTarget, GL_TEXTURE_WRAP_T, clampt ? GL_CLAMP_TO_EDGE : GL_REPEAT); + mTexOptionsDirty = true; + mAddressMode = mode; + } + + if (gGL.getTexUnit(gGL.getCurrentTexUnitIndex())->getCurrTexture() == mTexName) + { + gGL.getTexUnit(gGL.getCurrentTexUnitIndex())->setTextureAddressMode(mode); + mTexOptionsDirty = false; } - stop_glerror(); } -void LLImageGL::setMipFilterNearest(BOOL nearest, BOOL min_nearest) +void LLImageGL::setFilteringOption(LLTexUnit::eTextureFilterOptions option) { - mMipFilterNearest = nearest; + if (mFilterOption != option) + { + mTexOptionsDirty = true; + mFilterOption = option; + } - if (mTexName != 0) + if (mTexName != 0 && gGL.getTexUnit(gGL.getCurrentTexUnitIndex())->getCurrTexture() == mTexName) { - if (min_nearest) - { - glTexParameteri(mBindTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - } - else if (mHasMipMaps) - { - glTexParameteri(mBindTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - } - else - { - glTexParameteri(mBindTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - } - if (mMipFilterNearest) - { - glTexParameteri(mBindTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - } - else - { - glTexParameteri(mBindTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - } - if (gGLManager.mHasAnisotropic) - { - if (sGlobalUseAnisotropic && !mMipFilterNearest) - { - F32 largest_anisotropy; - glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &largest_anisotropy); - glTexParameterf(mBindTarget, GL_TEXTURE_MAX_ANISOTROPY_EXT, largest_anisotropy); - } - else - { - glTexParameterf(mBindTarget, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.f); - } - } + gGL.getTexUnit(gGL.getCurrentTexUnitIndex())->setTextureFilteringOption(option); + mTexOptionsDirty = false; + stop_glerror(); } - - stop_glerror(); } BOOL LLImageGL::getIsResident(BOOL test_now) @@ -1282,18 +1548,399 @@ S32 LLImageGL::getMipBytes(S32 discard_level) const return res; } +BOOL LLImageGL::isJustBound() const +{ + return (BOOL)(sLastFrameTime - mLastBindTime < 0.5f); +} + BOOL LLImageGL::getBoundRecently() const { return (BOOL)(sLastFrameTime - mLastBindTime < MIN_TEXTURE_LIFETIME); } -void LLImageGL::setTarget(const LLGLenum target, const LLGLenum bind_target) +void LLImageGL::setTarget(const LLGLenum target, const LLTexUnit::eTextureType bind_target) { mTarget = target; mBindTarget = bind_target; } +const S8 INVALID_OFFSET = -99 ; +void LLImageGL::setNeedsAlphaAndPickMask(BOOL need_mask) +{ + if(mNeedsAlphaAndPickMask != need_mask) + { + mNeedsAlphaAndPickMask = need_mask; + + if(mNeedsAlphaAndPickMask) + { + mAlphaOffset = 0 ; + } + else //do not need alpha mask + { + mAlphaOffset = INVALID_OFFSET ; + mIsMask = FALSE; + } + } +} + +void LLImageGL::calcAlphaChannelOffsetAndStride() +{ + if(mAlphaOffset == INVALID_OFFSET)//do not need alpha mask + { + return ; + } + + mAlphaStride = -1 ; + switch (mFormatPrimary) + { + case GL_LUMINANCE: + case GL_ALPHA: + mAlphaStride = 1; + break; + case GL_LUMINANCE_ALPHA: + mAlphaStride = 2; + break; + case GL_RGB: + mNeedsAlphaAndPickMask = FALSE ; + mIsMask = FALSE; + return ; //no alpha channel. + case GL_RGBA: + mAlphaStride = 4; + break; + case GL_BGRA_EXT: + mAlphaStride = 4; + break; + default: + break; + } + + mAlphaOffset = -1 ; + if (mFormatType == GL_UNSIGNED_BYTE) + { + mAlphaOffset = mAlphaStride - 1 ; + } + else if(is_little_endian()) + { + if (mFormatType == GL_UNSIGNED_INT_8_8_8_8) + { + mAlphaOffset = 0 ; + } + else if (mFormatType == GL_UNSIGNED_INT_8_8_8_8_REV) + { + mAlphaOffset = 3 ; + } + } + else //big endian + { + if (mFormatType == GL_UNSIGNED_INT_8_8_8_8) + { + mAlphaOffset = 3 ; + } + else if (mFormatType == GL_UNSIGNED_INT_8_8_8_8_REV) + { + mAlphaOffset = 0 ; + } + } + + if( mAlphaStride < 1 || //unsupported format + mAlphaOffset < 0 || //unsupported type + (mFormatPrimary == GL_BGRA_EXT && mFormatType != GL_UNSIGNED_BYTE)) //unknown situation + { + llwarns << "Cannot analyze alpha for image with format type " << std::hex << mFormatType << std::dec << llendl; + + mNeedsAlphaAndPickMask = FALSE ; + mIsMask = FALSE; + } +} + +void LLImageGL::analyzeAlpha(const void* data_in, U32 w, U32 h) +{ + if(!mNeedsAlphaAndPickMask) + { + return ; + } + + U32 length = w * h; + U32 alphatotal = 0; + + U32 sample[16]; + memset(sample, 0, sizeof(U32)*16); + + // generate histogram of quantized alpha. + // also add-in the histogram of a 2x2 box-sampled version. The idea is + // this will mid-skew the data (and thus increase the chances of not + // being used as a mask) from high-frequency alpha maps which + // suffer the worst from aliasing when used as alpha masks. + if (w >= 2 && h >= 2) + { + llassert(w%2 == 0); + llassert(h%2 == 0); + const GLubyte* rowstart = ((const GLubyte*) data_in) + mAlphaOffset; + for (U32 y = 0; y < h; y+=2) + { + const GLubyte* current = rowstart; + for (U32 x = 0; x < w; x+=2) + { + const U32 s1 = current[0]; + alphatotal += s1; + const U32 s2 = current[w * mAlphaStride]; + alphatotal += s2; + current += mAlphaStride; + const U32 s3 = current[0]; + alphatotal += s3; + const U32 s4 = current[w * mAlphaStride]; + alphatotal += s4; + current += mAlphaStride; + + ++sample[s1/16]; + ++sample[s2/16]; + ++sample[s3/16]; + ++sample[s4/16]; + + const U32 asum = (s1+s2+s3+s4); + alphatotal += asum; + sample[asum/(16*4)] += 4; + } + + rowstart += 2 * w * mAlphaStride; + } + length *= 2; // we sampled everything twice, essentially + } + else + { + const GLubyte* current = ((const GLubyte*) data_in) + mAlphaOffset; + for (U32 i = 0; i < length; i++) + { + const U32 s1 = *current; + alphatotal += s1; + ++sample[s1/16]; + current += mAlphaStride; + } + } + + // if more than 1/16th of alpha samples are mid-range, this + // shouldn't be treated as a 1-bit mask + + // also, if all of the alpha samples are clumped on one half + // of the range (but not at an absolute extreme), then consider + // this to be an intentional effect and don't treat as a mask. + + U32 midrangetotal = 0; + for (U32 i = 4; i < 11; i++) + { + midrangetotal += sample[i]; + } + U32 lowerhalftotal = 0; + for (U32 i = 0; i < 8; i++) + { + lowerhalftotal += sample[i]; + } + U32 upperhalftotal = 0; + for (U32 i = 8; i < 16; i++) + { + upperhalftotal += sample[i]; + } + + if (midrangetotal > length/16 || // lots of midrange, or + (lowerhalftotal == length && alphatotal != 0) || // all close to transparent but not all totally transparent, or + (upperhalftotal == length && alphatotal != 255*length)) // all close to opaque but not all totally opaque + { + mIsMask = FALSE; // not suitable for masking + } + else + { + mIsMask = TRUE; + } +} + //---------------------------------------------------------------------------- +void LLImageGL::updatePickMask(S32 width, S32 height, const U8* data_in) +{ + if(!mNeedsAlphaAndPickMask) + { + return ; + } + + delete [] mPickMask; + mPickMask = NULL; + mPickMaskWidth = mPickMaskHeight = 0; + + if (mFormatType != GL_UNSIGNED_BYTE || + mFormatPrimary != GL_RGBA) + { + //cannot generate a pick mask for this texture + return; + } + + U32 pick_width = width/2 + 1; + U32 pick_height = height/2 + 1; + + U32 size = pick_width * pick_height; + size = (size + 7) / 8; // pixelcount-to-bits + mPickMask = new U8[size]; + mPickMaskWidth = pick_width - 1; + mPickMaskHeight = pick_height - 1; + + memset(mPickMask, 0, sizeof(U8) * size); + + U32 pick_bit = 0; + + for (S32 y = 0; y < height; y += 2) + { + for (S32 x = 0; x < width; x += 2) + { + U8 alpha = data_in[(y*width+x)*4+3]; + + if (alpha > 32) + { + U32 pick_idx = pick_bit/8; + U32 pick_offset = pick_bit%8; + llassert(pick_idx < size); + + mPickMask[pick_idx] |= 1 << pick_offset; + } + + ++pick_bit; + } + } +} + +BOOL LLImageGL::getMask(const LLVector2 &tc) +{ + BOOL res = TRUE; + + if (mPickMask) + { + F32 u,v; + if (LL_LIKELY(tc.isFinite())) + { + u = tc.mV[0] - floorf(tc.mV[0]); + v = tc.mV[1] - floorf(tc.mV[1]); + } + else + { + LL_WARNS_ONCE("render") << "Ugh, non-finite u/v in mask pick" << LL_ENDL; + u = v = 0.f; + // removing assert per EXT-4388 + // llassert(false); + } + + if (LL_UNLIKELY(u < 0.f || u > 1.f || + v < 0.f || v > 1.f)) + { + LL_WARNS_ONCE("render") << "Ugh, u/v out of range in image mask pick" << LL_ENDL; + u = v = 0.f; + // removing assert per EXT-4388 + // llassert(false); + } + + S32 x = llfloor(u * mPickMaskWidth); + S32 y = llfloor(v * mPickMaskHeight); + + if (LL_UNLIKELY(x > mPickMaskWidth)) + { + LL_WARNS_ONCE("render") << "Ooh, width overrun on pick mask read, that coulda been bad." << LL_ENDL; + x = llmax((U16)0, mPickMaskWidth); + } + if (LL_UNLIKELY(y > mPickMaskHeight)) + { + LL_WARNS_ONCE("render") << "Ooh, height overrun on pick mask read, that woulda been bad." << LL_ENDL; + y = llmax((U16)0, mPickMaskHeight); + } + + S32 idx = y*mPickMaskWidth+x; + S32 offset = idx%8; + + res = mPickMask[idx/8] & (1 << offset) ? TRUE : FALSE; + } + + return res; +} + +void LLImageGL::setCategory(S32 category) +{ + if(!gAuditTexture) + { + return ; + } + if(mCategory != category) + { + if(mCategory > -1) + { + sTextureMemByCategory[mCategory] -= mTextureMemory ; + } + if(category > -1 && category < sMaxCatagories) + { + sTextureMemByCategory[category] += mTextureMemory ; + mCategory = category; + } + else + { + mCategory = -1 ; + } + } +} + +//for debug use +//val is a "power of two" number +S32 LLImageGL::getTextureCounterIndex(U32 val) +{ + //index range is [0, MAX_TEXTURE_LOG_SIZE]. + if(val < 2) + { + return 0 ; + } + else if(val >= (1 << MAX_TEXTURE_LOG_SIZE)) + { + return MAX_TEXTURE_LOG_SIZE ; + } + else + { + S32 ret = 0 ; + while(val >>= 1) + { + ++ret; + } + return ret ; + } +} + +//static +void LLImageGL::incTextureCounter(U32 val, S32 ncomponents, S32 category) +{ + sTextureLoadedCounter[getTextureCounterIndex(val)]++ ; + sTextureMemByCategory[category] += (S32)val * ncomponents ; +} + +//static +void LLImageGL::decTextureCounter(U32 val, S32 ncomponents, S32 category) +{ + sTextureLoadedCounter[getTextureCounterIndex(val)]-- ; + sTextureMemByCategory[category] += (S32)val * ncomponents ; +} + +void LLImageGL::setCurTexSizebar(S32 index, BOOL set_pick_size) +{ + sCurTexSizeBar = index ; + + if(set_pick_size) + { + sCurTexPickSize = (1 << index) ; + } + else + { + sCurTexPickSize = -1 ; + } +} +void LLImageGL::resetCurTexSizebar() +{ + sCurTexSizeBar = -1 ; + sCurTexPickSize = -1 ; +} +//---------------------------------------------------------------------------- + +//---------------------------------------------------------------------------- + // Manual Mip Generation /* @@ -1326,7 +1973,7 @@ void LLImageGL::setTarget(const LLGLenum target, const LLGLenum bind_target) llassert(w > 0 && h > 0 && cur_mip_data); U8 test = cur_mip_data[w*h*mComponents-1]; { - glTexImage2D(mTarget, m, mFormatInternal, w, h, 0, mFormatPrimary, mFormatType, cur_mip_data); + LLImageGL::setManualImage(mTarget, m, mFormatInternal, w, h, mFormatPrimary, mFormatType, cur_mip_data); stop_glerror(); } if (prev_mip_data && prev_mip_data != rawdata) diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h index 82ea147d6e..87a835cdcc 100644 --- a/indra/llrender/llimagegl.h +++ b/indra/llrender/llimagegl.h @@ -2,30 +2,25 @@ * @file llimagegl.h * @brief Object for managing images and their textures * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2007, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlife.com/developers/opensource/gplv2 + * Copyright (C) 2010, Linden Research, Inc. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at http://secondlife.com/developers/opensource/flossexception + * 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; + * version 2.1 of the License only. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -36,23 +31,32 @@ #include "llimage.h" #include "llgltypes.h" -#include "llmemory.h" +#include "llpointer.h" +#include "llrefcount.h" +#include "v2math.h" -//============================================================================ +#include "llrender.h" +class LLTextureAtlas ; +#define BYTES_TO_MEGA_BYTES(x) ((x) >> 20) +#define MEGA_BYTES_TO_BYTES(x) ((x) << 20) +//============================================================================ class LLImageGL : public LLRefCount { + friend class LLTexUnit; public: + static std::list<U32> sDeadTextureList; + + static void deleteDeadTextures(); + // Size calculation static S32 dataFormatBits(S32 dataformat); static S32 dataFormatBytes(S32 dataformat, S32 width, S32 height); static S32 dataFormatComponents(S32 dataformat); - // Wrapper for glBindTexture that keeps LLImageGL in sync. - // Usually you want stage = 0 and bind_target = GL_TEXTURE_2D - static void bindExternalTexture( LLGLuint gl_name, S32 stage, LLGLenum bind_target); - static void unbindTexture(S32 stage, LLGLenum target); - static void unbindTexture(S32 stage); // Uses GL_TEXTURE_2D (not a default arg to avoid gl.h dependency) + BOOL updateBindStats(S32 tex_mem) const ; + F32 getTimePassedSinceLastBound(); + void forceUpdateBindStats(void) const; // needs to be called every frame static void updateStats(F32 current_time); @@ -61,17 +65,18 @@ public: static void destroyGL(BOOL save_state = TRUE); static void restoreGL(); - // Sometimes called externally for textures not using LLImageGL (should go away...) - static S32 updateBoundTexMem(const S32 delta); - - static bool checkSize(S32 width, S32 height); + // Sometimes called externally for textures not using LLImageGL (should go away...) + static S32 updateBoundTexMem(const S32 mem, const S32 ncomponents, S32 category) ; + static bool checkSize(S32 width, S32 height); + + //for server side use only. // Not currently necessary for LLImageGL, but required in some derived classes, // so include for compatability static BOOL create(LLPointer<LLImageGL>& dest, BOOL usemipmaps = TRUE); static BOOL create(LLPointer<LLImageGL>& dest, U32 width, U32 height, U8 components, BOOL usemipmaps = TRUE); static BOOL create(LLPointer<LLImageGL>& dest, const LLImageRaw* imageraw, BOOL usemipmaps = TRUE); - + public: LLImageGL(BOOL usemipmaps = TRUE); LLImageGL(U32 width, U32 height, U8 components, BOOL usemipmaps = TRUE); @@ -79,117 +84,167 @@ public: protected: virtual ~LLImageGL(); - BOOL bindTextureInternal(const S32 stage = 0) const; + + void analyzeAlpha(const void* data_in, U32 w, U32 h); + void calcAlphaChannelOffsetAndStride(); public: virtual void dump(); // debugging info to llinfos - virtual BOOL bind(const S32 stage = 0) const; - + void setSize(S32 width, S32 height, S32 ncomponents); + void setComponents(S32 ncomponents) { mComponents = (S8)ncomponents ;} + + // These 3 functions currently wrap glGenTextures(), glDeleteTextures(), and glTexImage2D() + // for tracking purposes and will be deprecated in the future + static void generateTextures(S32 numTextures, U32 *textures); + static void deleteTextures(S32 numTextures, U32 *textures); + static void setManualImage(U32 target, S32 miplevel, S32 intformat, S32 width, S32 height, U32 pixformat, U32 pixtype, const void *pixels); - BOOL createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename = 0); + BOOL createGLTexture() ; + BOOL createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename = 0, BOOL to_create = TRUE, + S32 category = sMaxCatagories - 1); BOOL createGLTexture(S32 discard_level, const U8* data, BOOL data_hasmips = FALSE, S32 usename = 0); void setImage(const LLImageRaw* imageraw); void setImage(const U8* data_in, BOOL data_hasmips = FALSE); - BOOL setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height); - BOOL setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height); + BOOL setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height, BOOL force_fast_update = FALSE); + BOOL setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height, BOOL force_fast_update = FALSE); BOOL setSubImageFromFrameBuffer(S32 fb_x, S32 fb_y, S32 x_pos, S32 y_pos, S32 width, S32 height); - BOOL setDiscardLevel(S32 discard_level); + // Read back a raw image for this discard level, if it exists - BOOL readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compressed_ok); + BOOL readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compressed_ok) const; void destroyGLTexture(); - - void setClamp(BOOL clamps, BOOL clampt); - void setMipFilterNearest(BOOL nearest, BOOL min_nearest = FALSE); + void setExplicitFormat(LLGLint internal_format, LLGLenum primary_format, LLGLenum type_format = 0, BOOL swap_bytes = FALSE); - void dontDiscard() { mDontDiscard = 1; } + void setComponents(S8 ncomponents) { mComponents = ncomponents; } S32 getDiscardLevel() const { return mCurrentDiscardLevel; } S32 getMaxDiscardLevel() const { return mMaxDiscardLevel; } + S32 getCurrentWidth() const { return mWidth ;} + S32 getCurrentHeight() const { return mHeight ;} S32 getWidth(S32 discard_level = -1) const; S32 getHeight(S32 discard_level = -1) const; U8 getComponents() const { return mComponents; } S32 getBytes(S32 discard_level = -1) const; S32 getMipBytes(S32 discard_level = -1) const; BOOL getBoundRecently() const; + BOOL isJustBound() const; LLGLenum getPrimaryFormat() const { return mFormatPrimary; } - - BOOL getClampS() const { return mClampS; } - BOOL getClampT() const { return mClampT; } - BOOL getMipFilterNearest() const { return mMipFilterNearest; } - + LLGLenum getFormatType() const { return mFormatType; } + BOOL getHasGLTexture() const { return mTexName != 0; } LLGLuint getTexName() const { return mTexName; } + BOOL getIsAlphaMask() const { return mIsMask; } + BOOL getIsResident(BOOL test_now = FALSE); // not const - void setTarget(const LLGLenum target, const LLGLenum bind_target); + void setTarget(const LLGLenum target, const LLTexUnit::eTextureType bind_target); + + LLTexUnit::eTextureType getTarget(void) const { return mBindTarget; } + bool isGLTextureCreated(void) const { return mGLTextureCreated ; } + void setGLTextureCreated (bool initialized) { mGLTextureCreated = initialized; } BOOL getUseMipMaps() const { return mUseMipMaps; } - void setUseMipMaps(BOOL usemips) { mUseMipMaps = usemips; } - BOOL getUseDiscard() const { return mUseMipMaps && !mDontDiscard; } - BOOL getDontDiscard() const { return mDontDiscard; } + void setUseMipMaps(BOOL usemips) { mUseMipMaps = usemips; } - BOOL isValidForSculpt(S32 discard_level, S32 image_width, S32 image_height, S32 ncomponents) ; + void updatePickMask(S32 width, S32 height, const U8* data_in); + BOOL getMask(const LLVector2 &tc); -protected: + void checkTexSize(bool forced = false) const ; + + // Sets the addressing mode used to sample the texture + // (such as wrapping, mirrored wrapping, and clamp) + // Note: this actually gets set the next time the texture is bound. + void setAddressMode(LLTexUnit::eTextureAddressMode mode); + LLTexUnit::eTextureAddressMode getAddressMode(void) const { return mAddressMode; } + + // Sets the filtering options used to sample the texture + // (such as point sampling, bilinear interpolation, mipmapping, and anisotropic filtering) + // Note: this actually gets set the next time the texture is bound. + void setFilteringOption(LLTexUnit::eTextureFilterOptions option); + LLTexUnit::eTextureFilterOptions getFilteringOption(void) const { return mFilterOption; } + + LLGLenum getTexTarget()const { return mTarget ;} + S8 getDiscardLevelInAtlas()const {return mDiscardLevelInAtlas;} + U32 getTexelsInAtlas()const { return mTexelsInAtlas ;} + U32 getTexelsInGLTexture()const {return mTexelsInGLTexture;} + + void init(BOOL usemipmaps); virtual void cleanup(); // Clean up the LLImageGL so it can be reinitialized. Be careful when using this in derived class destructors + void setNeedsAlphaAndPickMask(BOOL need_mask); + + BOOL preAddToAtlas(S32 discard_level, const LLImageRaw* raw_image); + void postAddToAtlas() ; + public: // Various GL/Rendering options S32 mTextureMemory; mutable F32 mLastBindTime; // last time this was bound, by discard level - + private: LLPointer<LLImageRaw> mSaveData; // used for destroyGL/restoreGL + U8* mPickMask; //downsampled bitmap approximation of alpha channel. NULL if no alpha channel + U16 mPickMaskWidth; + U16 mPickMaskHeight; S8 mUseMipMaps; - S8 mHasMipMaps; S8 mHasExplicitFormat; // If false (default), GL format is f(mComponents) S8 mAutoGenMips; - -protected: - LLGLenum mTarget; // Normally GL_TEXTURE2D, sometimes something else (ex. cube maps) - LLGLenum mBindTarget; // NOrmally GL_TEXTURE2D, sometimes something else (ex. cube maps) + BOOL mIsMask; + BOOL mNeedsAlphaAndPickMask; + S8 mAlphaStride ; + S8 mAlphaOffset ; + + bool mGLTextureCreated ; LLGLuint mTexName; + U16 mWidth; + U16 mHeight; + S8 mCurrentDiscardLevel; + + S8 mDiscardLevelInAtlas; + U32 mTexelsInAtlas ; + U32 mTexelsInGLTexture; +protected: + LLGLenum mTarget; // Normally GL_TEXTURE2D, sometimes something else (ex. cube maps) + LLTexUnit::eTextureType mBindTarget; // Normally TT_TEXTURE, sometimes something else (ex. cube maps) + bool mHasMipMaps; + LLGLboolean mIsResident; - - U16 mWidth; - U16 mHeight; S8 mComponents; - S8 mMaxDiscardLevel; - S8 mCurrentDiscardLevel; - S8 mDontDiscard; // Keep full res version of this image (for UI, etc) - - S8 mClampS; // Need to save clamp state - S8 mClampT; - S8 mMipFilterNearest; // if TRUE, set magfilter to GL_NEAREST + S8 mMaxDiscardLevel; + + bool mTexOptionsDirty; + LLTexUnit::eTextureAddressMode mAddressMode; // Defaults to TAM_WRAP + LLTexUnit::eTextureFilterOptions mFilterOption; // Defaults to TFO_ANISOTROPIC LLGLint mFormatInternal; // = GL internalformat LLGLenum mFormatPrimary; // = GL format (pixel data format) LLGLenum mFormatType; BOOL mFormatSwapBytes;// if true, use glPixelStorei(GL_UNPACK_SWAP_BYTES, 1) - + // STATICS public: static std::set<LLImageGL*> sImageList; static S32 sCount; static F32 sLastFrameTime; - + static LLGLuint sCurrentBoundTextures[MAX_GL_TEXTURE_UNITS]; // Currently bound texture ID // Global memory statistics - static S32 sGlobalTextureMemory; // Tracks main memory texmem - static S32 sBoundTextureMemory; // Tracks bound texmem for last completed frame + static S32 sGlobalTextureMemoryInBytes; // Tracks main memory texmem + static S32 sBoundTextureMemoryInBytes; // Tracks bound texmem for last completed frame static S32 sCurBoundTextureMemory; // Tracks bound texmem for current frame static U32 sBindCount; // Tracks number of texture binds for current frame static U32 sUniqueCount; // Tracks number of unique texture binds for current frame static BOOL sGlobalUseAnisotropic; + static LLImageGL* sDefaultGLTexture ; + static BOOL sAutomatedTest; #if DEBUG_MISS BOOL mMissed; // Missed on last bind? @@ -197,6 +252,55 @@ public: #else BOOL getMissed() const { return FALSE; }; #endif + +public: + static void initClass(S32 num_catagories) ; + static void cleanupClass() ; +private: + static S32 sMaxCatagories ; + + //the flag to allow to call readBackRaw(...). + //can be removed if we do not use that function at all. + static BOOL sAllowReadBackRaw ; +// +//**************************************************************************************************** +//The below for texture auditing use only +//**************************************************************************************************** +private: + S32 mCategory ; +public: + void setCategory(S32 category) ; + S32 getCategory()const {return mCategory ;} + + //for debug use: show texture size distribution + //---------------------------------------- + static LLPointer<LLImageGL> sHighlightTexturep; //default texture to replace normal textures + static std::vector<S32> sTextureLoadedCounter ; + static std::vector<S32> sTextureBoundCounter ; + static std::vector<S32> sTextureCurBoundCounter ; + static S32 sCurTexSizeBar ; + static S32 sCurTexPickSize ; + + static void setHighlightTexture(S32 category) ; + static S32 getTextureCounterIndex(U32 val) ; + static void incTextureCounter(U32 val, S32 ncomponents, S32 category) ; + static void decTextureCounter(U32 val, S32 ncomponents, S32 category) ; + static void setCurTexSizebar(S32 index, BOOL set_pick_size = TRUE) ; + static void resetCurTexSizebar(); + //---------------------------------------- + + //for debug use: show texture category distribution + //---------------------------------------- + + static std::vector<S32> sTextureMemByCategory; + static std::vector<S32> sTextureMemByCategoryBound ; + static std::vector<S32> sTextureCurMemByCategoryBound ; + //---------------------------------------- +//**************************************************************************************************** +//End of definitions for texture auditing use only +//**************************************************************************************************** + }; +extern BOOL gAuditTexture; #endif // LL_LLIMAGEGL_H diff --git a/indra/llrender/llpostprocess.cpp b/indra/llrender/llpostprocess.cpp new file mode 100644 index 0000000000..d76b2d9004 --- /dev/null +++ b/indra/llrender/llpostprocess.cpp @@ -0,0 +1,580 @@ +/** + * @file llpostprocess.cpp + * @brief LLPostProcess class implementation + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * 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; + * version 2.1 of the License only. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llpostprocess.h" +#include "llglslshader.h" +#include "llsdserialize.h" +#include "llrender.h" + + +LLPostProcess * gPostProcess = NULL; + + +static const unsigned int NOISE_SIZE = 512; + +/// CALCULATING LUMINANCE (Using NTSC lum weights) +/// http://en.wikipedia.org/wiki/Luma_%28video%29 +static const float LUMINANCE_R = 0.299f; +static const float LUMINANCE_G = 0.587f; +static const float LUMINANCE_B = 0.114f; + +static const char * const XML_FILENAME = "postprocesseffects.xml"; + +LLPostProcess::LLPostProcess(void) : + initialized(false), + mAllEffects(LLSD::emptyMap()), + screenW(1), screenH(1) +{ + mSceneRenderTexture = NULL ; + mNoiseTexture = NULL ; + mTempBloomTexture = NULL ; + + noiseTextureScale = 1.0f; + + /* Do nothing. Needs to be updated to use our current shader system, and to work with the move into llrender. + std::string pathName(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "windlight", XML_FILENAME)); + LL_DEBUGS2("AppInit", "Shaders") << "Loading PostProcess Effects settings from " << pathName << LL_ENDL; + + llifstream effectsXML(pathName); + + if (effectsXML) + { + LLPointer<LLSDParser> parser = new LLSDXMLParser(); + + parser->parse(effectsXML, mAllEffects, LLSDSerialize::SIZE_UNLIMITED); + } + + if (!mAllEffects.has("default")) + { + LLSD & defaultEffect = (mAllEffects["default"] = LLSD::emptyMap()); + + defaultEffect["enable_night_vision"] = LLSD::Boolean(false); + defaultEffect["enable_bloom"] = LLSD::Boolean(false); + defaultEffect["enable_color_filter"] = LLSD::Boolean(false); + + /// NVG Defaults + defaultEffect["brightness_multiplier"] = 3.0; + defaultEffect["noise_size"] = 25.0; + defaultEffect["noise_strength"] = 0.4; + + // TODO BTest potentially add this to tweaks? + noiseTextureScale = 1.0f; + + /// Bloom Defaults + defaultEffect["extract_low"] = 0.95; + defaultEffect["extract_high"] = 1.0; + defaultEffect["bloom_width"] = 2.25; + defaultEffect["bloom_strength"] = 1.5; + + /// Color Filter Defaults + defaultEffect["brightness"] = 1.0; + defaultEffect["contrast"] = 1.0; + defaultEffect["saturation"] = 1.0; + + LLSD& contrastBase = (defaultEffect["contrast_base"] = LLSD::emptyArray()); + contrastBase.append(1.0); + contrastBase.append(1.0); + contrastBase.append(1.0); + contrastBase.append(0.5); + } + + setSelectedEffect("default"); + */ +} + +LLPostProcess::~LLPostProcess(void) +{ + invalidate() ; +} + +// static +void LLPostProcess::initClass(void) +{ + //this will cause system to crash at second time login + //if first time login fails due to network connection --- bao + //***llassert_always(gPostProcess == NULL); + //replaced by the following line: + if(gPostProcess) + return ; + + + gPostProcess = new LLPostProcess(); +} + +// static +void LLPostProcess::cleanupClass() +{ + delete gPostProcess; + gPostProcess = NULL; +} + +void LLPostProcess::setSelectedEffect(std::string const & effectName) +{ + mSelectedEffectName = effectName; + static_cast<LLSD &>(tweaks) = mAllEffects[effectName]; +} + +void LLPostProcess::saveEffect(std::string const & effectName) +{ + /* Do nothing. Needs to be updated to use our current shader system, and to work with the move into llrender. + mAllEffects[effectName] = tweaks; + + std::string pathName(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "windlight", XML_FILENAME)); + //llinfos << "Saving PostProcess Effects settings to " << pathName << llendl; + + llofstream effectsXML(pathName); + + LLPointer<LLSDFormatter> formatter = new LLSDXMLFormatter(); + + formatter->format(mAllEffects, effectsXML); + */ +} +void LLPostProcess::invalidate() +{ + mSceneRenderTexture = NULL ; + mNoiseTexture = NULL ; + mTempBloomTexture = NULL ; + initialized = FALSE ; +} + +void LLPostProcess::apply(unsigned int width, unsigned int height) +{ + if (!initialized || width != screenW || height != screenH){ + initialize(width, height); + } + if (shadersEnabled()){ + doEffects(); + } +} + +void LLPostProcess::initialize(unsigned int width, unsigned int height) +{ + screenW = width; + screenH = height; + createTexture(mSceneRenderTexture, screenW, screenH); + initialized = true; + + checkError(); + createNightVisionShader(); + createBloomShader(); + createColorFilterShader(); + checkError(); +} + +inline bool LLPostProcess::shadersEnabled(void) +{ + return (tweaks.useColorFilter().asBoolean() || + tweaks.useNightVisionShader().asBoolean() || + tweaks.useBloomShader().asBoolean() ); + +} + +void LLPostProcess::applyShaders(void) +{ + if (tweaks.useColorFilter()){ + applyColorFilterShader(); + checkError(); + } + if (tweaks.useNightVisionShader()){ + /// If any of the above shaders have been called update the frame buffer; + if (tweaks.useColorFilter()) + { + U32 tex = mSceneRenderTexture->getTexName() ; + copyFrameBuffer(tex, screenW, screenH); + } + applyNightVisionShader(); + checkError(); + } + if (tweaks.useBloomShader()){ + /// If any of the above shaders have been called update the frame buffer; + if (tweaks.useColorFilter().asBoolean() || tweaks.useNightVisionShader().asBoolean()) + { + U32 tex = mSceneRenderTexture->getTexName() ; + copyFrameBuffer(tex, screenW, screenH); + } + applyBloomShader(); + checkError(); + } +} + +void LLPostProcess::applyColorFilterShader(void) +{ + /* Do nothing. Needs to be updated to use our current shader system, and to work with the move into llrender. + gPostColorFilterProgram.bind(); + + gGL.getTexUnit(0)->activate(); + gGL.getTexUnit(0)->enable(LLTexUnit::TT_RECT_TEXTURE); + + gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_RECT_TEXTURE, sceneRenderTexture); + + getShaderUniforms(colorFilterUniforms, gPostColorFilterProgram.mProgramObject); + glUniform1iARB(colorFilterUniforms["RenderTexture"], 0); + glUniform1fARB(colorFilterUniforms["brightness"], tweaks.getBrightness()); + glUniform1fARB(colorFilterUniforms["contrast"], tweaks.getContrast()); + float baseI = (tweaks.getContrastBaseR() + tweaks.getContrastBaseG() + tweaks.getContrastBaseB()) / 3.0f; + baseI = tweaks.getContrastBaseIntensity() / ((baseI < 0.001f) ? 0.001f : baseI); + float baseR = tweaks.getContrastBaseR() * baseI; + float baseG = tweaks.getContrastBaseG() * baseI; + float baseB = tweaks.getContrastBaseB() * baseI; + glUniform3fARB(colorFilterUniforms["contrastBase"], baseR, baseG, baseB); + glUniform1fARB(colorFilterUniforms["saturation"], tweaks.getSaturation()); + glUniform3fARB(colorFilterUniforms["lumWeights"], LUMINANCE_R, LUMINANCE_G, LUMINANCE_B); + + LLGLEnable blend(GL_BLEND); + gGL.setSceneBlendType(LLRender::BT_REPLACE); + LLGLDepthTest depth(GL_FALSE); + + /// Draw a screen space quad + drawOrthoQuad(screenW, screenH, QUAD_NORMAL); + gPostColorFilterProgram.unbind(); + */ +} + +void LLPostProcess::createColorFilterShader(void) +{ + /// Define uniform names + colorFilterUniforms["RenderTexture"] = 0; + colorFilterUniforms["brightness"] = 0; + colorFilterUniforms["contrast"] = 0; + colorFilterUniforms["contrastBase"] = 0; + colorFilterUniforms["saturation"] = 0; + colorFilterUniforms["lumWeights"] = 0; +} + +void LLPostProcess::applyNightVisionShader(void) +{ + /* Do nothing. Needs to be updated to use our current shader system, and to work with the move into llrender. + gPostNightVisionProgram.bind(); + + gGL.getTexUnit(0)->activate(); + gGL.getTexUnit(0)->enable(LLTexUnit::TT_RECT_TEXTURE); + + getShaderUniforms(nightVisionUniforms, gPostNightVisionProgram.mProgramObject); + gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_RECT_TEXTURE, sceneRenderTexture); + glUniform1iARB(nightVisionUniforms["RenderTexture"], 0); + + gGL.getTexUnit(1)->activate(); + gGL.getTexUnit(1)->enable(LLTexUnit::TT_TEXTURE); + + gGL.getTexUnit(1)->bindManual(LLTexUnit::TT_TEXTURE, noiseTexture); + glUniform1iARB(nightVisionUniforms["NoiseTexture"], 1); + + + glUniform1fARB(nightVisionUniforms["brightMult"], tweaks.getBrightMult()); + glUniform1fARB(nightVisionUniforms["noiseStrength"], tweaks.getNoiseStrength()); + noiseTextureScale = 0.01f + ((101.f - tweaks.getNoiseSize()) / 100.f); + noiseTextureScale *= (screenH / NOISE_SIZE); + + + glUniform3fARB(nightVisionUniforms["lumWeights"], LUMINANCE_R, LUMINANCE_G, LUMINANCE_B); + + LLGLEnable blend(GL_BLEND); + gGL.setSceneBlendType(LLRender::BT_REPLACE); + LLGLDepthTest depth(GL_FALSE); + + /// Draw a screen space quad + drawOrthoQuad(screenW, screenH, QUAD_NOISE); + gPostNightVisionProgram.unbind(); + gGL.getTexUnit(0)->activate(); + */ +} + +void LLPostProcess::createNightVisionShader(void) +{ + /// Define uniform names + nightVisionUniforms["RenderTexture"] = 0; + nightVisionUniforms["NoiseTexture"] = 0; + nightVisionUniforms["brightMult"] = 0; + nightVisionUniforms["noiseStrength"] = 0; + nightVisionUniforms["lumWeights"] = 0; + + createNoiseTexture(mNoiseTexture); +} + +void LLPostProcess::applyBloomShader(void) +{ + +} + +void LLPostProcess::createBloomShader(void) +{ + createTexture(mTempBloomTexture, unsigned(screenW * 0.5), unsigned(screenH * 0.5)); + + /// Create Bloom Extract Shader + bloomExtractUniforms["RenderTexture"] = 0; + bloomExtractUniforms["extractLow"] = 0; + bloomExtractUniforms["extractHigh"] = 0; + bloomExtractUniforms["lumWeights"] = 0; + + /// Create Bloom Blur Shader + bloomBlurUniforms["RenderTexture"] = 0; + bloomBlurUniforms["bloomStrength"] = 0; + bloomBlurUniforms["texelSize"] = 0; + bloomBlurUniforms["blurDirection"] = 0; + bloomBlurUniforms["blurWidth"] = 0; +} + +void LLPostProcess::getShaderUniforms(glslUniforms & uniforms, GLhandleARB & prog) +{ + /// Find uniform locations and insert into map + std::map<const char *, GLuint>::iterator i; + for (i = uniforms.begin(); i != uniforms.end(); ++i){ + i->second = glGetUniformLocationARB(prog, i->first); + } +} + +void LLPostProcess::doEffects(void) +{ + /// Save GL State + glPushAttrib(GL_ALL_ATTRIB_BITS); + glPushClientAttrib(GL_ALL_ATTRIB_BITS); + + /// Copy the screen buffer to the render texture + { + U32 tex = mSceneRenderTexture->getTexName() ; + copyFrameBuffer(tex, screenW, screenH); + } + + /// Clear the frame buffer. + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + /// Change to an orthogonal view + viewOrthogonal(screenW, screenH); + + checkError(); + applyShaders(); + + LLGLSLShader::bindNoShader(); + checkError(); + + /// Change to a perspective view + viewPerspective(); + + /// Reset GL State + glPopClientAttrib(); + glPopAttrib(); + checkError(); +} + +void LLPostProcess::copyFrameBuffer(U32 & texture, unsigned int width, unsigned int height) +{ + gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_RECT_TEXTURE, texture); + glCopyTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, 0, 0, width, height, 0); +} + +void LLPostProcess::drawOrthoQuad(unsigned int width, unsigned int height, QuadType type) +{ +#if 0 + float noiseX = 0.f; + float noiseY = 0.f; + float screenRatio = 1.0f; + + if (type == QUAD_NOISE){ + noiseX = ((float) rand() / (float) RAND_MAX); + noiseY = ((float) rand() / (float) RAND_MAX); + screenRatio = (float) width / (float) height; + } + + + glBegin(GL_QUADS); + if (type != QUAD_BLOOM_EXTRACT){ + glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.f, (GLfloat) height); + } else { + glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.f, (GLfloat) height * 2.0f); + } + if (type == QUAD_NOISE){ + glMultiTexCoord2fARB(GL_TEXTURE1_ARB, + noiseX, + noiseTextureScale + noiseY); + } else if (type == QUAD_BLOOM_COMBINE){ + glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.f, (GLfloat) height * 0.5f); + } + glVertex2f(0.f, (GLfloat) screenH - height); + + if (type != QUAD_BLOOM_EXTRACT){ + glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.f, 0.f); + } else { + glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.f, 0.f); + } + if (type == QUAD_NOISE){ + glMultiTexCoord2fARB(GL_TEXTURE1_ARB, + noiseX, + noiseY); + } else if (type == QUAD_BLOOM_COMBINE){ + glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.f, 0.f); + } + glVertex2f(0.f, (GLfloat) height + (screenH - height)); + + + if (type != QUAD_BLOOM_EXTRACT){ + glMultiTexCoord2fARB(GL_TEXTURE0_ARB, (GLfloat) width, 0.f); + } else { + glMultiTexCoord2fARB(GL_TEXTURE0_ARB, (GLfloat) width * 2.0f, 0.f); + } + if (type == QUAD_NOISE){ + glMultiTexCoord2fARB(GL_TEXTURE1_ARB, + screenRatio * noiseTextureScale + noiseX, + noiseY); + } else if (type == QUAD_BLOOM_COMBINE){ + glMultiTexCoord2fARB(GL_TEXTURE1_ARB, (GLfloat) width * 0.5f, 0.f); + } + glVertex2f((GLfloat) width, (GLfloat) height + (screenH - height)); + + + if (type != QUAD_BLOOM_EXTRACT){ + glMultiTexCoord2fARB(GL_TEXTURE0_ARB, (GLfloat) width, (GLfloat) height); + } else { + glMultiTexCoord2fARB(GL_TEXTURE0_ARB, (GLfloat) width * 2.0f, (GLfloat) height * 2.0f); + } + if (type == QUAD_NOISE){ + glMultiTexCoord2fARB(GL_TEXTURE1_ARB, + screenRatio * noiseTextureScale + noiseX, + noiseTextureScale + noiseY); + } else if (type == QUAD_BLOOM_COMBINE){ + glMultiTexCoord2fARB(GL_TEXTURE1_ARB, (GLfloat) width * 0.5f, (GLfloat) height * 0.5f); + } + glVertex2f((GLfloat) width, (GLfloat) screenH - height); + glEnd(); +#endif +} + +void LLPostProcess::viewOrthogonal(unsigned int width, unsigned int height) +{ + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho( 0.f, (GLdouble) width , (GLdouble) height , 0.f, -1.f, 1.f ); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); +} + +void LLPostProcess::viewPerspective(void) +{ + glMatrixMode( GL_PROJECTION ); + glPopMatrix(); + glMatrixMode( GL_MODELVIEW ); + glPopMatrix(); +} + +void LLPostProcess::changeOrthogonal(unsigned int width, unsigned int height) +{ + viewPerspective(); + viewOrthogonal(width, height); +} + +void LLPostProcess::createTexture(LLPointer<LLImageGL>& texture, unsigned int width, unsigned int height) +{ + std::vector<GLubyte> data(width * height * 4, 0) ; + + texture = new LLImageGL(FALSE) ; + if(texture->createGLTexture()) + { + gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_RECT_TEXTURE, texture->getTexName()); + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 4, width, height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, &data[0]); + gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); + gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); + } +} + +void LLPostProcess::createNoiseTexture(LLPointer<LLImageGL>& texture) +{ + std::vector<GLubyte> buffer(NOISE_SIZE * NOISE_SIZE); + for (unsigned int i = 0; i < NOISE_SIZE; i++){ + for (unsigned int k = 0; k < NOISE_SIZE; k++){ + buffer[(i * NOISE_SIZE) + k] = (GLubyte)((double) rand() / ((double) RAND_MAX + 1.f) * 255.f); + } + } + + texture = new LLImageGL(FALSE) ; + if(texture->createGLTexture()) + { + gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, texture->getTexName()); + LLImageGL::setManualImage(GL_TEXTURE_2D, 0, GL_LUMINANCE, NOISE_SIZE, NOISE_SIZE, GL_LUMINANCE, GL_UNSIGNED_BYTE, &buffer[0]); + gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); + gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_WRAP); + } +} + +bool LLPostProcess::checkError(void) +{ + GLenum glErr; + bool retCode = false; + + glErr = glGetError(); + while (glErr != GL_NO_ERROR) + { + // shaderErrorLog << (const char *) gluErrorString(glErr) << std::endl; + char const * err_str_raw = (const char *) gluErrorString(glErr); + + if(err_str_raw == NULL) + { + std::ostringstream err_builder; + err_builder << "unknown error number " << glErr; + mShaderErrorString = err_builder.str(); + } + else + { + mShaderErrorString = err_str_raw; + } + + retCode = true; + glErr = glGetError(); + } + return retCode; +} + +void LLPostProcess::checkShaderError(GLhandleARB shader) +{ + GLint infologLength = 0; + GLint charsWritten = 0; + GLchar *infoLog; + + checkError(); // Check for OpenGL errors + + glGetObjectParameterivARB(shader, GL_OBJECT_INFO_LOG_LENGTH_ARB, &infologLength); + + checkError(); // Check for OpenGL errors + + if (infologLength > 0) + { + infoLog = (GLchar *)malloc(infologLength); + if (infoLog == NULL) + { + /// Could not allocate infolog buffer + return; + } + glGetInfoLogARB(shader, infologLength, &charsWritten, infoLog); + // shaderErrorLog << (char *) infoLog << std::endl; + mShaderErrorString = (char *) infoLog; + free(infoLog); + } + checkError(); // Check for OpenGL errors +} diff --git a/indra/llrender/llpostprocess.h b/indra/llrender/llpostprocess.h new file mode 100644 index 0000000000..e19de44c60 --- /dev/null +++ b/indra/llrender/llpostprocess.h @@ -0,0 +1,266 @@ +/** + * @file llpostprocess.h + * @brief LLPostProcess class definition + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * 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; + * version 2.1 of the License only. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_POSTPROCESS_H +#define LL_POSTPROCESS_H + +#include <map> +#include <fstream> +#include "llgl.h" +#include "llglheaders.h" + +class LLPostProcess +{ +public: + + typedef enum _QuadType { + QUAD_NORMAL, + QUAD_NOISE, + QUAD_BLOOM_EXTRACT, + QUAD_BLOOM_COMBINE + } QuadType; + + /// GLSL Shader Encapsulation Struct + typedef std::map<const char *, GLuint> glslUniforms; + + struct PostProcessTweaks : public LLSD { + inline PostProcessTweaks() : LLSD(LLSD::emptyMap()) + { + } + + inline LLSD & brightMult() { + return (*this)["brightness_multiplier"]; + } + + inline LLSD & noiseStrength() { + return (*this)["noise_strength"]; + } + + inline LLSD & noiseSize() { + return (*this)["noise_size"]; + } + + inline LLSD & extractLow() { + return (*this)["extract_low"]; + } + + inline LLSD & extractHigh() { + return (*this)["extract_high"]; + } + + inline LLSD & bloomWidth() { + return (*this)["bloom_width"]; + } + + inline LLSD & bloomStrength() { + return (*this)["bloom_strength"]; + } + + inline LLSD & brightness() { + return (*this)["brightness"]; + } + + inline LLSD & contrast() { + return (*this)["contrast"]; + } + + inline LLSD & contrastBaseR() { + return (*this)["contrast_base"][0]; + } + + inline LLSD & contrastBaseG() { + return (*this)["contrast_base"][1]; + } + + inline LLSD & contrastBaseB() { + return (*this)["contrast_base"][2]; + } + + inline LLSD & contrastBaseIntensity() { + return (*this)["contrast_base"][3]; + } + + inline LLSD & saturation() { + return (*this)["saturation"]; + } + + inline LLSD & useNightVisionShader() { + return (*this)["enable_night_vision"]; + } + + inline LLSD & useBloomShader() { + return (*this)["enable_bloom"]; + } + + inline LLSD & useColorFilter() { + return (*this)["enable_color_filter"]; + } + + + inline F32 getBrightMult() const { + return F32((*this)["brightness_multiplier"].asReal()); + } + + inline F32 getNoiseStrength() const { + return F32((*this)["noise_strength"].asReal()); + } + + inline F32 getNoiseSize() const { + return F32((*this)["noise_size"].asReal()); + } + + inline F32 getExtractLow() const { + return F32((*this)["extract_low"].asReal()); + } + + inline F32 getExtractHigh() const { + return F32((*this)["extract_high"].asReal()); + } + + inline F32 getBloomWidth() const { + return F32((*this)["bloom_width"].asReal()); + } + + inline F32 getBloomStrength() const { + return F32((*this)["bloom_strength"].asReal()); + } + + inline F32 getBrightness() const { + return F32((*this)["brightness"].asReal()); + } + + inline F32 getContrast() const { + return F32((*this)["contrast"].asReal()); + } + + inline F32 getContrastBaseR() const { + return F32((*this)["contrast_base"][0].asReal()); + } + + inline F32 getContrastBaseG() const { + return F32((*this)["contrast_base"][1].asReal()); + } + + inline F32 getContrastBaseB() const { + return F32((*this)["contrast_base"][2].asReal()); + } + + inline F32 getContrastBaseIntensity() const { + return F32((*this)["contrast_base"][3].asReal()); + } + + inline F32 getSaturation() const { + return F32((*this)["saturation"].asReal()); + } + + }; + + bool initialized; + PostProcessTweaks tweaks; + + // the map of all availible effects + LLSD mAllEffects; + +private: + LLPointer<LLImageGL> mSceneRenderTexture ; + LLPointer<LLImageGL> mNoiseTexture ; + LLPointer<LLImageGL> mTempBloomTexture ; + +public: + LLPostProcess(void); + + ~LLPostProcess(void); + + void apply(unsigned int width, unsigned int height); + void invalidate() ; + + /// Perform global initialization for this class. + static void initClass(void); + + // Cleanup of global data that's only inited once per class. + static void cleanupClass(); + + void setSelectedEffect(std::string const & effectName); + + inline std::string const & getSelectedEffect(void) const { + return mSelectedEffectName; + } + + void saveEffect(std::string const & effectName); + +private: + /// read in from file + std::string mShaderErrorString; + unsigned int screenW; + unsigned int screenH; + + float noiseTextureScale; + + /// Shader Uniforms + glslUniforms nightVisionUniforms; + glslUniforms bloomExtractUniforms; + glslUniforms bloomBlurUniforms; + glslUniforms colorFilterUniforms; + + // the name of currently selected effect in mAllEffects + // invariant: tweaks == mAllEffects[mSelectedEffectName] + std::string mSelectedEffectName; + + /// General functions + void initialize(unsigned int width, unsigned int height); + void doEffects(void); + void applyShaders(void); + bool shadersEnabled(void); + + /// Night Vision Functions + void createNightVisionShader(void); + void applyNightVisionShader(void); + + /// Bloom Functions + void createBloomShader(void); + void applyBloomShader(void); + + /// Color Filter Functions + void createColorFilterShader(void); + void applyColorFilterShader(void); + + /// OpenGL Helper Functions + void getShaderUniforms(glslUniforms & uniforms, GLhandleARB & prog); + void createTexture(LLPointer<LLImageGL>& texture, unsigned int width, unsigned int height); + void copyFrameBuffer(U32 & texture, unsigned int width, unsigned int height); + void createNoiseTexture(LLPointer<LLImageGL>& texture); + bool checkError(void); + void checkShaderError(GLhandleARB shader); + void drawOrthoQuad(unsigned int width, unsigned int height, QuadType type); + void viewOrthogonal(unsigned int width, unsigned int height); + void changeOrthogonal(unsigned int width, unsigned int height); + void viewPerspective(void); +}; + +extern LLPostProcess * gPostProcess; + + +#endif // LL_POSTPROCESS_H diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp new file mode 100644 index 0000000000..e26acd53a3 --- /dev/null +++ b/indra/llrender/llrender.cpp @@ -0,0 +1,1378 @@ +/** + * @file llrender.cpp + * @brief LLRender implementation + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * 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; + * version 2.1 of the License only. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llrender.h" + +#include "llvertexbuffer.h" +#include "llcubemap.h" +#include "llimagegl.h" +#include "llrendertarget.h" +#include "lltexture.h" + +LLRender gGL; + +// Handy copies of last good GL matrices +F64 gGLModelView[16]; +F64 gGLLastModelView[16]; +F64 gGLLastProjection[16]; +F64 gGLProjection[16]; +S32 gGLViewport[4]; + +U32 LLRender::sUICalls = 0; +U32 LLRender::sUIVerts = 0; + +static const U32 LL_NUM_TEXTURE_LAYERS = 16; + +static GLenum sGLTextureType[] = +{ + GL_TEXTURE_2D, + GL_TEXTURE_RECTANGLE_ARB, + GL_TEXTURE_CUBE_MAP_ARB +}; + +static GLint sGLAddressMode[] = +{ + GL_REPEAT, + GL_MIRRORED_REPEAT, + GL_CLAMP_TO_EDGE +}; + +static GLenum sGLCompareFunc[] = +{ + GL_NEVER, + GL_ALWAYS, + GL_LESS, + GL_LEQUAL, + GL_EQUAL, + GL_NOTEQUAL, + GL_GEQUAL, + GL_GREATER +}; + +const U32 immediate_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_COLOR | LLVertexBuffer::MAP_TEXCOORD0; + +static GLenum sGLBlendFactor[] = +{ + GL_ONE, + GL_ZERO, + GL_DST_COLOR, + GL_SRC_COLOR, + GL_ONE_MINUS_DST_COLOR, + GL_ONE_MINUS_SRC_COLOR, + GL_DST_ALPHA, + GL_SRC_ALPHA, + GL_ONE_MINUS_DST_ALPHA, + GL_ONE_MINUS_SRC_ALPHA, + + GL_ZERO // 'BF_UNDEF' +}; + +LLTexUnit::LLTexUnit(S32 index) +: mCurrTexType(TT_NONE), mCurrBlendType(TB_MULT), +mCurrColorOp(TBO_MULT), mCurrAlphaOp(TBO_MULT), +mCurrColorSrc1(TBS_TEX_COLOR), mCurrColorSrc2(TBS_PREV_COLOR), +mCurrAlphaSrc1(TBS_TEX_ALPHA), mCurrAlphaSrc2(TBS_PREV_ALPHA), +mCurrColorScale(1), mCurrAlphaScale(1), mCurrTexture(0), +mHasMipMaps(false) +{ + llassert_always(index < (S32)LL_NUM_TEXTURE_LAYERS); + mIndex = index; +} + +//static +U32 LLTexUnit::getInternalType(eTextureType type) +{ + return sGLTextureType[type]; +} + +void LLTexUnit::refreshState(void) +{ + // We set dirty to true so that the tex unit knows to ignore caching + // and we reset the cached tex unit state + + gGL.flush(); + + glActiveTextureARB(GL_TEXTURE0_ARB + mIndex); + if (mCurrTexType != TT_NONE) + { + glEnable(sGLTextureType[mCurrTexType]); + glBindTexture(sGLTextureType[mCurrTexType], mCurrTexture); + } + else + { + glDisable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + } + + if (mCurrBlendType != TB_COMBINE) + { + setTextureBlendType(mCurrBlendType); + } + else + { + setTextureCombiner(mCurrColorOp, mCurrColorSrc1, mCurrColorSrc2, false); + setTextureCombiner(mCurrAlphaOp, mCurrAlphaSrc1, mCurrAlphaSrc2, true); + } +} + +void LLTexUnit::activate(void) +{ + if (mIndex < 0) return; + + if ((S32)gGL.mCurrTextureUnitIndex != mIndex || gGL.mDirty) + { + gGL.flush(); + glActiveTextureARB(GL_TEXTURE0_ARB + mIndex); + gGL.mCurrTextureUnitIndex = mIndex; + } +} + +void LLTexUnit::enable(eTextureType type) +{ + if (mIndex < 0) return; + + if ( (mCurrTexType != type || gGL.mDirty) && (type != TT_NONE) ) + { + activate(); + if (mCurrTexType != TT_NONE && !gGL.mDirty) + { + disable(); // Force a disable of a previous texture type if it's enabled. + } + mCurrTexType = type; + + gGL.flush(); + glEnable(sGLTextureType[type]); + } +} + +void LLTexUnit::disable(void) +{ + if (mIndex < 0) return; + + if (mCurrTexType != TT_NONE) + { + activate(); + unbind(mCurrTexType); + gGL.flush(); + glDisable(sGLTextureType[mCurrTexType]); + mCurrTexType = TT_NONE; + } +} + +bool LLTexUnit::bind(LLTexture* texture, bool for_rendering, bool forceBind) +{ + stop_glerror(); + if (mIndex < 0) return false; + + gGL.flush(); + + LLImageGL* gl_tex = NULL ; + if (texture == NULL || !(gl_tex = texture->getGLTexture())) + { + llwarns << "NULL LLTexUnit::bind texture" << llendl; + return false; + } + + if (!gl_tex->getTexName()) //if texture does not exist + { + //if deleted, will re-generate it immediately + texture->forceImmediateUpdate() ; + + gl_tex->forceUpdateBindStats() ; + return texture->bindDefaultImage(mIndex); + } + + //in audit, replace the selected texture by the default one. + if(gAuditTexture && for_rendering && LLImageGL::sCurTexPickSize > 0) + { + if(texture->getWidth() * texture->getHeight() == LLImageGL::sCurTexPickSize) + { + gl_tex->updateBindStats(gl_tex->mTextureMemory); + return bind(LLImageGL::sHighlightTexturep.get()); + } + } + if ((mCurrTexture != gl_tex->getTexName()) || forceBind) + { + activate(); + enable(gl_tex->getTarget()); + mCurrTexture = gl_tex->getTexName(); + glBindTexture(sGLTextureType[gl_tex->getTarget()], mCurrTexture); + if(gl_tex->updateBindStats(gl_tex->mTextureMemory)) + { + texture->setActive() ; + texture->updateBindStatsForTester() ; + } + mHasMipMaps = gl_tex->mHasMipMaps; + if (gl_tex->mTexOptionsDirty) + { + gl_tex->mTexOptionsDirty = false; + setTextureAddressMode(gl_tex->mAddressMode); + setTextureFilteringOption(gl_tex->mFilterOption); + } + } + return true; +} + +bool LLTexUnit::bind(LLImageGL* texture, bool for_rendering, bool forceBind) +{ + stop_glerror(); + if (mIndex < 0) return false; + + if(!texture) + { + llwarns << "NULL LLTexUnit::bind texture" << llendl; + return false; + } + + if(!texture->getTexName()) + { + if(LLImageGL::sDefaultGLTexture && LLImageGL::sDefaultGLTexture->getTexName()) + { + return bind(LLImageGL::sDefaultGLTexture) ; + } + return false ; + } + + if ((mCurrTexture != texture->getTexName()) || forceBind) + { + gGL.flush(); + activate(); + enable(texture->getTarget()); + mCurrTexture = texture->getTexName(); + glBindTexture(sGLTextureType[texture->getTarget()], mCurrTexture); + texture->updateBindStats(texture->mTextureMemory); + mHasMipMaps = texture->mHasMipMaps; + if (texture->mTexOptionsDirty) + { + texture->mTexOptionsDirty = false; + setTextureAddressMode(texture->mAddressMode); + setTextureFilteringOption(texture->mFilterOption); + } + } + + return true; +} + +bool LLTexUnit::bind(LLCubeMap* cubeMap) +{ + if (mIndex < 0) return false; + + gGL.flush(); + + if (cubeMap == NULL) + { + llwarns << "NULL LLTexUnit::bind cubemap" << llendl; + return false; + } + + if (mCurrTexture != cubeMap->mImages[0]->getTexName()) + { + if (gGLManager.mHasCubeMap && LLCubeMap::sUseCubeMaps) + { + activate(); + enable(LLTexUnit::TT_CUBE_MAP); + mCurrTexture = cubeMap->mImages[0]->getTexName(); + glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, mCurrTexture); + mHasMipMaps = cubeMap->mImages[0]->mHasMipMaps; + cubeMap->mImages[0]->updateBindStats(cubeMap->mImages[0]->mTextureMemory); + if (cubeMap->mImages[0]->mTexOptionsDirty) + { + cubeMap->mImages[0]->mTexOptionsDirty = false; + setTextureAddressMode(cubeMap->mImages[0]->mAddressMode); + setTextureFilteringOption(cubeMap->mImages[0]->mFilterOption); + } + return true; + } + else + { + llwarns << "Using cube map without extension!" << llendl; + return false; + } + } + return true; +} + +// LLRenderTarget is unavailible on the mapserver since it uses FBOs. +#if !LL_MESA_HEADLESS +bool LLTexUnit::bind(LLRenderTarget* renderTarget, bool bindDepth) +{ + if (mIndex < 0) return false; + + gGL.flush(); + + if (bindDepth) + { + if (renderTarget->hasStencil()) + { + llerrs << "Cannot bind a render buffer for sampling. Allocate render target without a stencil buffer if sampling of depth buffer is required." << llendl; + } + + bindManual(renderTarget->getUsage(), renderTarget->getDepth()); + } + else + { + bindManual(renderTarget->getUsage(), renderTarget->getTexture()); + } + + return true; +} +#endif // LL_MESA_HEADLESS + +bool LLTexUnit::bindManual(eTextureType type, U32 texture, bool hasMips) +{ + if (mIndex < 0) + { + return false; + } + + if(mCurrTexture != texture) + { + gGL.flush(); + + activate(); + enable(type); + mCurrTexture = texture; + glBindTexture(sGLTextureType[type], texture); + mHasMipMaps = hasMips; + } + return true; +} + +void LLTexUnit::unbind(eTextureType type) +{ + stop_glerror(); + + if (mIndex < 0) return; + + // Disabled caching of binding state. + if (mCurrTexType == type) + { + gGL.flush(); + + activate(); + mCurrTexture = 0; + glBindTexture(sGLTextureType[type], 0); + } +} + +void LLTexUnit::setTextureAddressMode(eTextureAddressMode mode) +{ + if (mIndex < 0 || mCurrTexture == 0) return; + + gGL.flush(); + + activate(); + + glTexParameteri (sGLTextureType[mCurrTexType], GL_TEXTURE_WRAP_S, sGLAddressMode[mode]); + glTexParameteri (sGLTextureType[mCurrTexType], GL_TEXTURE_WRAP_T, sGLAddressMode[mode]); + if (mCurrTexType == TT_CUBE_MAP) + { + glTexParameteri (GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_R, sGLAddressMode[mode]); + } +} + +void LLTexUnit::setTextureFilteringOption(LLTexUnit::eTextureFilterOptions option) +{ + if (mIndex < 0 || mCurrTexture == 0) return; + + gGL.flush(); + + if (option == TFO_POINT) + { + glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + else + { + glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + + if (option >= TFO_TRILINEAR && mHasMipMaps) + { + glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + } + else if (option >= TFO_BILINEAR) + { + glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } + else + { + glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + + if (gGLManager.mHasAnisotropic) + { + if (LLImageGL::sGlobalUseAnisotropic && option == TFO_ANISOTROPIC) + { + if (gGL.mMaxAnisotropy < 1.f) + { + glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gGL.mMaxAnisotropy); + } + glTexParameterf(sGLTextureType[mCurrTexType], GL_TEXTURE_MAX_ANISOTROPY_EXT, gGL.mMaxAnisotropy); + } + else + { + glTexParameterf(sGLTextureType[mCurrTexType], GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.f); + } + } +} + +void LLTexUnit::setTextureBlendType(eTextureBlendType type) +{ + if (mIndex < 0) return; + + // Do nothing if it's already correctly set. + if (mCurrBlendType == type && !gGL.mDirty) + { + return; + } + + gGL.flush(); + + activate(); + mCurrBlendType = type; + S32 scale_amount = 1; + switch (type) + { + case TB_REPLACE: + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + break; + case TB_ADD: + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD); + break; + case TB_MULT: + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + break; + case TB_MULT_X2: + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + scale_amount = 2; + break; + case TB_ALPHA_BLEND: + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); + break; + case TB_COMBINE: + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB); + break; + default: + llerrs << "Unknown Texture Blend Type: " << type << llendl; + break; + } + setColorScale(scale_amount); + setAlphaScale(1); +} + +GLint LLTexUnit::getTextureSource(eTextureBlendSrc src) +{ + switch(src) + { + // All four cases should return the same value. + case TBS_PREV_COLOR: + case TBS_PREV_ALPHA: + case TBS_ONE_MINUS_PREV_COLOR: + case TBS_ONE_MINUS_PREV_ALPHA: + return GL_PREVIOUS_ARB; + + // All four cases should return the same value. + case TBS_TEX_COLOR: + case TBS_TEX_ALPHA: + case TBS_ONE_MINUS_TEX_COLOR: + case TBS_ONE_MINUS_TEX_ALPHA: + return GL_TEXTURE; + + // All four cases should return the same value. + case TBS_VERT_COLOR: + case TBS_VERT_ALPHA: + case TBS_ONE_MINUS_VERT_COLOR: + case TBS_ONE_MINUS_VERT_ALPHA: + return GL_PRIMARY_COLOR_ARB; + + // All four cases should return the same value. + case TBS_CONST_COLOR: + case TBS_CONST_ALPHA: + case TBS_ONE_MINUS_CONST_COLOR: + case TBS_ONE_MINUS_CONST_ALPHA: + return GL_CONSTANT_ARB; + + default: + llwarns << "Unknown eTextureBlendSrc: " << src << ". Using Vertex Color instead." << llendl; + return GL_PRIMARY_COLOR_ARB; + } +} + +GLint LLTexUnit::getTextureSourceType(eTextureBlendSrc src, bool isAlpha) +{ + switch(src) + { + // All four cases should return the same value. + case TBS_PREV_COLOR: + case TBS_TEX_COLOR: + case TBS_VERT_COLOR: + case TBS_CONST_COLOR: + return (isAlpha) ? GL_SRC_ALPHA: GL_SRC_COLOR; + + // All four cases should return the same value. + case TBS_PREV_ALPHA: + case TBS_TEX_ALPHA: + case TBS_VERT_ALPHA: + case TBS_CONST_ALPHA: + return GL_SRC_ALPHA; + + // All four cases should return the same value. + case TBS_ONE_MINUS_PREV_COLOR: + case TBS_ONE_MINUS_TEX_COLOR: + case TBS_ONE_MINUS_VERT_COLOR: + case TBS_ONE_MINUS_CONST_COLOR: + return (isAlpha) ? GL_ONE_MINUS_SRC_ALPHA : GL_ONE_MINUS_SRC_COLOR; + + // All four cases should return the same value. + case TBS_ONE_MINUS_PREV_ALPHA: + case TBS_ONE_MINUS_TEX_ALPHA: + case TBS_ONE_MINUS_VERT_ALPHA: + case TBS_ONE_MINUS_CONST_ALPHA: + return GL_ONE_MINUS_SRC_ALPHA; + + default: + llwarns << "Unknown eTextureBlendSrc: " << src << ". Using Source Color or Alpha instead." << llendl; + return (isAlpha) ? GL_SRC_ALPHA: GL_SRC_COLOR; + } +} + +void LLTexUnit::setTextureCombiner(eTextureBlendOp op, eTextureBlendSrc src1, eTextureBlendSrc src2, bool isAlpha) +{ + if (mIndex < 0) return; + + activate(); + if (mCurrBlendType != TB_COMBINE || gGL.mDirty) + { + mCurrBlendType = TB_COMBINE; + gGL.flush(); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB); + } + + // We want an early out, because this function does a LOT of stuff. + if ( ( (isAlpha && (mCurrAlphaOp == op) && (mCurrAlphaSrc1 == src1) && (mCurrAlphaSrc2 == src2)) + || (!isAlpha && (mCurrColorOp == op) && (mCurrColorSrc1 == src1) && (mCurrColorSrc2 == src2)) ) && !gGL.mDirty) + { + return; + } + + gGL.flush(); + + // Get the gl source enums according to the eTextureBlendSrc sources passed in + GLint source1 = getTextureSource(src1); + GLint source2 = getTextureSource(src2); + // Get the gl operand enums according to the eTextureBlendSrc sources passed in + GLint operand1 = getTextureSourceType(src1, isAlpha); + GLint operand2 = getTextureSourceType(src2, isAlpha); + // Default the scale amount to 1 + S32 scale_amount = 1; + GLenum comb_enum, src0_enum, src1_enum, src2_enum, operand0_enum, operand1_enum, operand2_enum; + + if (isAlpha) + { + // Set enums to ALPHA ones + comb_enum = GL_COMBINE_ALPHA_ARB; + src0_enum = GL_SOURCE0_ALPHA_ARB; + src1_enum = GL_SOURCE1_ALPHA_ARB; + src2_enum = GL_SOURCE2_ALPHA_ARB; + operand0_enum = GL_OPERAND0_ALPHA_ARB; + operand1_enum = GL_OPERAND1_ALPHA_ARB; + operand2_enum = GL_OPERAND2_ALPHA_ARB; + + // cache current combiner + mCurrAlphaOp = op; + mCurrAlphaSrc1 = src1; + mCurrAlphaSrc2 = src2; + } + else + { + // Set enums to RGB ones + comb_enum = GL_COMBINE_RGB_ARB; + src0_enum = GL_SOURCE0_RGB_ARB; + src1_enum = GL_SOURCE1_RGB_ARB; + src2_enum = GL_SOURCE2_RGB_ARB; + operand0_enum = GL_OPERAND0_RGB_ARB; + operand1_enum = GL_OPERAND1_RGB_ARB; + operand2_enum = GL_OPERAND2_RGB_ARB; + + // cache current combiner + mCurrColorOp = op; + mCurrColorSrc1 = src1; + mCurrColorSrc2 = src2; + } + + switch(op) + { + case TBO_REPLACE: + // Slightly special syntax (no second sources), just set all and return. + glTexEnvi(GL_TEXTURE_ENV, comb_enum, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, src0_enum, source1); + glTexEnvi(GL_TEXTURE_ENV, operand0_enum, operand1); + (isAlpha) ? setAlphaScale(1) : setColorScale(1); + return; + + case TBO_MULT: + glTexEnvi(GL_TEXTURE_ENV, comb_enum, GL_MODULATE); + break; + + case TBO_MULT_X2: + glTexEnvi(GL_TEXTURE_ENV, comb_enum, GL_MODULATE); + scale_amount = 2; + break; + + case TBO_MULT_X4: + glTexEnvi(GL_TEXTURE_ENV, comb_enum, GL_MODULATE); + scale_amount = 4; + break; + + case TBO_ADD: + glTexEnvi(GL_TEXTURE_ENV, comb_enum, GL_ADD); + break; + + case TBO_ADD_SIGNED: + glTexEnvi(GL_TEXTURE_ENV, comb_enum, GL_ADD_SIGNED_ARB); + break; + + case TBO_SUBTRACT: + glTexEnvi(GL_TEXTURE_ENV, comb_enum, GL_SUBTRACT_ARB); + break; + + case TBO_LERP_VERT_ALPHA: + glTexEnvi(GL_TEXTURE_ENV, comb_enum, GL_INTERPOLATE); + glTexEnvi(GL_TEXTURE_ENV, src2_enum, GL_PRIMARY_COLOR_ARB); + glTexEnvi(GL_TEXTURE_ENV, operand2_enum, GL_SRC_ALPHA); + break; + + case TBO_LERP_TEX_ALPHA: + glTexEnvi(GL_TEXTURE_ENV, comb_enum, GL_INTERPOLATE); + glTexEnvi(GL_TEXTURE_ENV, src2_enum, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, operand2_enum, GL_SRC_ALPHA); + break; + + case TBO_LERP_PREV_ALPHA: + glTexEnvi(GL_TEXTURE_ENV, comb_enum, GL_INTERPOLATE); + glTexEnvi(GL_TEXTURE_ENV, src2_enum, GL_PREVIOUS_ARB); + glTexEnvi(GL_TEXTURE_ENV, operand2_enum, GL_SRC_ALPHA); + break; + + case TBO_LERP_CONST_ALPHA: + glTexEnvi(GL_TEXTURE_ENV, comb_enum, GL_INTERPOLATE); + glTexEnvi(GL_TEXTURE_ENV, src2_enum, GL_CONSTANT_ARB); + glTexEnvi(GL_TEXTURE_ENV, operand2_enum, GL_SRC_ALPHA); + break; + + case TBO_LERP_VERT_COLOR: + glTexEnvi(GL_TEXTURE_ENV, comb_enum, GL_INTERPOLATE); + glTexEnvi(GL_TEXTURE_ENV, src2_enum, GL_PRIMARY_COLOR_ARB); + glTexEnvi(GL_TEXTURE_ENV, operand2_enum, (isAlpha) ? GL_SRC_ALPHA : GL_SRC_COLOR); + break; + + default: + llwarns << "Unknown eTextureBlendOp: " << op << ". Setting op to replace." << llendl; + // Slightly special syntax (no second sources), just set all and return. + glTexEnvi(GL_TEXTURE_ENV, comb_enum, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, src0_enum, source1); + glTexEnvi(GL_TEXTURE_ENV, operand0_enum, operand1); + (isAlpha) ? setAlphaScale(1) : setColorScale(1); + return; + } + + // Set sources, operands, and scale accordingly + glTexEnvi(GL_TEXTURE_ENV, src0_enum, source1); + glTexEnvi(GL_TEXTURE_ENV, operand0_enum, operand1); + glTexEnvi(GL_TEXTURE_ENV, src1_enum, source2); + glTexEnvi(GL_TEXTURE_ENV, operand1_enum, operand2); + (isAlpha) ? setAlphaScale(scale_amount) : setColorScale(scale_amount); +} + +void LLTexUnit::setColorScale(S32 scale) +{ + if (mCurrColorScale != scale || gGL.mDirty) + { + mCurrColorScale = scale; + gGL.flush(); + glTexEnvi( GL_TEXTURE_ENV, GL_RGB_SCALE, scale ); + } +} + +void LLTexUnit::setAlphaScale(S32 scale) +{ + if (mCurrAlphaScale != scale || gGL.mDirty) + { + mCurrAlphaScale = scale; + gGL.flush(); + glTexEnvi( GL_TEXTURE_ENV, GL_ALPHA_SCALE, scale ); + } +} + +// Useful for debugging that you've manually assigned a texture operation to the correct +// texture unit based on the currently set active texture in opengl. +void LLTexUnit::debugTextureUnit(void) +{ + if (mIndex < 0) return; + + GLint activeTexture; + glGetIntegerv(GL_ACTIVE_TEXTURE_ARB, &activeTexture); + if ((GL_TEXTURE0_ARB + mIndex) != activeTexture) + { + U32 set_unit = (activeTexture - GL_TEXTURE0_ARB); + llwarns << "Incorrect Texture Unit! Expected: " << set_unit << " Actual: " << mIndex << llendl; + } +} + + +LLRender::LLRender() + : mDirty(false), + mCount(0), + mMode(LLRender::TRIANGLES), + mCurrTextureUnitIndex(0), + mMaxAnisotropy(0.f) +{ + mBuffer = new LLVertexBuffer(immediate_mask, 0); + mBuffer->allocateBuffer(4096, 0, TRUE); + mBuffer->getVertexStrider(mVerticesp); + mBuffer->getTexCoord0Strider(mTexcoordsp); + mBuffer->getColorStrider(mColorsp); + + mTexUnits.reserve(LL_NUM_TEXTURE_LAYERS); + for (U32 i = 0; i < LL_NUM_TEXTURE_LAYERS; i++) + { + mTexUnits.push_back(new LLTexUnit(i)); + } + mDummyTexUnit = new LLTexUnit(-1); + + for (U32 i = 0; i < 4; i++) + { + mCurrColorMask[i] = true; + } + + mCurrAlphaFunc = CF_DEFAULT; + mCurrAlphaFuncVal = 0.01f; + mCurrBlendColorSFactor = BF_UNDEF; + mCurrBlendAlphaSFactor = BF_UNDEF; + mCurrBlendColorDFactor = BF_UNDEF; + mCurrBlendAlphaDFactor = BF_UNDEF; +} + +LLRender::~LLRender() +{ + shutdown(); +} + +void LLRender::shutdown() +{ + for (U32 i = 0; i < mTexUnits.size(); i++) + { + delete mTexUnits[i]; + } + mTexUnits.clear(); + delete mDummyTexUnit; + mDummyTexUnit = NULL; +} + +void LLRender::refreshState(void) +{ + mDirty = true; + + U32 active_unit = mCurrTextureUnitIndex; + + for (U32 i = 0; i < mTexUnits.size(); i++) + { + mTexUnits[i]->refreshState(); + } + + mTexUnits[active_unit]->activate(); + + setColorMask(mCurrColorMask[0], mCurrColorMask[1], mCurrColorMask[2], mCurrColorMask[3]); + + setAlphaRejectSettings(mCurrAlphaFunc, mCurrAlphaFuncVal); + + mDirty = false; +} + +void LLRender::translatef(const GLfloat& x, const GLfloat& y, const GLfloat& z) +{ + flush(); + glTranslatef(x,y,z); +} + +void LLRender::scalef(const GLfloat& x, const GLfloat& y, const GLfloat& z) +{ + flush(); + glScalef(x,y,z); +} + +void LLRender::pushMatrix() +{ + flush(); + glPushMatrix(); +} + +void LLRender::popMatrix() +{ + flush(); + glPopMatrix(); +} + +void LLRender::translateUI(F32 x, F32 y, F32 z) +{ + if (mUIOffset.empty()) + { + llerrs << "Need to push a UI translation frame before offsetting" << llendl; + } + + mUIOffset.back().mV[0] += x; + mUIOffset.back().mV[1] += y; + mUIOffset.back().mV[2] += z; +} + +void LLRender::scaleUI(F32 x, F32 y, F32 z) +{ + if (mUIScale.empty()) + { + llerrs << "Need to push a UI transformation frame before scaling." << llendl; + } + + mUIScale.back().scaleVec(LLVector3(x,y,z)); +} + +void LLRender::pushUIMatrix() +{ + if (mUIOffset.empty()) + { + mUIOffset.push_back(LLVector3(0,0,0)); + } + else + { + mUIOffset.push_back(mUIOffset.back()); + } + + if (mUIScale.empty()) + { + mUIScale.push_back(LLVector3(1,1,1)); + } + else + { + mUIScale.push_back(mUIScale.back()); + } +} + +void LLRender::popUIMatrix() +{ + if (mUIOffset.empty()) + { + llerrs << "UI offset stack blown." << llendl; + } + mUIOffset.pop_back(); + mUIScale.pop_back(); +} + +LLVector3 LLRender::getUITranslation() +{ + if (mUIOffset.empty()) + { + return LLVector3::zero; + } + return mUIOffset.back(); +} + +LLVector3 LLRender::getUIScale() +{ + if (mUIScale.empty()) + { + return LLVector3(1.f, 1.f, 1.f); + } + return mUIScale.back(); +} + + +void LLRender::loadUIIdentity() +{ + if (mUIOffset.empty()) + { + llerrs << "Need to push UI translation frame before clearing offset." << llendl; + } + mUIOffset.back().setVec(0,0,0); + mUIScale.back().setVec(1,1,1); +} + +void LLRender::setColorMask(bool writeColor, bool writeAlpha) +{ + setColorMask(writeColor, writeColor, writeColor, writeAlpha); +} + +void LLRender::setColorMask(bool writeColorR, bool writeColorG, bool writeColorB, bool writeAlpha) +{ + flush(); + + mCurrColorMask[0] = writeColorR; + mCurrColorMask[1] = writeColorG; + mCurrColorMask[2] = writeColorB; + mCurrColorMask[3] = writeAlpha; + + glColorMask(writeColorR ? GL_TRUE : GL_FALSE, + writeColorG ? GL_TRUE : GL_FALSE, + writeColorB ? GL_TRUE : GL_FALSE, + writeAlpha ? GL_TRUE : GL_FALSE); +} + +void LLRender::setSceneBlendType(eBlendType type) +{ + switch (type) + { + case BT_ALPHA: + blendFunc(BF_SOURCE_ALPHA, BF_ONE_MINUS_SOURCE_ALPHA); + break; + case BT_ADD: + blendFunc(BF_ONE, BF_ONE); + break; + case BT_ADD_WITH_ALPHA: + blendFunc(BF_SOURCE_ALPHA, BF_ONE); + break; + case BT_MULT: + blendFunc(BF_DEST_COLOR, BF_ZERO); + break; + case BT_MULT_ALPHA: + blendFunc(BF_DEST_ALPHA, BF_ZERO); + break; + case BT_MULT_X2: + blendFunc(BF_DEST_COLOR, BF_SOURCE_COLOR); + break; + case BT_REPLACE: + blendFunc(BF_ONE, BF_ZERO); + break; + default: + llerrs << "Unknown Scene Blend Type: " << type << llendl; + break; + } +} + +void LLRender::setAlphaRejectSettings(eCompareFunc func, F32 value) +{ + flush(); + + mCurrAlphaFunc = func; + mCurrAlphaFuncVal = value; + if (func == CF_DEFAULT) + { + glAlphaFunc(GL_GREATER, 0.01f); + } + else + { + glAlphaFunc(sGLCompareFunc[func], value); + } +} + +void LLRender::blendFunc(eBlendFactor sfactor, eBlendFactor dfactor) +{ + llassert(sfactor < BF_UNDEF); + llassert(dfactor < BF_UNDEF); + if (mCurrBlendColorSFactor != sfactor || mCurrBlendColorDFactor != dfactor || + mCurrBlendAlphaSFactor != sfactor || mCurrBlendAlphaDFactor != dfactor) + { + mCurrBlendColorSFactor = sfactor; + mCurrBlendAlphaSFactor = sfactor; + mCurrBlendColorDFactor = dfactor; + mCurrBlendAlphaDFactor = dfactor; + flush(); + glBlendFunc(sGLBlendFactor[sfactor], sGLBlendFactor[dfactor]); + } +} + +void LLRender::blendFunc(eBlendFactor color_sfactor, eBlendFactor color_dfactor, + eBlendFactor alpha_sfactor, eBlendFactor alpha_dfactor) +{ + llassert(color_sfactor < BF_UNDEF); + llassert(color_dfactor < BF_UNDEF); + llassert(alpha_sfactor < BF_UNDEF); + llassert(alpha_dfactor < BF_UNDEF); + if (!gGLManager.mHasBlendFuncSeparate) + { + LL_WARNS_ONCE("render") << "no glBlendFuncSeparateEXT(), using color-only blend func" << llendl; + blendFunc(color_sfactor, color_dfactor); + return; + } + if (mCurrBlendColorSFactor != color_sfactor || mCurrBlendColorDFactor != color_dfactor || + mCurrBlendAlphaSFactor != alpha_sfactor || mCurrBlendAlphaDFactor != alpha_dfactor) + { + mCurrBlendColorSFactor = color_sfactor; + mCurrBlendAlphaSFactor = alpha_sfactor; + mCurrBlendColorDFactor = color_dfactor; + mCurrBlendAlphaDFactor = alpha_dfactor; + flush(); + glBlendFuncSeparateEXT(sGLBlendFactor[color_sfactor], sGLBlendFactor[color_dfactor], + sGLBlendFactor[alpha_sfactor], sGLBlendFactor[alpha_dfactor]); + } +} + +LLTexUnit* LLRender::getTexUnit(U32 index) +{ + if (index < mTexUnits.size()) + { + return mTexUnits[index]; + } + else + { + lldebugs << "Non-existing texture unit layer requested: " << index << llendl; + return mDummyTexUnit; + } +} + +bool LLRender::verifyTexUnitActive(U32 unitToVerify) +{ + if (mCurrTextureUnitIndex == unitToVerify) + { + return true; + } + else + { + llwarns << "TexUnit currently active: " << mCurrTextureUnitIndex << " (expecting " << unitToVerify << ")" << llendl; + return false; + } +} + +void LLRender::clearErrors() +{ + while (glGetError()) + { + //loop until no more error flags left + } +} + +void LLRender::begin(const GLuint& mode) +{ + if (mode != mMode) + { + if (mMode == LLRender::QUADS || + mMode == LLRender::LINES || + mMode == LLRender::TRIANGLES || + mMode == LLRender::POINTS) + { + flush(); + } + else if (mCount != 0) + { + llerrs << "gGL.begin() called redundantly." << llendl; + } + + mMode = mode; + } +} + +void LLRender::end() +{ + if (mCount == 0) + { + return; + //IMM_ERRS << "GL begin and end called with no vertices specified." << llendl; + } + + if ((mMode != LLRender::QUADS && + mMode != LLRender::LINES && + mMode != LLRender::TRIANGLES && + mMode != LLRender::POINTS) || + mCount > 2048) + { + flush(); + } +} +void LLRender::flush() +{ + if (mCount > 0) + { +#if 0 + if (!glIsEnabled(GL_VERTEX_ARRAY)) + { + llerrs << "foo 1" << llendl; + } + + if (!glIsEnabled(GL_COLOR_ARRAY)) + { + llerrs << "foo 2" << llendl; + } + + if (!glIsEnabled(GL_TEXTURE_COORD_ARRAY)) + { + llerrs << "foo 3" << llendl; + } + + if (glIsEnabled(GL_NORMAL_ARRAY)) + { + llerrs << "foo 7" << llendl; + } + + GLvoid* pointer; + + glGetPointerv(GL_VERTEX_ARRAY_POINTER, &pointer); + if (pointer != &(mBuffer[0].v)) + { + llerrs << "foo 4" << llendl; + } + + glGetPointerv(GL_COLOR_ARRAY_POINTER, &pointer); + if (pointer != &(mBuffer[0].c)) + { + llerrs << "foo 5" << llendl; + } + + glGetPointerv(GL_TEXTURE_COORD_ARRAY_POINTER, &pointer); + if (pointer != &(mBuffer[0].uv)) + { + llerrs << "foo 6" << llendl; + } +#endif + + if (!mUIOffset.empty()) + { + sUICalls++; + sUIVerts += mCount; + } + + if (gDebugGL) + { + if (mMode == LLRender::QUADS) + { + if (mCount%4 != 0) + { + llerrs << "Incomplete quad rendered." << llendl; + } + } + + if (mMode == LLRender::TRIANGLES) + { + if (mCount%3 != 0) + { + llerrs << "Incomplete triangle rendered." << llendl; + } + } + + if (mMode == LLRender::LINES) + { + if (mCount%2 != 0) + { + llerrs << "Incomplete line rendered." << llendl; + } + } + } + + mBuffer->setBuffer(immediate_mask); + mBuffer->drawArrays(mMode, 0, mCount); + + mVerticesp[0] = mVerticesp[mCount]; + mTexcoordsp[0] = mTexcoordsp[mCount]; + mColorsp[0] = mColorsp[mCount]; + mCount = 0; + } +} + +void LLRender::vertex3f(const GLfloat& x, const GLfloat& y, const GLfloat& z) +{ + //the range of mVerticesp, mColorsp and mTexcoordsp is [0, 4095] + if (mCount > 4094) + { + // llwarns << "GL immediate mode overflow. Some geometry not drawn." << llendl; + return; + } + + if (mUIOffset.empty()) + { + mVerticesp[mCount] = LLVector3(x,y,z); + } + else + { + LLVector3 vert = (LLVector3(x,y,z)+mUIOffset.back()).scaledVec(mUIScale.back()); + mVerticesp[mCount] = vert; + } + + mCount++; + mVerticesp[mCount] = mVerticesp[mCount-1]; + mColorsp[mCount] = mColorsp[mCount-1]; + mTexcoordsp[mCount] = mTexcoordsp[mCount-1]; +} + +void LLRender::vertexBatchPreTransformed(LLVector3* verts, S32 vert_count) +{ + if (mCount + vert_count > 4094) + { + // llwarns << "GL immediate mode overflow. Some geometry not drawn." << llendl; + return; + } + + for (S32 i = 0; i < vert_count; i++) + { + mVerticesp[mCount] = verts[i]; + + mCount++; + mTexcoordsp[mCount] = mTexcoordsp[mCount-1]; + mColorsp[mCount] = mColorsp[mCount-1]; + } + + mVerticesp[mCount] = mVerticesp[mCount-1]; +} + +void LLRender::vertexBatchPreTransformed(LLVector3* verts, LLVector2* uvs, S32 vert_count) +{ + if (mCount + vert_count > 4094) + { + // llwarns << "GL immediate mode overflow. Some geometry not drawn." << llendl; + return; + } + + for (S32 i = 0; i < vert_count; i++) + { + mVerticesp[mCount] = verts[i]; + mTexcoordsp[mCount] = uvs[i]; + + mCount++; + mColorsp[mCount] = mColorsp[mCount-1]; + } + + mVerticesp[mCount] = mVerticesp[mCount-1]; + mTexcoordsp[mCount] = mTexcoordsp[mCount-1]; +} + +void LLRender::vertexBatchPreTransformed(LLVector3* verts, LLVector2* uvs, LLColor4U* colors, S32 vert_count) +{ + if (mCount + vert_count > 4094) + { + // llwarns << "GL immediate mode overflow. Some geometry not drawn." << llendl; + return; + } + + for (S32 i = 0; i < vert_count; i++) + { + mVerticesp[mCount] = verts[i]; + mTexcoordsp[mCount] = uvs[i]; + mColorsp[mCount] = colors[i]; + + mCount++; + } + + mVerticesp[mCount] = mVerticesp[mCount-1]; + mTexcoordsp[mCount] = mTexcoordsp[mCount-1]; + mColorsp[mCount] = mColorsp[mCount-1]; +} + +void LLRender::vertex2i(const GLint& x, const GLint& y) +{ + vertex3f((GLfloat) x, (GLfloat) y, 0); +} + +void LLRender::vertex2f(const GLfloat& x, const GLfloat& y) +{ + vertex3f(x,y,0); +} + +void LLRender::vertex2fv(const GLfloat* v) +{ + vertex3f(v[0], v[1], 0); +} + +void LLRender::vertex3fv(const GLfloat* v) +{ + vertex3f(v[0], v[1], v[2]); +} + +void LLRender::texCoord2f(const GLfloat& x, const GLfloat& y) +{ + mTexcoordsp[mCount] = LLVector2(x,y); +} + +void LLRender::texCoord2i(const GLint& x, const GLint& y) +{ + texCoord2f((GLfloat) x, (GLfloat) y); +} + +void LLRender::texCoord2fv(const GLfloat* tc) +{ + texCoord2f(tc[0], tc[1]); +} + +void LLRender::color4ub(const GLubyte& r, const GLubyte& g, const GLubyte& b, const GLubyte& a) +{ + mColorsp[mCount] = LLColor4U(r,g,b,a); +} +void LLRender::color4ubv(const GLubyte* c) +{ + color4ub(c[0], c[1], c[2], c[3]); +} + +void LLRender::color4f(const GLfloat& r, const GLfloat& g, const GLfloat& b, const GLfloat& a) +{ + color4ub((GLubyte) (llclamp(r, 0.f, 1.f)*255), + (GLubyte) (llclamp(g, 0.f, 1.f)*255), + (GLubyte) (llclamp(b, 0.f, 1.f)*255), + (GLubyte) (llclamp(a, 0.f, 1.f)*255)); +} + +void LLRender::color4fv(const GLfloat* c) +{ + color4f(c[0],c[1],c[2],c[3]); +} + +void LLRender::color3f(const GLfloat& r, const GLfloat& g, const GLfloat& b) +{ + color4f(r,g,b,1); +} + +void LLRender::color3fv(const GLfloat* c) +{ + color4f(c[0],c[1],c[2],1); +} + +void LLRender::debugTexUnits(void) +{ + LL_INFOS("TextureUnit") << "Active TexUnit: " << mCurrTextureUnitIndex << LL_ENDL; + std::string active_enabled = "false"; + for (U32 i = 0; i < mTexUnits.size(); i++) + { + if (getTexUnit(i)->mCurrTexType != LLTexUnit::TT_NONE) + { + if (i == mCurrTextureUnitIndex) active_enabled = "true"; + LL_INFOS("TextureUnit") << "TexUnit: " << i << " Enabled" << LL_ENDL; + LL_INFOS("TextureUnit") << "Enabled As: " ; + switch (getTexUnit(i)->mCurrTexType) + { + case LLTexUnit::TT_TEXTURE: + LL_CONT << "Texture 2D"; + break; + case LLTexUnit::TT_RECT_TEXTURE: + LL_CONT << "Texture Rectangle"; + break; + case LLTexUnit::TT_CUBE_MAP: + LL_CONT << "Cube Map"; + break; + default: + LL_CONT << "ARGH!!! NONE!"; + break; + } + LL_CONT << ", Texture Bound: " << getTexUnit(i)->mCurrTexture << LL_ENDL; + } + } + LL_INFOS("TextureUnit") << "Active TexUnit Enabled : " << active_enabled << LL_ENDL; +} + diff --git a/indra/llrender/llrender.h b/indra/llrender/llrender.h new file mode 100644 index 0000000000..2767aa64a8 --- /dev/null +++ b/indra/llrender/llrender.h @@ -0,0 +1,387 @@ +/** + * @file llrender.h + * @brief LLRender definition + * + * This class acts as a wrapper for OpenGL calls. + * The goal of this class is to minimize the number of api calls due to legacy rendering + * code, to define an interface for a multiple rendering API abstraction of the UI + * rendering, and to abstract out direct rendering calls in a way that is cleaner and easier to maintain. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * 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; + * version 2.1 of the License only. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLGLRENDER_H +#define LL_LLGLRENDER_H + +//#include "linden_common.h" + +#include "v2math.h" +#include "v3math.h" +#include "v4coloru.h" +#include "llstrider.h" +#include "llpointer.h" +#include "llglheaders.h" + +class LLVertexBuffer; +class LLCubeMap; +class LLImageGL; +class LLRenderTarget; +class LLTexture ; + +class LLTexUnit +{ + friend class LLRender; +public: + typedef enum + { + TT_TEXTURE = 0, // Standard 2D Texture + TT_RECT_TEXTURE, // Non power of 2 texture + TT_CUBE_MAP, // 6-sided cube map texture + TT_NONE // No texture type is currently enabled + } eTextureType; + + typedef enum + { + TAM_WRAP = 0, // Standard 2D Texture + TAM_MIRROR, // Non power of 2 texture + TAM_CLAMP // No texture type is currently enabled + } eTextureAddressMode; + + typedef enum + { // Note: If mipmapping or anisotropic are not enabled or supported it should fall back gracefully + TFO_POINT = 0, // Equal to: min=point, mag=point, mip=none. + TFO_BILINEAR, // Equal to: min=linear, mag=linear, mip=point. + TFO_TRILINEAR, // Equal to: min=linear, mag=linear, mip=linear. + TFO_ANISOTROPIC // Equal to: min=anisotropic, max=anisotropic, mip=linear. + } eTextureFilterOptions; + + typedef enum + { + TB_REPLACE = 0, + TB_ADD, + TB_MULT, + TB_MULT_X2, + TB_ALPHA_BLEND, + TB_COMBINE // Doesn't need to be set directly, setTexture___Blend() set TB_COMBINE automatically + } eTextureBlendType; + + typedef enum + { + TBO_REPLACE = 0, // Use Source 1 + TBO_MULT, // Multiply: ( Source1 * Source2 ) + TBO_MULT_X2, // Multiply then scale by 2: ( 2.0 * ( Source1 * Source2 ) ) + TBO_MULT_X4, // Multiply then scale by 4: ( 4.0 * ( Source1 * Source2 ) ) + TBO_ADD, // Add: ( Source1 + Source2 ) + TBO_ADD_SIGNED, // Add then subtract 0.5: ( ( Source1 + Source2 ) - 0.5 ) + TBO_SUBTRACT, // Subtract Source2 from Source1: ( Source1 - Source2 ) + TBO_LERP_VERT_ALPHA, // Interpolate based on Vertex Alpha (VA): ( Source1 * VA + Source2 * (1-VA) ) + TBO_LERP_TEX_ALPHA, // Interpolate based on Texture Alpha (TA): ( Source1 * TA + Source2 * (1-TA) ) + TBO_LERP_PREV_ALPHA, // Interpolate based on Previous Alpha (PA): ( Source1 * PA + Source2 * (1-PA) ) + TBO_LERP_CONST_ALPHA, // Interpolate based on Const Alpha (CA): ( Source1 * CA + Source2 * (1-CA) ) + TBO_LERP_VERT_COLOR // Interpolate based on Vertex Col (VC): ( Source1 * VC + Source2 * (1-VC) ) + // *Note* TBO_LERP_VERTEX_COLOR only works with setTextureColorBlend(), + // and falls back to TBO_LERP_VERTEX_ALPHA for setTextureAlphaBlend(). + } eTextureBlendOp; + + typedef enum + { + TBS_PREV_COLOR = 0, // Color from the previous texture stage + TBS_PREV_ALPHA, + TBS_ONE_MINUS_PREV_COLOR, + TBS_ONE_MINUS_PREV_ALPHA, + TBS_TEX_COLOR, // Color from the texture bound to this stage + TBS_TEX_ALPHA, + TBS_ONE_MINUS_TEX_COLOR, + TBS_ONE_MINUS_TEX_ALPHA, + TBS_VERT_COLOR, // The vertex color currently set + TBS_VERT_ALPHA, + TBS_ONE_MINUS_VERT_COLOR, + TBS_ONE_MINUS_VERT_ALPHA, + TBS_CONST_COLOR, // The constant color value currently set + TBS_CONST_ALPHA, + TBS_ONE_MINUS_CONST_COLOR, + TBS_ONE_MINUS_CONST_ALPHA + } eTextureBlendSrc; + + LLTexUnit(S32 index); + + // Refreshes renderer state of the texture unit to the cached values + // Needed when the render context has changed and invalidated the current state + void refreshState(void); + + // returns the index of this texture unit + S32 getIndex(void) const { return mIndex; } + + // Sets this tex unit to be the currently active one + void activate(void); + + // Enables this texture unit for the given texture type + // (automatically disables any previously enabled texture type) + void enable(eTextureType type); + + // Disables the current texture unit + void disable(void); + + // Binds the LLImageGL to this texture unit + // (automatically enables the unit for the LLImageGL's texture type) + bool bind(LLImageGL* texture, bool for_rendering = false, bool forceBind = false); + bool bind(LLTexture* texture, bool for_rendering = false, bool forceBind = false); + + // Binds a cubemap to this texture unit + // (automatically enables the texture unit for cubemaps) + bool bind(LLCubeMap* cubeMap); + + // Binds a render target to this texture unit + // (automatically enables the texture unit for the RT's texture type) + bool bind(LLRenderTarget * renderTarget, bool bindDepth = false); + + // Manually binds a texture to the texture unit + // (automatically enables the tex unit for the given texture type) + bool bindManual(eTextureType type, U32 texture, bool hasMips = false); + + // Unbinds the currently bound texture of the given type + // (only if there's a texture of the given type currently bound) + void unbind(eTextureType type); + + // Sets the addressing mode used to sample the texture + // Warning: this stays set for the bound texture forever, + // make sure you want to permanently change the address mode for the bound texture. + void setTextureAddressMode(eTextureAddressMode mode); + + // Sets the filtering options used to sample the texture + // Warning: this stays set for the bound texture forever, + // make sure you want to permanently change the filtering for the bound texture. + void setTextureFilteringOption(LLTexUnit::eTextureFilterOptions option); + + void setTextureBlendType(eTextureBlendType type); + + inline void setTextureColorBlend(eTextureBlendOp op, eTextureBlendSrc src1, eTextureBlendSrc src2 = TBS_PREV_COLOR) + { setTextureCombiner(op, src1, src2, false); } + + // NOTE: If *_COLOR enums are passed to src1 or src2, the corresponding *_ALPHA enum will be used instead. + inline void setTextureAlphaBlend(eTextureBlendOp op, eTextureBlendSrc src1, eTextureBlendSrc src2 = TBS_PREV_ALPHA) + { setTextureCombiner(op, src1, src2, true); } + + static U32 getInternalType(eTextureType type); + + U32 getCurrTexture(void) { return mCurrTexture; } + + eTextureType getCurrType(void) { return mCurrTexType; } + + void setHasMipMaps(bool hasMips) { mHasMipMaps = hasMips; } + +protected: + S32 mIndex; + U32 mCurrTexture; + eTextureType mCurrTexType; + eTextureBlendType mCurrBlendType; + eTextureBlendOp mCurrColorOp; + eTextureBlendSrc mCurrColorSrc1; + eTextureBlendSrc mCurrColorSrc2; + eTextureBlendOp mCurrAlphaOp; + eTextureBlendSrc mCurrAlphaSrc1; + eTextureBlendSrc mCurrAlphaSrc2; + S32 mCurrColorScale; + S32 mCurrAlphaScale; + bool mHasMipMaps; + + void debugTextureUnit(void); + void setColorScale(S32 scale); + void setAlphaScale(S32 scale); + GLint getTextureSource(eTextureBlendSrc src); + GLint getTextureSourceType(eTextureBlendSrc src, bool isAlpha = false); + void setTextureCombiner(eTextureBlendOp op, eTextureBlendSrc src1, eTextureBlendSrc src2, bool isAlpha = false); +}; + +class LLRender +{ + friend class LLTexUnit; +public: + + typedef enum { + TRIANGLES = 0, + TRIANGLE_STRIP, + TRIANGLE_FAN, + POINTS, + LINES, + LINE_STRIP, + QUADS, + LINE_LOOP, + NUM_MODES + } eGeomModes; + + typedef enum + { + CF_NEVER = 0, + CF_ALWAYS, + CF_LESS, + CF_LESS_EQUAL, + CF_EQUAL, + CF_NOT_EQUAL, + CF_GREATER_EQUAL, + CF_GREATER, + CF_DEFAULT + } eCompareFunc; + + typedef enum + { + BT_ALPHA = 0, + BT_ADD, + BT_ADD_WITH_ALPHA, // Additive blend modulated by the fragment's alpha. + BT_MULT, + BT_MULT_ALPHA, + BT_MULT_X2, + BT_REPLACE + } eBlendType; + + typedef enum + { + BF_ONE = 0, + BF_ZERO, + BF_DEST_COLOR, + BF_SOURCE_COLOR, + BF_ONE_MINUS_DEST_COLOR, + BF_ONE_MINUS_SOURCE_COLOR, + BF_DEST_ALPHA, + BF_SOURCE_ALPHA, + BF_ONE_MINUS_DEST_ALPHA, + BF_ONE_MINUS_SOURCE_ALPHA, + + BF_UNDEF + } eBlendFactor; + + LLRender(); + ~LLRender(); + void shutdown(); + + // Refreshes renderer state to the cached values + // Needed when the render context has changed and invalidated the current state + void refreshState(void); + + void translatef(const GLfloat& x, const GLfloat& y, const GLfloat& z); + void scalef(const GLfloat& x, const GLfloat& y, const GLfloat& z); + void pushMatrix(); + void popMatrix(); + + void translateUI(F32 x, F32 y, F32 z); + void scaleUI(F32 x, F32 y, F32 z); + void pushUIMatrix(); + void popUIMatrix(); + void loadUIIdentity(); + LLVector3 getUITranslation(); + LLVector3 getUIScale(); + + void flush(); + + void begin(const GLuint& mode); + void end(); + void vertex2i(const GLint& x, const GLint& y); + void vertex2f(const GLfloat& x, const GLfloat& y); + void vertex3f(const GLfloat& x, const GLfloat& y, const GLfloat& z); + void vertex2fv(const GLfloat* v); + void vertex3fv(const GLfloat* v); + + void texCoord2i(const GLint& x, const GLint& y); + void texCoord2f(const GLfloat& x, const GLfloat& y); + void texCoord2fv(const GLfloat* tc); + + void color4ub(const GLubyte& r, const GLubyte& g, const GLubyte& b, const GLubyte& a); + void color4f(const GLfloat& r, const GLfloat& g, const GLfloat& b, const GLfloat& a); + void color4fv(const GLfloat* c); + void color3f(const GLfloat& r, const GLfloat& g, const GLfloat& b); + void color3fv(const GLfloat* c); + void color4ubv(const GLubyte* c); + + void vertexBatchPreTransformed(LLVector3* verts, S32 vert_count); + void vertexBatchPreTransformed(LLVector3* verts, LLVector2* uvs, S32 vert_count); + void vertexBatchPreTransformed(LLVector3* verts, LLVector2* uvs, LLColor4U*, S32 vert_count); + + void setColorMask(bool writeColor, bool writeAlpha); + void setColorMask(bool writeColorR, bool writeColorG, bool writeColorB, bool writeAlpha); + void setSceneBlendType(eBlendType type); + + void setAlphaRejectSettings(eCompareFunc func, F32 value = 0.01f); + + // applies blend func to both color and alpha + void blendFunc(eBlendFactor sfactor, eBlendFactor dfactor); + // applies separate blend functions to color and alpha + void blendFunc(eBlendFactor color_sfactor, eBlendFactor color_dfactor, + eBlendFactor alpha_sfactor, eBlendFactor alpha_dfactor); + + LLTexUnit* getTexUnit(U32 index); + + U32 getCurrentTexUnitIndex(void) const { return mCurrTextureUnitIndex; } + + bool verifyTexUnitActive(U32 unitToVerify); + + void debugTexUnits(void); + + void clearErrors(); + + struct Vertex + { + GLfloat v[3]; + GLubyte c[4]; + GLfloat uv[2]; + }; + +public: + static U32 sUICalls; + static U32 sUIVerts; + +private: + bool mDirty; + U32 mCount; + U32 mMode; + U32 mCurrTextureUnitIndex; + bool mCurrColorMask[4]; + eCompareFunc mCurrAlphaFunc; + F32 mCurrAlphaFuncVal; + + LLPointer<LLVertexBuffer> mBuffer; + LLStrider<LLVector3> mVerticesp; + LLStrider<LLVector2> mTexcoordsp; + LLStrider<LLColor4U> mColorsp; + std::vector<LLTexUnit*> mTexUnits; + LLTexUnit* mDummyTexUnit; + + eBlendFactor mCurrBlendColorSFactor; + eBlendFactor mCurrBlendColorDFactor; + eBlendFactor mCurrBlendAlphaSFactor; + eBlendFactor mCurrBlendAlphaDFactor; + + F32 mMaxAnisotropy; + + std::vector<LLVector3> mUIOffset; + std::vector<LLVector3> mUIScale; + +}; + +extern F64 gGLModelView[16]; +extern F64 gGLLastModelView[16]; +extern F64 gGLLastProjection[16]; +extern F64 gGLProjection[16]; +extern S32 gGLViewport[4]; + +extern LLRender gGL; + +#endif diff --git a/indra/llrender/llrendersphere.cpp b/indra/llrender/llrendersphere.cpp new file mode 100644 index 0000000000..a5cd70445f --- /dev/null +++ b/indra/llrender/llrendersphere.cpp @@ -0,0 +1,185 @@ +/** + * @file llrendersphere.cpp + * @brief implementation of the LLRenderSphere class. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * 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; + * version 2.1 of the License only. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +// Sphere creates a set of display lists that can then be called to create +// a lit sphere at different LOD levels. You only need one instance of sphere +// per viewer - then call the appropriate list. + +#include "linden_common.h" + +#include "llrendersphere.h" +#include "llerror.h" + +#include "llglheaders.h" + +GLUquadricObj *gQuadObj2 = NULL; +LLRenderSphere gSphere; + +void drawSolidSphere(GLdouble radius, GLint slices, GLint stacks); + +void drawSolidSphere(GLdouble radius, GLint slices, GLint stacks) +{ + if (!gQuadObj2) + { + gQuadObj2 = gluNewQuadric(); + if (!gQuadObj2) + { + llwarns << "drawSolidSphere couldn't allocate quadric" << llendl; + return; + } + } + + gluQuadricDrawStyle(gQuadObj2, GLU_FILL); + gluQuadricNormals(gQuadObj2, GLU_SMOOTH); + // If we ever changed/used the texture or orientation state + // of quadObj, we'd need to change it to the defaults here + // with gluQuadricTexture and/or gluQuadricOrientation. + gluQuadricTexture(gQuadObj2, GL_TRUE); + gluSphere(gQuadObj2, radius, slices, stacks); +} + + +// A couple thoughts on sphere drawing: +// 1) You need more slices than stacks, but little less than 2:1 +// 2) At low LOD, setting stacks to an odd number avoids a "band" around the equator, making things look smoother +void LLRenderSphere::prerender() +{ + // Create a series of display lists for different LODs + mDList[0] = glGenLists(1); + glNewList(mDList[0], GL_COMPILE); + drawSolidSphere(1.0, 30, 20); + glEndList(); + + mDList[1] = glGenLists(1); + glNewList(mDList[1], GL_COMPILE); + drawSolidSphere(1.0, 20, 15); + glEndList(); + + mDList[2] = glGenLists(1); + glNewList(mDList[2], GL_COMPILE); + drawSolidSphere(1.0, 12, 8); + glEndList(); + + mDList[3] = glGenLists(1); + glNewList(mDList[3], GL_COMPILE); + drawSolidSphere(1.0, 8, 5); + glEndList(); +} + +void LLRenderSphere::cleanupGL() +{ + for (S32 detail = 0; detail < 4; detail++) + { + glDeleteLists(mDList[detail], 1); + mDList[detail] = 0; + } + + if (gQuadObj2) + { + gluDeleteQuadric(gQuadObj2); + gQuadObj2 = NULL; + } +} + +// Constants here are empirically derived from my eyeballs, JNC +// +// The toughest adjustment is the cutoff for the lowest LOD +// Maybe we should have more LODs at the low end? +void LLRenderSphere::render(F32 pixel_area) +{ + S32 level_of_detail; + + if (pixel_area > 10000.f) + { + level_of_detail = 0; + } + else if (pixel_area > 800.f) + { + level_of_detail = 1; + } + else if (pixel_area > 100.f) + { + level_of_detail = 2; + } + else + { + level_of_detail = 3; + } + glCallList(mDList[level_of_detail]); +} + + +void LLRenderSphere::render() +{ + glCallList(mDList[0]); +} + +inline LLVector3 polar_to_cart(F32 latitude, F32 longitude) +{ + return LLVector3(sin(F_TWO_PI * latitude) * cos(F_TWO_PI * longitude), + sin(F_TWO_PI * latitude) * sin(F_TWO_PI * longitude), + cos(F_TWO_PI * latitude)); +} + + +void LLRenderSphere::renderGGL() +{ + S32 const LATITUDE_SLICES = 20; + S32 const LONGITUDE_SLICES = 30; + + if (mSpherePoints.empty()) + { + mSpherePoints.resize(LATITUDE_SLICES + 1); + for (S32 lat_i = 0; lat_i < LATITUDE_SLICES + 1; lat_i++) + { + mSpherePoints[lat_i].resize(LONGITUDE_SLICES + 1); + for (S32 lon_i = 0; lon_i < LONGITUDE_SLICES + 1; lon_i++) + { + F32 lat = (F32)lat_i / LATITUDE_SLICES; + F32 lon = (F32)lon_i / LONGITUDE_SLICES; + + mSpherePoints[lat_i][lon_i] = polar_to_cart(lat, lon); + } + } + } + + gGL.begin(LLRender::TRIANGLES); + + for (S32 lat_i = 0; lat_i < LATITUDE_SLICES; lat_i++) + { + for (S32 lon_i = 0; lon_i < LONGITUDE_SLICES; lon_i++) + { + gGL.vertex3fv(mSpherePoints[lat_i][lon_i].mV); + gGL.vertex3fv(mSpherePoints[lat_i][lon_i+1].mV); + gGL.vertex3fv(mSpherePoints[lat_i+1][lon_i].mV); + + gGL.vertex3fv(mSpherePoints[lat_i+1][lon_i].mV); + gGL.vertex3fv(mSpherePoints[lat_i][lon_i+1].mV); + gGL.vertex3fv(mSpherePoints[lat_i+1][lon_i+1].mV); + } + } + gGL.end(); +} diff --git a/indra/llrender/llrendersphere.h b/indra/llrender/llrendersphere.h new file mode 100644 index 0000000000..96a6bec80c --- /dev/null +++ b/indra/llrender/llrendersphere.h @@ -0,0 +1,56 @@ +/** + * @file llrendersphere.h + * @brief interface for the LLRenderSphere class. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * 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; + * version 2.1 of the License only. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLRENDERSPHERE_H +#define LL_LLRENDERSPHERE_H + +#include "llmath.h" +#include "v3math.h" +#include "v4math.h" +#include "m3math.h" +#include "m4math.h" +#include "v4color.h" +#include "llgl.h" + +void lat2xyz(LLVector3 * result, F32 lat, F32 lon); // utility routine + +class LLRenderSphere +{ +public: + LLGLuint mDList[5]; + + void prerender(); + void cleanupGL(); + void render(F32 pixel_area); // of a box of size 1.0 at that position + void render(); // render at highest LOD + void renderGGL(); // render using LLRender + +private: + std::vector< std::vector<LLVector3> > mSpherePoints; +}; + +extern LLRenderSphere gSphere; +#endif diff --git a/indra/llrender/llrendertarget.cpp b/indra/llrender/llrendertarget.cpp index 5111c7ae2d..890230bbe5 100644 --- a/indra/llrender/llrendertarget.cpp +++ b/indra/llrender/llrendertarget.cpp @@ -2,39 +2,53 @@ * @file llrendertarget.cpp * @brief LLRenderTarget implementation * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2007, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlife.com/developers/opensource/gplv2 + * Copyright (C) 2010, Linden Research, Inc. + * + * 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; + * version 2.1 of the License only. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at http://secondlife.com/developers/opensource/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #include "linden_common.h" #include "llrendertarget.h" -#include "llglimmediate.h" +#include "llrender.h" #include "llgl.h" +LLRenderTarget* LLRenderTarget::sBoundTarget = NULL; + + + +void check_framebuffer_status() +{ + if (gDebugGL) + { + GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + switch (status) + { + case GL_FRAMEBUFFER_COMPLETE_EXT: + break; + default: + ll_fail("check_framebuffer_status failed"); + break; + } + } +} BOOL LLRenderTarget::sUseFBO = FALSE; @@ -47,7 +61,9 @@ LLRenderTarget::LLRenderTarget() : mStencil(0), mUseDepth(FALSE), mRenderDepth(FALSE), - mUsage(GL_TEXTURE_2D) + mUsage(LLTexUnit::TT_TEXTURE), + mSamples(0), + mSampleBuffer(NULL) { } @@ -56,40 +72,26 @@ LLRenderTarget::~LLRenderTarget() release(); } -void LLRenderTarget::allocate(U32 resx, U32 resy, U32 color_fmt, BOOL depth, U32 usage, BOOL use_fbo) + +void LLRenderTarget::setSampleBuffer(LLMultisampleBuffer* buffer) +{ + mSampleBuffer = buffer; +} + +void LLRenderTarget::allocate(U32 resx, U32 resy, U32 color_fmt, BOOL depth, BOOL stencil, LLTexUnit::eTextureType usage, BOOL use_fbo) { stop_glerror(); mResX = resx; mResY = resy; + mStencil = stencil; mUsage = usage; mUseDepth = depth; - release(); - - glGenTextures(1, (GLuint *) &mTex); - glBindTexture(mUsage, mTex); - glTexImage2D(mUsage, 0, color_fmt, mResX, mResY, 0, color_fmt, GL_UNSIGNED_BYTE, NULL); - - glTexParameteri(mUsage, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(mUsage, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - if (mUsage != GL_TEXTURE_RECTANGLE_ARB) - { - glTexParameteri(mUsage, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT); - glTexParameteri(mUsage, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT); - } - else - { - // ATI doesn't support mirrored repeat for rectangular textures. - glTexParameteri(mUsage, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(mUsage, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - } - - stop_glerror(); + release(); if ((sUseFBO || use_fbo) && gGLManager.mHasFramebufferObject) { - if (depth) { stop_glerror(); @@ -99,31 +101,141 @@ void LLRenderTarget::allocate(U32 resx, U32 resy, U32 color_fmt, BOOL depth, U32 glGenFramebuffersEXT(1, (GLuint *) &mFBO); - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFBO); - if (mDepth) { - glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, mUsage, mDepth, 0); - glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, mUsage, mDepth, 0); - stop_glerror(); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFBO); + if (mStencil) + { + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, mDepth); + stop_glerror(); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, mDepth); + stop_glerror(); + } + else + { + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, LLTexUnit::getInternalType(mUsage), mDepth, 0); + stop_glerror(); + } + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); } - - glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, - mUsage, mTex, 0); + stop_glerror(); + } + + addColorAttachment(color_fmt); +} + +void LLRenderTarget::addColorAttachment(U32 color_fmt) +{ + if (color_fmt == 0) + { + return; + } + + U32 offset = mTex.size(); + if (offset >= 4 || + (offset > 0 && (mFBO == 0 || !gGLManager.mHasDrawBuffers))) + { + llerrs << "Too many color attachments!" << llendl; + } + + U32 tex; + LLImageGL::generateTextures(1, &tex); + gGL.getTexUnit(0)->bindManual(mUsage, tex); + + stop_glerror(); + + LLImageGL::setManualImage(LLTexUnit::getInternalType(mUsage), 0, color_fmt, mResX, mResY, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + stop_glerror(); + + if (offset == 0) + { + gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); + } + else + { //don't filter data attachments + gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT); + } + if (mUsage != LLTexUnit::TT_RECT_TEXTURE) + { + gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_MIRROR); + } + else + { + // ATI doesn't support mirrored repeat for rectangular textures. + gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); + } + if (mFBO) + { + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFBO); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT+offset, + LLTexUnit::getInternalType(mUsage), tex, 0); + stop_glerror(); + check_framebuffer_status(); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); - stop_glerror(); } + + mTex.push_back(tex); + } void LLRenderTarget::allocateDepth() { - glGenTextures(1, (GLuint *) &mDepth); - glBindTexture(mUsage, mDepth); - glTexParameteri(mUsage, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(mUsage, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexImage2D(mUsage, 0, GL_DEPTH24_STENCIL8_EXT, mResX, mResY, 0, GL_DEPTH_STENCIL_EXT, GL_UNSIGNED_INT_24_8_EXT, NULL); + if (mStencil) + { + //use render buffers where stencil buffers are in play + glGenRenderbuffersEXT(1, (GLuint *) &mDepth); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, mDepth); + glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, mResX, mResY); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); + } + else + { + LLImageGL::generateTextures(1, &mDepth); + gGL.getTexUnit(0)->bindManual(mUsage, mDepth); + U32 internal_type = LLTexUnit::getInternalType(mUsage); + gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT); + LLImageGL::setManualImage(internal_type, 0, GL_DEPTH_COMPONENT32_ARB, mResX, mResY, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL); + } +} + +void LLRenderTarget::shareDepthBuffer(LLRenderTarget& target) +{ + if (!mFBO || !target.mFBO) + { + llerrs << "Cannot share depth buffer between non FBO render targets." << llendl; + } + + if (mDepth) + { + stop_glerror(); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, target.mFBO); + stop_glerror(); + + if (mStencil) + { + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, mDepth); + stop_glerror(); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, mDepth); + stop_glerror(); + } + else + { + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, LLTexUnit::getInternalType(mUsage), mDepth, 0); + stop_glerror(); + if (mStencil) + { + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, LLTexUnit::getInternalType(mUsage), mDepth, 0); + stop_glerror(); + } + } + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + + target.mUseDepth = TRUE; + } } void LLRenderTarget::release() @@ -134,27 +246,68 @@ void LLRenderTarget::release() mFBO = 0; } - if (mTex) + if (mTex.size() > 0) { - glDeleteTextures(1, (GLuint *) &mTex); - mTex = 0; + LLImageGL::deleteTextures(mTex.size(), &mTex[0]); + mTex.clear(); } if (mDepth) { - glDeleteTextures(1, (GLuint *) &mDepth); + if (mStencil) + { + glDeleteRenderbuffersEXT(1, (GLuint*) &mDepth); + stop_glerror(); + } + else + { + LLImageGL::deleteTextures(1, &mDepth); + stop_glerror(); + } mDepth = 0; } + + mSampleBuffer = NULL; + sBoundTarget = NULL; } void LLRenderTarget::bindTarget() { if (mFBO) { - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFBO); + stop_glerror(); + if (mSampleBuffer) + { + mSampleBuffer->bindTarget(this); + stop_glerror(); + } + else + { + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFBO); + stop_glerror(); + if (gGLManager.mHasDrawBuffers) + { //setup multiple render targets + GLenum drawbuffers[] = {GL_COLOR_ATTACHMENT0_EXT, + GL_COLOR_ATTACHMENT1_EXT, + GL_COLOR_ATTACHMENT2_EXT, + GL_COLOR_ATTACHMENT3_EXT}; + glDrawBuffersARB(mTex.size(), drawbuffers); + } + + if (mTex.empty()) + { //no color buffer to draw to + glDrawBuffer(GL_NONE); + glReadBuffer(GL_NONE); + } + + check_framebuffer_status(); + + stop_glerror(); + } } glViewport(0, 0, mResX, mResY); + sBoundTarget = this; } // static @@ -164,9 +317,10 @@ void LLRenderTarget::unbindTarget() { glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); } + sBoundTarget = NULL; } -void LLRenderTarget::clear() +void LLRenderTarget::clear(U32 mask_in) { U32 mask = GL_COLOR_BUFFER_BIT; if (mUseDepth) @@ -175,34 +329,45 @@ void LLRenderTarget::clear() } if (mFBO) { - glClear(mask); + check_framebuffer_status(); + stop_glerror(); + glClear(mask & mask_in); + stop_glerror(); } else { LLGLEnable scissor(GL_SCISSOR_TEST); glScissor(0, 0, mResX, mResY); - glClear(mask); + stop_glerror(); + glClear(mask & mask_in); } } -void LLRenderTarget::bindTexture() +U32 LLRenderTarget::getTexture(U32 attachment) const { - glBindTexture(mUsage, mTex); + if (attachment > mTex.size()-1) + { + llerrs << "Invalid attachment index." << llendl; + } + return mTex[attachment]; } -void LLRenderTarget::bindDepth() +void LLRenderTarget::bindTexture(U32 index, S32 channel) { - glBindTexture(mUsage, mDepth); + if (index > mTex.size()-1) + { + llerrs << "Invalid attachment index." << llendl; + } + gGL.getTexUnit(channel)->bindManual(mUsage, mTex[index]); } - void LLRenderTarget::flush(BOOL fetch_depth) { gGL.flush(); if (!mFBO) { - bindTexture(); - glCopyTexSubImage2D(mUsage, 0, 0, 0, 0, 0, mResX, mResY); + gGL.getTexUnit(0)->bind(this); + glCopyTexSubImage2D(LLTexUnit::getInternalType(mUsage), 0, 0, 0, 0, 0, mResX, mResY); if (fetch_depth) { @@ -211,19 +376,137 @@ void LLRenderTarget::flush(BOOL fetch_depth) allocateDepth(); } - bindDepth(); - glCopyTexImage2D(mUsage, 0, GL_DEPTH24_STENCIL8_EXT, 0, 0, mResX, mResY, 0); + gGL.getTexUnit(0)->bind(this); + glCopyTexImage2D(LLTexUnit::getInternalType(mUsage), 0, GL_DEPTH24_STENCIL8_EXT, 0, 0, mResX, mResY, 0); + } + + gGL.getTexUnit(0)->disable(); + } + else + { +#if !LL_DARWIN + + stop_glerror(); + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + + stop_glerror(); + + if (mSampleBuffer) + { + LLGLEnable multisample(GL_MULTISAMPLE_ARB); + stop_glerror(); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFBO); + stop_glerror(); + check_framebuffer_status(); + glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, mSampleBuffer->mFBO); + check_framebuffer_status(); + + stop_glerror(); + glBlitFramebufferEXT(0, 0, mResX, mResY, 0, 0, mResX, mResY, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST); + stop_glerror(); + + if (mTex.size() > 1) + { + for (U32 i = 1; i < mTex.size(); ++i) + { + glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + LLTexUnit::getInternalType(mUsage), mTex[i], 0); + stop_glerror(); + glFramebufferRenderbufferEXT(GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, mSampleBuffer->mTex[i]); + stop_glerror(); + glBlitFramebufferEXT(0, 0, mResX, mResY, 0, 0, mResX, mResY, GL_COLOR_BUFFER_BIT, GL_NEAREST); + stop_glerror(); + } + + for (U32 i = 0; i < mTex.size(); ++i) + { + glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT+i, + LLTexUnit::getInternalType(mUsage), mTex[i], 0); + stop_glerror(); + glFramebufferRenderbufferEXT(GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT+i, GL_RENDERBUFFER_EXT, mSampleBuffer->mTex[i]); + stop_glerror(); + } + } } +#endif + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + } +} + +void LLRenderTarget::copyContents(LLRenderTarget& source, S32 srcX0, S32 srcY0, S32 srcX1, S32 srcY1, + S32 dstX0, S32 dstY0, S32 dstX1, S32 dstY1, U32 mask, U32 filter) +{ +#if !LL_DARWIN + gGL.flush(); + if (!source.mFBO || !mFBO) + { + llerrs << "Cannot copy framebuffer contents for non FBO render targets." << llendl; + } + + if (mSampleBuffer) + { + mSampleBuffer->copyContents(source, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); } else { + if (mask == GL_DEPTH_BUFFER_BIT && source.mStencil != mStencil) + { + stop_glerror(); + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, source.mFBO); + gGL.getTexUnit(0)->bind(this, true); + stop_glerror(); + glCopyTexSubImage2D(LLTexUnit::getInternalType(mUsage), 0, srcX0, srcY0, dstX0, dstY0, dstX1, dstY1); + stop_glerror(); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + stop_glerror(); + } + else + { + glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, source.mFBO); + stop_glerror(); + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, mFBO); + stop_glerror(); + check_framebuffer_status(); + stop_glerror(); + glBlitFramebufferEXT(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); + stop_glerror(); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + stop_glerror(); + } + } +#endif +} + +//static +void LLRenderTarget::copyContentsToFramebuffer(LLRenderTarget& source, S32 srcX0, S32 srcY0, S32 srcX1, S32 srcY1, + S32 dstX0, S32 dstY0, S32 dstX1, S32 dstY1, U32 mask, U32 filter) +{ +#if !LL_DARWIN + if (!source.mFBO) + { + llerrs << "Cannot copy framebuffer contents for non FBO render targets." << llendl; + } + { + glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, source.mFBO); + stop_glerror(); + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0); + stop_glerror(); + check_framebuffer_status(); + stop_glerror(); + glBlitFramebufferEXT(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); + stop_glerror(); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + stop_glerror(); } +#endif } BOOL LLRenderTarget::isComplete() const { - return (mTex || mDepth) ? TRUE : FALSE; + return (!mTex.empty() || mDepth) ? TRUE : FALSE; } void LLRenderTarget::getViewport(S32* viewport) @@ -234,3 +517,192 @@ void LLRenderTarget::getViewport(S32* viewport) viewport[3] = mResY; } +//================================================== +// LLMultisampleBuffer implementation +//================================================== +LLMultisampleBuffer::LLMultisampleBuffer() +{ + +} + +LLMultisampleBuffer::~LLMultisampleBuffer() +{ + releaseSampleBuffer(); +} + +void LLMultisampleBuffer::releaseSampleBuffer() +{ + if (mFBO) + { + glDeleteFramebuffersEXT(1, (GLuint *) &mFBO); + mFBO = 0; + } + + if (mTex.size() > 0) + { + glDeleteRenderbuffersEXT(mTex.size(), (GLuint *) &mTex[0]); + mTex.clear(); + } + + if (mDepth) + { + glDeleteRenderbuffersEXT(1, (GLuint *) &mDepth); + mDepth = 0; + } +} + +void LLMultisampleBuffer::bindTarget() +{ + bindTarget(this); +} + +void LLMultisampleBuffer::bindTarget(LLRenderTarget* ref) +{ + if (!ref) + { + ref = this; + } + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFBO); + if (gGLManager.mHasDrawBuffers) + { //setup multiple render targets + GLenum drawbuffers[] = {GL_COLOR_ATTACHMENT0_EXT, + GL_COLOR_ATTACHMENT1_EXT, + GL_COLOR_ATTACHMENT2_EXT, + GL_COLOR_ATTACHMENT3_EXT}; + glDrawBuffersARB(ref->mTex.size(), drawbuffers); + } + + check_framebuffer_status(); + + glViewport(0, 0, mResX, mResY); + + sBoundTarget = this; +} + +void LLMultisampleBuffer::allocate(U32 resx, U32 resy, U32 color_fmt, BOOL depth, BOOL stencil, LLTexUnit::eTextureType usage, BOOL use_fbo ) +{ + allocate(resx,resy,color_fmt,depth,stencil,usage,use_fbo,2); +} + +void LLMultisampleBuffer::allocate(U32 resx, U32 resy, U32 color_fmt, BOOL depth, BOOL stencil, LLTexUnit::eTextureType usage, BOOL use_fbo, U32 samples ) +{ + stop_glerror(); + mResX = resx; + mResY = resy; + + mUsage = usage; + mUseDepth = depth; + mStencil = stencil; + + releaseSampleBuffer(); + + if (!gGLManager.mHasFramebufferMultisample) + { + llerrs << "Attempting to allocate unsupported render target type!" << llendl; + } + + mSamples = samples; + + if (mSamples <= 1) + { + llerrs << "Cannot create a multisample buffer with less than 2 samples." << llendl; + } + + stop_glerror(); + + if ((sUseFBO || use_fbo) && gGLManager.mHasFramebufferObject) + { + + if (depth) + { + stop_glerror(); + allocateDepth(); + stop_glerror(); + } + + glGenFramebuffersEXT(1, (GLuint *) &mFBO); + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFBO); + + if (mDepth) + { + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, mDepth); + if (mStencil) + { + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, mDepth); + } + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + } + + stop_glerror(); + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + stop_glerror(); + } + + addColorAttachment(color_fmt); +} + +void LLMultisampleBuffer::addColorAttachment(U32 color_fmt) +{ +#if !LL_DARWIN + if (color_fmt == 0) + { + return; + } + + U32 offset = mTex.size(); + if (offset >= 4 || + (offset > 0 && (mFBO == 0 || !gGLManager.mHasDrawBuffers))) + { + llerrs << "Too many color attachments!" << llendl; + } + + U32 tex; + glGenRenderbuffersEXT(1, &tex); + + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, tex); + glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, mSamples, color_fmt, mResX, mResY); + stop_glerror(); + + if (mFBO) + { + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFBO); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT+offset, GL_RENDERBUFFER_EXT, tex); + stop_glerror(); + GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + switch (status) + { + case GL_FRAMEBUFFER_COMPLETE_EXT: + break; + case GL_FRAMEBUFFER_UNSUPPORTED_EXT: + llerrs << "WTF?" << llendl; + break; + default: + llerrs << "WTF?" << llendl; + } + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + } + + mTex.push_back(tex); +#endif +} + +void LLMultisampleBuffer::allocateDepth() +{ +#if !LL_DARWIN + glGenRenderbuffersEXT(1, (GLuint* ) &mDepth); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, mDepth); + if (mStencil) + { + glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, mSamples, GL_DEPTH24_STENCIL8_EXT, mResX, mResY); + } + else + { + glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, mSamples, GL_DEPTH_COMPONENT16_ARB, mResX, mResY); + } +#endif +} + diff --git a/indra/llrender/llrendertarget.h b/indra/llrender/llrendertarget.h index df88640970..ae8613d9be 100644 --- a/indra/llrender/llrendertarget.h +++ b/indra/llrender/llrendertarget.h @@ -2,37 +2,36 @@ * @file llrendertarget.h * @brief Off screen render target abstraction. Loose wrapper for GL_EXT_framebuffer_objects. * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2007, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlife.com/developers/opensource/gplv2 + * Copyright (C) 2010, Linden Research, Inc. + * + * 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; + * version 2.1 of the License only. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at http://secondlife.com/developers/opensource/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #ifndef LL_LLRENDERTARGET_H #define LL_LLRENDERTARGET_H +// LLRenderTarget is unavailible on the mapserver since it uses FBOs. +#if !LL_MESA_HEADLESS + #include "llgl.h" +#include "llrender.h" /* SAMPLE USAGE: @@ -53,11 +52,12 @@ ... //use target as a texture - target.bindTexture(); + gGL.getTexUnit(INDEX)->bind(&target); ... <issue drawing commands> ... */ +class LLMultisampleBuffer; class LLRenderTarget { @@ -66,15 +66,25 @@ public: static BOOL sUseFBO; LLRenderTarget(); - ~LLRenderTarget(); + virtual ~LLRenderTarget(); //allocate resources for rendering //must be called before use //multiple calls will release previously allocated resources - void allocate(U32 resx, U32 resy, U32 color_fmt, BOOL depth, U32 usage = GL_TEXTURE_2D, BOOL use_fbo = FALSE); + void allocate(U32 resx, U32 resy, U32 color_fmt, BOOL depth, BOOL stencil, LLTexUnit::eTextureType usage = LLTexUnit::TT_TEXTURE, BOOL use_fbo = FALSE); + + //provide this render target with a multisample resource. + void setSampleBuffer(LLMultisampleBuffer* buffer); + + //add color buffer attachment + //limit of 4 color attachments per render target + virtual void addColorAttachment(U32 color_fmt); //allocate a depth texture - void allocateDepth(); + virtual void allocateDepth(); + + //share depth buffer with provided render target + virtual void shareDepthBuffer(LLRenderTarget& target); //free any allocated resources //safe to call redundantly @@ -82,14 +92,14 @@ public: //bind target for rendering //applies appropriate viewport - void bindTarget(); + virtual void bindTarget(); //unbind target for rendering static void unbindTarget(); //clear render targer, clears depth buffer if present, //uses scissor rect if in copy-to-texture mode - void clear(); + void clear(U32 mask = 0xFFFFFFFF); //get applied viewport void getViewport(S32* viewport); @@ -100,11 +110,14 @@ public: //get Y resolution U32 getHeight() const { return mResY; } - //bind results of render for sampling - void bindTexture(); + LLTexUnit::eTextureType getUsage(void) const { return mUsage; } + + U32 getTexture(U32 attachment = 0) const; - //bind results of render for sampling depth buffer - void bindDepth(); + U32 getDepth(void) const { return mDepth; } + BOOL hasStencil() const { return mStencil; } + + void bindTexture(U32 index, S32 channel); //flush rendering operations //must be called when rendering is complete @@ -114,23 +127,54 @@ public: // the current depth texture. A depth texture will be allocated if needed. void flush(BOOL fetch_depth = FALSE); + void copyContents(LLRenderTarget& source, S32 srcX0, S32 srcY0, S32 srcX1, S32 srcY1, + S32 dstX0, S32 dstY0, S32 dstX1, S32 dstY1, U32 mask, U32 filter); + + static void copyContentsToFramebuffer(LLRenderTarget& source, S32 srcX0, S32 srcY0, S32 srcX1, S32 srcY1, + S32 dstX0, S32 dstY0, S32 dstX1, S32 dstY1, U32 mask, U32 filter); + //Returns TRUE if target is ready to be rendered into. //That is, if the target has been allocated with at least //one renderable attachment (i.e. color buffer, depth buffer). BOOL isComplete() const; -private: + static LLRenderTarget* getCurrentBoundTarget() { return sBoundTarget; } + +protected: + friend class LLMultisampleBuffer; U32 mResX; U32 mResY; - U32 mTex; + std::vector<U32> mTex; U32 mFBO; U32 mDepth; - U32 mStencil; + BOOL mStencil; BOOL mUseDepth; BOOL mRenderDepth; - U32 mUsage; + LLTexUnit::eTextureType mUsage; + U32 mSamples; + LLMultisampleBuffer* mSampleBuffer; + + static LLRenderTarget* sBoundTarget; }; +class LLMultisampleBuffer : public LLRenderTarget +{ +public: + LLMultisampleBuffer(); + virtual ~LLMultisampleBuffer(); + + void releaseSampleBuffer(); + + virtual void bindTarget(); + void bindTarget(LLRenderTarget* ref); + virtual void allocate(U32 resx, U32 resy, U32 color_fmt, BOOL depth, BOOL stencil, LLTexUnit::eTextureType usage, BOOL use_fbo); + void allocate(U32 resx, U32 resy, U32 color_fmt, BOOL depth, BOOL stencil, LLTexUnit::eTextureType usage, BOOL use_fbo, U32 samples); + virtual void addColorAttachment(U32 color_fmt); + virtual void allocateDepth(); +}; + +#endif //!LL_MESA_HEADLESS + #endif diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp new file mode 100644 index 0000000000..c859d41e17 --- /dev/null +++ b/indra/llrender/llshadermgr.cpp @@ -0,0 +1,508 @@ +/** + * @file llshadermgr.cpp + * @brief Shader manager implementation. + * + * $LicenseInfo:firstyear=2005&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * 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; + * version 2.1 of the License only. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llshadermgr.h" + +#include "llfile.h" +#include "llrender.h" + +#if LL_DARWIN +#include "OpenGL/OpenGL.h" +#endif + +#ifdef LL_RELEASE_FOR_DOWNLOAD +#define UNIFORM_ERRS LL_WARNS_ONCE("Shader") +#else +#define UNIFORM_ERRS LL_ERRS("Shader") +#endif + +// Lots of STL stuff in here, using namespace std to keep things more readable +using std::vector; +using std::pair; +using std::make_pair; +using std::string; + +LLShaderMgr * LLShaderMgr::sInstance = NULL; + +LLShaderMgr::LLShaderMgr() +{ +} + + +LLShaderMgr::~LLShaderMgr() +{ +} + +// static +LLShaderMgr * LLShaderMgr::instance() +{ + if(NULL == sInstance) + { + LL_ERRS("Shaders") << "LLShaderMgr should already have been instantiated by the application!" << LL_ENDL; + } + + return sInstance; +} + +BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader) +{ + llassert_always(shader != NULL); + LLShaderFeatures *features = & shader->mFeatures; + + ////////////////////////////////////// + // Attach Vertex Shader Features First + ////////////////////////////////////// + + // NOTE order of shader object attaching is VERY IMPORTANT!!! + if (features->calculatesAtmospherics) + { + if (!shader->attachObject("windlight/atmosphericsVarsV.glsl")) + { + return FALSE; + } + } + + if (features->calculatesLighting) + { + if (!shader->attachObject("windlight/atmosphericsHelpersV.glsl")) + { + return FALSE; + } + + if (features->isSpecular) + { + if (!shader->attachObject("lighting/lightFuncSpecularV.glsl")) + { + return FALSE; + } + + if (!shader->attachObject("lighting/sumLightsSpecularV.glsl")) + { + return FALSE; + } + + if (!shader->attachObject("lighting/lightSpecularV.glsl")) + { + return FALSE; + } + } + else + { + if (!shader->attachObject("lighting/lightFuncV.glsl")) + { + return FALSE; + } + + if (!shader->attachObject("lighting/sumLightsV.glsl")) + { + return FALSE; + } + + if (!shader->attachObject("lighting/lightV.glsl")) + { + return FALSE; + } + } + } + + // NOTE order of shader object attaching is VERY IMPORTANT!!! + if (features->calculatesAtmospherics) + { + if (!shader->attachObject("windlight/atmosphericsV.glsl")) + { + return FALSE; + } + } + + if (features->hasSkinning) + { + if (!shader->attachObject("avatar/avatarSkinV.glsl")) + { + return FALSE; + } + } + + /////////////////////////////////////// + // Attach Fragment Shader Features Next + /////////////////////////////////////// + + if(features->calculatesAtmospherics) + { + if (!shader->attachObject("windlight/atmosphericsVarsF.glsl")) + { + return FALSE; + } + } + + // NOTE order of shader object attaching is VERY IMPORTANT!!! + if (features->hasGamma) + { + if (!shader->attachObject("windlight/gammaF.glsl")) + { + return FALSE; + } + } + + if (features->hasAtmospherics) + { + if (!shader->attachObject("windlight/atmosphericsF.glsl")) + { + return FALSE; + } + } + + if (features->hasTransport) + { + if (!shader->attachObject("windlight/transportF.glsl")) + { + return FALSE; + } + + // Test hasFullbright and hasShiny and attach fullbright and + // fullbright shiny atmos transport if we split them out. + } + + // NOTE order of shader object attaching is VERY IMPORTANT!!! + if (features->hasWaterFog) + { + if (!shader->attachObject("environment/waterFogF.glsl")) + { + return FALSE; + } + } + + if (features->hasLighting) + { + + if (features->hasWaterFog) + { + if (!shader->attachObject("lighting/lightWaterF.glsl")) + { + return FALSE; + } + } + + else + { + if (!shader->attachObject("lighting/lightF.glsl")) + { + return FALSE; + } + } + } + + // NOTE order of shader object attaching is VERY IMPORTANT!!! + else if (features->isFullbright) + { + + if (features->hasWaterFog) + { + if (!shader->attachObject("lighting/lightFullbrightWaterF.glsl")) + { + return FALSE; + } + } + + else if (features->isShiny) + { + if (!shader->attachObject("lighting/lightFullbrightShinyF.glsl")) + { + return FALSE; + } + } + + else + { + if (!shader->attachObject("lighting/lightFullbrightF.glsl")) + { + return FALSE; + } + } + } + + // NOTE order of shader object attaching is VERY IMPORTANT!!! + else if (features->isShiny) + { + + if (features->hasWaterFog) + { + if (!shader->attachObject("lighting/lightShinyWaterF.glsl")) + { + return FALSE; + } + } + + else + { + if (!shader->attachObject("lighting/lightShinyF.glsl")) + { + return FALSE; + } + } + } + return TRUE; +} + +//============================================================================ +// Load Shader + +static std::string get_object_log(GLhandleARB ret) +{ + std::string res; + + //get log length + GLint length; + glGetObjectParameterivARB(ret, GL_OBJECT_INFO_LOG_LENGTH_ARB, &length); + if (length > 0) + { + //the log could be any size, so allocate appropriately + GLcharARB* log = new GLcharARB[length]; + glGetInfoLogARB(ret, length, &length, log); + res = std::string((char *)log); + delete[] log; + } + return res; +} + +void LLShaderMgr::dumpObjectLog(GLhandleARB ret, BOOL warns) +{ + std::string log = get_object_log(ret); + if ( log.length() > 0 ) + { + if (warns) + { + LL_WARNS("ShaderLoading") << log << LL_ENDL; + } + else + { + LL_DEBUGS("ShaderLoading") << log << LL_ENDL; + } + } +} + +GLhandleARB LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shader_level, GLenum type) +{ + GLenum error; + error = glGetError(); + if (error != GL_NO_ERROR) + { + LL_WARNS("ShaderLoading") << "GL ERROR entering loadShaderFile(): " << error << LL_ENDL; + } + + LL_DEBUGS("ShaderLoading") << "Loading shader file: " << filename << " class " << shader_level << LL_ENDL; + + if (filename.empty()) + { + return 0; + } + + + //read in from file + LLFILE* file = NULL; + + S32 try_gpu_class = shader_level; + S32 gpu_class; + + //find the most relevant file + for (gpu_class = try_gpu_class; gpu_class > 0; gpu_class--) + { //search from the current gpu class down to class 1 to find the most relevant shader + std::stringstream fname; + fname << getShaderDirPrefix(); + fname << gpu_class << "/" << filename; + + LL_DEBUGS("ShaderLoading") << "Looking in " << fname.str() << LL_ENDL; + file = LLFile::fopen(fname.str(), "r"); /* Flawfinder: ignore */ + if (file) + { + LL_INFOS("ShaderLoading") << "Loading file: shaders/class" << gpu_class << "/" << filename << " (Want class " << gpu_class << ")" << LL_ENDL; + break; // done + } + } + + if (file == NULL) + { + LL_WARNS("ShaderLoading") << "GLSL Shader file not found: " << filename << LL_ENDL; + return 0; + } + + //we can't have any lines longer than 1024 characters + //or any shaders longer than 1024 lines... deal - DaveP + GLcharARB buff[1024]; + GLcharARB* text[1024]; + GLuint count = 0; + + + //copy file into memory + while( fgets((char *)buff, 1024, file) != NULL && count < LL_ARRAY_SIZE(buff) ) + { + text[count++] = (GLcharARB *)strdup((char *)buff); + } + fclose(file); + + //create shader object + GLhandleARB ret = glCreateShaderObjectARB(type); + error = glGetError(); + if (error != GL_NO_ERROR) + { + LL_WARNS("ShaderLoading") << "GL ERROR in glCreateShaderObjectARB: " << error << LL_ENDL; + } + else + { + //load source + glShaderSourceARB(ret, count, (const GLcharARB**) text, NULL); + error = glGetError(); + if (error != GL_NO_ERROR) + { + LL_WARNS("ShaderLoading") << "GL ERROR in glShaderSourceARB: " << error << LL_ENDL; + } + else + { + //compile source + glCompileShaderARB(ret); + error = glGetError(); + if (error != GL_NO_ERROR) + { + LL_WARNS("ShaderLoading") << "GL ERROR in glCompileShaderARB: " << error << LL_ENDL; + } + } + } + //free memory + for (GLuint i = 0; i < count; i++) + { + free(text[i]); + } + if (error == GL_NO_ERROR) + { + //check for errors + GLint success = GL_TRUE; + glGetObjectParameterivARB(ret, GL_OBJECT_COMPILE_STATUS_ARB, &success); + error = glGetError(); + if (error != GL_NO_ERROR || success == GL_FALSE) + { + //an error occured, print log + LL_WARNS("ShaderLoading") << "GLSL Compilation Error: (" << error << ") in " << filename << LL_ENDL; + dumpObjectLog(ret); + ret = 0; + } + } + else + { + ret = 0; + } + stop_glerror(); + + //successfully loaded, save results + if (ret) + { + // Add shader file to map + mShaderObjects[filename] = ret; + shader_level = try_gpu_class; + } + else + { + if (shader_level > 1) + { + shader_level--; + return loadShaderFile(filename,shader_level,type); + } + LL_WARNS("ShaderLoading") << "Failed to load " << filename << LL_ENDL; + } + return ret; +} + +BOOL LLShaderMgr::linkProgramObject(GLhandleARB obj, BOOL suppress_errors) +{ + //check for errors + glLinkProgramARB(obj); + GLint success = GL_TRUE; + glGetObjectParameterivARB(obj, GL_OBJECT_LINK_STATUS_ARB, &success); + if (!suppress_errors && success == GL_FALSE) + { + //an error occured, print log + LL_WARNS("ShaderLoading") << "GLSL Linker Error:" << LL_ENDL; + } + +// NOTE: Removing LL_DARWIN block as it doesn't seem to actually give the correct answer, +// but want it for reference once I move it. +#if 0 + // Force an evaluation of the gl state so the driver can tell if the shader will run in hardware or software + // per Apple's suggestion + glBegin(gGL.mMode); + glEnd(); + + // Query whether the shader can or cannot run in hardware + // http://developer.apple.com/qa/qa2007/qa1502.html + long vertexGPUProcessing; + CGLContextObj ctx = CGLGetCurrentContext(); + CGLGetParameter (ctx, kCGLCPGPUVertexProcessing, &vertexGPUProcessing); + long fragmentGPUProcessing; + CGLGetParameter (ctx, kCGLCPGPUFragmentProcessing, &fragmentGPUProcessing); + if (!fragmentGPUProcessing || !vertexGPUProcessing) + { + LL_WARNS("ShaderLoading") << "GLSL Linker: Running in Software:" << LL_ENDL; + success = GL_FALSE; + suppress_errors = FALSE; + } + +#else + std::string log = get_object_log(obj); + LLStringUtil::toLower(log); + if (log.find("software") != std::string::npos) + { + LL_WARNS("ShaderLoading") << "GLSL Linker: Running in Software:" << LL_ENDL; + success = GL_FALSE; + suppress_errors = FALSE; + } +#endif + if (!suppress_errors) + { + dumpObjectLog(obj, !success); + } + + return success; +} + +BOOL LLShaderMgr::validateProgramObject(GLhandleARB obj) +{ + //check program validity against current GL + glValidateProgramARB(obj); + GLint success = GL_TRUE; + glGetObjectParameterivARB(obj, GL_OBJECT_VALIDATE_STATUS_ARB, &success); + if (success == GL_FALSE) + { + LL_WARNS("ShaderLoading") << "GLSL program not valid: " << LL_ENDL; + dumpObjectLog(obj); + } + else + { + dumpObjectLog(obj, FALSE); + } + + return success; +} + diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h new file mode 100644 index 0000000000..c54c4608d7 --- /dev/null +++ b/indra/llrender/llshadermgr.h @@ -0,0 +1,70 @@ +/** + * @file llshadermgr.h + * @brief Shader Manager + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * 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; + * version 2.1 of the License only. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_SHADERMGR_H +#define LL_SHADERMGR_H + +#include "llgl.h" +#include "llglslshader.h" + +class LLShaderMgr +{ +public: + LLShaderMgr(); + virtual ~LLShaderMgr(); + + // singleton pattern implementation + static LLShaderMgr * instance(); + + BOOL attachShaderFeatures(LLGLSLShader * shader); + void dumpObjectLog(GLhandleARB ret, BOOL warns = TRUE); + BOOL linkProgramObject(GLhandleARB obj, BOOL suppress_errors = FALSE); + BOOL validateProgramObject(GLhandleARB obj); + GLhandleARB loadShaderFile(const std::string& filename, S32 & shader_level, GLenum type); + + // Implemented in the application to actually point to the shader directory. + virtual std::string getShaderDirPrefix(void) = 0; // Pure Virtual + + // Implemented in the application to actually update out of date uniforms for a particular shader + virtual void updateShaderUniforms(LLGLSLShader * shader) = 0; // Pure Virtual + +public: + // Map of shader names to compiled + std::map<std::string, GLhandleARB> mShaderObjects; + + //global (reserved slot) shader parameters + std::vector<std::string> mReservedAttribs; + + std::vector<std::string> mReservedUniforms; + +protected: + + // our parameter manager singleton instance + static LLShaderMgr * sInstance; + +}; //LLShaderMgr + +#endif diff --git a/indra/llrender/lltexture.cpp b/indra/llrender/lltexture.cpp new file mode 100644 index 0000000000..90fbcec2be --- /dev/null +++ b/indra/llrender/lltexture.cpp @@ -0,0 +1,31 @@ +/** + * @file lltexture.cpp + * + * $LicenseInfo:firstyear=2000&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * 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; + * version 2.1 of the License only. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#include "linden_common.h" +#include "lltexture.h" + +//virtual +LLTexture::~LLTexture() +{ +} diff --git a/indra/llrender/lltexture.h b/indra/llrender/lltexture.h new file mode 100644 index 0000000000..569a65c2e0 --- /dev/null +++ b/indra/llrender/lltexture.h @@ -0,0 +1,73 @@ +/** + * @file lltexture.h + * @brief LLTexture definition + * + * This class acts as a wrapper for OpenGL calls. + * The goal of this class is to minimize the number of api calls due to legacy rendering + * code, to define an interface for a multiple rendering API abstraction of the UI + * rendering, and to abstract out direct rendering calls in a way that is cleaner and easier to maintain. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * 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; + * version 2.1 of the License only. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_TEXTURE_H +#define LL_TEXTURE_H + +#include "llrefcount.h" +class LLImageGL ; +class LLTexUnit ; +class LLFontGL ; + +// +//this is an abstract class as the parent for the class LLViewerTexture +//through the following virtual functions, the class LLViewerTexture can be reached from /llrender. +// +class LLTexture : public LLRefCount +{ + friend class LLTexUnit ; + friend class LLFontGL ; + +protected: + virtual ~LLTexture(); + +public: + LLTexture(){} + + // + //interfaces to access LLViewerTexture + // + virtual S8 getType() const = 0 ; + virtual void setKnownDrawSize(S32 width, S32 height) = 0 ; + virtual bool bindDefaultImage(const S32 stage = 0) = 0 ; + virtual void forceImmediateUpdate() = 0 ; + virtual void setActive() = 0 ; + virtual S32 getWidth(S32 discard_level = -1) const = 0 ; + virtual S32 getHeight(S32 discard_level = -1) const = 0 ; + +private: + //note: do not make this function public. + virtual LLImageGL* getGLTexture() const = 0 ; + + virtual void updateBindStatsForTester() = 0 ; +}; +#endif + diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp index 2f053a6493..de4501dd0f 100644 --- a/indra/llrender/llvertexbuffer.cpp +++ b/indra/llrender/llvertexbuffer.cpp @@ -2,30 +2,25 @@ * @file llvertexbuffer.cpp * @brief LLVertexBuffer implementation * - * $LicenseInfo:firstyear=2003&license=viewergpl$ - * - * Copyright (c) 2003-2007, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2003&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlife.com/developers/opensource/gplv2 + * Copyright (C) 2010, Linden Research, Inc. + * + * 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; + * version 2.1 of the License only. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at http://secondlife.com/developers/opensource/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -36,9 +31,8 @@ #include "llvertexbuffer.h" // #include "llrender.h" #include "llglheaders.h" -#include "llmemory.h" #include "llmemtype.h" -#include "llglimmediate.h" +#include "llrender.h" //============================================================================ @@ -61,6 +55,7 @@ BOOL LLVertexBuffer::sVBOActive = FALSE; BOOL LLVertexBuffer::sIBOActive = FALSE; U32 LLVertexBuffer::sAllocatedBytes = 0; BOOL LLVertexBuffer::sMapped = FALSE; +BOOL LLVertexBuffer::sUseStreamDraw = TRUE; std::vector<U32> LLVertexBuffer::sDeleteList; @@ -68,15 +63,17 @@ S32 LLVertexBuffer::sTypeOffsets[LLVertexBuffer::TYPE_MAX] = { sizeof(LLVector3), // TYPE_VERTEX, sizeof(LLVector3), // TYPE_NORMAL, - sizeof(LLVector2), // TYPE_TEXCOORD, + sizeof(LLVector2), // TYPE_TEXCOORD0, + sizeof(LLVector2), // TYPE_TEXCOORD1, sizeof(LLVector2), // TYPE_TEXCOORD2, + sizeof(LLVector2), // TYPE_TEXCOORD3, sizeof(LLColor4U), // TYPE_COLOR, sizeof(LLVector3), // TYPE_BINORMAL, sizeof(F32), // TYPE_WEIGHT, sizeof(LLVector4), // TYPE_CLOTHWEIGHT, }; -U32 LLVertexBuffer::sGLMode[LLVertexBuffer::NUM_MODES] = +U32 LLVertexBuffer::sGLMode[LLRender::NUM_MODES] = { GL_TRIANGLES, GL_TRIANGLE_STRIP, @@ -102,8 +99,8 @@ void LLVertexBuffer::setupClientArrays(U32 data_mask) { MAP_VERTEX, MAP_NORMAL, - MAP_TEXCOORD, - MAP_COLOR + MAP_TEXCOORD0, + MAP_COLOR, }; GLenum array[] = @@ -111,9 +108,10 @@ void LLVertexBuffer::setupClientArrays(U32 data_mask) GL_VERTEX_ARRAY, GL_NORMAL_ARRAY, GL_TEXTURE_COORD_ARRAY, - GL_COLOR_ARRAY + GL_COLOR_ARRAY, }; + BOOL error = FALSE; for (U32 i = 0; i < 4; ++i) { if (sLastMask & mask[i]) @@ -122,11 +120,19 @@ void LLVertexBuffer::setupClientArrays(U32 data_mask) { //needs to be disabled glDisableClientState(array[i]); } - else + else if (gDebugGL) { //needs to be enabled, make sure it was (DEBUG TEMPORARY) if (i > 0 && !glIsEnabled(array[i])) { - llerrs << "Bad client state! " << array[i] << " disabled." << llendl; + if (gDebugSession) + { + error = TRUE; + gFailLog << "Bad client state! " << array[i] << " disabled." << std::endl; + } + else + { + llerrs << "Bad client state! " << array[i] << " disabled." << llendl; + } } } } @@ -136,43 +142,86 @@ void LLVertexBuffer::setupClientArrays(U32 data_mask) { //needs to be enabled glEnableClientState(array[i]); } - else if (glIsEnabled(array[i])) + else if (gDebugGL && glIsEnabled(array[i])) { //needs to be disabled, make sure it was (DEBUG TEMPORARY) - llerrs << "Bad client state! " << array[i] << " enabled." << llendl; + if (gDebugSession) + { + error = TRUE; + gFailLog << "Bad client state! " << array[i] << " enabled." << std::endl; + } + else + { + llerrs << "Bad client state! " << array[i] << " enabled." << llendl; + } } } } - if (sLastMask & MAP_TEXCOORD2) + if (error) { - if (!(data_mask & MAP_TEXCOORD2)) + ll_fail("LLVertexBuffer::setupClientArrays failed"); + } + + U32 map_tc[] = + { + MAP_TEXCOORD1, + MAP_TEXCOORD2, + MAP_TEXCOORD3 + }; + + for (U32 i = 0; i < 3; i++) + { + if (sLastMask & map_tc[i]) + { + if (!(data_mask & map_tc[i])) + { + glClientActiveTextureARB(GL_TEXTURE1_ARB+i); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glClientActiveTextureARB(GL_TEXTURE0_ARB); + } + } + else if (data_mask & map_tc[i]) + { + glClientActiveTextureARB(GL_TEXTURE1_ARB+i); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glClientActiveTextureARB(GL_TEXTURE0_ARB); + } + } + + if (sLastMask & MAP_BINORMAL) + { + if (!(data_mask & MAP_BINORMAL)) { - glClientActiveTextureARB(GL_TEXTURE1_ARB); + glClientActiveTextureARB(GL_TEXTURE2_ARB); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glClientActiveTextureARB(GL_TEXTURE0_ARB); } } - else if (data_mask & MAP_TEXCOORD2) + else if (data_mask & MAP_BINORMAL) { - glClientActiveTextureARB(GL_TEXTURE1_ARB); + glClientActiveTextureARB(GL_TEXTURE2_ARB); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glClientActiveTextureARB(GL_TEXTURE0_ARB); } - + sLastMask = data_mask; } } void LLVertexBuffer::drawRange(U32 mode, U32 start, U32 end, U32 count, U32 indices_offset) const { + llassert(mRequestedNumVerts >= 0); + if (start >= (U32) mRequestedNumVerts || - end >= (U32) mRequestedNumVerts) + end >= (U32) mRequestedNumVerts) { llerrs << "Bad vertex buffer draw range: [" << start << ", " << end << "]" << llendl; } + llassert(mRequestedNumIndices >= 0); + if (indices_offset >= (U32) mRequestedNumIndices || - indices_offset + count > (U32) mRequestedNumIndices) + indices_offset + count > (U32) mRequestedNumIndices) { llerrs << "Bad index buffer draw range: [" << indices_offset << ", " << indices_offset+count << "]" << llendl; } @@ -187,19 +236,23 @@ void LLVertexBuffer::drawRange(U32 mode, U32 start, U32 end, U32 count, U32 indi llerrs << "Wrong vertex buffer bound." << llendl; } - if (mode > NUM_MODES) + if (mode >= LLRender::NUM_MODES) { llerrs << "Invalid draw mode: " << mode << llendl; + return; } + stop_glerror(); glDrawRangeElements(sGLMode[mode], start, end, count, GL_UNSIGNED_SHORT, ((U16*) getIndicesPointer()) + indices_offset); + stop_glerror(); } void LLVertexBuffer::draw(U32 mode, U32 count, U32 indices_offset) const { + llassert(mRequestedNumIndices >= 0); if (indices_offset >= (U32) mRequestedNumIndices || - indices_offset + count > (U32) mRequestedNumIndices) + indices_offset + count > (U32) mRequestedNumIndices) { llerrs << "Bad index buffer draw range: [" << indices_offset << ", " << indices_offset+count << "]" << llendl; } @@ -214,20 +267,23 @@ void LLVertexBuffer::draw(U32 mode, U32 count, U32 indices_offset) const llerrs << "Wrong vertex buffer bound." << llendl; } - if (mode > NUM_MODES) + if (mode >= LLRender::NUM_MODES) { llerrs << "Invalid draw mode: " << mode << llendl; + return; } + stop_glerror(); glDrawElements(sGLMode[mode], count, GL_UNSIGNED_SHORT, ((U16*) getIndicesPointer()) + indices_offset); + stop_glerror(); } void LLVertexBuffer::drawArrays(U32 mode, U32 first, U32 count) const { - + llassert(mRequestedNumVerts >= 0); if (first >= (U32) mRequestedNumVerts || - first + count > (U32) mRequestedNumVerts) + first + count > (U32) mRequestedNumVerts) { llerrs << "Bad vertex buffer draw range: [" << first << ", " << first+count << "]" << llendl; } @@ -237,12 +293,15 @@ void LLVertexBuffer::drawArrays(U32 mode, U32 first, U32 count) const llerrs << "Wrong vertex buffer bound." << llendl; } - if (mode > NUM_MODES) + if (mode >= LLRender::NUM_MODES) { llerrs << "Invalid draw mode: " << mode << llendl; + return; } + stop_glerror(); glDrawArrays(sGLMode[mode], first, count); + stop_glerror(); } //static @@ -278,7 +337,7 @@ void LLVertexBuffer::unbind() //static void LLVertexBuffer::cleanupClass() { - LLMemType mt(LLMemType::MTYPE_VERTEX_DATA); + LLMemType mt2(LLMemType::MTYPE_VERTEX_CLEANUP_CLASS); unbind(); clientCopy(); // deletes GL buffers } @@ -296,7 +355,14 @@ void LLVertexBuffer::clientCopy(F64 max_time) LLVertexBuffer::LLVertexBuffer(U32 typemask, S32 usage) : LLRefCount(), - mNumVerts(0), mNumIndices(0), mUsage(usage), mGLBuffer(0), mGLIndices(0), + + mNumVerts(0), + mNumIndices(0), + mRequestedNumVerts(-1), + mRequestedNumIndices(-1), + mUsage(usage), + mGLBuffer(0), + mGLIndices(0), mMappedData(NULL), mMappedIndexData(NULL), mLocked(FALSE), mFinal(FALSE), @@ -305,12 +371,22 @@ LLVertexBuffer::LLVertexBuffer(U32 typemask, S32 usage) : mResized(FALSE), mDynamicSize(FALSE) { - LLMemType mt(LLMemType::MTYPE_VERTEX_DATA); + LLMemType mt2(LLMemType::MTYPE_VERTEX_CONSTRUCTOR); if (!sEnableVBOs) { mUsage = 0 ; } + + if (mUsage == GL_STREAM_DRAW_ARB && !sUseStreamDraw) + { + mUsage = 0; + } + if (mUsage == GL_STREAM_DRAW_ARB && !sUseStreamDraw) + { + mUsage = 0; + } + S32 stride = calcStride(typemask, mOffsets); mTypeMask = typemask; @@ -342,7 +418,7 @@ S32 LLVertexBuffer::calcStride(const U32& typemask, S32* offsets) //virtual LLVertexBuffer::~LLVertexBuffer() { - LLMemType mt(LLMemType::MTYPE_VERTEX_DATA); + LLMemType mt2(LLMemType::MTYPE_VERTEX_DESTRUCTOR); destroyGLBuffer(); destroyGLIndices(); sCount--; @@ -422,8 +498,8 @@ void LLVertexBuffer::releaseIndices() void LLVertexBuffer::createGLBuffer() { - LLMemType mt(LLMemType::MTYPE_VERTEX_DATA); - + LLMemType mt2(LLMemType::MTYPE_VERTEX_CREATE_VERTICES); + U32 size = getSize(); if (mGLBuffer) { @@ -454,7 +530,7 @@ void LLVertexBuffer::createGLBuffer() void LLVertexBuffer::createGLIndices() { - LLMemType mt(LLMemType::MTYPE_VERTEX_DATA); + LLMemType mt2(LLMemType::MTYPE_VERTEX_CREATE_INDICES); U32 size = getIndicesSize(); if (mGLIndices) @@ -486,7 +562,7 @@ void LLVertexBuffer::createGLIndices() void LLVertexBuffer::destroyGLBuffer() { - LLMemType mt(LLMemType::MTYPE_VERTEX_DATA); + LLMemType mt2(LLMemType::MTYPE_VERTEX_DESTROY_BUFFER); if (mGLBuffer) { if (useVBOs()) @@ -513,7 +589,7 @@ void LLVertexBuffer::destroyGLBuffer() void LLVertexBuffer::destroyGLIndices() { - LLMemType mt(LLMemType::MTYPE_VERTEX_DATA); + LLMemType mt2(LLMemType::MTYPE_VERTEX_DESTROY_INDICES); if (mGLIndices) { if (useVBOs()) @@ -540,7 +616,9 @@ void LLVertexBuffer::destroyGLIndices() void LLVertexBuffer::updateNumVerts(S32 nverts) { - LLMemType mt(LLMemType::MTYPE_VERTEX_DATA); + LLMemType mt2(LLMemType::MTYPE_VERTEX_UPDATE_VERTS); + + llassert(nverts >= 0); if (nverts >= 65535) { @@ -569,7 +647,10 @@ void LLVertexBuffer::updateNumVerts(S32 nverts) void LLVertexBuffer::updateNumIndices(S32 nindices) { - LLMemType mt(LLMemType::MTYPE_VERTEX_DATA); + LLMemType mt2(LLMemType::MTYPE_VERTEX_UPDATE_INDICES); + + llassert(nindices >= 0); + mRequestedNumIndices = nindices; if (!mDynamicSize) { @@ -590,7 +671,7 @@ void LLVertexBuffer::updateNumIndices(S32 nindices) void LLVertexBuffer::allocateBuffer(S32 nverts, S32 nindices, bool create) { - LLMemType mt(LLMemType::MTYPE_VERTEX_DATA); + LLMemType mt2(LLMemType::MTYPE_VERTEX_ALLOCATE_BUFFER); updateNumVerts(nverts); updateNumIndices(nindices); @@ -610,10 +691,13 @@ void LLVertexBuffer::allocateBuffer(S32 nverts, S32 nindices, bool create) void LLVertexBuffer::resizeBuffer(S32 newnverts, S32 newnindices) { + llassert(newnverts >= 0); + llassert(newnindices >= 0); + mRequestedNumVerts = newnverts; mRequestedNumIndices = newnindices; - LLMemType mt(LLMemType::MTYPE_VERTEX_DATA); + LLMemType mt2(LLMemType::MTYPE_VERTEX_RESIZE_BUFFER); mDynamicSize = TRUE; if (mUsage == GL_STATIC_DRAW_ARB) { //always delete/allocate static buffers on resize @@ -734,7 +818,7 @@ BOOL LLVertexBuffer::useVBOs() const return FALSE; } #endif - return sEnableVBOs; + return TRUE; } //---------------------------------------------------------------------------- @@ -742,7 +826,7 @@ BOOL LLVertexBuffer::useVBOs() const // Map for data access U8* LLVertexBuffer::mapBuffer(S32 access) { - LLMemType mt(LLMemType::MTYPE_VERTEX_DATA); + LLMemType mt2(LLMemType::MTYPE_VERTEX_MAP_BUFFER); if (mFinal) { llerrs << "LLVertexBuffer::mapBuffer() called on a finalized buffer." << llendl; @@ -754,25 +838,50 @@ U8* LLVertexBuffer::mapBuffer(S32 access) if (!mLocked && useVBOs()) { - setBuffer(0); - mLocked = TRUE; - stop_glerror(); - mMappedData = (U8*) glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB); - stop_glerror(); - mMappedIndexData = (U8*) glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB); - stop_glerror(); - /*if (sMapped) { - llerrs << "Mapped two VBOs at the same time!" << llendl; + LLMemType mt_v(LLMemType::MTYPE_VERTEX_MAP_BUFFER_VERTICES); + setBuffer(0); + mLocked = TRUE; + stop_glerror(); + mMappedData = (U8*) glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB); + stop_glerror(); + } + { + LLMemType mt_v(LLMemType::MTYPE_VERTEX_MAP_BUFFER_INDICES); + mMappedIndexData = (U8*) glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB); + stop_glerror(); } - sMapped = TRUE;*/ + if (!mMappedData) { + //-------------------- + //print out more debug info before crash + llinfos << "vertex buffer size: (num verts : num indices) = " << getNumVerts() << " : " << getNumIndices() << llendl ; + GLint size ; + glGetBufferParameterivARB(GL_ARRAY_BUFFER_ARB, GL_BUFFER_SIZE_ARB, &size) ; + llinfos << "GL_ARRAY_BUFFER_ARB size is " << size << llendl ; + //-------------------- + + GLint buff; + glGetIntegerv(GL_ARRAY_BUFFER_BINDING_ARB, &buff); + if ((GLuint)buff != mGLBuffer) + { + llerrs << "Invalid GL vertex buffer bound: " << buff << llendl; + } + + llerrs << "glMapBuffer returned NULL (no vertex data)" << llendl; } if (!mMappedIndexData) { + GLint buff; + glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB, &buff); + if ((GLuint)buff != mGLIndices) + { + llerrs << "Invalid GL index buffer bound: " << buff << llendl; + } + llerrs << "glMapBuffer returned NULL (no index data)" << llendl; } @@ -784,7 +893,7 @@ U8* LLVertexBuffer::mapBuffer(S32 access) void LLVertexBuffer::unmapBuffer() { - LLMemType mt(LLMemType::MTYPE_VERTEX_DATA); + LLMemType mt2(LLMemType::MTYPE_VERTEX_UNMAP_BUFFER); if (mMappedData || mMappedIndexData) { if (useVBOs() && mLocked) @@ -867,14 +976,22 @@ bool LLVertexBuffer::getIndexStrider(LLStrider<U16>& strider, S32 index) { return VertexBufferStrider<U16,TYPE_INDEX>::get(*this, strider, index); } -bool LLVertexBuffer::getTexCoordStrider(LLStrider<LLVector2>& strider, S32 index) +bool LLVertexBuffer::getTexCoord0Strider(LLStrider<LLVector2>& strider, S32 index) +{ + return VertexBufferStrider<LLVector2,TYPE_TEXCOORD0>::get(*this, strider, index); +} +bool LLVertexBuffer::getTexCoord1Strider(LLStrider<LLVector2>& strider, S32 index) { - return VertexBufferStrider<LLVector2,TYPE_TEXCOORD>::get(*this, strider, index); + return VertexBufferStrider<LLVector2,TYPE_TEXCOORD1>::get(*this, strider, index); } -bool LLVertexBuffer::getTexCoord2Strider(LLStrider<LLVector2>& strider, S32 index) +/*bool LLVertexBuffer::getTexCoord2Strider(LLStrider<LLVector2>& strider, S32 index) { return VertexBufferStrider<LLVector2,TYPE_TEXCOORD2>::get(*this, strider, index); } +bool LLVertexBuffer::getTexCoord3Strider(LLStrider<LLVector2>& strider, S32 index) +{ + return VertexBufferStrider<LLVector2,TYPE_TEXCOORD3>::get(*this, strider, index); +}*/ bool LLVertexBuffer::getNormalStrider(LLStrider<LLVector3>& strider, S32 index) { return VertexBufferStrider<LLVector3,TYPE_NORMAL>::get(*this, strider, index); @@ -898,7 +1015,7 @@ bool LLVertexBuffer::getClothWeightStrider(LLStrider<LLVector4>& strider, S32 in void LLVertexBuffer::setStride(S32 type, S32 new_stride) { - LLMemType mt(LLMemType::MTYPE_VERTEX_DATA); + LLMemType mt2(LLMemType::MTYPE_VERTEX_SET_STRIDE); if (mNumVerts) { llerrs << "LLVertexBuffer::setOffset called with mNumVerts = " << mNumVerts << llendl; @@ -920,7 +1037,7 @@ void LLVertexBuffer::setStride(S32 type, S32 new_stride) // Set for rendering void LLVertexBuffer::setBuffer(U32 data_mask) { - LLMemType mt(LLMemType::MTYPE_VERTEX_DATA); + LLMemType mt2(LLMemType::MTYPE_VERTEX_SET_BUFFER); //set up pointers if the data mask is different ... BOOL setup = (sLastMask != data_mask); @@ -952,21 +1069,76 @@ void LLVertexBuffer::setBuffer(U32 data_mask) sIBOActive = TRUE; } + BOOL error = FALSE; + if (gDebugGL) + { + GLint buff; + glGetIntegerv(GL_ARRAY_BUFFER_BINDING_ARB, &buff); + if ((GLuint)buff != mGLBuffer) + { + if (gDebugSession) + { + error = TRUE; + gFailLog << "Invalid GL vertex buffer bound: " << buff << std::endl; + } + else + { + llerrs << "Invalid GL vertex buffer bound: " << buff << llendl; + } + } + + if (mGLIndices) + { + glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB, &buff); + if ((GLuint)buff != mGLIndices) + { + if (gDebugSession) + { + error = TRUE; + gFailLog << "Invalid GL index buffer bound: " << buff << std::endl; + } + else + { + llerrs << "Invalid GL index buffer bound: " << buff << llendl; + } + } + } + } + if (mResized) { if (gDebugGL) { GLint buff; glGetIntegerv(GL_ARRAY_BUFFER_BINDING_ARB, &buff); - if (buff != mGLBuffer) + if ((GLuint)buff != mGLBuffer) { - llerrs << "Invalid GL vertex buffer bound: " << buff << llendl; + if (gDebugSession) + { + error = TRUE; + gFailLog << "Invalid GL vertex buffer bound: " << std::endl; + } + else + { + llerrs << "Invalid GL vertex buffer bound: " << buff << llendl; + } } - glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB, &buff); - if (buff != mGLIndices) + if (mGLIndices != 0) { - llerrs << "Invalid GL index buffer bound: " << buff << llendl; + glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB, &buff); + if ((GLuint)buff != mGLIndices) + { + if (gDebugSession) + { + error = TRUE; + gFailLog << "Invalid GL index buffer bound: "<< std::endl; + } + else + { + llerrs << "Invalid GL index buffer bound: " << buff << llendl; + } + } } } @@ -988,17 +1160,29 @@ void LLVertexBuffer::setBuffer(U32 data_mask) if (data_mask != 0) { - llerrs << "Buffer set for rendering before being filled after resize." << llendl; + if (gDebugSession) + { + error = TRUE; + gFailLog << "Buffer set for rendering before being filled after resize." << std::endl; + } + else + { + llerrs << "Buffer set for rendering before being filled after resize." << llendl; + } } } + if (error) + { + ll_fail("LLVertexBuffer::mapBuffer failed"); + } unmapBuffer(); } else { if (mGLBuffer) { - if (sEnableVBOs && sVBOActive) + if (sVBOActive) { glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); sBindCount++; @@ -1010,7 +1194,7 @@ void LLVertexBuffer::setBuffer(U32 data_mask) setup = TRUE; // ... or a client memory pointer changed } } - if (sEnableVBOs && mGLIndices && sIBOActive) + if (mGLIndices && sIBOActive) { /*if (sMapped) { @@ -1042,7 +1226,7 @@ void LLVertexBuffer::setBuffer(U32 data_mask) // virtual (default) void LLVertexBuffer::setupVertexBuffer(U32 data_mask) const { - LLMemType mt(LLMemType::MTYPE_VERTEX_DATA); + LLMemType mt2(LLMemType::MTYPE_VERTEX_SETUP_VERTEX_BUFFER); stop_glerror(); U8* base = useVBOs() ? NULL : mMappedData; S32 stride = mStride; @@ -1056,24 +1240,39 @@ void LLVertexBuffer::setupVertexBuffer(U32 data_mask) const { glNormalPointer(GL_FLOAT, stride, (void*)(base + mOffsets[TYPE_NORMAL])); } + if (data_mask & MAP_TEXCOORD3) + { + glClientActiveTextureARB(GL_TEXTURE3_ARB); + glTexCoordPointer(2,GL_FLOAT, stride, (void*)(base + mOffsets[TYPE_TEXCOORD3])); + glClientActiveTextureARB(GL_TEXTURE0_ARB); + } if (data_mask & MAP_TEXCOORD2) { - glClientActiveTextureARB(GL_TEXTURE1_ARB); + glClientActiveTextureARB(GL_TEXTURE2_ARB); glTexCoordPointer(2,GL_FLOAT, stride, (void*)(base + mOffsets[TYPE_TEXCOORD2])); glClientActiveTextureARB(GL_TEXTURE0_ARB); } - if (data_mask & MAP_TEXCOORD) + if (data_mask & MAP_TEXCOORD1) { - glTexCoordPointer(2,GL_FLOAT, stride, (void*)(base + mOffsets[TYPE_TEXCOORD])); + glClientActiveTextureARB(GL_TEXTURE1_ARB); + glTexCoordPointer(2,GL_FLOAT, stride, (void*)(base + mOffsets[TYPE_TEXCOORD1])); + glClientActiveTextureARB(GL_TEXTURE0_ARB); } - if (data_mask & MAP_COLOR) + if (data_mask & MAP_BINORMAL) { - glColorPointer(4, GL_UNSIGNED_BYTE, stride, (void*)(base + mOffsets[TYPE_COLOR])); + glClientActiveTextureARB(GL_TEXTURE2_ARB); + glTexCoordPointer(3,GL_FLOAT, stride, (void*)(base + mOffsets[TYPE_BINORMAL])); + glClientActiveTextureARB(GL_TEXTURE0_ARB); } - if (data_mask & MAP_BINORMAL) + if (data_mask & MAP_TEXCOORD0) + { + glTexCoordPointer(2,GL_FLOAT, stride, (void*)(base + mOffsets[TYPE_TEXCOORD0])); + } + if (data_mask & MAP_COLOR) { - glVertexAttribPointerARB(6, 3, GL_FLOAT, FALSE, stride, (void*)(base + mOffsets[TYPE_BINORMAL])); + glColorPointer(4, GL_UNSIGNED_BYTE, stride, (void*)(base + mOffsets[TYPE_COLOR])); } + if (data_mask & MAP_WEIGHT) { glVertexAttribPointerARB(1, 1, GL_FLOAT, FALSE, stride, (void*)(base + mOffsets[TYPE_WEIGHT])); diff --git a/indra/llrender/llvertexbuffer.h b/indra/llrender/llvertexbuffer.h index 2b5f4e200b..94fa790957 100644 --- a/indra/llrender/llvertexbuffer.h +++ b/indra/llrender/llvertexbuffer.h @@ -2,30 +2,25 @@ * @file llvertexbuffer.h * @brief LLVertexBuffer wrapper for OpengGL vertex buffer objects * - * $LicenseInfo:firstyear=2003&license=viewergpl$ - * - * Copyright (c) 2003-2007, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2003&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlife.com/developers/opensource/gplv2 + * Copyright (C) 2010, Linden Research, Inc. + * + * 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; + * version 2.1 of the License only. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at http://secondlife.com/developers/opensource/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -38,9 +33,10 @@ #include "v4math.h" #include "v4coloru.h" #include "llstrider.h" -#include "llmemory.h" +#include "llrender.h" #include <set> #include <vector> +#include <list> //============================================================================ // NOTES @@ -82,6 +78,8 @@ public: static LLVBOPool sStreamIBOPool; static LLVBOPool sDynamicIBOPool; + static BOOL sUseStreamDraw; + static void initClass(bool use_vbo); static void cleanupClass(); static void setupClientArrays(U32 data_mask); @@ -97,8 +95,10 @@ public: enum { TYPE_VERTEX, TYPE_NORMAL, - TYPE_TEXCOORD, + TYPE_TEXCOORD0, + TYPE_TEXCOORD1, TYPE_TEXCOORD2, + TYPE_TEXCOORD3, TYPE_COLOR, // These use VertexAttribPointer and should possibly be made generic TYPE_BINORMAL, @@ -110,31 +110,19 @@ public: enum { MAP_VERTEX = (1<<TYPE_VERTEX), MAP_NORMAL = (1<<TYPE_NORMAL), - MAP_TEXCOORD = (1<<TYPE_TEXCOORD), + MAP_TEXCOORD0 = (1<<TYPE_TEXCOORD0), + MAP_TEXCOORD1 = (1<<TYPE_TEXCOORD1), MAP_TEXCOORD2 = (1<<TYPE_TEXCOORD2), + MAP_TEXCOORD3 = (1<<TYPE_TEXCOORD3), MAP_COLOR = (1<<TYPE_COLOR), // These use VertexAttribPointer and should possibly be made generic MAP_BINORMAL = (1<<TYPE_BINORMAL), MAP_WEIGHT = (1<<TYPE_WEIGHT), MAP_CLOTHWEIGHT = (1<<TYPE_CLOTHWEIGHT), - MAP_DRAW = 0x2000, // Buffer is in draw (read-only) mode - MAP_MAPPED = 0x4000, // Indicates that buffer has been mapped, but not to any type of data - MAP_UNMAPPED = 0x8000 // Indicates that buffer has been logically un-mapped }; - enum { - TRIANGLES = 0, - TRIANGLE_STRIP, - TRIANGLE_FAN, - POINTS, - LINES, - LINE_STRIP, - QUADS, - LINE_LOOP, - NUM_MODES - }; protected: - friend class LLGLImmediate; + friend class LLRender; virtual ~LLVertexBuffer(); // use unref() @@ -173,8 +161,8 @@ public: // vb->unmapBuffer(); bool getVertexStrider(LLStrider<LLVector3>& strider, S32 index=0); bool getIndexStrider(LLStrider<U16>& strider, S32 index=0); - bool getTexCoordStrider(LLStrider<LLVector2>& strider, S32 index=0); - bool getTexCoord2Strider(LLStrider<LLVector2>& strider, S32 index=0); + bool getTexCoord0Strider(LLStrider<LLVector2>& strider, S32 index=0); + bool getTexCoord1Strider(LLStrider<LLVector2>& strider, S32 index=0); bool getNormalStrider(LLStrider<LLVector3>& strider, S32 index=0); bool getBinormalStrider(LLStrider<LLVector3>& strider, S32 index=0); bool getColorStrider(LLStrider<LLColor4U>& strider, S32 index=0); @@ -254,7 +242,7 @@ public: static BOOL sEnableVBOs; static S32 sTypeOffsets[TYPE_MAX]; - static U32 sGLMode[NUM_MODES]; + static U32 sGLMode[LLRender::NUM_MODES]; static U32 sGLRenderBuffer; static U32 sGLRenderIndices; static BOOL sVBOActive; |