summaryrefslogtreecommitdiff
path: root/indra/llrender
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llrender')
-rw-r--r--indra/llrender/CMakeLists.txt67
-rw-r--r--indra/llrender/llcubemap.cpp496
-rw-r--r--indra/llrender/llcubemap.h86
-rw-r--r--indra/llrender/llfontbitmapcache.cpp163
-rw-r--r--indra/llrender/llfontbitmapcache.h72
-rw-r--r--indra/llrender/llfontfreetype.cpp592
-rw-r--r--indra/llrender/llfontfreetype.h176
-rw-r--r--indra/llrender/llfontgl.cpp1827
-rw-r--r--indra/llrender/llfontgl.h315
-rw-r--r--indra/llrender/llfontregistry.cpp670
-rw-r--r--indra/llrender/llfontregistry.h112
-rw-r--r--indra/llrender/llgl.cpp2059
-rw-r--r--indra/llrender/llgl.h419
-rw-r--r--indra/llrender/llgldbg.cpp35
-rw-r--r--indra/llrender/llgldbg.h35
-rw-r--r--indra/llrender/llglheaders.h833
-rw-r--r--indra/llrender/llglslshader.cpp904
-rw-r--r--indra/llrender/llglslshader.h139
-rw-r--r--indra/llrender/llglstates.h281
-rw-r--r--indra/llrender/llgltypes.h39
-rw-r--r--indra/llrender/llimagegl.cpp1335
-rw-r--r--indra/llrender/llimagegl.h254
-rw-r--r--indra/llrender/llpostprocess.cpp580
-rw-r--r--indra/llrender/llpostprocess.h266
-rw-r--r--indra/llrender/llrender.cpp1378
-rw-r--r--indra/llrender/llrender.h387
-rw-r--r--indra/llrender/llrendersphere.cpp185
-rw-r--r--indra/llrender/llrendersphere.h56
-rw-r--r--indra/llrender/llrendertarget.cpp626
-rw-r--r--indra/llrender/llrendertarget.h112
-rw-r--r--indra/llrender/llshadermgr.cpp508
-rw-r--r--indra/llrender/llshadermgr.h70
-rw-r--r--indra/llrender/lltexture.cpp31
-rw-r--r--indra/llrender/lltexture.h73
-rw-r--r--indra/llrender/llvertexbuffer.cpp383
-rw-r--r--indra/llrender/llvertexbuffer.h70
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;